From e68f0c28e8220c8fcaba86b23cd8a8c03d1965b6 Mon Sep 17 00:00:00 2001 From: efedorova Date: Wed, 16 Oct 2013 12:53:28 +0400 Subject: [PATCH] Support all metadata api functional --- metadataclient/openstack/__init__.py | 0 metadataclient/openstack/common/__init__.py | 0 .../openstack/common/gettextutils.py | 50 ++++++ .../openstack/common/importutils.py | 67 ++++++++ metadataclient/openstack/common/strutils.py | 150 ++++++++++++++++++ metadataclient/v1/admin,py | 13 -- metadataclient/v1/client.py | 9 +- metadataclient/v1/metadata_admin.py | 97 +++++++---- metadataclient/v1/metadata_client.py | 40 +++-- 9 files changed, 370 insertions(+), 56 deletions(-) create mode 100644 metadataclient/openstack/__init__.py create mode 100644 metadataclient/openstack/common/__init__.py create mode 100644 metadataclient/openstack/common/gettextutils.py create mode 100644 metadataclient/openstack/common/importutils.py create mode 100644 metadataclient/openstack/common/strutils.py delete mode 100644 metadataclient/v1/admin,py diff --git a/metadataclient/openstack/__init__.py b/metadataclient/openstack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/metadataclient/openstack/common/__init__.py b/metadataclient/openstack/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/metadataclient/openstack/common/gettextutils.py b/metadataclient/openstack/common/gettextutils.py new file mode 100644 index 0000000..edb0b30 --- /dev/null +++ b/metadataclient/openstack/common/gettextutils.py @@ -0,0 +1,50 @@ +# 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 new file mode 100644 index 0000000..dbee325 --- /dev/null +++ b/metadataclient/openstack/common/importutils.py @@ -0,0 +1,67 @@ +# 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 new file mode 100644 index 0000000..281bb51 --- /dev/null +++ b/metadataclient/openstack/common/strutils.py @@ -0,0 +1,150 @@ +# 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/v1/admin,py b/metadataclient/v1/admin,py deleted file mode 100644 index 832bf4f..0000000 --- a/metadataclient/v1/admin,py +++ /dev/null @@ -1,13 +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. \ No newline at end of file diff --git a/metadataclient/v1/client.py b/metadataclient/v1/client.py index b9e1861..bff53e4 100644 --- a/metadataclient/v1/client.py +++ b/metadataclient/v1/client.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack LLC. -# All Rights Reserved. +# 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 @@ -13,6 +12,7 @@ # 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 @@ -31,5 +31,6 @@ class Client(http.HTTPClient): def __init__(self, *args, **kwargs): """Initialize a new client for the Murano Metadata Client v1 API.""" super(Client, self).__init__(*args, **kwargs) - self.metadata_client = metadata_client.MetadataClientManager(self) - # self.metadata_admin = metadata_admin.MetadataAdminManager(self) + 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 index 5bd3228..d277b0b 100644 --- a/metadataclient/v1/metadata_admin.py +++ b/metadataclient/v1/metadata_admin.py @@ -11,48 +11,85 @@ # 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 base +import StringIO -class MetadataAdmin(base.Resource): - def __repr__(self): - return "" % self._info - - -class MetadataAdminManager(base.Manager): - resource_class = MetadataAdmin +class Controller(object): + def __init__(self, http_client): + self.http_client = http_client def list_ui(self, path=None): if path: - return self._list('/v1/admin/ui/{path}'.format(path=path)) + url = '/v1/admin/ui/{path}'.format(path=path) else: - return self._list('/v1/admin/ui') + url = '/v1/admin/ui' + resp, body = self.http_client.json_request('GET', url) + return body def list_agent(self, path=None): if path: - return self._list('/v1/admin/agent/{path}'.format(path=path)) + url = '/v1/admin/agent/{path}'.format(path=path) else: - return self._list('/v1/admin/agent') - - def list_heat(self, path=None): - if path: - return self._list('/v1/admin/heat/{path}'.format(path=path)) - else: - return self._list('/v1/admin/heat') - - def list_workflows(self, path=None): - if path: - return self._list('/v1/admin/workflows/{path}'.format(path=path)) - else: - return self._list('/v1/admin/workflows') + url = '/v1/admin/agent' + resp, body = self.http_client.json_request('GET', url) + return body def list_scripts(self, path=None): if path: - return self._list('/v1/admin/scripts/{path}'.format(path=path)) + url = '/v1/admin/scripts/{path}'.format(path=path) else: - return self._list('/v1/admin/scripts') + url = '/v1/admin/scripts' + resp, body = self.http_client.json_request('GET', url) + return body - def get_file(self): - #application/octet - pass + def list_workflows(self, path=None): + if path: + url = '/v1/admin/workflows/{path}'.format(path=path) + else: + url = '/v1/admin/workflows' + resp, body = self.http_client.json_request('GET', url) + return body + + def list_heat(self, path=None): + if path: + url = '/v1/admin/heat/{path}'.format(path=path) + else: + url = '/v1/admin/heat' + resp, body = self.http_client.json_request('GET', url) + return body + + def list_manifests(self, path=None): + if path: + url = '/v1/admin/manifests/{path}'.format(path=path) + else: + url = '/v1/admin/manifests' + resp, body = self.http_client.json_request('GET', url) + return body + + def upload_file(self, data_type, file_data): + url = '/v1/admin/{0}'.format(data_type) + hdrs = {'Content-Type': 'application/octet-stream'} + self.http_client.raw_request('POST', url, + headers=hdrs, + body=file_data) + + def upload_file_to_dir(self, data_type, path, file_data): + url = '/v1/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 = '/v1/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 = '/v1/admin/{0}/{1}'.format(data_type, dir_name) + self.http_client.json_request('PUT', url) + + def delete_dir(self, data_type, path): + url = '/v1/admin/{0}/{1}'.format(data_type, path) + self.http_client.json_request('DELETE', url) \ No newline at end of file diff --git a/metadataclient/v1/metadata_client.py b/metadataclient/v1/metadata_client.py index 4e5d229..cd509ac 100644 --- a/metadataclient/v1/metadata_client.py +++ b/metadataclient/v1/metadata_client.py @@ -15,16 +15,38 @@ from muranoclient.common import base -class MetadataClient(base.Resource): - def __repr__(self): - return "" % self._info +# class MetadataClient(base.Resource): +# def __repr__(self): +# return "" % self._info +# +# +# class MetadataClientManager(base.Manager): +# resource_class = MetadataClient +# +# def get_ui_archive(self): +# return self._get('/v1/client/ui') +# +# def conductor(self): +# return self._get('/v1/client/conductor') +class Controller(object): + def __init__(self, http_client): + self.http_client = http_client -class MetadataClientManager(base.Manager): - resource_class = MetadataClient + def get_ui_data(self): + """ + Download tar.gz with - def get_ui_archive(self): - return self._get('/v1/client/ui') + """ + url = '/v1/client/ui' + resp, body = self.http_client.archive_request('GET', url) + return body - def conductor(self): - return self._get('/v1/client/conductor') \ No newline at end of file + def get_conductor_data(self): + """ + Download tar.gz with + + """ + url = '/v1/client/conductor' + resp, body = self.http_client.archive_request('GET', url) + return body \ No newline at end of file