diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e52bb44..0000000 --- a/.gitignore +++ /dev/null @@ -1,55 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg* -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -cover/ -.coverage* -!.coveragerc -.tox -nosetests.xml -.testrepository -.venv - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Complexity -output/*.html -output/*/index.html - -# Sphinx -doc/build - -# pbr generates these -AUTHORS -ChangeLog - -# Editors -*~ -.*.swp -.*sw? diff --git a/.gitreview b/.gitreview deleted file mode 100644 index ed1766d..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/python-wsmanclient.git diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 9645922..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,20 +0,0 @@ -How To Contribute -================= - -If you would like to contribute to the development of OpenStack, you must -follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -If you already have a good understanding of how the system works and your -OpenStack accounts are set up, you can skip to the development workflow -section of this documentation to learn how changes to OpenStack should be -submitted for review via the Gerrit tool: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-wsmanclient diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 7b53707..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,4 +0,0 @@ -python-wsmanclient Style Commandments -===================================== - -Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8f71f43..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - 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. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/README.rst b/README.rst index 99bd9fe..4e18e6f 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,12 @@ -python-wsmanclient -================== +This project is no longer maintained. -Python client that speaks Web Services-Management protocol. +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +The WSMAN protocol implementation available here is still developed as part of +python-dracclient: https://git.openstack.org/cgit/openstack/python-dracclient + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-ironic on Freenode. diff --git a/doc/source/client-api.rst b/doc/source/client-api.rst deleted file mode 100644 index 259d95e..0000000 --- a/doc/source/client-api.rst +++ /dev/null @@ -1,69 +0,0 @@ -Client API -========== - -The client does not yet implement the complete Web Services-Management -protocol. You can find the currently supported methods here. - - -WS-Enumeration --------------- - -enumerate -~~~~~~~~~ -Executes enumerate operation over WSMan and returns an ``lxml.etree.Element`` -object of the response received. - -Required parameters: - -* ``resource_uri``: URI of resource to enumerate. - -Optional parameters: - -* ``optimization``: flag to enable enumeration optimization. If disabled, the - enumeration returns only an enumeration context. Defaults to ``True``. - -* ``max_elems``: maximum number of elements returned by the operation. Defaults - to ``100``. - -* ``auto_pull``: flag to enable automatic pull on the enumeration context, - merging the items returned. Defaults to ``True``. - -* ``filter_query``: filter query string. - -* ``filter_dialect``: filter dialect. Valid options are: ``cql`` and ``wql``. - Defaults to ``cql``. - -pull -~~~~ -Executes pull operation over WSMan and returns an ``lxml.etree.Element`` -object of the response received. - -Required parameters: - -* ``resource_uri``: URI of resource to pull. - -* ``context``: enumeration context. - -Optional parameters: - -* ``max_elems``: maximum number of elements returned by the operation. Defaults - to ``100``. - - -Custom Actions ---------------- - -invoke -~~~~~~ -Executes custom action over WSMan and returns an ``lxml.etree.Element`` -object of the response received. - -Required parameters: - -* ``resource_uri``: URI of resource to invoke. - -* ``method``: name of the method to invoke. - -* ``selector``: dictionary of selectors. - -* ``properties``: dictionary of properties. diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 3cf5154..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - 'oslosphinx' -] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'python-wsmanclient' -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_path = ["."] -# html_theme = '_theme' -# html_static_path = ['static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), -] - -# Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index ac7b6bc..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 6409d61..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -Welcome to python-wsmanclient's documentation! -============================================== - -.. toctree:: - :maxdepth: 2 - - usage - client-api - Contributing - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/usage.rst b/doc/source/usage.rst deleted file mode 100644 index c8f1e12..0000000 --- a/doc/source/usage.rst +++ /dev/null @@ -1,14 +0,0 @@ -Usage -===== - -Create a client object by providing the connection details:: - - client = wsmanclient.Client('1.2.3.4', 'username', 's3cr3t') - -.. note:: - By default it will use port 443, '/wsman' as path and https protocol. - -You can override the default port, path and protocol:: - - client = wsmanclient.Client('1.2.3.4', 'username', 's3cr3t', port=443, - path='/wsman', protocol='https') diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c7609c6..0000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# 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. - -lxml>=2.3 -pbr>=1.6 -requests>=2.5.2 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 207594e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,28 +0,0 @@ -[metadata] -name = python-wsmanclient -summary = Python client that speaks Web Services-Management protocol -description-file = README.rst -maintainer = Imre Farkas -maintainer_email = ifarkas@redhat.com -home-page = https://launchpad.net/python-wsmanclient -license = Apache-2 -classifier = - Development Status :: 3 - Alpha - Environment :: OpenStack - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - -[files] -packages = - wsmanclient - -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source diff --git a/setup.py b/setup.py deleted file mode 100644 index f369aa7..0000000 --- a/setup.py +++ /dev/null @@ -1,28 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# 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'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index ac016f0..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -hacking<0.11,>=0.10.0 - -coverage>=3.6 -doc8 -mock>=1.2 -requests-mock>=0.6 - -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -oslosphinx>=2.5.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a34580c..0000000 --- a/tox.ini +++ /dev/null @@ -1,29 +0,0 @@ -[tox] -envlist = pep8,py27,py34 - -[testenv] -install_command = pip install -U -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -usedevelop = True -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -setenv = PYTHONDONTWRITEBYTECODE=1 -commands = - coverage run --branch --source wsmanclient --omit "wsmanclient/tests*" -m unittest discover wsmanclient.tests - coverage report -m --fail-under 90 - -[testenv:venv] -commands = {posargs} - -[testenv:pep8] -basepython = python2.7 -commands = - flake8 wsmanclient - doc8 README.rst CONTRIBUTING.rst HACKING.rst doc/source - -[testenv:docs] -commands = python setup.py build_sphinx - -[flake8] -max-complexity=15 -show-source = True diff --git a/wsmanclient/__init__.py b/wsmanclient/__init__.py deleted file mode 100644 index 9a6c4d6..0000000 --- a/wsmanclient/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from wsmanclient.client import Client # noqa diff --git a/wsmanclient/client.py b/wsmanclient/client.py deleted file mode 100644 index f641dfb..0000000 --- a/wsmanclient/client.py +++ /dev/null @@ -1,379 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging -import uuid - -from lxml import etree as ElementTree -import requests -import requests.exceptions - -from wsmanclient import exceptions - -LOG = logging.getLogger(__name__) - -NS_SOAP_ENV = 'http://www.w3.org/2003/05/soap-envelope' -NS_WS_ADDR = 'http://schemas.xmlsoap.org/ws/2004/08/addressing' -NS_WS_ADDR_ANONYM_ROLE = ('http://schemas.xmlsoap.org/ws/2004/08/addressing/' - 'role/anonymous') -NS_WSMAN = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' -NS_WSMAN_ENUM = 'http://schemas.xmlsoap.org/ws/2004/09/enumeration' - -NS_MAP = {'s': NS_SOAP_ENV, - 'wsa': NS_WS_ADDR, - 'wsman': NS_WSMAN} - -FILTER_DIALECT_MAP = {'cql': 'http://schemas.dmtf.org/wbem/cql/1/dsp0202.pdf', - 'wql': 'http://schemas.microsoft.com/wbem/wsman/1/WQL'} - - -class Client(object): - """Simple client for talking over WSMan protocol""" - - def __init__(self, host, username, password, port=443, path='/wsman', - protocol='https'): - self.host = host - self.username = username - self.password = password - self.port = port - self.path = path - self.protocol = protocol - self.endpoint = ('%(protocol)s://%(host)s:%(port)s%(path)s' % { - 'protocol': self.protocol, - 'host': self.host, - 'port': self.port, - 'path': self.path}) - - def _do_request(self, payload): - payload = payload.build() - LOG.debug('Sending request to %(endpoint)s: %(payload)s', - {'endpoint': self.endpoint, 'payload': payload}) - try: - resp = requests.post( - self.endpoint, - auth=requests.auth.HTTPBasicAuth(self.username, self.password), - data=payload, - # TODO(ifarkas): enable cert verification - verify=False) - except requests.exceptions.RequestException: - LOG.exception('Request failed') - raise exceptions.RequestFailure() - - LOG.debug('Received response from %(endpoint)s: %(payload)s', - {'endpoint': self.endpoint, 'payload': resp.content}) - if not resp.ok: - raise exceptions.InvalidResponse( - status_code=resp.status_code, - reason=resp.reason) - else: - return resp - - def enumerate(self, resource_uri, optimization=True, max_elems=100, - auto_pull=True, filter_query=None, filter_dialect='cql'): - """Executes enumerate operation over WSMan - - :param resource_uri: URI of resource to enumerate - :param optimization: flag to enable enumeration optimization. If - disabled, the enumeration returns only an - enumeration context. - :param max_elems: maximum number of elements returned by the operation - :param auto_pull: flag to enable automatic pull on the enumeration - context, merging the items returned - :param filter_query: filter query string - :param filter_dialect: filter dialect. Valid options are: 'cql' and - 'wql' - :returns: an lxml.etree.Element object of the response received - :raises: RequestFailure on request failures - :raises: InvalidResponse when receiving invalid response - :raises: InvalidFilterDialect when filter_dialect parameter is invalid - """ - - payload = _EnumeratePayload(self.endpoint, resource_uri, - optimization, max_elems, - filter_query, filter_dialect) - - resp = self._do_request(payload) - resp_xml = ElementTree.fromstring(resp.content) - - if auto_pull: - find_items_query = './/{%s}Items' % NS_WSMAN_ENUM - full_resp_xml = resp_xml - - context = self._enum_context(full_resp_xml) - while context is not None: - resp_xml = self.pull(resource_uri, context, max_elems) - context = self._enum_context(resp_xml) - - items_xml = full_resp_xml.find(find_items_query) - if items_xml is not None: - # merge enumeration items - for item in resp_xml.find(find_items_query): - items_xml.append(item) - else: - full_resp_xml = resp_xml - - # remove enumeration context because items are already merged - enum_context_elem = full_resp_xml.find('.//{%s}EnumerationContext' - % NS_WSMAN_ENUM) - if enum_context_elem is not None: - enum_context_elem.getparent().remove(enum_context_elem) - - return full_resp_xml - else: - return resp_xml - - def pull(self, resource_uri, context, max_elems=100): - """Executes pull operation over WSMan - - :param resource_uri: URI of resource to pull - :param context: enumeration context - :param max_elems: maximum number of elements returned by the operation - :returns: an lxml.etree.Element object of the response received - :raises: RequestFailure on request failures - :raises: InvalidResponse when receiving invalid response - """ - - payload = _PullPayload(self.endpoint, resource_uri, context, - max_elems) - resp = self._do_request(payload) - resp_xml = ElementTree.fromstring(resp.content) - - return resp_xml - - def invoke(self, resource_uri, method, selectors, properties): - """Executes invoke operation over WSMan - - :param resource_uri: URI of resource to invoke - :param method: name of the method to invoke - :param selector: dict of selectors - :param properties: dict of properties - :returns: an lxml.etree.Element object of the response received - :raises: RequestFailure on request failures - :raises: InvalidResponse when receiving invalid response - """ - - payload = _InvokePayload(self.endpoint, resource_uri, method, - selectors, properties) - resp = self._do_request(payload) - resp_xml = ElementTree.fromstring(resp.content) - - return resp_xml - - def _enum_context(self, resp): - context_elem = resp.find('.//{%s}EnumerationContext' % NS_WSMAN_ENUM) - if context_elem is not None: - return context_elem.text - - -class _Payload(object): - """Payload generation for WSMan requests.""" - - def build(self): - request = self._create_envelope() - self._add_header(request) - self._add_body(request) - - return ElementTree.tostring(request) - - def _create_envelope(self): - return ElementTree.Element('{%s}Envelope' % NS_SOAP_ENV, nsmap=NS_MAP) - - def _add_header(self, envelope): - header = ElementTree.SubElement(envelope, '{%s}Header' % NS_SOAP_ENV) - - qn_must_understand = ElementTree.QName(NS_SOAP_ENV, 'mustUnderstand') - - to_elem = ElementTree.SubElement(header, '{%s}To' % NS_WS_ADDR) - to_elem.set(qn_must_understand, 'true') - to_elem.text = self.endpoint - - resource_elem = ElementTree.SubElement(header, - '{%s}ResourceURI' % NS_WSMAN) - resource_elem.set(qn_must_understand, 'true') - resource_elem.text = self.resource_uri - - msg_id_elem = ElementTree.SubElement(header, - '{%s}MessageID' % NS_WS_ADDR) - msg_id_elem.set(qn_must_understand, 'true') - msg_id_elem.text = 'uuid:%s' % uuid.uuid4() - - reply_to_elem = ElementTree.SubElement(header, - '{%s}ReplyTo' % NS_WS_ADDR) - reply_to_addr_elem = ElementTree.SubElement(reply_to_elem, - '{%s}Address' % NS_WS_ADDR) - reply_to_addr_elem.text = NS_WS_ADDR_ANONYM_ROLE - - return header - - def _add_body(self, envelope): - return ElementTree.SubElement(envelope, '{%s}Body' % NS_SOAP_ENV) - - -class _EnumeratePayload(_Payload): - """Payload generation for WSMan enumerate operation""" - - def __init__(self, endpoint, resource_uri, optimization=True, - max_elems=100, filter_query=None, filter_dialect=None): - self.endpoint = endpoint - self.resource_uri = resource_uri - self.filter_dialect = None - self.filter_query = None - self.optimization = optimization - self.max_elems = max_elems - - if filter_query is not None: - try: - self.filter_dialect = FILTER_DIALECT_MAP[filter_dialect] - except KeyError: - valid_opts = ', '.join(FILTER_DIALECT_MAP) - raise exceptions.InvalidFilterDialect( - invalid_filter=filter_dialect, supported=valid_opts) - - self.filter_query = filter_query - - def _add_header(self, envelope): - header = super(_EnumeratePayload, self)._add_header(envelope) - - action_elem = ElementTree.SubElement(header, '{%s}Action' % NS_WS_ADDR) - action_elem.set('{%s}mustUnderstand' % NS_SOAP_ENV, 'true') - action_elem.text = NS_WSMAN_ENUM + '/Enumerate' - - return header - - def _add_body(self, envelope): - body = super(_EnumeratePayload, self)._add_body(envelope) - - enum_elem = ElementTree.SubElement(body, - '{%s}Enumerate' % NS_WSMAN_ENUM, - nsmap={'wsen': NS_WSMAN_ENUM}) - - if self.filter_query is not None: - self._add_filter(enum_elem) - - if self.optimization: - self._add_enum_optimization(enum_elem) - - return body - - def _add_enum_optimization(self, enum_elem): - ElementTree.SubElement(enum_elem, - '{%s}OptimizeEnumeration' % NS_WSMAN) - - max_elem_elem = ElementTree.SubElement(enum_elem, - '{%s}MaxElements' % NS_WSMAN) - max_elem_elem.text = str(self.max_elems) - - def _add_filter(self, enum_elem): - filter_elem = ElementTree.SubElement(enum_elem, - '{%s}Filter' % NS_WSMAN) - filter_elem.set('Dialect', self.filter_dialect) - filter_elem.text = self.filter_query - - -class _PullPayload(_Payload): - """Payload generation for WSMan pull operation""" - - def __init__(self, endpoint, resource_uri, context, max_elems=100): - self.endpoint = endpoint - self.resource_uri = resource_uri - self.context = context - self.max_elems = max_elems - - def _add_header(self, envelope): - header = super(_PullPayload, self)._add_header(envelope) - - action_elem = ElementTree.SubElement(header, '{%s}Action' % NS_WS_ADDR) - action_elem.set('{%s}mustUnderstand' % NS_SOAP_ENV, 'true') - action_elem.text = NS_WSMAN_ENUM + '/Pull' - - return header - - def _add_body(self, envelope): - body = super(_PullPayload, self)._add_body(envelope) - - pull_elem = ElementTree.SubElement(body, - '{%s}Pull' % NS_WSMAN_ENUM, - nsmap={'wsen': NS_WSMAN_ENUM}) - - enum_context_elem = ElementTree.SubElement( - pull_elem, '{%s}EnumerationContext' % NS_WSMAN_ENUM) - enum_context_elem.text = self.context - - self._add_enum_optimization(pull_elem) - - return body - - def _add_enum_optimization(self, pull_elem): - max_elem_elem = ElementTree.SubElement(pull_elem, - '{%s}MaxElements' % NS_WSMAN) - max_elem_elem.text = str(self.max_elems) - - -class _InvokePayload(_Payload): - """Payload generation for WSMan invoke operation""" - - def __init__(self, endpoint, resource_uri, method, selectors=None, - properties=None): - self.endpoint = endpoint - self.resource_uri = resource_uri - self.method = method - self.selectors = selectors - self.properties = properties - - def _add_header(self, envelope): - header = super(_InvokePayload, self)._add_header(envelope) - - action_elem = ElementTree.SubElement(header, '{%s}Action' % NS_WS_ADDR) - action_elem.set('{%s}mustUnderstand' % NS_SOAP_ENV, 'true') - action_elem.text = ('%(resource_uri)s/%(method)s' % - {'resource_uri': self.resource_uri, - 'method': self.method}) - - self._add_selectors(header) - - return header - - def _add_body(self, envelope): - body = super(_InvokePayload, self)._add_body(envelope) - self._add_properties(body) - - return body - - def _add_selectors(self, header): - selector_set_elem = ElementTree.SubElement( - header, '{%s}SelectorSet' % NS_WSMAN) - - for (name, value) in self.selectors.items(): - selector_elem = ElementTree.SubElement(selector_set_elem, - '{%s}Selector' % NS_WSMAN) - selector_elem.set('Name', name) - selector_elem.text = value - - def _add_properties(self, body): - method_elem = ElementTree.SubElement( - body, - ('{%(resource_uri)s}%(method)s_INPUT' % - {'resource_uri': self.resource_uri, - 'method': self.method})) - - for (name, value) in self.properties.items(): - if not isinstance(value, list): - value = [value] - - for item in value: - property_elem = ElementTree.SubElement( - method_elem, - ('{%(resource_uri)s}%(name)s' % - {'resource_uri': self.resource_uri, - 'name': name})) - property_elem.text = item diff --git a/wsmanclient/exceptions.py b/wsmanclient/exceptions.py deleted file mode 100644 index 3578009..0000000 --- a/wsmanclient/exceptions.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -class BaseClientException(Exception): - - msg_fmt = 'An unknown exception occurred' - - def __init__(self, message=None, **kwargs): - message = self.msg_fmt % kwargs - super(BaseClientException, self).__init__(message) - - -class RequestFailure(BaseClientException): - msg_fmt = ('WSMan request failed') - - -class InvalidResponse(BaseClientException): - msg_fmt = ('Invalid response received. Status code: "%(status_code)s", ' - 'reason: "%(reason)s"') - - -class InvalidFilterDialect(BaseClientException): - msg_fmt = ('Invalid filter dialect "%(invalid_filter)s". ' - 'Supported options are %(supported)s') diff --git a/wsmanclient/tests/__init__.py b/wsmanclient/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/wsmanclient/tests/test_client.py b/wsmanclient/tests/test_client.py deleted file mode 100644 index a53a22b..0000000 --- a/wsmanclient/tests/test_client.py +++ /dev/null @@ -1,315 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import collections -import unittest -import uuid - -import lxml.etree -import lxml.objectify -import mock -import requests_mock - -from wsmanclient import client -from wsmanclient import exceptions -from wsmanclient.tests import utils as test_utils - - -class ClientTestCase(unittest.TestCase): - - def setUp(self): - super(ClientTestCase, self).setUp() - self.client = client.Client(**test_utils.FAKE_ENDPOINT) - - @requests_mock.Mocker() - def test_enumerate(self, mock_requests): - expected_resp = 'yay!' - mock_requests.post('https://1.2.3.4:443/wsman', text=expected_resp) - - resp = self.client.enumerate('resource', auto_pull=False) - self.assertEqual('yay!', resp.text) - - def test_enumerate_with_request_failure(self): - self.client = client.Client('malformed://^@*', 'user', 'pass') - - self.assertRaises(exceptions.RequestFailure, - self.client.enumerate, 'resource') - - @requests_mock.Mocker() - def test_enumerate_with_invalid_status_code(self, mock_requests): - mock_requests.post('https://1.2.3.4:443/wsman', status_code=500, - reason='dumb request') - - self.assertRaises(exceptions.InvalidResponse, - self.client.enumerate, 'resource') - - @requests_mock.Mocker() - def test_enumerate_with_auto_pull(self, mock_requests): - mock_requests.post( - 'https://1.2.3.4:443/wsman', - [{'text': test_utils.WSManEnumerations['context'][0]}, - {'text': test_utils.WSManEnumerations['context'][1]}, - {'text': test_utils.WSManEnumerations['context'][2]}, - {'text': test_utils.WSManEnumerations['context'][3]}]) - - resp_xml = self.client.enumerate('FooResource') - - foo_resource_uri = 'http://FooResource' - bar_resource_uri = 'http://BarResource' - self.assertEqual( - 3, len(resp_xml.findall('.//{%s}FooResource' % foo_resource_uri))) - self.assertEqual( - 1, len(resp_xml.findall('.//{%s}BazResource' % bar_resource_uri))) - self.assertEqual( - 0, len(resp_xml.findall( - './/{%s}EnumerationContext' % client.NS_WSMAN_ENUM))) - - @requests_mock.Mocker() - @mock.patch.object(client.Client, 'pull', autospec=True) - def test_enumerate_with_auto_pull_without_optimization(self, mock_requests, - mock_pull): - mock_requests.post('https://1.2.3.4:443/wsman', - text=test_utils.WSManEnumerations['context'][0]) - mock_pull.return_value = lxml.etree.fromstring( - test_utils.WSManEnumerations['context'][3]) - - self.client.enumerate('FooResource', optimization=False, max_elems=42) - - mock_pull.assert_called_once_with(self.client, 'FooResource', - 'enum-context-uuid', 42) - - @requests_mock.Mocker() - def test_pull(self, mock_requests): - expected_resp = 'yay!' - mock_requests.post('https://1.2.3.4:443/wsman', text=expected_resp) - - resp = self.client.pull('resource', 'context-uuid') - - self.assertEqual('yay!', resp.text) - - @requests_mock.Mocker() - def test_invoke(self, mock_requests): - expected_resp = 'yay!' - mock_requests.post('https://1.2.3.4:443/wsman', text=expected_resp) - - resp = self.client.invoke('http://resource', 'method', - {'selector': 'foo'}, {'property': 'bar'}) - - self.assertEqual('yay!', resp.text) - - -class PayloadTestCase(unittest.TestCase): - - def setUp(self): - super(PayloadTestCase, self).setUp() - client.NS_MAP = collections.OrderedDict([ - ('s', client.NS_SOAP_ENV), - ('wsa', client.NS_WS_ADDR), - ('wsman', client.NS_WSMAN)]) - - @mock.patch.object(uuid, 'uuid4', autospec=True) - def test_build_enum(self, mock_uuid): - expected_payload = """ - - - http://host:443/wsman - http://resource_uri - uuid:1234-12 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate - - - - - 100 - - - -""" # noqa - expected_payload_obj = lxml.objectify.fromstring(expected_payload) - - mock_uuid.return_value = '1234-12' - payload = client._EnumeratePayload('http://host:443/wsman', - 'http://resource_uri').build() - payload_obj = lxml.objectify.fromstring(payload) - - self.assertEqual(lxml.etree.tostring(expected_payload_obj), - lxml.etree.tostring(payload_obj)) - - def test_enumerate_without_optimization(self): - payload = client._EnumeratePayload( - 'http://host:443/wsman', 'http://resource_uri', optimization=False, - max_elems=42).build() - payload_xml = lxml.etree.fromstring(payload) - - optimize_enum_elems = payload_xml.findall( - './/{%s}OptimizeEnumeration' % client.NS_WSMAN) - max_elem_elems = payload_xml.findall( - './/{%s}MaxElements' % client.NS_WSMAN) - self.assertEqual([], optimize_enum_elems) - self.assertEqual([], max_elem_elems) - - @mock.patch.object(uuid, 'uuid4', autospec=True) - def test_build_enum_with_filter(self, mock_uuid): - expected_payload = """ - - - http://host:443/wsman - http://resource_uri - uuid:1234-12 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate - - - - DROP TABLE users - - 100 - - - -""" # noqa - expected_payload_obj = lxml.objectify.fromstring(expected_payload) - - mock_uuid.return_value = '1234-12' - payload = client._EnumeratePayload( - 'http://host:443/wsman', 'http://resource_uri', - filter_query='DROP TABLE users', filter_dialect='cql').build() - payload_obj = lxml.objectify.fromstring(payload) - - self.assertEqual(lxml.etree.tostring(expected_payload_obj), - lxml.etree.tostring(payload_obj)) - - def test_build_enum_with_invalid_filter_dialect(self): - invalid_dialect = 'foo' - self.assertRaises(exceptions.InvalidFilterDialect, - client._EnumeratePayload, - 'http://host:443/wsman', 'http://resource_uri', - filter_query='DROP TABLE users', - filter_dialect=invalid_dialect) - - @mock.patch.object(uuid, 'uuid4', autospec=True) - def test_build_pull(self, mock_uuid): - expected_payload = """ - - - http://host:443/wsman - http://resource_uri - uuid:1234-12 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull - - - - context-uuid - 100 - - - -""" # noqa - expected_payload_obj = lxml.objectify.fromstring(expected_payload) - - mock_uuid.return_value = '1234-12' - payload = client._PullPayload('http://host:443/wsman', - 'http://resource_uri', - 'context-uuid').build() - payload_obj = lxml.objectify.fromstring(payload) - - self.assertEqual(lxml.etree.tostring(expected_payload_obj), - lxml.etree.tostring(payload_obj)) - - @mock.patch.object(uuid, 'uuid4', autospec=True) - def test_build_invoke(self, mock_uuid): - expected_payload = """ - - - http://host:443/wsman - http://resource_uri - uuid:1234-12 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://resource_uri/method - - foo - - - - - bar - - - -""" # noqa - expected_payload_obj = lxml.objectify.fromstring(expected_payload) - - mock_uuid.return_value = '1234-12' - payload = client._InvokePayload( - 'http://host:443/wsman', 'http://resource_uri', 'method', - {'selector': 'foo'}, {'property': 'bar'}).build() - payload_obj = lxml.objectify.fromstring(payload) - - self.assertEqual(lxml.etree.tostring(expected_payload_obj), - lxml.etree.tostring(payload_obj)) - - @mock.patch.object(uuid, 'uuid4', autospec=True) - def test_build_invoke_with_list_in_properties(self, mock_uuid): - expected_payload = """ - - - http://host:443/wsman - http://resource_uri - uuid:1234-12 - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - http://resource_uri/method - - foo - - - - - foo - bar - baz - - - -""" # noqa - expected_payload_obj = lxml.objectify.fromstring(expected_payload) - - mock_uuid.return_value = '1234-12' - payload = client._InvokePayload( - 'http://host:443/wsman', 'http://resource_uri', 'method', - {'selector': 'foo'}, {'property': ['foo', 'bar', 'baz']}).build() - payload_obj = lxml.objectify.fromstring(payload) - - self.assertEqual(lxml.etree.tostring(expected_payload_obj), - lxml.etree.tostring(payload_obj)) diff --git a/wsmanclient/tests/utils.py b/wsmanclient/tests/utils.py deleted file mode 100644 index a17cb3a..0000000 --- a/wsmanclient/tests/utils.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os - -FAKE_ENDPOINT = { - 'host': '1.2.3.4', - 'port': '443', - 'path': '/wsman', - 'protocol': 'https', - 'username': 'admin', - 'password': 's3cr3t' -} - - -def load_wsman_xml(name): - """Helper function to load a WSMan XML response from a file.""" - - with open(os.path.join(os.path.dirname(__file__), 'wsman_mocks', - '%s.xml' % name), 'r') as f: - xml_body = f.read() - - return xml_body - -WSManEnumerations = { - 'context': [ - load_wsman_xml('wsman-enum_context-1'), - load_wsman_xml('wsman-enum_context-2'), - load_wsman_xml('wsman-enum_context-3'), - load_wsman_xml('wsman-enum_context-4'), - ] -} diff --git a/wsmanclient/tests/wsman_mocks/wsman-enum_context-1.xml b/wsmanclient/tests/wsman_mocks/wsman-enum_context-1.xml deleted file mode 100644 index e9b265b..0000000 --- a/wsmanclient/tests/wsman_mocks/wsman-enum_context-1.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - http://schemas.xmlsoap.org/ws/2004/09/enumeration/EnumerateResponse - uuid:89afbea0-2005-1005-8002-fd0aa2bdb228 - uuid:babd467b-2009-1009-8096-fcc71555dbe0 - - - - enum-context-uuid - - - diff --git a/wsmanclient/tests/wsman_mocks/wsman-enum_context-2.xml b/wsmanclient/tests/wsman_mocks/wsman-enum_context-2.xml deleted file mode 100644 index cab5e11..0000000 --- a/wsmanclient/tests/wsman_mocks/wsman-enum_context-2.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - http://schemas.xmlsoap.org/ws/2004/09/enumeration/PullResponse - uuid:89ec4d7c-2005-1005-8003-fd0aa2bdb228 - uuid:bac57212-2009-1009-8097-fcc71555dbe0 - - - - enum-context-uuid - - - 1 - - - - - diff --git a/wsmanclient/tests/wsman_mocks/wsman-enum_context-3.xml b/wsmanclient/tests/wsman_mocks/wsman-enum_context-3.xml deleted file mode 100644 index 38190c7..0000000 --- a/wsmanclient/tests/wsman_mocks/wsman-enum_context-3.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - http://schemas.xmlsoap.org/ws/2004/09/enumeration/PullResponse - uuid:89f3b75a-2005-1005-8004-fd0aa2bdb228 - uuid:baccc4b1-2009-1009-8098-fcc71555dbe0 - - - - enum-context-uuid - - - 2 - - - 3 - - - - - diff --git a/wsmanclient/tests/wsman_mocks/wsman-enum_context-4.xml b/wsmanclient/tests/wsman_mocks/wsman-enum_context-4.xml deleted file mode 100644 index 15e0490..0000000 --- a/wsmanclient/tests/wsman_mocks/wsman-enum_context-4.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - http://schemas.xmlsoap.org/ws/2004/09/enumeration/PullResponse - uuid:8b0bcd65-2005-1005-8026-fd0aa2bdb228 - uuid:bbe513cd-2009-1009-80ba-fcc71555dbe0 - - - - - - 4 - - - - - - diff --git a/wsmanclient/version.py b/wsmanclient/version.py deleted file mode 100644 index dbfe24d..0000000 --- a/wsmanclient/version.py +++ /dev/null @@ -1,16 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import pbr.version - -version_info = pbr.version.VersionInfo('python-wsmanclient')