Support all metadata api functional

This commit is contained in:
efedorova 2013-10-16 12:53:28 +04:00
parent a44cb523b9
commit e68f0c28e8
9 changed files with 370 additions and 56 deletions

View File

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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 "<Metadata Client %s>" % 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)

View File

@ -15,16 +15,38 @@
from muranoclient.common import base
class MetadataClient(base.Resource):
def __repr__(self):
return "<Metadata Client %s>" % self._info
# class MetadataClient(base.Resource):
# def __repr__(self):
# return "<Metadata Client %s>" % 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')
def get_conductor_data(self):
"""
Download tar.gz with
"""
url = '/v1/client/conductor'
resp, body = self.http_client.archive_request('GET', url)
return body