From 3f262284f3ce78ce23b4e3c8e9fed112fc56e37d Mon Sep 17 00:00:00 2001 From: Zhou Ya Date: Thu, 24 Nov 2016 13:57:31 +0800 Subject: add escalator cli framework JIRA:ESCALATOR-36 This patch will support escalatorclient,and this is just the frame of escalatorclient,with this code you can use 'python setup.py sdist' to generate escalatorclient package. Change-Id: Id7b602345f7cb78bb548b589d1297a201056699a Signed-off-by: Zhou Ya --- client/escalatorclient/v1/__init__.py | 16 ++ client/escalatorclient/v1/client.py | 36 +++++ client/escalatorclient/v1/shell.py | 182 +++++++++++++++++++++ client/escalatorclient/v1/versions.py | 294 ++++++++++++++++++++++++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 client/escalatorclient/v1/__init__.py create mode 100644 client/escalatorclient/v1/client.py create mode 100644 client/escalatorclient/v1/shell.py create mode 100644 client/escalatorclient/v1/versions.py (limited to 'client/escalatorclient/v1') diff --git a/client/escalatorclient/v1/__init__.py b/client/escalatorclient/v1/__init__.py new file mode 100644 index 0000000..cd35765 --- /dev/null +++ b/client/escalatorclient/v1/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from escalatorclient.v1.client import Client # noqa diff --git a/client/escalatorclient/v1/client.py b/client/escalatorclient/v1/client.py new file mode 100644 index 0000000..f74300f --- /dev/null +++ b/client/escalatorclient/v1/client.py @@ -0,0 +1,36 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from escalatorclient.common import http +from escalatorclient.common import utils +from escalatorclient.v1.versions import VersionManager + + +class Client(object): + """Client for the escalator v1 API. + + :param string endpoint: A user-supplied endpoint URL for the escalator + service. + :param string token: Token for authentication. + :param integer timeout: Allows customization of the timeout for client + http requests. (optional) + """ + + def __init__(self, endpoint, *args, **kwargs): + """Initialize a new client for the escalator v1 API.""" + endpoint, version = utils.strip_version(endpoint) + self.version = version or 1.0 + self.http_client = http.HTTPClient(endpoint, *args, **kwargs) + self.node = VersionManager(self.http_client) diff --git a/client/escalatorclient/v1/shell.py b/client/escalatorclient/v1/shell.py new file mode 100644 index 0000000..9f9ea4f --- /dev/null +++ b/client/escalatorclient/v1/shell.py @@ -0,0 +1,182 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from __future__ import print_function + +import copy +import functools +import pprint +import os +import json + +from oslo_utils import encodeutils +from oslo_utils import strutils +import escalatorclient.v1.versions +from escalatorclient.common import utils +from escalatorclient import exc + +_bool_strict = functools.partial(strutils.bool_from_string, strict=True) + + +def _escalator_show(escalator, max_column_width=80): + info = copy.deepcopy(escalator._info) + exclusive_field = ('deleted', 'deleted_at') + for field in exclusive_field: + if field in info: + info.pop(field) + utils.print_dict(info, max_column_width=max_column_width) + + +@utils.arg('--type', metavar='', + help='Type of escalator version, supported type are "internal": ' + 'the internal version of escalator.') +def do_version(dc, args): + """Get version of escalator.""" + fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) + + # Filter out values we can't use + VERSION_PARAMS = escalatorclient.v1.version.VERSION_PARAMS + fields = dict(filter(lambda x: x[0] in VERSION_PARAMS, fields.items())) + version = dc.version.version(**fields) + _escalator_show(version) + + +@utils.arg('id', metavar='', + help='Filter version to those that have this id.') +def do_version_detail(dc, args): + """Get backend_types of escalator.""" + version = utils.find_resource(dc.versions, args.id) + _escalator_show(version) + + +@utils.arg('name', metavar='', + help='name of version.') +@utils.arg('type', metavar='', + help='version type.eg redhat7.0...') +@utils.arg('--size', metavar='', + help='size of the version file.') +@utils.arg('--checksum', metavar='', + help='md5 of version file') +@utils.arg('--version', metavar='', + help='version number of version file') +@utils.arg('--description', metavar='', + help='description of version file') +@utils.arg('--status', metavar='', + help='version file status.default:init') +def do_version_add(dc, args): + """Add a version.""" + + fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) + + # Filter out values we can't use + CREATE_PARAMS = escalatorclient.v1.versions.CREATE_PARAMS + fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items())) + + version = dc.versions.add(**fields) + _escalator_show(version) + + +@utils.arg('id', metavar='', + help='ID of versions.') +@utils.arg('--name', metavar='', + help='name of version.') +@utils.arg('--type', metavar='', + help='version type.eg redhat7.0...') +@utils.arg('--size', metavar='', + help='size of the version file.') +@utils.arg('--checksum', metavar='', + help='md5 of version file') +@utils.arg('--version', metavar='', + help='version number of version file') +@utils.arg('--description', metavar='', + help='description of version file') +@utils.arg('--status', metavar='', + help='version file status.default:init') +def do_version_update(dc, args): + """Add a version.""" + + fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) + + # Filter out values we can't use + CREATE_PARAMS = escalatorclient.v1.versions.CREATE_PARAMS + fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items())) + version_id = fields.get('id', None) + version = dc.versions.update(version_id, **fields) + _escalator_show(version) + + +@utils.arg('id', metavar='', nargs='+', + help='ID of versions.') +def do_version_delete(dc, args): + """Delete specified template(s).""" + fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) + versions = fields.get('id', None) + for version in versions: + try: + if args.verbose: + print('Requesting version delete for %s ...' % + encodeutils.safe_decode(version), end=' ') + dc.versions.delete(version) + if args.verbose: + print('[Done]') + except exc.HTTPException as e: + if args.verbose: + print('[Fail]') + print('%s: Unable to delete version %s' % (e, version)) + + +@utils.arg('--name', metavar='', + help='Filter version to those that have this name.') +@utils.arg('--status', metavar='', + help='Filter version status.') +@utils.arg('--type', metavar='', + help='Filter by type.') +@utils.arg('--version', metavar='', + help='Filter by version number.') +@utils.arg('--page-size', metavar='', default=None, type=int, + help='Number to request in each paginated request.') +@utils.arg('--sort-key', default='name', + choices=escalatorclient.v1.versions.SORT_KEY_VALUES, + help='Sort version list by specified field.') +@utils.arg('--sort-dir', default='asc', + choices=escalatorclient.v1.versions.SORT_DIR_VALUES, + help='Sort version list in specified direction.') +def do_version_list(dc, args): + """List hosts you can access.""" + filter_keys = ['name', 'type', 'status', 'version'] + filter_items = [(key, getattr(args, key)) for key in filter_keys] + filters = dict([item for item in filter_items if item[1] is not None]) + + kwargs = {'filters': filters} + if args.page_size is not None: + kwargs['page_size'] = args.page_size + + kwargs['sort_key'] = args.sort_key + kwargs['sort_dir'] = args.sort_dir + + versions = dc.versions.list(**kwargs) + + columns = ['ID', 'NAME', 'TYPE', 'VERSION', 'size', + 'checksum', 'description', 'status', 'VERSION_PATCH'] + + utils.print_list(versions, columns) + + +@utils.arg('id', metavar='', + help='Filter version patch to those that have this id.') +def do_version_patch_detail(dc, args): + """Get version_patch of escalator.""" + version = utils.find_resource(dc.version_patchs, args.id) + _escalator_show(version) diff --git a/client/escalatorclient/v1/versions.py b/client/escalatorclient/v1/versions.py new file mode 100644 index 0000000..f54ea23 --- /dev/null +++ b/client/escalatorclient/v1/versions.py @@ -0,0 +1,294 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from oslo_utils import encodeutils +from oslo_utils import strutils +import six +import six.moves.urllib.parse as urlparse + +from escalatorclient.common import utils +from escalatorclient.openstack.common.apiclient import base + +CREATE_PARAMS = ('id', 'name', 'description', 'type', 'version', 'size', + 'checksum', 'status', 'os_status', 'version_patch') + +DEFAULT_PAGE_SIZE = 200 +VERSION_PARAMS = ('type') +SORT_DIR_VALUES = ('asc', 'desc') +SORT_KEY_VALUES = ( + 'name', 'id', 'cluster_id', 'created_at', 'updated_at', 'status') + +OS_REQ_ID_HDR = 'x-openstack-request-id' + + +class Version(base.Resource): + + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def delete(self, **kwargs): + return self.manager.delete(self) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class VersionManager(base.ManagerWithFind): + resource_class = Version + + def _list(self, url, response_key, obj_class=None, body=None): + resp, body = self.client.get(url) + + if obj_class is None: + obj_class = self.resource_class + + data = body[response_key] + return ([obj_class(self, res, loaded=True) for res in data if res], + resp) + + def _version_meta_from_headers(self, headers): + meta = {'properties': {}} + safe_decode = encodeutils.safe_decode + for key, value in six.iteritems(headers): + value = safe_decode(value, incoming='utf-8') + if key.startswith('x-image-meta-property-'): + _key = safe_decode(key[22:], incoming='utf-8') + meta['properties'][_key] = value + elif key.startswith('x-image-meta-'): + _key = safe_decode(key[13:], incoming='utf-8') + meta[_key] = value + + for key in ['is_public', 'protected', 'deleted']: + if key in meta: + meta[key] = strutils.bool_from_string(meta[key]) + + return self._format_version_meta_for_user(meta) + + def _version_meta_to_headers(self, fields): + headers = {} + fields_copy = copy.deepcopy(fields) + for key, value in six.iteritems(fields_copy): + headers['%s' % key] = utils.to_str(value) + return headers + + @staticmethod + def _format_version_meta_for_user(meta): + for key in ['size', 'min_ram', 'min_disk']: + if key in meta: + try: + meta[key] = int(meta[key]) if meta[key] else 0 + except ValueError: + pass + return meta + + def get(self, version, **kwargs): + """Get the metadata for a specific version. + + :param version: image object or id to look up + :rtype: :class:`version` + """ + version_id = base.getid(version) + resp, body = self.client.get('/v1/versions/%s' + % urlparse.quote(str(version_id))) + # meta = self._version_meta_from_headers(resp.headers) + return_request_id = kwargs.get('return_req_id', None) + if return_request_id is not None: + return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None)) + # return version(self, meta) + return Version(self, self._format_version_meta_for_user( + body['version'])) + + def _build_params(self, parameters): + params = {'limit': parameters.get('page_size', DEFAULT_PAGE_SIZE)} + + if 'marker' in parameters: + params['marker'] = parameters['marker'] + + sort_key = parameters.get('sort_key') + if sort_key is not None: + if sort_key in SORT_KEY_VALUES: + params['sort_key'] = sort_key + else: + raise ValueError('sort_key must be one of the following: %s.' + % ', '.join(SORT_KEY_VALUES)) + + sort_dir = parameters.get('sort_dir') + if sort_dir is not None: + if sort_dir in SORT_DIR_VALUES: + params['sort_dir'] = sort_dir + else: + raise ValueError('sort_dir must be one of the following: %s.' + % ', '.join(SORT_DIR_VALUES)) + + filters = parameters.get('filters', {}) + params.update(filters) + + return params + + def list(self, **kwargs): + """Get a list of versions. + + :param page_size: number of items to request in each paginated request + :param limit: maximum number of versions to return + :param marker:begin returning versions that appear later in version + list than that represented by this version id + :param filters: dict of direct comparison filters that mimics the + structure of an version object + :param return_request_id: If an empty list is provided, populate this + list with the request ID value from the header + x-openstack-request-id + :rtype: list of :class:`version` + """ + absolute_limit = kwargs.get('limit') + page_size = kwargs.get('page_size', DEFAULT_PAGE_SIZE) + + def paginate(qp, return_request_id=None): + for param, value in six.iteritems(qp): + if isinstance(value, six.string_types): + # Note(flaper87) Url encoding should + # be moved inside http utils, at least + # shouldn't be here. + # + # Making sure all params are str before + # trying to encode them + qp[param] = encodeutils.safe_decode(value) + + url = '/v1/versions?%s' % urlparse.urlencode(qp) + versions, resp = self._list(url, "versions") + + if return_request_id is not None: + return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None)) + + for version in versions: + yield version + + return_request_id = kwargs.get('return_req_id', None) + + params = self._build_params(kwargs) + + seen = 0 + while True: + seen_last_page = 0 + filtered = 0 + for version in paginate(params, return_request_id): + last_version = version.id + + if (absolute_limit is not None and + seen + seen_last_page >= absolute_limit): + # Note(kragniz): we've seen enough images + return + else: + seen_last_page += 1 + yield version + + seen += seen_last_page + + if seen_last_page + filtered == 0: + # Note(kragniz): we didn't get any versions in the last page + return + + if absolute_limit is not None and seen >= absolute_limit: + # Note(kragniz): reached the limit of versions to return + return + + if page_size and seen_last_page + filtered < page_size: + # Note(kragniz): we've reached the last page of the versions + return + + # Note(kragniz): there are more versions to come + params['marker'] = last_version + seen_last_page = 0 + + def add(self, **kwargs): + """Add a version + + TODO(bcwaldon): document accepted params + """ + + fields = {} + for field in kwargs: + if field in CREATE_PARAMS: + fields[field] = kwargs[field] + elif field == 'return_req_id': + continue + else: + msg = 'create() got an unexpected keyword argument \'%s\'' + raise TypeError(msg % field) + + hdrs = self._version_meta_to_headers(fields) + + resp, body = self.client.post('/v1/versions', + headers=None, + data=hdrs) + return_request_id = kwargs.get('return_req_id', None) + if return_request_id is not None: + return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None)) + + return Version(self, self._format_version_meta_for_user( + body['version'])) + + def delete(self, version, **kwargs): + """Delete an version.""" + url = "/v1/versions/%s" % base.getid(version) + resp, body = self.client.delete(url) + return_request_id = kwargs.get('return_req_id', None) + if return_request_id is not None: + return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None)) + + def update(self, version, **kwargs): + """Update an version + + TODO(bcwaldon): document accepted params + """ + hdrs = {} + fields = {} + for field in kwargs: + if field in CREATE_PARAMS: + fields[field] = kwargs[field] + elif field == 'return_req_id': + continue + hdrs.update(self._version_meta_to_headers(fields)) + + url = '/v1/versions/%s' % base.getid(version) + resp, body = self.client.put(url, headers=None, data=hdrs) + return_request_id = kwargs.get('return_req_id', None) + if return_request_id is not None: + return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None)) + + return Version(self, self._format_version_meta_for_user( + body['version_meta'])) + + def version(self, **kwargs): + """Get internal or external version of escalator. + + TODO(bcwaldon): document accepted params + """ + fields = {} + for field in kwargs: + if field in VERSION_PARAMS: + fields[field] = kwargs[field] + else: + msg = 'install() got an unexpected keyword argument \'%s\'' + raise TypeError(msg % field) + + url = '/v1/version' + hdrs = self._restore_meta_to_headers(fields) + resp, body = self.client.post(url, headers=None, data=hdrs) + return Version(self, body) -- cgit 1.2.3-korg