Create USM Horizon Internal API
This first commit adds a new internal API for starlingx dashboard that will be referenced by multiple screens. The 'software' endpoint will then replace the patching and upgrading operations. Test plan: PASS: The starlingx-dashboard package is built successfully. PASS: Browse Horizon, check that the other services are still working (e.g. sysinv, fault management, vim). Story: 2010676 Task: 48997 Signed-off-by: Agustin Carranza <agustin.carranza@windriver.com> Change-Id: I391eff6875e6f3b051051a6532bd2d0982a80d98
This commit is contained in:
parent
8121f7d24d
commit
ce0da259f1
|
@ -11,7 +11,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copyright (c) 2017-2019 Wind River Systems, Inc.
|
||||
# Copyright (c) 2017-2024 Wind River Systems, Inc.
|
||||
#
|
||||
|
||||
from starlingx_dashboard.api import base
|
||||
|
@ -21,6 +21,7 @@ from starlingx_dashboard.api import fm
|
|||
from starlingx_dashboard.api import neutron
|
||||
from starlingx_dashboard.api import patch
|
||||
from starlingx_dashboard.api import sysinv
|
||||
from starlingx_dashboard.api import usm
|
||||
from starlingx_dashboard.api import vim
|
||||
|
||||
|
||||
|
@ -32,5 +33,6 @@ __all__ = [
|
|||
"neutron",
|
||||
"patch",
|
||||
"sysinv",
|
||||
"usm",
|
||||
"vim",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
#
|
||||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from horizon import messages
|
||||
|
||||
import logging
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
from requests_toolbelt import MultipartEncoder
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
USM_API_SERVICENAME = "usm"
|
||||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, version, url, token_id):
|
||||
self.version = version
|
||||
self.url = url
|
||||
self.token_id = token_id
|
||||
|
||||
def _make_request(self, token_id, method, api_version, api_cmd,
|
||||
encoder=None):
|
||||
url = self.url
|
||||
url += "%s/software/%s" % (api_version, api_cmd)
|
||||
|
||||
headers = {"X-Auth-Token": token_id,
|
||||
"Accept": "application/json"}
|
||||
|
||||
if method == 'GET':
|
||||
req = requests.get(url, headers=headers)
|
||||
elif method == 'POST':
|
||||
if encoder is not None:
|
||||
headers['Content-Type'] = encoder.content_type
|
||||
req = requests.post(url, headers=headers, data=encoder)
|
||||
|
||||
resp = req.json()
|
||||
|
||||
return resp
|
||||
|
||||
def get_releases(self):
|
||||
return self._make_request(self.token_id, "GET", self.version,
|
||||
"query?show=all")
|
||||
|
||||
def show_release(self, release_id):
|
||||
return self._make_request(self.token_id, "GET", self.version,
|
||||
"show/%s" % release_id)
|
||||
|
||||
def get_hosts(self):
|
||||
return self._make_request(self.token_id, "GET", self.version,
|
||||
"host_list")
|
||||
|
||||
def install_local(self):
|
||||
return self._make_request(self.token_id, "GET", self.version,
|
||||
"host_install")
|
||||
|
||||
def upload(self, releasefile):
|
||||
encoder = MultipartEncoder(fields=releasefile)
|
||||
return self._make_request(self.token_id, "POST", self.version,
|
||||
"upload", encoder=encoder)
|
||||
|
||||
def delete(self, release_ids):
|
||||
releases = "/".join(release_ids)
|
||||
return self._make_request(self.token_id, "POST", self.version,
|
||||
"delete/%s" % releases)
|
||||
|
||||
def commit(self, release_ids):
|
||||
releases = "/".join(release_ids)
|
||||
return self._make_request(self.token_id, "POST", self.version,
|
||||
"commit_patch/%s" % releases)
|
||||
|
||||
|
||||
def _software_client(request):
|
||||
o = urlparse(base.url_for(request, USM_API_SERVICENAME))
|
||||
url = "://".join((o.scheme, o.netloc))
|
||||
return Client("v1", url, token_id=request.user.token.id)
|
||||
|
||||
|
||||
class Release(object):
|
||||
_attrs = ['state',
|
||||
'sw_version',
|
||||
'status',
|
||||
'unremovable',
|
||||
'summary',
|
||||
'description',
|
||||
'install_instructions',
|
||||
'warnings',
|
||||
'reboot_required',
|
||||
'requires']
|
||||
|
||||
|
||||
class Host(object):
|
||||
_attrs = ['hostname',
|
||||
'ip',
|
||||
'latest_sysroot_commit',
|
||||
'nodetype',
|
||||
'patch_current',
|
||||
'patch_failed',
|
||||
'requires_reboot',
|
||||
'secs_since_ack',
|
||||
'stale_details',
|
||||
'sw_version',
|
||||
'state',
|
||||
'allow_insvc_patching',
|
||||
'interim_state']
|
||||
|
||||
|
||||
def get_releases(request):
|
||||
releases = []
|
||||
try:
|
||||
info = _software_client(request).get_releases()
|
||||
except Exception:
|
||||
return releases
|
||||
|
||||
if info:
|
||||
for r in info['sd'].items():
|
||||
release = Release()
|
||||
|
||||
for a in release._attrs:
|
||||
if a == 'requires':
|
||||
release.requires = [str(rs) for rs in r[1][a]]
|
||||
continue
|
||||
|
||||
if a == 'reboot_required':
|
||||
# Default to "N"
|
||||
setattr(release, a, str(r[1].get(a, "N")))
|
||||
continue
|
||||
# Must handle older patches that have metadata that is missing
|
||||
# newer attributes. Default missing attributes to "".
|
||||
setattr(release, a, str(r[1].get(a, "")))
|
||||
release.release_id = str(r[0])
|
||||
|
||||
releases.append(release)
|
||||
return releases
|
||||
|
||||
|
||||
def get_release(request, release_id):
|
||||
releases = get_releases(request)
|
||||
release = next((r for r in releases if r.release_id == release_id), None)
|
||||
|
||||
# add on release contents
|
||||
data = _software_client(request).show_release(release_id)
|
||||
|
||||
release.contents = {}
|
||||
if "number_of_commits" in data['contents'][release_id] and \
|
||||
data['contents'][release_id]['number_of_commits'] != "":
|
||||
release.contents["number_of_commits"] = \
|
||||
data['contents'][release_id]['number_of_commits']
|
||||
if "base" in data['contents'][release_id] and \
|
||||
data['contents'][release_id]['base']['commit'] != "":
|
||||
release.contents["base_commit"] = \
|
||||
data['contents'][release_id]["base"]['commit']
|
||||
for i in range(int(data['contents'][release_id]['number_of_commits'])):
|
||||
release.contents["commit%s" % (i + 1)] = \
|
||||
data['contents'][release_id]["commit%s" % (i + 1)]['commit']
|
||||
|
||||
return release
|
||||
|
||||
|
||||
def get_hosts(request):
|
||||
hosts = []
|
||||
default_value = None
|
||||
try:
|
||||
info = _software_client(request).get_hosts()
|
||||
except Exception:
|
||||
return hosts
|
||||
|
||||
if info:
|
||||
for h in info['data']:
|
||||
host = Host()
|
||||
for a in host._attrs:
|
||||
# if host received doesn't have this attribute,
|
||||
# add it with a default value
|
||||
if hasattr(h, a):
|
||||
setattr(host, a, h[a])
|
||||
else:
|
||||
setattr(host, a, default_value)
|
||||
LOG.debug("Attribute not found. Adding default:"
|
||||
"%s", a)
|
||||
hosts.append(host)
|
||||
return hosts
|
||||
|
||||
|
||||
def get_host(request, hostname):
|
||||
phosts = get_hosts(request)
|
||||
return next((phost for phost in phosts if phost.hostname == hostname),
|
||||
None)
|
||||
|
||||
|
||||
def get_message(request, data):
|
||||
LOG.info("RESPONSE: %s", data)
|
||||
if not data or ('error' in data and data["error"] != ""):
|
||||
error_msg = data["error"] or "Invalid release file"
|
||||
messages.error(request, error_msg)
|
||||
LOG.error(error_msg)
|
||||
if 'warning' in data and data["warning"] != "":
|
||||
return data["warning"]
|
||||
if 'info' in data and data["info"] != "":
|
||||
return data["info"]
|
||||
return ""
|
||||
|
||||
|
||||
def host_install(request):
|
||||
resp = _software_client(request).host_install()
|
||||
return get_message(request, resp)
|
||||
|
||||
|
||||
def upload_software(request, softwarefile, name):
|
||||
_file = {'file': (name, softwarefile,)}
|
||||
resp = _software_client(request).upload(_file)
|
||||
return get_message(request, resp)
|
||||
|
||||
|
||||
def software_delete_req(request, software_id):
|
||||
resp = _software_client(request).delete(software_id)
|
||||
return get_message(request, resp)
|
||||
|
||||
|
||||
def software_commit_req(request, software_id):
|
||||
resp = _software_client(request).commit(software_id)
|
||||
return get_message(request, resp)
|
Loading…
Reference in New Issue