From 0cff70c60777391c7a3590c612670488799593c3 Mon Sep 17 00:00:00 2001 From: tkucherera Date: Wed, 9 Aug 2023 10:37:35 -0400 Subject: [PATCH 1/5] firmware update --- confluent_server/confluent/httpapi.py | 96 +++++++++++++++++++++------ 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py index 5a145a0c..a357d72a 100644 --- a/confluent_server/confluent/httpapi.py +++ b/confluent_server/confluent/httpapi.py @@ -55,6 +55,12 @@ except ModuleNotFoundError: import urllib.parse as urlparse import eventlet.websocket import eventlet.wsgi + +#these modifications might need to be changed later now just proof of concept +import os +import cgi +#from cgi import parse_qs, escape + #scgi = eventlet.import_patched('flup.server.scgi') tlvdata = confluent.tlvdata @@ -171,6 +177,7 @@ def _get_query_dict(env, reqbody, reqtype): qdict = {} try: qstring = env['QUERY_STRING'] + except KeyError: qstring = None if qstring: @@ -288,16 +295,12 @@ def _authorize_request(env, operation, reqbody): authdata = auth.authorize(name, element=element, operation=operation) else: element = None - if not authdata: - if 'HTTP_CONFLUENTSESSION' in env: - sessionid = env['HTTP_CONFLUENTSESSION'] + if (not authdata) and 'HTTP_COOKIE' in env: + cidx = (env['HTTP_COOKIE']).find('confluentsessionid=') + if cidx >= 0: + sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51] + sessid = sessionid sessid = sessionid - elif 'HTTP_COOKIE' in env: - cidx = (env['HTTP_COOKIE']).find('confluentsessionid=') - if cidx >= 0: - sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51] - sessid = sessionid - if sessionid: if sessionid in httpsessions: if _csrf_valid(env, httpsessions[sessionid]): if env['PATH_INFO'] == '/sessions/current/logout': @@ -487,8 +490,6 @@ def wsock_handler(ws): elif clientmsg[0] == '!': msg = json.loads(clientmsg[1:]) action = msg.get('operation', None) - if not action: - action = msg.get('action', None) targ = msg.get('target', None) if targ: authdata = auth.authorize(name, targ, operation=action) @@ -526,13 +527,6 @@ def wsock_handler(ws): datacallback=datacallback, width=width, height=height) myconsoles[clientsessid] = consession - elif action == 'resize': - clientsessid = '{0}'.format(msg['sessid']) - myconsoles[clientsessid].resize( - width=msg['width'], height=msg['height']) - if action == 'break': - clientsessid = '{0}'.format(msg['sessid']) - myconsoles[clientsessid].send_break() elif action == 'stop': sessid = '{0}'.format(msg.get('sessid', None)) if sessid in myconsoles: @@ -666,9 +660,13 @@ def resourcehandler_backend(env, start_response): start_response('302 Found', headers) yield '' return - if 'CONTENT_LENGTH' in env and int(env['CONTENT_LENGTH']) > 0: - reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) + + if 'CONTENT_LENGTH' in env and int(env['CONTENT_LENGTH']) > 0: + reqbody = '' + # reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) reqtype = env['CONTENT_TYPE'] + if 'application/json' in reqtype: + reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) operation = opmap[env['REQUEST_METHOD']] querydict = _get_query_dict(env, reqbody, reqtype) if operation != 'retrieve' and 'restexplorerop' in querydict: @@ -887,6 +885,60 @@ def resourcehandler_backend(env, start_response): start_response('200 OK', headers) yield rsp return + elif (operation == 'create' and ('/firmware/updates/active' in env['PATH_INFO'])): + #TODO figure out why content length shows data being there but data string is empty (TK) + # Solution the wsgi.iput stream can only be read once and it is being read at the top + if 'multipart/form-data' in reqtype: + field_storage = cgi.FieldStorage(fp=env['wsgi.input'], environ=env, keep_blank_values=True) + for item in field_storage.list: + if item.filename: + storage_file_path = '/var/lib/confluent/client_assets/' + item.filename + file_content = item.file.read() + with open(storage_file_path, 'wb') as file: + file.write(file_content) + yield json.dumps({'data': storage_file_path}) + start_response('200 OK', headers) + return + + url = env['PATH_INFO'] + if 'application/json' in reqtype: + if not isinstance(reqbody, str): + reqbody = reqbody.decode('utf8') + pbody = json.loads(reqbody) + args = pbody['args'] + args_dict = {'filename': args} + try: + args_dict.update({'bank': pbody['bank']}) + except KeyError: + pass + noderrs = {} + nodeurls = {} + # start_response('202 Accepted', headers) + hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict) + for res in hdlr: + if isinstance(res, confluent.messages.CreatedResource): + watchurl = res.kvpairs['created'] + currnode = watchurl.split('/')[1] + nodeurls[currnode] = '/' + watchurl + yield json.dumps({'data': nodeurls}) + start_response('200 OK', headers) + return + + elif (operation == 'delete' and ('/firmware/updates/active' in env['PATH_INFO'])): + url = env['PATH_INFO'] + if 'application/json' in reqtype: + if not isinstance(reqbody, str): + reqbody = reqbody.decode('utf8') + pbody = json.loads(reqbody) + args = pbody['args'] + try: + os.remove(args) + start_response('200 OK', headers) + return + except Exception as e: + return e + + else: # normal request url = env['PATH_INFO'] @@ -972,6 +1024,7 @@ def _assemble_html(responses, resource, querydict, url, extension): else: pendingrsp.append(rsp) for rsp in pendingrsp: + print(rsp.html() + "
") yield rsp.html() + "
" if iscollection: # localpath = url[:-2] (why was this here??) @@ -1070,6 +1123,7 @@ def serve(bind_host, bind_port): try: sock = eventlet.listen( (bind_host, bind_port, 0, 0), family=socket.AF_INET6) + except socket.error as e: if e.errno != 98: raise @@ -1104,4 +1158,4 @@ class HttpApi(object): self.server = eventlet.spawn(serve, self.bind_host, self.bind_port) -_cleaner = eventlet.spawn(_sessioncleaner) +_cleaner = eventlet.spawn(_sessioncleaner) \ No newline at end of file From 2c90227f636348e6253a8d07859a51f01dd4388d Mon Sep 17 00:00:00 2001 From: tkucherera Date: Wed, 9 Aug 2023 10:48:09 -0400 Subject: [PATCH 2/5] "Firmware Update " --- confluent_server/confluent/httpapi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py index a357d72a..d418302d 100644 --- a/confluent_server/confluent/httpapi.py +++ b/confluent_server/confluent/httpapi.py @@ -886,7 +886,6 @@ def resourcehandler_backend(env, start_response): yield rsp return elif (operation == 'create' and ('/firmware/updates/active' in env['PATH_INFO'])): - #TODO figure out why content length shows data being there but data string is empty (TK) # Solution the wsgi.iput stream can only be read once and it is being read at the top if 'multipart/form-data' in reqtype: field_storage = cgi.FieldStorage(fp=env['wsgi.input'], environ=env, keep_blank_values=True) From 5768514ba9ef05de0f4b9a108adeff7db1433087 Mon Sep 17 00:00:00 2001 From: tkucherera Date: Wed, 20 Sep 2023 14:29:21 -0400 Subject: [PATCH 3/5] nodeattrib -s attrib.batch --- confluent_client/bin/nodeattrib | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/confluent_client/bin/nodeattrib b/confluent_client/bin/nodeattrib index 4ed96406..8c6f078d 100755 --- a/confluent_client/bin/nodeattrib +++ b/confluent_client/bin/nodeattrib @@ -53,6 +53,8 @@ argparser.add_option('-p', '--prompt', action='store_true', argparser.add_option('-m', '--maxnodes', type='int', help='Prompt if trying to set attributes on more ' 'than specified number of nodes') +argparser.add_option('-s', '--set', dest='set', metavar='settings.batch', + default=False, help='set attributes using a batch file') (options, args) = argparser.parse_args() @@ -109,6 +111,23 @@ elif options.clear or options.environment or options.prompt: sys.stderr.write('Attribute names required with specified options\n') argparser.print_help() exitcode = 400 + +elif options.set: + arglist = [noderange] + showtype='current' + argfile = open(options.set, 'r') + argset = argfile.readline() + while argset: + try: + argset = argset[:argset.index('#')] + except ValueError: + pass + argset = argset.strip() + if argset: + arglist += shlex.split(argset) + argset = argfile.readline() + session.stop_if_noderange_over(noderange, options.maxnodes) + exitcode=client.updateattrib(session,arglist,nodetype, noderange, options, None) if exitcode != 0: sys.exit(exitcode) From 4ecbbdfd9a55d351a375ed92370be7799db81f47 Mon Sep 17 00:00:00 2001 From: tkucherera Date: Wed, 20 Sep 2023 14:37:07 -0400 Subject: [PATCH 4/5] nodeattrib -s use with batch file --- confluent_server/confluent/httpapi.py | 95 ++++++--------------------- 1 file changed, 21 insertions(+), 74 deletions(-) diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py index d418302d..5a145a0c 100644 --- a/confluent_server/confluent/httpapi.py +++ b/confluent_server/confluent/httpapi.py @@ -55,12 +55,6 @@ except ModuleNotFoundError: import urllib.parse as urlparse import eventlet.websocket import eventlet.wsgi - -#these modifications might need to be changed later now just proof of concept -import os -import cgi -#from cgi import parse_qs, escape - #scgi = eventlet.import_patched('flup.server.scgi') tlvdata = confluent.tlvdata @@ -177,7 +171,6 @@ def _get_query_dict(env, reqbody, reqtype): qdict = {} try: qstring = env['QUERY_STRING'] - except KeyError: qstring = None if qstring: @@ -295,12 +288,16 @@ def _authorize_request(env, operation, reqbody): authdata = auth.authorize(name, element=element, operation=operation) else: element = None - if (not authdata) and 'HTTP_COOKIE' in env: - cidx = (env['HTTP_COOKIE']).find('confluentsessionid=') - if cidx >= 0: - sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51] - sessid = sessionid + if not authdata: + if 'HTTP_CONFLUENTSESSION' in env: + sessionid = env['HTTP_CONFLUENTSESSION'] sessid = sessionid + elif 'HTTP_COOKIE' in env: + cidx = (env['HTTP_COOKIE']).find('confluentsessionid=') + if cidx >= 0: + sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51] + sessid = sessionid + if sessionid: if sessionid in httpsessions: if _csrf_valid(env, httpsessions[sessionid]): if env['PATH_INFO'] == '/sessions/current/logout': @@ -490,6 +487,8 @@ def wsock_handler(ws): elif clientmsg[0] == '!': msg = json.loads(clientmsg[1:]) action = msg.get('operation', None) + if not action: + action = msg.get('action', None) targ = msg.get('target', None) if targ: authdata = auth.authorize(name, targ, operation=action) @@ -527,6 +526,13 @@ def wsock_handler(ws): datacallback=datacallback, width=width, height=height) myconsoles[clientsessid] = consession + elif action == 'resize': + clientsessid = '{0}'.format(msg['sessid']) + myconsoles[clientsessid].resize( + width=msg['width'], height=msg['height']) + if action == 'break': + clientsessid = '{0}'.format(msg['sessid']) + myconsoles[clientsessid].send_break() elif action == 'stop': sessid = '{0}'.format(msg.get('sessid', None)) if sessid in myconsoles: @@ -660,13 +666,9 @@ def resourcehandler_backend(env, start_response): start_response('302 Found', headers) yield '' return - - if 'CONTENT_LENGTH' in env and int(env['CONTENT_LENGTH']) > 0: - reqbody = '' - # reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) + if 'CONTENT_LENGTH' in env and int(env['CONTENT_LENGTH']) > 0: + reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) reqtype = env['CONTENT_TYPE'] - if 'application/json' in reqtype: - reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) operation = opmap[env['REQUEST_METHOD']] querydict = _get_query_dict(env, reqbody, reqtype) if operation != 'retrieve' and 'restexplorerop' in querydict: @@ -885,59 +887,6 @@ def resourcehandler_backend(env, start_response): start_response('200 OK', headers) yield rsp return - elif (operation == 'create' and ('/firmware/updates/active' in env['PATH_INFO'])): - # Solution the wsgi.iput stream can only be read once and it is being read at the top - if 'multipart/form-data' in reqtype: - field_storage = cgi.FieldStorage(fp=env['wsgi.input'], environ=env, keep_blank_values=True) - for item in field_storage.list: - if item.filename: - storage_file_path = '/var/lib/confluent/client_assets/' + item.filename - file_content = item.file.read() - with open(storage_file_path, 'wb') as file: - file.write(file_content) - yield json.dumps({'data': storage_file_path}) - start_response('200 OK', headers) - return - - url = env['PATH_INFO'] - if 'application/json' in reqtype: - if not isinstance(reqbody, str): - reqbody = reqbody.decode('utf8') - pbody = json.loads(reqbody) - args = pbody['args'] - args_dict = {'filename': args} - try: - args_dict.update({'bank': pbody['bank']}) - except KeyError: - pass - noderrs = {} - nodeurls = {} - # start_response('202 Accepted', headers) - hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict) - for res in hdlr: - if isinstance(res, confluent.messages.CreatedResource): - watchurl = res.kvpairs['created'] - currnode = watchurl.split('/')[1] - nodeurls[currnode] = '/' + watchurl - yield json.dumps({'data': nodeurls}) - start_response('200 OK', headers) - return - - elif (operation == 'delete' and ('/firmware/updates/active' in env['PATH_INFO'])): - url = env['PATH_INFO'] - if 'application/json' in reqtype: - if not isinstance(reqbody, str): - reqbody = reqbody.decode('utf8') - pbody = json.loads(reqbody) - args = pbody['args'] - try: - os.remove(args) - start_response('200 OK', headers) - return - except Exception as e: - return e - - else: # normal request url = env['PATH_INFO'] @@ -1023,7 +972,6 @@ def _assemble_html(responses, resource, querydict, url, extension): else: pendingrsp.append(rsp) for rsp in pendingrsp: - print(rsp.html() + "
") yield rsp.html() + "
" if iscollection: # localpath = url[:-2] (why was this here??) @@ -1122,7 +1070,6 @@ def serve(bind_host, bind_port): try: sock = eventlet.listen( (bind_host, bind_port, 0, 0), family=socket.AF_INET6) - except socket.error as e: if e.errno != 98: raise @@ -1157,4 +1104,4 @@ class HttpApi(object): self.server = eventlet.spawn(serve, self.bind_host, self.bind_port) -_cleaner = eventlet.spawn(_sessioncleaner) \ No newline at end of file +_cleaner = eventlet.spawn(_sessioncleaner) From bd0b16992d400ad5039a1b28e93be43ecc38cf7a Mon Sep 17 00:00:00 2001 From: tkucherera Date: Wed, 20 Sep 2023 14:42:17 -0400 Subject: [PATCH 5/5] man page --- confluent_client/doc/man/nodeattrib.ronn.tmpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/confluent_client/doc/man/nodeattrib.ronn.tmpl b/confluent_client/doc/man/nodeattrib.ronn.tmpl index 6684fce0..b1330198 100644 --- a/confluent_client/doc/man/nodeattrib.ronn.tmpl +++ b/confluent_client/doc/man/nodeattrib.ronn.tmpl @@ -7,7 +7,8 @@ nodeattrib(8) -- List or change confluent nodes attributes `nodeattrib [ ...]` `nodeattrib -c ...` `nodeattrib -e ...` -`nodeattrib -p ...` +`nodeattrib -p ...` +`nodeattrib -s ` ## DESCRIPTION @@ -58,7 +59,10 @@ to a blank value will allow masking a group defined attribute with an empty valu * `-p`, `--prompt`: Request interactive prompting to provide values rather than the command line or environment variables. - + +* `-s`, `--set`: + Set attributes using a batch file + * `-m MAXNODES`, `--maxnodes=MAXNODES`: Prompt if trying to set attributes on more than specified number of nodes.