python-tackerclient-0.11.0/0000775000175100017510000000000013234643340015606 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/0000775000175100017510000000000013234643340020277 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/0000775000175100017510000000000013234643340021577 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/_static/0000775000175100017510000000000013234643340023225 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/_static/.placeholder0000666000175100017510000000000013234643056025504 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/conf.py0000666000175100017510000002064513234643056023113 0ustar zuulzuul00000000000000# 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', 'openstackdocstheme' ] # 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' # Release notes are version independent. release = '' version = '' # 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 = 'openstackdocs' # 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 # " v 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 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' # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/python-tackerclient' bug_project = 'python-tackerclient' bug_tag = '' python-tackerclient-0.11.0/releasenotes/source/pike.rst0000666000175100017510000000021713234643056023267 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike python-tackerclient-0.11.0/releasenotes/source/index.rst0000666000175100017510000000022713234643056023447 0ustar zuulzuul00000000000000Python-TackerClient Release Notes ================================= Contents: .. toctree:: :maxdepth: 2 unreleased pike ocata newton python-tackerclient-0.11.0/releasenotes/source/unreleased.rst0000666000175100017510000000015313234643056024465 0ustar zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: python-tackerclient-0.11.0/releasenotes/source/_templates/0000775000175100017510000000000013234643340023734 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/_templates/.placeholder0000666000175100017510000000000013234643056026213 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/source/ocata.rst0000666000175100017510000000023013234643056023421 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata python-tackerclient-0.11.0/releasenotes/source/newton.rst0000666000175100017510000000023213234643056023646 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton python-tackerclient-0.11.0/releasenotes/notes/0000775000175100017510000000000013234643340021427 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/releasenotes/notes/update-vim-without-config-c3b637741889eff6.yaml0000666000175100017510000000013713234643056031522 0ustar zuulzuul00000000000000--- features: - VIM can be updated without config-file argument in tacker vim-update command python-tackerclient-0.11.0/releasenotes/notes/deprecate-direct-yaml-cli-input-812564bab1b99b4b.yaml0000666000175100017510000000035013234643056032576 0ustar zuulzuul00000000000000--- deprecations: - | Direct YAML input of any kind is now deprecated. Only file based YAML input is supported. This deprecation is across all resources like VNFFGD template, VNFD template and VNF configuration input. ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000python-tackerclient-0.11.0/releasenotes/notes/deprecate-infra-mgmt-driver-attributes-e371624c50accee8.yamlpython-tackerclient-0.11.0/releasenotes/notes/deprecate-infra-mgmt-driver-attributes-e371624c50accee0000666000175100017510000000020713234643056033231 0ustar zuulzuul00000000000000--- deprecations: - infra_driver and mgmt_driver attributes in VNFD client attribute is deprecated and will be removed in Ocata. ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000python-tackerclient-0.11.0/releasenotes/notes/remove-passing-mgmt-and-infra-driver-from-client-c9135f84480b2cae.yamlpython-tackerclient-0.11.0/releasenotes/notes/remove-passing-mgmt-and-infra-driver-from-client-c91350000666000175100017510000000010113234643056033356 0ustar zuulzuul00000000000000--- fixes: - Remove passing mgmt and infra driver from client. python-tackerclient-0.11.0/releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml0000666000175100017510000000006013234643056027177 0ustar zuulzuul00000000000000--- features: - Add client support for VNFFG. python-tackerclient-0.11.0/releasenotes/notes/multi-delete-support-in-tacker-acd4a7e86114f0be.yaml0000666000175100017510000000010613234643056032644 0ustar zuulzuul00000000000000--- features: - Add support for multi delete feature for resources. python-tackerclient-0.11.0/releasenotes/notes/network-services-descriptor-06f6abe90adb40f3.yaml0000666000175100017510000000034113234643056032351 0ustar zuulzuul00000000000000--- features: - | CLIs to onboard Network Services Descriptor (NSD) based on TOSCA Simple Profile for NFV and to create Network Services using NSD to create multiple related VNFs using a single TOSCA template. python-tackerclient-0.11.0/releasenotes/notes/del-project_and_user_id-e9dd396f83a162d6.yaml0000666000175100017510000000024513234643056031320 0ustar zuulzuul00000000000000--- features: - As user gives input of project and user name in vim_config.yaml, delete the user and project id from the vim specific commands output. ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000python-tackerclient-0.11.0/releasenotes/notes/add-creating-ns-vnffg-from-template-213eee7f1820aa0c.yamlpython-tackerclient-0.11.0/releasenotes/notes/add-creating-ns-vnffg-from-template-213eee7f1820aa0c.y0000666000175100017510000000020113234643056032715 0ustar zuulzuul00000000000000--- features: - | Support to create directly VNFFG and NS from its descriptor template without creating VNFFGD and NSD.python-tackerclient-0.11.0/releasenotes/notes/vnf-inline-template-25f6a0b66f7407a1.yaml0000666000175100017510000000012213234643056030323 0ustar zuulzuul00000000000000--- features: - Support to create VNF with direct VNFD template input from CLI. ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000python-tackerclient-0.11.0/releasenotes/notes/vnfd-vnf-vim-name-mandatory-in-tacker-cli-dfe802af6de5c80e.yamlpython-tackerclient-0.11.0/releasenotes/notes/vnfd-vnf-vim-name-mandatory-in-tacker-cli-dfe802af6de50000666000175100017510000000010313234643056033202 0ustar zuulzuul00000000000000--- features: - Made VNFD/VNF/VIM names mandatory in tacker CLI. python-tackerclient-0.11.0/releasenotes/notes/new-commmand-vnf-resource-list-d5422ab917f0892f.yaml0000666000175100017510000000015413234643056032431 0ustar zuulzuul00000000000000--- features: - Adds new CLI command 'vnf-resource-list' to view VNF resources, such as VDU, CP, etc. python-tackerclient-0.11.0/ChangeLog0000664000175100017510000002251613234643337017374 0ustar zuulzuul00000000000000CHANGES ======= 0.11.0 ------ * Show classifier's name * Updated from global requirements * Complete VIM osc commands * Updated from global requirements * fix misspell * Updated from global requirements * Add "cert\_verify" in vim\_config file to support insecure VIM * Add "--vnffgd-template" to vnffg-update command * Base OpenStackClient(OSC) plugin support * Add doc migration framework * let hacking install its dependencies * Update the documentation link for doc migration * Updated from global requirements * Avoid tox\_install.sh for constraints support * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Implement client to support Kubernetes as VIM * Updated from global requirements * Fixes symmetrical for vnffg update * Fixes passing boolean as string for symmetrical * Move oslosphinx and openstackdocstheme to test-requirements * Updated from global requirements * Updated from global requirements * Update creating directly VNFFG and NS from descriptor template * Update reno for stable/pike * Updated from global requirements * Updated from global requirements * Switch from oslosphinx to openstackdocstheme * Error handling for vnfd-create with empty vnfd-file * Updated from global requirements 0.10.0 ------ * Add release note for vim update without config-file argument * Replace six.iteritems() with .items() * Updated from global requirements * Updated from global requirements * Add releasenotes for bug 1629169 * Auth\_creds should show the user specified values * Add "is\_default" attribute in vim-list,and remove some unnecessary info * Updated from global requirements * Updated from global requirements * Updated from global requirements * 'update-vim' to update params without config file * Updated from global requirements * Updated from global requirements * Updated from global requirements * direct input for config was deprecated in ocata. Removing now * Updated from global requirements * Refactor mox references to use the mock library in pythontackerclient * Remove log translations * Revert "Switch to oslo\_log" * Switch to oslo\_log * vnfd-delete gives ambiguous success message * The Python 3.5 is added * Updated from global requirements * sync test-requirement with global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update reno for stable/ocata 0.9.0 ----- * Add release notes for Ocata * Deprecate direct YAML input in tackerclient * Support VNF creation from inline VNF template * Removes unused utf-8 encoding declaration * Adds vnfd\_id coloumn in vnf-list * Remove unwanted 'required' parameters * Remove support for legacy VNFD templates * Improve help text for events-list command options * Fix invalid unit test code in python-tackerclient 0.8.0 ----- * Implement client support for NSD * Replaced e.message with str(e) * Support parameter input for vnffg template * Add Constraints support * Remove passing infra and mgmt driver from client * Fix oslo.i18n problems in python-tackerclient * Changed the help message of --config-file parameter * Updated from global requirements * Show team and repo badges on README * Updated from global requirements * Add \_\_ne\_\_ built-in function * Usability improvements of vim-update options * Updated from global requirements * Updated from global requirements * Add stevedore to requirements * Updated from global requirements * Revert "Remove unused import library" * make python34 CI job to pass * mox to mock refactor * Remove unused cliff tablib from test requirements * rename vm into vnfm * Enable release notes translation * Updated home-page info with the developer documentation * Updated from global requirements * Using assertIsInstance() instead of assertEqual(True, isinstance()) * Remove commented out code in test\_shell.py * Updated from global requirements * Updated from global requirements * Fix cString ImportError for py34 * Updated from global requirements * Updated from global requirements * Body can not be None while creating vnfd * Add support for multi delete * Update reno for stable/newton * Remove "else" branch in "create\_vnfd" function * Allow auth url without port for vim registration 0.7.0 ----- * Moving test files to vm directory * Updated from global requirements * Modify MyURLComparator to handle deep match on URL * Deprecate infra\_driver and mgmt\_driver * Remove list\_vnf\_resource\_pagination unit test * Removing test cases for already removed methods * Add client support for VNFFG in NFVO * cli: modify vnfd, param & config attr. to dict obj * Adds client commands for listing a VNF resources * Revert "Creates details API to fetch VNF detials" * Creates details API to fetch VNF detials 0.6.0 ----- * Adds audit support in client * Remove '--config' option when create/update a vim * Updated from global requirements * Add "Description" parameter while creating VNF with CLI * VNF scaling: CLI and python client * VNFD legacy template deprecation warning * Updated from global requirements 0.5.0 ----- * Add domain information into auth cred * Unified formats of log\_xxx function * Make VNFD/VNF/VIM Name Mandatory in Tacker CLI * Remove the mask password logic in vim list and vim show * Remove discover from test-requirements * Transition default VIM to API and DB operation * Remove unused import library * remove unused LOG * Updated from global requirements * Add .idea/ to python-tackerclient .gitignore * Fix ext-show command error * Updated from global requirements * Updated from global requirements 0.4.0 ----- * Change the initial letter to capitals in Tacker CLI help texts * Updated from global requirements * Add client side support for error\_reason * Change bind\_port to 9890 * Add reno support to python-tackerclient * Updated from global requirements * Updated from global requirements * Fix incompatible code with python3 for tox pep8 test * Capitalize help descriptions * Updated from global requirements * Updated from global requirements * Display Health status of VIM * Updated from global requirements * Updated from global requirements * py3.x: Use six.iteritems for Iterable dict items * Fix deprecation warning in tackerclient * Do not display password in VIM command outputs * Clean device from help message * Updated from global requirements * Updated from global requirements * Clean unused code * Cleanup copyright header * Updated from global requirements * Cleanup vim settings 0.3.0 ----- * Fix attr for vim update * Implement client support for multisite VIM * Cleanup Oslo Incubator code * Help message correction * Clean device related unused path * Clean up references to neutron * Code clean-up in client.py * Updated from global requirements * Removing "device" CLI from master branch * Fix summary and author in setup.cfg * Updated from global requirements * Updated from global requirements * Clean up flake8 ignore list * Code correction while calling base class method * Updated from global requirements * Remove argparse from requirements * Renamed 'servicevm' in python-tackerclient * Fix H238 errors in tackerclient code * use keystoneclient exceptions instead of oslo-incubator code * Fix H405 errors in tackerclient code * Put py34 first in the env order of tox * Fix H105 errors in tackerclient code * Fix E265, E129, E113 errors in tackerclient code * Updated from global requirements * Drop py33 support * Remove service instance related stuff and fix unittests * Deprecated tox -downloadcache option removed * Updated from global requirements * Remove py26 support * Update requirements according to global requirements * Added command to display VNFD's template * delete interface attach/detach commands 0.2.0 ----- * Fix for vnfd-delete with vnfd-name as arg * Escape yaml string to configure VDU * Update .gitreview for new namespace * Limit description length in vnfd-list * Implement Tacker MANO API client changes * Change ignore-errors to ignore\_errors * Fix drop of config values supplied by end user * Added validation for vnfd-create * Parameterization support added for VNFD templates * Support Tacker client in master branch * The vnf-create and vnfd-create output is hard to read * Add instructions to tackerclient * Add --vnfd-name option to vnf-create command * Clean up list output for device, vnf and vnfd * call \_super method for \_XtachInterface parent class * update\_vnf: support config as yaml file * vnfd: name and description was not supported * device, vnf: name column support * vnf-create: key error by tenant\_id * tackerclient: api for vnfd, vnd * vnfd, vnf: command options * teach vnf-create config * add vnf related command * add vnfd commends * remove commands for service * command: device-template and device * Revert "service instance: drop command related to service instance" * XML\_NS\_V20 -> XML\_NS\_V10 * catch up neutronclient change * use servicevm as service type for openstack client * rename --kwargs to --attributes * attach/detach interface command * service instance: drop command related to service instance * correct repo in .gitreview * Work toward Python 3.4 support and testing * implement servicevm related command * rename neutron to tacker * rename neutron to tacker * remove unnecessary neutronclient files * remove unnecessary neutron file under neutronclient/tests * setup logger name of NeutronCommand automatically * import python-neutronclient of 1bce6e437e2dd1fa5de6fc2ccdd0ee8ac3f44d18 * Added .gitreview python-tackerclient-0.11.0/tox.ini0000666000175100017510000000207313234643056017131 0ustar zuulzuul00000000000000[tox] envlist = py35,py27,pypy,pep8 minversion = 2.0 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C usedevelop = True install_command = pip install {opts} {packages} deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] commands = flake8 distribute = false [testenv:venv] commands = {posargs} [testenv:docs] commands = sphinx-build -W -b html doc/source doc/build/html [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}' [flake8] # E125 continuation line does not distinguish itself from next logical line ignore = E125 show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools python-tackerclient-0.11.0/setup.py0000666000175100017510000000200613234643056017324 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) python-tackerclient-0.11.0/PKG-INFO0000664000175100017510000000235213234643340016705 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: python-tackerclient Version: 0.11.0 Summary: CLI and Client Library for OpenStack Tacker Home-page: https://docs.openstack.org/tacker/latest Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-tackerclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on This is the client API library for Tacker. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 python-tackerclient-0.11.0/requirements.txt0000666000175100017510000000122713234643056021102 0ustar zuulzuul00000000000000# 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!=2.1.0,>=2.0.0 # Apache-2.0 cliff!=2.9.0,>=2.8.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD requests>=2.14.2 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 simplejson>=3.5.1 # MIT six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD oslo.i18n>=3.15.3 # Apache-2.0 osc-lib>=1.8.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 python-tackerclient-0.11.0/AUTHORS0000664000175100017510000000546413234643337016675 0ustar zuulzuul00000000000000Andreas Jaeger Anh Tran Anusree Bharath Thiruveedula Bob HADDLETON Cong Phuoc Hoang Dharmendra Kushwaha Dimitrios Markou Dirk Mueller Doug Hellmann Flavio Percoco Hideki Saito Isaku Yamahata Janki Janki Janki Chhatbar Janki Chhatbar Janonymous Jeremy Stanley KLuka Kanagaraj Manickam Kawaguchi Kentaro Lu lei Luka Krajger Manikantha Srinivas Tadi Martin Oemke Monty Taylor Naoya Harada Neeldhwaj Pathak Ondřej Nový OpenStack Release Bot Ronald Bradford Saju Madhavan Sharat Sharma Shrinath Suresh Sridhar Ramaswamy Sripriya Steve Martinelli Swapnil Kulkarni (coolsvap) Thomas Bechtold Tim Rozet Tin Lam Tony Breeds Tony Xu Trevor McCasland Trinath Somanchi Vishwanath Jayaraman Zuul aksingh an.abdulrehman changzhi claire1006 dharmendra dharmendra kushwaha digambar gecong1973 gong yong sheng gongysh howardlee huxining janki ji-xuepeng kavithahr lingyongxu rajat29 ricolin shihanzhang shu-mutou sunqingliang6 vagrant venkatamahesh vish xu-haiwei yong sheng gong python-tackerclient-0.11.0/tacker_test.sh0000777000175100017510000000044613234643056020467 0ustar zuulzuul00000000000000#!/bin/bash set -x function die() { local exitcode=$? set +o xtrace echo $@ exit $exitcode } noauth_tenant_id=me if [ $1 == 'noauth' ]; then NOAUTH="--tenant_id $noauth_tenant_id" else NOAUTH= fi FORMAT=" --request-format xml" # test the CRUD of xxx # TODO(yamahata) python-tackerclient-0.11.0/.coveragerc0000666000175100017510000000017613234643056017741 0ustar zuulzuul00000000000000[run] branch = True source = tackerclient omit = tackerclient/openstack/*,tackerclient/tests/* [report] ignore_errors = True python-tackerclient-0.11.0/.pylintrc0000666000175100017510000000255213234643056017465 0ustar zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [MASTER] # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore=test [Messages Control] # NOTE(justinsb): We might want to have a 2nd strict pylintrc in future # C0111: Don't require docstrings on every method # W0511: TODOs in code comments are fine. # W0142: *args and **kwargs are fine. # W0622: Redefining id is fine. disable=C0111,W0511,W0142,W0622 [Basic] # Variable names can be 1 to 31 characters long, with lowercase and underscores variable-rgx=[a-z_][a-z0-9_]{0,30}$ # Argument names can be 2 to 31 characters long, with lowercase and underscores argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long # and be lowecased with underscores method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ # Module names matching quantum-* are ok (files in bin/) module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(quantum-[a-z0-9_-]+))$ # Don't require docstrings on tests. no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ [Design] max-public-methods=100 min-public-methods=0 max-args=6 [Variables] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ python-tackerclient-0.11.0/tackerclient/0000775000175100017510000000000013234643340020256 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/version.py0000666000175100017510000000136713234643056022332 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # 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 pbr.version __version__ = pbr.version.VersionInfo('python-tackerclient').version_string() python-tackerclient-0.11.0/tackerclient/client.py0000666000175100017510000003430413234643056022120 0ustar zuulzuul00000000000000# Copyright 2012 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. # try: import json except ImportError: import simplejson as json import logging import os from keystoneclient import access from keystoneclient import adapter import requests from tackerclient.common import exceptions from tackerclient.common import utils from tackerclient.i18n import _ _logger = logging.getLogger(__name__) if os.environ.get('TACKERCLIENT_DEBUG'): ch = logging.StreamHandler() _logger.setLevel(logging.DEBUG) _logger.addHandler(ch) _requests_log_level = logging.DEBUG else: _requests_log_level = logging.WARNING logging.getLogger("requests").setLevel(_requests_log_level) MAX_URI_LEN = 8192 class HTTPClient(object): """Handles the REST calls and responses, include authn.""" USER_AGENT = 'python-tackerclient' CONTENT_TYPE = 'application/json' def __init__(self, username=None, user_id=None, tenant_name=None, tenant_id=None, password=None, auth_url=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, endpoint_type='publicURL', auth_strategy='keystone', ca_cert=None, log_credentials=False, service_type='nfv-orchestration', **kwargs): self.username = username self.user_id = user_id self.tenant_name = tenant_name self.tenant_id = tenant_id self.password = password self.auth_url = auth_url.rstrip('/') if auth_url else None self.service_type = service_type self.endpoint_type = endpoint_type self.region_name = region_name self.timeout = timeout self.auth_token = token self.auth_tenant_id = None self.auth_user_id = None self.endpoint_url = endpoint_url self.auth_strategy = auth_strategy self.log_credentials = log_credentials if insecure: self.verify_cert = False else: self.verify_cert = ca_cert if ca_cert else True def _cs_request(self, *args, **kwargs): kargs = {} kargs.setdefault('headers', kwargs.get('headers', {})) kargs['headers']['User-Agent'] = self.USER_AGENT if 'body' in kwargs: kargs['body'] = kwargs['body'] if self.log_credentials: log_kargs = kargs else: log_kargs = self._strip_credentials(kargs) utils.http_log_req(_logger, args, log_kargs) try: resp, body = self.request(*args, **kargs) except requests.exceptions.SSLError as e: raise exceptions.SslCertificateValidationError(reason=e) except Exception as e: # Wrap the low-level connection error (socket timeout, redirect # limit, decompression error, etc) into our custom high-level # connection exception (it is excepted in the upper layers of code) _logger.debug("throwing ConnectionFailed : %s", e) raise exceptions.ConnectionFailed(reason=e) utils.http_log_resp(_logger, resp, body) if resp.status_code == 401: raise exceptions.Unauthorized(message=body) return resp, body def _strip_credentials(self, kwargs): if kwargs.get('body') and self.password: log_kwargs = kwargs.copy() log_kwargs['body'] = kwargs['body'].replace(self.password, 'REDACTED') return log_kwargs else: return kwargs def authenticate_and_fetch_endpoint_url(self): if not self.auth_token: self.authenticate() elif not self.endpoint_url: self.endpoint_url = self._get_endpoint_url() def request(self, url, method, body=None, headers=None, **kwargs): """Request without authentication.""" content_type = kwargs.pop('content_type', None) or 'application/json' headers = headers or {} headers.setdefault('Accept', content_type) if body: headers.setdefault('Content-Type', content_type) headers['User-Agent'] = self.USER_AGENT resp = requests.request( method, url, data=body, headers=headers, verify=self.verify_cert, timeout=self.timeout, **kwargs) return resp, resp.text def _check_uri_length(self, action): uri_len = len(self.endpoint_url) + len(action) if uri_len > MAX_URI_LEN: raise exceptions.RequestURITooLong( excess=uri_len - MAX_URI_LEN) def do_request(self, url, method, **kwargs): # Ensure client always has correct uri - do not guesstimate anything self.authenticate_and_fetch_endpoint_url() self._check_uri_length(url) # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: kwargs.setdefault('headers', {}) if self.auth_token is None: self.auth_token = "" kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._cs_request(self.endpoint_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized: self.authenticate() resp, body = self._cs_request( self.endpoint_url + url, method, **kwargs) return resp, body def _extract_service_catalog(self, body): """Set the client's service catalog from the response data.""" self.auth_ref = access.AccessInfo.factory(body=body) self.service_catalog = self.auth_ref.service_catalog self.auth_token = self.auth_ref.auth_token self.auth_tenant_id = self.auth_ref.tenant_id self.auth_user_id = self.auth_ref.user_id if not self.endpoint_url: self.endpoint_url = self.service_catalog.url_for( region_name=self.region_name, service_type=self.service_type, endpoint_type=self.endpoint_type) def _authenticate_keystone(self): if self.user_id: creds = {'userId': self.user_id, 'password': self.password} else: creds = {'username': self.username, 'password': self.password} if self.tenant_id: body = {'auth': {'passwordCredentials': creds, 'tenantId': self.tenant_id, }, } else: body = {'auth': {'passwordCredentials': creds, 'tenantName': self.tenant_name, }, } if self.auth_url is None: raise exceptions.NoAuthURLProvided() token_url = self.auth_url + "/tokens" resp, resp_body = self._cs_request(token_url, "POST", body=json.dumps(body), content_type="application/json", allow_redirects=True) if resp.status_code != 200: raise exceptions.Unauthorized(message=resp_body) if resp_body: try: resp_body = json.loads(resp_body) except ValueError: pass else: resp_body = None self._extract_service_catalog(resp_body) def _authenticate_noauth(self): if not self.endpoint_url: message = _('For "noauth" authentication strategy, the endpoint ' 'must be specified either in the constructor or ' 'using --os-url') raise exceptions.Unauthorized(message=message) def authenticate(self): if self.auth_strategy == 'keystone': self._authenticate_keystone() elif self.auth_strategy == 'noauth': self._authenticate_noauth() else: err_msg = _('Unknown auth strategy: %s') % self.auth_strategy raise exceptions.Unauthorized(message=err_msg) def _get_endpoint_url(self): if self.auth_url is None: raise exceptions.NoAuthURLProvided() url = self.auth_url + '/tokens/%s/endpoints' % self.auth_token try: resp, body = self._cs_request(url, "GET") except exceptions.Unauthorized: # rollback to authenticate() to handle case when tacker client # is initialized just before the token is expired self.authenticate() return self.endpoint_url body = json.loads(body) for endpoint in body.get('endpoints', []): if (endpoint['type'] == 'nfv-orchestration' and endpoint.get('region') == self.region_name): if self.endpoint_type not in endpoint: raise exceptions.EndpointTypeNotFound( type_=self.endpoint_type) return endpoint[self.endpoint_type] raise exceptions.EndpointNotFound() def get_auth_info(self): return {'auth_token': self.auth_token, 'auth_tenant_id': self.auth_tenant_id, 'auth_user_id': self.auth_user_id, 'endpoint_url': self.endpoint_url} class SessionClient(adapter.Adapter): def request(self, *args, **kwargs): kwargs.setdefault('authenticated', False) kwargs.setdefault('raise_exc', False) content_type = kwargs.pop('content_type', None) or 'application/json' headers = kwargs.setdefault('headers', {}) headers.setdefault('Accept', content_type) try: kwargs.setdefault('data', kwargs.pop('body')) except KeyError: pass if kwargs.get('data'): headers.setdefault('Content-Type', content_type) resp = super(SessionClient, self).request(*args, **kwargs) return resp, resp.text def _check_uri_length(self, url): uri_len = len(self.endpoint_url) + len(url) if uri_len > MAX_URI_LEN: raise exceptions.RequestURITooLong( excess=uri_len - MAX_URI_LEN) def do_request(self, url, method, **kwargs): kwargs.setdefault('authenticated', True) self._check_uri_length(url) return self.request(url, method, **kwargs) @property def endpoint_url(self): # NOTE(jamielennox): This is used purely by the CLI and should be # removed when the CLI gets smarter. return self.get_endpoint() @property def auth_token(self): # NOTE(jamielennox): This is used purely by the CLI and should be # removed when the CLI gets smarter. return self.get_token() def authenticate(self): # NOTE(jamielennox): This is used purely by the CLI and should be # removed when the CLI gets smarter. self.get_token() def get_auth_info(self): auth_info = {'auth_token': self.auth_token, 'endpoint_url': self.endpoint_url} # NOTE(jamielennox): This is the best we can do here. It will work # with identity plugins which is the primary case but we should # deprecate it's usage as much as possible. try: get_access = (self.auth or self.session.auth).get_access except AttributeError: pass else: auth_ref = get_access(self.session) auth_info['auth_tenant_id'] = auth_ref.project_id auth_info['auth_user_id'] = auth_ref.user_id return auth_info # FIXME(bklei): Should refactor this to use kwargs and only # explicitly list arguments that are not None. def construct_http_client(username=None, user_id=None, tenant_name=None, tenant_id=None, password=None, auth_url=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, endpoint_type='publicURL', log_credentials=None, auth_strategy='keystone', ca_cert=None, service_type='nfv-orchestration', session=None, **kwargs): if session: kwargs.setdefault('user_agent', 'python-tackerclient') kwargs.setdefault('interface', endpoint_type) return SessionClient(session=session, service_type=service_type, region_name=region_name, **kwargs) else: # FIXME(bklei): username and password are now optional. Need # to test that they were provided in this mode. Should also # refactor to use kwargs. return HTTPClient(username=username, password=password, tenant_id=tenant_id, tenant_name=tenant_name, user_id=user_id, auth_url=auth_url, token=token, endpoint_url=endpoint_url, insecure=insecure, timeout=timeout, region_name=region_name, endpoint_type=endpoint_type, service_type=service_type, ca_cert=ca_cert, log_credentials=log_credentials, auth_strategy=auth_strategy) python-tackerclient-0.11.0/tackerclient/common/0000775000175100017510000000000013234643340021546 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/common/clientmanager.py0000666000175100017510000000731113234643056024741 0ustar zuulzuul00000000000000# Copyright 2012 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. # """Manage access to the clients, including authenticating when needed. """ from tackerclient import client from tackerclient.tacker import client as tacker_client class ClientCache(object): """Descriptor class for caching created client handles.""" def __init__(self, factory): self.factory = factory self._handle = None def __get__(self, instance, owner): # Tell the ClientManager to login to keystone if self._handle is None: self._handle = self.factory(instance) return self._handle class ClientManager(object): """Manages access to API clients, including authentication.""" tacker = ClientCache(tacker_client.make_client) def __init__(self, token=None, url=None, auth_url=None, endpoint_type=None, tenant_name=None, tenant_id=None, username=None, user_id=None, password=None, region_name=None, api_version=None, auth_strategy=None, insecure=False, ca_cert=None, log_credentials=False, service_type=None, timeout=None, retries=0, raise_errors=True, session=None, auth=None, ): self._token = token self._url = url self._auth_url = auth_url self._service_type = service_type self._endpoint_type = endpoint_type self._tenant_name = tenant_name self._tenant_id = tenant_id self._username = username self._user_id = user_id self._password = password self._region_name = region_name self._api_version = api_version self._service_catalog = None self._auth_strategy = auth_strategy self._insecure = insecure self._ca_cert = ca_cert self._log_credentials = log_credentials self._timeout = timeout self._retries = retries self._raise_errors = raise_errors self._session = session self._auth = auth return def initialize(self): if not self._url: httpclient = client.construct_http_client( username=self._username, user_id=self._user_id, tenant_name=self._tenant_name, tenant_id=self._tenant_id, password=self._password, region_name=self._region_name, auth_url=self._auth_url, service_type=self._service_type, endpoint_type=self._endpoint_type, insecure=self._insecure, ca_cert=self._ca_cert, timeout=self._timeout, session=self._session, auth=self._auth, log_credentials=self._log_credentials) httpclient.authenticate() # Populate other password flow attributes self._token = httpclient.auth_token self._url = httpclient.endpoint_url python-tackerclient-0.11.0/tackerclient/common/command.py0000666000175100017510000000204313234643056023543 0ustar zuulzuul00000000000000# Copyright 2012 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 cliff import command class OpenStackCommand(command.Command): """Base class for OpenStack commands.""" api = None def run(self, parsed_args): if not self.api: return else: return super(OpenStackCommand, self).run(parsed_args) def get_data(self, parsed_args): pass def take_action(self, parsed_args): return self.get_data(parsed_args) python-tackerclient-0.11.0/tackerclient/common/validators.py0000666000175100017510000000503613234643056024302 0ustar zuulzuul00000000000000# Copyright 2014 NEC Corporation # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from tackerclient.common import exceptions from tackerclient.i18n import _ def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None): val = getattr(parsed_args, attr_name, None) if val is None: return try: if not isinstance(val, int): int_val = int(val, 0) else: int_val = val if ((min_value is None or min_value <= int_val) and (max_value is None or int_val <= max_value)): return except (ValueError, TypeError): pass if min_value is not None and max_value is not None: msg = (_('%(attr_name)s "%(val)s" should be an integer ' '[%(min)i:%(max)i].') % {'attr_name': attr_name.replace('_', '-'), 'val': val, 'min': min_value, 'max': max_value}) elif min_value is not None: msg = (_('%(attr_name)s "%(val)s" should be an integer ' 'greater than or equal to %(min)i.') % {'attr_name': attr_name.replace('_', '-'), 'val': val, 'min': min_value}) elif max_value is not None: msg = (_('%(attr_name)s "%(val)s" should be an integer ' 'smaller than or equal to %(max)i.') % {'attr_name': attr_name.replace('_', '-'), 'val': val, 'max': max_value}) else: msg = (_('%(attr_name)s "%(val)s" should be an integer.') % {'attr_name': attr_name.replace('_', '-'), 'val': val}) raise exceptions.CommandError(msg) def validate_ip_subnet(parsed_args, attr_name): val = getattr(parsed_args, attr_name) if not val: return try: netaddr.IPNetwork(val) except (netaddr.AddrFormatError, ValueError): raise exceptions.CommandError( (_('%(attr_name)s "%(val)s" is not a valid CIDR.') % {'attr_name': attr_name.replace('_', '-'), 'val': val})) python-tackerclient-0.11.0/tackerclient/common/utils.py0000666000175100017510000001327613234643056023277 0ustar zuulzuul00000000000000# Copyright 2011, VMware, Inc. # # 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. # # Borrowed from nova code base, more utilities will be added/borrowed as and # when needed. """Utilities and helper functions.""" import argparse import logging import os from oslo_log import versionutils from oslo_utils import encodeutils from oslo_utils import importutils import six from tackerclient.common import exceptions from tackerclient.i18n import _ def env(*vars, **kwargs): """Returns the first environment variable set. If none are non-empty, defaults to '' or keyword arg default. """ for v in vars: value = os.environ.get(v) if value: return value return kwargs.get('default', '') def get_client_class(api_name, version, version_map): """Returns the client class for the requested API version. :param api_name: the name of the API, e.g. 'compute', 'image', etc :param version: the requested API version :param version_map: a dict of client classes keyed by version :rtype: a client class for the requested API version """ try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = _("Invalid %(api_name)s client version '%(version)s'. must be " "one of: %(map_keys)s") msg = msg % {'api_name': api_name, 'version': version, 'map_keys': ', '.join(version_map.keys())} raise exceptions.UnsupportedVersion(msg) return importutils.import_class(client_path) def get_item_properties(item, fields, mixed_case_fields=(), formatters=None): """Return a tuple containing the item properties. :param item: a single item resource (e.g. Server, Tenant, etc) :param fields: tuple of strings with the desired field names :param mixed_case_fields: tuple of field names to preserve case :param formatters: dictionary mapping field names to callables to format the values """ if formatters is None: formatters = {} row = [] for field in fields: if field in formatters: row.append(formatters[field](item)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') if not hasattr(item, field_name) and isinstance(item, dict): data = item[field_name] else: data = getattr(item, field_name, '') if data is None: data = '' row.append(data) return tuple(row) def str2bool(strbool): if strbool is None: return None return strbool.lower() == 'true' def str2dict(strdict): """Convert key1=value1,key2=value2,... string into dictionary. :param strdict: key1=value1,key2=value2 """ if not strdict: return {} return dict([kv.split('=', 1) for kv in strdict.split(',')]) def http_log_req(_logger, args, kwargs): if not _logger.isEnabledFor(logging.DEBUG): return string_parts = ['curl -i'] for element in args: if element in ('GET', 'POST', 'DELETE', 'PUT'): string_parts.append(' -X %s' % element) else: string_parts.append(' %s' % element) for element in kwargs['headers']: header = ' -H "%s: %s"' % (element, kwargs['headers'][element]) string_parts.append(header) if 'body' in kwargs and kwargs['body']: string_parts.append(" -d '%s'" % (kwargs['body'])) req = encodeutils.safe_encode("".join(string_parts)) _logger.debug("\nREQ: %s\n", req) def http_log_resp(_logger, resp, body): if not _logger.isEnabledFor(logging.DEBUG): return _logger.debug("RESP:%(code)s %(headers)s %(body)s\n", {'code': resp.status_code, 'headers': resp.headers, 'body': body}) def _safe_encode_without_obj(data): if isinstance(data, six.string_types): return encodeutils.safe_encode(data) return data def safe_encode_list(data): return list(map(_safe_encode_without_obj, data)) def safe_encode_dict(data): def _encode_item(item): k, v = item if isinstance(v, list): return (k, safe_encode_list(v)) elif isinstance(v, dict): return (k, safe_encode_dict(v)) return (k, _safe_encode_without_obj(v)) return dict(list(map(_encode_item, data.items()))) def add_boolean_argument(parser, name, **kwargs): for keyword in ('metavar', 'choices'): kwargs.pop(keyword, None) default = kwargs.pop('default', argparse.SUPPRESS) parser.add_argument( name, metavar='{True,False}', choices=['True', 'true', 'False', 'false'], default=default, **kwargs) def get_file_path(filename): file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../%s' % filename)) return file_path def deprecate_warning(what, as_of, in_favor_of=None, remove_in=1): versionutils.deprecation_warning(as_of=as_of, what=what, in_favor_of=in_favor_of, remove_in=remove_in) python-tackerclient-0.11.0/tackerclient/common/__init__.py0000666000175100017510000000000013234643056023653 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/common/_i18n.py0000666000175100017510000000227113234643056023046 0ustar zuulzuul00000000000000# 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 https://docs.openstack.org/oslo.i18n/latest/user/index.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 def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) python-tackerclient-0.11.0/tackerclient/common/serializer.py0000666000175100017510000003514013234643056024302 0ustar zuulzuul00000000000000# Copyright 2013 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 logging from xml.etree import ElementTree as etree from xml.parsers import expat from oslo_serialization import jsonutils import six from tackerclient.common import constants from tackerclient.common import exceptions as exception from tackerclient.i18n import _ LOG = logging.getLogger(__name__) if six.PY3: long = int class ActionDispatcher(object): """Maps method name to local methods through action name.""" def dispatch(self, *args, **kwargs): """Find and call local method.""" action = kwargs.pop('action', 'default') action_method = getattr(self, str(action), self.default) return action_method(*args, **kwargs) def default(self, data): raise NotImplementedError() class DictSerializer(ActionDispatcher): """Default request body serialization.""" def serialize(self, data, action='default'): return self.dispatch(data, action=action) def default(self, data): return "" class JSONDictSerializer(DictSerializer): """Default JSON request body serialization.""" def default(self, data): def sanitizer(obj): return six.text_type(obj) return jsonutils.dumps(data, default=sanitizer) class XMLDictSerializer(DictSerializer): def __init__(self, metadata=None, xmlns=None): """XMLDictSerializer constructor. :param metadata: information needed to deserialize XML into a dictionary. :param xmlns: XML namespace to include with serialized XML """ super(XMLDictSerializer, self).__init__() self.metadata = metadata or {} if not xmlns: xmlns = self.metadata.get('xmlns') if not xmlns: xmlns = constants.XML_NS_V10 self.xmlns = xmlns def default(self, data): """Default serializer of XMLDictSerializer. :param data: expect data to contain a single key as XML root, or contain another '*_links' key as atom links. Other case will use 'VIRTUAL_ROOT_KEY' as XML root. """ try: links = None has_atom = False if data is None: root_key = constants.VIRTUAL_ROOT_KEY root_value = None else: link_keys = [k for k in six.iterkeys(data) or [] if k.endswith('_links')] if link_keys: links = data.pop(link_keys[0], None) has_atom = True root_key = (len(data) == 1 and list(data.keys())[0] or constants.VIRTUAL_ROOT_KEY) root_value = data.get(root_key, data) doc = etree.Element("_temp_root") used_prefixes = [] self._to_xml_node(doc, self.metadata, root_key, root_value, used_prefixes) if links: self._create_link_nodes(list(doc)[0], links) return self.to_xml_string(list(doc)[0], used_prefixes, has_atom) except AttributeError as e: LOG.exception(str(e)) return '' def __call__(self, data): # Provides a migration path to a cleaner WSGI layer, this # "default" stuff and extreme extensibility isn't being used # like originally intended return self.default(data) def to_xml_string(self, node, used_prefixes, has_atom=False): self._add_xmlns(node, used_prefixes, has_atom) return etree.tostring(node, encoding='UTF-8') # NOTE(ameade): the has_atom should be removed after all of the # XML serializers and view builders have been updated to the current # spec that required all responses include the xmlns:atom, the has_atom # flag is to prevent current tests from breaking def _add_xmlns(self, node, used_prefixes, has_atom=False): node.set('xmlns', self.xmlns) node.set(constants.TYPE_XMLNS, self.xmlns) if has_atom: node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE) node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE) ext_ns = self.metadata.get(constants.EXT_NS, {}) for prefix in used_prefixes: if prefix in ext_ns: node.set('xmlns:' + prefix, ext_ns[prefix]) def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): """Recursive method to convert data members to XML nodes.""" result = etree.SubElement(parent, nodename) if ":" in nodename: used_prefixes.append(nodename.split(":", 1)[0]) # TODO(bcwaldon): accomplish this without a type-check if isinstance(data, list): if not data: result.set( constants.TYPE_ATTR, constants.TYPE_LIST) return result singular = metadata.get('plurals', {}).get(nodename, None) if singular is None: if nodename.endswith('s'): singular = nodename[:-1] else: singular = 'item' for item in data: self._to_xml_node(result, metadata, singular, item, used_prefixes) # TODO(bcwaldon): accomplish this without a type-check elif isinstance(data, dict): if not data: result.set( constants.TYPE_ATTR, constants.TYPE_DICT) return result attrs = metadata.get('attributes', {}).get(nodename, {}) for k, v in sorted(data.items()): if k in attrs: result.set(k, str(v)) else: self._to_xml_node(result, metadata, k, v, used_prefixes) elif data is None: result.set(constants.XSI_ATTR, 'true') else: if isinstance(data, bool): result.set( constants.TYPE_ATTR, constants.TYPE_BOOL) elif isinstance(data, int): result.set( constants.TYPE_ATTR, constants.TYPE_INT) elif isinstance(data, long): result.set( constants.TYPE_ATTR, constants.TYPE_LONG) elif isinstance(data, float): result.set( constants.TYPE_ATTR, constants.TYPE_FLOAT) LOG.debug("Data %(data)s type is %(type)s", {'data': data, 'type': type(data)}) result.text = six.text_type(data) return result def _create_link_nodes(self, xml_doc, links): for link in links: link_node = etree.SubElement(xml_doc, 'atom:link') link_node.set('rel', link['rel']) link_node.set('href', link['href']) class TextDeserializer(ActionDispatcher): """Default request body deserialization.""" def deserialize(self, datastring, action='default'): return self.dispatch(datastring, action=action) def default(self, datastring): return {} class JSONDeserializer(TextDeserializer): def _from_json(self, datastring): try: return jsonutils.loads(datastring) except ValueError: msg = _("Cannot understand JSON") raise exception.MalformedResponseBody(reason=msg) def default(self, datastring): return {'body': self._from_json(datastring)} class XMLDeserializer(TextDeserializer): def __init__(self, metadata=None): """XMLDeserializer constructor. :param metadata: information needed to deserialize XML into a dictionary. """ super(XMLDeserializer, self).__init__() self.metadata = metadata or {} xmlns = self.metadata.get('xmlns') if not xmlns: xmlns = constants.XML_NS_V10 self.xmlns = xmlns def _get_key(self, tag): tags = tag.split("}", 1) if len(tags) == 2: ns = tags[0][1:] bare_tag = tags[1] ext_ns = self.metadata.get(constants.EXT_NS, {}) if ns == self.xmlns: return bare_tag for prefix, _ns in ext_ns.items(): if ns == _ns: return prefix + ":" + bare_tag else: return tag def _get_links(self, root_tag, node): link_nodes = node.findall(constants.ATOM_LINK_NOTATION) root_tag = self._get_key(node.tag) link_key = "%s_links" % root_tag link_list = [] for link in link_nodes: link_list.append({'rel': link.get('rel'), 'href': link.get('href')}) # Remove link node in order to avoid link node being # processed as an item in _from_xml_node node.remove(link) return link_list and {link_key: link_list} or {} def _from_xml(self, datastring): if datastring is None: return None plurals = set(self.metadata.get('plurals', {})) try: node = etree.fromstring(datastring) root_tag = self._get_key(node.tag) links = self._get_links(root_tag, node) result = self._from_xml_node(node, plurals) # There is no case where root_tag = constants.VIRTUAL_ROOT_KEY # and links is not None because of the way data are serialized if root_tag == constants.VIRTUAL_ROOT_KEY: return result return dict({root_tag: result}, **links) except Exception as e: parseError = False # Python2.7 if (hasattr(etree, 'ParseError') and isinstance(e, getattr(etree, 'ParseError'))): parseError = True # Python2.6 elif isinstance(e, expat.ExpatError): parseError = True if parseError: msg = _("Cannot understand XML") raise exception.MalformedResponseBody(reason=msg) else: raise def _from_xml_node(self, node, listnames): """Convert a minidom node to a simple Python type. :param node: minidom node name :param listnames: list of XML node names whose subnodes should be considered list items. """ attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil"))) attrType = node.get(str(etree.QName( self.metadata.get('xmlns'), "type"))) if (attrNil and attrNil.lower() == 'true'): return None elif not len(node) and not node.text: if (attrType and attrType == constants.TYPE_DICT): return {} elif (attrType and attrType == constants.TYPE_LIST): return [] else: return '' elif (len(node) == 0 and node.text): converters = {constants.TYPE_BOOL: lambda x: x.lower() == 'true', constants.TYPE_INT: lambda x: int(x), constants.TYPE_LONG: lambda x: long(x), constants.TYPE_FLOAT: lambda x: float(x)} if attrType and attrType in converters: return converters[attrType](node.text) else: return node.text elif self._get_key(node.tag) in listnames: return [self._from_xml_node(n, listnames) for n in node] else: result = dict() for attr in node.keys(): if (attr == 'xmlns' or attr.startswith('xmlns:') or attr == constants.XSI_ATTR or attr == constants.TYPE_ATTR): continue result[self._get_key(attr)] = node.get(attr) children = list(node) for child in children: result[self._get_key(child.tag)] = self._from_xml_node( child, listnames) return result def default(self, datastring): return {'body': self._from_xml(datastring)} def __call__(self, datastring): # Adding a migration path to allow us to remove unncessary classes return self.default(datastring) # NOTE(maru): this class is duplicated from tacker.wsgi class Serializer(object): """Serializes and deserializes dictionaries to certain MIME types.""" def __init__(self, metadata=None, default_xmlns=None): """Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information needed to serialize a dictionary to that type. """ self.metadata = metadata or {} self.default_xmlns = default_xmlns def _get_serialize_handler(self, content_type): handlers = { 'application/json': JSONDictSerializer(), 'application/xml': XMLDictSerializer(self.metadata), } try: return handlers[content_type] except Exception: raise exception.InvalidContentType(content_type=content_type) def serialize(self, data, content_type): """Serialize a dictionary into the specified content type.""" return self._get_serialize_handler(content_type).serialize(data) def deserialize(self, datastring, content_type): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ return self.get_deserialize_handler(content_type).deserialize( datastring) def get_deserialize_handler(self, content_type): handlers = { 'application/json': JSONDeserializer(), 'application/xml': XMLDeserializer(self.metadata), } try: return handlers[content_type] except Exception: raise exception.InvalidContentType(content_type=content_type) python-tackerclient-0.11.0/tackerclient/common/exceptions.py0000666000175100017510000001335113234643056024312 0ustar zuulzuul00000000000000# Copyright 2011 VMware, 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.i18n import _ """ Tacker base exception handling. Exceptions are classified into three categories: * Exceptions corresponding to exceptions from tacker server: This type of exceptions should inherit one of exceptions in HTTP_EXCEPTION_MAP. * Exceptions from client library: This type of exceptions should inherit TackerClientException. * Exceptions from CLI code: This type of exceptions should inherit TackerCLIError. """ class TackerException(Exception): """Base Tacker Exception. To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. """ message = _("An unknown exception occurred.") def __init__(self, message=None, **kwargs): if message: self.message = message try: self._error_string = self.message % kwargs except Exception: # at least get the core message out if something happened self._error_string = self.message def __str__(self): return self._error_string class TackerClientException(TackerException): """Base exception which exceptions from Tacker are mapped into. NOTE: on the client side, we use different exception types in order to allow client library users to handle server exceptions in try...except blocks. The actual error message is the one generated on the server side. """ status_code = 0 def __init__(self, message=None, **kwargs): if 'status_code' in kwargs: self.status_code = kwargs['status_code'] super(TackerClientException, self).__init__(message, **kwargs) # Base exceptions from Tacker class BadRequest(TackerClientException): status_code = 400 class Unauthorized(TackerClientException): status_code = 401 message = _("Unauthorized: bad credentials.") class Forbidden(TackerClientException): status_code = 403 message = _("Forbidden: your credentials don't give you access to this " "resource.") class NotFound(TackerClientException): status_code = 404 class Conflict(TackerClientException): status_code = 409 class InternalServerError(TackerClientException): status_code = 500 class ServiceUnavailable(TackerClientException): status_code = 503 HTTP_EXCEPTION_MAP = { 400: BadRequest, 401: Unauthorized, 403: Forbidden, 404: NotFound, 409: Conflict, 500: InternalServerError, 503: ServiceUnavailable, } # Exceptions mapped to Tacker server exceptions # These are defined if a user of client library needs specific exception. # Exception name should be + 'Client' # e.g., NetworkNotFound -> NetworkNotFoundClient class NetworkNotFoundClient(NotFound): pass class PortNotFoundClient(NotFound): pass class StateInvalidClient(BadRequest): pass class NetworkInUseClient(Conflict): pass class PortInUseClient(Conflict): pass class IpAddressInUseClient(Conflict): pass class InvalidIpForNetworkClient(BadRequest): pass class OverQuotaClient(Conflict): pass class IpAddressGenerationFailureClient(Conflict): pass class MacAddressInUseClient(Conflict): pass class ExternalIpAddressExhaustedClient(BadRequest): pass # Exceptions from client library class NoAuthURLProvided(Unauthorized): message = _("auth_url was not provided to the Tacker client") class EndpointNotFound(TackerClientException): message = _("Could not find Service or Region in Service Catalog.") class EndpointTypeNotFound(TackerClientException): message = _("Could not find endpoint type %(type_)s in Service Catalog.") class AmbiguousEndpoints(TackerClientException): message = _("Found more than one matching endpoint in Service Catalog: " "%(matching_endpoints)") class RequestURITooLong(TackerClientException): """Raised when a request fails with HTTP error 414.""" def __init__(self, **kwargs): self.excess = kwargs.get('excess', 0) super(RequestURITooLong, self).__init__(**kwargs) class ConnectionFailed(TackerClientException): message = _("Connection to tacker failed: %(reason)s") class SslCertificateValidationError(TackerClientException): message = _("SSL certificate validation has failed: %(reason)s") class MalformedResponseBody(TackerClientException): message = _("Malformed response body: %(reason)s") class InvalidContentType(TackerClientException): message = _("Invalid content type %(content_type)s.") class InvalidInput(TackerClientException): message = _("Invalid input: %(reason)s") # Command line exceptions class TackerCLIError(TackerException): """Exception raised when command line parsing fails.""" pass class CommandError(TackerCLIError): pass class UnsupportedVersion(TackerCLIError): """Unsupported Version. Indicates that the user is trying to use an unsupported version of the API. """ pass class TackerClientNoUniqueMatch(TackerCLIError): message = _("Multiple %(resource)s matches found for name '%(name)s'," " use an ID to be more specific.") python-tackerclient-0.11.0/tackerclient/common/constants.py0000666000175100017510000000221713234643056024144 0ustar zuulzuul00000000000000# Copyright (c) 2012 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. EXT_NS = '_extension_ns' XML_NS_V10 = 'http://openstack.org/tacker/api/v1.0' XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance" XSI_ATTR = "xsi:nil" XSI_NIL_ATTR = "xmlns:xsi" TYPE_XMLNS = "xmlns:tacker" TYPE_ATTR = "tacker:type" VIRTUAL_ROOT_KEY = "_v_root" ATOM_NAMESPACE = "http://www.w3.org/2005/Atom" ATOM_XMLNS = "xmlns:atom" ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE TYPE_BOOL = "bool" TYPE_INT = "int" TYPE_LONG = "long" TYPE_FLOAT = "float" TYPE_LIST = "list" TYPE_DICT = "dict" PLURALS = {'templates': 'template', 'devices': 'device'} python-tackerclient-0.11.0/tackerclient/common/extension.py0000666000175100017510000000645013234643056024147 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Hosting 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 stevedore import extension from tackerclient.tacker import v1_0 as tackerV10 def _discover_via_entry_points(): emgr = extension.ExtensionManager('tackerclient.extension', invoke_on_load=False) return ((ext.name, ext.plugin) for ext in emgr) class TackerClientExtension(tackerV10.TackerCommand): pagination_support = False _formatters = {} sorting_support = False class ClientExtensionShow(TackerClientExtension, tackerV10.ShowCommand): def get_data(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): return super(ClientExtensionShow, self).get_data(parsed_args) class ClientExtensionList(TackerClientExtension, tackerV10.ListCommand): def get_data(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): return super(ClientExtensionList, self).get_data(parsed_args) class ClientExtensionDelete(TackerClientExtension, tackerV10.DeleteCommand): def run(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): return super(ClientExtensionDelete, self).run(parsed_args) class ClientExtensionCreate(TackerClientExtension, tackerV10.CreateCommand): def get_data(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): return super(ClientExtensionCreate, self).get_data(parsed_args) class ClientExtensionUpdate(TackerClientExtension, tackerV10.UpdateCommand): def run(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): return super(ClientExtensionUpdate, self).run(parsed_args) python-tackerclient-0.11.0/tackerclient/i18n.py0000666000175100017510000000135713234643056021423 0ustar zuulzuul00000000000000# 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 oslo_i18n as i18n _translators = i18n.TranslatorFactory(domain='tackerclient') # The primary translation function using the well-known name "_" _ = _translators.primary python-tackerclient-0.11.0/tackerclient/__init__.py0000666000175100017510000000000013234643056022363 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/0000775000175100017510000000000013234643340021527 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/client.py0000666000175100017510000000507113234643056023370 0ustar zuulzuul00000000000000# Copyright 2012 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 tackerclient.common._i18n import _ from tackerclient.common import exceptions from tackerclient.common import utils API_NAME = 'nfv-orchestration' API_VERSIONS = { '1.0': 'tackerclient.v1_0.client.Client', } def make_client(instance): """Returns an tacker client.""" tacker_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS, ) instance.initialize() url = instance._url url = url.rstrip("/") if '1.0' == instance._api_version[API_NAME]: client = tacker_client(username=instance._username, tenant_name=instance._tenant_name, password=instance._password, region_name=instance._region_name, auth_url=instance._auth_url, endpoint_url=url, endpoint_type=instance._endpoint_type, token=instance._token, auth_strategy=instance._auth_strategy, insecure=instance._insecure, ca_cert=instance._ca_cert, retries=instance._retries, raise_errors=instance._raise_errors, session=instance._session, auth=instance._auth) return client else: raise exceptions.UnsupportedVersion(_("API version %s is not " "supported") % instance._api_version[API_NAME]) def Client(api_version, *args, **kwargs): """Return an tacker client. :param api_version: only 1.0 is supported now """ tacker_client = utils.get_client_class( API_NAME, api_version, API_VERSIONS, ) return tacker_client(*args, **kwargs) python-tackerclient-0.11.0/tackerclient/tacker/__init__.py0000666000175100017510000000000013234643056023634 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/0000775000175100017510000000000013234643340022274 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/events/0000775000175100017510000000000013234643340023600 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/events/events.py0000666000175100017510000000615513234643056025473 0ustar zuulzuul00000000000000# 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.tacker import v1_0 as tackerV10 _EVENT = "event" class ListEventsBase(tackerV10.ListCommand): """Base class for list command.""" list_columns = ['id', 'resource_type', 'resource_id', 'resource_state', 'event_type', 'timestamp', 'event_details'] def get_parser(self, prog_name): parser = super(ListEventsBase, self).get_parser(prog_name) parser.add_argument('--id', help='id of the event to look up.') parser.add_argument('--resource-id', help='resource id of the events to look up.') parser.add_argument('--resource-state', help='resource state of the events to look up.') parser.add_argument('--event-type', help='event type of the events to look up.') return parser def args2search_opts(self, parsed_args): search_opts = super(ListEventsBase, self).args2search_opts( parsed_args) if parsed_args.id: search_opts.update({'id': parsed_args.id}) if parsed_args.resource_id: search_opts.update({'resource_id': parsed_args.resource_id}) if parsed_args.resource_state: search_opts.update({'resource_state': parsed_args.resource_state}) if parsed_args.event_type: search_opts.update({'event_type': parsed_args.event_type}) return search_opts class ListResourceEvents(ListEventsBase): """List events of resources.""" resource = _EVENT def get_parser(self, prog_name): parser = super(ListResourceEvents, self).get_parser(prog_name) parser.add_argument('--resource-type', help='resource type of the events to look up.') return parser def args2search_opts(self, parsed_args): search_opts = super(ListResourceEvents, self).args2search_opts( parsed_args) if parsed_args.resource_type: search_opts.update({'resource_type': parsed_args.resource_type}) return search_opts class ListVNFEvents(ListEventsBase): """List events of VNFs.""" resource = "vnf_event" class ListVNFDEvents(ListEventsBase): """List events of VNFDs.""" resource = "vnfd_event" class ListVIMEvents(ListEventsBase): """List events of VIMs.""" resource = "vim_event" class ShowEvent(tackerV10.ShowCommand): """Show event given the event id.""" resource = _EVENT python-tackerclient-0.11.0/tackerclient/tacker/v1_0/events/__init__.py0000666000175100017510000000000013234643056025705 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/vnfm/0000775000175100017510000000000013234643340023242 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/vnfm/vnfd.py0000666000175100017510000000737513234643056024573 0ustar zuulzuul00000000000000# # Copyright 2013 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. from __future__ import print_function from oslo_serialization import jsonutils import yaml from tackerclient.common import exceptions from tackerclient.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _VNFD = "vnfd" class ListVNFD(tackerV10.ListCommand): """List VNFD that belong to a given tenant.""" resource = _VNFD list_columns = ['id', 'name', 'template_source', 'description'] def get_parser(self, prog_name): parser = super(ListVNFD, self).get_parser(prog_name) parser.add_argument( '--template-source', help=_("List VNFD with specified template source. Available \ options are 'onboarded' (default), 'inline' or 'all'"), action='store', default='onboarded') return parser def args2search_opts(self, parsed_args): search_opts = super(ListVNFD, self).args2search_opts(parsed_args) template_source = parsed_args.template_source if parsed_args.template_source: search_opts.update({'template_source': template_source}) return search_opts class ShowVNFD(tackerV10.ShowCommand): """Show information of a given VNFD.""" resource = _VNFD class CreateVNFD(tackerV10.CreateCommand): """Create a VNFD.""" resource = _VNFD remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument('--vnfd-file', help=_('Specify VNFD file')) parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the VNFD')) parser.add_argument( '--description', help=_('Set a description for the VNFD')) def args2body(self, parsed_args): body = {self.resource: {}} vnfd = None if not parsed_args.vnfd_file: raise exceptions.InvalidInput("Invalid input for vnfd file") with open(parsed_args.vnfd_file) as f: vnfd = f.read() try: vnfd = yaml.load(vnfd, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if not vnfd: raise exceptions.InvalidInput("vnfd file is empty") body[self.resource]['attributes'] = {'vnfd': vnfd} tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description']) return body class DeleteVNFD(tackerV10.DeleteCommand): """Delete given VNFD(s).""" 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!')) python-tackerclient-0.11.0/tackerclient/tacker/v1_0/vnfm/vnf.py0000666000175100017510000002562013234643056024420 0ustar zuulzuul00000000000000# # Copyright 2013 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 yaml from tackerclient.common import exceptions from tackerclient.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _VNF = 'vnf' _RESOURCE = 'resource' class ListVNF(tackerV10.ListCommand): """List VNF that belong to a given tenant.""" resource = _VNF list_columns = ['id', 'name', 'mgmt_url', 'status', 'vim_id', 'vnfd_id'] class ShowVNF(tackerV10.ShowCommand): """Show information of a given VNF.""" resource = _VNF class CreateVNF(tackerV10.CreateCommand): """Create a VNF.""" resource = _VNF remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the VNF')) parser.add_argument( '--description', help=_('Set description for the VNF')) vnfd_group = parser.add_mutually_exclusive_group(required=True) vnfd_group.add_argument( '--vnfd-id', help=_('VNFD ID to use as template to create VNF')) vnfd_group.add_argument( '--vnfd-name', help=_('VNFD Name to use as template to create VNF')) vnfd_group.add_argument( '--vnfd-template', help=_("VNFD file to create VNF")) vim_group = parser.add_mutually_exclusive_group() vim_group.add_argument( '--vim-id', help=_('VIM ID to 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=_('YAML file with VNF configuration')) parser.add_argument( '--param-file', help=_('Specify parameter 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 config = None if parsed_args.config_file: with open(parsed_args.config_file) as f: config_yaml = f.read() try: config = yaml.load( config_yaml, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if config: args['attributes']['config'] = config if parsed_args.vim_region_name: args.setdefault('placement_attr', {})['region_name'] = \ parsed_args.vim_region_name tacker_client = self.get_client() tacker_client.format = parsed_args.request_format if parsed_args.vim_name: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'vim', parsed_args. vim_name) parsed_args.vim_id = _id if parsed_args.vnfd_name: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'vnfd', parsed_args. vnfd_name) parsed_args.vnfd_id = _id elif parsed_args.vnfd_template: with open(parsed_args.vnfd_template) as f: template = f.read() try: args['vnfd_template'] = yaml.load( template, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if parsed_args.param_file: with open(parsed_args.param_file) as f: param_yaml = f.read() try: args['attributes']['param_values'] = yaml.load( param_yaml, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description', 'vnfd_id', 'vim_id']) return body class UpdateVNF(tackerV10.UpdateCommand): """Update a given VNF.""" resource = _VNF def add_known_arguments(self, parser): parser.add_argument( '--config-file', help=_('YAML file with VNF configuration')) parser.add_argument( '--config', help=_('Specify config yaml data')) def args2body(self, parsed_args): body = {self.resource: {}} # config arg passed as data overrides config yaml when both args passed config = None if parsed_args.config_file: with open(parsed_args.config_file) as f: config_yaml = f.read() try: config = yaml.load(config_yaml, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if parsed_args.config: config = parsed_args.config if isinstance(config, str) or isinstance(config, unicode): config_str = parsed_args.config.decode('unicode_escape') try: config = yaml.load(config_str, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if config: body[self.resource]['attributes'] = {'config': config} tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id']) return body class DeleteVNF(tackerV10.DeleteCommand): """Delete given VNF(s).""" resource = _VNF deleted_msg = {'vnf': 'delete initiated'} class ListVNFResources(tackerV10.ListCommand): """List resources of a VNF like VDU, CP, etc.""" list_columns = ['name', 'id', 'type'] allow_names = True resource = _VNF def get_id(self): if self.resource: return self.resource.upper() def get_parser(self, prog_name): parser = super(ListVNFResources, self).get_parser(prog_name) if self.allow_names: help_str = _('ID or name of %s to look up') else: help_str = _('ID of %s to look up') parser.add_argument( 'id', metavar=self.get_id(), help=help_str % self.resource) return parser def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) tacker_client = self.get_client() tacker_client.format = parsed_args.request_format if self.allow_names: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, self.resource, parsed_args.id) else: _id = parsed_args.id data = self.retrieve_list_by_id(_id, parsed_args) self.extend_list(data, parsed_args) return self.setup_columns(data, parsed_args) def retrieve_list_by_id(self, id, parsed_args): """Retrieve a list of sub resources from Tacker server""" tacker_client = self.get_client() tacker_client.format = parsed_args.request_format _extra_values = tackerV10.parse_args_to_dict(self.values_specs) tackerV10._merge_args(self, parsed_args, _extra_values, self.values_specs) search_opts = self.args2search_opts(parsed_args) search_opts.update(_extra_values) if self.pagination_support: page_size = parsed_args.page_size if page_size: search_opts.update({'limit': page_size}) if self.sorting_support: keys = parsed_args.sort_key if keys: search_opts.update({'sort_key': keys}) dirs = parsed_args.sort_dir len_diff = len(keys) - len(dirs) if len_diff > 0: dirs += ['asc'] * len_diff elif len_diff < 0: dirs = dirs[:len(keys)] if dirs: search_opts.update({'sort_dir': dirs}) obj_lister = getattr(tacker_client, "list_vnf_resources") data = obj_lister(id, **search_opts) return data.get('resources', []) class ScaleVNF(tackerV10.TackerCommand): """Scale a VNF.""" api = 'nfv-orchestration' resource = None log = None def get_parser(self, prog_name): parser = super(ScaleVNF, self).get_parser(prog_name) 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) obj_creator = getattr(tacker_client, "scale_vnf") obj_creator(body["scale"].pop('vnf_id'), body) def add_known_arguments(self, parser): vnf_group = parser.add_mutually_exclusive_group(required=True) vnf_group.add_argument( '--vnf-id', help=_('VNF ID')) vnf_group.add_argument( '--vnf-name', help=_('VNF name')) parser.add_argument( '--scaling-policy-name', help=_('VNF policy name used to scale')) parser.add_argument( '--scaling-type', help=_('VNF scaling type, it could be either "out" or "in"')) def args2body(self, parsed_args): args = {} body = {"scale": args} if parsed_args.vnf_name: tacker_client = self.get_client() tacker_client.format = parsed_args.request_format _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'vnf', parsed_args. vnf_name) parsed_args.vnf_id = _id args['vnf_id'] = parsed_args.vnf_id args['type'] = parsed_args.scaling_type args['policy'] = parsed_args.scaling_policy_name return body python-tackerclient-0.11.0/tackerclient/tacker/v1_0/vnfm/__init__.py0000666000175100017510000000000013234643056025347 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/0000775000175100017510000000000013234643340023244 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/vnffgd.py0000666000175100017510000000654613234643056025111 0ustar zuulzuul00000000000000# 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 __future__ import print_function import yaml from oslo_serialization import jsonutils from tackerclient.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _VNFFGD = "vnffgd" class ListVNFFGD(tackerV10.ListCommand): """List VNFFGDs that belong to a given tenant.""" resource = _VNFFGD list_columns = ['id', 'name', 'template_source', 'description'] def get_parser(self, prog_name): parser = super(ListVNFFGD, self).get_parser(prog_name) parser.add_argument( '--template-source', help=_("List VNFFGD with specified template source. Available \ options are 'onboarded' (default), 'inline' or 'all'"), action='store', default='onboarded') return parser def args2search_opts(self, parsed_args): search_opts = super(ListVNFFGD, self).args2search_opts(parsed_args) template_source = parsed_args.template_source if parsed_args.template_source: search_opts.update({'template_source': template_source}) return search_opts class ShowVNFFGD(tackerV10.ShowCommand): """Show information of a given VNFFGD.""" resource = _VNFFGD class CreateVNFFGD(tackerV10.CreateCommand): """Create a VNFFGD.""" resource = _VNFFGD remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument('--vnffgd-file', help=_('Specify VNFFGD file')) parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the VNFFGD')) parser.add_argument( '--description', help=_('Set a description for the VNFFGD')) def args2body(self, parsed_args): body = {self.resource: {}} if parsed_args.vnffgd_file: with open(parsed_args.vnffgd_file) as f: vnffgd = yaml.safe_load(f.read()) body[self.resource]['template'] = {'vnffgd': vnffgd} tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description']) return body class DeleteVNFFGD(tackerV10.DeleteCommand): """Delete a given VNFFGD.""" resource = _VNFFGD class ShowTemplateVNFFGD(tackerV10.ShowCommand): """Show template of a given VNFFGD.""" resource = _VNFFGD 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('vnffgd', None) except (IndexError, TypeError, ValueError) as e: self.log.debug('Data handling error: %s', str(e)) print(template or _('Unable to display VNFFGD template!')) python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/nsd.py0000666000175100017510000000644313234643056024417 0ustar zuulzuul00000000000000# 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 __future__ import print_function import yaml from oslo_serialization import jsonutils from tackerclient.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _NSD = "nsd" class ListNSD(tackerV10.ListCommand): """List NSDs that belong to a given tenant.""" resource = _NSD list_columns = ['id', 'name', 'template_source', 'description'] def get_parser(self, prog_name): parser = super(ListNSD, self).get_parser(prog_name) parser.add_argument( '--template-source', help=_("List NSD with specified template source. Available \ options are 'onboared' (default), 'inline' or 'all'"), action='store', default='onboarded') return parser def args2search_opts(self, parsed_args): search_opts = super(ListNSD, self).args2search_opts(parsed_args) template_source = parsed_args.template_source if parsed_args.template_source: search_opts.update({'template_source': template_source}) return search_opts class ShowNSD(tackerV10.ShowCommand): """Show information of a given NSD.""" resource = _NSD class CreateNSD(tackerV10.CreateCommand): """Create a NSD.""" resource = _NSD remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument('--nsd-file', help='Specify NSD file', required=True) parser.add_argument( 'name', metavar='NAME', help='Set a name for the NSD') parser.add_argument( '--description', help='Set a description for the NSD') def args2body(self, parsed_args): body = {self.resource: {}} nsd = None with open(parsed_args.nsd_file) as f: nsd = yaml.safe_load(f.read()) tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description']) if nsd: body[self.resource]['attributes'] = {'nsd': nsd} return body class DeleteNSD(tackerV10.DeleteCommand): """Delete a given NSD.""" resource = _NSD class ShowTemplateNSD(tackerV10.ShowCommand): """Show template of a given NSD.""" resource = _NSD 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('nsd', None) except (IndexError, TypeError, ValueError) as e: self.log.debug('Data handling error: %s', str(e)) print(template or _('Unable to display NSD template!')) python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/ns.py0000666000175100017510000001077713234643056024260 0ustar zuulzuul00000000000000# 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.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _NS = 'ns' _RESOURCE = 'resource' class ListNS(tackerV10.ListCommand): """List NS that belong to a given tenant.""" resource = _NS list_columns = ['id', 'name', 'nsd_id', 'mgmt_urls', 'status'] class ShowNS(tackerV10.ShowCommand): """Show information of a given NS.""" resource = _NS class CreateNS(tackerV10.CreateCommand): """Create a NS.""" resource = _NS remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the NS')) parser.add_argument( '--description', help=_('Set description for the NS')) nsd_group = parser.add_mutually_exclusive_group(required=True) nsd_group.add_argument( '--nsd-id', help=_('NSD ID to use as template to create NS')) nsd_group.add_argument( '--nsd-template', help=_('NSD file to create NS')) nsd_group.add_argument( '--nsd-name', help=_('NSD name to use as template to create NS')) vim_group = parser.add_mutually_exclusive_group() vim_group.add_argument( '--vim-id', help=_('VIM ID to use to create NS on the specified VIM')) vim_group.add_argument( '--vim-name', help=_('VIM name to use to create NS on the specified VIM')) parser.add_argument( '--vim-region-name', help=_('VIM Region to use to create NS on the specified VIM')) parser.add_argument( '--param-file', help=_('Specify parameter yaml file')) def args2body(self, parsed_args): args = {'attributes': {}} body = {self.resource: args} if parsed_args.vim_region_name: args.setdefault('placement_attr', {})['region_name'] = \ parsed_args.vim_region_name tacker_client = self.get_client() tacker_client.format = parsed_args.request_format if parsed_args.vim_name: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'vim', parsed_args. vim_name) parsed_args.vim_id = _id if parsed_args.nsd_name: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'nsd', parsed_args. nsd_name) parsed_args.nsd_id = _id elif parsed_args.nsd_template: with open(parsed_args.nsd_template) as f: template = f.read() try: args['nsd_template'] = yaml.load( template, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if not args['nsd_template']: raise exceptions.InvalidInput('The nsd file is empty') if parsed_args.param_file: with open(parsed_args.param_file) as f: param_yaml = f.read() try: args['attributes']['param_values'] = yaml.load( param_yaml, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description', 'nsd_id', 'vim_id']) return body class DeleteNS(tackerV10.DeleteCommand): """Delete given NS(s).""" resource = _NS deleted_msg = {'ns': 'delete initiated'} python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/vim.py0000666000175100017510000001120713234643056024420 0ustar zuulzuul00000000000000# 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 oslo_utils import strutils from tackerclient.common import exceptions from tackerclient.i18n import _ 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', 'is_default', 'placement_attr', '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): parser.add_argument( '--config-file', required=True, help=_('YAML file with VIM configuration parameters')) parser.add_argument( 'name', metavar='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() try: config_param = yaml.load(vim_config, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) 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'] = vim_utils.validate_auth_url(auth_url).geturl() 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): parser.add_argument( '--config-file', required=False, help=_('YAML file with VIM configuration parameters')) parser.add_argument( '--name', help=_('New name for the VIM')) parser.add_argument( '--description', help=_('New description for the VIM')) parser.add_argument( '--is-default', type=strutils.bool_from_string, metavar='{True,False}', help=_('Indicate whether the VIM is used as default')) def args2body(self, parsed_args): body = {self.resource: {}} config_param = None # 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() try: config_param = yaml.load(config_yaml) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) vim_obj = body[self.resource] if config_param is not None: vim_utils.args2body_vim(config_param, vim_obj) tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'description', 'is_default']) # type attribute is read-only, it can't be updated, so remove it # in update method body['vim'].pop('type', None) return body class DeleteVIM(tackerV10.DeleteCommand): """Delete given VIM(s).""" resource = _VIM python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/__init__.py0000666000175100017510000000000013234643056025351 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/vim_utils.py0000666000175100017510000000757413234643056025654 0ustar zuulzuul00000000000000# 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 six.moves.urllib.parse as urlparse 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_type = ['openstack', 'kubernetes'] cert_verify_type = ['True', 'False'] if 'type' in config_param: vim['type'] = config_param.pop('type', '') if not vim['type'] in vim_type: raise exceptions.TackerClientException( message='Supported VIM types: openstack, kubernetes', status_code=400) else: vim['type'] = 'openstack' if vim['type'] == 'openstack': vim['vim_project'] = { 'name': config_param.pop('project_name', ''), 'project_domain_name': config_param.pop('project_domain_name', '')} if not vim['vim_project']['name']: raise exceptions.TackerClientException( message='Project name must be specified', status_code=404) cert_verify = config_param.pop('cert_verify', 'True') if cert_verify not in cert_verify_type: raise exceptions.TackerClientException( message='Supported cert_verify types: True, False', status_code=400) vim['auth_cred'] = {'username': config_param.pop('username', ''), 'password': config_param.pop('password', ''), 'user_domain_name': config_param.pop('user_domain_name', ''), 'cert_verify': cert_verify} elif vim['type'] == 'kubernetes': vim['vim_project'] = { 'name': config_param.pop('project_name', '')} if not vim['vim_project']['name']: raise exceptions.TackerClientException( message='Project name must be specified in Kubernetes VIM,' 'it is namespace in Kubernetes environment', status_code=404) if ('username' in config_param) and ('password' in config_param): vim['auth_cred'] = { 'username': config_param.pop('username', ''), 'password': config_param.pop('password', '')} elif 'bearer_token' in config_param: vim['auth_cred'] = { 'bearer_token': config_param.pop('bearer_token', '')} else: raise exceptions.TackerClientException( message='username and password or bearer_token must be' 'provided', status_code=404) if 'ssl_ca_cert' in config_param: ssl_ca_cert = config_param.pop('ssl_ca_cert', '') if ssl_ca_cert == 'None': vim['auth_cred']['ssl_ca_cert'] = None else: vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert else: raise exceptions.TackerClientException( message='ssl_ca_cert must be provided or leave it with None', status_code=404) def validate_auth_url(url): url_parts = urlparse.urlparse(url) if not url_parts.scheme or not url_parts.netloc: raise exceptions.TackerClientException(message='Invalid auth URL') return url_parts python-tackerclient-0.11.0/tackerclient/tacker/v1_0/nfvo/vnffg.py0000666000175100017510000001662513234643056024744 0ustar zuulzuul00000000000000# 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.i18n import _ from tackerclient.tacker import v1_0 as tackerV10 _VNFFG = 'vnffg' _NFP = 'nfp' _SFC = 'sfc' _FC = 'classifier' class ListFC(tackerV10.ListCommand): """List FCs that belong to a given tenant.""" resource = _FC list_columns = ['id', 'status', 'nfp_id', 'chain_id'] def extend_list(self, data, parsed_args): """Update the list_columns list. This method update the list_columns list by adding the 'name' column in case the retrieved FC list from the tacker server side contains the names of the FCs. """ for item in data: if 'name' in item: self.list_columns.insert(1, 'name') break class ShowFC(tackerV10.ShowCommand): """Show information of a given FC.""" resource = _FC class ListSFC(tackerV10.ListCommand): """List SFCs that belong to a given tenant.""" resource = _SFC list_columns = ['id', 'status', 'nfp_id'] class ShowSFC(tackerV10.ShowCommand): """Show information of a given SFC.""" resource = _SFC class ListNFP(tackerV10.ListCommand): """List NFPs that belong to a given tenant.""" resource = _NFP list_columns = ['id', 'name', 'status', 'vnffg_id', 'path_id'] class ShowNFP(tackerV10.ShowCommand): """Show information of a given NFP.""" resource = _NFP class ListVNFFG(tackerV10.ListCommand): """List VNFFGs that belong to a given tenant.""" resource = _VNFFG list_columns = ['id', 'name', 'description', 'status', 'vnffgd_id'] class ShowVNFFG(tackerV10.ShowCommand): """Show information of a given VNFFG.""" resource = _VNFFG class CreateVNFFG(tackerV10.CreateCommand): """Create a VNFFG.""" resource = _VNFFG remove_output_fields = ["attributes"] def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the VNFFG')) vnffgd_group = parser.add_mutually_exclusive_group(required=True) vnffgd_group.add_argument( '--vnffgd-id', help=_('VNFFGD ID to use as template to create VNFFG')) vnffgd_group.add_argument( '--vnffgd-name', help=_('VNFFGD Name to use as template to create VNFFG')) vnffgd_group.add_argument( '--vnffgd-template', help=_('VNFFGD file to create VNFFG')) parser.add_argument( '--vnf-mapping', help=_('List of logical VNFD name to VNF instance name mapping. ' 'Example: VNF1:my_vnf1,VNF2:my_vnf2')) parser.add_argument( '--symmetrical', action='store_true', default=False, help=_('Should a reverse path be created for the NFP')) parser.add_argument( '--param-file', help='Specify parameter yaml file' ) def args2body(self, parsed_args): args = {'attributes': {}} body = {self.resource: args} tacker_client = self.get_client() tacker_client.format = parsed_args.request_format if parsed_args.vnf_mapping: _vnf_mapping = dict() _vnf_mappings = parsed_args.vnf_mapping.split(",") for mapping in _vnf_mappings: vnfd_name, vnf = mapping.split(":", 1) _vnf_mapping[vnfd_name] = \ tackerV10.find_resourceid_by_name_or_id( tacker_client, 'vnf', vnf) parsed_args.vnf_mapping = _vnf_mapping if parsed_args.vnffgd_name: _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, 'vnffgd', parsed_args. vnffgd_name) parsed_args.vnffgd_id = _id elif parsed_args.vnffgd_template: with open(parsed_args.vnffgd_template) as f: template = f.read() try: args['vnffgd_template'] = yaml.load( template, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if not args['vnffgd_template']: raise exceptions.InvalidInput('The vnffgd file is empty') if parsed_args.param_file: with open(parsed_args.param_file) as f: param_yaml = f.read() try: args['attributes']['param_values'] = yaml.load( param_yaml, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'name', 'vnffgd_id', 'symmetrical', 'vnf_mapping']) return body class UpdateVNFFG(tackerV10.UpdateCommand): """Update a given VNFFG.""" resource = _VNFFG def add_known_arguments(self, parser): parser.add_argument( '--vnffgd-template', help=_('VNFFGD file to update VNFFG') ) parser.add_argument( '--vnf-mapping', help=_('List of logical VNFD name to VNF instance name mapping. ' 'Example: VNF1:my_vnf1,VNF2:my_vnf2')) parser.add_argument( '--symmetrical', action='store_true', default=False, help=_('Should a reverse path be created for the NFP')) def args2body(self, parsed_args): args = {} body = {self.resource: args} tacker_client = self.get_client() tacker_client.format = parsed_args.request_format if parsed_args.vnf_mapping: _vnf_mapping = dict() _vnf_mappings = parsed_args.vnf_mapping.split(",") for mapping in _vnf_mappings: vnfd_name, vnf = mapping.split(":", 1) _vnf_mapping[vnfd_name] = \ tackerV10.find_resourceid_by_name_or_id( tacker_client, 'vnf', vnf) parsed_args.vnf_mapping = _vnf_mapping if parsed_args.vnffgd_template: with open(parsed_args.vnffgd_template) as f: template = f.read() try: args['vnffgd_template'] = yaml.load( template, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) if not args['vnffgd_template']: raise exceptions.InvalidInput('The vnffgd template is empty') tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id', 'vnf_mapping', 'symmetrical']) return body class DeleteVNFFG(tackerV10.DeleteCommand): """Delete a given VNFFG.""" resource = _VNFFG python-tackerclient-0.11.0/tackerclient/tacker/v1_0/__init__.py0000666000175100017510000006326013234643056024422 0ustar zuulzuul00000000000000# Copyright 2012 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 __future__ import print_function import abc import argparse import logging import re from cliff.formatters import table from cliff import lister from cliff import show from oslo_serialization import jsonutils import six from tackerclient.common._i18n import _ from tackerclient.common import command from tackerclient.common import exceptions from tackerclient.common import utils HEX_ELEM = '[0-9A-Fa-f]' UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', HEX_ELEM + '{4}', HEX_ELEM + '{4}', HEX_ELEM + '{12}']) def _get_resource_plural(resource, client): plurals = getattr(client, 'EXTED_PLURALS', []) for k in plurals: if plurals[k] == resource: return k return resource + 's' def find_resourceid_by_id(client, resource, resource_id): resource_plural = _get_resource_plural(resource, client) obj_lister = getattr(client, "list_%s" % resource_plural) if resource == 'event': match = resource_id.isdigit() and resource_id != 0 else: match = re.match(UUID_PATTERN, resource_id) collection = resource_plural if match: data = obj_lister(id=resource_id, fields='id') if data and data[collection]: return data[collection][0]['id'] not_found_message = (_("Unable to find %(resource)s with id " "'%(id)s'") % {'resource': resource, 'id': resource_id}) # 404 is used to simulate server side behavior raise exceptions.TackerClientException( message=not_found_message, status_code=404) def _find_resourceid_by_name(client, resource, name): resource_plural = _get_resource_plural(resource, client) obj_lister = getattr(client, "list_%s" % resource_plural) data = obj_lister(name=name, fields='id') collection = resource_plural info = data[collection] if len(info) > 1: raise exceptions.TackerClientNoUniqueMatch(resource=resource, name=name) elif len(info) == 0: not_found_message = (_("Unable to find %(resource)s with name " "'%(name)s'") % {'resource': resource, 'name': name}) # 404 is used to simulate server side behavior raise exceptions.TackerClientException( message=not_found_message, status_code=404) else: return info[0]['id'] def find_resourceid_by_name_or_id(client, resource, name_or_id): try: return find_resourceid_by_id(client, resource, name_or_id) except exceptions.TackerClientException: return _find_resourceid_by_name(client, resource, name_or_id) def add_show_list_common_argument(parser): parser.add_argument( '-D', '--show-details', help=_('Show detailed info'), action='store_true', default=False,) parser.add_argument( '--show_details', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '--fields', help=argparse.SUPPRESS, action='append', default=[]) parser.add_argument( '-F', '--field', dest='fields', metavar='FIELD', help=_('Specify the field(s) to be returned by server. You can ' 'repeat this option.'), action='append', default=[]) def add_pagination_argument(parser): parser.add_argument( '-P', '--page-size', dest='page_size', metavar='SIZE', type=int, help=_("Specify retrieve unit of each request, then split one request " "to several requests"), default=None) def add_sorting_argument(parser): parser.add_argument( '--sort-key', dest='sort_key', metavar='FIELD', action='append', help=_("Sorts the list by the specified fields in the specified " "directions. You can repeat this option, but you must " "specify an equal number of sort_dir and sort_key values. " "Extra sort_dir options are ignored. Missing sort_dir options " "use the default asc value."), default=[]) parser.add_argument( '--sort-dir', dest='sort_dir', metavar='{asc,desc}', help=_("Sorts the list in the specified direction. You can repeat " "this option."), action='append', default=[], choices=['asc', 'desc']) def is_number(s): try: float(s) # for int, long and float except ValueError: try: complex(s) # for complex except ValueError: return False return True def _process_previous_argument(current_arg, _value_number, current_type_str, _list_flag, _values_specs, _clear_flag, values_specs): if current_arg is not None: if _value_number == 0 and (current_type_str or _list_flag): # This kind of argument should have value raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) if _value_number > 1 or _list_flag or current_type_str == 'list': current_arg.update({'nargs': '+'}) elif _value_number == 0: if _clear_flag: # if we have action=clear, we use argument's default # value None for argument _values_specs.pop() else: # We assume non value argument as bool one current_arg.update({'action': 'store_true'}) def parse_args_to_dict(values_specs): '''It is used to analyze the extra command options to command. Besides known options and arguments, our commands also support user to put more options to the end of command line. For example, list_nets -- --tag x y --key1 value1, where '-- --tag x y --key1 value1' is extra options to our list_nets. This feature can support V1.0 API's fields selection and filters. For example, to list networks which has name 'test4', we can have list_nets -- --name=test4. value spec is: --key type=int|bool|... value. Type is one of Python built-in types. By default, type is string. The key without value is a bool option. Key with two values will be a list option. ''' # values_specs for example: '-- --tag x y --key1 type=int value1' # -- is a pseudo argument values_specs_copy = values_specs[:] if values_specs_copy and values_specs_copy[0] == '--': del values_specs_copy[0] # converted ArgumentParser arguments for each of the options _options = {} # the argument part for current option in _options current_arg = None # the string after remove meta info in values_specs # for example, '--tag x y --key1 value1' _values_specs = [] # record the count of values for an option # for example: for '--tag x y', it is 2, while for '--key1 value1', it is 1 _value_number = 0 # list=true _list_flag = False # action=clear _clear_flag = False # the current item in values_specs current_item = None # the str after 'type=' current_type_str = None for _item in values_specs_copy: if _item.startswith('--'): # Deal with previous argument if any _process_previous_argument( current_arg, _value_number, current_type_str, _list_flag, _values_specs, _clear_flag, values_specs) # Init variables for current argument current_item = _item _list_flag = False _clear_flag = False current_type_str = None if "=" in _item: _value_number = 1 _item = _item.split('=')[0] else: _value_number = 0 if _item in _options: raise exceptions.CommandError( _("Duplicated options %s") % ' '.join(values_specs)) else: _options.update({_item: {}}) current_arg = _options[_item] _item = current_item elif _item.startswith('type='): if current_arg is None: raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) if 'type' not in current_arg: current_type_str = _item.split('=', 2)[1] current_arg.update({'type': eval(current_type_str)}) if current_type_str == 'bool': current_arg.update({'type': utils.str2bool}) elif current_type_str == 'dict': current_arg.update({'type': utils.str2dict}) continue elif _item == 'list=true': _list_flag = True continue elif _item == 'action=clear': _clear_flag = True continue if not _item.startswith('--'): # 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)): raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) _value_number += 1 _values_specs.append(_item) # Deal with last one argument _process_previous_argument( current_arg, _value_number, current_type_str, _list_flag, _values_specs, _clear_flag, values_specs) # populate the parser with arguments _parser = argparse.ArgumentParser(add_help=False) for opt, optspec in _options.items(): _parser.add_argument(opt, **optspec) _args = _parser.parse_args(_values_specs) result_dict = {} for opt in _options.keys(): _opt = opt.split('--', 2)[1] _opt = _opt.replace('-', '_') _value = getattr(_args, _opt) result_dict.update({_opt: _value}) return result_dict def _merge_args(qCmd, parsed_args, _extra_values, value_specs): """Merge arguments from _extra_values into parsed_args. If an argument value are provided in both and it is a list, the values in _extra_values will be merged into parsed_args. @param parsed_args: the parsed args from known options @param _extra_values: the other parsed arguments in unknown parts @param values_specs: the unparsed unknown parts """ temp_values = _extra_values.copy() for key, value in temp_values.items(): 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]))): arg_value.extend(value) _extra_values.pop(key) def update_dict(obj, dict, attributes): """Update dict with fields from obj.attributes :param obj: the object updated into dict :param dict: the result dictionary :param attributes: a list of attributes belonging to obj """ for attribute in attributes: if hasattr(obj, attribute) and getattr(obj, attribute) is not None: dict[attribute] = getattr(obj, attribute) class TableFormater(table.TableFormatter): """This class is used to keep consistency with prettytable 0.6. https://bugs.launchpad.net/python-tackerclient/+bug/1165962 """ def emit_list(self, column_names, data, stdout, parsed_args): if column_names: super(TableFormater, self).emit_list(column_names, data, stdout, parsed_args) else: stdout.write('\n') # command.OpenStackCommand is abstract class so that metaclass of # subclass must be subclass of metaclass of all its base. # otherwise metaclass conflict exception is raised. class TackerCommandMeta(abc.ABCMeta): def __new__(cls, name, bases, cls_dict): if 'log' not in cls_dict: cls_dict['log'] = logging.getLogger( cls_dict['__module__'] + '.' + name) return super(TackerCommandMeta, cls).__new__(cls, name, bases, cls_dict) @six.add_metaclass(TackerCommandMeta) class TackerCommand(command.OpenStackCommand): api = 'nfv-orchestration' values_specs = [] json_indent = None def __init__(self, app, app_args): super(TackerCommand, self).__init__(app, app_args) # 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() def get_client(self): return self.app.client_manager.tacker def get_parser(self, prog_name): parser = super(TackerCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', help=_('The xml or json request format'), default='json', choices=['json', 'xml', ], ) parser.add_argument( '--request_format', choices=['json', 'xml', ], help=argparse.SUPPRESS) return parser def format_output_data(self, data): # Modify data to make it more readable if self.resource in data: for k, v in data[self.resource].items(): if isinstance(v, list): value = '\n'.join(jsonutils.dumps( i, indent=self.json_indent) if isinstance(i, dict) else str(i) for i in v) data[self.resource][k] = value elif isinstance(v, dict): value = jsonutils.dumps(v, indent=self.json_indent) data[self.resource][k] = value elif v is None: data[self.resource][k] = '' def add_known_arguments(self, parser): pass def args2body(self, parsed_args): return {} class CreateCommand(TackerCommand, show.ShowOne): """Create a resource for a given tenant """ api = 'nfv-orchestration' resource = None log = None remove_output_fields = [] def get_parser(self, prog_name): parser = super(CreateCommand, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='TENANT_ID', help=_('The owner tenant ID'), ) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) self.add_known_arguments(parser) return parser def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) tacker_client = self.get_client() tacker_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) body = self.args2body(parsed_args) body[self.resource].update(_extra_values) obj_creator = getattr(tacker_client, "create_%s" % self.resource) data = obj_creator(body) self.format_output_data(data) # {u'network': {u'id': u'e9424a76-6db4-4c93-97b6-ec311cd51f19'}} info = self.resource in data and data[self.resource] or None if info: print(_('Created a new %s:') % self.resource, file=self.app.stdout) for f in self.remove_output_fields: if f in info: info.pop(f) else: info = {'': ''} return zip(*sorted(info.items())) class UpdateCommand(TackerCommand): """Update resource's information.""" api = 'nfv-orchestration' resource = None log = None allow_names = True def get_parser(self, prog_name): parser = super(UpdateCommand, self).get_parser(prog_name) parser.add_argument( 'id', metavar=self.resource.upper(), help=_('ID or name of %s to update') % self.resource) self.add_known_arguments(parser) return parser def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) tacker_client = self.get_client() tacker_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) body = self.args2body(parsed_args) if self.resource in body: body[self.resource].update(_extra_values) else: body[self.resource] = _extra_values if not body[self.resource]: raise exceptions.CommandError( _("Must specify new values to update %s") % self.resource) if self.allow_names: _id = find_resourceid_by_name_or_id( tacker_client, self.resource, parsed_args.id) else: _id = find_resourceid_by_id( tacker_client, self.resource, parsed_args.id) obj_updator = getattr(tacker_client, "update_%s" % self.resource) obj_updator(_id, body) print((_('Updated %(resource)s: %(id)s') % {'id': parsed_args.id, 'resource': self.resource}), file=self.app.stdout) return class DeleteCommand(TackerCommand): """Delete given resource(s) """ api = 'nfv-orchestration' resource = None log = None allow_names = True deleted_msg = {} def get_parser(self, prog_name): parser = super(DeleteCommand, self).get_parser(prog_name) if self.allow_names: help_str = _('IDs or names of %s to delete') else: help_str = _('IDs of %s to delete') parser.add_argument( 'ids', nargs='+', metavar=self.resource.upper(), help=help_str % self.resource) return parser def run(self, parsed_args): failure = False deleted_ids = [] failed_items = {} tacker_client = self.get_client() tacker_client.format = parsed_args.request_format obj_deleter = getattr(tacker_client, "delete_%s" % self.resource) for resource_id in parsed_args.ids: try: if self.allow_names: _id = find_resourceid_by_name_or_id( tacker_client, self.resource, resource_id) else: _id = resource_id obj_deleter(_id) deleted_ids.append(resource_id) except Exception as e: failure = True failed_items[resource_id] = e if failure: msg = '' if deleted_ids: status_msg = self.deleted_msg.get(self.resource, 'deleted') msg = (_('Successfully %(status_msg)s %(resource)s(s):' ' %(deleted_list)s') % {'status_msg': status_msg, 'deleted_list': ', '.join(deleted_ids), 'resource': self.resource}) err_msg = _("\n\nUnable to delete the below" " %s(s):") % self.resource for failed_id, error in failed_items.iteritems(): err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s') % {'failed_id': failed_id, 'error': error}) msg += err_msg raise exceptions.CommandError(msg) else: print((_('All specified %(resource)s(s) %(msg)s successfully') % {'msg': self.deleted_msg.get(self.resource, 'deleted'), 'resource': self.resource})) return class ListCommand(TackerCommand, lister.Lister): """List resources that belong to a given tenant """ api = 'nfv-orchestration' resource = None log = None _formatters = {} list_columns = [] unknown_parts_flag = True pagination_support = False sorting_support = False def get_parser(self, prog_name): parser = super(ListCommand, self).get_parser(prog_name) add_show_list_common_argument(parser) if self.pagination_support: add_pagination_argument(parser) if self.sorting_support: add_sorting_argument(parser) return parser def args2search_opts(self, parsed_args): search_opts = {} fields = parsed_args.fields if parsed_args.fields: search_opts.update({'fields': fields}) if parsed_args.show_details: search_opts.update({'verbose': 'True'}) return search_opts def call_server(self, tacker_client, search_opts, parsed_args): resource_plural = _get_resource_plural(self.resource, tacker_client) obj_lister = getattr(tacker_client, "list_%s" % resource_plural) data = obj_lister(**search_opts) return data def retrieve_list(self, parsed_args): """Retrieve a list of resources from Tacker server""" tacker_client = self.get_client() tacker_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) search_opts = self.args2search_opts(parsed_args) search_opts.update(_extra_values) if self.pagination_support: page_size = parsed_args.page_size if page_size: search_opts.update({'limit': page_size}) if self.sorting_support: keys = parsed_args.sort_key if keys: search_opts.update({'sort_key': keys}) dirs = parsed_args.sort_dir len_diff = len(keys) - len(dirs) if len_diff > 0: dirs += ['asc'] * len_diff elif len_diff < 0: dirs = dirs[:len(keys)] if dirs: search_opts.update({'sort_dir': dirs}) data = self.call_server(tacker_client, search_opts, parsed_args) collection = _get_resource_plural(self.resource, tacker_client) return data.get(collection, []) def extend_list(self, data, parsed_args): """Update a retrieved list. This method provides a way to modify a original list returned from the tacker server. For example, you can add subnet cidr information to a list network. """ pass def setup_columns(self, info, parsed_args): _columns = len(info) > 0 and sorted(info[0].keys()) or [] if not _columns: # clean the parsed_args.columns so that cliff will not break parsed_args.columns = [] elif parsed_args.columns: _columns = [x for x in parsed_args.columns if x in _columns] elif self.list_columns: # if no -c(s) by user and list_columns, we use columns in # both list_columns and returned resource. # Also Keep their order the same as in list_columns _columns = [x for x in self.list_columns if x in _columns] return (_columns, (utils.get_item_properties( s, _columns, formatters=self._formatters, ) for s in info), ) def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) data = self.retrieve_list(parsed_args) self.extend_list(data, parsed_args) return self.setup_columns(data, parsed_args) class ShowCommand(TackerCommand, show.ShowOne): """Show information of a given resource """ api = 'nfv-orchestration' resource = None log = None allow_names = True def get_id(self): if self.resource: return self.resource.upper() def get_parser(self, prog_name): parser = super(ShowCommand, self).get_parser(prog_name) add_show_list_common_argument(parser) if self.allow_names: help_str = _('ID or name of %s to look up') else: help_str = _('ID of %s to look up') parser.add_argument( 'id', metavar=self.get_id(), help=help_str % self.resource) return parser def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) tacker_client = self.get_client() tacker_client.format = parsed_args.request_format params = {} if parsed_args.show_details: params = {'verbose': 'True'} if parsed_args.fields: params = {'fields': parsed_args.fields} if self.allow_names: _id = find_resourceid_by_name_or_id(tacker_client, self.resource, parsed_args.id) else: _id = parsed_args.id obj_shower = getattr(tacker_client, "show_%s" % self.resource) data = obj_shower(_id, **params) self.format_output_data(data) resource = data[self.resource] if self.resource in data: return zip(*sorted(resource.items())) else: return None python-tackerclient-0.11.0/tackerclient/tacker/v1_0/extension.py0000666000175100017510000000176313234643056024677 0ustar zuulzuul00000000000000# Copyright 2012 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 tackerclient.tacker import v1_0 as cmd_base class ListExt(cmd_base.ListCommand): """List all extensions.""" resource = 'extension' list_columns = ['alias', 'name'] class ShowExt(cmd_base.ShowCommand): """Show information of a given resource.""" resource = "extension" allow_names = False def get_id(self): return 'EXT-ALIAS' python-tackerclient-0.11.0/tackerclient/v1_0/0000775000175100017510000000000013234643340021023 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/v1_0/client.py0000666000175100017510000006023213234643056022664 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import logging import time import requests import six.moves.urllib.parse as urlparse from tackerclient import client from tackerclient.common import constants from tackerclient.common import exceptions 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): """Exception handler for API v1.0 client. This routine generates the appropriate Tacker exception according to the contents of the response body. :param status_code: HTTP error status code :param error_content: deserialized body of error response """ error_dict = None if isinstance(error_content, dict): error_dict = error_content.get('TackerError') # Find real error type bad_tacker_error_flag = False if error_dict: # If Tacker key is found, it will definitely contain # a 'message' and 'type' keys? try: error_type = error_dict['type'] error_message = error_dict['message'] if error_dict['detail']: error_message += "\n" + error_dict['detail'] except Exception: bad_tacker_error_flag = True if not bad_tacker_error_flag: # If corresponding exception is defined, use it. client_exc = getattr(exceptions, '%sClient' % error_type, None) # Otherwise look up per status-code client exception if not client_exc: client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code) if client_exc: raise client_exc(message=error_message, status_code=status_code) else: raise exceptions.TackerClientException( status_code=status_code, message=error_message) else: raise exceptions.TackerClientException(status_code=status_code, message=error_dict) else: message = None if isinstance(error_content, dict): message = error_content.get('message') if message: raise exceptions.TackerClientException(status_code=status_code, message=message) # If we end up here the exception was not a tacker error msg = "%s-%s" % (status_code, error_content) raise exceptions.TackerClientException(status_code=status_code, message=msg) class APIParamsCall(object): """A Decorator to support formating and tenant overriding and filters.""" def __init__(self, function): self.function = function def __get__(self, instance, owner): def with_params(*args, **kwargs): _format = instance.format if 'format' in kwargs: instance.format = kwargs['format'] ret = self.function(instance, *args, **kwargs) instance.format = _format return ret return with_params class ClientBase(object): """Client for the OpenStack Tacker v1.0 API. :param string username: Username for authentication. (optional) :param string user_id: User ID for authentication. (optional) :param string password: Password for authentication. (optional) :param string token: Token for authentication. (optional) :param string tenant_name: Tenant name. (optional) :param string tenant_id: Tenant id. (optional) :param string auth_strategy: 'keystone' by default, 'noauth' for no authentication against keystone. (optional) :param string auth_url: Keystone service endpoint for authorization. :param string service_type: Network service type to pull from the keystone catalog (e.g. 'network') (optional) :param string endpoint_type: Network service endpoint type to pull from the keystone catalog (e.g. 'publicURL', 'internalURL', or 'adminURL') (optional) :param string region_name: Name of a region to select when choosing an endpoint from the service catalog. :param string endpoint_url: A user-supplied endpoint URL for the tacker service. Lazy-authentication is possible for API service calls if endpoint is set at instantiation.(optional) :param integer timeout: Allows customization of the timeout for client http requests. (optional) :param bool insecure: SSL certificate validation. (optional) :param bool log_credentials: Allow for logging of passwords or not. Defaults to False. (optional) :param string ca_cert: SSL CA bundle file to use. (optional) :param integer retries: How many times idempotent (GET, PUT, DELETE) requests to Tacker server should be retried if they fail (default: 0). :param bool raise_errors: If True then exceptions caused by connection failure are propagated to the caller. (default: True) :param session: Keystone client auth session to use. (optional) :param auth: Keystone auth plugin to use. (optional) Example:: from tackerclient.v1_0 import client tacker = client.Client(username=USER, password=PASS, tenant_name=TENANT_NAME, auth_url=KEYSTONE_URL) nets = tacker.list_networks() ... """ # API has no way to report plurals, so we have to hard code them # This variable should be overridden by a child class. EXTED_PLURALS = {} def __init__(self, **kwargs): """Initialize a new client for the Tacker v1.0 API.""" super(ClientBase, self).__init__() self.retries = kwargs.pop('retries', 0) self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) self.version = '1.0' self.format = 'json' self.action_prefix = "/v%s" % (self.version) self.retry_interval = 1 def _handle_fault_response(self, status_code, response_body): # Create exception with HTTP status code and message _logger.debug("Error message: %s", response_body) # Add deserialized error message to exception arguments try: des_error_body = self.deserialize(response_body, status_code) except Exception: # If unable to deserialized body it is probably not a # Tacker error des_error_body = {'message': response_body} # Raise the appropriate exception exception_handler_v10(status_code, des_error_body) def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id action += ".%s" % self.format action = self.action_prefix + action if type(params) is dict and params: params = utils.safe_encode_dict(params) action += '?' + urlparse.urlencode(params, doseq=1) if body: body = self.serialize(body) resp, replybody = self.httpclient.do_request( action, method, body=body, content_type=self.content_type()) status_code = resp.status_code if status_code in (requests.codes.ok, requests.codes.created, requests.codes.accepted, requests.codes.no_content): return self.deserialize(replybody, status_code) else: if not replybody: replybody = resp.reason self._handle_fault_response(status_code, replybody) def get_auth_info(self): return self.httpclient.get_auth_info() def serialize(self, data): """Serializes a dictionary into either XML or JSON. A dictionary with a single key can be passed and it can contain any structure. """ if data is None: return None elif type(data) is dict: return serializer.Serializer( self.get_attr_metadata()).serialize(data, self.content_type()) else: raise Exception(_("Unable to serialize object of type = '%s'") % type(data)) def deserialize(self, data, status_code): """Deserializes an XML or JSON string into a dictionary.""" if status_code == 204: return data return serializer.Serializer(self.get_attr_metadata()).deserialize( data, self.content_type())['body'] def get_attr_metadata(self): if self.format == 'json': return {} old_request_format = self.format self.format = 'json' exts = self.list_extensions()['extensions'] self.format = old_request_format ns = dict([(ext['alias'], ext['namespace']) for ext in exts]) self.EXTED_PLURALS.update(constants.PLURALS) return {'plurals': self.EXTED_PLURALS, 'xmlns': constants.XML_NS_V10, constants.EXT_NS: ns} def content_type(self, _format=None): """Returns the mime-type for either 'xml' or 'json'. Defaults to the currently set format. """ _format = _format or self.format return "application/%s" % (_format) def retry_request(self, method, action, body=None, headers=None, params=None): """Call do_request with the default retry configuration. Only idempotent requests should retry failed connection attempts. :raises: ConnectionFailed if the maximum # of retries is exceeded """ max_attempts = self.retries + 1 for i in range(max_attempts): try: return self.do_request(method, action, body=body, headers=headers, params=params) except exceptions.ConnectionFailed: # Exception has already been logged by do_request() if i < self.retries: _logger.debug('Retrying connection to Tacker service') time.sleep(self.retry_interval) elif self.raise_errors: raise if self.retries: msg = (_("Failed to connect to Tacker server after %d attempts") % max_attempts) else: msg = _("Failed to connect Tacker server") raise exceptions.ConnectionFailed(reason=msg) def delete(self, action, body=None, headers=None, params=None): return self.retry_request("DELETE", action, body=body, headers=headers, params=params) def get(self, action, body=None, headers=None, params=None): return self.retry_request("GET", action, body=body, headers=headers, params=params) def post(self, action, body=None, headers=None, params=None): # Do not retry POST requests to avoid the orphan objects problem. return self.do_request("POST", action, body=body, headers=headers, params=params) def put(self, action, body=None, headers=None, params=None): return self.retry_request("PUT", action, body=body, headers=headers, params=params) def list(self, collection, path, retrieve_all=True, **params): if retrieve_all: res = [] for r in self._pagination(collection, path, **params): res.extend(r[collection]) return {collection: res} else: return self._pagination(collection, path, **params) def _pagination(self, collection, path, **params): if params.get('page_reverse', False): linkrel = 'previous' else: linkrel = 'next' next = True while next: res = self.get(path, params=params) yield res next = False try: for link in res['%s_links' % collection]: if link['rel'] == linkrel: query_str = urlparse.urlparse(link['href']).query params = urlparse.parse_qs(query_str) next = True break except KeyError: break class Client(ClientBase): extensions_path = "/extensions" extension_path = "/extensions/%s" vnfds_path = '/vnfds' vnfd_path = '/vnfds/%s' vnfs_path = '/vnfs' vnf_path = '/vnfs/%s' vnf_scale_path = '/vnfs/%s/actions' vnf_resources_path = '/vnfs/%s/resources' vims_path = '/vims' vim_path = '/vims/%s' events_path = '/events' event_path = '/events/%s' vnffgds_path = '/vnffgds' vnffgd_path = '/vnffgds/%s' vnffgs_path = '/vnffgs' vnffg_path = '/vnffgs/%s' nfps_path = '/nfps' nfp_path = '/nfps/%s' sfcs_path = '/sfcs' sfc_path = '/sfcs/%s' fcs_path = '/classifiers' fc_path = '/classifiers/%s' nsds_path = '/nsds' nsd_path = '/nsds/%s' nss_path = '/nss' ns_path = '/nss/%s' # API has no way to report plurals, so we have to hard code them # EXTED_PLURALS = {} @APIParamsCall def list_extensions(self, **_params): """Fetch a list of all exts on server side.""" return self.get(self.extensions_path, params=_params) @APIParamsCall def show_extension(self, ext_alias, **_params): """Fetch a list of all exts on server side.""" return self.get(self.extension_path % ext_alias, params=_params) _VNFD = "vnfd" _NSD = "nsd" @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 vnfd.get('description'): if len(vnfd['description']) > DEFAULT_DESC_LENGTH: vnfd['description'] = \ vnfd['description'][:DEFAULT_DESC_LENGTH] vnfd['description'] += '...' return vnfds_dict @APIParamsCall def show_vnfd(self, vnfd, **_params): return self.get(self.vnfd_path % vnfd, params=_params) @APIParamsCall def create_vnfd(self, body): body[self._VNFD]['service_types'] = [{'service_type': 'vnfd'}] return self.post(self.vnfds_path, body) @APIParamsCall def delete_vnfd(self, vnfd): return self.delete(self.vnfd_path % vnfd) @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 @APIParamsCall def show_vnf(self, vnf, **_params): return self.get(self.vnf_path % vnf, params=_params) @APIParamsCall def create_vnf(self, body): return self.post(self.vnfs_path, body=body) @APIParamsCall def delete_vnf(self, vnf): return self.delete(self.vnf_path % vnf) @APIParamsCall def update_vnf(self, vnf, body): return self.put(self.vnf_path % vnf, body=body) @APIParamsCall def list_vnf_resources(self, vnf, retrieve_all=True, **_params): return self.list('resources', self.vnf_resources_path % vnf, retrieve_all, **_params) @APIParamsCall def scale_vnf(self, vnf, body=None): return self.post(self.vnf_scale_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): 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): 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) @APIParamsCall def list_events(self, retrieve_all=True, **_params): events = self.list('events', self.events_path, retrieve_all, **_params) return events @APIParamsCall def list_vnf_events(self, retrieve_all=True, **_params): _params['resource_type'] = 'vnf' events = self.list('events', self.events_path, retrieve_all, **_params) vnf_events = {} vnf_events['vnf_events'] = events['events'] return vnf_events @APIParamsCall def list_vnfd_events(self, retrieve_all=True, **_params): _params['resource_type'] = 'vnfd' events = self.list('events', self.events_path, retrieve_all, **_params) vnfd_events = {} vnfd_events['vnfd_events'] = events['events'] return vnfd_events @APIParamsCall def list_vim_events(self, retrieve_all=True, **_params): _params['resource_type'] = 'vim' events = self.list('events', self.events_path, retrieve_all, **_params) vim_events = {} vim_events['vim_events'] = events['events'] return vim_events @APIParamsCall def show_event(self, event_id, **_params): return self.get(self.event_path % event_id, params=_params) _VNFFGD = "vnffgd" @APIParamsCall def create_vnffgd(self, body): return self.post(self.vnffgds_path, body) @APIParamsCall def list_vnffgds(self, retrieve_all=True, **_params): vnffgds_dict = self.list(self._VNFFGD + 's', self.vnffgds_path, retrieve_all, **_params) for vnffgd in vnffgds_dict['vnffgds']: if 'description' in vnffgd.keys() and \ len(vnffgd['description']) > DEFAULT_DESC_LENGTH: vnffgd['description'] = vnffgd['description'][ :DEFAULT_DESC_LENGTH] vnffgd['description'] += '...' return vnffgds_dict @APIParamsCall def show_vnffgd(self, vnffgd, **_params): return self.get(self.vnffgd_path % vnffgd, params=_params) @APIParamsCall def delete_vnffgd(self, vnffgd): return self.delete(self.vnffgd_path % vnffgd) @APIParamsCall def list_vnffgs(self, retrieve_all=True, **_params): vnffgs = self.list('vnffgs', self.vnffgs_path, retrieve_all, **_params) for vnffg in vnffgs['vnffgs']: error_reason = vnffg.get('error_reason', None) if error_reason and \ len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: vnffg['error_reason'] = error_reason[ :DEFAULT_ERROR_REASON_LENGTH] vnffg['error_reason'] += '...' return vnffgs @APIParamsCall def show_vnffg(self, vnffg, **_params): return self.get(self.vnffg_path % vnffg, params=_params) @APIParamsCall def create_vnffg(self, body): return self.post(self.vnffgs_path, body=body) @APIParamsCall def delete_vnffg(self, vnffg): return self.delete(self.vnffg_path % vnffg) @APIParamsCall def update_vnffg(self, vnffg, body): return self.put(self.vnffg_path % vnffg, body=body) @APIParamsCall def list_sfcs(self, retrieve_all=True, **_params): sfcs = self.list('sfcs', self.sfcs_path, retrieve_all, **_params) for chain in sfcs['sfcs']: error_reason = chain.get('error_reason', None) if error_reason and \ len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: chain['error_reason'] = error_reason[ :DEFAULT_ERROR_REASON_LENGTH] chain['error_reason'] += '...' return sfcs @APIParamsCall def show_sfc(self, chain, **_params): return self.get(self.sfc_path % chain, params=_params) @APIParamsCall def list_nfps(self, retrieve_all=True, **_params): nfps = self.list('nfps', self.nfps_path, retrieve_all, **_params) for nfp in nfps['nfps']: error_reason = nfp.get('error_reason', None) if error_reason and \ len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: nfp['error_reason'] = error_reason[ :DEFAULT_ERROR_REASON_LENGTH] nfp['error_reason'] += '...' return nfps @APIParamsCall def show_nfp(self, nfp, **_params): return self.get(self.nfp_path % nfp, params=_params) @APIParamsCall def list_classifiers(self, retrieve_all=True, **_params): classifiers = self.list('classifiers', self.fcs_path, retrieve_all, **_params) for classifier in classifiers['classifiers']: error_reason = classifier.get('error_reason', None) if error_reason and \ len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: classifier['error_reason'] = error_reason[ :DEFAULT_ERROR_REASON_LENGTH] classifier['error_reason'] += '...' return classifiers @APIParamsCall def show_classifier(self, classifier, **_params): return self.get(self.fc_path % classifier, params=_params) @APIParamsCall def list_nsds(self, retrieve_all=True, **_params): nsds_dict = self.list(self._NSD + 's', self.nsds_path, retrieve_all, **_params) for nsd in nsds_dict['nsds']: if 'description' in nsd.keys() and \ len(nsd['description']) > DEFAULT_DESC_LENGTH: nsd['description'] = nsd['description'][:DEFAULT_DESC_LENGTH] nsd['description'] += '...' return nsds_dict @APIParamsCall def show_nsd(self, nsd, **_params): return self.get(self.nsd_path % nsd, params=_params) @APIParamsCall def create_nsd(self, body): return self.post(self.nsds_path, body) @APIParamsCall def delete_nsd(self, nsd): return self.delete(self.nsd_path % nsd) @APIParamsCall def list_nss(self, retrieve_all=True, **_params): nss = self.list('nss', self.nss_path, retrieve_all, **_params) for ns in nss['nss']: error_reason = ns.get('error_reason', None) if error_reason and \ len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: ns['error_reason'] = error_reason[ :DEFAULT_ERROR_REASON_LENGTH] ns['error_reason'] += '...' return nss @APIParamsCall def show_ns(self, ns, **_params): return self.get(self.ns_path % ns, params=_params) @APIParamsCall def create_ns(self, body): return self.post(self.nss_path, body=body) @APIParamsCall def delete_ns(self, ns): return self.delete(self.ns_path % ns) python-tackerclient-0.11.0/tackerclient/v1_0/__init__.py0000666000175100017510000000000013234643056023130 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/0000775000175100017510000000000013234643340021042 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/v1/0000775000175100017510000000000013234643340021370 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/v1/nfvo/0000775000175100017510000000000013234643340022340 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/v1/nfvo/vim.py0000666000175100017510000002276213234643056023524 0ustar zuulzuul00000000000000# 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 osc_lib.command import command from osc_lib import utils from oslo_utils import strutils from tackerclient.common import exceptions from tackerclient.i18n import _ from tackerclient.osc import sdk_utils from tackerclient.osc import utils as tacker_osc_utils from tackerclient.tacker import v1_0 as tackerV10 from tackerclient.tacker.v1_0.nfvo import vim_utils _attr_map = ( ('id', 'ID', tacker_osc_utils.LIST_BOTH), ('name', 'Name', tacker_osc_utils.LIST_BOTH), ('tenant_id', 'Tenant_id', tacker_osc_utils.LIST_BOTH), ('type', 'Type', tacker_osc_utils.LIST_BOTH), ('is_default', 'Is Default', tacker_osc_utils.LIST_BOTH), ('placement_attr', 'Placement attribution', tacker_osc_utils.LIST_LONG_ONLY), ('status', 'Status', tacker_osc_utils.LIST_BOTH), ) _VIM = 'vim' class ListVIM(command.Lister): _description = _("List VIMs that belong to a given tenant.") def get_parser(self, prog_name): parser = super(ListVIM, self).get_parser(prog_name) parser.add_argument( '--long', action='store_true', help=_("List additional fields in output") ) return parser def take_action(self, parsed_args): client = self.app.client_manager.tackerclient data = client.list_vims() headers, columns = tacker_osc_utils.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, ) for s in data[_VIM + 's'])) class ShowVIM(command.ShowOne): _description = _("Display VIM details") def get_parser(self, prog_name): parser = super(ShowVIM, self).get_parser(prog_name) parser.add_argument( _VIM, metavar="", help=_("VIM to display (name or ID)") ) return parser def take_action(self, parsed_args): client = self.app.client_manager.tackerclient obj_id = tackerV10.find_resourceid_by_name_or_id( client, _VIM, parsed_args.vim) obj = client.show_vim(obj_id) display_columns, columns = _get_columns(obj[_VIM]) data = utils.get_item_properties( sdk_utils.DictModel(obj[_VIM]), columns, formatters=_formatters) return (display_columns, data) class CreateVIM(command.ShowOne): _description = _("Register a new VIM") def get_parser(self, prog_name): parser = super(CreateVIM, self).get_parser(prog_name) parser.add_argument( 'name', metavar='NAME', help=_('Set a name for the VIM')) parser.add_argument( '--config-file', required=True, help=_('YAML file with VIM configuration parameters')) 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')) return parser def args2body(self, parsed_args): body = {_VIM: {}} if parsed_args.config_file: with open(parsed_args.config_file) as f: vim_config = f.read() try: config_param = yaml.load(vim_config, Loader=yaml.SafeLoader) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) vim_obj = body[_VIM] 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'] = vim_utils.validate_auth_url(auth_url).geturl() vim_utils.args2body_vim(config_param, vim_obj) tackerV10.update_dict(parsed_args, body[_VIM], ['tenant_id', 'name', 'description', 'is_default']) return body def take_action(self, parsed_args): client = self.app.client_manager.tackerclient vim = client.create_vim(self.args2body(parsed_args)) display_columns, columns = _get_columns(vim[_VIM]) data = utils.get_item_properties( sdk_utils.DictModel(vim[_VIM]), columns, formatters=_formatters) return (display_columns, data) class DeleteVIM(command.Command): _description = _("Delete VIM(s).") def get_parser(self, prog_name): parser = super(DeleteVIM, self).get_parser(prog_name) parser.add_argument( _VIM, metavar="", nargs="+", help=_("VIM(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): client = self.app.client_manager.tackerclient failure = False deleted_ids = [] failed_items = {} for resource_id in parsed_args.vim: try: obj = tackerV10.find_resourceid_by_name_or_id( client, _VIM, resource_id) client.delete_vim(obj) deleted_ids.append(resource_id) except Exception as e: failure = True failed_items[resource_id] = e if failure: msg = '' if deleted_ids: msg = (_('Successfully deleted %(resource)s(s):' ' %(deleted_list)s') % {'deleted_list': ', '.join(deleted_ids), 'resource': _VIM}) err_msg = _("\n\nUnable to delete the below" " %s(s):") % _VIM for failed_id, error in failed_items.iteritems(): err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s') % {'failed_id': failed_id, 'error': error}) msg += err_msg raise exceptions.CommandError(msg) else: print((_('All specified %(resource)s(s) deleted successfully') % {'resource': _VIM})) return class UpdateVIM(command.ShowOne): _description = _("Update VIM.") def get_parser(self, prog_name): parser = super(UpdateVIM, self).get_parser(prog_name) parser.add_argument( 'id', metavar="VIM", help=_('ID or name of %s to update') % _VIM) parser.add_argument( '--config-file', required=False, help=_('YAML file with VIM configuration parameters')) parser.add_argument( '--name', help=_('New name for the VIM')) parser.add_argument( '--description', help=_('New description for the VIM')) parser.add_argument( '--is-default', type=strutils.bool_from_string, metavar='{True,False}', help=_('Indicate whether the VIM is used as default')) return parser def args2body(self, parsed_args): body = {_VIM: {}} config_param = None # 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() try: config_param = yaml.load(config_yaml) except yaml.YAMLError as e: raise exceptions.InvalidInput(e) vim_obj = body[_VIM] if config_param is not None: vim_utils.args2body_vim(config_param, vim_obj) tackerV10.update_dict(parsed_args, body[_VIM], ['tenant_id', 'name', 'description', 'is_default']) # type attribute is read-only, it can't be updated, so remove it # in update method body[_VIM].pop('type', None) return body def take_action(self, parsed_args): client = self.app.client_manager.tackerclient obj_id = tackerV10.find_resourceid_by_name_or_id( client, _VIM, parsed_args.id) vim = client.update_vim(obj_id, self.args2body(parsed_args)) display_columns, columns = _get_columns(vim[_VIM]) data = utils.get_item_properties( sdk_utils.DictModel(vim[_VIM]), columns, formatters=_formatters) return (display_columns, data) _formatters = { 'auth_cred': tacker_osc_utils.format_dict_with_indention, 'placement_attr': tacker_osc_utils.format_dict_with_indention, 'vim_project': tacker_osc_utils.format_dict_with_indention, } def _get_columns(item): column_map = { 'tenant_id': 'project_id', } return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) python-tackerclient-0.11.0/tackerclient/osc/v1/nfvo/__init__.py0000666000175100017510000000000013234643056024445 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/v1/__init__.py0000666000175100017510000000000013234643056023475 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/utils.py0000666000175100017510000001677513234643056022602 0ustar zuulzuul00000000000000# Copyright 2016 NEC Corporation # # 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. """This module should contain OSC plugin generic methods. Methods in this module are candidates adopted to osc-lib. Stuffs specific to tackerclient OSC plugin should not be added to this module. They should go to tackerclient.osc.v1.utils. """ import operator from keystoneclient import exceptions as identity_exc from keystoneclient.v3 import domains from keystoneclient.v3 import projects from osc_lib import utils from oslo_serialization import jsonutils from tackerclient.i18n import _ LIST_BOTH = 'both' LIST_SHORT_ONLY = 'short_only' LIST_LONG_ONLY = 'long_only' def format_dict_with_indention(data): """Return a formatted string of key value pairs :param data: a dict :rtype: a string formatted to key='value' """ if data is None: return None return jsonutils.dumps(data, indent=4) def get_column_definitions(attr_map, long_listing): """Return table headers and column names for a listing table. :param attr_map: a list of table entry definitions. Each entry should be a tuple consisting of (API attribute name, header name, listing mode). For example: (('id', 'ID', LIST_BOTH), ('name', 'Name', LIST_BOTH), ('tenant_id', 'Project', LIST_LONG_ONLY)) The third field of each tuple must be one of LIST_BOTH, LIST_LONG_ONLY (a corresponding column is shown only in a long mode), or LIST_SHORT_ONLY (a corresponding column is shown only in a short mode). :param long_listing: A boolean value which indicates a long listing or not. In most cases, parsed_args.long is passed to this argument. :return: A tuple of a list of table headers and a list of column names. """ if long_listing: headers = [hdr for col, hdr, listing_mode in attr_map if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] columns = [col for col, hdr, listing_mode in attr_map if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] else: headers = [hdr for col, hdr, listing_mode in attr_map if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] columns = [col for col, hdr, listing_mode in attr_map if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] return headers, columns def get_columns(item, attr_map=None): """Return pair of resource attributes and corresponding display names. Assume the following item and attr_map are passed. item: {'id': 'myid', 'name': 'myname', 'foo': 'bar', 'tenant_id': 'mytenan'} attr_map: (('id', 'ID', LIST_BOTH), ('name', 'Name', LIST_BOTH), ('tenant_id', 'Project', LIST_LONG_ONLY)) This method returns: (('id', 'name', 'tenant_id', 'foo'), # attributes ('ID', 'Name', 'Project', 'foo') # display names Both tuples of attributes and display names are sorted by display names in the alphabetical order. Attributes not found in a given attr_map are kept as-is. :param item: a dictionary which represents a resource. Keys of the dictionary are expected to be attributes of the resource. Values are not referred to by this method. :param attr_map: a list of mapping from attribute to display name. The same format is used as for get_column_definitions attr_map. :return: A pair of tuple of attributes and tuple of display names. """ attr_map = attr_map or tuple([]) _attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map) columns = [(column, _attr_map_dict.get(column, column)) for column in item.keys()] columns = sorted(columns, key=operator.itemgetter(1)) return (tuple(col[0] for col in columns), tuple(col[1] for col in columns)) # TODO(amotoki): Use osc-lib version once osc-lib provides this. def add_project_owner_option_to_parser(parser): """Register project and project domain options. :param parser: argparse.Argument parser object. """ parser.add_argument( '--project', metavar='', help=_("Owner's project (name or ID)") ) # Borrowed from openstackclient.identity.common # as it is not exposed officially. parser.add_argument( '--project-domain', metavar='', help=_('Domain the project belongs to (name or ID). ' 'This can be used in case collisions between project names ' 'exist.'), ) # The following methods are borrowed from openstackclient.identity.common # as it is not exposed officially. # TODO(amotoki): Use osc-lib version once osc-lib provides this. def find_domain(identity_client, name_or_id): return _find_identity_resource(identity_client.domains, name_or_id, domains.Domain) def find_project(identity_client, name_or_id, domain_name_or_id=None): domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) if not domain_id: return _find_identity_resource(identity_client.projects, name_or_id, projects.Project) else: return _find_identity_resource(identity_client.projects, name_or_id, projects.Project, domain_id=domain_id) def _get_domain_id_if_requested(identity_client, domain_name_or_id): if not domain_name_or_id: return None domain = find_domain(identity_client, domain_name_or_id) return domain.id def _find_identity_resource(identity_client_manager, name_or_id, resource_type, **kwargs): """Find a specific identity resource. Using keystoneclient's manager, attempt to find a specific resource by its name or ID. If Forbidden to find the resource (a common case if the user does not have permission), then return the resource by creating a local instance of keystoneclient's Resource. The parameter identity_client_manager is a keystoneclient manager, for example: keystoneclient.v3.users or keystoneclient.v3.projects. The parameter resource_type is a keystoneclient resource, for example: keystoneclient.v3.users.User or keystoneclient.v3.projects.Project. :param identity_client_manager: the manager that contains the resource :type identity_client_manager: `keystoneclient.base.CrudManager` :param name_or_id: the resources's name or ID :type name_or_id: string :param resource_type: class that represents the resource type :type resource_type: `keystoneclient.base.Resource` :returns: the resource in question :rtype: `keystoneclient.base.Resource` """ try: identity_resource = utils.find_resource(identity_client_manager, name_or_id, **kwargs) if identity_resource is not None: return identity_resource except identity_exc.Forbidden: pass return resource_type(None, {'id': name_or_id, 'name': name_or_id}) # The above are borrowed from openstackclient.identity.common. python-tackerclient-0.11.0/tackerclient/osc/__init__.py0000666000175100017510000000000013234643056023147 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/osc/sdk_utils.py0000666000175100017510000000726013234643056023430 0ustar zuulzuul00000000000000# 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. def get_osc_show_columns_for_sdk_resource( sdk_resource, osc_column_map, invisible_columns=None ): """Get and filter the display and attribute columns for an SDK resource. Common utility function for preparing the output of an OSC show command. Some of the columns may need to get renamed, others made invisible. :param sdk_resource: An SDK resource :param osc_column_map: A hash of mappings for display column names :param invisible_columns: A list of invisible column names :returns: Two tuples containing the names of the display and attribute columns """ if getattr(sdk_resource, 'allow_get', None) is not None: resource_dict = sdk_resource.to_dict( body=True, headers=False, ignore_none=False) else: resource_dict = sdk_resource # Build the OSC column names to display for the SDK resource. attr_map = {} display_columns = list(resource_dict.keys()) invisible_columns = [] if invisible_columns is None else invisible_columns for col_name in invisible_columns: if col_name in display_columns: display_columns.remove(col_name) for sdk_attr, osc_attr in osc_column_map.items(): if sdk_attr in display_columns: attr_map[osc_attr] = sdk_attr display_columns.remove(sdk_attr) if osc_attr not in display_columns: display_columns.append(osc_attr) sorted_display_columns = sorted(display_columns) # Build the SDK attribute names for the OSC column names. attr_columns = [] for column in sorted_display_columns: new_column = attr_map[column] if column in attr_map else column attr_columns.append(new_column) return tuple(sorted_display_columns), tuple(attr_columns) class DictModel(dict): """Convert dict into an object that provides attribute access to values.""" def __init__(self, *args, **kwargs): """Convert dict values to DictModel values.""" super(DictModel, self).__init__(*args, **kwargs) def needs_upgrade(item): return isinstance(item, dict) and not isinstance(item, DictModel) def upgrade(item): """Upgrade item if it needs to be upgraded.""" if needs_upgrade(item): return DictModel(item) else: return item for key, value in self.items(): if isinstance(value, (list, tuple)): # Keep the same type but convert dicts to DictModels self[key] = type(value)( (upgrade(item) for item in value) ) elif needs_upgrade(value): # Change dict instance values to DictModel instance values self[key] = DictModel(value) def __getattr__(self, name): try: return self[name] except KeyError as e: raise AttributeError(e) def __setattr__(self, name, value): self[name] = value def __delattr__(self, name): del self[name] def __str__(self): pairs = ['%s=%s' % (k, v) for k, v in self.items()] return ', '.join(sorted(pairs)) python-tackerclient-0.11.0/tackerclient/osc/plugin.py0000666000175100017510000000341313234643056022721 0ustar zuulzuul00000000000000# 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. # """OpenStackClient plugin for nfv-orchestration service.""" import logging from osc_lib import utils LOG = logging.getLogger(__name__) # Required by the OSC plugin interface DEFAULT_TACKER_API_VERSION = '1' API_NAME = 'tackerclient' API_VERSION_OPTION = 'os_tacker_api_version' API_VERSIONS = { '1': 'tackerclient.v1_0.client.Client', } def make_client(instance): """Returns a client to the ClientManager.""" tacker_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS) LOG.debug('Instantiating tacker client: %s', tacker_client) kwargs = {'service_type': 'nfv-orchestration', 'region_name': instance._region_name, 'endpoint_type': instance._interface, 'interface': instance._interface, 'session': instance.session } client = tacker_client(**kwargs) return client def build_option_parser(parser): """Hook to add global options.""" parser.add_argument( '--os-tacker-api-version', metavar='', default=utils.env( 'OS_TACKER_API_VERSION', default=DEFAULT_TACKER_API_VERSION)) return parser python-tackerclient-0.11.0/tackerclient/tests/0000775000175100017510000000000013234643340021420 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/0000775000175100017510000000000013234643340022377 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/test_shell.py0000666000175100017510000001574413234643056025140 0ustar zuulzuul00000000000000# Copyright (C) 2013 Yahoo! 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 argparse import logging import os import re import six import sys import fixtures from keystoneclient import session import mock import testtools from testtools import matchers from tackerclient.common import clientmanager from tackerclient import shell as openstack_shell DEFAULT_USERNAME = 'username' DEFAULT_PASSWORD = 'password' 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_API_VERSION = '1.0' class ShellTest(testtools.TestCase): FAKE_ENV = { 'OS_USERNAME': DEFAULT_USERNAME, 'OS_PASSWORD': DEFAULT_PASSWORD, 'OS_TENANT_ID': DEFAULT_TENANT_ID, 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, 'OS_AUTH_URL': DEFAULT_AUTH_URL} # Patch os.environ to avoid required auth info. def setUp(self): super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture( fixtures.EnvironmentVariable( var, self.FAKE_ENV[var])) def shell(self, argstr, check=False): orig = (sys.stdout, sys.stderr) clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: sys.stdout = six.StringIO() sys.stderr = six.StringIO() _shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) _shell.run(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: stdout = sys.stdout.getvalue() stderr = sys.stderr.getvalue() sys.stdout.close() sys.stderr.close() sys.stdout, sys.stderr = orig os.environ = _old_env return stdout, stderr def test_run_unknown_command(self): self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) stdout, stderr = self.shell('fake', check=True) self.assertFalse(stdout) self.assertEqual("Unknown command ['fake']", stderr.strip()) def test_help(self): required = 'usage:' help_text, stderr = self.shell('help') self.assertThat( help_text, matchers.MatchesRegex(required)) self.assertFalse(stderr) def test_help_on_subcommand(self): required = [ '.*?^usage: .* vnfd-list'] stdout, stderr = self.shell('help vnfd-list') for r in required: self.assertThat( stdout, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) self.assertFalse(stderr) def test_help_command(self): required = 'usage:' help_text, stderr = self.shell('help vnfd-create') self.assertThat( help_text, matchers.MatchesRegex(required)) self.assertFalse(stderr) def test_unknown_auth_strategy(self): self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) stdout, stderr = self.shell('--os-auth-strategy fake ' 'vnfd-list') self.assertFalse(stdout) def test_auth(self): with mock.patch.object(openstack_shell.TackerShell, 'run_subcommand'), \ mock.patch.object(session, 'Session'), \ mock.patch.object(clientmanager, 'ClientManager') as mock_cmgr: shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) shell.options = mock.Mock() auth_session = shell._get_keystone_session() 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') shell.authenticate_user() shell.run(cmdline.split()) mock_cmgr.assert_called_with( raise_errors=False, retries=0, timeout=None, 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', endpoint_type='publicURL', insecure=False, ca_cert=None, log_credentials=True, session=auth_session, auth=auth_session.auth) def test_build_option_parser(self): tacker_shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) result = tacker_shell.build_option_parser('descr', DEFAULT_API_VERSION) self.assertIsInstance(result, argparse.ArgumentParser) @mock.patch.object(openstack_shell.TackerShell, 'run') def test_main_with_unicode(self, mock_run): mock_run.return_value = 0 unicode_text = u'\u7f51\u7edc' argv = ['net-list', unicode_text, unicode_text.encode('utf-8')] ret = openstack_shell.main(argv=argv) mock_run.assert_called_once_with([u'net-list', unicode_text, unicode_text]) self.assertEqual(0, ret) def test_endpoint_option(self): shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) parser = shell.build_option_parser('descr', DEFAULT_API_VERSION) # Neither $OS_ENDPOINT_TYPE nor --endpoint-type namespace = parser.parse_args([]) self.assertEqual('publicURL', namespace.endpoint_type) # --endpoint-type but not $OS_ENDPOINT_TYPE namespace = parser.parse_args(['--endpoint-type=admin']) self.assertEqual('admin', namespace.endpoint_type) def test_endpoint_environment_variable(self): fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE", "public") self.useFixture(fixture) shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) parser = shell.build_option_parser('descr', DEFAULT_API_VERSION) # $OS_ENDPOINT_TYPE but not --endpoint-type namespace = parser.parse_args([]) self.assertEqual("public", namespace.endpoint_type) # --endpoint-type and $OS_ENDPOINT_TYPE namespace = parser.parse_args(['--endpoint-type=admin']) self.assertEqual('admin', namespace.endpoint_type) python-tackerclient-0.11.0/tackerclient/tests/unit/test_http.py0000666000175100017510000000473713234643056025010 0ustar zuulzuul00000000000000# Copyright (C) 2013 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 mock import testtools from tackerclient.client import HTTPClient from tackerclient.common import exceptions from tackerclient.tests.unit.test_cli10 import MyResp AUTH_TOKEN = 'test_token' END_URL = 'test_url' METHOD = 'GET' URL = 'http://test.test:1234/v1.0/test' headers = {'User-Agent': 'python-tackerclient'} class TestHTTPClient(testtools.TestCase): def setUp(self): super(TestHTTPClient, self).setUp() self.addCleanup(mock.patch.stopall) self.http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) @mock.patch('tackerclient.client.HTTPClient.request') def test_request_error(self, mock_request): mock_request.side_effect = Exception('error msg') self.assertRaises( exceptions.ConnectionFailed, self.http._cs_request, URL, METHOD ) @mock.patch('tackerclient.client.HTTPClient.request') def test_request_success(self, mock_request): rv_should_be = MyResp(200), 'test content' mock_request.return_value = rv_should_be self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD)) @mock.patch('tackerclient.client.HTTPClient.request') def test_request_unauthorized(self, mock_request): mock_request.return_value = MyResp(401), 'unauthorized message' e = self.assertRaises(exceptions.Unauthorized, self.http._cs_request, URL, METHOD) self.assertEqual('unauthorized message', str(e)) mock_request.assert_called_with(URL, METHOD, headers=headers) @mock.patch('tackerclient.client.HTTPClient.request') def test_request_forbidden_is_returned_to_caller(self, mock_request): rv_should_be = MyResp(403), 'forbidden message' mock_request.return_value = rv_should_be self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD)) python-tackerclient-0.11.0/tackerclient/tests/unit/test_cli10.py0000666000175100017510000007750213234643056024741 0ustar zuulzuul00000000000000# Copyright 2012 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 urllib import contextlib import fixtures import mock import six import six.moves.urllib.parse as urlparse import sys import testtools from tackerclient.common import constants from tackerclient.common import exceptions from tackerclient import shell from tackerclient.tacker import v1_0 as tackerV1_0 from tackerclient.tacker.v1_0 import TackerCommand from tackerclient.tests.unit import test_utils from tackerclient.v1_0 import client API_VERSION = "1.0" FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' @contextlib.contextmanager def capture_std_streams(): fake_stdout, fake_stderr = six.StringIO(), six.StringIO() stdout, stderr = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = fake_stdout, fake_stderr yield fake_stdout, fake_stderr finally: sys.stdout, sys.stderr = stdout, stderr class FakeStdout(object): def __init__(self): self.content = [] def write(self, text): self.content.append(text) def make_string(self): result = '' for line in self.content: result = result + line return result class MyResp(object): def __init__(self, status_code, headers=None, reason=None): self.status_code = status_code self.headers = headers or {} self.reason = reason class MyApp(object): def __init__(self, _stdout): self.stdout = _stdout def end_url(path, query=None, format=FORMAT): _url_str = ENDURL + "/v" + API_VERSION + path + "." + format return query and _url_str + "?" + query or _url_str class MyUrlComparator(object): def __init__(self, lhs, client): self.lhs = lhs self.client = client def equals(self, rhs): lhsp = urlparse.urlparse(self.lhs) rhsp = urlparse.urlparse(rhs) lhs_qs = urlparse.parse_qsl(lhsp.query) rhs_qs = urlparse.parse_qsl(rhsp.query) return (lhsp.scheme == rhsp.scheme and lhsp.netloc == rhsp.netloc and lhsp.path == rhsp.path and len(lhs_qs) == len(rhs_qs) and set(lhs_qs) == set(rhs_qs)) def __str__(self): if self.client and self.client.format != FORMAT: lhs_parts = self.lhs.split("?", 1) if len(lhs_parts) == 2: lhs = ("%s.%s?%s" % (lhs_parts[0][:-4], self.client.format, lhs_parts[1])) else: lhs = ("%s.%s" % (lhs_parts[0][:-4], self.client.format)) return lhs return self.lhs def __repr__(self): return str(self) def __eq__(self, rhs): return self.equals(rhs) def __ne__(self, rhs): return not self.__eq__(rhs) class MyComparator(object): def __init__(self, lhs, client): self.lhs = lhs self.client = client def _com_dict(self, lhs, rhs): if len(lhs) != len(rhs): return False for key, value in lhs.items(): if key not in rhs: return False rhs_value = rhs[key] if not self._com(value, rhs_value): return False return True def _com_list(self, lhs, rhs): if len(lhs) != len(rhs): return False for lhs_value in lhs: if lhs_value not in rhs: return False return True def _com(self, lhs, rhs): if lhs is None: return rhs is None if isinstance(lhs, dict): if not isinstance(rhs, dict): return False return self._com_dict(lhs, rhs) if isinstance(lhs, list): if not isinstance(rhs, list): return False return self._com_list(lhs, rhs) if isinstance(lhs, tuple): if not isinstance(rhs, tuple): return False return self._com_list(lhs, rhs) return lhs == rhs def equals(self, rhs): if self.client: rhs = self.client.deserialize(rhs, 200) return self._com(self.lhs, rhs) def __repr__(self): if self.client: return self.client.serialize(self.lhs) return str(self.lhs) def __eq__(self, rhs): return self.equals(rhs) def __ne__(self, rhs): return not self.__eq__(rhs) class CLITestV10Base(testtools.TestCase): format = 'json' test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' id_field = 'id' def _find_resourceid(self, client, resource, name_or_id): return name_or_id def setUp(self, plurals={}): """Prepare the test environment.""" super(CLITestV10Base, self).setUp() client.Client.EXTED_PLURALS.update(constants.PLURALS) client.Client.EXTED_PLURALS.update(plurals) self.metadata = {'plurals': client.Client.EXTED_PLURALS, 'xmlns': constants.XML_NS_V10, constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}} self.endurl = ENDURL self.fake_stdout = FakeStdout() self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout)) self.useFixture(fixtures.MonkeyPatch( 'tackerclient.tacker.v1_0.find_resourceid_by_name_or_id', self._find_resourceid)) self.useFixture(fixtures.MonkeyPatch( 'tackerclient.tacker.v1_0.find_resourceid_by_id', self._find_resourceid)) self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) @mock.patch.object(TackerCommand, 'get_client') def _test_create_resource(self, resource, cmd, name, myid, args, position_names, position_values, mock_get, tenant_id=None, get_client_called_count=1, tags=None, admin_state_up=True, extra_body=None, **kwargs): mock_get.return_value = self.client non_admin_status_resources = ['vnfd', 'vnf', 'vim', 'vnffgd', 'vnffg'] 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': _body = MyComparator(body, self.client) else: _body = self.client.serialize(body) with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(200), resstr) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser('create_' + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( end_url(path, format=self.format), 'POST', body=_body, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) self.assertEqual(get_client_called_count, mock_get.call_count) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) if name: self.assertIn(name, _str) @mock.patch.object(TackerCommand, 'get_client') def _test_list_columns(self, cmd, resources_collection, resources_out, mock_get, args=['-f', 'json']): mock_get.return_value = self.client self.client.format = self.format resstr = self.client.serialize(resources_out) path = getattr(self.client, resources_collection + "_path") with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(200), resstr) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("list_" + resources_collection) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( end_url(path, format=self.format), 'GET', body=None, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) mock_get.assert_called_once_with() def _test_list_resources(self, resources, cmd, detail=False, tags=[], fields_1=[], fields_2=[], page_size=None, sort_key=[], sort_dir=[], response_contents=None, base_args=None, path=None, template_source=None): if response_contents is None: contents = [{self.id_field: 'myid1', }, {self.id_field: 'myid2', }, ] else: contents = response_contents reses = {resources: contents} self.client.format = self.format resstr = self.client.serialize(reses) # url method body query = "" args = base_args if base_args is not None else [] if detail: args.append('-D') args.extend(['--request-format', self.format]) if fields_1: for field in fields_1: args.append('--fields') args.append(field) if template_source is not None: args.append("--template-source") args.append(template_source) query += 'template_source=' + template_source if tags: args.append('--') args.append("--tag") for tag in tags: args.append(tag) if isinstance(tag, six.string_types): tag = urllib.quote(tag.encode('utf-8')) if query: query += "&tag=" + tag else: query = "tag=" + tag if (not tags) and fields_2: args.append('--') if fields_2: args.append("--fields") for field in fields_2: args.append(field) if detail: query = query and query + '&verbose=True' or 'verbose=True' fields_1.extend(fields_2) for field in fields_1: if query: query += "&fields=" + field else: query = "fields=" + field if page_size: args.append("--page-size") args.append(str(page_size)) if query: query += "&limit=%s" % page_size else: query = "limit=%s" % page_size if sort_key: for key in sort_key: args.append('--sort-key') args.append(key) if query: query += '&' query += 'sort_key=%s' % key if sort_dir: len_diff = len(sort_key) - len(sort_dir) if len_diff > 0: sort_dir += ['asc'] * len_diff elif len_diff < 0: sort_dir = sort_dir[:len(sort_key)] for dir in sort_dir: args.append('--sort-dir') args.append(dir) if query: query += '&' query += 'sort_dir=%s' % dir if path is None: path = getattr(self.client, resources + "_path") with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(200), resstr) with mock.patch.object(TackerCommand, 'get_client') as mock_get: mock_get.return_value = self.client cmd_parser = cmd.get_parser("list_" + resources) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( MyUrlComparator(end_url(path, query, format=self.format), self.client), 'GET', body=None, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) _str = self.fake_stdout.make_string() if response_contents is None: self.assertIn('myid1', _str) return _str @mock.patch.object(TackerCommand, 'get_client') def _test_list_sub_resources(self, resources, api_resource, cmd, myid, mock_get, detail=False, tags=[], fields_1=[], fields_2=[], page_size=None, sort_key=[], sort_dir=[], response_contents=None, base_args=None, path=None): mock_get.return_value = self.client if response_contents is None: contents = [{self.id_field: 'myid1', }, {self.id_field: 'myid2', }, ] else: contents = response_contents reses = {api_resource: contents} self.client.format = self.format resstr = self.client.serialize(reses) # url method body query = "" args = base_args if base_args is not None else [] if detail: args.append('-D') args.extend(['--request-format', self.format]) if fields_1: for field in fields_1: args.append('--fields') args.append(field) if tags: args.append('--') args.append("--tag") for tag in tags: args.append(tag) if isinstance(tag, six.string_types): tag = urllib.quote(tag.encode('utf-8')) if query: query += "&tag=" + tag else: query = "tag=" + tag if (not tags) and fields_2: args.append('--') if fields_2: args.append("--fields") for field in fields_2: args.append(field) if detail: query = query and query + '&verbose=True' or 'verbose=True' fields_1.extend(fields_2) for field in fields_1: if query: query += "&fields=" + field else: query = "fields=" + field if page_size: args.append("--page-size") args.append(str(page_size)) if query: query += "&limit=%s" % page_size else: query = "limit=%s" % page_size if sort_key: for key in sort_key: args.append('--sort-key') args.append(key) if query: query += '&' query += 'sort_key=%s' % key if sort_dir: len_diff = len(sort_key) - len(sort_dir) if len_diff > 0: sort_dir += ['asc'] * len_diff elif len_diff < 0: sort_dir = sort_dir[:len(sort_key)] for dir in sort_dir: args.append('--sort-dir') args.append(dir) if query: query += '&' query += 'sort_dir=%s' % dir if path is None: path = getattr(self.client, resources + "_path") with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(200), resstr) comparator = MyUrlComparator( end_url(path % myid, query=query, format=self.format), self.client) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("list_" + resources) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( comparator, 'GET', body=None, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) _str = self.fake_stdout.make_string() if response_contents is None: self.assertIn('myid1', _str) return _str # TODO(gongysh) add pagination unit test BUG 1633255 # def _test_list_sub_resources_with_pagination( # self, resources, api_resource, cmd, myid): # self.mox.StubOutWithMock(cmd, "get_client") # self.mox.StubOutWithMock(self.client.httpclient, "request") # cmd.get_client().MultipleTimes().AndReturn(self.client) # path = getattr(self.client, resources + "_path") # fake_query = "marker=myid2&limit=2" # reses1 = {api_resource: [{'id': 'myid1', }, # {'id': 'myid2', }], # '%s_links' % api_resource: [ # {'href': end_url(path % myid, fake_query), # 'rel': 'next'}] # } # reses2 = {api_resource: [{'id': 'myid3', }, # {'id': 'myid4', }]} # self.client.format = self.format # resstr1 = self.client.serialize(reses1) # resstr2 = self.client.serialize(reses2) # self.client.httpclient.request( # end_url(path % myid, "", format=self.format), 'GET', # body=None, # headers=mox.ContainsKeyValue( # 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) # self.client.httpclient.request( # MyUrlComparator(end_url(path % myid, fake_query, # format=self.format), self.client), 'GET', # body=None, headers=mox.ContainsKeyValue( # 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) # self.mox.ReplayAll() # cmd_parser = cmd.get_parser("list_" + resources) # args = [myid, '--request-format', self.format] # shell.run_command(cmd, cmd_parser, args) # self.mox.VerifyAll() # self.mox.UnsetStubs() # def _test_list_resources_with_pagination(self, resources, cmd): # self.mox.StubOutWithMock(cmd, "get_client") # self.mox.StubOutWithMock(self.client.httpclient, "request") # cmd.get_client().MultipleTimes().AndReturn(self.client) # path = getattr(self.client, resources + "_path") # fake_query = "marker=myid2&limit=2" # reses1 = {resources: [{'id': 'myid1', }, # {'id': 'myid2', }], # '%s_links' % resources: [ # {'href': end_url(path, fake_query), # 'rel': 'next'}]} # reses2 = {resources: [{'id': 'myid3', }, # {'id': 'myid4', }]} # self.client.format = self.format # resstr1 = self.client.serialize(reses1) # resstr2 = self.client.serialize(reses2) # self.client.httpclient.request( # end_url(path, "", format=self.format), 'GET', # body=None, # headers=mox.ContainsKeyValue( # 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) # self.client.httpclient.request( # MyUrlComparator(end_url(path, fake_query, format=self.format), # self.client), 'GET', body=None, # headers=mox.ContainsKeyValue( # 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) # self.mox.ReplayAll() # cmd_parser = cmd.get_parser("list_" + resources) # args = ['--request-format', self.format] # shell.run_command(cmd, cmd_parser, args) # self.mox.VerifyAll() # self.mox.UnsetStubs() @mock.patch.object(TackerCommand, 'get_client') def _test_update_resource(self, resource, cmd, myid, args, extrafields, mock_get, get_client_called_count=1): mock_get.return_value = self.client body = {resource: extrafields} path = getattr(self.client, resource + "_path") self.client.format = self.format # Work around for LP #1217791. XML deserializer called from # MyComparator does not decodes XML string correctly. if self.format == 'json': _body = MyComparator(body, self.client) else: _body = self.client.serialize(body) with mock.patch.object(self.client.httpclient, 'request') as mock_req: comparator = MyUrlComparator( end_url(path % myid, format=self.format), self.client) mock_req.return_value = (MyResp(204), None) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("update_" + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( comparator, 'PUT', body=_body, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) self.assertEqual(get_client_called_count, mock_get.call_count) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) def _test_show_resource(self, resource, cmd, myid, args, fields=[]): with mock.patch.object(cmd, 'get_client') as mock_get: mock_get.return_value = self.client query = "&".join(["fields=%s" % field for field in fields]) expected_res = {resource: {self.id_field: myid, 'name': 'myname', }, } self.client.format = self.format resstr = self.client.serialize(expected_res) path = getattr(self.client, resource + "_path") with mock.patch.object(self.client.httpclient, 'request') as\ mock_req: mock_req.return_value = (MyResp(200), resstr) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("show_" + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( end_url(path % myid, query, format=self.format), 'GET', body=None, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) _str = self.fake_stdout.make_string() mock_get.assert_called_once_with() self.assertIn(myid, _str) self.assertIn('myname', _str) @mock.patch.object(TackerCommand, 'get_client') def _test_delete_resource(self, resource, cmd, myid, args, mock_get): deleted_msg = {'vnf': 'delete initiated'} mock_get.return_value = self.client path = getattr(self.client, resource + "_path") with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(204), None) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("delete_" + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( end_url(path % myid, format=self.format), 'DELETE', body=None, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) mock_get.assert_called_once_with() _str = self.fake_stdout.make_string() msg = 'All specified %(resource)s(s) %(msg)s successfully\n' % { 'msg': deleted_msg.get(resource, 'deleted'), 'resource': resource} self.assertEqual(msg, _str) @mock.patch.object(TackerCommand, 'get_client') def _test_update_resource_action(self, resource, cmd, myid, action, args, body, mock_get, retval=None): mock_get.return_value = self.client path = getattr(self.client, resource + "_path") path_action = '%s/%s' % (myid, action) with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(204), retval) args.extend(['--request-format', self.format]) cmd_parser = cmd.get_parser("delete_" + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( end_url(path % path_action, format=self.format), 'PUT', body=MyComparator(body, self.client), headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) class ClientV1TestJson(CLITestV10Base): def test_do_request_unicode(self): self.client.format = self.format unicode_text = u'\u7f51\u7edc' action = u'/test' params = {'test': unicode_text} body = params expect_body = self.client.serialize(body) self.client.httpclient.auth_token = unicode_text with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(200), expect_body) res_body = self.client.do_request('PUT', action, body=body, params=params) expected_uri = u'localurl/v1.0/test.json?test=%E7%BD%91%E7%BB%9C' mock_req.assert_called_with( expected_uri, 'PUT', body=expect_body, headers={'X-Auth-Token': unicode_text, 'User-Agent': 'python-tackerclient'}) # test response with unicode self.assertEqual(res_body, body) def test_do_request_error_without_response_body(self): self.client.format = self.format params = {'test': 'value'} expect_query = urlparse.urlencode(params) self.client.httpclient.auth_token = 'token' with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (MyResp(400, reason='An error'), '') self.client.httpclient.request( end_url('/test', query=expect_query, format=self.format), 'PUT', body='', headers={'X-Auth-Token': 'token'} ) error = self.assertRaises(exceptions.TackerClientException, self.client.do_request, 'PUT', '/test', body='', params=params) self.assertEqual("An error", str(error)) class CLITestV10ExceptionHandler(CLITestV10Base): def _test_exception_handler_v10( self, expected_exception, status_code, expected_msg, error_type=None, error_msg=None, error_detail=None, error_content=None): if error_content is None: error_content = {'TackerError': {'type': error_type, 'message': error_msg, 'detail': error_detail}} e = self.assertRaises(expected_exception, client.exception_handler_v10, status_code, error_content) self.assertEqual(status_code, e.status_code) self.assertEqual(expected_exception.__name__, e.__class__.__name__) if expected_msg is None: if error_detail: expected_msg = '\n'.join([error_msg, error_detail]) else: expected_msg = error_msg self.assertEqual(expected_msg, e.message) def test_exception_handler_v10_ip_address_in_use(self): err_msg = ('Unable to complete operation for network ' 'fake-network-uuid. The IP address fake-ip is in use.') self._test_exception_handler_v10( exceptions.IpAddressInUseClient, 409, err_msg, 'IpAddressInUse', err_msg, '') def test_exception_handler_v10_tacker_known_error(self): known_error_map = [ ('NetworkNotFound', exceptions.NetworkNotFoundClient, 404), ('PortNotFound', exceptions.PortNotFoundClient, 404), ('NetworkInUse', exceptions.NetworkInUseClient, 409), ('PortInUse', exceptions.PortInUseClient, 409), ('StateInvalid', exceptions.StateInvalidClient, 400), ('IpAddressInUse', exceptions.IpAddressInUseClient, 409), ('IpAddressGenerationFailure', exceptions.IpAddressGenerationFailureClient, 409), ('ExternalIpAddressExhausted', exceptions.ExternalIpAddressExhaustedClient, 400), ('OverQuota', exceptions.OverQuotaClient, 409), ] error_msg = 'dummy exception message' error_detail = 'sample detail' for server_exc, client_exc, status_code in known_error_map: self._test_exception_handler_v10( client_exc, status_code, error_msg + '\n' + error_detail, server_exc, error_msg, error_detail) def test_exception_handler_v10_tacker_known_error_without_detail(self): error_msg = 'Network not found' error_detail = '' self._test_exception_handler_v10( exceptions.NetworkNotFoundClient, 404, error_msg, 'NetworkNotFound', error_msg, error_detail) def test_exception_handler_v10_unknown_error_to_per_code_exception(self): for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): error_msg = 'Unknown error' error_detail = 'This is detail' self._test_exception_handler_v10( client_exc, status_code, error_msg + '\n' + error_detail, 'UnknownError', error_msg, error_detail) def test_exception_handler_v10_tacker_unknown_status_code(self): error_msg = 'Unknown error' error_detail = 'This is detail' self._test_exception_handler_v10( exceptions.TackerClientException, 501, error_msg + '\n' + error_detail, 'UnknownError', error_msg, error_detail) def test_exception_handler_v10_bad_tacker_error(self): error_content = {'TackerError': {'unknown_key': 'UNKNOWN'}} self._test_exception_handler_v10( exceptions.TackerClientException, 500, expected_msg={'unknown_key': 'UNKNOWN'}, error_content=error_content) def test_exception_handler_v10_error_dict_contains_message(self): error_content = {'message': 'This is an error message'} self._test_exception_handler_v10( exceptions.TackerClientException, 500, expected_msg='This is an error message', error_content=error_content) def test_exception_handler_v10_error_dict_not_contain_message(self): error_content = {'error': 'This is an error message'} expected_msg = '%s-%s' % (500, error_content) self._test_exception_handler_v10( exceptions.TackerClientException, 500, expected_msg=expected_msg, error_content=error_content) def test_exception_handler_v10_default_fallback(self): error_content = 'This is an error message' expected_msg = '%s-%s' % (500, error_content) self._test_exception_handler_v10( exceptions.TackerClientException, 500, expected_msg=expected_msg, error_content=error_content) python-tackerclient-0.11.0/tackerclient/tests/unit/__init__.py0000666000175100017510000000000013234643056024504 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/test_casual_args.py0000666000175100017510000001123413234643056026303 0ustar zuulzuul00000000000000# Copyright 2012 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 testtools from tackerclient.common import exceptions from tackerclient.tacker import v1_0 as tackerV10 class CLITestArgs(testtools.TestCase): def test_empty(self): _mydict = tackerV10.parse_args_to_dict([]) self.assertEqual({}, _mydict) def test_default_bool(self): _specs = ['--my_bool', '--arg1', 'value1'] _mydict = tackerV10.parse_args_to_dict(_specs) self.assertTrue(_mydict['my_bool']) def test_bool_true(self): _specs = ['--my-bool', 'type=bool', 'true', '--arg1', 'value1'] _mydict = tackerV10.parse_args_to_dict(_specs) self.assertTrue(_mydict['my_bool']) def test_bool_false(self): _specs = ['--my_bool', 'type=bool', 'false', '--arg1', 'value1'] _mydict = tackerV10.parse_args_to_dict(_specs) self.assertFalse(_mydict['my_bool']) def test_nargs(self): _specs = ['--tag', 'x', 'y', '--arg1', 'value1'] _mydict = tackerV10.parse_args_to_dict(_specs) self.assertIn('x', _mydict['tag']) self.assertIn('y', _mydict['tag']) def test_badarg(self): _specs = ['--tag=t', 'x', 'y', '--arg1', 'value1'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) def test_badarg_with_minus(self): _specs = ['--arg1', 'value1', '-D'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) def test_goodarg_with_minus_number(self): _specs = ['--arg1', 'value1', '-1', '-1.0'] _mydict = tackerV10.parse_args_to_dict(_specs) self.assertEqual(['value1', '-1', '-1.0'], _mydict['arg1']) def test_badarg_duplicate(self): _specs = ['--tag=t', '--arg1', 'value1', '--arg1', 'value1'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) def test_badarg_early_type_specification(self): _specs = ['type=dict', 'key=value'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) def test_arg(self): _specs = ['--tag=t', '--arg1', 'value1'] self.assertEqual('value1', tackerV10.parse_args_to_dict(_specs)['arg1']) def test_dict_arg(self): _specs = ['--tag=t', '--arg1', 'type=dict', 'key1=value1,key2=value2'] arg1 = tackerV10.parse_args_to_dict(_specs)['arg1'] self.assertEqual('value1', arg1['key1']) self.assertEqual('value2', arg1['key2']) def test_dict_arg_with_attribute_named_type(self): _specs = ['--tag=t', '--arg1', 'type=dict', 'type=value1,key2=value2'] arg1 = tackerV10.parse_args_to_dict(_specs)['arg1'] self.assertEqual('value1', arg1['type']) self.assertEqual('value2', arg1['key2']) def test_list_of_dict_arg(self): _specs = ['--tag=t', '--arg1', 'type=dict', 'list=true', 'key1=value1,key2=value2'] arg1 = tackerV10.parse_args_to_dict(_specs)['arg1'] self.assertEqual('value1', arg1[0]['key1']) self.assertEqual('value2', arg1[0]['key2']) def test_clear_action(self): _specs = ['--anyarg', 'action=clear'] args = tackerV10.parse_args_to_dict(_specs) self.assertIsNone(args['anyarg']) def test_bad_values_str(self): _specs = ['--strarg', 'type=str'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) def test_bad_values_list(self): _specs = ['--listarg', 'list=true', 'type=str'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) _specs = ['--listarg', 'type=list'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) _specs = ['--listarg', 'type=list', 'action=clear'] self.assertRaises(exceptions.CommandError, tackerV10.parse_args_to_dict, _specs) python-tackerclient-0.11.0/tackerclient/tests/unit/test_command_meta.py0000666000175100017510000000253513234643056026447 0ustar zuulzuul00000000000000# Copyright 2013 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 logging import testtools from testtools import helpers from tackerclient.tacker import v1_0 as tackerV10 class TestCommandMeta(testtools.TestCase): def test_tacker_command_meta_defines_log(self): class FakeCommand(tackerV10.TackerCommand): pass self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) self.assertIsInstance(FakeCommand.log, logging.getLoggerClass()) self.assertEqual(FakeCommand.log.name, __name__ + ".FakeCommand") def test_tacker_command_log_defined_explicitly(self): class FakeCommand(tackerV10.TackerCommand): log = None self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) self.assertIsNone(FakeCommand.log) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/0000775000175100017510000000000013234643340023021 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vnffg.py0000666000175100017510000001072113234643056026537 0ustar zuulzuul00000000000000# 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.common import utils from tackerclient.tacker.v1_0.nfvo import vnffg from tackerclient.tests.unit import test_cli10 API_VERSION = "1.0" FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' class CLITestV10VmVNFFGJSON(test_cli10.CLITestV10Base): _RESOURCE = 'vnffg' _RESOURCES = 'vnffgs' def setUp(self): plurals = {'vnffgs': 'vnffg'} super(CLITestV10VmVNFFGJSON, self).setUp(plurals=plurals) def test_create_vnffg_all_params(self): cmd = vnffg.CreateVNFFG(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' vnffgd_id = 'vnffgd' vnffg_name = 'fake-vnffg' vnf_mapping = 'VNFD1:VNF1' args = [ vnffg_name, '--vnffgd-id', vnffgd_id, '--vnf-mapping', vnf_mapping, '--symmetrical'] position_names = ['vnffgd_id', 'vnf_mapping', 'symmetrical'] position_values = [vnffgd_id, {"VNFD1": "VNF1"}, True] extra_body = {'name': vnffg_name, 'attributes': {}} self._test_create_resource(self._RESOURCE, cmd, None, my_id, args, position_names, position_values, extra_body=extra_body, get_client_called_count=2) def test_create_vnffg_with_mandatory_params(self): cmd = vnffg.CreateVNFFG(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' vnffg_name = 'fake-vnffg' vnffgd_id = 'vnffgd' args = [ vnffg_name, '--vnffgd-id', vnffgd_id, ] position_names = ['vnffgd_id'] position_values = [vnffgd_id] extra_body = {'symmetrical': False, 'name': vnffg_name, 'attributes': {}} self._test_create_resource(self._RESOURCE, cmd, vnffg_name, my_id, args, position_names, position_values, extra_body=extra_body, get_client_called_count=2) def test_list_vnffgs(self): cmd = vnffg.ListVNFFG(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True) def test_list_vnffgs_pagenation(self): cmd = vnffg.ListVNFFG(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True) def test_show_vnffg_id(self): cmd = vnffg.ShowVNFFG(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_vnffg_id_name(self): cmd = vnffg.ShowVNFFG(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_vnffg(self): cmd = vnffg.UpdateVNFFG(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' update_vnffg = utils.get_file_path( 'tests/unit/vm/samples/vnffg_update_file.yaml') vnf_mapping = 'VNFD1:VNF1' args = [ my_id, '--vnf-mapping', vnf_mapping, '--vnffgd-template', str(update_vnffg), '--symmetrical' ] extra_fields = { "vnf_mapping": {"VNFD1": "VNF1"}, "vnffgd_template": "abcxyz", "symmetrical": True } self._test_update_resource(self._RESOURCE, cmd, my_id, args, extra_fields, get_client_called_count=2) def test_delete_vnffg(self): cmd = vnffg.DeleteVNFFG(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' args = [my_id] self._test_delete_resource(self._RESOURCE, cmd, my_id, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/0000775000175100017510000000000013234643340024465 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_k8s_bearer_token.yaml0000666000175100017510000000016113234643056031455 0ustar zuulzuul00000000000000auth_url: 'https://1.2.3.4:6443' bearer_token: 'xyz' ssl_ca_cert: None project_name: 'default' type: 'kubernetes'python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_config.yaml0000666000175100017510000000026313234643056027500 0ustar zuulzuul00000000000000auth_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' type: 'openstack'python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vnffg_update_file.yaml0000666000175100017510000000000613234643056031022 0ustar zuulzuul00000000000000abcxyzpython-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml0000666000175100017510000000014213234643056033747 0ustar zuulzuul00000000000000username: 'xyz' password: '12345' ssl_ca_cert: 'abcxyz' project_name: 'default' type: 'kubernetes'python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_config_without_auth_url.yaml0000666000175100017510000000022413234643056033163 0ustar zuulzuul00000000000000username: 'xyz' password: '12345' project_name: 'abc' project_domain_name: 'prj_domain_name' user_domain_name: 'user_domain_name' type: 'openstack' python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_k8s_config.yaml0000666000175100017510000000020313234643056030257 0ustar zuulzuul00000000000000auth_url: 'https://1.2.3.4:6443' username: 'xyz' password: '12345' ssl_ca_cert: 'abcxyz' project_name: 'default' type: 'kubernetes'python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_config_with_false_cert_verify.yaml0000666000175100017510000000031013234643056034277 0ustar zuulzuul00000000000000auth_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' cert_verify: 'False' type: 'openstack'././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yamlpython-tackerclient-0.11.0/tackerclient/tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.0000666000175100017510000000012013234643056034253 0ustar zuulzuul00000000000000bearer_token: 'xyz' ssl_ca_cert: None project_name: 'default' type: 'kubernetes'python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vnffgd.py0000666000175100017510000001042113234643056026700 0ustar zuulzuul00000000000000# 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 mock_open from mock import patch import sys from tackerclient.tacker.v1_0.nfvo import vnffgd from tackerclient.tests.unit import test_cli10 class CLITestV10VmVNFFGDJSON(test_cli10.CLITestV10Base): _RESOURCE = 'vnffgd' _RESOURCES = 'vnffgds' def setUp(self): plurals = {'vnffgds': 'vnffgd'} super(CLITestV10VmVNFFGDJSON, self).setUp(plurals=plurals) @patch("tackerclient.tacker.v1_0.nfvo.vnffgd.open", side_effect=mock_open(read_data="vnffgd"), create=True) def test_create_vnffgd_all_params(self, mo): cmd = vnffgd.CreateVNFFGD(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'my-name' attr_key = 'vnffgd' attr_val = 'vnffgd' description = 'vnffgd description' args = [ name, '--vnffgd-file', 'vnffgd_file', '--description', description, ] position_names = ['name', 'description'] position_values = [name, description] extra_body = { 'template': {attr_key: attr_val}, } self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values, extra_body=extra_body) @patch("tackerclient.tacker.v1_0.nfvo.vnffgd.open", side_effect=mock_open(read_data="vnffgd"), create=True) def test_create_vnffgd_with_mandatory_params(self, mo): cmd = vnffgd.CreateVNFFGD(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'my-name' attr_key = 'vnffgd' attr_val = 'vnffgd' args = [ name, '--vnffgd-file', 'vnffgd_file', ] position_names = ['name'] position_values = [name] extra_body = { 'template': {attr_key: attr_val}, } self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values, extra_body=extra_body) def test_list_vnffgds(self): cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='onboarded') def test_list_inline_vnffgds(self): cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='inline') def test_list_all_vnffgds(self): cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='all') def test_list_vnffgds_pagenation(self): cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='onboarded') def test_show_vnffgd_id(self): cmd = vnffgd.ShowVNFFGD(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_vnffgd_id_name(self): cmd = vnffgd.ShowVNFFGD(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_vnffgd(self): cmd = vnffgd.DeleteVNFFGD(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' args = [my_id] self._test_delete_resource(self._RESOURCE, cmd, my_id, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_v10_event.py0000666000175100017510000000535513234643056027247 0ustar zuulzuul00000000000000# 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.events import events from tackerclient.tests.unit import test_cli10 API_VERSION = "1.0" FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' class CLITestV10EventJSON(test_cli10.CLITestV10Base): _EVT_RESOURCE = 'event' _EVT_RESOURCES = _EVT_RESOURCE + 's' _VNF_EVT_RESOURCE = "vnf_event" _VNF_EVT_RESOURCES = _VNF_EVT_RESOURCE + 's' _VNFD_EVT_RESOURCE = "vnfd_event" _VNFD_EVT_RESOURCES = _VNFD_EVT_RESOURCE + 's' _VIM_EVT_RESOURCE = "vim_event" _VIM_EVT_RESOURCES = _VIM_EVT_RESOURCE + 's' def setUp(self): plurals = {'events': 'event', 'vnf_events': 'vnf_event', 'vnfd_events': 'vnfd_event', 'vim_events': 'vim_event'} super(CLITestV10EventJSON, self).setUp(plurals=plurals) def test_list_events(self): cmd = events.ListResourceEvents(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._EVT_RESOURCES, cmd, True) def test_show_event_id(self): cmd = events.ShowEvent(test_cli10.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(self._EVT_RESOURCE, cmd, self.test_id, args, ['id']) def notest_list_vnf_events(self): # TODO(vishwanathj): Need to enhance _test_list_resources() # for supporting filters to get this test working cmd = events.ListVNFEvents(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._VNF_EVT_RESOURCES, cmd, True) def notest_list_vnfd_events(self): # TODO(vishwanathj): Need to enhance _test_list_resources() # for supporting filters to get this test working cmd = events.ListVNFDEvents(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._VNFD_EVT_RESOURCES, cmd, True) def notest_list_vim_events(self): # TODO(vishwanathj): Need to enhance _test_list_resources() # for supporting filters to get this test working cmd = events.ListVIMEvents(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._VIM_EVT_RESOURCES, cmd, True) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vnfd.py0000666000175100017510000001264613234643056026376 0ustar zuulzuul00000000000000# 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. from mock import mock_open from mock import patch import sys from tackerclient.common.exceptions import InvalidInput from tackerclient.tacker.v1_0.vnfm 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) @patch("tackerclient.tacker.v1_0.vnfm.vnfd.open", side_effect=mock_open(read_data="vnfd"), create=True) def test_create_vnfd_all_params(self, mo): cmd = vnfd.CreateVNFD( test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'my-name' attr_key = 'vnfd' attr_val = 'vnfd' args = [ name, '--vnfd-file', 'vnfd-file' ] position_names = ['name'] position_values = [name] 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) @patch("tackerclient.tacker.v1_0.vnfm.vnfd.open", side_effect=mock_open(read_data="vnfd"), create=True) def test_create_vnfd_with_mandatory_params(self, mo): cmd = vnfd.CreateVNFD( test_cli10.MyApp(sys.stdout), None) name = 'my_name' my_id = 'my-id' args = [name, '--vnfd-file', 'vnfd-file', ] position_names = ['name'] position_values = [name] extra_body = { 'service_types': [{'service_type': 'vnfd'}], 'attributes': {'vnfd': 'vnfd'} } self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values, extra_body=extra_body) @patch("tackerclient.tacker.v1_0.vnfm.vnfd.open", side_effect=mock_open(read_data=""), create=True) def test_create_vnfd_with_empty_file(self, mo): cmd = vnfd.CreateVNFD( test_cli10.MyApp(sys.stdout), None) name = 'my_name' my_id = 'my-id' args = [name, '--vnfd-file', 'vnfd-file', ] position_names = ['name'] position_values = [name] extra_body = { 'service_types': [{'service_type': 'vnfd'}], 'attributes': {'vnfd': 'vnfd'} } err = None try: self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values, extra_body=extra_body) except InvalidInput: err = True self.assertEqual(True, err) def test_list_vnfds(self): cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='onboarded') def test_list_inline_vnfds(self): cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='inline') def test_list_all_vnfds(self): cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None) self._test_list_resources(self._RESOURCES, cmd, True, template_source='all') def test_list_vnfds_pagenation(self): cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None) print(cmd) self._test_list_resources(self._RESOURCES, cmd, True, template_source='onboarded') 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) def test_multi_delete_vnfd(self): cmd = vnfd.DeleteVNFD( test_cli10.MyApp(sys.stdout), None) vnfd_ids = 'my-id1 my-id2 my-id3' args = [vnfd_ids] self._test_delete_resource(self._RESOURCE, cmd, vnfd_ids, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vim_k8s.py0000666000175100017510000001554313234643056027020 0ustar zuulzuul00000000000000# 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.common import exceptions from tackerclient.common import utils 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': 'default'} self.auth_cred = {'username': 'xyz', 'password': '12345', 'ssl_ca_cert': 'abcxyz'} self.auth_url = 'https://1.2.3.4:6443' self.type = 'kubernetes' def test_register_vim_all_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_config.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] extra_body = {'type': 'kubernetes', '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_no_auth_url(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'test_vim' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] extra_body = {'type': 'kubernetes', 'name': name, 'description': description, 'is_default': False} message = 'Auth URL must be specified' ex = self.assertRaises(exceptions.TackerClientException, self._test_create_resource, self._RESOURCE, cmd, None, my_id, args, position_names, position_values, extra_body=extra_body) self.assertEqual(message, ex.message) self.assertEqual(404, ex.status_code) def test_register_vim_with_mandatory_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_config.yaml') args = [ name, '--config-file', vim_config, ] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [ self.auth_cred, self.vim_project, self.auth_url, self.type ] extra_body = {'type': 'kubernetes', 'name': name, 'is_default': False} self._test_create_resource(self._RESOURCE, cmd, name, 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_all_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml') my_id = 'my-id' name = 'new_name' name = 'new_name' description = 'new_description' is_default = 'True' args = [ my_id, '--config-file', str(update_config), '--name', name, '--description', description, '--is_default', is_default] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred, 'is_default': 'True', 'name': name, 'description': description} self._test_update_resource(self._RESOURCE, cmd, my_id, args, extra_fields) def test_update_vim_with_mandatory_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml') my_id = 'my-id' args = [ my_id, '--config-file', str(update_config)] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred} self._test_update_resource(self._RESOURCE, cmd, my_id, args, 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) def test_multi_delete_vim(self): cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None) vim_ids = 'my-id1 my-id2 my-id3' args = [vim_ids] self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/__init__.py0000666000175100017510000000000013234643056025126 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vnf.py0000666000175100017510000002023513234643056026223 0ustar zuulzuul00000000000000# 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 import mock from tackerclient import shell from tackerclient.tacker import v1_0 as tackerV1_0 from tackerclient.tacker.v1_0 import TackerCommand from tackerclient.tacker.v1_0.vnfm import vnf from tackerclient.tests.unit import test_cli10 from tackerclient.tests.unit import test_utils API_VERSION = "1.0" FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base): _RESOURCE = 'vnf' _RESOURCES = 'vnfs' _VNF_RESOURCES = 'vnf_resources' def setUp(self): plurals = {'vnfs': 'vnf', 'resources': 'resource'} super(CLITestV10VmVNFJSON, self).setUp(plurals=plurals) @mock.patch.object(TackerCommand, 'get_client') def _test_create_resource(self, resource, cmd, name, myid, args, position_names, position_values, mock_get, tenant_id=None, tags=None, admin_state_up=True, extra_body=None, **kwargs): mock_get.return_value = 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': _body = test_cli10.MyComparator(body, self.client) else: _body = self.client.serialize(body) with mock.patch.object(self.client.httpclient, 'request') as mock_req: mock_req.return_value = (test_cli10.MyResp(200), resstr) args.extend(['--request-format', self.format]) args.extend(['--vnfd-id', 'vnfd']) cmd_parser = cmd.get_parser('create_' + resource) shell.run_command(cmd, cmd_parser, args) mock_req.assert_called_once_with( test_cli10.end_url(path, format=self.format), 'POST', body=_body, headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN)) mock_get.assert_any_call() def test_create_vnf_all_params(self): cmd = vnf.CreateVNF(test_cli10.MyApp(sys.stdout), None) name = 'my_name' my_id = 'my-id' vnfd_id = 'vnfd' vim_id = 'vim_id' description = 'my-description' region_name = 'region' key = 'key' value = 'value' args = [ name, '--vnfd-id', vnfd_id, '--vim-id', vim_id, '--description', description, '--vim-region-name', region_name, '--%s' % key, value] position_names = [ 'name', 'vnfd_id', 'vim_id', 'description', 'attributes', ] position_values = [ name, vnfd_id, vim_id, description, {}, ] extra_body = {key: value, 'placement_attr': {'region_name': region_name}} self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values, extra_body=extra_body) def test_create_vnf_with_vnfd_id(self): cmd = vnf.CreateVNF(test_cli10.MyApp(sys.stdout), None) name = 'my_name' my_id = 'my-id' vnfd_id = 'vnfd' args = [ name, '--vnfd-id', vnfd_id, ] position_names = ['name', 'vnfd_id', 'attributes'] position_values = [name, vnfd_id, {}] self._test_create_resource(self._RESOURCE, cmd, name, my_id, args, position_names, position_values) def test_create_vnf_with_description_param(self): cmd = vnf.CreateVNF(test_cli10.MyApp(sys.stdout), None) name = 'my_name' my_id = 'my-id' vnfd_id = 'vnfd' description = 'my-description' args = [ name, '--vnfd-id', vnfd_id, '--description', description, ] position_names = ['name', 'vnfd_id', 'description', 'attributes'] position_values = [name, vnfd_id, description, {}] 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) def test_list_vnf_resources(self): cmd = vnf.ListVNFResources(test_cli10.MyApp(sys.stdout), None) base_args = [self.test_id] response = [{'name': 'CP11', 'id': 'id1', 'type': 'NeutronPort'}, {'name': 'CP12', 'id': 'id2', 'type': 'NeutronPort'}] val = self._test_list_sub_resources(self._VNF_RESOURCES, 'resources', cmd, self.test_id, response_contents=response, detail=True, base_args=base_args) self.assertIn('id1', val) self.assertIn('NeutronPort', val) self.assertIn('CP11', val) def test_multi_delete_vnf(self): cmd = vnf.DeleteVNF(test_cli10.MyApp(sys.stdout), None) vnf_ids = 'vnf1 vnf2 vnf3' args = [vnf_ids] self._test_delete_resource(self._RESOURCE, cmd, vnf_ids, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_vim_utils.py0000666000175100017510000001064313234643056026457 0ustar zuulzuul00000000000000# 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 TestVIMUtils(testtools.TestCase): def test_args2body_vim(self): config_param = {'project_name': sentinel.prj_name, 'username': sentinel.usrname1, 'password': sentinel.password1, 'project_domain_name': sentinel.prj_domain_name1, 'user_domain_name': sentinel.user_domain.name, 'cert_verify': 'True', 'type': 'openstack'} vim = {} auth_cred = config_param.copy() auth_cred.pop('project_name') auth_cred.pop('project_domain_name') auth_cred.pop('type') expected_vim = {'auth_cred': auth_cred, 'vim_project': {'name': sentinel.prj_name, 'project_domain_name': sentinel.prj_domain_name1}, 'type': 'openstack'} vim_utils.args2body_vim(config_param.copy(), vim) self.assertEqual(expected_vim, vim) def test_args2body_kubernetes_vim(self): config_param = {'username': sentinel.usrname1, 'password': sentinel.password1, 'ssl_ca_cert': 'abcxyz', 'project_name': sentinel.prj_name, 'type': 'kubernetes'} vim = {} auth_cred = config_param.copy() auth_cred.pop('project_name') auth_cred.pop('type') expected_vim = {'auth_cred': auth_cred, 'vim_project': {'name': sentinel.prj_name}, 'type': 'kubernetes'} vim_utils.args2body_vim(config_param.copy(), vim) self.assertEqual(expected_vim, vim) def test_args2body_kubernetes_vim_bearer(self): config_param = {'bearer_token': sentinel.bearer_token, 'ssl_ca_cert': None, 'project_name': sentinel.prj_name, 'type': 'kubernetes'} vim = {} auth_cred = config_param.copy() auth_cred.pop('project_name') auth_cred.pop('type') expected_vim = {'auth_cred': auth_cred, 'vim_project': {'name': sentinel.prj_name}, 'type': 'kubernetes'} vim_utils.args2body_vim(config_param.copy(), vim) self.assertEqual(expected_vim, vim) def test_args2body_vim_no_project(self): config_param = {'username': sentinel.usrname1, 'password': sentinel.password1, 'user_domain_name': sentinel.user_domain.name, 'cert_verify': 'True', 'type': 'openstack'} vim = {} self.assertRaises(exceptions.TackerClientException, vim_utils.args2body_vim, config_param, vim) def test_validate_auth_url_with_port(self): auth_url = "http://localhost:8000/test" url_parts = vim_utils.validate_auth_url(auth_url) self.assertEqual('http', url_parts.scheme) self.assertEqual('localhost:8000', url_parts.netloc) self.assertEqual(8000, url_parts.port) def test_validate_auth_url_without_port(self): auth_url = "http://localhost/test" url_parts = vim_utils.validate_auth_url(auth_url) self.assertEqual('http', url_parts.scheme) self.assertEqual('localhost', url_parts.netloc) def test_validate_auth_url_exception(self): auth_url = "localhost/test" self.assertRaises(exceptions.TackerClientException, vim_utils.validate_auth_url, auth_url) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vim.py0000666000175100017510000002011613234643056026223 0ustar zuulzuul00000000000000# 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.common import exceptions from tackerclient.common import utils 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', 'project_domain_name': 'prj_domain_name'} self.auth_cred = {'username': 'xyz', 'password': '12345', 'user_domain_name': 'user_domain_name', 'cert_verify': 'True'} self.auth_url = 'http://1.2.3.4:5000' self.type = 'openstack' def test_register_vim_all_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] 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_false_cert_verify(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' # change cert_verify to False self.auth_cred = {'username': 'xyz', 'password': '12345', 'user_domain_name': 'user_domain_name', 'cert_verify': 'False'} description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config_with_false_cert_verify.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] 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_no_auth_url(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'test_vim' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config_without_auth_url.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] extra_body = {'type': 'openstack', 'name': name, 'description': description, 'is_default': False} message = 'Auth URL must be specified' ex = self.assertRaises(exceptions.TackerClientException, self._test_create_resource, self._RESOURCE, cmd, None, my_id, args, position_names, position_values, extra_body=extra_body) self.assertEqual(message, ex.message) self.assertEqual(404, ex.status_code) def test_register_vim_with_mandatory_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config.yaml') args = [ name, '--config-file', vim_config, ] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [ self.auth_cred, self.vim_project, self.auth_url, self.type ] extra_body = {'type': 'openstack', 'name': name, 'is_default': False} self._test_create_resource(self._RESOURCE, cmd, name, 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_all_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config_without_auth_url.yaml') my_id = 'my-id' name = 'new_name' description = 'new_description' is_default = 'True' args = [ my_id, '--config-file', str(update_config), '--name', name, '--description', description, '--is_default', is_default] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred, 'is_default': 'True', 'name': name, 'description': description} self._test_update_resource(self._RESOURCE, cmd, my_id, args, extra_fields) def test_update_vim_with_mandatory_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_config_without_auth_url.yaml') my_id = 'my-id' args = [ my_id, '--config-file', str(update_config)] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred} self._test_update_resource(self._RESOURCE, cmd, my_id, args, 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) def test_multi_delete_vim(self): cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None) vim_ids = 'my-id1 my-id2 my-id3' args = [vim_ids] self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args) python-tackerclient-0.11.0/tackerclient/tests/unit/vm/test_cli10_vim_k8s_with_bearer_token.py0000666000175100017510000001547013234643056032572 0ustar zuulzuul00000000000000# 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.common import exceptions from tackerclient.common import utils 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': 'default'} self.auth_cred = {'bearer_token': 'xyz', 'ssl_ca_cert': None} self.auth_url = 'https://1.2.3.4:6443' self.type = 'kubernetes' def test_register_vim_all_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_bearer_token.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] extra_body = {'type': 'kubernetes', '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_no_auth_url(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) my_id = 'my-id' name = 'test_vim' description = 'Vim Description' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml') args = [ name, '--config-file', vim_config, '--description', description] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [self.auth_cred, self.vim_project, self.auth_url, self.type] extra_body = {'type': 'kubernetes', 'name': name, 'description': description, 'is_default': False} message = 'Auth URL must be specified' ex = self.assertRaises(exceptions.TackerClientException, self._test_create_resource, self._RESOURCE, cmd, None, my_id, args, position_names, position_values, extra_body=extra_body) self.assertEqual(message, ex.message) self.assertEqual(404, ex.status_code) def test_register_vim_with_mandatory_params(self): cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None) name = 'my-name' my_id = 'my-id' vim_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_bearer_token.yaml') args = [ name, '--config-file', vim_config, ] position_names = ['auth_cred', 'vim_project', 'auth_url', 'type'] position_values = [ self.auth_cred, self.vim_project, self.auth_url, self.type ] extra_body = {'type': 'kubernetes', 'name': name, 'is_default': False} self._test_create_resource(self._RESOURCE, cmd, name, 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_all_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml') my_id = 'my-id' name = 'new_name' description = 'new_description' is_default = 'True' args = [ my_id, '--config-file', str(update_config), '--name', name, '--description', description, '--is_default', is_default] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred, 'is_default': 'True', 'name': name, 'description': description} self._test_update_resource(self._RESOURCE, cmd, my_id, args, extra_fields) def test_update_vim_with_mandatory_params(self): cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None) update_config = utils.get_file_path( 'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml') my_id = 'my-id' args = [ my_id, '--config-file', str(update_config)] extra_fields = {'vim_project': self.vim_project, 'auth_cred': self.auth_cred} self._test_update_resource(self._RESOURCE, cmd, my_id, args, 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) def test_multi_delete_vim(self): cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None) vim_ids = 'my-id1 my-id2 my-id3' args = [vim_ids] self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args) python-tackerclient-0.11.0/tackerclient/tests/unit/test_auth.py0000666000175100017510000003414713234643056024770 0ustar zuulzuul00000000000000# Copyright 2012 NEC Corporation # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import copy import json import uuid from keystoneclient import exceptions as k_exceptions import mock import requests import testtools from tackerclient import client from tackerclient.common import exceptions USERNAME = 'testuser' USER_ID = 'testuser_id' TENANT_NAME = 'testtenant' TENANT_ID = 'testtenant_id' PASSWORD = 'password' AUTH_URL = 'authurl' ENDPOINT_URL = 'localurl' ENDPOINT_OVERRIDE = 'otherurl' TOKEN = 'tokentoken' REGION = 'RegionTest' NOAUTH = 'noauth' KS_TOKEN_RESULT = { 'access': { 'token': {'id': TOKEN, 'expires': '2012-08-11T07:49:01Z', 'tenant': {'id': str(uuid.uuid1())}}, 'user': {'id': str(uuid.uuid1())}, 'serviceCatalog': [ {'endpoints_links': [], 'endpoints': [{'adminURL': ENDPOINT_URL, 'internalURL': ENDPOINT_URL, 'publicURL': ENDPOINT_URL, 'region': REGION}], 'type': 'nfv-orchestration', 'name': 'Tacker Service'} ] } } ENDPOINTS_RESULT = { 'endpoints': [{ 'type': 'nfv-orchestration', 'name': 'Tacker Service', 'region': REGION, 'adminURL': ENDPOINT_URL, 'internalURL': ENDPOINT_URL, 'publicURL': ENDPOINT_URL }] } def get_response(status_code, headers=None): response = mock.Mock().CreateMock(requests.Response) response.headers = headers or {} response.status_code = status_code return response resp_200 = get_response(200) resp_401 = get_response(401) headers = {'X-Auth-Token': '', 'User-Agent': 'python-tackerclient'} expected_headers = {'X-Auth-Token': TOKEN, 'User-Agent': 'python-tackerclient'} agent_header = {'User-Agent': 'python-tackerclient'} class CLITestAuthNoAuth(testtools.TestCase): def setUp(self): """Prepare the test environment.""" super(CLITestAuthNoAuth, self).setUp() self.client = client.HTTPClient(username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, endpoint_url=ENDPOINT_URL, auth_strategy=NOAUTH, region_name=REGION) self.addCleanup(mock.patch.stopall) @mock.patch('tackerclient.client.HTTPClient.request') def test_get_noauth(self, mock_request): mock_request.return_value = (resp_200, '') self.client.do_request('/resource', 'GET', headers=headers) mock_request.assert_called_once_with( ENDPOINT_URL + '/resource', 'GET', headers=headers) self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) class CLITestAuthKeystone(testtools.TestCase): # Auth Body expected auth_body = ('{"auth": {"tenantName": "testtenant", ' '"passwordCredentials": ' '{"username": "testuser", "password": "password"}}}') def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystone, self).setUp() self.client = client.HTTPClient(username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) self.addCleanup(mock.patch.stopall) 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. """ client_ = client.HTTPClient(username=USERNAME, tenant_name=TENANT_NAME, token=TOKEN, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) expected = {'auth_token': TOKEN, 'auth_tenant_id': None, 'auth_user_id': None, 'endpoint_url': self.client.endpoint_url} self.assertEqual(client_.get_auth_info(), expected) @mock.patch('tackerclient.client.HTTPClient.request') def test_get_token(self, mock_request): mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT)) self.client.do_request('/resource', 'GET') mock_request.assert_called_with( ENDPOINT_URL + '/resource', 'GET', headers=expected_headers) self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) self.assertEqual(self.client.auth_token, TOKEN) @mock.patch('tackerclient.client.HTTPClient.request') def test_refresh_token(self, mock_request): self.client.auth_token = TOKEN self.client.endpoint_url = ENDPOINT_URL # If a token is expired, tacker server retruns 401 mock_request.return_value = (resp_401, '') self.assertRaises(exceptions.Unauthorized, self.client.do_request, '/resource', 'GET') mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT)) self.client.do_request('/resource', 'GET') mock_request.assert_called_with( ENDPOINT_URL + '/resource', 'GET', headers=expected_headers) @mock.patch('tackerclient.client.HTTPClient.request') def test_refresh_token_no_auth_url(self, mock_request): self.client.auth_url = None self.client.auth_token = TOKEN self.client.endpoint_url = ENDPOINT_URL # If a token is expired, tacker server retruns 401 mock_request.return_value = (resp_401, '') self.assertRaises(exceptions.NoAuthURLProvided, self.client.do_request, '/resource', 'GET') expected_url = ENDPOINT_URL + '/resource' mock_request.assert_called_with(expected_url, 'GET', headers=expected_headers) def test_get_endpoint_url_with_invalid_auth_url(self): # Handle the case when auth_url is not provided self.client.auth_url = None self.assertRaises(exceptions.NoAuthURLProvided, self.client._get_endpoint_url) @mock.patch('tackerclient.client.HTTPClient.request') def test_get_endpoint_url(self, mock_request): self.client.auth_token = TOKEN mock_request.return_value = (resp_200, json.dumps(ENDPOINTS_RESULT)) self.client.do_request('/resource', 'GET') mock_request.assert_called_with( ENDPOINT_URL + '/resource', 'GET', headers=expected_headers) mock_request.return_value = (resp_200, '') self.client.do_request('/resource', 'GET', headers=headers) mock_request.assert_called_with( ENDPOINT_URL + '/resource', 'GET', headers=headers) @mock.patch('tackerclient.client.HTTPClient.request') def test_use_given_endpoint_url(self, mock_request): self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_url=ENDPOINT_OVERRIDE) self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) self.client.auth_token = TOKEN mock_request.return_value = (resp_200, '') self.client.do_request('/resource', 'GET', headers=headers) mock_request.assert_called_with( ENDPOINT_OVERRIDE + '/resource', 'GET', headers=headers) self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) @mock.patch('tackerclient.client.HTTPClient.request') def test_get_endpoint_url_other(self, mock_request): self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL') self.client.auth_token = TOKEN mock_request.return_value = (resp_200, json.dumps(ENDPOINTS_RESULT)) self.assertRaises(exceptions.EndpointTypeNotFound, self.client.do_request, '/resource', 'GET') expected_url = AUTH_URL + '/tokens/%s/endpoints' % TOKEN headers = {'User-Agent': 'python-tackerclient'} mock_request.assert_called_with(expected_url, 'GET', headers=headers) @mock.patch('tackerclient.client.HTTPClient.request') def test_get_endpoint_url_failed(self, mock_request): self.client.auth_token = TOKEN self.client.auth_url = AUTH_URL + '/tokens/%s/endpoints' % TOKEN mock_request.return_value = (resp_401, '') self.assertRaises(exceptions.Unauthorized, self.client.do_request, '/resource', 'GET') def test_endpoint_type(self): resources = copy.deepcopy(KS_TOKEN_RESULT) endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] endpoints['internalURL'] = 'internal' endpoints['adminURL'] = 'admin' endpoints['publicURL'] = 'public' # Test default behavior is to choose public. self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) self.client._extract_service_catalog(resources) self.assertEqual(self.client.endpoint_url, 'public') # Test admin url self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL') self.client._extract_service_catalog(resources) self.assertEqual(self.client.endpoint_url, 'admin') # Test public url self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL') self.client._extract_service_catalog(resources) self.assertEqual(self.client.endpoint_url, 'public') # Test internal url self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL') self.client._extract_service_catalog(resources) self.assertEqual(self.client.endpoint_url, 'internal') # Test url that isn't found in the service catalog self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL') self.assertRaises(k_exceptions.EndpointNotFound, self.client._extract_service_catalog, resources) @mock.patch('tackerclient.client.HTTPClient.request') @mock.patch('tackerclient.common.utils.http_log_req') def test_strip_credentials_from_log(self, mock_http_log_req, mock_request,): body = ('{"auth": {"tenantId": "testtenant_id",' '"passwordCredentials": {"password": "password",' '"userId": "testuser_id"}}}') expected_body = ('{"auth": {"tenantId": "testtenant_id",' '"REDACTEDCredentials": {"REDACTED": "REDACTED",' '"userId": "testuser_id"}}}') _headers = {'headers': expected_headers, 'body': expected_body} mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT)) self.client.do_request('/resource', 'GET', body=body) args, kwargs = mock_http_log_req.call_args # Check that credentials are stripped while logging. self.assertEqual(_headers, args[2]) class CLITestAuthKeystoneWithId(CLITestAuthKeystone): # Auth Body expected auth_body = ('{"auth": {"passwordCredentials": ' '{"password": "password", "userId": "testuser_id"}, ' '"tenantId": "testtenant_id"}}') def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithId, self).setUp() self.client = client.HTTPClient(user_id=USER_ID, tenant_id=TENANT_ID, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone): # Auth Body expected auth_body = ('{"auth": {"passwordCredentials": ' '{"password": "password", "userId": "testuser_id"}, ' '"tenantId": "testtenant_id"}}') def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithIdandName, self).setUp() self.client = client.HTTPClient(username=USERNAME, user_id=USER_ID, tenant_id=TENANT_ID, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) python-tackerclient-0.11.0/tackerclient/tests/unit/test_validators.py0000666000175100017510000001000313234643056026160 0ustar zuulzuul00000000000000# Copyright 2014 NEC Corporation # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools from tackerclient.common import exceptions from tackerclient.common import validators class FakeParsedArgs(object): pass class ValidatorTest(testtools.TestCase): def _test_validate_int(self, attr_val, attr_name='attr1', min_value=1, max_value=10): obj = FakeParsedArgs() setattr(obj, attr_name, attr_val) ret = validators.validate_int_range(obj, attr_name, min_value, max_value) # Come here only if there is no exception. self.assertIsNone(ret) def _test_validate_int_error(self, attr_val, expected_msg, attr_name='attr1', expected_exc=None, min_value=1, max_value=10): if expected_exc is None: expected_exc = exceptions.CommandError e = self.assertRaises(expected_exc, self._test_validate_int, attr_val, attr_name, min_value, max_value) self.assertEqual(expected_msg, str(e)) def test_validate_int_min_max(self): self._test_validate_int(1) self._test_validate_int(10) self._test_validate_int('1') self._test_validate_int('10') self._test_validate_int('0x0a') self._test_validate_int_error( 0, 'attr1 "0" should be an integer [1:10].') self._test_validate_int_error( 11, 'attr1 "11" should be an integer [1:10].') self._test_validate_int_error( '0x10', 'attr1 "0x10" should be an integer [1:10].') def test_validate_int_min_only(self): self._test_validate_int(1, max_value=None) self._test_validate_int(10, max_value=None) self._test_validate_int(11, max_value=None) self._test_validate_int_error( 0, 'attr1 "0" should be an integer greater than or equal to 1.', max_value=None) def test_validate_int_max_only(self): self._test_validate_int(0, min_value=None) self._test_validate_int(1, min_value=None) self._test_validate_int(10, min_value=None) self._test_validate_int_error( 11, 'attr1 "11" should be an integer smaller than or equal to 10.', min_value=None) def test_validate_int_no_limit(self): self._test_validate_int(0, min_value=None, max_value=None) self._test_validate_int(1, min_value=None, max_value=None) self._test_validate_int(10, min_value=None, max_value=None) self._test_validate_int(11, min_value=None, max_value=None) self._test_validate_int_error( 'abc', 'attr1 "abc" should be an integer.', min_value=None, max_value=None) def _test_validate_subnet(self, attr_val, attr_name='attr1'): obj = FakeParsedArgs() setattr(obj, attr_name, attr_val) ret = validators.validate_ip_subnet(obj, attr_name) # Come here only if there is no exception. self.assertIsNone(ret) def test_validate_ip_subnet(self): self._test_validate_subnet('192.168.2.0/24') self._test_validate_subnet('192.168.2.3/20') self._test_validate_subnet('192.168.2.1') e = self.assertRaises(exceptions.CommandError, self._test_validate_subnet, '192.168.2.256') self.assertEqual('attr1 "192.168.2.256" is not a valid CIDR.', str(e)) python-tackerclient-0.11.0/tackerclient/tests/unit/test_ssl.py0000666000175100017510000000603513234643056024623 0ustar zuulzuul00000000000000# Copyright (C) 2013 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 fixtures from keystoneclient import session import mock import requests import testtools from tackerclient import client from tackerclient.common import clientmanager from tackerclient.common import exceptions from tackerclient import shell as openstack_shell AUTH_TOKEN = 'test_token' END_URL = 'test_url' METHOD = 'GET' URL = 'http://test.test:1234/v1.0/' CA_CERT = '/tmp/test/path' DEFAULT_API_VERSION = '1.0' class TestSSL(testtools.TestCase): def setUp(self): super(TestSSL, self).setUp() self.useFixture(fixtures.EnvironmentVariable('OS_TOKEN', AUTH_TOKEN)) self.useFixture(fixtures.EnvironmentVariable('OS_URL', END_URL)) self.addCleanup(mock.patch.stopall) def _test_verify_client_manager(self, cacert): with mock.patch.object(session, 'Session'), \ mock.patch.object(clientmanager, 'ClientManager') as mock_cmgr: mock_cmgr.return_value = 0 shell = openstack_shell.TackerShell(DEFAULT_API_VERSION) shell.options = mock.Mock() auth_session = shell._get_keystone_session() shell.run(cacert) mock_cmgr.assert_called_with( api_version={'nfv-orchestration': '1.0'}, auth=auth_session.auth, auth_strategy='keystone', auth_url='', ca_cert=CA_CERT, endpoint_type='publicURL', insecure=False, log_credentials=True, password='', raise_errors=False, region_name='', retries=0, service_type='nfv-orchestration', session=auth_session, tenant_id='', tenant_name='', timeout=None, token='test_token', url='test_url', user_id='', username='') def test_ca_cert_passed(self): cacert = ['--os-cacert', CA_CERT] self._test_verify_client_manager(cacert) def test_ca_cert_passed_as_env_var(self): self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT)) self._test_verify_client_manager([]) @mock.patch.object(client.HTTPClient, 'request') def test_proper_exception_is_raised_when_cert_validation_fails(self, mock_req): http = client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) mock_req.side_effect = requests.exceptions.SSLError() self.assertRaises( exceptions.SslCertificateValidationError, http._cs_request, URL, METHOD ) python-tackerclient-0.11.0/tackerclient/tests/unit/test_cli10_extensions.py0000666000175100017510000000362313234643056027211 0ustar zuulzuul00000000000000# Copyright 2013 NEC Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from tackerclient.tacker.v1_0.extension import ListExt from tackerclient.tacker.v1_0.extension import ShowExt from tackerclient.tests.unit.test_cli10 import CLITestV10Base from tackerclient.tests.unit.test_cli10 import MyApp class CLITestV10Extension(CLITestV10Base): id_field = 'alias' def test_list_extensions(self): resources = 'extensions' cmd = ListExt(MyApp(sys.stdout), None) contents = [{'alias': 'ext1', 'name': 'name1', 'other': 'other1'}, {'alias': 'ext2', 'name': 'name2', 'other': 'other2'}] ret = self._test_list_resources(resources, cmd, response_contents=contents) ret_words = set(ret.split()) # Check only the default columns are shown. self.assertIn('name', ret_words) self.assertIn('alias', ret_words) self.assertNotIn('other', ret_words) def test_show_extension(self): # -F option does not work for ext-show at the moment, so -F option # is not passed in the commandline args as other tests do. resource = 'extension' cmd = ShowExt(MyApp(sys.stdout), None) args = [self.test_id] ext_alias = self.test_id self._test_show_resource(resource, cmd, ext_alias, args, fields=[]) python-tackerclient-0.11.0/tackerclient/tests/unit/test_utils.py0000666000175100017510000001174213234643056025163 0ustar zuulzuul00000000000000# Copyright (C) 2013 Yahoo! 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 testtools from tackerclient.common import exceptions from tackerclient.common import utils class TestUtils(testtools.TestCase): def test_string_to_bool_true(self): self.assertTrue(utils.str2bool('true')) def test_string_to_bool_false(self): self.assertFalse(utils.str2bool('false')) def test_string_to_bool_None(self): self.assertIsNone(utils.str2bool(None)) def test_string_to_dictionary(self): input_str = 'key1=value1,key2=value2' expected = {'key1': 'value1', 'key2': 'value2'} self.assertEqual(expected, utils.str2dict(input_str)) def test_none_string_to_dictionary(self): input_str = '' expected = {} self.assertEqual(expected, utils.str2dict(input_str)) input_str = None expected = {} self.assertEqual(expected, utils.str2dict(input_str)) def test_get_dict_item_properties(self): item = {'name': 'test_name', 'id': 'test_id'} fields = ('name', 'id') actual = utils.get_item_properties(item=item, fields=fields) self.assertEqual(('test_name', 'test_id'), actual) def test_get_object_item_properties_mixed_case_fields(self): class Fake(object): def __init__(self): self.id = 'test_id' self.name = 'test_name' self.test_user = 'test' fields = ('name', 'id', 'test user') mixed_fields = ('test user', 'ID') item = Fake() actual = utils.get_item_properties(item, fields, mixed_fields) self.assertEqual(('test_name', 'test_id', 'test'), actual) def test_get_object_item_desired_fields_differ_from_item(self): class Fake(object): def __init__(self): self.id = 'test_id_1' self.name = 'test_name' self.test_user = 'test' fields = ('name', 'id', 'test user') item = Fake() actual = utils.get_item_properties(item, fields) self.assertNotEqual(('test_name', 'test_id', 'test'), actual) def test_get_object_item_desired_fields_is_empty(self): class Fake(object): def __init__(self): self.id = 'test_id_1' self.name = 'test_name' self.test_user = 'test' fields = [] item = Fake() actual = utils.get_item_properties(item, fields) self.assertEqual((), actual) def test_get_object_item_with_formatters(self): class Fake(object): def __init__(self): self.id = 'test_id' self.name = 'test_name' self.test_user = 'test' class FakeCallable(object): def __call__(self, *args, **kwargs): return 'pass' fields = ('name', 'id', 'test user', 'is_public') formatters = {'is_public': FakeCallable()} item = Fake() act = utils.get_item_properties(item, fields, formatters=formatters) self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act) class ImportClassTestCase(testtools.TestCase): def test_get_client_class_invalid_version(self): self.assertRaises( exceptions.UnsupportedVersion, utils.get_client_class, 'image', '2', {'image': '2'}) class ContainsKeyValue(object): """Checks whether a key/value pair is in a dict parameter. The ContainsKeyValue class is a helper for mock.assert_*() method. It enables strict check than the built in mock.ANY helper, and is the equivalent of the mox.ContainsKeyValue() function from the legacy mox library Example usage could be: mock_some_method.assert_called_once_with( "hello", ContainsKeyValue('foo', bar), mock.ANY, "world", ContainsKeyValue('hello', world)) """ def __init__(self, wantkey, wantvalue): self.wantkey = wantkey self.wantvalue = wantvalue def __eq__(self, other): try: return other[self.wantkey] == self.wantvalue except (KeyError, TypeError): return False def __ne__(self, other): try: return other[self.wantkey] != self.wantvalue except (KeyError, TypeError): return True def __repr__(self): return "" python-tackerclient-0.11.0/tackerclient/tests/__init__.py0000666000175100017510000000000013234643056023525 0ustar zuulzuul00000000000000python-tackerclient-0.11.0/tackerclient/shell.py0000666000175100017510000010062613234643056021752 0ustar zuulzuul00000000000000# Copyright 2012 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. # """ Command-line interface to the Tacker APIs """ from __future__ import print_function import argparse import getpass import inspect import itertools import logging import os 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 import session from oslo_utils import encodeutils import six.moves.urllib.parse as urlparse from cliff import app from cliff import commandmanager from tackerclient.common import clientmanager from tackerclient.common import command as openstack_command from tackerclient.common import exceptions as exc from tackerclient.common import extension as client_extension from tackerclient.common import utils from tackerclient.i18n import _ from tackerclient.tacker.v1_0.events import events from tackerclient.tacker.v1_0 import extension from tackerclient.tacker.v1_0.nfvo import ns from tackerclient.tacker.v1_0.nfvo import nsd from tackerclient.tacker.v1_0.nfvo import vim from tackerclient.tacker.v1_0.nfvo import vnffg from tackerclient.tacker.v1_0.nfvo import vnffgd from tackerclient.tacker.v1_0.vnfm import vnf from tackerclient.tacker.v1_0.vnfm import vnfd from tackerclient.version import __version__ VERSION = '1.0' TACKER_API_VERSION = '1.0' def run_command(cmd, cmd_parser, sub_argv): _argv = sub_argv index = -1 values_specs = [] if '--' in sub_argv: index = sub_argv.index('--') _argv = sub_argv[:index] values_specs = sub_argv[index:] known_args, _values_specs = cmd_parser.parse_known_args(_argv) cmd.values_specs = (index == -1 and _values_specs or values_specs) return cmd.run(known_args) def env(*_vars, **kwargs): """Search for the first defined of possibly many env vars. Returns the first environment variable defined in vars, or returns the default defined in kwargs. """ for v in _vars: value = os.environ.get(v, None) if value: return value return kwargs.get('default', '') def check_non_negative_int(value): try: value = int(value) except ValueError: raise argparse.ArgumentTypeError(_("invalid int value: %r") % value) if value < 0: raise argparse.ArgumentTypeError(_("input value %d is negative") % value) return value class BashCompletionCommand(openstack_command.OpenStackCommand): """Prints all of the commands and options for bash-completion.""" resource = "bash_completion" COMMAND_V1 = { 'bash-completion': BashCompletionCommand, 'ext-list': extension.ListExt, 'ext-show': extension.ShowExt, # 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, 'vnf-delete': vnf.DeleteVNF, 'vnf-list': vnf.ListVNF, 'vnf-show': vnf.ShowVNF, 'vnf-scale': vnf.ScaleVNF, 'vnf-resource-list': vnf.ListVNFResources, # '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, 'events-list': events.ListResourceEvents, 'event-show': events.ShowEvent, 'vnf-events-list': events.ListVNFEvents, 'vim-events-list': events.ListVIMEvents, 'vnfd-events-list': events.ListVNFDEvents, 'vnffgd-create': vnffgd.CreateVNFFGD, 'vnffgd-delete': vnffgd.DeleteVNFFGD, 'vnffgd-list': vnffgd.ListVNFFGD, 'vnffgd-show': vnffgd.ShowVNFFGD, 'vnffgd-template-show': vnffgd.ShowTemplateVNFFGD, 'vnffg-create': vnffg.CreateVNFFG, 'vnffg-delete': vnffg.DeleteVNFFG, 'vnffg-list': vnffg.ListVNFFG, 'vnffg-show': vnffg.ShowVNFFG, 'vnffg-update': vnffg.UpdateVNFFG, 'nfp-list': vnffg.ListNFP, 'nfp-show': vnffg.ShowNFP, 'chain-list': vnffg.ListSFC, 'chain-show': vnffg.ShowSFC, 'classifier-list': vnffg.ListFC, 'classifier-show': vnffg.ShowFC, 'nsd-create': nsd.CreateNSD, 'nsd-list': nsd.ListNSD, 'nsd-delete': nsd.DeleteNSD, 'nsd-show': nsd.ShowNSD, 'nsd-template-show': nsd.ShowTemplateNSD, 'ns-create': ns.CreateNS, 'ns-list': ns.ListNS, 'ns-delete': ns.DeleteNS, 'ns-show': ns.ShowNS, } COMMANDS = {'1.0': COMMAND_V1} class HelpAction(argparse.Action): """Provides a custom action for the -h and --help options. The commands are determined by checking the CommandManager instance, passed in as the "default" value for the action. :returns: a list of the commands """ def __call__(self, parser, namespace, values, option_string=None): outputs = [] max_len = 0 app = self.default parser.print_help(app.stdout) app.stdout.write(_('\nCommands for API v%s:\n') % app.api_version) command_manager = app.command_manager for name, ep in sorted(command_manager): factory = ep.load() cmd = factory(self, None) one_liner = cmd.get_description().split('\n')[0] outputs.append((name, one_liner)) max_len = max(len(name), max_len) for (name, one_liner) in outputs: app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner)) sys.exit(0) class TackerShell(app.App): # verbose logging levels WARNING_LEVEL = 0 INFO_LEVEL = 1 DEBUG_LEVEL = 2 CONSOLE_MESSAGE_FORMAT = '%(message)s' DEBUG_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' log = logging.getLogger(__name__) def __init__(self, apiversion): super(TackerShell, self).__init__( description=__doc__.strip(), version=VERSION, command_manager=commandmanager.CommandManager('tacker.cli'), ) self.commands = COMMANDS for k, v in self.commands[apiversion].items(): self.command_manager.add_command(k, v) self._register_extensions(VERSION) # Pop the 'complete' to correct the outputs of 'tacker help'. self.command_manager.commands.pop('complete') # This is instantiated in initialize_app() only when using # password flow auth self.auth_client = None self.api_version = apiversion def build_option_parser(self, description, version): """Return an argparse option parser for this application. Subclasses may override this method to extend the parser with more global options. :param description: full description of the application :paramtype description: str :param version: version number for the application :paramtype version: str """ parser = argparse.ArgumentParser( description=description, add_help=False, ) parser.add_argument( '--version', action='version', version=__version__, ) parser.add_argument( '-v', '--verbose', '--debug', action='count', dest='verbose_level', default=self.DEFAULT_VERBOSE_LEVEL, help=_('Increase verbosity of output and show tracebacks on' ' errors. You can repeat this option.')) parser.add_argument( '-q', '--quiet', action='store_const', dest='verbose_level', const=0, help=_('Suppress output except warnings and errors.')) parser.add_argument( '-h', '--help', action=HelpAction, nargs=0, default=self, # tricky help=_("Show this help message and exit.")) parser.add_argument( '-r', '--retries', metavar="NUM", type=check_non_negative_int, default=0, help=_("How many times the request to the Tacker server should " "be retried if it fails.")) # FIXME(bklei): this method should come from python-keystoneclient self._append_global_identity_args(parser) return parser def _append_global_identity_args(self, parser): # FIXME(bklei): these are global identity (Keystone) arguments which # should be consistent and shared by all service clients. Therefore, # they should be provided by python-keystoneclient. We will need to # refactor this code once this functionality is available in # python-keystoneclient. # # Note: At that time we'll need to decide if we can just abandon # the deprecated args (--service-type and --endpoint-type). parser.add_argument( '--os-service-type', metavar='', default=env('OS_TACKER_SERVICE_TYPE', default='nfv-orchestration'), help=_('Defaults to env[OS_TACKER_SERVICE_TYPE] or \ nfv-orchestration.')) parser.add_argument( '--os-endpoint-type', metavar='', default=env('OS_ENDPOINT_TYPE', default='publicURL'), help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.')) # FIXME(bklei): --service-type is deprecated but kept in for # backward compatibility. parser.add_argument( '--service-type', metavar='', default=env('OS_TACKER_SERVICE_TYPE', default='nfv-orchestration'), help=_('DEPRECATED! Use --os-service-type.')) # FIXME(bklei): --endpoint-type is deprecated but kept in for # backward compatibility. parser.add_argument( '--endpoint-type', metavar='', default=env('OS_ENDPOINT_TYPE', default='publicURL'), help=_('DEPRECATED! Use --os-endpoint-type.')) parser.add_argument( '--os-auth-strategy', metavar='', default=env('OS_AUTH_STRATEGY', default='keystone'), help=_('DEPRECATED! Only keystone is supported.')) parser.add_argument( '--os_auth_strategy', help=argparse.SUPPRESS) parser.add_argument( '--os-auth-url', metavar='', default=env('OS_AUTH_URL'), help=_('Authentication URL, defaults to env[OS_AUTH_URL].')) parser.add_argument( '--os_auth_url', help=argparse.SUPPRESS) project_name_group = parser.add_mutually_exclusive_group() project_name_group.add_argument( '--os-tenant-name', metavar='', default=env('OS_TENANT_NAME'), help=_('Authentication tenant name, defaults to ' 'env[OS_TENANT_NAME].')) project_name_group.add_argument( '--os-project-name', metavar='', default=utils.env('OS_PROJECT_NAME'), help=_('Another way to specify tenant name. ' 'This option is mutually exclusive with ' ' --os-tenant-name. ' 'Defaults to env[OS_PROJECT_NAME].')) parser.add_argument( '--os_tenant_name', help=argparse.SUPPRESS) project_id_group = parser.add_mutually_exclusive_group() project_id_group.add_argument( '--os-tenant-id', metavar='', default=env('OS_TENANT_ID'), help=_('Authentication tenant ID, defaults to ' 'env[OS_TENANT_ID].')) project_id_group.add_argument( '--os-project-id', metavar='', default=utils.env('OS_PROJECT_ID'), help=_('Another way to specify tenant ID. ' 'This option is mutually exclusive with ' ' --os-tenant-id. ' 'Defaults to env[OS_PROJECT_ID].')) parser.add_argument( '--os-username', metavar='', default=utils.env('OS_USERNAME'), help=_('Authentication username, defaults to env[OS_USERNAME].')) parser.add_argument( '--os_username', help=argparse.SUPPRESS) parser.add_argument( '--os-user-id', metavar='', default=env('OS_USER_ID'), help=_('Authentication user ID (Env: OS_USER_ID)')) parser.add_argument( '--os_user_id', help=argparse.SUPPRESS) parser.add_argument( '--os-user-domain-id', metavar='', default=utils.env('OS_USER_DOMAIN_ID'), help=_('OpenStack user domain ID. ' 'Defaults to env[OS_USER_DOMAIN_ID].')) parser.add_argument( '--os_user_domain_id', help=argparse.SUPPRESS) parser.add_argument( '--os-user-domain-name', metavar='', default=utils.env('OS_USER_DOMAIN_NAME'), help=_('OpenStack user domain name. ' 'Defaults to env[OS_USER_DOMAIN_NAME].')) parser.add_argument( '--os_user_domain_name', help=argparse.SUPPRESS) parser.add_argument( '--os_project_id', help=argparse.SUPPRESS) parser.add_argument( '--os_project_name', help=argparse.SUPPRESS) parser.add_argument( '--os-project-domain-id', metavar='', default=utils.env('OS_PROJECT_DOMAIN_ID'), help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) parser.add_argument( '--os-project-domain-name', metavar='', default=utils.env('OS_PROJECT_DOMAIN_NAME'), help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].')) parser.add_argument( '--os-cert', metavar='', default=utils.env('OS_CERT'), help=_("Path of certificate file to use in SSL " "connection. This file can optionally be " "prepended with the private key. Defaults " "to env[OS_CERT].")) parser.add_argument( '--os-cacert', metavar='', default=env('OS_CACERT', default=None), help=_("Specify a CA bundle file to use in " "verifying a TLS (https) server certificate. " "Defaults to env[OS_CACERT].")) parser.add_argument( '--os-key', metavar='', default=utils.env('OS_KEY'), help=_("Path of client key to use in SSL " "connection. This option is not necessary " "if your key is prepended to your certificate " "file. Defaults to env[OS_KEY].")) parser.add_argument( '--os-password', metavar='', default=utils.env('OS_PASSWORD'), help=_('Authentication password, defaults to env[OS_PASSWORD].')) parser.add_argument( '--os_password', help=argparse.SUPPRESS) parser.add_argument( '--os-region-name', metavar='', default=env('OS_REGION_NAME'), help=_('Authentication region name, defaults to ' 'env[OS_REGION_NAME].')) parser.add_argument( '--os_region_name', help=argparse.SUPPRESS) parser.add_argument( '--os-token', metavar='', default=env('OS_TOKEN'), help=_('Authentication token, defaults to env[OS_TOKEN].')) parser.add_argument( '--os_token', help=argparse.SUPPRESS) parser.add_argument( '--http-timeout', metavar='', default=env('OS_NETWORK_TIMEOUT', default=None), type=float, help=_('Timeout in seconds to wait for an HTTP response. Defaults ' 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) parser.add_argument( '--os-url', metavar='', default=env('OS_URL'), help=_('Defaults to env[OS_URL].')) parser.add_argument( '--os_url', help=argparse.SUPPRESS) parser.add_argument( '--insecure', action='store_true', default=env('TACKERCLIENT_INSECURE', default=False), help=_("Explicitly allow tackerclient to perform \"insecure\" " "SSL (https) requests. The server's certificate will " "not be verified against any certificate authorities. " "This option should be used with caution.")) def _bash_completion(self): """Prints all of the commands and options for bash-completion.""" commands = set() options = set() for option, _action in self.parser._option_string_actions.items(): options.add(option) for command_name, command in self.command_manager: commands.add(command_name) cmd_factory = command.load() cmd = cmd_factory(self, None) cmd_parser = cmd.get_parser('') for option, _action in cmd_parser._option_string_actions.items(): options.add(option) print(' '.join(commands | options)) def _register_extensions(self, version): for name, module in itertools.chain( client_extension._discover_via_entry_points()): self._extend_shell_commands(module, version) def _extend_shell_commands(self, module, version): classes = inspect.getmembers(module, inspect.isclass) for cls_name, cls in classes: if (issubclass(cls, client_extension.TackerClientExtension) and hasattr(cls, 'shell_command')): cmd = cls.shell_command if hasattr(cls, 'versions'): if version not in cls.versions: continue try: self.command_manager.add_command(cmd, cls) self.commands[version][cmd] = cls except TypeError: pass def run(self, argv): """Equivalent to the main program for the application. :param argv: input arguments and options :paramtype argv: list of str """ try: index = 0 command_pos = -1 help_pos = -1 help_command_pos = -1 for arg in argv: if arg == 'bash-completion' and help_command_pos == -1: self._bash_completion() return 0 if arg in self.commands[self.api_version]: if command_pos == -1: command_pos = index elif arg in ('-h', '--help'): if help_pos == -1: help_pos = index elif arg == 'help': if help_command_pos == -1: help_command_pos = index index = index + 1 if command_pos > -1 and help_pos > command_pos: argv = ['help', argv[command_pos]] if help_command_pos > -1 and command_pos == -1: argv[help_command_pos] = '--help' self.options, remainder = self.parser.parse_known_args(argv) self.configure_logging() self.interactive_mode = not remainder self.initialize_app(remainder) except Exception as err: if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception(err) raise else: self.log.error(err) return 1 if self.interactive_mode: _argv = [sys.argv[0]] sys.argv = _argv return self.interact() return self.run_subcommand(remainder) def run_subcommand(self, argv): subcommand = self.command_manager.find_command(argv) cmd_factory, cmd_name, sub_argv = subcommand cmd = cmd_factory(self, self.options) try: self.prepare_to_run_command(cmd) full_name = (cmd_name if self.interactive_mode else ' '.join([self.NAME, cmd_name]) ) cmd_parser = cmd.get_parser(full_name) return run_command(cmd, cmd_parser, sub_argv) except Exception as e: if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception("%s", e) raise self.log.error("%s", e) return 1 def authenticate_user(self): """Authentication validation. Make sure the user has provided all of the authentication info we need. """ if self.options.os_auth_strategy == 'keystone': if self.options.os_token or self.options.os_url: # Token flow auth takes priority if not self.options.os_token: raise exc.CommandError( _("You must provide a token via" " either --os-token or env[OS_TOKEN]" " when providing a service URL")) if not self.options.os_url: raise exc.CommandError( _("You must provide a service URL via" " either --os-url or env[OS_URL]" " when providing a token")) else: # Validate password flow auth project_info = (self.options.os_tenant_name or self.options.os_tenant_id or (self.options.os_project_name and (self.options.os_project_domain_name or self.options.os_project_domain_id)) or self.options.os_project_id) if (not self.options.os_username and not self.options.os_user_id): raise exc.CommandError( _("You must provide a username or user ID via" " --os-username, env[OS_USERNAME] or" " --os-user-id, env[OS_USER_ID]")) if not self.options.os_password: # No password, If we've got a tty, try prompting for it if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): # Check for Ctl-D try: self.options.os_password = getpass.getpass( 'OS Password: ') except EOFError: pass # No password because we didn't have a tty or the # user Ctl-D when prompted. if not self.options.os_password: raise exc.CommandError( _("You must provide a password via" " either --os-password or env[OS_PASSWORD]")) if (not project_info): # tenent is deprecated in Keystone v3. Use the latest # terminology instead. raise exc.CommandError( _("You must provide a project_id or project_name (" "with project_domain_name or project_domain_id) " "via " " --os-project-id (env[OS_PROJECT_ID])" " --os-project-name (env[OS_PROJECT_NAME])," " --os-project-domain-id " "(env[OS_PROJECT_DOMAIN_ID])" " --os-project-domain-name " "(env[OS_PROJECT_DOMAIN_NAME])")) if not self.options.os_auth_url: raise exc.CommandError( _("You must provide an auth url via" " either --os-auth-url or via env[OS_AUTH_URL]")) auth_session = self._get_keystone_session() auth = auth_session.auth else: # not keystone if not self.options.os_url: raise exc.CommandError( _("You must provide a service URL via" " either --os-url or env[OS_URL]")) auth_session = None auth = None self.client_manager = clientmanager.ClientManager( token=self.options.os_token, url=self.options.os_url, auth_url=self.options.os_auth_url, tenant_name=self.options.os_tenant_name, tenant_id=self.options.os_tenant_id, username=self.options.os_username, user_id=self.options.os_user_id, password=self.options.os_password, region_name=self.options.os_region_name, api_version=self.api_version, auth_strategy=self.options.os_auth_strategy, # FIXME (bklei) honor deprecated service_type and # endpoint type until they are removed service_type=self.options.os_service_type or self.options.service_type, endpoint_type=self.options.os_endpoint_type or self.endpoint_type, insecure=self.options.insecure, ca_cert=self.options.os_cacert, timeout=self.options.http_timeout, retries=self.options.retries, raise_errors=False, session=auth_session, auth=auth, log_credentials=True) return def initialize_app(self, argv): """Global app init bits: * set up API versions * validate authentication info """ super(TackerShell, self).initialize_app(argv) self.api_version = {'nfv-orchestration': self.api_version} # If the user is not asking for help, make sure they # have given us auth. cmd_name = None if argv: cmd_info = self.command_manager.find_command(argv) cmd_factory, cmd_name, sub_argv = cmd_info if self.interactive_mode or cmd_name != 'help': self.authenticate_user() def configure_logging(self): """Create logging handlers for any log output.""" root_logger = logging.getLogger('') # Set up logging to a file root_logger.setLevel(logging.DEBUG) # Send higher-level messages to the console via stderr console = logging.StreamHandler(self.stderr) console_level = {self.WARNING_LEVEL: logging.WARNING, self.INFO_LEVEL: logging.INFO, self.DEBUG_LEVEL: logging.DEBUG, }.get(self.options.verbose_level, logging.DEBUG) # The default log level is INFO, in this situation, set the # log level of the console to WARNING, to avoid displaying # useless messages. This equals using "--quiet" if console_level == logging.INFO: console.setLevel(logging.WARNING) else: console.setLevel(console_level) if logging.DEBUG == console_level: formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) else: formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) logging.getLogger('iso8601.iso8601').setLevel(logging.WARNING) logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) console.setFormatter(formatter) root_logger.addHandler(console) return def get_v2_auth(self, v2_auth_url): return v2_auth.Password( v2_auth_url, username=self.options.os_username, password=self.options.os_password, tenant_id=self.options.os_tenant_id, tenant_name=self.options.os_tenant_name) def get_v3_auth(self, v3_auth_url): project_id = self.options.os_project_id or self.options.os_tenant_id project_name = (self.options.os_project_name or self.options.os_tenant_name) return v3_auth.Password( v3_auth_url, username=self.options.os_username, password=self.options.os_password, user_id=self.options.os_user_id, user_domain_name=self.options.os_user_domain_name, user_domain_id=self.options.os_user_domain_id, project_id=project_id, project_name=project_name, project_domain_name=self.options.os_project_domain_name, project_domain_id=self.options.os_project_domain_id ) def _discover_auth_versions(self, session, auth_url): # discover the API versions the server is supporting base on the # given URL try: ks_discover = discover.Discover(session=session, auth_url=auth_url) return (ks_discover.url_for('2.0'), ks_discover.url_for('3.0')) except ks_exc.ClientException: # Identity service may not support discover API version. # Lets try to figure out the API version from the original URL. url_parts = urlparse.urlparse(auth_url) (scheme, netloc, path, params, query, fragment) = url_parts path = path.lower() if path.startswith('/v3'): return (None, auth_url) elif path.startswith('/v2'): return (auth_url, None) else: # not enough information to determine the auth version msg = _('Unable to determine the Keystone version ' 'to authenticate with using the given ' 'auth_url. Identity service may not support API ' 'version discovery. Please provide a versioned ' 'auth_url instead.') raise exc.CommandError(msg) def _get_keystone_session(self): # first create a Keystone session cacert = self.options.os_cacert or None cert = self.options.os_cert or None key = self.options.os_key or None insecure = self.options.insecure or False ks_session = session.Session.construct(dict(cacert=cacert, cert=cert, key=key, insecure=insecure)) # discover the supported keystone versions using the given url (v2_auth_url, v3_auth_url) = self._discover_auth_versions( session=ks_session, auth_url=self.options.os_auth_url) # Determine which authentication plugin to use. First inspect the # auth_url to see the supported version. If both v3 and v2 are # supported, then use the highest version if possible. user_domain_name = self.options.os_user_domain_name or None user_domain_id = self.options.os_user_domain_id or None project_domain_name = self.options.os_project_domain_name or None project_domain_id = self.options.os_project_domain_id or None domain_info = (user_domain_name or user_domain_id or project_domain_name or project_domain_id) if (v2_auth_url and not domain_info) or not v3_auth_url: ks_session.auth = self.get_v2_auth(v2_auth_url) else: ks_session.auth = self.get_v3_auth(v3_auth_url) return ks_session def main(argv=sys.argv[1:]): try: return TackerShell(TACKER_API_VERSION).run( list(map(encodeutils.safe_decode, argv))) except KeyboardInterrupt: print("... terminating tacker client", file=sys.stderr) return 130 except exc.TackerClientException: return 1 except Exception as e: print(e) return 1 if __name__ == "__main__": sys.exit(main(sys.argv[1:])) python-tackerclient-0.11.0/tools/0000775000175100017510000000000013234643340016746 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/tools/tacker.bash_completion0000666000175100017510000000156413234643056023323 0ustar zuulzuul00000000000000_tacker_opts="" # lazy init _tacker_flags="" # lazy init _tacker_opts_exp="" # lazy init _tacker() { local cur prev nbc cflags COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_tacker_opts" == "x" ] ; then nbc="`tacker bash-completion`" _tacker_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/\s\s*/ /g"`" _tacker_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/\s\s*/ /g"`" _tacker_opts_exp="`echo "$_tacker_opts" | sed -e "s/\s/|/g"`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_tacker_opts_exp)" " && "$prev" != "help" ]] ; then COMPLETION_CACHE=~/.tackerclient/*/*-cache cflags="$_tacker_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_tacker_opts}" -- ${cur})) fi return 0 } complete -F _tacker tacker python-tackerclient-0.11.0/README.rst0000666000175100017510000000046613234643056017311 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-tackerclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on This is the client API library for Tacker. python-tackerclient-0.11.0/setup.cfg0000666000175100017510000000261413234643340017434 0ustar zuulzuul00000000000000[metadata] name = python-tackerclient summary = CLI and Client Library for OpenStack Tacker description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/tacker/latest classifier = Environment :: OpenStack Intended Audience :: Developers Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = tackerclient [global] setup-hooks = pbr.hooks.setup_hook [entry_points] console_scripts = tacker = tackerclient.shell:main openstack.cli.extension = tackerclient = tackerclient.osc.plugin openstack.tackerclient.v1 = vim_register = tackerclient.osc.v1.nfvo.vim:CreateVIM vim_list = tackerclient.osc.v1.nfvo.vim:ListVIM vim_set = tackerclient.osc.v1.nfvo.vim:UpdateVIM vim_delete = tackerclient.osc.v1.nfvo.vim:DeleteVIM vim_show = tackerclient.osc.v1.nfvo.vim:ShowVIM [build_sphinx] 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 [egg_info] tag_build = tag_date = 0 python-tackerclient-0.11.0/.testr.conf0000666000175100017510000000035013234643056017700 0ustar zuulzuul00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tackerclient/tests/unit} $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list python-tackerclient-0.11.0/LICENSE0000666000175100017510000002363713234643056016634 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. python-tackerclient-0.11.0/doc/0000775000175100017510000000000013234643340016353 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/0000775000175100017510000000000013234643340017653 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/reference/0000775000175100017510000000000013234643340021611 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/reference/index.rst0000666000175100017510000000166113234643056023464 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) tackerclient Python API ======================== Basic Usage ----------- #TODO python-tackerclient-0.11.0/doc/source/conf.py0000666000175100017510000000310413234643056021156 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # # -- General configuration --------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'reno.sphinxext', 'openstackdocstheme', 'cliff.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/python-tackerclient' bug_project = 'python-tackerclient' bug_tag = 'doc' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. copyright = u'OpenStack Foundation' # 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 # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'openstackdocs' # Output file base name for HTML help builder. htmlhelp_basename = 'tackerclientdoc' # -- Options for cliff.sphinxext plugin --------------------------------------- autoprogram_cliff_application = 'openstack' python-tackerclient-0.11.0/doc/source/cli/0000775000175100017510000000000013234643340020422 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/cli/index.rst0000666000175100017510000000220113234643056022264 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ========= Using CLI ========= There are two CLIs which support the Networking API: `OpenStackClient (OSC) `__ and :doc:`neutron CLI ` (deprecated). OpenStackClient --------------- #TODO neutron CLI ----------- #TODO python-tackerclient-0.11.0/doc/source/install/0000775000175100017510000000000013234643340021321 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/install/index.rst0000666000175100017510000000247713234643056023202 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ================= Install Guide ================= To install ``python-tackerclient``, it is required to have ``pip`` (in most cases). Make sure that ``pip`` is installed. Then type:: $ pip install python-tackerclient Or, if it is needed to install ``python-tackerclient`` from master branch, type:: $ pip install git+https://github.com/openstack/python-tackerclient.git After ``python-tackerclient`` is installed you will see command ``tacker`` in your environment. python-tackerclient-0.11.0/doc/source/index.rst0000666000175100017510000000314413234643056021524 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ================================== python-tackerclient documentation ================================== This is a client for OpenStack NFV MANO API. It provides :doc:`Python API bindings ` (the tackerclient module) and :doc:`command-line interface (CLI) `. User Documentation ------------------ .. toctree:: :maxdepth: 2 cli/index reference/index Contributor Guide ----------------- In the :doc:`Contributor Guide `, you will find information on tackerclient's lower level programming details or APIs as well as the transition to OpenStack client. .. toctree:: :maxdepth: 2 contributor/index History ------- Release notes is available at http://docs.openstack.org/releasenotes/python-tackerclient/. python-tackerclient-0.11.0/doc/source/contributor/0000775000175100017510000000000013234643340022225 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/doc/source/contributor/index.rst0000666000175100017510000000207313234643056024076 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ================= Contributor Guide ================= In the Contributor Guide, you will find information on tackerclient's lower level programming details or APIs as well as the transition to OpenStack client. #TODO python-tackerclient-0.11.0/HACKING.rst0000666000175100017510000000227313234643056017416 0ustar zuulzuul00000000000000Tacker Style Commandments ================================ - Step 1: Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest - Step 2: Read on Running Tests ------------- The testing system is based on a combination of tox and testr. The canonical approach to running tests is to simply run the command `tox`. This will create virtual environments, populate them with dependencies and run all of the tests that OpenStack CI systems run. Behind the scenes, tox is running `testr run --parallel`, but is set up such that you can supply any additional testr arguments that are needed to tox. For example, you can run: `tox -- --analyze-isolation` to cause tox to tell testr to add --analyze-isolation to its argument list. It is also possible to run the tests inside of a virtual environment you have created, or it is possible that you have all of the dependencies installed locally already. In this case, you can interact with the testr command directly. Running `testr run` will run the entire test suite. `testr run --parallel` will run it in parallel (this is the default incantation tox uses.) More information about testr can be found at: https://wiki.openstack.org/wiki/Testr python-tackerclient-0.11.0/python_tackerclient.egg-info/0000775000175100017510000000000013234643340023351 5ustar zuulzuul00000000000000python-tackerclient-0.11.0/python_tackerclient.egg-info/dependency_links.txt0000664000175100017510000000000113234643337027425 0ustar zuulzuul00000000000000 python-tackerclient-0.11.0/python_tackerclient.egg-info/PKG-INFO0000664000175100017510000000235213234643337024456 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: python-tackerclient Version: 0.11.0 Summary: CLI and Client Library for OpenStack Tacker Home-page: https://docs.openstack.org/tacker/latest Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-tackerclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on This is the client API library for Tacker. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 python-tackerclient-0.11.0/python_tackerclient.egg-info/requires.txt0000664000175100017510000000044413234643337025761 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 cliff!=2.9.0,>=2.8.0 iso8601>=0.1.11 netaddr>=0.7.18 requests>=2.14.2 python-keystoneclient>=3.8.0 simplejson>=3.5.1 six>=1.10.0 stevedore>=1.20.0 Babel!=2.4.0,>=2.3.4 oslo.i18n>=3.15.3 osc-lib>=1.8.0 oslo.log>=3.36.0 oslo.utils>=3.33.0 oslo.serialization!=2.19.1,>=2.18.0 python-tackerclient-0.11.0/python_tackerclient.egg-info/pbr.json0000664000175100017510000000005613234643337025036 0ustar zuulzuul00000000000000{"git_version": "8509a59", "is_release": true}python-tackerclient-0.11.0/python_tackerclient.egg-info/SOURCES.txt0000664000175100017510000001102613234643340025235 0ustar zuulzuul00000000000000.coveragerc .pylintrc .testr.conf AUTHORS ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst requirements.txt setup.cfg setup.py tacker_test.sh test-requirements.txt tox.ini doc/source/conf.py doc/source/index.rst doc/source/cli/index.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/reference/index.rst python_tackerclient.egg-info/PKG-INFO python_tackerclient.egg-info/SOURCES.txt python_tackerclient.egg-info/dependency_links.txt python_tackerclient.egg-info/entry_points.txt python_tackerclient.egg-info/not-zip-safe python_tackerclient.egg-info/pbr.json python_tackerclient.egg-info/requires.txt python_tackerclient.egg-info/top_level.txt releasenotes/notes/add-creating-ns-vnffg-from-template-213eee7f1820aa0c.yaml releasenotes/notes/del-project_and_user_id-e9dd396f83a162d6.yaml releasenotes/notes/deprecate-direct-yaml-cli-input-812564bab1b99b4b.yaml releasenotes/notes/deprecate-infra-mgmt-driver-attributes-e371624c50accee8.yaml releasenotes/notes/multi-delete-support-in-tacker-acd4a7e86114f0be.yaml releasenotes/notes/network-services-descriptor-06f6abe90adb40f3.yaml releasenotes/notes/new-commmand-vnf-resource-list-d5422ab917f0892f.yaml releasenotes/notes/remove-passing-mgmt-and-infra-driver-from-client-c9135f84480b2cae.yaml releasenotes/notes/update-vim-without-config-c3b637741889eff6.yaml releasenotes/notes/vnf-inline-template-25f6a0b66f7407a1.yaml releasenotes/notes/vnfd-vnf-vim-name-mandatory-in-tacker-cli-dfe802af6de5c80e.yaml releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tackerclient/__init__.py tackerclient/client.py tackerclient/i18n.py tackerclient/shell.py tackerclient/version.py tackerclient/common/__init__.py tackerclient/common/_i18n.py tackerclient/common/clientmanager.py tackerclient/common/command.py tackerclient/common/constants.py tackerclient/common/exceptions.py tackerclient/common/extension.py tackerclient/common/serializer.py tackerclient/common/utils.py tackerclient/common/validators.py tackerclient/osc/__init__.py tackerclient/osc/plugin.py tackerclient/osc/sdk_utils.py tackerclient/osc/utils.py tackerclient/osc/v1/__init__.py tackerclient/osc/v1/nfvo/__init__.py tackerclient/osc/v1/nfvo/vim.py tackerclient/tacker/__init__.py tackerclient/tacker/client.py tackerclient/tacker/v1_0/__init__.py tackerclient/tacker/v1_0/extension.py tackerclient/tacker/v1_0/events/__init__.py tackerclient/tacker/v1_0/events/events.py tackerclient/tacker/v1_0/nfvo/__init__.py tackerclient/tacker/v1_0/nfvo/ns.py tackerclient/tacker/v1_0/nfvo/nsd.py tackerclient/tacker/v1_0/nfvo/vim.py tackerclient/tacker/v1_0/nfvo/vim_utils.py tackerclient/tacker/v1_0/nfvo/vnffg.py tackerclient/tacker/v1_0/nfvo/vnffgd.py tackerclient/tacker/v1_0/vnfm/__init__.py tackerclient/tacker/v1_0/vnfm/vnf.py tackerclient/tacker/v1_0/vnfm/vnfd.py tackerclient/tests/__init__.py tackerclient/tests/unit/__init__.py tackerclient/tests/unit/test_auth.py tackerclient/tests/unit/test_casual_args.py tackerclient/tests/unit/test_cli10.py tackerclient/tests/unit/test_cli10_extensions.py tackerclient/tests/unit/test_command_meta.py tackerclient/tests/unit/test_http.py tackerclient/tests/unit/test_shell.py tackerclient/tests/unit/test_ssl.py tackerclient/tests/unit/test_utils.py tackerclient/tests/unit/test_validators.py tackerclient/tests/unit/vm/__init__.py tackerclient/tests/unit/vm/test_cli10_v10_event.py tackerclient/tests/unit/vm/test_cli10_vim.py tackerclient/tests/unit/vm/test_cli10_vim_k8s.py tackerclient/tests/unit/vm/test_cli10_vim_k8s_with_bearer_token.py tackerclient/tests/unit/vm/test_cli10_vnf.py tackerclient/tests/unit/vm/test_cli10_vnfd.py tackerclient/tests/unit/vm/test_cli10_vnffg.py tackerclient/tests/unit/vm/test_cli10_vnffgd.py tackerclient/tests/unit/vm/test_vim_utils.py tackerclient/tests/unit/vm/samples/vim_config.yaml tackerclient/tests/unit/vm/samples/vim_config_with_false_cert_verify.yaml tackerclient/tests/unit/vm/samples/vim_config_without_auth_url.yaml tackerclient/tests/unit/vm/samples/vim_k8s_bearer_token.yaml tackerclient/tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml tackerclient/tests/unit/vm/samples/vim_k8s_config.yaml tackerclient/tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml tackerclient/tests/unit/vm/samples/vnffg_update_file.yaml tackerclient/v1_0/__init__.py tackerclient/v1_0/client.py tools/tacker.bash_completionpython-tackerclient-0.11.0/python_tackerclient.egg-info/entry_points.txt0000664000175100017510000000061613234643337026660 0ustar zuulzuul00000000000000[console_scripts] tacker = tackerclient.shell:main [openstack.cli.extension] tackerclient = tackerclient.osc.plugin [openstack.tackerclient.v1] vim_delete = tackerclient.osc.v1.nfvo.vim:DeleteVIM vim_list = tackerclient.osc.v1.nfvo.vim:ListVIM vim_register = tackerclient.osc.v1.nfvo.vim:CreateVIM vim_set = tackerclient.osc.v1.nfvo.vim:UpdateVIM vim_show = tackerclient.osc.v1.nfvo.vim:ShowVIM python-tackerclient-0.11.0/python_tackerclient.egg-info/top_level.txt0000664000175100017510000000001513234643337026105 0ustar zuulzuul00000000000000tackerclient python-tackerclient-0.11.0/python_tackerclient.egg-info/not-zip-safe0000664000175100017510000000000113234643324025601 0ustar zuulzuul00000000000000 python-tackerclient-0.11.0/MANIFEST.in0000666000175100017510000000020313234643056017345 0ustar zuulzuul00000000000000include tox.ini include LICENSE README.rst HACKING.rst include AUTHORS include ChangeLog include tools/* recursive-include tests * python-tackerclient-0.11.0/test-requirements.txt0000666000175100017510000000110413234643056022051 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD python-subunit>=1.0.0 # Apache-2.0/BSD sphinx!=1.6.6,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT oslosphinx>=4.7.0 # Apache-2.0 openstackdocstheme>=1.18.1 # Apache-2.0 # releasenotes reno>=2.5.0 # Apache-2.0 mock>=2.0.0 # BSD