From 4b445f24d664d0ed720293aeeaa5a6a195e51d06 Mon Sep 17 00:00:00 2001 From: liguomin Date: Thu, 30 Mar 2017 13:28:58 +0800 Subject: support all cluster update option Change-Id: I0ff8951d9106535ed8ee0b1fbdba345c52a00dc8 Signed-off-by: liguomin --- api/escalator/api/v1/install.py | 132 +++++++++++++++++++++++++++++++++++ api/escalator/api/v1/router.py | 7 ++ api/escalator/installer/daisy/api.py | 4 ++ client/escalatorclient/v1/client.py | 2 + client/escalatorclient/v1/shell.py | 25 +++++++ client/escalatorclient/v1/update.py | 105 ++++++++++++++++++++++++++++ 6 files changed, 275 insertions(+) create mode 100644 api/escalator/api/v1/install.py create mode 100644 client/escalatorclient/v1/update.py diff --git a/api/escalator/api/v1/install.py b/api/escalator/api/v1/install.py new file mode 100644 index 0000000..e356c26 --- /dev/null +++ b/api/escalator/api/v1/install.py @@ -0,0 +1,132 @@ +# Copyright 2013 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. + + +""" +/hosts endpoint for Daisy v1 API +""" + +from oslo_log import log as logging +from webob.exc import HTTPBadRequest + +from escalator import i18n +from escalator import notifier + +from escalator.api import policy +import escalator.api.v1 +from escalator.common import exception +from escalator.common import utils +from escalator.common import wsgi +from escalator.api.v1 import controller +import escalator.installer.daisy.api as daisy_api + +LOG = logging.getLogger(__name__) +_ = i18n._ +_LE = i18n._LE +_LI = i18n._LI +_LW = i18n._LW +SUPPORTED_PARAMS = escalator.api.v1.SUPPORTED_PARAMS +SUPPORTED_FILTERS = escalator.api.v1.SUPPORTED_FILTERS +ACTIVE_IMMUTABLE = escalator.api.v1.ACTIVE_IMMUTABLE + + +# if some backends have order constraint, please add here +# if backend not in the next three order list, we will be +# think it does't have order constraint. +BACKENDS_INSTALL_ORDER = ['proton', 'zenic', 'tecs', 'kolla'] +BACKENDS_UPGRADE_ORDER = ['proton', 'zenic', 'tecs', 'kolla'] +BACKENDS_UNINSTALL_ORDER = [] + + +class InstallTask(object): + + """ + Class for install OS and TECS. + """ + """ Definition for install states.""" + + def __init__(self, req, cluster_id, skip_pxe_ipmi): + self.req = req + self.cluster_id = cluster_id + self.skip_pxe_ipmi = skip_pxe_ipmi + + +class Controller(controller.BaseController): + + """ + WSGI controller for hosts resource in Daisy v1 API + + The hosts resource API is a RESTful web service for host data. The API + is as follows:: + + GET /hosts -- Returns a set of brief metadata about hosts + GET /hosts/detail -- Returns a set of detailed metadata about + hosts + HEAD /hosts/ -- Return metadata about an host with id + GET /hosts/ -- Return host data for host with id + POST /hosts -- Store host data and return metadata about the + newly-stored host + PUT /hosts/ -- Update host metadata and/or upload host + data for a previously-reserved host + DELETE /hosts/ -- Delete the host with id + """ + + def __init__(self): + self.notifier = notifier.Notifier() + self.policy = policy.Enforcer() + + @utils.mutating + def update_cluster(self, req, cluster_id, install_meta): + """ + upgrade cluster. + """ + self._enforce(req, 'update_cluster') + try: + status = daisy_api.update(req.context) + except exception.Invalid as e: + raise HTTPBadRequest(explanation=e.msg, request=req) + return status + + +class InstallDeserializer(wsgi.JSONRequestDeserializer): + """Handles deserialization of specific controller method requests.""" + + def _deserialize(self, request): + result = {} + result["install_meta"] = utils.get_dict_meta(request) + return result + + def update_cluster(self, request): + return self._deserialize(request) + + +class InstallSerializer(wsgi.JSONResponseSerializer): + """Handles serialization of specific controller method responses.""" + + def __init__(self): + self.notifier = notifier.Notifier() + + def update_cluster(self, response, result): + response.status = 201 + response.headers['Content-Type'] = 'application/json' + response.body = self.to_json(result) + return response + + +def create_resource(): + """Image members resource factory method""" + deserializer = InstallDeserializer() + serializer = InstallSerializer() + return wsgi.Resource(Controller(), deserializer, serializer) diff --git a/api/escalator/api/v1/router.py b/api/escalator/api/v1/router.py index 5942cb1..24d254c 100644 --- a/api/escalator/api/v1/router.py +++ b/api/escalator/api/v1/router.py @@ -15,6 +15,7 @@ from escalator.common import wsgi from escalator.api.v1 import versions from escalator.api.v1 import clusters +from escalator.api.v1 import install class API(wsgi.Router): @@ -37,4 +38,10 @@ class API(wsgi.Router): action='version', conditions={'method': ['POST']}) + install_resource = install.create_resource() + mapper.connect("/update/{cluster_id}", + controller=install_resource, + action='update_progress', + conditions={'method': ['GET']}) + super(API, self).__init__(mapper) diff --git a/api/escalator/installer/daisy/api.py b/api/escalator/installer/daisy/api.py index 661d0d5..6cd8bd1 100644 --- a/api/escalator/installer/daisy/api.py +++ b/api/escalator/installer/daisy/api.py @@ -12,3 +12,7 @@ def cluster_list(request): def cluster_get(request, cluster_id): return daisyclient(request).clusters.get(cluster_id) + + +def update(request, cluster_id): + return daisyclient(request).install.update(cluster_id) diff --git a/client/escalatorclient/v1/client.py b/client/escalatorclient/v1/client.py index 974fc4e..bda8bf9 100644 --- a/client/escalatorclient/v1/client.py +++ b/client/escalatorclient/v1/client.py @@ -17,6 +17,7 @@ from escalatorclient.common import http from escalatorclient.common import utils from escalatorclient.v1.versions import VersionManager from escalatorclient.v1.clusters import ClusterManager +from escalatorclient.v1.update import UpdateManager class Client(object): @@ -36,3 +37,4 @@ class Client(object): self.http_client = http.HTTPClient(endpoint, *args, **kwargs) self.versions = VersionManager(self.http_client) self.clusters = ClusterManager(self.http_client) + self.update = UpdateManager(self.http_client) diff --git a/client/escalatorclient/v1/shell.py b/client/escalatorclient/v1/shell.py index 10aa1bc..501f01b 100644 --- a/client/escalatorclient/v1/shell.py +++ b/client/escalatorclient/v1/shell.py @@ -20,6 +20,7 @@ import functools from oslo_utils import strutils import escalatorclient.v1.versions import escalatorclient.v1.clusters +import escalatorclient.v1.update from escalatorclient.common import utils _bool_strict = functools.partial(strutils.bool_from_string, strict=True) @@ -138,3 +139,27 @@ def do_cluster_detail(gc, args): columns = ['ID', 'Name', 'Description', 'Nodes', 'Networks', 'Auto_scale', 'Use_dns'] utils.print_list(cluster, columns) + + +@utils.arg('cluster_id', metavar='', + help='The cluster ID to update os and TECS.') +@utils.arg('--hosts', metavar='', nargs='+', + help='The host ID to update') +@utils.arg('--update-object', metavar='', + help='update object:vplat or tecs or zenic......') +@utils.arg('--version-id', metavar='', + help='if not patch, update version id is used to update.') +@utils.arg('--version-patch-id', metavar='', + help='if update version patch, version patch id is needed') +@utils.arg('--update-script', metavar='', + help='update script in /var/lib/daisy/os') +def do_update(gc, args): + """update TECS.""" + fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) + + # Filter out values we can't use + CREATE_PARAMS = escalatorclient.v1.update.CREATE_PARAMS + fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items())) + + update = gc.update.update(**fields) + _escalator_show(update) diff --git a/client/escalatorclient/v1/update.py b/client/escalatorclient/v1/update.py new file mode 100644 index 0000000..6989a0a --- /dev/null +++ b/client/escalatorclient/v1/update.py @@ -0,0 +1,105 @@ +# 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 +import six + +from escalatorclient.common import utils +from escalatorclient.openstack.common.apiclient import base + +CREATE_PARAMS = ( + 'cluster_id', 'version_id', 'version_patch_id', 'hosts', + 'update_object', 'update_script') + +OS_REQ_ID_HDR = 'x-openstack-request-id' + + +class Update(base.Resource): + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class UpdateManager(base.ManagerWithFind): + resource_class = Update + + def _Update_meta_to_headers(self, fields): + headers = {} + fields_copy = copy.deepcopy(fields) + + # NOTE(flaper87): Convert to str, headers + # that are not instance of basestring. All + # headers will be encoded later, before the + # request is sent. + + for key, value in six.iteritems(fields_copy): + headers['%s' % key] = utils.to_str(value) + return headers + + @staticmethod + def _format_update_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 list(self, **kwargs): + pass + + def query_progress(self, **kwargs): + fields = {} + for field in kwargs: + if field in CREATE_PARAMS: + fields[field] = kwargs[field] + else: + msg = 'update() got an unexpected argument \'%s\'' + raise TypeError(msg % field) + + if "cluster" in fields: + url = '/v1/update/%s' % fields['cluster_id'] + + resp, body = self.client.get(url) + return Update(self, self._format_update_meta_for_user(body)) + + def update(self, **kwargs): + """Update a cluster + + 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 = 'update() got an unexpected argument \'%s\'' + raise TypeError(msg % field) + + if "cluster_id" in fields: + url = '/v1/update/%s' % fields['cluster_id'] + + hdrs = self._Update_meta_to_headers(fields) + resp, body = self.client.post(url, headers=None, data=hdrs) + return Update(self, self._format_update_meta_for_user(body)) -- cgit 1.2.3-korg