From 582a4de62dfa54bd393227e698a8dcf5f9b744c2 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 23 Aug 2018 16:36:41 -0400 Subject: [PATCH] Add CLI and directory support for nodesupport --- confluent_client/bin/nodesupport | 145 ++++++++++++++++++ confluent_server/confluent/firmwaremanager.py | 12 +- .../plugins/hardwaremanagement/ipmi.py | 4 +- 3 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 confluent_client/bin/nodesupport diff --git a/confluent_client/bin/nodesupport b/confluent_client/bin/nodesupport new file mode 100644 index 00000000..fedfa5c9 --- /dev/null +++ b/confluent_client/bin/nodesupport @@ -0,0 +1,145 @@ +#!/usr/bin/python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2018 Lenovo +# +# 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 optparse +import os +import signal +import sys +import time + +try: + signal.signal(signal.SIGPIPE, signal.SIG_DFL) +except AttributeError: + pass +path = os.path.dirname(os.path.realpath(__file__)) +path = os.path.realpath(os.path.join(path, '..', 'lib', 'python')) +if path.startswith('/opt'): + sys.path.append(path) + +import confluent.client as client +import confluent.screensqueeze as sj q + +exitcode = 0 + + +def get_update_progress(session, url): + for res in session.read(url): + status = res['phase'] + percent = res['progress'] + detail = res['detail'] + if status == 'error': + text = 'error!' + else: + text = '{0}: {1:3.0f}%'.format(status, percent) + return text, status, detail + + +def printerror(res, node=None): + global exitcode + if 'errorcode' in res: + exitcode = res['errorcode'] + if 'error' in res: + if node: + sys.stderr.write('{0}: {1}\n'.format(node, res['error'])) + else: + sys.stderr.write('{0}\n'.format(res['error'])) + if 'errorcode' not in res: + exitcode = 1 + for node in res.get('databynode', ()): + printerror(res['databynode'][node], node) + + + +def download_servicedata(noderange, media): + global exitcode + session = client.Command() + output = sq.ScreenPrinter(noderange, session) + filename = os.path.abspath(media) + resource = '/noderange/{0}/media/uploads/'.format(noderange) + upargs = {'filename': filename} + noderrs = {} + nodeurls = {} + for res in session.create(resource, upargs): + if 'created' not in res: + for nodename in res.get('databynode', ()): + output.set_output(nodename, 'error!') + noderrs[nodename] = res['databynode'][nodename].get( + 'error', 'Unknown Error') + continue + watchurl = res['created'] + currnode = watchurl.split('/')[1] + nodeurls[currnode] = '/' + watchurl + while nodeurls: + for node in list(nodeurls): + progress, status, err = get_update_progress( + session, nodeurls[node]) + if status == 'error': + exitcode = 1 + noderrs[node] = err + if status in ('error', 'complete', 'pending'): + list(session.delete(nodeurls[node])) + del nodeurls[node] + output.set_output(node, progress) + time.sleep(2) + allerrnodes = ','.join(noderrs) + if noderrs: + sys.stderr.write( + 'Nodes had errors retrieving service data ({0})!\n'.format(allerrnodes)) + for node in noderrs: + sys.stderr.write('{0}: {1}\n'.format(node, noderrs[node])) + +funmap = { + 'servicedata': download_servicedata, +} + + +class OptParser(optparse.OptionParser): + + def format_epilog(self, formatter): + return self.expand_prog_name(self.epilog) + +def main(): + argparser = OptParser( + usage="Usage: %prog [servicedata] " + "", + epilog='\nservicedata will save service data to the given ' + 'directory\n' + '\n\nSee `man %prog` for more info.\n') + (options, args) = argparser.parse_args() + media = None + try: + noderange = args[0] + operation = args[1] + arglength = 2 + if operation == 'servicedata': + media = args[2] + arglength = 3 + if len(args) > arglength: + argparser.print_help() + sys.exit(1) + except IndexError: + argparser.print_help() + sys.exit(1) + client.check_globbing(noderange) + try: + handler = funmap[operation] + except KeyError: + argparser.print_help() + sys.exit(1) + handler(noderange, media) +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/confluent_server/confluent/firmwaremanager.py b/confluent_server/confluent/firmwaremanager.py index 361ee4d5..6d04c503 100644 --- a/confluent_server/confluent/firmwaremanager.py +++ b/confluent_server/confluent/firmwaremanager.py @@ -22,6 +22,7 @@ import confluent.exceptions as exc import confluent.messages as msg import eventlet import os +import pwd import socket updatesbytarget = {} @@ -30,12 +31,14 @@ downloadsbytarget = {} updatepool = eventlet.greenpool.GreenPool(256) -def execupdate(handler, filename, updateobj, type): +def execupdate(handler, filename, updateobj, type, owner, node): if type != 'ffdc' and not os.path.exists(filename): errstr = '{0} does not appear to exist on {1}'.format( filename, socket.gethostname()) updateobj.handle_progress({'phase': 'error', 'progress': 0.0, 'detail': errstr}) + if type == 'ffdc' and os.path.isdir(filename): + filename += '/' + node + '.svcdata' try: if type == 'firmware': completion = handler(filename, progress=updateobj.handle_progress, @@ -44,6 +47,9 @@ def execupdate(handler, filename, updateobj, type): completion = handler(filename, progress=updateobj.handle_progress) if completion is None: completion = 'complete' + if owner: + pwent = pwd.getpwnam(owner) + os.chown(filename, pwent.pw_uid, pwent.pw_gid) updateobj.handle_progress({'phase': completion, 'progress': 100.0}) except exc.PubkeyInvalid as pi: errstr = 'Certificate mismatch detected, does not match value in ' \ @@ -56,14 +62,14 @@ def execupdate(handler, filename, updateobj, type): class Updater(object): def __init__(self, node, handler, filename, tenant=None, name=None, - bank=None, type='firmware'): + bank=None, type='firmware', owner=None): self.bank = bank self.node = node self.phase = 'initializing' self.detail = '' self.percent = 0.0 self.updateproc = updatepool.spawn(execupdate, handler, filename, - self, type) + self, type, owner, node) if type == 'firmware': myparty = updatesbytarget elif type == 'mediaupload': diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index 15c8a948..a6b3f279 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -385,6 +385,7 @@ class IpmiHandler(object): self.error = None eventlet.sleep(0) self.cfg = cfd[node] + self.current_user = cfd.current_user self.loggedin = False self.node = node self.element = element @@ -508,7 +509,8 @@ class IpmiHandler(object): def handle_servicedata_fetch(self): u = firmwaremanager.Updater( self.node, self.ipmicmd.get_diagnostic_data, - self.inputdata.filename, self.tenant, type='ffdc') + self.inputdata.filename, self.tenant, type='ffdc', + self.current_user) self.output.put(msg.CreatedResource( 'nodes/{0}/support/servicedata/{1}'.format(self.node, u.name)))