diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 37ec93a..0000000 --- a/LICENSE +++ /dev/null @@ -1,191 +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: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -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 -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 148bcf2..adaff92 100644 --- a/README.rst +++ b/README.rst @@ -1,36 +1,5 @@ -Murano -====== -Murano Project introduces an application catalog, which allows application -developers and cloud administrators to publish various cloud-ready -applications in a browsable‎ categorised catalog, which may be used by the -cloud users (including the inexperienced ones) to pick-up the needed -applications and services and composes the reliable environments out of them -in a “push-the-button” manner. +DEPRECATED: murano-metadataclient +================================= -murano-metadataclient ---------------------- -murano-metadataclient is a python bindings for murano-repository project. - -Project Resources ------------------ -* `Murano at Launchpad `__ -* `Wiki `__ -* `Code Review `__ -* `Sources `__ -* `Developers Guide `__ - -How To Participate ------------------- -If you would like to ask some questions or make proposals, feel free to reach -us on #murano IRC channel at FreeNode. Typically somebody from our team will -be online at IRC from 6:00 to 20:00 UTC. You can also contact Murano community -directly by openstack-dev@lists.openstack.org adding [Murano] to a subject. - -We’re holding public weekly meetings on Tuesdays at 17:00 UTC -on #openstack-meeting-alt IRC channel at FreeNode. - -If you want to contribute either to docs or to code, simply send us change -request via `gerrit `__. -You can `file bugs `__ and -`register blueprints `__ on -Launchpad. +**Warning** - this repository is deprecated. All functionality has been moved +to `python-muranoclient `__ diff --git a/metadataclient/__init__.py b/metadataclient/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/metadataclient/client.py b/metadataclient/client.py deleted file mode 100644 index cb5b18a..0000000 --- a/metadataclient/client.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - -from metadataclient.common import utils - - -def Client(version, *args, **kwargs): - module = utils.import_versioned_module(version, 'client') - client_class = getattr(module, 'Client') - return client_class(*args, **kwargs) diff --git a/metadataclient/common/__init__.py b/metadataclient/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/metadataclient/common/base.py b/metadataclient/common/base.py deleted file mode 100644 index 136ff2c..0000000 --- a/metadataclient/common/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# 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. - -""" -Base utilities to build API operation managers and objects on top of. -""" - -import copy - - -# Python 2.4 compat -try: - all -except NameError: - def all(iterable): - return True not in (not x for x in iterable) - - -def getid(obj): - """ - Abstracts the common pattern of allowing both an object or an object's ID - (UUID) as a parameter when dealing with relationships. - """ - try: - return obj.id - except AttributeError: - return obj - - -class Manager(object): - """ - Managers interact with a particular type of API (servers, flavors, images, - etc.) and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, api): - self.api = api - - def _list(self, url, response_key=None, obj_class=None, - body=None, headers={}): - - resp, body = self.api.json_request('GET', url, headers=headers) - - if obj_class is None: - obj_class = self.resource_class - - if response_key: - if not response_key in body: - body[response_key] = [] - data = body[response_key] - else: - data = body - return [obj_class(self, res, loaded=True) for res in data if res] - - def _delete(self, url, headers={}): - self.api.raw_request('DELETE', url, headers=headers) - - def _update(self, url, body, response_key=None, headers={}): - resp, body = self.api.json_request('PUT', url, body=body, - headers=headers) - # PUT requests may not return a body - if body: - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - def _create(self, url, body=None, response_key=None, - return_raw=False, headers={}): - - if body: - resp, body = self.api.json_request('POST', url, - body=body, headers=headers) - else: - resp, body = self.api.json_request('POST', url, headers=headers) - if return_raw: - if response_key: - return body[response_key] - return body - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - def _get(self, url, response_key=None, return_raw=False, headers={}): - resp, body = self.api.json_request('GET', url, headers=headers) - if return_raw: - if response_key: - return body[response_key] - return body - if response_key: - return self.resource_class(self, body[response_key]) - return self.resource_class(self, body) - - -class Resource(object): - """ - A resource represents a particular instance of an object (tenant, user, - etc). This is pretty much just a bag for attributes. - - :param manager: Manager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - def __init__(self, manager, info, loaded=False): - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - def _add_details(self, info): - for (k, v) in info.iteritems(): - setattr(self, k, v) - - def __getattr__(self, k): - if k not in self.__dict__: - # NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded(): - self.get() - return self.__getattr__(k) - - raise AttributeError(k) - else: - return self.__dict__[k] - - def __repr__(self): - reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and - k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) - - def get(self): - # set_loaded() first ... so if we have to bail, we know we tried. - self.set_loaded(True) - if not hasattr(self.manager, 'get'): - return - - new = self.manager.get(self.id) - if new: - self._add_details(new._info) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - if hasattr(self, 'id') and hasattr(other, 'id'): - return self.id == other.id - return self._info == other._info - - def is_loaded(self): - return self._loaded - - def set_loaded(self, val): - self._loaded = val - - def to_dict(self): - return copy.deepcopy(self._info) diff --git a/metadataclient/common/exceptions.py b/metadataclient/common/exceptions.py deleted file mode 100644 index a8e2fef..0000000 --- a/metadataclient/common/exceptions.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# 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 - - -class BaseException(Exception): - """An error occurred.""" - def __init__(self, message=None): - self.message = message - - def __str__(self): - return self.message or self.__class__.__doc__ - - -class CommandError(BaseException): - """Invalid usage of CLI.""" - - -class InvalidEndpoint(BaseException): - """The provided endpoint is invalid.""" - - -class CommunicationError(BaseException): - """Unable to communicate with server.""" - - -class ClientException(Exception): - """DEPRECATED!""" - - -class HTTPException(ClientException): - """Base exception for all HTTP-derived exceptions.""" - code = 'N/A' - - def __init__(self, details=None): - self.details = details or self.__class__.__name__ - - def __str__(self): - return "%s (HTTP %s)" % (self.details, self.code) - - -class HTTPMultipleChoices(HTTPException): - code = 300 - - def __str__(self): - self.details = ("Requested version of OpenStack Images API is not" - "available.") - return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code, - self.details) - - -class BadRequest(HTTPException): - """DEPRECATED!""" - code = 400 - - -class HTTPBadRequest(BadRequest): - pass - - -class Unauthorized(HTTPException): - """DEPRECATED!""" - code = 401 - - -class HTTPUnauthorized(Unauthorized): - pass - - -class Forbidden(HTTPException): - """DEPRECATED!""" - code = 403 - - -class HTTPForbidden(Forbidden): - pass - - -class NotFound(HTTPException): - """DEPRECATED!""" - code = 404 - - -class HTTPNotFound(NotFound): - pass - - -class HTTPMethodNotAllowed(HTTPException): - code = 405 - - -class Conflict(HTTPException): - """DEPRECATED!""" - code = 409 - - -class HTTPConflict(Conflict): - pass - - -class OverLimit(HTTPException): - """DEPRECATED!""" - code = 413 - - -class HTTPOverLimit(OverLimit): - pass - - -class HTTPInternalServerError(HTTPException): - code = 500 - - -class HTTPNotImplemented(HTTPException): - code = 501 - - -class HTTPBadGateway(HTTPException): - code = 502 - - -class ServiceUnavailable(HTTPException): - """DEPRECATED!""" - code = 503 - - -class HTTPServiceUnavailable(ServiceUnavailable): - pass - - -#NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception -# classes -_code_map = {} -for obj_name in dir(sys.modules[__name__]): - if obj_name.startswith('HTTP'): - obj = getattr(sys.modules[__name__], obj_name) - _code_map[obj.code] = obj - - -def from_response(response, body=None): - """Return an instance of an HTTPException based on httplib response.""" - cls = _code_map.get(response.status, HTTPException) - if body: - details = body.replace('\n\n', '\n') - return cls(details=details) - - return cls() - - -class NoTokenLookupException(Exception): - """DEPRECATED!""" - pass - - -class EndpointNotFound(Exception): - """DEPRECATED!""" - pass - - -class SSLConfigurationError(BaseException): - pass - - -class SSLCertificateError(BaseException): - pass diff --git a/metadataclient/common/http.py b/metadataclient/common/http.py deleted file mode 100644 index a62c2af..0000000 --- a/metadataclient/common/http.py +++ /dev/null @@ -1,492 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# 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 errno -import hashlib -import httplib -import logging -import posixpath -import socket -import StringIO -import struct -import urlparse - -try: - import json -except ImportError: - import simplejson as json - -# Python 2.5 compat fix -if not hasattr(urlparse, 'parse_qsl'): - import cgi - urlparse.parse_qsl = cgi.parse_qsl - -import OpenSSL - -from metadataclient.common import exceptions as exc -from metadataclient.common import utils -from metadataclient.openstack.common import strutils - -try: - from eventlet import patcher - # Handle case where we are running in a monkey patched environment - if patcher.is_monkey_patched('socket'): - from eventlet.green.httplib import HTTPSConnection - from eventlet.green.OpenSSL.SSL import GreenConnection as Connection - from eventlet.greenio import GreenSocket - # TODO(mclaren): A getsockopt workaround: see 'getsockopt' doc string - GreenSocket.getsockopt = utils.getsockopt - else: - raise ImportError -except ImportError: - from httplib import HTTPSConnection - from OpenSSL.SSL import Connection as Connection - - -LOG = logging.getLogger(__name__) -USER_AGENT = 'python-metadatalient' -CHUNKSIZE = 1024 * 64 # 64kB - - -class HTTPClient(object): - - def __init__(self, endpoint, **kwargs): - self.endpoint = endpoint - endpoint_parts = self.parse_endpoint(self.endpoint) - self.endpoint_scheme = endpoint_parts.scheme - self.endpoint_hostname = endpoint_parts.hostname - self.endpoint_port = endpoint_parts.port - self.endpoint_path = endpoint_parts.path - - self.connection_class = self.get_connection_class(self.endpoint_scheme) - self.connection_kwargs = self.get_connection_kwargs( - self.endpoint_scheme, **kwargs) - - self.identity_headers = kwargs.get('identity_headers') - self.auth_token = kwargs.get('token') - if self.identity_headers: - if self.identity_headers.get('X-Auth-Token'): - self.auth_token = self.identity_headers.get('X-Auth-Token') - del self.identity_headers['X-Auth-Token'] - - @staticmethod - def parse_endpoint(endpoint): - return urlparse.urlparse(endpoint) - - @staticmethod - def get_connection_class(scheme): - if scheme == 'https': - return VerifiedHTTPSConnection - else: - return httplib.HTTPConnection - - @staticmethod - def get_connection_kwargs(scheme, **kwargs): - _kwargs = {'timeout': float(kwargs.get('timeout', 600))} - - if scheme == 'https': - _kwargs['cacert'] = kwargs.get('cacert', None) - _kwargs['cert_file'] = kwargs.get('cert_file', None) - _kwargs['key_file'] = kwargs.get('key_file', None) - _kwargs['insecure'] = kwargs.get('insecure', False) - _kwargs['ssl_compression'] = kwargs.get('ssl_compression', True) - - return _kwargs - - def get_connection(self): - _class = self.connection_class - try: - return _class(self.endpoint_hostname, self.endpoint_port, - **self.connection_kwargs) - except httplib.InvalidURL: - raise exc.InvalidEndpoint() - - def log_curl_request(self, method, url, kwargs): - curl = ['curl -i -X %s' % method] - - for (key, value) in kwargs['headers'].items(): - header = '-H \'%s: %s\'' % (key, value) - curl.append(header) - - conn_params_fmt = [ - ('key_file', '--key %s'), - ('cert_file', '--cert %s'), - ('cacert', '--cacert %s'), - ] - for (key, fmt) in conn_params_fmt: - value = self.connection_kwargs.get(key) - if value: - curl.append(fmt % value) - - if self.connection_kwargs.get('insecure'): - curl.append('-k') - - if kwargs.get('body') is not None: - curl.append('-d \'%s\'' % kwargs['body']) - - curl.append('%s%s' % (self.endpoint, url)) - LOG.debug(strutils.safe_encode(' '.join(curl))) - - @staticmethod - def log_http_response(resp, body=None): - status = (resp.version / 10.0, resp.status, resp.reason) - dump = ['\nHTTP/%.1f %s %s' % status] - dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()]) - dump.append('') - if body: - dump.extend([body, '']) - LOG.debug(strutils.safe_encode('\n'.join(dump))) - - @staticmethod - def encode_headers(headers): - """Encodes headers. - - Note: This should be used right before - sending anything out. - - :param headers: Headers to encode - :returns: Dictionary with encoded headers' - names and values - """ - to_str = strutils.safe_encode - return dict([(to_str(h), to_str(v)) for h, v in headers.iteritems()]) - - def _http_request(self, url, method, **kwargs): - """Send an http request with the specified characteristics. - - Wrapper around httplib.HTTP(S)Connection.request to handle tasks such - as setting headers and error handling. - """ - # Copy the kwargs so we can reuse the original in case of redirects - kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) - kwargs['headers'].setdefault('User-Agent', USER_AGENT) - if self.auth_token: - kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) - - if self.identity_headers: - for k, v in self.identity_headers.iteritems(): - kwargs['headers'].setdefault(k, v) - - self.log_curl_request(method, url, kwargs) - conn = self.get_connection() - - # Note(flaper87): Before letting headers / url fly, - # they should be encoded otherwise httplib will - # complain. If we decide to rely on python-request - # this wont be necessary anymore. - kwargs['headers'] = self.encode_headers(kwargs['headers']) - - try: - if self.endpoint_path: - url = '%s/%s' % (self.endpoint_path, url) - conn_url = posixpath.normpath(url) - # Note(flaper87): Ditto, headers / url - # encoding to make httplib happy. - conn_url = strutils.safe_encode(conn_url) - if kwargs['headers'].get('Transfer-Encoding') == 'chunked': - conn.putrequest(method, conn_url) - for header, value in kwargs['headers'].items(): - conn.putheader(header, value) - conn.endheaders() - chunk = kwargs['body'].read(CHUNKSIZE) - # Chunk it, baby... - while chunk: - conn.send('%x\r\n%s\r\n' % (len(chunk), chunk)) - chunk = kwargs['body'].read(CHUNKSIZE) - conn.send('0\r\n\r\n') - else: - conn.request(method, conn_url, **kwargs) - resp = conn.getresponse() - except socket.gaierror as e: - message = "Error finding address for %s: %s" % ( - self.endpoint_hostname, e) - raise exc.InvalidEndpoint(message=message) - except (socket.error, socket.timeout) as e: - endpoint = self.endpoint - message = "Error communicating with %(endpoint)s %(e)s" % locals() - raise exc.CommunicationError(message=message) - - body_iter = ResponseBodyIterator(resp) - - # Read body into string if it isn't obviously image data - if resp.getheader('content-type', None) != 'application/octet-stream': - body_str = ''.join([chunk for chunk in body_iter]) - self.log_http_response(resp, body_str) - body_iter = StringIO.StringIO(body_str) - else: - self.log_http_response(resp) - - if 400 <= resp.status < 600: - LOG.error("Request returned failure status.") - raise exc.from_response(resp, body_str) - elif resp.status in (301, 302, 305): - # Redirected. Reissue the request to the new location. - return self._http_request(resp['location'], method, **kwargs) - elif resp.status == 300: - raise exc.from_response(resp) - - return resp, body_iter - - def json_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', 'application/json') - - if 'body' in kwargs: - kwargs['body'] = json.dumps(kwargs['body']) - - resp, body_iter = self._http_request(url, method, **kwargs) - - if 'application/json' in resp.getheader('content-type', None): - body = ''.join([chunk for chunk in body_iter]) - try: - body = json.loads(body) - except ValueError: - LOG.error('Could not decode response body as JSON') - else: - body = None - - return resp, body - - def raw_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', - 'application/octet-stream') - if 'body' in kwargs: - if (hasattr(kwargs['body'], 'read') - and method.lower() in ('post', 'put')): - # We use 'Transfer-Encoding: chunked' because - # body size may not always be known in advance. - kwargs['headers']['Transfer-Encoding'] = 'chunked' - return self._http_request(url, method, **kwargs) - - -class OpenSSLConnectionDelegator(object): - """ - An OpenSSL.SSL.Connection delegator. - - Supplies an additional 'makefile' method which httplib requires - and is not present in OpenSSL.SSL.Connection. - - Note: Since it is not possible to inherit from OpenSSL.SSL.Connection - a delegator must be used. - """ - def __init__(self, *args, **kwargs): - self.connection = Connection(*args, **kwargs) - - def __getattr__(self, name): - return getattr(self.connection, name) - - def makefile(self, *args, **kwargs): - # Making sure socket is closed when this file is closed - # since we now avoid closing socket on connection close - # see new close method under VerifiedHTTPSConnection - kwargs['close'] = True - - return socket._fileobject(self.connection, *args, **kwargs) - - -class VerifiedHTTPSConnection(HTTPSConnection): - """ - Extended HTTPSConnection which uses the OpenSSL library - for enhanced SSL support. - Note: Much of this functionality can eventually be replaced - with native Python 3.3 code. - """ - def __init__(self, host, port=None, key_file=None, cert_file=None, - cacert=None, timeout=None, insecure=False, - ssl_compression=True): - HTTPSConnection.__init__(self, host, port, - key_file=key_file, - cert_file=cert_file) - self.key_file = key_file - self.cert_file = cert_file - self.timeout = timeout - self.insecure = insecure - self.ssl_compression = ssl_compression - self.cacert = cacert - self.setcontext() - - @staticmethod - def host_matches_cert(host, x509): - """ - Verify that the the x509 certificate we have received - from 'host' correctly identifies the server we are - connecting to, ie that the certificate's Common Name - or a Subject Alternative Name matches 'host'. - """ - # First see if we can match the CN - if x509.get_subject().commonName == host: - return True - - # Also try Subject Alternative Names for a match - san_list = None - for i in xrange(x509.get_extension_count()): - ext = x509.get_extension(i) - if ext.get_short_name() == 'subjectAltName': - san_list = str(ext) - for san in ''.join(san_list.split()).split(','): - if san == "DNS:%s" % host: - return True - - # Server certificate does not match host - msg = ('Host "%s" does not match x509 certificate contents: ' - 'CommonName "%s"' % (host, x509.get_subject().commonName)) - if san_list is not None: - msg = msg + ', subjectAltName "%s"' % san_list - raise exc.SSLCertificateError(msg) - - def verify_callback(self, connection, x509, errnum, - depth, preverify_ok): - # NOTE(leaman): preverify_ok may be a non-boolean type - preverify_ok = bool(preverify_ok) - if x509.has_expired(): - msg = "SSL Certificate expired on '%s'" % x509.get_notAfter() - raise exc.SSLCertificateError(msg) - - if depth == 0 and preverify_ok: - # We verify that the host matches against the last - # certificate in the chain - return self.host_matches_cert(self.host, x509) - else: - # Pass through OpenSSL's default result - return preverify_ok - - def setcontext(self): - """ - Set up the OpenSSL context. - """ - self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) - - if self.ssl_compression is False: - self.context.set_options(0x20000) # SSL_OP_NO_COMPRESSION - - if self.insecure is not True: - self.context.set_verify(OpenSSL.SSL.VERIFY_PEER, - self.verify_callback) - else: - self.context.set_verify(OpenSSL.SSL.VERIFY_NONE, - lambda *args: True) - - if self.cert_file: - try: - self.context.use_certificate_file(self.cert_file) - except Exception as e: - msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e) - raise exc.SSLConfigurationError(msg) - if self.key_file is None: - # We support having key and cert in same file - try: - self.context.use_privatekey_file(self.cert_file) - except Exception as e: - msg = ('No key file specified and unable to load key ' - 'from "%s" %s' % (self.cert_file, e)) - raise exc.SSLConfigurationError(msg) - - if self.key_file: - try: - self.context.use_privatekey_file(self.key_file) - except Exception as e: - msg = 'Unable to load key from "%s" %s' % (self.key_file, e) - raise exc.SSLConfigurationError(msg) - - if self.cacert: - try: - self.context.load_verify_locations(self.cacert) - except Exception as e: - msg = 'Unable to load CA from "%s"' % (self.cacert, e) - raise exc.SSLConfigurationError(msg) - else: - self.context.set_default_verify_paths() - - def connect(self): - """ - Connect to an SSL port using the OpenSSL library and apply - per-connection parameters. - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if self.timeout is not None: - # '0' microseconds - sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, - struct.pack('fL', self.timeout, 0)) - self.sock = OpenSSLConnectionDelegator(self.context, sock) - self.sock.connect((self.host, self.port)) - - def close(self): - if self.sock: - # Removing reference to socket but don't close it yet. - # Response close will close both socket and associated - # file. Closing socket too soon will cause response - # reads to fail with socket IO error 'Bad file descriptor'. - self.sock = None - - # Calling close on HTTPConnection to continue doing that cleanup. - HTTPSConnection.close(self) - - -class ResponseBodyIterator(object): - """ - A class that acts as an iterator over an HTTP response. - - This class will also check response body integrity when iterating over - the instance and if a checksum was supplied using `set_checksum` method, - else by default the class will not do any integrity check. - """ - - def __init__(self, resp): - self._resp = resp - self._checksum = None - self._size = int(resp.getheader('content-length', 0)) - self._end_reached = False - - def set_checksum(self, checksum): - """ - Set checksum to check against when iterating over this instance. - - :raise: AttributeError if iterator is already consumed. - """ - if self._end_reached: - raise AttributeError("Can't set checksum for an already consumed" - " iterator") - self._checksum = checksum - - def __len__(self): - return int(self._size) - - def __iter__(self): - md5sum = hashlib.md5() - while True: - try: - chunk = self.next() - except StopIteration: - self._end_reached = True - # NOTE(mouad): Check image integrity when the end of response - # body is reached. - md5sum = md5sum.hexdigest() - if self._checksum is not None and md5sum != self._checksum: - raise IOError(errno.EPIPE, - 'Corrupted image. Checksum was %s ' - 'expected %s' % (md5sum, self._checksum)) - raise - else: - yield chunk - md5sum.update(chunk) - - def next(self): - chunk = self._resp.read(CHUNKSIZE) - if chunk: - return chunk - else: - raise StopIteration() diff --git a/metadataclient/common/utils.py b/metadataclient/common/utils.py deleted file mode 100644 index 0602974..0000000 --- a/metadataclient/common/utils.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# 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 uuid - -import os -from metadataclient.common import exceptions -import prettytable -from metadataclient.openstack.common import importutils - - -# Decorator for cli-args -def arg(*args, **kwargs): - def _decorator(func): - # Because of the sematics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) - return func - return _decorator - - -def pretty_choice_list(l): - return ', '.join("'%s'" % i for i in l) - - -def print_list(objs, fields, field_labels, formatters={}, sortby=0): - pt = prettytable.PrettyTable([f for f in field_labels], caching=False) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](o)) - else: - data = getattr(o, field, None) or '' - row.append(data) - pt.add_row(row) - print pt.get_string(sortby=field_labels[sortby]) - - -def print_dict(d, formatters={}): - pt = prettytable.PrettyTable(['Property', 'Value'], caching=False) - pt.align = 'l' - - for field in d.keys(): - if field in formatters: - pt.add_row([field, formatters[field](d[field])]) - else: - pt.add_row([field, d[field]]) - print pt.get_string(sortby='Property') - - -def find_resource(manager, name_or_id): - """Helper for the _find_* methods.""" - # first try to get entity as integer id - try: - if isinstance(name_or_id, int) or name_or_id.isdigit(): - return manager.get(int(name_or_id)) - except exceptions.NotFound: - pass - - # now try to get entity as uuid - try: - uuid.UUID(str(name_or_id)) - return manager.get(name_or_id) - except (ValueError, exceptions.NotFound): - pass - - # finally try to find entity by name - try: - return manager.find(name=name_or_id) - except exceptions.NotFound: - msg = "No %s with a name or ID of '%s' exists." % \ - (manager.resource_class.__name__.lower(), name_or_id) - raise exceptions.CommandError(msg) - - -def string_to_bool(arg): - return arg.strip().lower() in ('t', 'true', 'yes', '1') - - -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 import_versioned_module(version, submodule=None): - module = 'muranoclient.v%s' % version - if submodule: - module = '.'.join((module, submodule)) - return importutils.import_module(module) - - -def exit(msg=''): - if msg: - print >> sys.stderr, msg - sys.exit(1) - - -def getsockopt(self, *args, **kwargs): - """ - A function which allows us to monkey patch eventlet's - GreenSocket, adding a required 'getsockopt' method. - TODO: (mclaren) we can remove this once the eventlet fix - (https://bitbucket.org/eventlet/eventlet/commits/609f230) - lands in mainstream packages. - NOTE: Already in 0.13, but we can't be sure that all clients - that use python client also use newest eventlet - """ - return self.fd.getsockopt(*args, **kwargs) diff --git a/metadataclient/exc.py b/metadataclient/exc.py deleted file mode 100644 index a8e2fef..0000000 --- a/metadataclient/exc.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright 2012 OpenStack LLC. -# 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 - - -class BaseException(Exception): - """An error occurred.""" - def __init__(self, message=None): - self.message = message - - def __str__(self): - return self.message or self.__class__.__doc__ - - -class CommandError(BaseException): - """Invalid usage of CLI.""" - - -class InvalidEndpoint(BaseException): - """The provided endpoint is invalid.""" - - -class CommunicationError(BaseException): - """Unable to communicate with server.""" - - -class ClientException(Exception): - """DEPRECATED!""" - - -class HTTPException(ClientException): - """Base exception for all HTTP-derived exceptions.""" - code = 'N/A' - - def __init__(self, details=None): - self.details = details or self.__class__.__name__ - - def __str__(self): - return "%s (HTTP %s)" % (self.details, self.code) - - -class HTTPMultipleChoices(HTTPException): - code = 300 - - def __str__(self): - self.details = ("Requested version of OpenStack Images API is not" - "available.") - return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code, - self.details) - - -class BadRequest(HTTPException): - """DEPRECATED!""" - code = 400 - - -class HTTPBadRequest(BadRequest): - pass - - -class Unauthorized(HTTPException): - """DEPRECATED!""" - code = 401 - - -class HTTPUnauthorized(Unauthorized): - pass - - -class Forbidden(HTTPException): - """DEPRECATED!""" - code = 403 - - -class HTTPForbidden(Forbidden): - pass - - -class NotFound(HTTPException): - """DEPRECATED!""" - code = 404 - - -class HTTPNotFound(NotFound): - pass - - -class HTTPMethodNotAllowed(HTTPException): - code = 405 - - -class Conflict(HTTPException): - """DEPRECATED!""" - code = 409 - - -class HTTPConflict(Conflict): - pass - - -class OverLimit(HTTPException): - """DEPRECATED!""" - code = 413 - - -class HTTPOverLimit(OverLimit): - pass - - -class HTTPInternalServerError(HTTPException): - code = 500 - - -class HTTPNotImplemented(HTTPException): - code = 501 - - -class HTTPBadGateway(HTTPException): - code = 502 - - -class ServiceUnavailable(HTTPException): - """DEPRECATED!""" - code = 503 - - -class HTTPServiceUnavailable(ServiceUnavailable): - pass - - -#NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception -# classes -_code_map = {} -for obj_name in dir(sys.modules[__name__]): - if obj_name.startswith('HTTP'): - obj = getattr(sys.modules[__name__], obj_name) - _code_map[obj.code] = obj - - -def from_response(response, body=None): - """Return an instance of an HTTPException based on httplib response.""" - cls = _code_map.get(response.status, HTTPException) - if body: - details = body.replace('\n\n', '\n') - return cls(details=details) - - return cls() - - -class NoTokenLookupException(Exception): - """DEPRECATED!""" - pass - - -class EndpointNotFound(Exception): - """DEPRECATED!""" - pass - - -class SSLConfigurationError(BaseException): - pass - - -class SSLCertificateError(BaseException): - pass diff --git a/metadataclient/openstack/__init__.py b/metadataclient/openstack/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/metadataclient/openstack/common/__init__.py b/metadataclient/openstack/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/metadataclient/openstack/common/gettextutils.py b/metadataclient/openstack/common/gettextutils.py deleted file mode 100644 index edb0b30..0000000 --- a/metadataclient/openstack/common/gettextutils.py +++ /dev/null @@ -1,50 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Red Hat, 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. - -""" -gettext for openstack-common modules. - -Usual usage in an openstack.common module: - - from glanceclient.openstack.common.gettextutils import _ -""" - -import gettext -import os - -_localedir = os.environ.get('glanceclient'.upper() + '_LOCALEDIR') -_t = gettext.translation('glanceclient', localedir=_localedir, fallback=True) - - -def _(msg): - return _t.ugettext(msg) - - -def install(domain): - """Install a _() function using the given translation domain. - - Given a translation domain, install a _() function using gettext's - install() function. - - The main difference from gettext.install() is that we allow - overriding the default localedir (e.g. /usr/share/locale) using - a translation-domain-specific environment variable (e.g. - NOVA_LOCALEDIR). - """ - gettext.install(domain, - localedir=os.environ.get(domain.upper() + '_LOCALEDIR'), - unicode=True) diff --git a/metadataclient/openstack/common/importutils.py b/metadataclient/openstack/common/importutils.py deleted file mode 100644 index dbee325..0000000 --- a/metadataclient/openstack/common/importutils.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Import related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class.""" - mod_str, _sep, class_str = import_str.rpartition('.') - try: - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """ - Import a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/metadataclient/openstack/common/strutils.py b/metadataclient/openstack/common/strutils.py deleted file mode 100644 index 281bb51..0000000 --- a/metadataclient/openstack/common/strutils.py +++ /dev/null @@ -1,150 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -System-level utilities and helper functions. -""" - -import sys - -from metadataclient.openstack.common.gettextutils import _ - - -TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') -FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') - - -def int_from_bool_as_string(subject): - """ - Interpret a string as a boolean and return either 1 or 0. - - Any string value in: - - ('True', 'true', 'On', 'on', '1') - - is interpreted as a boolean True. - - Useful for JSON-decoded stuff and config file parsing - """ - return bool_from_string(subject) and 1 or 0 - - -def bool_from_string(subject, strict=False): - """ - Interpret a string as a boolean. - - A case-insensitive match is performed such that strings matching 't', - 'true', 'on', 'y', 'yes', or '1' are considered True and, when - `strict=False`, anything else is considered False. - - Useful for JSON-decoded stuff and config file parsing. - - If `strict=True`, unrecognized values, including None, will raise a - ValueError which is useful when parsing values passed in from an API call. - Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. - """ - if not isinstance(subject, basestring): - subject = str(subject) - - lowered = subject.strip().lower() - - if lowered in TRUE_STRINGS: - return True - elif lowered in FALSE_STRINGS: - return False - elif strict: - acceptable = ', '.join( - "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) - msg = _("Unrecognized value '%(val)s', acceptable values are:" - " %(acceptable)s") % {'val': subject, - 'acceptable': acceptable} - raise ValueError(msg) - else: - return False - - -def safe_decode(text, incoming=None, errors='strict'): - """ - Decodes incoming str using `incoming` if they're - not already unicode. - - :param incoming: Text's current encoding - :param errors: Errors handling policy. See here for valid - values http://docs.python.org/2/library/codecs.html - :returns: text or a unicode `incoming` encoded - representation of it. - :raises TypeError: If text is not an isntance of basestring - """ - if not isinstance(text, basestring): - raise TypeError("%s can't be decoded" % type(text)) - - if isinstance(text, unicode): - return text - - if not incoming: - incoming = (sys.stdin.encoding or - sys.getdefaultencoding()) - - try: - return text.decode(incoming, errors) - except UnicodeDecodeError: - # Note(flaper87) If we get here, it means that - # sys.stdin.encoding / sys.getdefaultencoding - # didn't return a suitable encoding to decode - # text. This happens mostly when global LANG - # var is not set correctly and there's no - # default encoding. In this case, most likely - # python will use ASCII or ANSI encoders as - # default encodings but they won't be capable - # of decoding non-ASCII characters. - # - # Also, UTF-8 is being used since it's an ASCII - # extension. - return text.decode('utf-8', errors) - - -def safe_encode(text, incoming=None, - encoding='utf-8', errors='strict'): - """ - Encodes incoming str/unicode using `encoding`. If - incoming is not specified, text is expected to - be encoded with current python's default encoding. - (`sys.getdefaultencoding`) - - :param incoming: Text's current encoding - :param encoding: Expected encoding for text (Default UTF-8) - :param errors: Errors handling policy. See here for valid - values http://docs.python.org/2/library/codecs.html - :returns: text or a bytestring `encoding` encoded - representation of it. - :raises TypeError: If text is not an isntance of basestring - """ - if not isinstance(text, basestring): - raise TypeError("%s can't be encoded" % type(text)) - - if not incoming: - incoming = (sys.stdin.encoding or - sys.getdefaultencoding()) - - if isinstance(text, unicode): - return text.encode(encoding, errors) - elif text and encoding != incoming: - # Decode text before encoding it with `encoding` - text = safe_decode(text, incoming, errors) - return text.encode(encoding, errors) - - return text diff --git a/metadataclient/shell.py b/metadataclient/shell.py deleted file mode 100644 index de555e9..0000000 --- a/metadataclient/shell.py +++ /dev/null @@ -1,322 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - -""" -Command-line interface to the Murano Metadata Repository API. -""" - -import argparse -import logging -import sys -import httplib2 -from keystoneclient.v2_0 import client as ksclient - -import metadataclient -from metadataclient.common import exceptions -from metadataclient.common import utils - - -class MuranoRepositoryShell(object): - - def get_base_parser(self): - parser = argparse.ArgumentParser( - prog='muranorepository', - description=__doc__.strip(), - epilog='See "metadata help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=HelpFormatter, - ) - - # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS, - ) - - parser.add_argument('-d', '--debug', - default=bool(utils.env('METADATACLIENT_DEBUG')), - action='store_true', - help='Defaults to env[METADATACLIENT_DEBUG]') - - parser.add_argument('-v', '--verbose', - default=False, action="store_true", - help="Print more verbose output") - - parser.add_argument('--get-schema', - default=False, action="store_true", - dest='get_schema', - help='Force retrieving the schema used to generate' - ' portions of the help text rather than using' - ' a cached copy. Ignored with api version 1') - - parser.add_argument('-k', '--insecure', - default=False, - action='store_true', - help='Explicitly allow metadataclient 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.') - - parser.add_argument('--cert-file', - help='Path of certificate file to use in SSL ' - 'connection. This file can optionally be ' - 'prepended with the private key.') - - parser.add_argument('--key-file', - help='Path of client key to use in SSL ' - 'connection. This option is not necessary ' - 'if your key is prepended to your cert file.') - - parser.add_argument('--os-cacert', - metavar='', - dest='os_cacert', - default=utils.env('OS_CACERT'), - help='Path of CA TLS certificate(s) used to ' - 'verify the remote server\'s certificate. ' - 'Without this option metadata looks for the ' - 'default system CA certificates.') - - parser.add_argument('--ca-file', - dest='os_cacert', - help='DEPRECATED! Use --os-cacert.') - - parser.add_argument('--timeout', - default=600, - help='Number of seconds to wait for a response') - - parser.add_argument('--os-tenant-id', - default=utils.env('OS_TENANT_ID'), - help='Defaults to env[OS_TENANT_ID]') - - parser.add_argument('--os-tenant-name', - default=utils.env('OS_TENANT_NAME'), - help='Defaults to env[OS_TENANT_NAME]') - - parser.add_argument('--os-auth-url', - default=utils.env('OS_AUTH_URL'), - help='Defaults to env[OS_AUTH_URL]') - - parser.add_argument('--os-region-name', - default=utils.env('OS_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME]') - - parser.add_argument('--os-auth-token', - default=utils.env('OS_AUTH_TOKEN'), - help='Defaults to env[OS_AUTH_TOKEN]') - - parser.add_argument('--os-service-type', - default=utils.env('OS_SERVICE_TYPE'), - help='Defaults to env[OS_SERVICE_TYPE]') - - parser.add_argument('--os-endpoint-type', - default=utils.env('OS_ENDPOINT_TYPE'), - help='Defaults to env[OS_ENDPOINT_TYPE]') - - parser.add_argument('--murano-metadata-url', - default=utils.env('MURANO_METADATA_URL'), - help='Defaults to env[MURANO_METADATA_URL]') - - parser.add_argument('--murano_metadata-api-version', - default=utils.env( - 'MURANO_METADATA_API_VERSION', default='1'), - help='Defaults to env[MURANO_METADATA_API_VERSION]' - ' or 1') - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='') - submodule = utils.import_versioned_module(version, 'shell') - self._find_actions(subparsers, submodule) - self._find_actions(subparsers, self) - - return parser - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hypen-separated instead of underscores. - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser(command, - help=help, - description=desc, - add_help=False, - formatter_class=HelpFormatter - ) - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS, - ) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - def _get_ksclient(self, **kwargs): - """Get an endpoint and auth token from Keystone. - - :param username: name of user - :param password: user's password - :param tenant_id: unique identifier of tenant - :param tenant_name: name of tenant - :param auth_url: endpoint to authenticate against - """ - return ksclient.Client(username=kwargs.get('username'), - password=kwargs.get('password'), - tenant_id=kwargs.get('tenant_id'), - tenant_name=kwargs.get('tenant_name'), - auth_url=kwargs.get('auth_url'), - insecure=kwargs.get('insecure')) - - def _get_endpoint(self, client, **kwargs): - """Get an endpoint using the provided keystone client.""" - return client.service_catalog.url_for( - service_type=kwargs.get('service_type') or 'metering', - endpoint_type=kwargs.get('endpoint_type') or 'publicURL') - - def _setup_debugging(self, debug): - if debug: - logging.basicConfig( - format="%(levelname)s (%(module)s:%(lineno)d) %(message)s", - level=logging.DEBUG) - - httplib2.debuglevel = 1 - else: - logging.basicConfig( - format="%(levelname)s %(message)s", - level=logging.INFO) - - def main(self, argv): - # Parse args once to find version - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - self._setup_debugging(options.debug) - - # build available subcommands based on version - api_version = options.murano_metadata_api_version - subcommand_parser = self.get_subcommand_parser(api_version) - self.parser = subcommand_parser - - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if options.help or not argv: - self.do_help(options) - return 0 - - # Parse args again and call whatever callback was selected - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help command right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - - if args.os_auth_token and args.murano_metadata_url: - token = args.os_auth_token - endpoint = args.murano_metadata_url - else: - if not args.os_username: - raise exceptions.CommandError("You must provide a username " - "via either --os-username " - "or via env[OS_USERNAME]") - - if not args.os_password: - raise exceptions.CommandError("You must provide a password " - "via either --os-password " - "or via env[OS_PASSWORD]") - - if not (args.os_tenant_id or args.os_tenant_name): - raise exceptions.CommandError("You must provide a tenant_id " - "via either --os-tenant-id " - "or via env[OS_TENANT_ID]") - - if not args.os_auth_url: - raise exceptions.CommandError("You must provide an auth url " - "via either --os-auth-url or " - "via env[OS_AUTH_URL]") - kwargs = { - 'username': args.os_username, - 'password': args.os_password, - 'tenant_id': args.os_tenant_id, - 'tenant_name': args.os_tenant_name, - 'auth_url': args.os_auth_url, - 'service_type': args.os_service_type, - 'endpoint_type': args.os_endpoint_type, - 'insecure': args.insecure - } - _ksclient = self._get_ksclient(**kwargs) - token = args.os_auth_token or _ksclient.auth_token - - url = args.murano_metadata_url - endpoint = url or self._get_endpoint(_ksclient, **kwargs) - - kwargs = { - 'token': token, - 'insecure': args.insecure, - 'timeout': args.timeout, - 'ca_file': args.ca_file, - 'cert_file': args.cert_file, - 'key_file': args.key_file, - } - - client = metadataclient.Client(api_version, endpoint, **kwargs) - - try: - args.func(client, args) - except exceptions.Unauthorized: - msg = "Invalid OpenStack Identity credentials." - raise exceptions.CommandError(msg) - - @utils.arg('command', metavar='', nargs='?', - help='Display help for ') - def do_help(self, args): - """ - Display help about this program or one of its subcommands. - """ - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - msg = "'%s' is not a valid subcommand" - raise exceptions.CommandError(msg % args.command) - else: - self.parser.print_help() - - -class HelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(HelpFormatter, self).start_section(heading) - - -def main(): - try: - MuranoRepositoryShell().main(sys.argv[1:]) - - except Exception, e: - print >> sys.stderr, e - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/metadataclient/v1/__init__.py b/metadataclient/v1/__init__.py deleted file mode 100644 index f256c11..0000000 --- a/metadataclient/v1/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - -from metadataclient.v1.client import Client diff --git a/metadataclient/v1/client.py b/metadataclient/v1/client.py deleted file mode 100644 index bff53e4..0000000 --- a/metadataclient/v1/client.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - - -from metadataclient.common import http -from metadataclient.v1 import metadata_client -from metadataclient.v1 import metadata_admin - - -class Client(http.HTTPClient): - """Client for the Murano Metadata Repository v1 API. - - :param string endpoint: A user-supplied endpoint URL for the glance - service. - :param string token: Token for authentication. - :param integer timeout: Allows customization of the timeout for client - http requests. (optional) - """ - - def __init__(self, *args, **kwargs): - """Initialize a new client for the Murano Metadata Client v1 API.""" - super(Client, self).__init__(*args, **kwargs) - self.http_client = http.HTTPClient(*args, **kwargs) - self.metadata_client = metadata_client.Controller(self) - self.metadata_admin = metadata_admin.Controller(self) diff --git a/metadataclient/v1/metadata_admin.py b/metadataclient/v1/metadata_admin.py deleted file mode 100644 index f2e556e..0000000 --- a/metadataclient/v1/metadata_admin.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. -import StringIO -import types -from os.path import dirname, basename - -from metadataclient import exc -from urllib import quote, urlencode - - -class Wrapper(object): - def __init__(self, entity_id, **kwargs): - self.id = entity_id - for key, value in kwargs.items(): - setattr(self, key, value) - - def __str__(self): - if isinstance(self.id, types.StringTypes): - return self.id.replace('##', '/') - else: - return str(self.id) - - def __repr__(self): - return 'RowWrapper({0})'.format(str(self)) - - -class Controller(object): - def __init__(self, http_client): - self.http_client = http_client - - def list_services(self): - resp, body = self.http_client.json_request('GET', '/admin/services') - services = body.get('services', None) - if services is not None: - return [Wrapper(service['full_service_name'], **service) - for service in services] - else: - raise exc.HTTPInternalServerError() - - def get_service_files(self, data_type=None, service=None): - all_files = [] - - def get_files(_data_type): - included_files = {} - if service: - resp, body = self.http_client.json_request( - 'GET', - '/admin/services/{service}'.format(service=service)) - for path in body.get(_data_type, []): - included_files[path] = True - - resp, body = self.http_client.json_request( - 'GET', '/admin/{data_type}'.format(data_type=_data_type)) - files = body.get(_data_type, []) - - return [Wrapper('{0}##{1}'.format(_data_type, path), - path=dirname(path), filename=basename(path), - selected=included_files.get(path, False), - data_type=_data_type) - for path in files] - - if data_type: - all_files.extend(get_files(data_type)) - else: - # FixME: need to get list of data types directly from server - for data_type in ('ui', 'workflows', 'heat', 'agent', 'scripts'): - all_files.extend(get_files(data_type)) - - return all_files - - def get_service_info(self, service): - resp, body = self.http_client.json_request( - 'GET', '/admin/services/{service}/info'.format(service=service)) - return body - - def download_service(self, service): - resp, body = self.http_client.raw_request( - 'GET', '/client/services/{service}'.format(service=service)) - return body - - def upload_service(self, data): - resp, body = self.http_client.raw_request( - 'POST', '/admin/services/', body=data) - return body - - def delete_service(self, service): - resp, body = self.http_client.raw_request( - 'DELETE', '/admin/services/{service}'.format(service=service)) - return body - - def toggle_enabled(self, service): - resp, body = self.http_client.raw_request( - 'POST', '/admin/services/{service}/toggle_enabled'.format( - service=service)) - return body - - def list_ui(self, path=None): - if path: - url = quote('/admin/ui/{path}'.format(path=path)) - else: - url = '/admin/ui' - resp, body = self.http_client.json_request('GET', url) - return body - - def list_agent(self, path=None): - if path: - url = quote('/admin/agent/{path}'.format(path=path)) - else: - url = '/admin/agent' - resp, body = self.http_client.json_request('GET', url) - return body - - def list_scripts(self, path=None): - if path: - url = quote('/admin/scripts/{path}'.format(path=path)) - else: - url = '/admin/scripts' - resp, body = self.http_client.json_request('GET', url) - return body - - def list_workflows(self, path=None): - if path: - url = quote('/admin/workflows/{path}'.format(path=path)) - else: - url = '/admin/workflows' - resp, body = self.http_client.json_request('GET', url) - return body - - def list_heat(self, path=None): - if path: - url = quote('/admin/heat/{path}'.format(path=path)) - else: - url = '/admin/heat' - resp, body = self.http_client.json_request('GET', url) - return body - - def list_manifests(self, path=None): - if path: - url = quote('/admin/manifests/{path}'.format(path=path)) - else: - url = '/admin/manifests' - resp, body = self.http_client.json_request('GET', url) - return body - - def upload_file(self, data_type, file_data, file_name=None): - if file_name: - params = urlencode({'filename': file_name}) - url = '/admin/{0}?{1}'.format(data_type, params) - else: - url = '/admin/{0}'.format(data_type) - hdrs = {'Content-Type': 'application/octet-stream'} - resp, body = self.http_client.raw_request('POST', url, - headers=hdrs, - body=file_data) - return resp - - def _update_service(self, service_files, service_id, service_info): - service_info.update(service_files) - url = quote('/admin/services/{service}'.format(service=service_id)) - resp, body = self.http_client.json_request('PUT', url, - body=service_info) - return resp, body - - def upload_file_to_service(self, data_type, file_data, - file_name, service_id): - self.upload_file(data_type, file_data, file_name) - service_info = self.get_service_info(service_id) - resp, service_files = self.http_client.json_request( - 'GET', '/admin/services/{service}'.format(service=service_id)) - existing_files = service_files.get(data_type) - if existing_files: - service_files[data_type].append(file_name) - else: - service_files[data_type] = [file_name] - resp, body = self._update_service(service_files, - service_id, - service_info) - return body - - def upload_file_to_dir(self, data_type, path, file_data): - url = quote('/admin/{0}/{1}'.format(data_type, path)) - hdrs = {'Content-Type': 'application/octet-stream'} - self.http_client.raw_request('POST', url, - headers=hdrs, - body=file_data) - - def get_file(self, data_type, file_path): - url = quote('/admin/{0}/{1}'.format(data_type, file_path)) - resp, body = self.http_client.raw_request('GET', url) - body_str = ''.join([chunk for chunk in body]) - return StringIO.StringIO(body_str) - - def create_directory(self, data_type, dir_name): - url = '/admin/{0}/{1}'.format(data_type, dir_name) - self.http_client.json_request('PUT', url) - - def delete(self, data_type, path): - url = quote('/admin/{0}/{1}'.format(data_type, path)) - self.http_client.raw_request('DELETE', url) - - def delete_from_service(self, data_type, filename, service_id): - service_info = self.get_service_info(service_id) - resp, service_files = self.http_client.json_request( - 'GET', '/admin/services/{service}'.format(service=service_id)) - files = service_files.get(data_type) - if filename in files: - service_files[data_type].remove(filename) - resp, body = self._update_service(service_files, - service_id, - service_info) - if resp.status == 200: - url = quote('/admin/{0}/{1}'.format(data_type, filename)) - self.http_client.raw_request('DELETE', url) - return body - - def create_or_update_service(self, service, json_data): - # Increment version in case of modification - json_data['service_version'] += 1 - - json_data['version'] = u'0.1' # Version of metadata - url = quote('/admin/services/{service}'.format(service=service)) - resp, body = self.http_client.json_request('PUT', url, body=json_data) - return body diff --git a/metadataclient/v1/metadata_client.py b/metadataclient/v1/metadata_client.py deleted file mode 100644 index 63ebefc..0000000 --- a/metadataclient/v1/metadata_client.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - - -class Controller(object): - def __init__(self, http_client): - self.http_client = http_client - - def _get_data(self, endpoint_type, hash_sum=None): - if hash_sum: - url = '/client/{0}?hash={1}'.format(endpoint_type, hash_sum) - else: - url = '/client/{0}'.format(endpoint_type) - return self.http_client.raw_request('GET', url) - - def get_ui_data(self, hash_sum=None): - """ - Download tar.gz with ui metadata. Returns a tuple - (status, body_iterator) where status can be either 200 or 304. In the - 304 case there is no sense in iterating with body_iterator. - - """ - return self._get_data('ui', hash_sum=hash_sum) - - def get_conductor_data(self, hash_sum=None): - """ - Download tar.gz with conductor metadata. Returns a tuple - (status, body_iterator) where status can be either 200 or 304. In the - 304 case there is no sense in iterating with body_iterator. - - """ - return self._get_data('conductor', hash_sum=hash_sum) diff --git a/metadataclient/v1/shell.py b/metadataclient/v1/shell.py deleted file mode 100644 index 10121ba..0000000 --- a/metadataclient/v1/shell.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2013 Mirantis, 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. - -from muranoclient.common import utils - - -def do_environment_list(cc, args={}): - """List the environments""" - environments = cc.environments.list() - field_labels = ['ID', 'Name', 'Created', 'Updated'] - fields = ['id', 'name', 'created', 'updated'] - utils.print_list(environments, fields, field_labels, sortby=0) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3c1a99c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -pbr>=0.6,<1.0 -argparse -PrettyTable>=0.7,<0.8 -python-keystoneclient>=0.6.0 -httplib2>=0.7.5 -iso8601>=0.1.8 -six>=1.5.2 -Babel>=1.3 -pyOpenSSL>=0.11 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 290fd0a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,43 +0,0 @@ -[metadata] -name = murano-metadataclient -version = 0.4 -summary = murano-metadataclient -description-file = - README.rst -license = Apache License, Version 2.0 -author = Mirantis, Inc. -author-email = murano-all@lists.openstack.org -home-page = htts://launchpad.net/murano -classifier = - Development Status :: 4 - Beta - Environment :: Console - Intended Audience :: Developers - Intended Audience :: Information Technology - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python - -[files] -packages = - metadataclient - -[entry_points] -console_scripts = - murano-metadataclient = metadataclient.shell:main - -[global] -setup-hooks = - pbr.hooks.setup_hook - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html diff --git a/setup.py b/setup.py deleted file mode 100644 index 70c2b3f..0000000 --- a/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# 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 - -setuptools.setup( - setup_requires=['pbr'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 8a2bd2f..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ - -mock>=1.0 -anyjson>=0.3.3 -mox>=0.5.3 -nose -nose-exclude -nosexcover -openstack.nose_plugin>=0.7 -nosehtmloutput>=0.0.3 -pep8==1.3.3 -sphinx>=1.1.2,<1.2 -unittest2 -httpretty>=0.6.3 - diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 890b136..0000000 --- a/tox.ini +++ /dev/null @@ -1,52 +0,0 @@ -[tox] -envlist = pep8,pyflakes - -[testenv] -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -#commands = nosetests - -[testenv:pep8] -deps = pep8==1.3.3 -commands = pep8 --repeat --show-source metadataclient setup.py - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -#commands = nosetests --cover-erase --cover-package=metadataclient --with-xcoverage - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 - -[testenv:jenkins27] -basepython = python2.7 - -[testenv:py33] -basepython = python2.7 -deps= -commands= - -[testenv:docs] -commands = ls - -[testenv:pyflakes] -deps = flake8 -#commands = flake8 - -[flake8] -# H301 one import per line -# H302 import only modules -ignore = H301,H302,F401,F812 -show-source = true -builtins = _ -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools