1#!/usr/bin/python3 2""" 3 Copyright 2017,2019 IBM Corporation 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16""" 17import argparse 18import requests 19import getpass 20import json 21import os 22import urllib3 23import time, datetime 24import binascii 25import subprocess 26import platform 27import zipfile 28import tarfile 29import tempfile 30import hashlib 31import re 32import uuid 33import ssl 34import socket 35import select 36import http.client 37from subprocess import check_output 38import traceback 39 40 41MAX_NBD_PACKET_SIZE = 131088 42jsonHeader = {'Content-Type' : 'application/json'} 43xAuthHeader = {} 44baseTimeout = 60 45serverTypeMap = { 46 'ActiveDirectory' : 'active_directory', 47 'OpenLDAP' : 'openldap' 48 } 49 50class NBDPipe: 51 52 def openHTTPSocket(self,args): 53 54 try: 55 _create_unverified_https_context = ssl._create_unverified_context 56 except AttributeError: 57 # Legacy Python that doesn't verify HTTPS certificates by default 58 pass 59 else: 60 # Handle target environment that doesn't support HTTPS verification 61 ssl._create_default_https_context = _create_unverified_https_context 62 63 64 token = gettoken(args) 65 self.conn = http.client.HTTPSConnection(args.host,port=443) 66 67 uri = "/redfish/v1/Systems/system/LogServices/Dump/attachment/"+args.dumpNum 68 69 self.conn.request("GET",uri, headers={"X-Auth-Token":token}) 70 71 def openTCPSocket(self): 72 # Create a TCP/IP socket 73 self.tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 74 # Connect the socket to the port where the server is listening 75 server_address = ('localhost', 1043) 76 self.tcp.connect(server_address) 77 78 def waitformessage(self): 79 inputs = [self.conn.sock,self.tcp] 80 outputs = [] 81 message_queues = {} 82 while True: 83 readable, writable, exceptional = select.select( 84 inputs, outputs, inputs) 85 86 for s in readable: 87 if s is self.conn.sock: 88 89 data = self.conn.sock.recv(MAX_NBD_PACKET_SIZE) 90 print("<<HTTP") 91 if data: 92 self.tcp.send(data) 93 else: 94 print ("BMC Closed the connection") 95 self.conn.close() 96 self.tcp.close() 97 sys.exit(1) 98 elif s is self.tcp: 99 data = self.tcp.recv(MAX_NBD_PACKET_SIZE) 100 print(">>TCP") 101 if data: 102 self.conn.sock.send(data) 103 else: 104 print("NBD server closed the connection") 105 self.conn.sock.close() 106 self.tcp.close() 107 sys.exit(1) 108 for s in exceptional: 109 inputs.remove(s) 110 print("Exceptional closing the socket") 111 s.close() 112 113def getsize(host,args,session): 114 url = "https://"+host+"/redfish/v1/Systems/system/LogServices/Dump/Entries/"+str(args.dumpNum) 115 try: 116 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 117 if resp.status_code==200: 118 size = resp.json()["Oem"]["OpenBmc"]['AdditionalDataSizeBytes'] 119 return size 120 else: 121 return "Failed get Size" 122 except(requests.exceptions.Timeout): 123 return connectionErrHandler(args.json, "Timeout", None) 124 125 except(requests.exceptions.ConnectionError) as err: 126 return connectionErrHandler(args.json, "ConnectionError", err) 127 128def gettoken(args): 129 mysess = requests.session() 130 resp = mysess.post('https://'+args.host+'/login', headers=jsonHeader,json={"data":[args.user,args.PW]},verify=False) 131 if resp.status_code == 200: 132 cookie = resp.headers['Set-Cookie'] 133 match = re.search('SESSION=(\w+);', cookie) 134 return match.group(1) 135 136 137 138def get_pid(name): 139 try: 140 pid = map(int, check_output(["pidof", "-s",name])) 141 except Exception: 142 pid = 0 143 144 return pid 145 146def findThisProcess( process_name ): 147 ps = subprocess.Popen("ps -eaf | grep "+process_name, shell=True, stdout=subprocess.PIPE) 148 output = ps.stdout.read() 149 ps.stdout.close() 150 ps.wait() 151 pid = get_pid(process_name) 152 return output 153 154def isThisProcessRunning( process_name ): 155 pid = get_pid(process_name) 156 if (pid == 0 ): 157 return False 158 else: 159 return True 160 161def NBDSetup(host,args,session): 162 user=os.getenv("SUDO_USER") 163 if user is None: 164 path = os.getcwd() 165 nbdServerPath = path + "/nbd-server" 166 if not os.path.exists(nbdServerPath): 167 print("Error: this program did not run as sudo!\nplease copy nbd-server to current directory and run script again") 168 exit() 169 170 if isThisProcessRunning('nbd-server') == True: 171 print("nbd-server already Running! killing the nbd-server") 172 os.system('killall nbd-server') 173 174 if (args.dumpSaveLoc is not None): 175 if(os.path.exists(args.dumpSaveLoc)): 176 print("Error: File already exists.") 177 exit() 178 179 fp= open(args.dumpSaveLoc,"w") 180 sizeInBytes = getsize(host,args,session) 181 #Round off size to mutiples of 1024 182 size = int(sizeInBytes) 183 mod = size % 1024 184 if mod : 185 roundoff = 1024 - mod 186 size = size + roundoff 187 188 cmd = 'chmod 777 ' + args.dumpSaveLoc 189 os.system(cmd) 190 191 #Run truncate to create file with given size 192 cmd = 'truncate -s ' + str(size) + ' '+ args.dumpSaveLoc 193 os.system(cmd) 194 195 if user is None: 196 cmd = './nbd-server 1043 '+ args.dumpSaveLoc 197 else: 198 cmd = 'nbd-server 1043 '+ args.dumpSaveLoc 199 os.system(cmd) 200 201 202def hilight(textToColor, color, bold): 203 """ 204 Used to add highlights to various text for displaying in a terminal 205 206 @param textToColor: string, the text to be colored 207 @param color: string, used to color the text red or green 208 @param bold: boolean, used to bold the textToColor 209 @return: Buffered reader containing the modified string. 210 """ 211 if(sys.platform.__contains__("win")): 212 if(color == "red"): 213 os.system('color 04') 214 elif(color == "green"): 215 os.system('color 02') 216 else: 217 os.system('color') #reset to default 218 return textToColor 219 else: 220 attr = [] 221 if(color == "red"): 222 attr.append('31') 223 elif(color == "green"): 224 attr.append('32') 225 else: 226 attr.append('0') 227 if bold: 228 attr.append('1') 229 else: 230 attr.append('0') 231 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr),textToColor) 232 233def connectionErrHandler(jsonFormat, errorStr, err): 234 """ 235 Error handler various connection errors to bmcs 236 237 @param jsonFormat: boolean, used to output in json format with an error code. 238 @param errorStr: string, used to color the text red or green 239 @param err: string, the text from the exception 240 """ 241 if errorStr == "Timeout": 242 if not jsonFormat: 243 return("FQPSPIN0000M: Connection timed out. Ensure you have network connectivity to the bmc") 244 else: 245 conerror = {} 246 conerror['CommonEventID'] = 'FQPSPIN0000M' 247 conerror['sensor']="N/A" 248 conerror['state']="N/A" 249 conerror['additionalDetails'] = "N/A" 250 conerror['Message']="Connection timed out. Ensure you have network connectivity to the BMC" 251 conerror['LengthyDescription'] = "While trying to establish a connection with the specified BMC, the BMC failed to respond in adequate time. Verify the BMC is functioning properly, and the network connectivity to the BMC is stable." 252 conerror['Serviceable']="Yes" 253 conerror['CallHomeCandidate']= "No" 254 conerror['Severity'] = "Critical" 255 conerror['EventType'] = "Communication Failure/Timeout" 256 conerror['VMMigrationFlag'] = "Yes" 257 conerror["AffectedSubsystem"] = "Interconnect (Networking)" 258 conerror["timestamp"] = str(int(time.time())) 259 conerror["UserAction"] = "Verify network connectivity between the two systems and the bmc is functional." 260 eventdict = {} 261 eventdict['event0'] = conerror 262 eventdict['numAlerts'] = '1' 263 errorMessageStr = errorMessageStr = json.dumps(eventdict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) 264 return(errorMessageStr) 265 elif errorStr == "ConnectionError": 266 if not jsonFormat: 267 return("FQPSPIN0001M: " + str(err)) 268 else: 269 conerror = {} 270 conerror['CommonEventID'] = 'FQPSPIN0001M' 271 conerror['sensor']="N/A" 272 conerror['state']="N/A" 273 conerror['additionalDetails'] = str(err) 274 conerror['Message']="Connection Error. View additional details for more information" 275 conerror['LengthyDescription'] = "A connection error to the specified BMC occurred and additional details are provided. Review these details to resolve the issue." 276 conerror['Serviceable']="Yes" 277 conerror['CallHomeCandidate']= "No" 278 conerror['Severity'] = "Critical" 279 conerror['EventType'] = "Communication Failure/Timeout" 280 conerror['VMMigrationFlag'] = "Yes" 281 conerror["AffectedSubsystem"] = "Interconnect (Networking)" 282 conerror["timestamp"] = str(int(time.time())) 283 conerror["UserAction"] = "Correct the issue highlighted in additional details and try again" 284 eventdict = {} 285 eventdict['event0'] = conerror 286 eventdict['numAlerts'] = '1' 287 errorMessageStr = json.dumps(eventdict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) 288 return(errorMessageStr) 289 290 else: 291 return("Unknown Error: "+ str(err)) 292 293 294def setColWidth(keylist, numCols, dictForOutput, colNames): 295 """ 296 Sets the output width of the columns to display 297 298 @param keylist: list, list of strings representing the keys for the dictForOutput 299 @param numcols: the total number of columns in the final output 300 @param dictForOutput: dictionary, contains the information to print to the screen 301 @param colNames: list, The strings to use for the column headings, in order of the keylist 302 @return: A list of the column widths for each respective column. 303 """ 304 colWidths = [] 305 for x in range(0, numCols): 306 colWidths.append(0) 307 for key in dictForOutput: 308 for x in range(0, numCols): 309 colWidths[x] = max(colWidths[x], len(str(dictForOutput[key][keylist[x]]))) 310 311 for x in range(0, numCols): 312 colWidths[x] = max(colWidths[x], len(colNames[x])) +2 313 314 return colWidths 315 316def loadPolicyTable(pathToPolicyTable): 317 """ 318 loads a json based policy table into a dictionary 319 320 @param value: boolean, the value to convert 321 @return: A string of "Yes" or "No" 322 """ 323 policyTable = {} 324 if(os.path.exists(pathToPolicyTable)): 325 with open(pathToPolicyTable, 'r') as stream: 326 try: 327 contents =json.load(stream) 328 policyTable = contents['events'] 329 except Exception as err: 330 print(err) 331 return policyTable 332 333 334def boolToString(value): 335 """ 336 converts a boolean value to a human readable string value 337 338 @param value: boolean, the value to convert 339 @return: A string of "Yes" or "No" 340 """ 341 if(value): 342 return "Yes" 343 else: 344 return "No" 345 346def stringToInt(text): 347 """ 348 returns an integer if the string can be converted, otherwise returns the string 349 350 @param text: the string to try to convert to an integer 351 """ 352 if text.isdigit(): 353 return int(text) 354 else: 355 return text 356 357def naturalSort(text): 358 """ 359 provides a way to naturally sort a list 360 361 @param text: the key to convert for sorting 362 @return list containing the broken up string parts by integers and strings 363 """ 364 stringPartList = [] 365 for c in re.split('(\d+)', text): 366 stringPartList.append(stringToInt(c)) 367 return stringPartList 368 369def tableDisplay(keylist, colNames, output): 370 """ 371 Logs into the BMC and creates a session 372 373 @param keylist: list, keys for the output dictionary, ordered by colNames 374 @param colNames: Names for the Table of the columns 375 @param output: The dictionary of data to display 376 @return: Session object 377 """ 378 colWidth = setColWidth(keylist, len(colNames), output, colNames) 379 row = "" 380 outputText = "" 381 for i in range(len(colNames)): 382 if (i != 0): row = row + "| " 383 row = row + colNames[i].ljust(colWidth[i]) 384 outputText += row + "\n" 385 386 output_keys = list(output.keys()) 387 output_keys.sort(key=naturalSort) 388 for key in output_keys: 389 row = "" 390 for i in range(len(keylist)): 391 if (i != 0): row = row + "| " 392 row = row + output[key][keylist[i]].ljust(colWidth[i]) 393 outputText += row + "\n" 394 395 return outputText 396 397def checkFWactivation(host, args, session): 398 """ 399 Checks the software inventory for an image that is being activated. 400 401 @return: True if an image is being activated, false is no activations are happening 402 """ 403 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 404 try: 405 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 406 except(requests.exceptions.Timeout): 407 print(connectionErrHandler(args.json, "Timeout", None)) 408 return(True) 409 except(requests.exceptions.ConnectionError) as err: 410 print( connectionErrHandler(args.json, "ConnectionError", err)) 411 return True 412 fwInfo = resp.json()['data'] 413 for key in fwInfo: 414 if 'Activation' in fwInfo[key]: 415 if 'Activating' in fwInfo[key]['Activation'] or 'Activating' in fwInfo[key]['RequestedActivation']: 416 return True 417 return False 418 419def login(host, username, pw,jsonFormat, allowExpiredPassword): 420 """ 421 Logs into the BMC and creates a session 422 423 @param host: string, the hostname or IP address of the bmc to log into 424 @param username: The user name for the bmc to log into 425 @param pw: The password for the BMC to log into 426 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true. 427 @param allowExpiredPassword: true, if the requested operation should 428 be allowed when the password is expired 429 @return: Session object 430 """ 431 if(jsonFormat==False): 432 print("Attempting login...") 433 mysess = requests.session() 434 try: 435 r = mysess.post('https://'+host+'/login', headers=jsonHeader, json = {"data": [username, pw]}, verify=False, timeout=baseTimeout) 436 if r.status_code == 200: 437 cookie = r.headers['Set-Cookie'] 438 match = re.search('SESSION=(\w+);', cookie) 439 if match: 440 xAuthHeader['X-Auth-Token'] = match.group(1) 441 jsonHeader.update(xAuthHeader) 442 loginMessage = json.loads(r.text) 443 if (loginMessage['status'] != "ok"): 444 print(loginMessage["data"]["description"].encode('utf-8')) 445 sys.exit(1) 446 if (('extendedMessage' in r.json()) and 447 ('The password for this account must be changed' in r.json()['extendedMessage'])): 448 if not allowExpiredPassword: 449 print("The password for this system has expired and must be changed"+ 450 "\nsee openbmctool.py set_password --help") 451 logout(host, username, pw, mysess, jsonFormat) 452 sys.exit(1) 453# if(sys.version_info < (3,0)): 454# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 455# if sys.version_info >= (3,0): 456# requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) 457 return mysess 458 else: 459 return None 460 except(requests.exceptions.Timeout): 461 return (connectionErrHandler(jsonFormat, "Timeout", None)) 462 except(requests.exceptions.ConnectionError) as err: 463 return (connectionErrHandler(jsonFormat, "ConnectionError", err)) 464 465 466def logout(host, username, pw, session, jsonFormat): 467 """ 468 Logs out of the bmc and terminates the session 469 470 @param host: string, the hostname or IP address of the bmc to log out of 471 @param username: The user name for the bmc to log out of 472 @param pw: The password for the BMC to log out of 473 @param session: the active session to use 474 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true. 475 """ 476 try: 477 r = session.post('https://'+host+'/logout', headers=jsonHeader,json = {"data": [username, pw]}, verify=False, timeout=baseTimeout) 478 except(requests.exceptions.Timeout): 479 print(connectionErrHandler(jsonFormat, "Timeout", None)) 480 481 if(jsonFormat==False): 482 if r.status_code == 200: 483 print('User ' +username + ' has been logged out') 484 485 486def fru(host, args, session): 487 """ 488 prints out the system inventory. deprecated see fruPrint and fruList 489 490 @param host: string, the hostname or IP address of the bmc 491 @param args: contains additional arguments used by the fru sub command 492 @param session: the active session to use 493 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 494 """ 495 #url="https://"+host+"/org/openbmc/inventory/system/chassis/enumerate" 496 497 #print(url) 498 #res = session.get(url, headers=httpHeader, verify=False) 499 #print(res.text) 500 #sample = res.text 501 502 #inv_list = json.loads(sample)["data"] 503 504 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate" 505 try: 506 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 507 except(requests.exceptions.Timeout): 508 return(connectionErrHandler(args.json, "Timeout", None)) 509 510 sample = res.text 511# inv_list.update(json.loads(sample)["data"]) 512# 513# #determine column width's 514# colNames = ["FRU Name", "FRU Type", "Has Fault", "Is FRU", "Present", "Version"] 515# colWidths = setColWidth(["FRU Name", "fru_type", "fault", "is_fru", "present", "version"], 6, inv_list, colNames) 516# 517# print("FRU Name".ljust(colWidths[0])+ "FRU Type".ljust(colWidths[1]) + "Has Fault".ljust(colWidths[2]) + "Is FRU".ljust(colWidths[3])+ 518# "Present".ljust(colWidths[4]) + "Version".ljust(colWidths[5])) 519# format the output 520# for key in sorted(inv_list.keys()): 521# keyParts = key.split("/") 522# isFRU = "True" if (inv_list[key]["is_fru"]==1) else "False" 523# 524# fruEntry = (keyParts[len(keyParts) - 1].ljust(colWidths[0]) + inv_list[key]["fru_type"].ljust(colWidths[1])+ 525# inv_list[key]["fault"].ljust(colWidths[2])+isFRU.ljust(colWidths[3])+ 526# inv_list[key]["present"].ljust(colWidths[4])+ inv_list[key]["version"].ljust(colWidths[5])) 527# if(isTTY): 528# if(inv_list[key]["is_fru"] == 1): 529# color = "green" 530# bold = True 531# else: 532# color='black' 533# bold = False 534# fruEntry = hilight(fruEntry, color, bold) 535# print (fruEntry) 536 return sample 537 538def fruPrint(host, args, session): 539 """ 540 prints out all inventory 541 542 @param host: string, the hostname or IP address of the bmc 543 @param args: contains additional arguments used by the fru sub command 544 @param session: the active session to use 545 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 546 @return returns the total fru list. 547 """ 548 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate" 549 try: 550 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 551 except(requests.exceptions.Timeout): 552 return(connectionErrHandler(args.json, "Timeout", None)) 553 554 frulist={} 555# print(res.text) 556 if res.status_code==200: 557 frulist['Hardware'] = res.json()['data'] 558 else: 559 if not args.json: 560 return "Error retrieving the system inventory. BMC message: {msg}".format(msg=res.json()['message']) 561 else: 562 return res.json() 563 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 564 try: 565 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 566 except(requests.exceptions.Timeout): 567 return(connectionErrHandler(args.json, "Timeout", None)) 568# print(res.text) 569 if res.status_code==200: 570 frulist['Software'] = res.json()['data'] 571 else: 572 if not args.json(): 573 return "Error retrieving the system inventory. BMC message: {msg}".format(msg=res.json()['message']) 574 else: 575 return res.json() 576 return frulist 577 578 579def fruList(host, args, session): 580 """ 581 prints out all inventory or only a specific specified item 582 583 @param host: string, the hostname or IP address of the bmc 584 @param args: contains additional arguments used by the fru sub command 585 @param session: the active session to use 586 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 587 """ 588 if(args.items==True): 589 return fruPrint(host, args, session) 590 else: 591 return fruPrint(host, args, session) 592 593 594 595def fruStatus(host, args, session): 596 """ 597 prints out the status of all FRUs 598 599 @param host: string, the hostname or IP address of the bmc 600 @param args: contains additional arguments used by the fru sub command 601 @param session: the active session to use 602 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 603 """ 604 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate" 605 try: 606 res = session.get(url, headers=jsonHeader, verify=False) 607 except(requests.exceptions.Timeout): 608 return(connectionErrHandler(args.json, "Timeout", None)) 609# print(res.text) 610 frulist = res.json()['data'] 611 frus = {} 612 for key in frulist: 613 component = frulist[key] 614 isFru = False 615 present = False 616 func = False 617 hasSels = False 618 keyPieces = key.split('/') 619 fruName = keyPieces[-1] 620 if 'core' in fruName: #associate cores to cpus 621 fruName = keyPieces[-2] + '-' + keyPieces[-1] 622 if 'Functional' in component: 623 if('Present' in component): 624 if 'FieldReplaceable' in component: 625 if component['FieldReplaceable'] == 1: 626 isFru = True 627 if "fan" in fruName: 628 isFru = True; 629 if component['Present'] == 1: 630 present = True 631 if component['Functional'] == 1: 632 func = True 633 if ((key + "/fault") in frulist): 634 hasSels = True; 635 if args.verbose: 636 if hasSels: 637 loglist = [] 638 faults = frulist[key+"/fault"]['endpoints'] 639 for item in faults: 640 loglist.append(item.split('/')[-1]) 641 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() } 642 else: 643 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" } 644 else: 645 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) } 646 elif "power_supply" in fruName or "powersupply" in fruName: 647 if component['Present'] ==1: 648 present = True 649 isFru = True 650 if ((key + "/fault") in frulist): 651 hasSels = True; 652 if args.verbose: 653 if hasSels: 654 loglist = [] 655 faults = frulist[key+"/fault"]['endpoints'] 656 for item in faults: 657 loglist.append(item.split('/')[-1]) 658 frus[fruName] = {"compName": fruName, "Functional": "No", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() } 659 else: 660 frus[fruName] = {"compName": fruName, "Functional": "Yes", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" } 661 else: 662 frus[fruName] = {"compName": fruName, "Functional": boolToString(not hasSels), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) } 663 if not args.json: 664 if not args.verbose: 665 colNames = ["Component", "Is a FRU", "Present", "Functional", "Has Logs"] 666 keylist = ["compName", "IsFru", "Present", "Functional", "hasSEL"] 667 else: 668 colNames = ["Component", "Is a FRU", "Present", "Functional", "Assoc. Log Number(s)"] 669 keylist = ["compName", "IsFru", "Present", "Functional", "selList"] 670 return tableDisplay(keylist, colNames, frus) 671 else: 672 return str(json.dumps(frus, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 673 674def sensor(host, args, session): 675 """ 676 prints out all sensors 677 678 @param host: string, the hostname or IP address of the bmc 679 @param args: contains additional arguments used by the sensor sub command 680 @param session: the active session to use 681 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 682 """ 683 url="https://"+host+"/xyz/openbmc_project/sensors/enumerate" 684 try: 685 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 686 except(requests.exceptions.Timeout): 687 return(connectionErrHandler(args.json, "Timeout", None)) 688 689 #Get OCC status 690 url="https://"+host+"/org/open_power/control/enumerate" 691 try: 692 occres = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 693 except(requests.exceptions.Timeout): 694 return(connectionErrHandler(args.json, "Timeout", None)) 695 if not args.json: 696 colNames = ['sensor', 'type', 'units', 'value', 'target'] 697 sensors = res.json()["data"] 698 output = {} 699 for key in sensors: 700 senDict = {} 701 keyparts = key.split("/") 702 703 # Associations like the following also show up here: 704 # /xyz/openbmc_project/sensors/<type>/<name>/<assoc-name> 705 # Skip them. 706 # Note: keyparts[0] = '' which is why there are 7 segments. 707 if len(keyparts) > 6: 708 continue 709 710 senDict['sensorName'] = keyparts[-1] 711 senDict['type'] = keyparts[-2] 712 try: 713 senDict['units'] = sensors[key]['Unit'].split('.')[-1] 714 except KeyError: 715 senDict['units'] = "N/A" 716 if('Scale' in sensors[key]): 717 scale = 10 ** sensors[key]['Scale'] 718 else: 719 scale = 1 720 try: 721 senDict['value'] = str(sensors[key]['Value'] * scale) 722 except KeyError: 723 if 'value' in sensors[key]: 724 senDict['value'] = sensors[key]['value'] 725 else: 726 senDict['value'] = "N/A" 727 if 'Target' in sensors[key]: 728 senDict['target'] = str(sensors[key]['Target']) 729 else: 730 senDict['target'] = 'N/A' 731 output[senDict['sensorName']] = senDict 732 733 occstatus = occres.json()["data"] 734 if '/org/open_power/control/occ0' in occstatus: 735 occ0 = occstatus["/org/open_power/control/occ0"]['OccActive'] 736 if occ0 == 1: 737 occ0 = 'Active' 738 else: 739 occ0 = 'Inactive' 740 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'} 741 occ1 = occstatus["/org/open_power/control/occ1"]['OccActive'] 742 if occ1 == 1: 743 occ1 = 'Active' 744 else: 745 occ1 = 'Inactive' 746 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'} 747 else: 748 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'} 749 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'} 750 keylist = ['sensorName', 'type', 'units', 'value', 'target'] 751 752 return tableDisplay(keylist, colNames, output) 753 else: 754 return res.text + occres.text 755 756def sel(host, args, session): 757 """ 758 prints out the bmc alerts 759 760 @param host: string, the hostname or IP address of the bmc 761 @param args: contains additional arguments used by the sel sub command 762 @param session: the active session to use 763 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 764 """ 765 766 url="https://"+host+"/xyz/openbmc_project/logging/entry/enumerate" 767 try: 768 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 769 except(requests.exceptions.Timeout): 770 return(connectionErrHandler(args.json, "Timeout", None)) 771 return res.text 772 773 774def parseESEL(args, eselRAW): 775 """ 776 parses the esel data and gets predetermined search terms 777 778 @param eselRAW: string, the raw esel string from the bmc 779 @return: A dictionary containing the quick snapshot data unless args.fullEsel is listed then a full PEL log is returned 780 """ 781 eselParts = {} 782 esel_bin = binascii.unhexlify(''.join(eselRAW.split()[16:])) 783 #search terms contains the search term as the key and the return dictionary key as it's value 784 searchTerms = { 'Signature Description':'signatureDescription', 'devdesc':'devdesc', 785 'Callout type': 'calloutType', 'Procedure':'procedure', 'Sensor Type': 'sensorType'} 786 uniqueID = str(uuid.uuid4()) 787 eselBinPath = tempfile.gettempdir() + os.sep + uniqueID + 'esel.bin' 788 with open(eselBinPath, 'wb') as f: 789 f.write(esel_bin) 790 errlPath = "" 791 #use the right errl file for the machine architecture 792 arch = platform.machine() 793 if(arch =='x86_64' or arch =='AMD64'): 794 if os.path.exists('/opt/ibm/ras/bin/x86_64/errl'): 795 errlPath = '/opt/ibm/ras/bin/x86_64/errl' 796 elif os.path.exists('errl/x86_64/errl'): 797 errlPath = 'errl/x86_64/errl' 798 else: 799 errlPath = 'x86_64/errl' 800 elif (platform.machine()=='ppc64le'): 801 if os.path.exists('/opt/ibm/ras/bin/ppc64le/errl'): 802 errlPath = '/opt/ibm/ras/bin/ppc64le/errl' 803 elif os.path.exists('errl/ppc64le/errl'): 804 errlPath = 'errl/ppc64le/errl' 805 else: 806 errlPath = 'ppc64le/errl' 807 else: 808 print("machine architecture not supported for parsing eSELs") 809 return eselParts 810 811 if(os.path.exists(errlPath)): 812 output= subprocess.check_output([errlPath, '-d', '--file='+eselBinPath]).decode('utf-8') 813# output = proc.communicate()[0] 814 lines = output.split('\n') 815 816 if(hasattr(args, 'fullEsel')): 817 return output 818 819 for i in range(0, len(lines)): 820 lineParts = lines[i].split(':') 821 if(len(lineParts)>1): #ignore multi lines, output formatting lines, and other information 822 for term in searchTerms: 823 if(term in lineParts[0]): 824 temp = lines[i][lines[i].find(':')+1:].strip()[:-1].strip() 825 if lines[i+1].find(':') != -1: 826 if (len(lines[i+1].split(':')[0][1:].strip())==0): 827 while(len(lines[i][:lines[i].find(':')].strip())>2): 828 #has multiple lines, process and update line counter 829 if((i+1) <= len(lines)): 830 i+=1 831 else: 832 i=i-1 833 break 834 #Append the content from the next line removing the pretty display characters 835 #Finds the first colon then starts 2 characters after, then removes all whitespace 836 temp = temp + lines[i][lines[i].find(':')+2:].strip()[:-1].strip()[:-1].strip() 837 if(searchTerms[term] in eselParts): 838 eselParts[searchTerms[term]] = eselParts[searchTerms[term]] + ", " + temp 839 else: 840 eselParts[searchTerms[term]] = temp 841 os.remove(eselBinPath) 842 else: 843 print("errl file cannot be found") 844 845 return eselParts 846 847 848def getESELSeverity(esel): 849 """ 850 Finds the severity type in an eSEL from the User Header section. 851 @param esel - the eSEL data 852 @return severity - e.g. 'Critical' 853 """ 854 855 # everything but 1 and 2 are Critical 856 # '1': 'recovered', 857 # '2': 'predictive', 858 # '4': 'unrecoverable', 859 # '5': 'critical', 860 # '6': 'diagnostic', 861 # '7': 'symptom' 862 severities = { 863 '1': 'Informational', 864 '2': 'Warning' 865 } 866 867 try: 868 headerPosition = esel.index('55 48') # 'UH' 869 # The severity is the last byte in the 8 byte section (a byte is ' bb') 870 severity = esel[headerPosition:headerPosition+32].split(' ')[-1] 871 type = severity[0] 872 except ValueError: 873 print("Could not find severity value in UH section in eSEL") 874 type = 'x'; 875 876 return severities.get(type, 'Critical') 877 878 879def sortSELs(events): 880 """ 881 sorts the sels by timestamp, then log entry number 882 883 @param events: Dictionary containing events 884 @return: list containing a list of the ordered log entries, and dictionary of keys 885 """ 886 logNumList = [] 887 timestampList = [] 888 eventKeyDict = {} 889 eventsWithTimestamp = {} 890 logNum2events = {} 891 for key in events: 892 if key == 'numAlerts': continue 893 if 'callout' in key: continue 894 timestamp = (events[key]['timestamp']) 895 if timestamp not in timestampList: 896 eventsWithTimestamp[timestamp] = [events[key]['logNum']] 897 else: 898 eventsWithTimestamp[timestamp].append(events[key]['logNum']) 899 #map logNumbers to the event dictionary keys 900 eventKeyDict[str(events[key]['logNum'])] = key 901 902 timestampList = list(eventsWithTimestamp.keys()) 903 timestampList.sort() 904 for ts in timestampList: 905 if len(eventsWithTimestamp[ts]) > 1: 906 tmplist = eventsWithTimestamp[ts] 907 tmplist.sort() 908 logNumList = logNumList + tmplist 909 else: 910 logNumList = logNumList + eventsWithTimestamp[ts] 911 912 return [logNumList, eventKeyDict] 913 914 915def parseAlerts(policyTable, selEntries, args): 916 """ 917 parses alerts in the IBM CER format, using an IBM policy Table 918 919 @param policyTable: dictionary, the policy table entries 920 @param selEntries: dictionary, the alerts retrieved from the bmc 921 @return: A dictionary of the parsed entries, in chronological order 922 """ 923 eventDict = {} 924 eventNum ="" 925 count = 0 926 esel = "" 927 eselParts = {} 928 i2cdevice= "" 929 eselSeverity = None 930 931 'prepare and sort the event entries' 932 sels = {} 933 for key in selEntries: 934 if '/xyz/openbmc_project/logging/entry/' not in key: continue 935 if 'callout' not in key: 936 sels[key] = selEntries[key] 937 sels[key]['logNum'] = key.split('/')[-1] 938 sels[key]['timestamp'] = selEntries[key]['Timestamp'] 939 sortedEntries = sortSELs(sels) 940 logNumList = sortedEntries[0] 941 eventKeyDict = sortedEntries[1] 942 943 for logNum in logNumList: 944 key = eventKeyDict[logNum] 945 hasEsel=False 946 i2creadFail = False 947 if 'callout' in key: 948 continue 949 else: 950 messageID = str(selEntries[key]['Message']) 951 addDataPiece = selEntries[key]['AdditionalData'] 952 calloutIndex = 0 953 calloutFound = False 954 for i in range(len(addDataPiece)): 955 if("CALLOUT_INVENTORY_PATH" in addDataPiece[i]): 956 calloutIndex = i 957 calloutFound = True 958 fruCallout = str(addDataPiece[calloutIndex]).split('=')[1] 959 if("CALLOUT_DEVICE_PATH" in addDataPiece[i]): 960 i2creadFail = True 961 962 fruCallout = str(addDataPiece[calloutIndex]).split('=')[1] 963 964 # Fall back to "I2C"/"FSI" if dev path isn't in policy table 965 if (messageID + '||' + fruCallout) not in policyTable: 966 i2cdevice = str(addDataPiece[i]).strip().split('=')[1] 967 i2cdevice = '/'.join(i2cdevice.split('/')[-4:]) 968 if 'fsi' in str(addDataPiece[calloutIndex]).split('=')[1]: 969 fruCallout = 'FSI' 970 else: 971 fruCallout = 'I2C' 972 calloutFound = True 973 if("CALLOUT_GPIO_NUM" in addDataPiece[i]): 974 if not calloutFound: 975 fruCallout = 'GPIO' 976 calloutFound = True 977 if("CALLOUT_IIC_BUS" in addDataPiece[i]): 978 if not calloutFound: 979 fruCallout = "I2C" 980 calloutFound = True 981 if("CALLOUT_IPMI_SENSOR_NUM" in addDataPiece[i]): 982 if not calloutFound: 983 fruCallout = "IPMI" 984 calloutFound = True 985 if("ESEL" in addDataPiece[i]): 986 esel = str(addDataPiece[i]).strip().split('=')[1] 987 eselSeverity = getESELSeverity(esel) 988 if args.devdebug: 989 eselParts = parseESEL(args, esel) 990 hasEsel=True 991 if("GPU" in addDataPiece[i]): 992 fruCallout = '/xyz/openbmc_project/inventory/system/chassis/motherboard/gpu' + str(addDataPiece[i]).strip()[-1] 993 calloutFound = True 994 if("PROCEDURE" in addDataPiece[i]): 995 fruCallout = str(hex(int(str(addDataPiece[i]).split('=')[1])))[2:] 996 calloutFound = True 997 if("RAIL_NAME" in addDataPiece[i]): 998 calloutFound=True 999 fruCallout = str(addDataPiece[i]).split('=')[1].strip() 1000 if("INPUT_NAME" in addDataPiece[i]): 1001 calloutFound=True 1002 fruCallout = str(addDataPiece[i]).split('=')[1].strip() 1003 if("SENSOR_TYPE" in addDataPiece[i]): 1004 calloutFound=True 1005 fruCallout = str(addDataPiece[i]).split('=')[1].strip() 1006 1007 if(calloutFound): 1008 if fruCallout.strip() != "": 1009 policyKey = messageID +"||" + fruCallout 1010 1011 # Also use the severity for hostboot errors 1012 if eselSeverity and messageID == 'org.open_power.Host.Error.Event': 1013 policyKey += '||' + eselSeverity 1014 1015 # if not in the table, fall back to the original key 1016 if policyKey not in policyTable: 1017 policyKey = policyKey.replace('||'+eselSeverity, '') 1018 1019 if policyKey not in policyTable: 1020 policyKey = messageID 1021 else: 1022 policyKey = messageID 1023 else: 1024 policyKey = messageID 1025 event = {} 1026 eventNum = str(count) 1027 if policyKey in policyTable: 1028 for pkey in policyTable[policyKey]: 1029 if(type(policyTable[policyKey][pkey])== bool): 1030 event[pkey] = boolToString(policyTable[policyKey][pkey]) 1031 else: 1032 if (i2creadFail and pkey == 'Message'): 1033 event[pkey] = policyTable[policyKey][pkey] + ' ' +i2cdevice 1034 else: 1035 event[pkey] = policyTable[policyKey][pkey] 1036 event['timestamp'] = selEntries[key]['Timestamp'] 1037 event['resolved'] = bool(selEntries[key]['Resolved']) 1038 if(hasEsel): 1039 if args.devdebug: 1040 event['eselParts'] = eselParts 1041 event['raweSEL'] = esel 1042 event['logNum'] = key.split('/')[-1] 1043 eventDict['event' + eventNum] = event 1044 1045 else: 1046 severity = str(selEntries[key]['Severity']).split('.')[-1] 1047 if severity == 'Error': 1048 severity = 'Critical' 1049 eventDict['event'+eventNum] = {} 1050 eventDict['event' + eventNum]['error'] = "error: Not found in policy table: " + policyKey 1051 eventDict['event' + eventNum]['timestamp'] = selEntries[key]['Timestamp'] 1052 eventDict['event' + eventNum]['Severity'] = severity 1053 if(hasEsel): 1054 if args.devdebug: 1055 eventDict['event' +eventNum]['eselParts'] = eselParts 1056 eventDict['event' +eventNum]['raweSEL'] = esel 1057 eventDict['event' +eventNum]['logNum'] = key.split('/')[-1] 1058 eventDict['event' +eventNum]['resolved'] = bool(selEntries[key]['Resolved']) 1059 count += 1 1060 return eventDict 1061 1062 1063def selDisplay(events, args): 1064 """ 1065 displays alerts in human readable format 1066 1067 @param events: Dictionary containing events 1068 @return: 1069 """ 1070 activeAlerts = [] 1071 historyAlerts = [] 1072 sortedEntries = sortSELs(events) 1073 logNumList = sortedEntries[0] 1074 eventKeyDict = sortedEntries[1] 1075 keylist = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message'] 1076 if(args.devdebug): 1077 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message', 'eSEL contents'] 1078 keylist.append('eSEL') 1079 else: 1080 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity', 'Message'] 1081 for log in logNumList: 1082 selDict = {} 1083 alert = events[eventKeyDict[str(log)]] 1084 if('error' in alert): 1085 selDict['Entry'] = alert['logNum'] 1086 selDict['ID'] = 'Unknown' 1087 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S") 1088 msg = alert['error'] 1089 polMsg = msg.split("policy table:")[0] 1090 msg = msg.split("policy table:")[1] 1091 msgPieces = msg.split("||") 1092 err = msgPieces[0] 1093 if(err.find("org.open_power.")!=-1): 1094 err = err.split("org.open_power.")[1] 1095 elif(err.find("xyz.openbmc_project.")!=-1): 1096 err = err.split("xyz.openbmc_project.")[1] 1097 else: 1098 err = msgPieces[0] 1099 callout = "" 1100 if len(msgPieces) >1: 1101 callout = msgPieces[1] 1102 if(callout.find("/org/open_power/")!=-1): 1103 callout = callout.split("/org/open_power/")[1] 1104 elif(callout.find("/xyz/openbmc_project/")!=-1): 1105 callout = callout.split("/xyz/openbmc_project/")[1] 1106 else: 1107 callout = msgPieces[1] 1108 selDict['Message'] = polMsg +"policy table: "+ err + "||" + callout 1109 selDict['Serviceable'] = 'Unknown' 1110 selDict['Severity'] = alert['Severity'] 1111 else: 1112 selDict['Entry'] = alert['logNum'] 1113 selDict['ID'] = alert['CommonEventID'] 1114 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S") 1115 selDict['Message'] = alert['Message'] 1116 selDict['Serviceable'] = alert['Serviceable'] 1117 selDict['Severity'] = alert['Severity'] 1118 1119 1120 eselOrder = ['refCode','signatureDescription', 'eselType', 'devdesc', 'calloutType', 'procedure'] 1121 if ('eselParts' in alert and args.devdebug): 1122 eselOutput = "" 1123 for item in eselOrder: 1124 if item in alert['eselParts']: 1125 eselOutput = eselOutput + item + ": " + alert['eselParts'][item] + " | " 1126 selDict['eSEL'] = eselOutput 1127 else: 1128 if args.devdebug: 1129 selDict['eSEL'] = "None" 1130 1131 if not alert['resolved']: 1132 activeAlerts.append(selDict) 1133 else: 1134 historyAlerts.append(selDict) 1135 mergedOutput = activeAlerts + historyAlerts 1136 colWidth = setColWidth(keylist, len(colNames), dict(enumerate(mergedOutput)), colNames) 1137 1138 output = "" 1139 if(len(activeAlerts)>0): 1140 row = "" 1141 output +="----Active Alerts----\n" 1142 for i in range(0, len(colNames)): 1143 if i!=0: row =row + "| " 1144 row = row + colNames[i].ljust(colWidth[i]) 1145 output += row + "\n" 1146 1147 for i in range(0,len(activeAlerts)): 1148 row = "" 1149 for j in range(len(activeAlerts[i])): 1150 if (j != 0): row = row + "| " 1151 row = row + activeAlerts[i][keylist[j]].ljust(colWidth[j]) 1152 output += row + "\n" 1153 1154 if(len(historyAlerts)>0): 1155 row = "" 1156 output+= "----Historical Alerts----\n" 1157 for i in range(len(colNames)): 1158 if i!=0: row =row + "| " 1159 row = row + colNames[i].ljust(colWidth[i]) 1160 output += row + "\n" 1161 1162 for i in range(0, len(historyAlerts)): 1163 row = "" 1164 for j in range(len(historyAlerts[i])): 1165 if (j != 0): row = row + "| " 1166 row = row + historyAlerts[i][keylist[j]].ljust(colWidth[j]) 1167 output += row + "\n" 1168# print(events[eventKeyDict[str(log)]]) 1169 return output 1170 1171 1172def selPrint(host, args, session): 1173 """ 1174 prints out all bmc alerts 1175 1176 @param host: string, the hostname or IP address of the bmc 1177 @param args: contains additional arguments used by the fru sub command 1178 @param session: the active session to use 1179 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1180 """ 1181 if(args.policyTableLoc is None): 1182 if os.path.exists('policyTable.json'): 1183 ptableLoc = "policyTable.json" 1184 elif os.path.exists('/opt/ibm/ras/lib/policyTable.json'): 1185 ptableLoc = '/opt/ibm/ras/lib/policyTable.json' 1186 else: 1187 ptableLoc = 'lib/policyTable.json' 1188 else: 1189 ptableLoc = args.policyTableLoc 1190 policyTable = loadPolicyTable(ptableLoc) 1191 rawselEntries = "" 1192 if(hasattr(args, 'fileloc') and args.fileloc is not None): 1193 if os.path.exists(args.fileloc): 1194 with open(args.fileloc, 'r') as selFile: 1195 selLines = selFile.readlines() 1196 rawselEntries = ''.join(selLines) 1197 else: 1198 print("Error: File not found") 1199 sys.exit(1) 1200 else: 1201 rawselEntries = sel(host, args, session) 1202 loadFailed = False 1203 try: 1204 selEntries = json.loads(rawselEntries) 1205 except ValueError: 1206 loadFailed = True 1207 if loadFailed: 1208 cleanSels = json.dumps(rawselEntries).replace('\\n', '') 1209 #need to load json twice as original content was string escaped a second time 1210 selEntries = json.loads(json.loads(cleanSels)) 1211 selEntries = selEntries['data'] 1212 1213 if 'description' in selEntries: 1214 if(args.json): 1215 return("{\n\t\"numAlerts\": 0\n}") 1216 else: 1217 return("No log entries found") 1218 1219 else: 1220 if(len(policyTable)>0): 1221 events = parseAlerts(policyTable, selEntries, args) 1222 if(args.json): 1223 events["numAlerts"] = len(events) 1224 retValue = str(json.dumps(events, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 1225 return retValue 1226 elif(hasattr(args, 'fullSel')): 1227 return events 1228 else: 1229 #get log numbers to order event entries sequentially 1230 return selDisplay(events, args) 1231 else: 1232 if(args.json): 1233 return selEntries 1234 else: 1235 print("error: Policy Table not found.") 1236 return selEntries 1237 1238def selList(host, args, session): 1239 """ 1240 prints out all all bmc alerts, or only prints out the specified alerts 1241 1242 @param host: string, the hostname or IP address of the bmc 1243 @param args: contains additional arguments used by the fru sub command 1244 @param session: the active session to use 1245 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1246 """ 1247 return(sel(host, args, session)) 1248 1249 1250def selClear(host, args, session): 1251 """ 1252 clears all alerts 1253 1254 @param host: string, the hostname or IP address of the bmc 1255 @param args: contains additional arguments used by the fru sub command 1256 @param session: the active session to use 1257 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1258 """ 1259 url="https://"+host+"/xyz/openbmc_project/logging/action/DeleteAll" 1260 data = "{\"data\": [] }" 1261 1262 try: 1263 res = session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1264 except(requests.exceptions.Timeout): 1265 return(connectionErrHandler(args.json, "Timeout", None)) 1266 if res.status_code == 200: 1267 return "The Alert Log has been cleared. Please allow a few minutes for the action to complete." 1268 else: 1269 print("Unable to clear the logs, trying to clear 1 at a time") 1270 sels = json.loads(sel(host, args, session))['data'] 1271 for key in sels: 1272 if 'callout' not in key: 1273 logNum = key.split('/')[-1] 1274 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete" 1275 try: 1276 session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1277 except(requests.exceptions.Timeout): 1278 return connectionErrHandler(args.json, "Timeout", None) 1279 sys.exit(1) 1280 except(requests.exceptions.ConnectionError) as err: 1281 return connectionErrHandler(args.json, "ConnectionError", err) 1282 sys.exit(1) 1283 return ('Sel clearing complete') 1284 1285def selSetResolved(host, args, session): 1286 """ 1287 sets a sel entry to resolved 1288 1289 @param host: string, the hostname or IP address of the bmc 1290 @param args: contains additional arguments used by the fru sub command 1291 @param session: the active session to use 1292 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1293 """ 1294 url="https://"+host+"/xyz/openbmc_project/logging/entry/" + str(args.selNum) + "/attr/Resolved" 1295 data = "{\"data\": 1 }" 1296 try: 1297 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1298 except(requests.exceptions.Timeout): 1299 return(connectionErrHandler(args.json, "Timeout", None)) 1300 if res.status_code == 200: 1301 return "Sel entry "+ str(args.selNum) +" is now set to resolved" 1302 else: 1303 return "Unable to set the alert to resolved" 1304 1305def selResolveAll(host, args, session): 1306 """ 1307 sets a sel entry to resolved 1308 1309 @param host: string, the hostname or IP address of the bmc 1310 @param args: contains additional arguments used by the fru sub command 1311 @param session: the active session to use 1312 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1313 """ 1314 rawselEntries = sel(host, args, session) 1315 loadFailed = False 1316 try: 1317 selEntries = json.loads(rawselEntries) 1318 except ValueError: 1319 loadFailed = True 1320 if loadFailed: 1321 cleanSels = json.dumps(rawselEntries).replace('\\n', '') 1322 #need to load json twice as original content was string escaped a second time 1323 selEntries = json.loads(json.loads(cleanSels)) 1324 selEntries = selEntries['data'] 1325 1326 if 'description' in selEntries: 1327 if(args.json): 1328 return("{\n\t\"selsResolved\": 0\n}") 1329 else: 1330 return("No log entries found") 1331 else: 1332 d = vars(args) 1333 successlist = [] 1334 failedlist = [] 1335 for key in selEntries: 1336 if 'callout' not in key: 1337 d['selNum'] = key.split('/')[-1] 1338 resolved = selSetResolved(host,args,session) 1339 if 'Sel entry' in resolved: 1340 successlist.append(d['selNum']) 1341 else: 1342 failedlist.append(d['selNum']) 1343 output = "" 1344 successlist.sort() 1345 failedlist.sort() 1346 if len(successlist)>0: 1347 output = "Successfully resolved: " +', '.join(successlist) +"\n" 1348 if len(failedlist)>0: 1349 output += "Failed to resolve: " + ', '.join(failedlist) + "\n" 1350 return output 1351 1352def chassisPower(host, args, session): 1353 """ 1354 called by the chassis function. Controls the power state of the chassis, or gets the status 1355 1356 @param host: string, the hostname or IP address of the bmc 1357 @param args: contains additional arguments used by the fru sub command 1358 @param session: the active session to use 1359 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1360 """ 1361 if(args.powcmd == 'on'): 1362 if checkFWactivation(host, args, session): 1363 return ("Chassis Power control disabled during firmware activation") 1364 print("Attempting to Power on...:") 1365 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition" 1366 data = '{"data":"xyz.openbmc_project.State.Host.Transition.On"}' 1367 try: 1368 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1369 except(requests.exceptions.Timeout): 1370 return(connectionErrHandler(args.json, "Timeout", None)) 1371 return res.text 1372 elif(args.powcmd == 'softoff'): 1373 if checkFWactivation(host, args, session): 1374 return ("Chassis Power control disabled during firmware activation") 1375 print("Attempting to Power off gracefully...:") 1376 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition" 1377 data = '{"data":"xyz.openbmc_project.State.Host.Transition.Off"}' 1378 try: 1379 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1380 except(requests.exceptions.Timeout): 1381 return(connectionErrHandler(args.json, "Timeout", None)) 1382 return res.text 1383 elif(args.powcmd == 'hardoff'): 1384 if checkFWactivation(host, args, session): 1385 return ("Chassis Power control disabled during firmware activation") 1386 print("Attempting to Power off immediately...:") 1387 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/RequestedPowerTransition" 1388 data = '{"data":"xyz.openbmc_project.State.Chassis.Transition.Off"}' 1389 try: 1390 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1391 except(requests.exceptions.Timeout): 1392 return(connectionErrHandler(args.json, "Timeout", None)) 1393 return res.text 1394 elif(args.powcmd == 'status'): 1395 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState" 1396 try: 1397 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1398 except(requests.exceptions.Timeout): 1399 return(connectionErrHandler(args.json, "Timeout", None)) 1400 chassisState = json.loads(res.text)['data'].split('.')[-1] 1401 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/CurrentHostState" 1402 try: 1403 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1404 except(requests.exceptions.Timeout): 1405 return(connectionErrHandler(args.json, "Timeout", None)) 1406 hostState = json.loads(res.text)['data'].split('.')[-1] 1407 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/CurrentBMCState" 1408 try: 1409 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1410 except(requests.exceptions.Timeout): 1411 return(connectionErrHandler(args.json, "Timeout", None)) 1412 bmcState = json.loads(res.text)['data'].split('.')[-1] 1413 if(args.json): 1414 outDict = {"Chassis Power State" : chassisState, "Host Power State" : hostState, "BMC Power State":bmcState} 1415 return json.dumps(outDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) 1416 else: 1417 return "Chassis Power State: " +chassisState + "\nHost Power State: " + hostState + "\nBMC Power State: " + bmcState 1418 else: 1419 return "Invalid chassis power command" 1420 1421 1422def chassisIdent(host, args, session): 1423 """ 1424 called by the chassis function. Controls the identify led of the chassis. Sets or gets the state 1425 1426 @param host: string, the hostname or IP address of the bmc 1427 @param args: contains additional arguments used by the fru sub command 1428 @param session: the active session to use 1429 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1430 """ 1431 if(args.identcmd == 'on'): 1432 print("Attempting to turn identify light on...:") 1433 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted" 1434 data = '{"data":true}' 1435 try: 1436 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1437 except(requests.exceptions.Timeout): 1438 return(connectionErrHandler(args.json, "Timeout", None)) 1439 return res.text 1440 elif(args.identcmd == 'off'): 1441 print("Attempting to turn identify light off...:") 1442 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted" 1443 data = '{"data":false}' 1444 try: 1445 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 1446 except(requests.exceptions.Timeout): 1447 return(connectionErrHandler(args.json, "Timeout", None)) 1448 return res.text 1449 elif(args.identcmd == 'status'): 1450 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify" 1451 try: 1452 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1453 except(requests.exceptions.Timeout): 1454 return(connectionErrHandler(args.json, "Timeout", None)) 1455 status = json.loads(res.text)['data'] 1456 if(args.json): 1457 return status 1458 else: 1459 if status['Asserted'] == 0: 1460 return "Identify light is off" 1461 else: 1462 return "Identify light is blinking" 1463 else: 1464 return "Invalid chassis identify command" 1465 1466 1467def chassis(host, args, session): 1468 """ 1469 controls the different chassis commands 1470 1471 @param host: string, the hostname or IP address of the bmc 1472 @param args: contains additional arguments used by the fru sub command 1473 @param session: the active session to use 1474 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1475 """ 1476 if(hasattr(args, 'powcmd')): 1477 result = chassisPower(host,args,session) 1478 elif(hasattr(args, 'identcmd')): 1479 result = chassisIdent(host, args, session) 1480 else: 1481 return "This feature is not yet implemented" 1482 return result 1483 1484def dumpRetrieve(host, args, session): 1485 """ 1486 Downloads dump of given dump type 1487 1488 @param host: string, the hostname or IP address of the bmc 1489 @param args: contains additional arguments used by the collectServiceData sub command 1490 @param session: the active session to use 1491 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1492 """ 1493 dumpType = args.dumpType 1494 if (args.dumpType=="SystemDump"): 1495 dumpResp=systemDumpRetrieve(host,args,session) 1496 elif(args.dumpType=="bmc"): 1497 dumpResp=bmcDumpRetrieve(host,args,session) 1498 return dumpResp 1499 1500def dumpList(host, args, session): 1501 """ 1502 Lists dump of the given dump type 1503 1504 @param host: string, the hostname or IP address of the bmc 1505 @param args: contains additional arguments used by the collectServiceData sub command 1506 @param session: the active session to use 1507 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1508 """ 1509 if (args.dumpType=="SystemDump"): 1510 dumpResp=systemDumpList(host,args,session) 1511 elif(args.dumpType=="bmc"): 1512 dumpResp=bmcDumpList(host,args,session) 1513 return dumpResp 1514 1515def dumpDelete(host, args, session): 1516 """ 1517 Deletes dump of the given dump type 1518 1519 @param host: string, the hostname or IP address of the bmc 1520 @param args: contains additional arguments used by the collectServiceData sub command 1521 @param session: the active session to use 1522 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1523 """ 1524 if (args.dumpType=="SystemDump"): 1525 dumpResp=systemDumpDelete(host,args,session) 1526 elif(args.dumpType=="bmc"): 1527 dumpResp=bmcDumpDelete(host,args,session) 1528 return dumpResp 1529 1530def dumpDeleteAll(host, args, session): 1531 """ 1532 Deletes all dumps of the given dump type 1533 1534 @param host: string, the hostname or IP address of the bmc 1535 @param args: contains additional arguments used by the collectServiceData sub command 1536 @param session: the active session to use 1537 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1538 """ 1539 if (args.dumpType=="SystemDump"): 1540 dumpResp=systemDumpDeleteAll(host,args,session) 1541 elif(args.dumpType=="bmc"): 1542 dumpResp=bmcDumpDeleteAll(host,args,session) 1543 return dumpResp 1544 1545def dumpCreate(host, args, session): 1546 """ 1547 Creates dump for the given dump type 1548 1549 @param host: string, the hostname or IP address of the bmc 1550 @param args: contains additional arguments used by the collectServiceData sub command 1551 @param session: the active session to use 1552 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1553 """ 1554 if (args.dumpType=="SystemDump"): 1555 dumpResp=systemDumpCreate(host,args,session) 1556 elif(args.dumpType=="bmc"): 1557 dumpResp=bmcDumpCreate(host,args,session) 1558 return dumpResp 1559 1560 1561def bmcDumpRetrieve(host, args, session): 1562 """ 1563 Downloads a dump file from the bmc 1564 1565 @param host: string, the hostname or IP address of the bmc 1566 @param args: contains additional arguments used by the collectServiceData sub command 1567 @param session: the active session to use 1568 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1569 """ 1570 dumpNum = args.dumpNum 1571 if (args.dumpSaveLoc is not None): 1572 saveLoc = args.dumpSaveLoc 1573 else: 1574 saveLoc = tempfile.gettempdir() 1575 url ='https://'+host+'/download/dump/' + str(dumpNum) 1576 try: 1577 r = session.get(url, headers=jsonHeader, stream=True, verify=False, timeout=baseTimeout) 1578 if (args.dumpSaveLoc is not None): 1579 if os.path.exists(saveLoc): 1580 if saveLoc[-1] != os.path.sep: 1581 saveLoc = saveLoc + os.path.sep 1582 filename = saveLoc + host+'-dump' + str(dumpNum) + '.tar.xz' 1583 1584 else: 1585 return 'Invalid save location specified' 1586 else: 1587 filename = tempfile.gettempdir()+os.sep + host+'-dump' + str(dumpNum) + '.tar.xz' 1588 1589 with open(filename, 'wb') as f: 1590 for chunk in r.iter_content(chunk_size =1024): 1591 if chunk: 1592 f.write(chunk) 1593 return 'Saved as ' + filename 1594 1595 except(requests.exceptions.Timeout): 1596 return connectionErrHandler(args.json, "Timeout", None) 1597 1598 except(requests.exceptions.ConnectionError) as err: 1599 return connectionErrHandler(args.json, "ConnectionError", err) 1600 1601def bmcDumpList(host, args, session): 1602 """ 1603 Lists the number of dump files on the bmc 1604 1605 @param host: string, the hostname or IP address of the bmc 1606 @param args: contains additional arguments used by the collectServiceData sub command 1607 @param session: the active session to use 1608 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1609 """ 1610 url ='https://'+host+'/xyz/openbmc_project/dump/list' 1611 try: 1612 r = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1613 dumpList = r.json() 1614 formattedList = [] 1615 #remove items that aren't dump entries 'entry, internal, manager endpoints' 1616 if 'data' in dumpList: 1617 for entry in dumpList['data']: 1618 if 'entry' in entry: 1619 if entry.split('/')[-1].isnumeric(): 1620 formattedList.append(entry) 1621 dumpList['data']= formattedList 1622 return dumpList 1623 except(requests.exceptions.Timeout): 1624 return connectionErrHandler(args.json, "Timeout", None) 1625 1626 except(requests.exceptions.ConnectionError) as err: 1627 return connectionErrHandler(args.json, "ConnectionError", err) 1628 1629def bmcDumpDelete(host, args, session): 1630 """ 1631 Deletes BMC dump files from the bmc 1632 1633 @param host: string, the hostname or IP address of the bmc 1634 @param args: contains additional arguments used by the collectServiceData sub command 1635 @param session: the active session to use 1636 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1637 """ 1638 dumpList = [] 1639 successList = [] 1640 failedList = [] 1641 if args.dumpNum is not None: 1642 if isinstance(args.dumpNum, list): 1643 dumpList = args.dumpNum 1644 else: 1645 dumpList.append(args.dumpNum) 1646 for dumpNum in dumpList: 1647 url ='https://'+host+'/xyz/openbmc_project/dump/entry/'+str(dumpNum)+'/action/Delete' 1648 try: 1649 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1650 if r.status_code == 200: 1651 successList.append(str(dumpNum)) 1652 else: 1653 failedList.append(str(dumpNum)) 1654 except(requests.exceptions.Timeout): 1655 return connectionErrHandler(args.json, "Timeout", None) 1656 except(requests.exceptions.ConnectionError) as err: 1657 return connectionErrHandler(args.json, "ConnectionError", err) 1658 output = "Successfully deleted dumps: " + ', '.join(successList) 1659 if(len(failedList)>0): 1660 output+= '\nFailed to delete dumps: ' + ', '.join(failedList) 1661 return output 1662 else: 1663 return 'You must specify an entry number to delete' 1664 1665def bmcDumpDeleteAll(host, args, session): 1666 """ 1667 Deletes All BMC dump files from the bmc 1668 1669 @param host: string, the hostname or IP address of the bmc 1670 @param args: contains additional arguments used by the collectServiceData sub command 1671 @param session: the active session to use 1672 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1673 """ 1674 dumpResp = bmcDumpList(host, args, session) 1675 if 'FQPSPIN0000M' in dumpResp or 'FQPSPIN0001M'in dumpResp: 1676 return dumpResp 1677 dumpList = dumpResp['data'] 1678 d = vars(args) 1679 dumpNums = [] 1680 for dump in dumpList: 1681 dumpNum = dump.strip().split('/')[-1] 1682 if dumpNum.isdigit(): 1683 dumpNums.append(int(dumpNum)) 1684 d['dumpNum'] = dumpNums 1685 1686 return bmcDumpDelete(host, args, session) 1687 1688 1689def bmcDumpCreate(host, args, session): 1690 """ 1691 Creates a bmc dump file 1692 1693 @param host: string, the hostname or IP address of the bmc 1694 @param args: contains additional arguments used by the collectServiceData sub command 1695 @param session: the active session to use 1696 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1697 """ 1698 url = 'https://'+host+'/xyz/openbmc_project/dump/action/CreateDump' 1699 try: 1700 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1701 info = r.json() 1702 if(r.status_code == 200 and not args.json): 1703 return ('Dump successfully created') 1704 elif(args.json): 1705 return info 1706 elif 'data' in info: 1707 if 'QuotaExceeded' in info['data']['description']: 1708 return 'BMC dump space is full. Please delete at least one existing dump entry and try again.' 1709 else: 1710 return "Failed to create a BMC dump. BMC Response:\n {resp}".format(resp=info) 1711 else: 1712 return "Failed to create a BMC dump. BMC Response:\n {resp}".format(resp=info) 1713 except(requests.exceptions.Timeout): 1714 return connectionErrHandler(args.json, "Timeout", None) 1715 except(requests.exceptions.ConnectionError) as err: 1716 return connectionErrHandler(args.json, "ConnectionError", err) 1717 1718 1719def systemDumpRetrieve(host, args, session): 1720 """ 1721 Downloads system dump 1722 1723 @param host: string, the hostname or IP address of the bmc 1724 @param args: contains additional arguments used by the collectServiceData sub command 1725 @param session: the active session to use 1726 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1727 """ 1728 NBDSetup(host,args,session) 1729 pipe = NBDPipe() 1730 pipe.openHTTPSocket(args) 1731 pipe.openTCPSocket() 1732 pipe.waitformessage() 1733 1734def systemDumpList(host, args, session): 1735 """ 1736 Lists system dumps 1737 1738 @param host: string, the hostname or IP address of the bmc 1739 @param args: contains additional arguments used by the collectServiceData sub command 1740 @param session: the active session to use 1741 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1742 """ 1743 url = "https://"+host+"/redfish/v1/Systems/system/LogServices/Dump/Entries" 1744 try: 1745 r = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1746 dumpList = r.json() 1747 return dumpList 1748 except(requests.exceptions.Timeout): 1749 return connectionErrHandler(args.json, "Timeout", None) 1750 1751 except(requests.exceptions.ConnectionError) as err: 1752 return connectionErrHandler(args.json, "ConnectionError", err) 1753 1754 1755def systemDumpDelete(host, args, session): 1756 """ 1757 Deletes system dump 1758 1759 @param host: string, the hostname or IP address of the bmc 1760 @param args: contains additional arguments used by the collectServiceData sub command 1761 @param session: the active session to use 1762 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1763 """ 1764 dumpList = [] 1765 successList = [] 1766 failedList = [] 1767 if args.dumpNum is not None: 1768 if isinstance(args.dumpNum, list): 1769 dumpList = args.dumpNum 1770 else: 1771 dumpList.append(args.dumpNum) 1772 for dumpNum in dumpList: 1773 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Entries/'+ str(dumpNum) 1774 try: 1775 r = session.delete(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1776 if r.status_code == 200: 1777 successList.append(str(dumpNum)) 1778 else: 1779 failedList.append(str(dumpNum)) 1780 except(requests.exceptions.Timeout): 1781 return connectionErrHandler(args.json, "Timeout", None) 1782 except(requests.exceptions.ConnectionError) as err: 1783 return connectionErrHandler(args.json, "ConnectionError", err) 1784 output = "Successfully deleted dumps: " + ', '.join(successList) 1785 if(len(failedList)>0): 1786 output+= '\nFailed to delete dumps: ' + ', '.join(failedList) 1787 return output 1788 else: 1789 return 'You must specify an entry number to delete' 1790 1791def systemDumpDeleteAll(host, args, session): 1792 """ 1793 Deletes All system dumps 1794 1795 @param host: string, the hostname or IP address of the bmc 1796 @param args: contains additional arguments used by the collectServiceData sub command 1797 @param session: the active session to use 1798 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1799 """ 1800 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog' 1801 try: 1802 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1803 if(r.status_code == 200 and not args.json): 1804 return ('Dumps successfully cleared') 1805 elif(args.json): 1806 return r.json() 1807 else: 1808 return ('Failed to clear dumps') 1809 except(requests.exceptions.Timeout): 1810 return connectionErrHandler(args.json, "Timeout", None) 1811 except(requests.exceptions.ConnectionError) as err: 1812 return connectionErrHandler(args.json, "ConnectionError", err) 1813 1814def systemDumpCreate(host, args, session): 1815 """ 1816 Creates a system dump 1817 1818 @param host: string, the hostname or IP address of the bmc 1819 @param args: contains additional arguments used by the collectServiceData sub command 1820 @param session: the active session to use 1821 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1822 """ 1823 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Actions/Oem/OemLogService.CollectDiagnosticData' 1824 try: 1825 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1826 if(r.status_code == 200): 1827 return r.json() 1828 else: 1829 return ('Failed to create dump') 1830 except(requests.exceptions.Timeout): 1831 return connectionErrHandler(args.json, "Timeout", None) 1832 except(requests.exceptions.ConnectionError) as err: 1833 return connectionErrHandler(args.json, "ConnectionError", err) 1834 1835def csdDumpInitiate(host, args, session): 1836 """ 1837 Starts the process of getting the current list of dumps then initiates the creation of one. 1838 1839 @param host: string, the hostname or IP address of the bmc 1840 @param args: contains additional arguments used by the collectServiceData sub command 1841 @param session: the active session to use 1842 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1843 """ 1844 errorInfo = "" 1845 dumpcount = 0 1846 try: 1847 d = vars(args) 1848 d['json'] = True 1849 except Exception as e: 1850 errorInfo += "Failed to set the json flag to True \n Exception: {eInfo}\n".format(eInfo=e) 1851 exc_type, exc_obj, exc_tb = sys.exc_info() 1852 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1853 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1854 errorInfo += traceback.format_exc() 1855 1856 try: 1857 for i in range(3): 1858 dumpInfo = bmcDumpList(host, args, session) 1859 if 'data' in dumpInfo: 1860 dumpcount = len(dumpInfo['data']) 1861 break 1862 else: 1863 errorInfo+= "Dump List Message returned: " + json.dumps(dumpInfo,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1864 except Exception as e: 1865 errorInfo+= "Failed to collect the list of dumps.\nException: {eInfo}\n".format(eInfo=e) 1866 exc_type, exc_obj, exc_tb = sys.exc_info() 1867 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1868 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1869 errorInfo += traceback.format_exc() 1870 1871 #Create a user initiated dump 1872 dumpFailure = True 1873 try: 1874 for i in range(3): 1875 dumpcreated = bmcDumpCreate(host, args, session) 1876 if 'message' in dumpcreated: 1877 if 'ok' in dumpcreated['message'].lower(): 1878 dumpFailure = False 1879 break 1880 elif 'data' in dumpcreated: 1881 if 'QuotaExceeded' in dumpcreated['data']['description']: 1882 print('Not enough dump space on the BMC to create a new dump. Please delete the oldest entry (lowest number) and rerun the collect_service_data command.') 1883 errorInfo+='Dump Space is full. No new dump was created with this collection' 1884 break 1885 else: 1886 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1887 else: 1888 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1889 else: 1890 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1891 except Exception as e: 1892 errorInfo+= "Dump create exception encountered: {eInfo}\n".format(eInfo=e) 1893 exc_type, exc_obj, exc_tb = sys.exc_info() 1894 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1895 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1896 errorInfo += traceback.format_exc() 1897 1898 output = {} 1899 output['errors'] = errorInfo 1900 output['dumpcount'] = dumpcount 1901 if dumpFailure: output['dumpFailure'] = True 1902 return output 1903 1904def csdInventory(host, args,session, fileDir): 1905 """ 1906 Collects the BMC inventory, retrying if necessary 1907 1908 @param host: string, the hostname or IP address of the bmc 1909 @param args: contains additional arguments used by the collectServiceData sub command 1910 @param session: the active session to use 1911 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1912 @param fileDir: string representation of the path to use for putting files created 1913 """ 1914 errorInfo = "===========Inventory =============\n" 1915 output={} 1916 inventoryCollected = False 1917 try: 1918 for i in range(3): 1919 frulist = fruPrint(host, args, session) 1920 if 'Hardware' in frulist: 1921 inventoryCollected = True 1922 break 1923 else: 1924 errorInfo += json.dumps(frulist, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 1925 except Exception as e: 1926 errorInfo += "Inventory collection exception: {eInfo}\n".format(eInfo=e) 1927 exc_type, exc_obj, exc_tb = sys.exc_info() 1928 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1929 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1930 errorInfo += traceback.format_exc() 1931 if inventoryCollected: 1932 try: 1933 with open(fileDir +os.sep+'inventory.txt', 'w') as f: 1934 f.write(json.dumps(frulist, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 1935 print("Inventory collected and stored in " + fileDir + os.sep + "inventory.txt") 1936 output['fileLoc'] = fileDir+os.sep+'inventory.txt' 1937 except Exception as e: 1938 print("Failed to write inventory to file.") 1939 errorInfo += "Error writing inventory to the file. Exception: {eInfo}\n".format(eInfo=e) 1940 exc_type, exc_obj, exc_tb = sys.exc_info() 1941 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1942 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1943 errorInfo += traceback.format_exc() 1944 1945 output['errors'] = errorInfo 1946 1947 return output 1948 1949def csdSensors(host, args,session, fileDir): 1950 """ 1951 Collects the BMC sensor readings, retrying if necessary 1952 1953 @param host: string, the hostname or IP address of the bmc 1954 @param args: contains additional arguments used by the collectServiceData sub command 1955 @param session: the active session to use 1956 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1957 @param fileDir: string representation of the path to use for putting files created 1958 """ 1959 errorInfo = "===========Sensors =============\n" 1960 sensorsCollected = False 1961 output={} 1962 try: 1963 d = vars(args) 1964 d['json'] = False 1965 except Exception as e: 1966 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 1967 exc_type, exc_obj, exc_tb = sys.exc_info() 1968 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1969 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1970 errorInfo += traceback.format_exc() 1971 1972 try: 1973 for i in range(3): 1974 sensorReadings = sensor(host, args, session) 1975 if 'OCC0' in sensorReadings: 1976 sensorsCollected = True 1977 break 1978 else: 1979 errorInfo += sensorReadings 1980 except Exception as e: 1981 errorInfo += "Sensor reading collection exception: {eInfo}\n".format(eInfo=e) 1982 exc_type, exc_obj, exc_tb = sys.exc_info() 1983 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1984 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1985 errorInfo += traceback.format_exc() 1986 if sensorsCollected: 1987 try: 1988 with open(fileDir +os.sep+'sensorReadings.txt', 'w') as f: 1989 f.write(sensorReadings) 1990 print("Sensor readings collected and stored in " + fileDir + os.sep+ "sensorReadings.txt") 1991 output['fileLoc'] = fileDir+os.sep+'sensorReadings.txt' 1992 except Exception as e: 1993 print("Failed to write sensor readings to file system.") 1994 errorInfo += "Error writing sensor readings to the file. Exception: {eInfo}\n".format(eInfo=e) 1995 exc_type, exc_obj, exc_tb = sys.exc_info() 1996 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1997 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1998 errorInfo += traceback.format_exc() 1999 2000 output['errors'] = errorInfo 2001 return output 2002 2003def csdLEDs(host,args, session, fileDir): 2004 """ 2005 Collects the BMC LED status, retrying if necessary 2006 2007 @param host: string, the hostname or IP address of the bmc 2008 @param args: contains additional arguments used by the collectServiceData sub command 2009 @param session: the active session to use 2010 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2011 @param fileDir: string representation of the path to use for putting files created 2012 """ 2013 errorInfo = "===========LEDs =============\n" 2014 ledsCollected = False 2015 output={} 2016 try: 2017 d = vars(args) 2018 d['json'] = True 2019 except Exception as e: 2020 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2021 exc_type, exc_obj, exc_tb = sys.exc_info() 2022 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2023 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2024 errorInfo += traceback.format_exc() 2025 try: 2026 url="https://"+host+"/xyz/openbmc_project/led/enumerate" 2027 httpHeader = {'Content-Type':'application/json'} 2028 for i in range(3): 2029 try: 2030 ledRes = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2031 if ledRes.status_code == 200: 2032 ledsCollected = True 2033 leds = ledRes.json()['data'] 2034 break 2035 else: 2036 errorInfo += ledRes.text 2037 except(requests.exceptions.Timeout): 2038 errorInfo+=json.dumps( connectionErrHandler(args.json, "Timeout", None), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2039 except(requests.exceptions.ConnectionError) as err: 2040 errorInfo += json.dumps(connectionErrHandler(args.json, "ConnectionError", err), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2041 exc_type, exc_obj, exc_tb = sys.exc_info() 2042 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2043 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2044 errorInfo += traceback.format_exc() 2045 except Exception as e: 2046 errorInfo += "LED status collection exception: {eInfo}\n".format(eInfo=e) 2047 exc_type, exc_obj, exc_tb = sys.exc_info() 2048 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2049 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2050 errorInfo += traceback.format_exc() 2051 2052 if ledsCollected: 2053 try: 2054 with open(fileDir +os.sep+'ledStatus.txt', 'w') as f: 2055 f.write(json.dumps(leds, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 2056 print("LED status collected and stored in " + fileDir + os.sep+ "ledStatus.txt") 2057 output['fileLoc'] = fileDir+os.sep+'ledStatus.txt' 2058 except Exception as e: 2059 print("Failed to write LED status to file system.") 2060 errorInfo += "Error writing LED status to the file. Exception: {eInfo}\n".format(eInfo=e) 2061 exc_type, exc_obj, exc_tb = sys.exc_info() 2062 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2063 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2064 errorInfo += traceback.format_exc() 2065 2066 output['errors'] = errorInfo 2067 return output 2068 2069def csdSelShortList(host, args, session, fileDir): 2070 """ 2071 Collects the BMC log entries, retrying if necessary 2072 2073 @param host: string, the hostname or IP address of the bmc 2074 @param args: contains additional arguments used by the collectServiceData sub command 2075 @param session: the active session to use 2076 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2077 @param fileDir: string representation of the path to use for putting files created 2078 """ 2079 errorInfo = "===========SEL Short List =============\n" 2080 selsCollected = False 2081 output={} 2082 try: 2083 d = vars(args) 2084 d['json'] = False 2085 except Exception as e: 2086 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2087 exc_type, exc_obj, exc_tb = sys.exc_info() 2088 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2089 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2090 errorInfo += traceback.format_exc() 2091 2092 try: 2093 for i in range(3): 2094 sels = selPrint(host,args,session) 2095 if '----Active Alerts----' in sels or 'No log entries found' in sels or '----Historical Alerts----' in sels: 2096 selsCollected = True 2097 break 2098 else: 2099 errorInfo += sels + '\n' 2100 except Exception as e: 2101 errorInfo += "SEL short list collection exception: {eInfo}\n".format(eInfo=e) 2102 exc_type, exc_obj, exc_tb = sys.exc_info() 2103 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2104 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2105 errorInfo += traceback.format_exc() 2106 2107 if selsCollected: 2108 try: 2109 with open(fileDir +os.sep+'SELshortlist.txt', 'w') as f: 2110 f.write(sels) 2111 print("SEL short list collected and stored in " + fileDir + os.sep+ "SELshortlist.txt") 2112 output['fileLoc'] = fileDir+os.sep+'SELshortlist.txt' 2113 except Exception as e: 2114 print("Failed to write SEL short list to file system.") 2115 errorInfo += "Error writing SEL short list to the file. Exception: {eInfo}\n".format(eInfo=e) 2116 exc_type, exc_obj, exc_tb = sys.exc_info() 2117 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2118 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2119 errorInfo += traceback.format_exc() 2120 2121 output['errors'] = errorInfo 2122 return output 2123 2124def csdParsedSels(host, args, session, fileDir): 2125 """ 2126 Collects the BMC log entries, retrying if necessary 2127 2128 @param host: string, the hostname or IP address of the bmc 2129 @param args: contains additional arguments used by the collectServiceData sub command 2130 @param session: the active session to use 2131 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2132 @param fileDir: string representation of the path to use for putting files created 2133 """ 2134 errorInfo = "===========SEL Parsed List =============\n" 2135 selsCollected = False 2136 output={} 2137 try: 2138 d = vars(args) 2139 d['json'] = True 2140 d['fullEsel'] = True 2141 except Exception as e: 2142 errorInfo += "Failed to set the json flag to True \n Exception: {eInfo}\n".format(eInfo=e) 2143 exc_type, exc_obj, exc_tb = sys.exc_info() 2144 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2145 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2146 errorInfo += traceback.format_exc() 2147 2148 try: 2149 for i in range(3): 2150 parsedfullsels = json.loads(selPrint(host,args,session)) 2151 if 'numAlerts' in parsedfullsels: 2152 selsCollected = True 2153 break 2154 else: 2155 errorInfo += parsedfullsels + '\n' 2156 except Exception as e: 2157 errorInfo += "Parsed full SELs collection exception: {eInfo}\n".format(eInfo=e) 2158 exc_type, exc_obj, exc_tb = sys.exc_info() 2159 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2160 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2161 errorInfo += traceback.format_exc() 2162 2163 if selsCollected: 2164 try: 2165 sortedSELs = sortSELs(parsedfullsels) 2166 with open(fileDir +os.sep+'parsedSELs.txt', 'w') as f: 2167 for log in sortedSELs[0]: 2168 esel = "" 2169 parsedfullsels[sortedSELs[1][str(log)]]['timestamp'] = datetime.datetime.fromtimestamp(int(parsedfullsels[sortedSELs[1][str(log)]]['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S") 2170 if ('raweSEL' in parsedfullsels[sortedSELs[1][str(log)]] and args.devdebug): 2171 esel = parsedfullsels[sortedSELs[1][str(log)]]['raweSEL'] 2172 del parsedfullsels[sortedSELs[1][str(log)]]['raweSEL'] 2173 f.write(json.dumps(parsedfullsels[sortedSELs[1][str(log)]],sort_keys=True, indent=4, separators=(',', ': '))) 2174 if(args.devdebug and esel != ""): 2175 f.write(parseESEL(args, esel)) 2176 print("Parsed SELs collected and stored in " + fileDir + os.sep+ "parsedSELs.txt") 2177 output['fileLoc'] = fileDir+os.sep+'parsedSELs.txt' 2178 except Exception as e: 2179 print("Failed to write fully parsed SELs to file system.") 2180 errorInfo += "Error writing fully parsed SELs to the file. Exception: {eInfo}\n".format(eInfo=e) 2181 exc_type, exc_obj, exc_tb = sys.exc_info() 2182 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2183 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2184 errorInfo += traceback.format_exc() 2185 2186 output['errors'] = errorInfo 2187 return output 2188 2189def csdFullEnumeration(host, args, session, fileDir): 2190 """ 2191 Collects a full enumeration of /xyz/openbmc_project/, retrying if necessary 2192 2193 @param host: string, the hostname or IP address of the bmc 2194 @param args: contains additional arguments used by the collectServiceData sub command 2195 @param session: the active session to use 2196 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2197 @param fileDir: string representation of the path to use for putting files created 2198 """ 2199 errorInfo = "===========BMC Full Enumeration =============\n" 2200 bmcFullCollected = False 2201 output={} 2202 try: 2203 d = vars(args) 2204 d['json'] = True 2205 except Exception as e: 2206 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2207 exc_type, exc_obj, exc_tb = sys.exc_info() 2208 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2209 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2210 errorInfo += traceback.format_exc() 2211 try: 2212 print("Attempting to get a full BMC enumeration") 2213 url="https://"+host+"/xyz/openbmc_project/enumerate" 2214 httpHeader = {'Content-Type':'application/json'} 2215 for i in range(3): 2216 try: 2217 bmcRes = session.get(url, headers=jsonHeader, verify=False, timeout=180) 2218 if bmcRes.status_code == 200: 2219 bmcFullCollected = True 2220 fullEnumeration = bmcRes.json() 2221 break 2222 else: 2223 errorInfo += bmcRes.text 2224 except(requests.exceptions.Timeout): 2225 errorInfo+=json.dumps( connectionErrHandler(args.json, "Timeout", None), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2226 except(requests.exceptions.ConnectionError) as err: 2227 errorInfo += json.dumps(connectionErrHandler(args.json, "ConnectionError", err), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2228 exc_type, exc_obj, exc_tb = sys.exc_info() 2229 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2230 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2231 errorInfo += traceback.format_exc() 2232 except Exception as e: 2233 errorInfo += "RAW BMC data collection exception: {eInfo}\n".format(eInfo=e) 2234 exc_type, exc_obj, exc_tb = sys.exc_info() 2235 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2236 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2237 errorInfo += traceback.format_exc() 2238 2239 if bmcFullCollected: 2240 try: 2241 with open(fileDir +os.sep+'bmcFullRaw.txt', 'w') as f: 2242 f.write(json.dumps(fullEnumeration, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 2243 print("RAW BMC data collected and saved into " + fileDir + os.sep+ "bmcFullRaw.txt") 2244 output['fileLoc'] = fileDir+os.sep+'bmcFullRaw.txt' 2245 except Exception as e: 2246 print("Failed to write RAW BMC data to file system.") 2247 errorInfo += "Error writing RAW BMC data collection to the file. Exception: {eInfo}\n".format(eInfo=e) 2248 exc_type, exc_obj, exc_tb = sys.exc_info() 2249 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2250 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2251 errorInfo += traceback.format_exc() 2252 2253 output['errors'] = errorInfo 2254 return output 2255 2256def csdCollectAllDumps(host, args, session, fileDir): 2257 """ 2258 Collects all of the bmc dump files and stores them in fileDir 2259 2260 @param host: string, the hostname or IP address of the bmc 2261 @param args: contains additional arguments used by the collectServiceData sub command 2262 @param session: the active session to use 2263 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2264 @param fileDir: string representation of the path to use for putting files created 2265 """ 2266 2267 errorInfo = "===========BMC Dump Collection =============\n" 2268 dumpListCollected = False 2269 output={} 2270 dumpList = {} 2271 try: 2272 d = vars(args) 2273 d['json'] = True 2274 d['dumpSaveLoc'] = fileDir 2275 except Exception as e: 2276 errorInfo += "Failed to set the json flag to True, or failed to set the dumpSave Location \n Exception: {eInfo}\n".format(eInfo=e) 2277 exc_type, exc_obj, exc_tb = sys.exc_info() 2278 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2279 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2280 errorInfo += traceback.format_exc() 2281 2282 print('Collecting bmc dump files') 2283 2284 try: 2285 for i in range(3): 2286 dumpResp = bmcDumpList(host, args, session) 2287 if 'message' in dumpResp: 2288 if 'ok' in dumpResp['message'].lower(): 2289 dumpList = dumpResp['data'] 2290 dumpListCollected = True 2291 break 2292 else: 2293 errorInfo += "Status was not OK when retrieving the list of dumps available. \n Response: \n{resp}\n".format(resp=dumpResp) 2294 else: 2295 errorInfo += "Invalid response received from the BMC while retrieving the list of dumps available.\n {resp}\n".format(resp=dumpResp) 2296 except Exception as e: 2297 errorInfo += "BMC dump list exception: {eInfo}\n".format(eInfo=e) 2298 exc_type, exc_obj, exc_tb = sys.exc_info() 2299 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2300 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2301 errorInfo += traceback.format_exc() 2302 2303 if dumpListCollected: 2304 output['fileList'] = [] 2305 for dump in dumpList: 2306 try: 2307 if '/xyz/openbmc_project/dump/internal/manager' not in dump: 2308 d['dumpNum'] = int(dump.strip().split('/')[-1]) 2309 print('retrieving dump file ' + str(d['dumpNum'])) 2310 filename = bmcDumpRetrieve(host, args, session).split('Saved as ')[-1] 2311 output['fileList'].append(filename) 2312 except Exception as e: 2313 print("Unable to collect dump: {dumpInfo}".format(dumpInfo=dump)) 2314 errorInfo += "Exception collecting a bmc dump {dumpInfo}\n {eInfo}\n".format(dumpInfo=dump, eInfo=e) 2315 exc_type, exc_obj, exc_tb = sys.exc_info() 2316 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2317 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2318 errorInfo += traceback.format_exc() 2319 output['errors'] = errorInfo 2320 return output 2321 2322def collectServiceData(host, args, session): 2323 """ 2324 Collects all data needed for service from the BMC 2325 2326 @param host: string, the hostname or IP address of the bmc 2327 @param args: contains additional arguments used by the collectServiceData sub command 2328 @param session: the active session to use 2329 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2330 """ 2331 2332 global toolVersion 2333 filelist = [] 2334 errorInfo = "" 2335 2336 #get current number of bmc dumps and create a new bmc dump 2337 dumpInitdata = csdDumpInitiate(host, args, session) 2338 if 'dumpFailure' in dumpInitdata: 2339 return 'Collect service data is stopping due to not being able to create a new dump. No service data was collected.' 2340 dumpcount = dumpInitdata['dumpcount'] 2341 errorInfo += dumpInitdata['errors'] 2342 #create the directory to put files 2343 try: 2344 args.silent = True 2345 myDir = tempfile.gettempdir()+os.sep + host + "--" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S") 2346 os.makedirs(myDir) 2347 2348 except Exception as e: 2349 print('Unable to create the temporary directory for data collection. Ensure sufficient privileges to create temporary directory. Aborting.') 2350 exc_type, exc_obj, exc_tb = sys.exc_info() 2351 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2352 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2353 errorInfo += traceback.format_exc() 2354 return("Python exception: {eInfo}".format(eInfo = e)) 2355 2356 #Collect Inventory 2357 inventoryData = csdInventory(host, args, session, myDir) 2358 if 'fileLoc' in inventoryData: 2359 filelist.append(inventoryData['fileLoc']) 2360 errorInfo += inventoryData['errors'] 2361 #Read all the sensor and OCC status 2362 sensorData = csdSensors(host,args,session,myDir) 2363 if 'fileLoc' in sensorData: 2364 filelist.append(sensorData['fileLoc']) 2365 errorInfo += sensorData['errors'] 2366 #Collect all of the LEDs status 2367 ledStatus = csdLEDs(host, args, session, myDir) 2368 if 'fileLoc' in ledStatus: 2369 filelist.append(ledStatus['fileLoc']) 2370 errorInfo += ledStatus['errors'] 2371 2372 #Collect the bmc logs 2373 selShort = csdSelShortList(host, args, session, myDir) 2374 if 'fileLoc' in selShort: 2375 filelist.append(selShort['fileLoc']) 2376 errorInfo += selShort['errors'] 2377 2378 parsedSELs = csdParsedSels(host, args, session, myDir) 2379 if 'fileLoc' in parsedSELs: 2380 filelist.append(parsedSELs['fileLoc']) 2381 errorInfo += parsedSELs['errors'] 2382 2383 #collect RAW bmc enumeration 2384 bmcRaw = csdFullEnumeration(host, args, session, myDir) 2385 if 'fileLoc' in bmcRaw: 2386 filelist.append(bmcRaw['fileLoc']) 2387 errorInfo += bmcRaw['errors'] 2388 2389 #wait for new dump to finish being created 2390 waitingForNewDump = True 2391 count = 0; 2392 print("Waiting for new BMC dump to finish being created. Wait time could be up to 5 minutes") 2393 while(waitingForNewDump): 2394 dumpList = bmcDumpList(host, args, session)['data'] 2395 if len(dumpList) > dumpcount: 2396 waitingForNewDump = False 2397 break; 2398 elif(count>150): 2399 print("Timed out waiting for bmc to make a new dump file. Continuing without it.") 2400 break; 2401 else: 2402 time.sleep(2) 2403 count += 1 2404 2405 #collect all of the dump files 2406 getBMCDumps = csdCollectAllDumps(host, args, session, myDir) 2407 if 'fileList' in getBMCDumps: 2408 filelist+= getBMCDumps['fileList'] 2409 errorInfo += getBMCDumps['errors'] 2410 2411 #write the runtime errors to a file 2412 try: 2413 with open(myDir +os.sep+'openbmctoolRuntimeErrors.txt', 'w') as f: 2414 f.write(errorInfo) 2415 print("OpenBMC tool runtime errors collected and stored in " + myDir + os.sep+ "openbmctoolRuntimeErrors.txt") 2416 filelist.append(myDir+os.sep+'openbmctoolRuntimeErrors.txt') 2417 except Exception as e: 2418 print("Failed to write OpenBMC tool runtime errors to file system.") 2419 2420 #create the zip file 2421 try: 2422 filename = myDir.split(tempfile.gettempdir()+os.sep)[-1] + "_" + toolVersion + '_openbmc.zip' 2423 zf = zipfile.ZipFile(myDir+os.sep + filename, 'w') 2424 for myfile in filelist: 2425 zf.write(myfile, os.path.basename(myfile)) 2426 zf.close() 2427 print("Zip file with all collected data created and stored in: {fileInfo}".format(fileInfo=myDir+os.sep+filename)) 2428 except Exception as e: 2429 print("Failed to create zip file with collected information") 2430 return "data collection finished" 2431 2432 2433def healthCheck(host, args, session): 2434 """ 2435 runs a health check on the platform 2436 2437 @param host: string, the hostname or IP address of the bmc 2438 @param args: contains additional arguments used by the bmc sub command 2439 @param session: the active session to use 2440 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2441 """ 2442 #check fru status and get as json to easily work through 2443 d = vars(args) 2444 useJson = d['json'] 2445 d['json'] = True 2446 d['verbose']= False 2447 2448 frus = json.loads(fruStatus(host, args, session)) 2449 2450 hwStatus= "OK" 2451 performanceStatus = "OK" 2452 for key in frus: 2453 if frus[key]["Functional"] == "No" and frus[key]["Present"] == "Yes": 2454 hwStatus= "Degraded" 2455 if("power_supply" in key or "powersupply" in key): 2456 gpuCount =0 2457 for comp in frus: 2458 if "gv100card" in comp: 2459 gpuCount +=1 2460 if gpuCount > 4: 2461 hwStatus = "Critical" 2462 performanceStatus="Degraded" 2463 break; 2464 elif("fan" in key): 2465 hwStatus = "Degraded" 2466 else: 2467 performanceStatus = "Degraded" 2468 if useJson: 2469 output = {"Hardware Status": hwStatus, "Performance": performanceStatus} 2470 output = json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) 2471 else: 2472 output = ("Hardware Status: " + hwStatus + 2473 "\nPerformance: " +performanceStatus ) 2474 2475 2476 #SW407886: Clear the duplicate entries 2477 #collect the dups 2478 d['devdebug'] = False 2479 sels = json.loads(selPrint(host, args, session)) 2480 logNums2Clr = [] 2481 oldestLogNum={"logNum": "bogus" ,"key" : ""} 2482 count = 0 2483 if sels['numAlerts'] > 0: 2484 for key in sels: 2485 if "numAlerts" in key: 2486 continue 2487 try: 2488 if "slave@00:00/00:00:00:06/sbefifo1-dev0/occ1-dev0" in sels[key]['Message']: 2489 count += 1 2490 if count > 1: 2491 #preserve first occurrence 2492 if sels[key]['timestamp'] < sels[oldestLogNum['key']]['timestamp']: 2493 oldestLogNum['key']=key 2494 oldestLogNum['logNum'] = sels[key]['logNum'] 2495 else: 2496 oldestLogNum['key']=key 2497 oldestLogNum['logNum'] = sels[key]['logNum'] 2498 logNums2Clr.append(sels[key]['logNum']) 2499 except KeyError: 2500 continue 2501 if(count >0): 2502 logNums2Clr.remove(oldestLogNum['logNum']) 2503 #delete the dups 2504 if count >1: 2505 data = "{\"data\": [] }" 2506 for logNum in logNums2Clr: 2507 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete" 2508 try: 2509 session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2510 except(requests.exceptions.Timeout): 2511 deleteFailed = True 2512 except(requests.exceptions.ConnectionError) as err: 2513 deleteFailed = True 2514 #End of defect resolve code 2515 d['json'] = useJson 2516 return output 2517 2518 2519 2520def bmc(host, args, session): 2521 """ 2522 handles various bmc level commands, currently bmc rebooting 2523 2524 @param host: string, the hostname or IP address of the bmc 2525 @param args: contains additional arguments used by the bmc sub command 2526 @param session: the active session to use 2527 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2528 """ 2529 if(args.type is not None): 2530 return bmcReset(host, args, session) 2531 if(args.info): 2532 return "Not implemented at this time" 2533 2534 2535 2536def bmcReset(host, args, session): 2537 """ 2538 controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots. 2539 2540 @param host: string, the hostname or IP address of the bmc 2541 @param args: contains additional arguments used by the bmcReset sub command 2542 @param session: the active session to use 2543 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2544 """ 2545 if checkFWactivation(host, args, session): 2546 return ("BMC reset control disabled during firmware activation") 2547 if(args.type == "warm"): 2548 print("\nAttempting to reboot the BMC...:") 2549 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition" 2550 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}' 2551 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2552 return res.text 2553 elif(args.type =="cold"): 2554 print("\nAttempting to reboot the BMC...:") 2555 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition" 2556 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}' 2557 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2558 return res.text 2559 else: 2560 return "invalid command" 2561 2562def gardClear(host, args, session): 2563 """ 2564 clears the gard records from the bmc 2565 2566 @param host: string, the hostname or IP address of the bmc 2567 @param args: contains additional arguments used by the gardClear sub command 2568 @param session: the active session to use 2569 """ 2570 url="https://"+host+"/org/open_power/control/gard/action/Reset" 2571 data = '{"data":[]}' 2572 try: 2573 2574 res = session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2575 if res.status_code == 404: 2576 return "Command not supported by this firmware version" 2577 else: 2578 return res.text 2579 except(requests.exceptions.Timeout): 2580 return connectionErrHandler(args.json, "Timeout", None) 2581 except(requests.exceptions.ConnectionError) as err: 2582 return connectionErrHandler(args.json, "ConnectionError", err) 2583 2584def activateFWImage(host, args, session): 2585 """ 2586 activates a firmware image on the bmc 2587 2588 @param host: string, the hostname or IP address of the bmc 2589 @param args: contains additional arguments used by the fwflash sub command 2590 @param session: the active session to use 2591 @param fwID: the unique ID of the fw image to activate 2592 """ 2593 fwID = args.imageID 2594 2595 #determine the existing versions 2596 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2597 try: 2598 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2599 except(requests.exceptions.Timeout): 2600 return connectionErrHandler(args.json, "Timeout", None) 2601 except(requests.exceptions.ConnectionError) as err: 2602 return connectionErrHandler(args.json, "ConnectionError", err) 2603 existingSoftware = json.loads(resp.text)['data'] 2604 altVersionID = '' 2605 versionType = '' 2606 imageKey = '/xyz/openbmc_project/software/'+fwID 2607 if imageKey in existingSoftware: 2608 versionType = existingSoftware[imageKey]['Purpose'] 2609 for key in existingSoftware: 2610 if imageKey == key: 2611 continue 2612 if 'Purpose' in existingSoftware[key]: 2613 if versionType == existingSoftware[key]['Purpose']: 2614 altVersionID = key.split('/')[-1] 2615 2616 2617 2618 2619 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/attr/Priority" 2620 url1="https://"+host+"/xyz/openbmc_project/software/"+ altVersionID + "/attr/Priority" 2621 data = "{\"data\": 0}" 2622 data1 = "{\"data\": 1 }" 2623 try: 2624 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2625 resp1 = session.put(url1, headers=jsonHeader, data=data1, verify=False, timeout=baseTimeout) 2626 except(requests.exceptions.Timeout): 2627 return connectionErrHandler(args.json, "Timeout", None) 2628 except(requests.exceptions.ConnectionError) as err: 2629 return connectionErrHandler(args.json, "ConnectionError", err) 2630 if(not args.json): 2631 if resp.status_code == 200 and resp1.status_code == 200: 2632 return 'Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. ' 2633 else: 2634 return "Firmware activation failed." 2635 else: 2636 return resp.text + resp1.text 2637 2638def activateStatus(host, args, session): 2639 if checkFWactivation(host, args, session): 2640 return("Firmware is currently being activated. Do not reboot the BMC or start the Host OS") 2641 else: 2642 return("No firmware activations are pending") 2643 2644def extractFWimage(path, imageType): 2645 """ 2646 extracts the bmc image and returns information about the package 2647 2648 @param path: the path and file name of the firmware image 2649 @param imageType: The type of image the user is trying to flash. Host or BMC 2650 @return: the image id associated with the package. returns an empty string on error. 2651 """ 2652 f = tempfile.TemporaryFile() 2653 tmpDir = tempfile.gettempdir() 2654 newImageID = "" 2655 if os.path.exists(path): 2656 try: 2657 imageFile = tarfile.open(path,'r') 2658 contents = imageFile.getmembers() 2659 for tf in contents: 2660 if 'MANIFEST' in tf.name: 2661 imageFile.extract(tf.name, path=tmpDir) 2662 with open(tempfile.gettempdir() +os.sep+ tf.name, 'r') as imageInfo: 2663 for line in imageInfo: 2664 if 'purpose' in line: 2665 purpose = line.split('=')[1] 2666 if imageType not in purpose.split('.')[-1]: 2667 print('The specified image is not for ' + imageType) 2668 print('Please try again with the image for ' + imageType) 2669 return "" 2670 if 'version' == line.split('=')[0]: 2671 version = line.split('=')[1].strip().encode('utf-8') 2672 m = hashlib.sha512() 2673 m.update(version) 2674 newImageID = m.hexdigest()[:8] 2675 break 2676 try: 2677 os.remove(tempfile.gettempdir() +os.sep+ tf.name) 2678 except OSError: 2679 pass 2680 return newImageID 2681 except tarfile.ExtractError as e: 2682 print('Unable to extract information from the firmware file.') 2683 print('Ensure you have write access to the directory: ' + tmpDir) 2684 return newImageID 2685 except tarfile.TarError as e: 2686 print('This is not a valid firmware file.') 2687 return newImageID 2688 print("This is not a valid firmware file.") 2689 return newImageID 2690 else: 2691 print('The filename and path provided are not valid.') 2692 return newImageID 2693 2694def getAllFWImageIDs(fwInvDict): 2695 """ 2696 gets a list of all the firmware image IDs 2697 2698 @param fwInvDict: the dictionary to search for FW image IDs 2699 @return: list containing string representation of the found image ids 2700 """ 2701 idList = [] 2702 for key in fwInvDict: 2703 if 'Version' in fwInvDict[key]: 2704 idList.append(key.split('/')[-1]) 2705 return idList 2706 2707def fwFlash(host, args, session): 2708 """ 2709 updates the bmc firmware and pnor firmware 2710 2711 @param host: string, the hostname or IP address of the bmc 2712 @param args: contains additional arguments used by the fwflash sub command 2713 @param session: the active session to use 2714 """ 2715 d = vars(args) 2716 if(args.type == 'bmc'): 2717 purp = 'BMC' 2718 else: 2719 purp = 'Host' 2720 2721 #check power state of the machine. No concurrent FW updates allowed 2722 d['powcmd'] = 'status' 2723 powerstate = chassisPower(host, args, session) 2724 if 'Chassis Power State: On' in powerstate: 2725 return("Aborting firmware update. Host is powered on. Please turn off the host and try again.") 2726 2727 #determine the existing images on the bmc 2728 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2729 try: 2730 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2731 except(requests.exceptions.Timeout): 2732 return connectionErrHandler(args.json, "Timeout", None) 2733 except(requests.exceptions.ConnectionError) as err: 2734 return connectionErrHandler(args.json, "ConnectionError", err) 2735 oldsoftware = json.loads(resp.text)['data'] 2736 2737 #Extract the tar and get information from the manifest file 2738 newversionID = extractFWimage(args.fileloc, purp) 2739 if newversionID == "": 2740 return "Unable to verify FW image." 2741 2742 2743 #check if the new image is already on the bmc 2744 if newversionID not in getAllFWImageIDs(oldsoftware): 2745 2746 #upload the file 2747 httpHeader = {'Content-Type':'application/octet-stream'} 2748 httpHeader.update(xAuthHeader) 2749 url="https://"+host+"/upload/image" 2750 data=open(args.fileloc,'rb').read() 2751 print("Uploading file to BMC") 2752 try: 2753 resp = session.post(url, headers=httpHeader, data=data, verify=False) 2754 except(requests.exceptions.Timeout): 2755 return connectionErrHandler(args.json, "Timeout", None) 2756 except(requests.exceptions.ConnectionError) as err: 2757 return connectionErrHandler(args.json, "ConnectionError", err) 2758 if resp.status_code != 200: 2759 return "Failed to upload the file to the bmc" 2760 else: 2761 print("Upload complete.") 2762 2763 #verify bmc processed the image 2764 software ={} 2765 for i in range(0, 5): 2766 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2767 try: 2768 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2769 except(requests.exceptions.Timeout): 2770 return connectionErrHandler(args.json, "Timeout", None) 2771 except(requests.exceptions.ConnectionError) as err: 2772 return connectionErrHandler(args.json, "ConnectionError", err) 2773 software = json.loads(resp.text)['data'] 2774 #check if bmc is done processing the new image 2775 if (newversionID in getAllFWImageIDs(software)): 2776 break 2777 else: 2778 time.sleep(15) 2779 2780 #activate the new image 2781 print("Activating new image: "+newversionID) 2782 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID + "/attr/RequestedActivation" 2783 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}' 2784 try: 2785 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2786 except(requests.exceptions.Timeout): 2787 return connectionErrHandler(args.json, "Timeout", None) 2788 except(requests.exceptions.ConnectionError) as err: 2789 return connectionErrHandler(args.json, "ConnectionError", err) 2790 2791 #wait for the activation to complete, timeout after ~1 hour 2792 i=0 2793 while i < 360: 2794 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID 2795 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}' 2796 try: 2797 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2798 except(requests.exceptions.Timeout): 2799 return connectionErrHandler(args.json, "Timeout", None) 2800 except(requests.exceptions.ConnectionError) as err: 2801 return connectionErrHandler(args.json, "ConnectionError", err) 2802 fwInfo = json.loads(resp.text)['data'] 2803 if 'Activating' not in fwInfo['Activation'] and 'Activating' not in fwInfo['RequestedActivation']: 2804 print('') 2805 break 2806 else: 2807 sys.stdout.write('.') 2808 sys.stdout.flush() 2809 time.sleep(10) #check every 10 seconds 2810 return "Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. " 2811 else: 2812 print("This image has been found on the bmc. Activating image: " + newversionID) 2813 2814 d['imageID'] = newversionID 2815 return activateFWImage(host, args, session) 2816 2817def getFWInventoryAttributes(rawFWInvItem, ID): 2818 """ 2819 gets and lists all of the firmware in the system. 2820 2821 @return: returns a dictionary containing the image attributes 2822 """ 2823 reqActivation = rawFWInvItem["RequestedActivation"].split('.')[-1] 2824 pendingActivation = "" 2825 if reqActivation == "None": 2826 pendingActivation = "No" 2827 else: 2828 pendingActivation = "Yes" 2829 firmwareAttr = {ID: { 2830 "Purpose": rawFWInvItem["Purpose"].split('.')[-1], 2831 "Version": rawFWInvItem["Version"], 2832 "RequestedActivation": pendingActivation, 2833 "ID": ID}} 2834 2835 if "ExtendedVersion" in rawFWInvItem: 2836 firmwareAttr[ID]['ExtendedVersion'] = rawFWInvItem['ExtendedVersion'].split(',') 2837 else: 2838 firmwareAttr[ID]['ExtendedVersion'] = "" 2839 return firmwareAttr 2840 2841def parseFWdata(firmwareDict): 2842 """ 2843 creates a dictionary with parsed firmware data 2844 2845 @return: returns a dictionary containing the image attributes 2846 """ 2847 firmwareInfoDict = {"Functional": {}, "Activated":{}, "NeedsActivated":{}} 2848 for key in firmwareDict['data']: 2849 #check for valid endpoint 2850 if "Purpose" in firmwareDict['data'][key]: 2851 id = key.split('/')[-1] 2852 if firmwareDict['data'][key]['Activation'].split('.')[-1] == "Active": 2853 fwActivated = True 2854 else: 2855 fwActivated = False 2856 if 'Priority' in firmwareDict['data'][key]: 2857 if firmwareDict['data'][key]['Priority'] == 0: 2858 firmwareInfoDict['Functional'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2859 elif firmwareDict['data'][key]['Priority'] >= 0 and fwActivated: 2860 firmwareInfoDict['Activated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2861 else: 2862 firmwareInfoDict['NeedsActivated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2863 else: 2864 firmwareInfoDict['NeedsActivated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2865 emptySections = [] 2866 for key in firmwareInfoDict: 2867 if len(firmwareInfoDict[key])<=0: 2868 emptySections.append(key) 2869 for key in emptySections: 2870 del firmwareInfoDict[key] 2871 return firmwareInfoDict 2872 2873def displayFWInvenory(firmwareInfoDict, args): 2874 """ 2875 gets and lists all of the firmware in the system. 2876 2877 @return: returns a string containing all of the firmware information 2878 """ 2879 output = "" 2880 if not args.json: 2881 for key in firmwareInfoDict: 2882 for subkey in firmwareInfoDict[key]: 2883 firmwareInfoDict[key][subkey]['ExtendedVersion'] = str(firmwareInfoDict[key][subkey]['ExtendedVersion']) 2884 if not args.verbose: 2885 output = "---Running Images---\n" 2886 colNames = ["Purpose", "Version", "ID"] 2887 keylist = ["Purpose", "Version", "ID"] 2888 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"]) 2889 if "Activated" in firmwareInfoDict: 2890 output += "\n---Available Images---\n" 2891 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"]) 2892 if "NeedsActivated" in firmwareInfoDict: 2893 output += "\n---Needs Activated Images---\n" 2894 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"]) 2895 2896 else: 2897 output = "---Running Images---\n" 2898 colNames = ["Purpose", "Version", "ID", "Pending Activation", "Extended Version"] 2899 keylist = ["Purpose", "Version", "ID", "RequestedActivation", "ExtendedVersion"] 2900 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"]) 2901 if "Activated" in firmwareInfoDict: 2902 output += "\n---Available Images---\n" 2903 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"]) 2904 if "NeedsActivated" in firmwareInfoDict: 2905 output += "\n---Needs Activated Images---\n" 2906 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"]) 2907 return output 2908 else: 2909 return str(json.dumps(firmwareInfoDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 2910 2911def firmwareList(host, args, session): 2912 """ 2913 gets and lists all of the firmware in the system. 2914 2915 @return: returns a string containing all of the firmware information 2916 """ 2917 url="https://{hostname}/xyz/openbmc_project/software/enumerate".format(hostname=host) 2918 try: 2919 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2920 except(requests.exceptions.Timeout): 2921 return(connectionErrHandler(args.json, "Timeout", None)) 2922 firmwareDict = json.loads(res.text) 2923 2924 #sort the received information 2925 firmwareInfoDict = parseFWdata(firmwareDict) 2926 2927 #display the information 2928 return displayFWInvenory(firmwareInfoDict, args) 2929 2930 2931def deleteFWVersion(host, args, session): 2932 """ 2933 deletes a firmware version on the BMC 2934 2935 @param host: string, the hostname or IP address of the BMC 2936 @param args: contains additional arguments used by the fwflash sub command 2937 @param session: the active session to use 2938 @param fwID: the unique ID of the fw version to delete 2939 """ 2940 fwID = args.versionID 2941 2942 print("Deleting version: "+fwID) 2943 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/action/Delete" 2944 data = "{\"data\": [] }" 2945 2946 try: 2947 res = session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2948 except(requests.exceptions.Timeout): 2949 return(connectionErrHandler(args.json, "Timeout", None)) 2950 if res.status_code == 200: 2951 return ('The firmware version has been deleted') 2952 else: 2953 return ('Unable to delete the specified firmware version') 2954 2955 2956def restLogging(host, args, session): 2957 """ 2958 Called by the logging function. Turns REST API logging on/off. 2959 2960 @param host: string, the hostname or IP address of the bmc 2961 @param args: contains additional arguments used by the logging sub command 2962 @param session: the active session to use 2963 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2964 """ 2965 url="https://"+host+"/xyz/openbmc_project/logging/rest_api_logs/attr/Enabled" 2966 2967 if(args.rest_logging == 'on'): 2968 data = '{"data": 1}' 2969 elif(args.rest_logging == 'off'): 2970 data = '{"data": 0}' 2971 else: 2972 return "Invalid logging rest_api command" 2973 2974 try: 2975 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2976 except(requests.exceptions.Timeout): 2977 return(connectionErrHandler(args.json, "Timeout", None)) 2978 return res.text 2979 2980 2981def remoteLogging(host, args, session): 2982 """ 2983 Called by the logging function. View config information for/disable remote logging (rsyslog). 2984 2985 @param host: string, the hostname or IP address of the bmc 2986 @param args: contains additional arguments used by the logging sub command 2987 @param session: the active session to use 2988 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2989 """ 2990 2991 url="https://"+host+"/xyz/openbmc_project/logging/config/remote" 2992 2993 try: 2994 if(args.remote_logging == 'view'): 2995 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2996 elif(args.remote_logging == 'disable'): 2997 res = session.put(url + '/attr/Port', headers=jsonHeader, json = {"data": 0}, verify=False, timeout=baseTimeout) 2998 res = session.put(url + '/attr/Address', headers=jsonHeader, json = {"data": ""}, verify=False, timeout=baseTimeout) 2999 else: 3000 return "Invalid logging remote_logging command" 3001 except(requests.exceptions.Timeout): 3002 return(connectionErrHandler(args.json, "Timeout", None)) 3003 return res.text 3004 3005 3006def remoteLoggingConfig(host, args, session): 3007 """ 3008 Called by the logging function. Configures remote logging (rsyslog). 3009 3010 @param host: string, the hostname or IP address of the bmc 3011 @param args: contains additional arguments used by the logging sub command 3012 @param session: the active session to use 3013 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 3014 """ 3015 3016 url="https://"+host+"/xyz/openbmc_project/logging/config/remote" 3017 3018 try: 3019 res = session.put(url + '/attr/Port', headers=jsonHeader, json = {"data": args.port}, verify=False, timeout=baseTimeout) 3020 res = session.put(url + '/attr/Address', headers=jsonHeader, json = {"data": args.address}, verify=False, timeout=baseTimeout) 3021 except(requests.exceptions.Timeout): 3022 return(connectionErrHandler(args.json, "Timeout", None)) 3023 return res.text 3024 3025def redfishSupportPresent(host, session): 3026 url = "https://" + host + "/redfish/v1" 3027 try: 3028 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3029 except(requests.exceptions.Timeout): 3030 return False 3031 except(requests.exceptions.ConnectionError) as err: 3032 return False 3033 if resp.status_code != 200: 3034 return False 3035 else: 3036 return True 3037 3038def certificateUpdate(host, args, session): 3039 """ 3040 Called by certificate management function. update server/client/authority certificates 3041 Example: 3042 certificate update server https -f cert.pem 3043 certificate update authority ldap -f Root-CA.pem 3044 certificate update client ldap -f cert.pem 3045 @param host: string, the hostname or IP address of the bmc 3046 @param args: contains additional arguments used by the certificate update sub command 3047 @param session: the active session to use 3048 """ 3049 httpHeader = {'Content-Type': 'application/octet-stream'} 3050 httpHeader.update(xAuthHeader) 3051 data = open(args.fileloc, 'r').read() 3052 try: 3053 if redfishSupportPresent(host, session): 3054 if(args.type.lower() == 'server' and args.service.lower() != "https"): 3055 return "Invalid service type" 3056 if(args.type.lower() == 'client' and args.service.lower() != "ldap"): 3057 return "Invalid service type" 3058 if(args.type.lower() == 'authority' and args.service.lower() != "ldap"): 3059 return "Invalid service type" 3060 url = ""; 3061 if(args.type.lower() == 'server'): 3062 url = "https://" + host + \ 3063 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates" 3064 elif(args.type.lower() == 'client'): 3065 url = "https://" + host + \ 3066 "/redfish/v1/AccountService/LDAP/Certificates" 3067 elif(args.type.lower() == 'authority'): 3068 url = "https://" + host + \ 3069 "/redfish/v1/Managers/bmc/Truststore/Certificates" 3070 else: 3071 return "Unsupported certificate type" 3072 resp = session.post(url, headers=httpHeader, data=data, 3073 verify=False) 3074 else: 3075 url = "https://" + host + "/xyz/openbmc_project/certs/" + \ 3076 args.type.lower() + "/" + args.service.lower() 3077 resp = session.put(url, headers=httpHeader, data=data, verify=False) 3078 except(requests.exceptions.Timeout): 3079 return(connectionErrHandler(args.json, "Timeout", None)) 3080 except(requests.exceptions.ConnectionError) as err: 3081 return connectionErrHandler(args.json, "ConnectionError", err) 3082 if resp.status_code != 200: 3083 print(resp.text) 3084 return "Failed to update the certificate" 3085 else: 3086 print("Update complete.") 3087 3088def certificateDelete(host, args, session): 3089 """ 3090 Called by certificate management function to delete certificate 3091 Example: 3092 certificate delete server https 3093 certificate delete authority ldap 3094 certificate delete client ldap 3095 @param host: string, the hostname or IP address of the bmc 3096 @param args: contains additional arguments used by the certificate delete sub command 3097 @param session: the active session to use 3098 """ 3099 if redfishSupportPresent(host, session): 3100 return "Not supported, please use certificate replace instead"; 3101 httpHeader = {'Content-Type': 'multipart/form-data'} 3102 httpHeader.update(xAuthHeader) 3103 url = "https://" + host + "/xyz/openbmc_project/certs/" + args.type.lower() + "/" + args.service.lower() 3104 print("Deleting certificate url=" + url) 3105 try: 3106 resp = session.delete(url, headers=httpHeader) 3107 except(requests.exceptions.Timeout): 3108 return(connectionErrHandler(args.json, "Timeout", None)) 3109 except(requests.exceptions.ConnectionError) as err: 3110 return connectionErrHandler(args.json, "ConnectionError", err) 3111 if resp.status_code != 200: 3112 print(resp.text) 3113 return "Failed to delete the certificate" 3114 else: 3115 print("Delete complete.") 3116 3117def certificateReplace(host, args, session): 3118 """ 3119 Called by certificate management function. replace server/client/ 3120 authority certificates 3121 Example: 3122 certificate replace server https -f cert.pem 3123 certificate replace authority ldap -f Root-CA.pem 3124 certificate replace client ldap -f cert.pem 3125 @param host: string, the hostname or IP address of the bmc 3126 @param args: contains additional arguments used by the certificate 3127 replace sub command 3128 @param session: the active session to use 3129 """ 3130 cert = open(args.fileloc, 'r').read() 3131 try: 3132 if redfishSupportPresent(host, session): 3133 httpHeader = {'Content-Type': 'application/json'} 3134 httpHeader.update(xAuthHeader) 3135 url = ""; 3136 if(args.type.lower() == 'server' and args.service.lower() != "https"): 3137 return "Invalid service type" 3138 if(args.type.lower() == 'client' and args.service.lower() != "ldap"): 3139 return "Invalid service type" 3140 if(args.type.lower() == 'authority' and args.service.lower() != "ldap"): 3141 return "Invalid service type" 3142 if(args.type.lower() == 'server'): 3143 url = "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1" 3144 elif(args.type.lower() == 'client'): 3145 url = "/redfish/v1/AccountService/LDAP/Certificates/1" 3146 elif(args.type.lower() == 'authority'): 3147 url = "/redfish/v1/Managers/bmc/Truststore/Certificates/1" 3148 replaceUrl = "https://" + host + \ 3149 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate" 3150 data ={"CertificateUri":{"@odata.id":url}, "CertificateType":"PEM", 3151 "CertificateString":cert} 3152 resp = session.post(replaceUrl, headers=httpHeader, json=data, verify=False) 3153 else: 3154 httpHeader = {'Content-Type': 'application/octet-stream'} 3155 httpHeader.update(xAuthHeader) 3156 url = "https://" + host + "/xyz/openbmc_project/certs/" + \ 3157 args.type.lower() + "/" + args.service.lower() 3158 resp = session.delete(url, headers=httpHeader) 3159 resp = session.put(url, headers=httpHeader, data=cert, verify=False) 3160 except(requests.exceptions.Timeout): 3161 return(connectionErrHandler(args.json, "Timeout", None)) 3162 except(requests.exceptions.ConnectionError) as err: 3163 return connectionErrHandler(args.json, "ConnectionError", err) 3164 if resp.status_code != 200: 3165 print(resp.text) 3166 return "Failed to replace the certificate" 3167 else: 3168 print("Replace complete.") 3169 return resp.text 3170 3171def certificateDisplay(host, args, session): 3172 """ 3173 Called by certificate management function. display server/client/ 3174 authority certificates 3175 Example: 3176 certificate display server 3177 certificate display authority 3178 certificate display client 3179 @param host: string, the hostname or IP address of the bmc 3180 @param args: contains additional arguments used by the certificate 3181 display sub command 3182 @param session: the active session to use 3183 """ 3184 if not redfishSupportPresent(host, session): 3185 return "Not supported"; 3186 3187 httpHeader = {'Content-Type': 'application/octet-stream'} 3188 httpHeader.update(xAuthHeader) 3189 if(args.type.lower() == 'server'): 3190 url = "https://" + host + \ 3191 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1" 3192 elif(args.type.lower() == 'client'): 3193 url = "https://" + host + \ 3194 "/redfish/v1/AccountService/LDAP/Certificates/1" 3195 elif(args.type.lower() == 'authority'): 3196 url = "https://" + host + \ 3197 "/redfish/v1/Managers/bmc/Truststore/Certificates/1" 3198 try: 3199 resp = session.get(url, headers=httpHeader, verify=False) 3200 except(requests.exceptions.Timeout): 3201 return(connectionErrHandler(args.json, "Timeout", None)) 3202 except(requests.exceptions.ConnectionError) as err: 3203 return connectionErrHandler(args.json, "ConnectionError", err) 3204 if resp.status_code != 200: 3205 print(resp.text) 3206 return "Failed to display the certificate" 3207 else: 3208 print("Display complete.") 3209 return resp.text 3210 3211def certificateList(host, args, session): 3212 """ 3213 Called by certificate management function. 3214 Example: 3215 certificate list 3216 @param host: string, the hostname or IP address of the bmc 3217 @param args: contains additional arguments used by the certificate 3218 list sub command 3219 @param session: the active session to use 3220 """ 3221 if not redfishSupportPresent(host, session): 3222 return "Not supported"; 3223 3224 httpHeader = {'Content-Type': 'application/octet-stream'} 3225 httpHeader.update(xAuthHeader) 3226 url = "https://" + host + \ 3227 "/redfish/v1/CertificateService/CertificateLocations/" 3228 try: 3229 resp = session.get(url, headers=httpHeader, verify=False) 3230 except(requests.exceptions.Timeout): 3231 return(connectionErrHandler(args.json, "Timeout", None)) 3232 except(requests.exceptions.ConnectionError) as err: 3233 return connectionErrHandler(args.json, "ConnectionError", err) 3234 if resp.status_code != 200: 3235 print(resp.text) 3236 return "Failed to list certificates" 3237 else: 3238 print("List certificates complete.") 3239 return resp.text 3240 3241def certificateGenerateCSR(host, args, session): 3242 """ 3243 Called by certificate management function. Generate CSR for server/ 3244 client certificates 3245 Example: 3246 certificate generatecsr server NJ w3.ibm.com US IBM IBM-UNIT NY EC prime256v1 cp abc.com an.com,bm.com gn sn un in 3247 certificate generatecsr client NJ w3.ibm.com US IBM IBM-UNIT NY EC prime256v1 cp abc.com an.com,bm.com gn sn un in 3248 @param host: string, the hostname or IP address of the bmc 3249 @param args: contains additional arguments used by the certificate replace sub command 3250 @param session: the active session to use 3251 """ 3252 if not redfishSupportPresent(host, session): 3253 return "Not supported"; 3254 3255 httpHeader = {'Content-Type': 'application/octet-stream'} 3256 httpHeader.update(xAuthHeader) 3257 url = ""; 3258 if(args.type.lower() == 'server'): 3259 url = "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" 3260 usage_list = ["ServerAuthentication"] 3261 elif(args.type.lower() == 'client'): 3262 url = "/redfish/v1/AccountService/LDAP/Certificates/" 3263 usage_list = ["ClientAuthentication"] 3264 elif(args.type.lower() == 'authority'): 3265 url = "/redfish/v1/Managers/bmc/Truststore/Certificates/" 3266 print("Generating CSR url=" + url) 3267 generateCSRUrl = "https://" + host + \ 3268 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR" 3269 try: 3270 alt_name_list = args.alternativeNames.split(",") 3271 data ={"CertificateCollection":{"@odata.id":url}, 3272 "CommonName":args.commonName, "City":args.city, 3273 "Country":args.country, "Organization":args.organization, 3274 "OrganizationalUnit":args.organizationUnit, "State":args.state, 3275 "KeyPairAlgorithm":args.keyPairAlgorithm, "KeyCurveId":args.keyCurveId, 3276 "AlternativeNames":alt_name_list, "ContactPerson":args.contactPerson, 3277 "Email":args.email, "GivenName":args.givenname, "Initials":args.initials, 3278 "KeyUsage":usage_list, "Surname":args.surname, 3279 "UnstructuredName":args.unstructuredname} 3280 resp = session.post(generateCSRUrl, headers=httpHeader, 3281 json=data, verify=False) 3282 except(requests.exceptions.Timeout): 3283 return(connectionErrHandler(args.json, "Timeout", None)) 3284 except(requests.exceptions.ConnectionError) as err: 3285 return connectionErrHandler(args.json, "ConnectionError", err) 3286 if resp.status_code != 200: 3287 print(resp.text) 3288 return "Failed to generate CSR" 3289 else: 3290 print("GenerateCSR complete.") 3291 return resp.text 3292 3293def enableLDAPConfig(host, args, session): 3294 """ 3295 Called by the ldap function. Configures LDAP. 3296 3297 @param host: string, the hostname or IP address of the bmc 3298 @param args: contains additional arguments used by the ldap subcommand 3299 @param session: the active session to use 3300 @param args.json: boolean, if this flag is set to true, the output will 3301 be provided in json format for programmatic consumption 3302 """ 3303 3304 if(isRedfishSupport): 3305 return enableLDAP(host, args, session) 3306 else: 3307 return enableLegacyLDAP(host, args, session) 3308 3309def enableLegacyLDAP(host, args, session): 3310 """ 3311 Called by the ldap function. Configures LDAP on Lagecy systems. 3312 3313 @param host: string, the hostname or IP address of the bmc 3314 @param args: contains additional arguments used by the ldap subcommand 3315 @param session: the active session to use 3316 @param args.json: boolean, if this flag is set to true, the output will 3317 be provided in json format for programmatic consumption 3318 """ 3319 3320 url='https://'+host+'/xyz/openbmc_project/user/ldap/action/CreateConfig' 3321 scope = { 3322 'sub' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.sub', 3323 'one' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.one', 3324 'base': 'xyz.openbmc_project.User.Ldap.Create.SearchScope.base' 3325 } 3326 3327 serverType = { 3328 'ActiveDirectory' : 'xyz.openbmc_project.User.Ldap.Create.Type.ActiveDirectory', 3329 'OpenLDAP' : 'xyz.openbmc_project.User.Ldap.Create.Type.OpenLdap' 3330 } 3331 3332 data = {"data": [args.uri, args.bindDN, args.baseDN, args.bindPassword, scope[args.scope], serverType[args.serverType]]} 3333 3334 try: 3335 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3336 except(requests.exceptions.Timeout): 3337 return(connectionErrHandler(args.json, "Timeout", None)) 3338 except(requests.exceptions.ConnectionError) as err: 3339 return connectionErrHandler(args.json, "ConnectionError", err) 3340 3341 return res.text 3342 3343def enableLDAP(host, args, session): 3344 """ 3345 Called by the ldap function. Configures LDAP for systems with latest user-manager design changes 3346 3347 @param host: string, the hostname or IP address of the bmc 3348 @param args: contains additional arguments used by the ldap subcommand 3349 @param session: the active session to use 3350 @param args.json: boolean, if this flag is set to true, the output will 3351 be provided in json format for programmatic consumption 3352 """ 3353 3354 scope = { 3355 'sub' : 'xyz.openbmc_project.User.Ldap.Config.SearchScope.sub', 3356 'one' : 'xyz.openbmc_project.User.Ldap.Config.SearchScope.one', 3357 'base': 'xyz.openbmc_project.User.Ldap.Config.SearchScope.base' 3358 } 3359 3360 serverType = { 3361 'ActiveDirectory' : 'xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory', 3362 'OpenLDAP' : 'xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap' 3363 } 3364 3365 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 3366 3367 serverTypeEnabled = getLDAPTypeEnabled(host,session) 3368 serverTypeToBeEnabled = args.serverType 3369 3370 #If the given LDAP type is already enabled, then return 3371 if (serverTypeToBeEnabled == serverTypeEnabled): 3372 return("Server type " + serverTypeToBeEnabled + " is already enabled...") 3373 3374 try: 3375 3376 # Copy the role map from the currently enabled LDAP server type 3377 # to the newly enabled server type 3378 # Disable the currently enabled LDAP server type. Unless 3379 # it is disabled, we cannot enable a new LDAP server type 3380 if (serverTypeEnabled is not None): 3381 3382 if (serverTypeToBeEnabled != serverTypeEnabled): 3383 res = syncRoleMap(host,args,session,serverTypeEnabled,serverTypeToBeEnabled) 3384 3385 data = "{\"data\": 0 }" 3386 res = session.put(url + serverTypeMap[serverTypeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3387 3388 data = {"data": args.baseDN} 3389 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBaseDN', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3390 if (res.status_code != requests.codes.ok): 3391 print("Updates to the property LDAPBaseDN failed...") 3392 return(res.text) 3393 3394 data = {"data": args.bindDN} 3395 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBindDN', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3396 if (res.status_code != requests.codes.ok): 3397 print("Updates to the property LDAPBindDN failed...") 3398 return(res.text) 3399 3400 data = {"data": args.bindPassword} 3401 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBindDNPassword', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3402 if (res.status_code != requests.codes.ok): 3403 print("Updates to the property LDAPBindDNPassword failed...") 3404 return(res.text) 3405 3406 data = {"data": scope[args.scope]} 3407 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPSearchScope', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3408 if (res.status_code != requests.codes.ok): 3409 print("Updates to the property LDAPSearchScope failed...") 3410 return(res.text) 3411 3412 data = {"data": args.uri} 3413 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPServerURI', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3414 if (res.status_code != requests.codes.ok): 3415 print("Updates to the property LDAPServerURI failed...") 3416 return(res.text) 3417 3418 data = {"data": args.groupAttrName} 3419 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/GroupNameAttribute', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3420 if (res.status_code != requests.codes.ok): 3421 print("Updates to the property GroupNameAttribute failed...") 3422 return(res.text) 3423 3424 data = {"data": args.userAttrName} 3425 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/UserNameAttribute', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3426 if (res.status_code != requests.codes.ok): 3427 print("Updates to the property UserNameAttribute failed...") 3428 return(res.text) 3429 3430 #After updating the properties, enable the new server type 3431 data = "{\"data\": 1 }" 3432 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3433 3434 except(requests.exceptions.Timeout): 3435 return(connectionErrHandler(args.json, "Timeout", None)) 3436 except(requests.exceptions.ConnectionError) as err: 3437 return connectionErrHandler(args.json, "ConnectionError", err) 3438 return res.text 3439 3440def disableLDAP(host, args, session): 3441 """ 3442 Called by the ldap function. Deletes the LDAP Configuration. 3443 3444 @param host: string, the hostname or IP address of the bmc 3445 @param args: contains additional arguments used by the ldap subcommand 3446 @param session: the active session to use 3447 @param args.json: boolean, if this flag is set to true, the output 3448 will be provided in json format for programmatic consumption 3449 """ 3450 3451 try: 3452 if (isRedfishSupport) : 3453 3454 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 3455 3456 serverTypeEnabled = getLDAPTypeEnabled(host,session) 3457 3458 if (serverTypeEnabled is not None): 3459 #To keep the role map in sync, 3460 #If the server type being disabled has role map, then 3461 # - copy the role map to the other server type(s) 3462 for serverType in serverTypeMap.keys(): 3463 if (serverType != serverTypeEnabled): 3464 res = syncRoleMap(host,args,session,serverTypeEnabled,serverType) 3465 3466 #Disable the currently enabled LDAP server type 3467 data = "{\"data\": 0 }" 3468 res = session.put(url + serverTypeMap[serverTypeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3469 3470 else: 3471 return("LDAP server has not been enabled...") 3472 3473 else : 3474 url='https://'+host+'/xyz/openbmc_project/user/ldap/config/action/delete' 3475 data = {"data": []} 3476 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3477 3478 except(requests.exceptions.Timeout): 3479 return(connectionErrHandler(args.json, "Timeout", None)) 3480 except(requests.exceptions.ConnectionError) as err: 3481 return connectionErrHandler(args.json, "ConnectionError", err) 3482 3483 return res.text 3484 3485def enableDHCP(host, args, session): 3486 3487 """ 3488 Called by the network function. Enables DHCP. 3489 3490 @param host: string, the hostname or IP address of the bmc 3491 @param args: contains additional arguments used by the ldap subcommand 3492 args.json: boolean, if this flag is set to true, the output 3493 will be provided in json format for programmatic consumption 3494 @param session: the active session to use 3495 """ 3496 3497 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3498 "/attr/DHCPEnabled" 3499 data = "{\"data\": 1 }" 3500 try: 3501 res = session.put(url, headers=jsonHeader, data=data, verify=False, 3502 timeout=baseTimeout) 3503 3504 except(requests.exceptions.Timeout): 3505 return(connectionErrHandler(args.json, "Timeout", None)) 3506 except(requests.exceptions.ConnectionError) as err: 3507 return connectionErrHandler(args.json, "ConnectionError", err) 3508 if res.status_code == 403: 3509 return "The specified Interface"+"("+args.Interface+")"+\ 3510 " doesn't exist" 3511 3512 return res.text 3513 3514 3515def disableDHCP(host, args, session): 3516 """ 3517 Called by the network function. Disables DHCP. 3518 3519 @param host: string, the hostname or IP address of the bmc 3520 @param args: contains additional arguments used by the ldap subcommand 3521 args.json: boolean, if this flag is set to true, the output 3522 will be provided in json format for programmatic consumption 3523 @param session: the active session to use 3524 """ 3525 3526 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3527 "/attr/DHCPEnabled" 3528 data = "{\"data\": 0 }" 3529 try: 3530 res = session.put(url, headers=jsonHeader, data=data, verify=False, 3531 timeout=baseTimeout) 3532 except(requests.exceptions.Timeout): 3533 return(connectionErrHandler(args.json, "Timeout", None)) 3534 except(requests.exceptions.ConnectionError) as err: 3535 return connectionErrHandler(args.json, "ConnectionError", err) 3536 if res.status_code == 403: 3537 return "The specified Interface"+"("+args.Interface+")"+\ 3538 " doesn't exist" 3539 return res.text 3540 3541 3542def getHostname(host, args, session): 3543 3544 """ 3545 Called by the network function. Prints out the Hostname. 3546 3547 @param host: string, the hostname or IP address of the bmc 3548 @param args: contains additional arguments used by the ldap subcommand 3549 args.json: boolean, if this flag is set to true, the output 3550 will be provided in json format for programmatic consumption 3551 @param session: the active session to use 3552 """ 3553 3554 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/HostName" 3555 3556 try: 3557 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3558 except(requests.exceptions.Timeout): 3559 return(connectionErrHandler(args.json, "Timeout", None)) 3560 except(requests.exceptions.ConnectionError) as err: 3561 return connectionErrHandler(args.json, "ConnectionError", err) 3562 3563 return res.text 3564 3565 3566def setHostname(host, args, session): 3567 """ 3568 Called by the network function. Sets the Hostname. 3569 3570 @param host: string, the hostname or IP address of the bmc 3571 @param args: contains additional arguments used by the ldap subcommand 3572 args.json: boolean, if this flag is set to true, the output 3573 will be provided in json format for programmatic consumption 3574 @param session: the active session to use 3575 """ 3576 3577 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/HostName" 3578 3579 data = {"data": args.HostName} 3580 3581 try: 3582 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3583 timeout=baseTimeout) 3584 except(requests.exceptions.Timeout): 3585 return(connectionErrHandler(args.json, "Timeout", None)) 3586 except(requests.exceptions.ConnectionError) as err: 3587 return connectionErrHandler(args.json, "ConnectionError", err) 3588 3589 return res.text 3590 3591 3592def getDomainName(host, args, session): 3593 3594 """ 3595 Called by the network function. Prints out the DomainName. 3596 3597 @param host: string, the hostname or IP address of the bmc 3598 @param args: contains additional arguments used by the ldap subcommand 3599 args.json: boolean, if this flag is set to true, the output 3600 will be provided in json format for programmatic consumption 3601 @param session: the active session to use 3602 """ 3603 3604 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3605 "/attr/DomainName" 3606 3607 try: 3608 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3609 except(requests.exceptions.Timeout): 3610 return(connectionErrHandler(args.json, "Timeout", None)) 3611 except(requests.exceptions.ConnectionError) as err: 3612 return connectionErrHandler(args.json, "ConnectionError", err) 3613 if res.status_code == 404: 3614 return "The DomainName is not configured on Interface"+"("+args.Interface+")" 3615 3616 return res.text 3617 3618 3619def setDomainName(host, args, session): 3620 """ 3621 Called by the network function. Sets the DomainName. 3622 3623 @param host: string, the hostname or IP address of the bmc 3624 @param args: contains additional arguments used by the ldap subcommand 3625 args.json: boolean, if this flag is set to true, the output 3626 will be provided in json format for programmatic consumption 3627 @param session: the active session to use 3628 """ 3629 3630 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3631 "/attr/DomainName" 3632 3633 data = {"data": args.DomainName.split(",")} 3634 3635 try: 3636 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3637 timeout=baseTimeout) 3638 except(requests.exceptions.Timeout): 3639 return(connectionErrHandler(args.json, "Timeout", None)) 3640 except(requests.exceptions.ConnectionError) as err: 3641 return connectionErrHandler(args.json, "ConnectionError", err) 3642 if res.status_code == 403: 3643 return "Failed to set Domain Name" 3644 3645 return res.text 3646 3647 3648def getMACAddress(host, args, session): 3649 3650 """ 3651 Called by the network function. Prints out the MACAddress. 3652 3653 @param host: string, the hostname or IP address of the bmc 3654 @param args: contains additional arguments used by the ldap subcommand 3655 args.json: boolean, if this flag is set to true, the output 3656 will be provided in json format for programmatic consumption 3657 @param session: the active session to use 3658 """ 3659 3660 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3661 "/attr/MACAddress" 3662 3663 try: 3664 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3665 except(requests.exceptions.Timeout): 3666 return(connectionErrHandler(args.json, "Timeout", None)) 3667 except(requests.exceptions.ConnectionError) as err: 3668 return connectionErrHandler(args.json, "ConnectionError", err) 3669 if res.status_code == 404: 3670 return "Failed to get MACAddress" 3671 3672 return res.text 3673 3674 3675def setMACAddress(host, args, session): 3676 """ 3677 Called by the network function. Sets the MACAddress. 3678 3679 @param host: string, the hostname or IP address of the bmc 3680 @param args: contains additional arguments used by the ldap subcommand 3681 args.json: boolean, if this flag is set to true, the output 3682 will be provided in json format for programmatic consumption 3683 @param session: the active session to use 3684 """ 3685 3686 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3687 "/attr/MACAddress" 3688 3689 data = {"data": args.MACAddress} 3690 3691 try: 3692 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3693 timeout=baseTimeout) 3694 except(requests.exceptions.Timeout): 3695 return(connectionErrHandler(args.json, "Timeout", None)) 3696 except(requests.exceptions.ConnectionError) as err: 3697 return connectionErrHandler(args.json, "ConnectionError", err) 3698 if res.status_code == 403: 3699 return "Failed to set MACAddress" 3700 3701 return res.text 3702 3703 3704def getDefaultGateway(host, args, session): 3705 3706 """ 3707 Called by the network function. Prints out the DefaultGateway. 3708 3709 @param host: string, the hostname or IP address of the bmc 3710 @param args: contains additional arguments used by the ldap subcommand 3711 args.json: boolean, if this flag is set to true, the output 3712 will be provided in json format for programmatic consumption 3713 @param session: the active session to use 3714 """ 3715 3716 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/DefaultGateway" 3717 3718 try: 3719 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3720 except(requests.exceptions.Timeout): 3721 return(connectionErrHandler(args.json, "Timeout", None)) 3722 except(requests.exceptions.ConnectionError) as err: 3723 return connectionErrHandler(args.json, "ConnectionError", err) 3724 if res.status_code == 404: 3725 return "Failed to get Default Gateway info" 3726 3727 return res.text 3728 3729 3730def setDefaultGateway(host, args, session): 3731 """ 3732 Called by the network function. Sets the DefaultGateway. 3733 3734 @param host: string, the hostname or IP address of the bmc 3735 @param args: contains additional arguments used by the ldap subcommand 3736 args.json: boolean, if this flag is set to true, the output 3737 will be provided in json format for programmatic consumption 3738 @param session: the active session to use 3739 """ 3740 3741 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/DefaultGateway" 3742 3743 data = {"data": args.DefaultGW} 3744 3745 try: 3746 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3747 timeout=baseTimeout) 3748 except(requests.exceptions.Timeout): 3749 return(connectionErrHandler(args.json, "Timeout", None)) 3750 except(requests.exceptions.ConnectionError) as err: 3751 return connectionErrHandler(args.json, "ConnectionError", err) 3752 if res.status_code == 403: 3753 return "Failed to set Default Gateway" 3754 3755 return res.text 3756 3757 3758def viewNWConfig(host, args, session): 3759 """ 3760 Called by the ldap function. Prints out network configured properties 3761 3762 @param host: string, the hostname or IP address of the bmc 3763 @param args: contains additional arguments used by the ldap subcommand 3764 args.json: boolean, if this flag is set to true, the output 3765 will be provided in json format for programmatic consumption 3766 @param session: the active session to use 3767 @return returns LDAP's configured properties. 3768 """ 3769 url = "https://"+host+"/xyz/openbmc_project/network/enumerate" 3770 try: 3771 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3772 except(requests.exceptions.Timeout): 3773 return(connectionErrHandler(args.json, "Timeout", None)) 3774 except(requests.exceptions.ConnectionError) as err: 3775 return connectionErrHandler(args.json, "ConnectionError", err) 3776 except(requests.exceptions.RequestException) as err: 3777 return connectionErrHandler(args.json, "RequestException", err) 3778 if res.status_code == 404: 3779 return "LDAP server config has not been created" 3780 return res.text 3781 3782 3783def getDNS(host, args, session): 3784 3785 """ 3786 Called by the network function. Prints out DNS servers on the interface 3787 3788 @param host: string, the hostname or IP address of the bmc 3789 @param args: contains additional arguments used by the ldap subcommand 3790 args.json: boolean, if this flag is set to true, the output 3791 will be provided in json format for programmatic consumption 3792 @param session: the active session to use 3793 """ 3794 3795 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3796 + "/attr/Nameservers" 3797 3798 try: 3799 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3800 except(requests.exceptions.Timeout): 3801 return(connectionErrHandler(args.json, "Timeout", None)) 3802 except(requests.exceptions.ConnectionError) as err: 3803 return connectionErrHandler(args.json, "ConnectionError", err) 3804 if res.status_code == 404: 3805 return "The NameServer is not configured on Interface"+"("+args.Interface+")" 3806 3807 return res.text 3808 3809 3810def setDNS(host, args, session): 3811 """ 3812 Called by the network function. Sets DNS servers on the interface. 3813 3814 @param host: string, the hostname or IP address of the bmc 3815 @param args: contains additional arguments used by the ldap subcommand 3816 args.json: boolean, if this flag is set to true, the output 3817 will be provided in json format for programmatic consumption 3818 @param session: the active session to use 3819 """ 3820 3821 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3822 + "/attr/Nameservers" 3823 3824 data = {"data": args.DNSServers.split(",")} 3825 3826 try: 3827 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3828 timeout=baseTimeout) 3829 except(requests.exceptions.Timeout): 3830 return(connectionErrHandler(args.json, "Timeout", None)) 3831 except(requests.exceptions.ConnectionError) as err: 3832 return connectionErrHandler(args.json, "ConnectionError", err) 3833 if res.status_code == 403: 3834 return "Failed to set DNS" 3835 3836 return res.text 3837 3838 3839def getNTP(host, args, session): 3840 3841 """ 3842 Called by the network function. Prints out NTP servers on the interface 3843 3844 @param host: string, the hostname or IP address of the bmc 3845 @param args: contains additional arguments used by the ldap subcommand 3846 args.json: boolean, if this flag is set to true, the output 3847 will be provided in json format for programmatic consumption 3848 @param session: the active session to use 3849 """ 3850 3851 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3852 + "/attr/NTPServers" 3853 try: 3854 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3855 except(requests.exceptions.Timeout): 3856 return(connectionErrHandler(args.json, "Timeout", None)) 3857 except(requests.exceptions.ConnectionError) as err: 3858 return connectionErrHandler(args.json, "ConnectionError", err) 3859 if res.status_code == 404: 3860 return "The NTPServer is not configured on Interface"+"("+args.Interface+")" 3861 3862 return res.text 3863 3864 3865def setNTP(host, args, session): 3866 """ 3867 Called by the network function. Sets NTP servers on the interface. 3868 3869 @param host: string, the hostname or IP address of the bmc 3870 @param args: contains additional arguments used by the ldap subcommand 3871 args.json: boolean, if this flag is set to true, the output 3872 will be provided in json format for programmatic consumption 3873 @param session: the active session to use 3874 """ 3875 3876 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3877 + "/attr/NTPServers" 3878 3879 data = {"data": args.NTPServers.split(",")} 3880 3881 try: 3882 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3883 timeout=baseTimeout) 3884 except(requests.exceptions.Timeout): 3885 return(connectionErrHandler(args.json, "Timeout", None)) 3886 except(requests.exceptions.ConnectionError) as err: 3887 return connectionErrHandler(args.json, "ConnectionError", err) 3888 if res.status_code == 403: 3889 return "Failed to set NTP" 3890 3891 return res.text 3892 3893 3894def addIP(host, args, session): 3895 """ 3896 Called by the network function. Configures IP address on given interface 3897 3898 @param host: string, the hostname or IP address of the bmc 3899 @param args: contains additional arguments used by the ldap subcommand 3900 args.json: boolean, if this flag is set to true, the output 3901 will be provided in json format for programmatic consumption 3902 @param session: the active session to use 3903 """ 3904 3905 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3906 + "/action/IP" 3907 protocol = { 3908 'ipv4': 'xyz.openbmc_project.Network.IP.Protocol.IPv4', 3909 'ipv6': 'xyz.openbmc_project.Network.IP.Protocol.IPv6' 3910 } 3911 3912 data = {"data": [protocol[args.type], args.address, int(args.prefixLength), 3913 args.gateway]} 3914 3915 try: 3916 res = session.post(url, headers=jsonHeader, json=data, verify=False, 3917 timeout=baseTimeout) 3918 except(requests.exceptions.Timeout): 3919 return(connectionErrHandler(args.json, "Timeout", None)) 3920 except(requests.exceptions.ConnectionError) as err: 3921 return connectionErrHandler(args.json, "ConnectionError", err) 3922 if res.status_code == 404: 3923 return "The specified Interface" + "(" + args.Interface + ")" +\ 3924 " doesn't exist" 3925 3926 return res.text 3927 3928 3929def getIP(host, args, session): 3930 """ 3931 Called by the network function. Prints out IP address of given interface 3932 3933 @param host: string, the hostname or IP address of the bmc 3934 @param args: contains additional arguments used by the ldap subcommand 3935 args.json: boolean, if this flag is set to true, the output 3936 will be provided in json format for programmatic consumption 3937 @param session: the active session to use 3938 """ 3939 3940 url = "https://" + host+"/xyz/openbmc_project/network/" + args.Interface +\ 3941 "/enumerate" 3942 try: 3943 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3944 except(requests.exceptions.Timeout): 3945 return(connectionErrHandler(args.json, "Timeout", None)) 3946 except(requests.exceptions.ConnectionError) as err: 3947 return connectionErrHandler(args.json, "ConnectionError", err) 3948 if res.status_code == 404: 3949 return "The specified Interface" + "(" + args.Interface + ")" +\ 3950 " doesn't exist" 3951 3952 return res.text 3953 3954 3955def deleteIP(host, args, session): 3956 """ 3957 Called by the network function. Deletes the IP address from given Interface 3958 3959 @param host: string, the hostname or IP address of the bmc 3960 @param args: contains additional arguments used by the ldap subcommand 3961 @param session: the active session to use 3962 @param args.json: boolean, if this flag is set to true, the output 3963 will be provided in json format for programmatic consumption 3964 """ 3965 3966 url = "https://"+host+"/xyz/openbmc_project/network/" + args.Interface+\ 3967 "/enumerate" 3968 data = {"data": []} 3969 try: 3970 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3971 except(requests.exceptions.Timeout): 3972 return(connectionErrHandler(args.json, "Timeout", None)) 3973 except(requests.exceptions.ConnectionError) as err: 3974 return connectionErrHandler(args.json, "ConnectionError", err) 3975 if res.status_code == 404: 3976 return "The specified Interface" + "(" + args.Interface + ")" +\ 3977 " doesn't exist" 3978 objDict = json.loads(res.text) 3979 if not objDict['data']: 3980 return "No object found for given address on given Interface" 3981 for obj in objDict['data']: 3982 try: 3983 if args.address in objDict['data'][obj]['Address']: 3984 url = "https://"+host+obj+"/action/Delete" 3985 try: 3986 res = session.post(url, headers=jsonHeader, json=data, 3987 verify=False, timeout=baseTimeout) 3988 except(requests.exceptions.Timeout): 3989 return(connectionErrHandler(args.json, "Timeout", None)) 3990 except(requests.exceptions.ConnectionError) as err: 3991 return connectionErrHandler(args.json, "ConnectionError", err) 3992 return res.text 3993 else: 3994 continue 3995 except KeyError: 3996 continue 3997 return "No object found for address " + args.address + \ 3998 " on Interface(" + args.Interface + ")" 3999 4000 4001def addVLAN(host, args, session): 4002 """ 4003 Called by the network function. Creates VLAN on given interface. 4004 4005 @param host: string, the hostname or IP address of the bmc 4006 @param args: contains additional arguments used by the ldap subcommand 4007 args.json: boolean, if this flag is set to true, the output 4008 will be provided in json format for programmatic consumption 4009 @param session: the active session to use 4010 """ 4011 4012 url = "https://" + host+"/xyz/openbmc_project/network/action/VLAN" 4013 4014 data = {"data": [args.Interface,int(args.Identifier)]} 4015 try: 4016 res = session.post(url, headers=jsonHeader, json=data, verify=False, 4017 timeout=baseTimeout) 4018 except(requests.exceptions.Timeout): 4019 return(connectionErrHandler(args.json, "Timeout", None)) 4020 except(requests.exceptions.ConnectionError) as err: 4021 return connectionErrHandler(args.json, "ConnectionError", err) 4022 if res.status_code == 400: 4023 return "Adding VLAN to interface" + "(" + args.Interface + ")" +\ 4024 " failed" 4025 4026 return res.text 4027 4028 4029def deleteVLAN(host, args, session): 4030 """ 4031 Called by the network function. Creates VLAN on given interface. 4032 4033 @param host: string, the hostname or IP address of the bmc 4034 @param args: contains additional arguments used by the ldap subcommand 4035 args.json: boolean, if this flag is set to true, the output 4036 will be provided in json format for programmatic consumption 4037 @param session: the active session to use 4038 """ 4039 4040 url = "https://" + host+"/xyz/openbmc_project/network/"+args.Interface+"/action/Delete" 4041 data = {"data": []} 4042 4043 try: 4044 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4045 except(requests.exceptions.Timeout): 4046 return(connectionErrHandler(args.json, "Timeout", None)) 4047 except(requests.exceptions.ConnectionError) as err: 4048 return connectionErrHandler(args.json, "ConnectionError", err) 4049 if res.status_code == 404: 4050 return "The specified VLAN"+"("+args.Interface+")" +" doesn't exist" 4051 4052 return res.text 4053 4054 4055def viewDHCPConfig(host, args, session): 4056 """ 4057 Called by the network function. Shows DHCP configured Properties. 4058 4059 @param host: string, the hostname or IP address of the bmc 4060 @param args: contains additional arguments used by the ldap subcommand 4061 args.json: boolean, if this flag is set to true, the output 4062 will be provided in json format for programmatic consumption 4063 @param session: the active session to use 4064 """ 4065 4066 url="https://"+host+"/xyz/openbmc_project/network/config/dhcp" 4067 4068 try: 4069 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4070 except(requests.exceptions.Timeout): 4071 return(connectionErrHandler(args.json, "Timeout", None)) 4072 except(requests.exceptions.ConnectionError) as err: 4073 return connectionErrHandler(args.json, "ConnectionError", err) 4074 4075 return res.text 4076 4077 4078def configureDHCP(host, args, session): 4079 """ 4080 Called by the network function. Configures/updates DHCP Properties. 4081 4082 @param host: string, the hostname or IP address of the bmc 4083 @param args: contains additional arguments used by the ldap subcommand 4084 args.json: boolean, if this flag is set to true, the output 4085 will be provided in json format for programmatic consumption 4086 @param session: the active session to use 4087 """ 4088 4089 4090 try: 4091 url="https://"+host+"/xyz/openbmc_project/network/config/dhcp" 4092 if(args.DNSEnabled == True): 4093 data = '{"data": 1}' 4094 else: 4095 data = '{"data": 0}' 4096 res = session.put(url + '/attr/DNSEnabled', headers=jsonHeader, 4097 data=data, verify=False, timeout=baseTimeout) 4098 if(args.HostNameEnabled == True): 4099 data = '{"data": 1}' 4100 else: 4101 data = '{"data": 0}' 4102 res = session.put(url + '/attr/HostNameEnabled', headers=jsonHeader, 4103 data=data, verify=False, timeout=baseTimeout) 4104 if(args.NTPEnabled == True): 4105 data = '{"data": 1}' 4106 else: 4107 data = '{"data": 0}' 4108 res = session.put(url + '/attr/NTPEnabled', headers=jsonHeader, 4109 data=data, verify=False, timeout=baseTimeout) 4110 if(args.SendHostNameEnabled == True): 4111 data = '{"data": 1}' 4112 else: 4113 data = '{"data": 0}' 4114 res = session.put(url + '/attr/SendHostNameEnabled', headers=jsonHeader, 4115 data=data, verify=False, timeout=baseTimeout) 4116 except(requests.exceptions.Timeout): 4117 return(connectionErrHandler(args.json, "Timeout", None)) 4118 except(requests.exceptions.ConnectionError) as err: 4119 return connectionErrHandler(args.json, "ConnectionError", err) 4120 4121 return res.text 4122 4123 4124def nwReset(host, args, session): 4125 4126 """ 4127 Called by the network function. Resets networks setting to factory defaults. 4128 4129 @param host: string, the hostname or IP address of the bmc 4130 @param args: contains additional arguments used by the ldap subcommand 4131 args.json: boolean, if this flag is set to true, the output 4132 will be provided in json format for programmatic consumption 4133 @param session: the active session to use 4134 """ 4135 4136 url = "https://"+host+"/xyz/openbmc_project/network/action/Reset" 4137 data = '{"data":[] }' 4138 try: 4139 res = session.post(url, headers=jsonHeader, data=data, verify=False, 4140 timeout=baseTimeout) 4141 4142 except(requests.exceptions.Timeout): 4143 return(connectionErrHandler(args.json, "Timeout", None)) 4144 except(requests.exceptions.ConnectionError) as err: 4145 return connectionErrHandler(args.json, "ConnectionError", err) 4146 4147 return res.text 4148 4149def getLDAPTypeEnabled(host,session): 4150 4151 """ 4152 Called by LDAP related functions to find the LDAP server type that has been enabled. 4153 Returns None if LDAP has not been configured. 4154 4155 @param host: string, the hostname or IP address of the bmc 4156 @param session: the active session to use 4157 """ 4158 4159 enabled = False 4160 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/' 4161 for key,value in serverTypeMap.items(): 4162 data = {"data": []} 4163 try: 4164 res = session.get(url + value + '/attr/Enabled', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4165 except(requests.exceptions.Timeout): 4166 print(connectionErrHandler(args.json, "Timeout", None)) 4167 return 4168 except(requests.exceptions.ConnectionError) as err: 4169 print(connectionErrHandler(args.json, "ConnectionError", err)) 4170 return 4171 4172 enabled = res.json()['data'] 4173 if (enabled): 4174 return key 4175 4176def syncRoleMap(host,args,session,fromServerType,toServerType): 4177 4178 """ 4179 Called by LDAP related functions to sync the role maps 4180 Returns False if LDAP has not been configured. 4181 4182 @param host: string, the hostname or IP address of the bmc 4183 @param session: the active session to use 4184 @param fromServerType : Server type whose role map has to be copied 4185 @param toServerType : Server type to which role map has to be copied 4186 """ 4187 4188 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 4189 4190 try: 4191 #Note: If the fromServerType has no role map, then 4192 #the toServerType will not have any role map. 4193 4194 #delete the privilege mapping from the toServerType and 4195 #then copy the privilege mapping from fromServerType to 4196 #toServerType. 4197 args.serverType = toServerType 4198 res = deleteAllPrivilegeMapping(host, args, session) 4199 4200 data = {"data": []} 4201 res = session.get(url + serverTypeMap[fromServerType] + '/role_map/enumerate', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4202 #Previously enabled server type has no role map 4203 if (res.status_code != requests.codes.ok): 4204 4205 #fromServerType has no role map; So, no need to copy 4206 #role map to toServerType. 4207 return 4208 4209 objDict = json.loads(res.text) 4210 dataDict = objDict['data'] 4211 for key,value in dataDict.items(): 4212 data = {"data": [value["GroupName"], value["Privilege"]]} 4213 res = session.post(url + serverTypeMap[toServerType] + '/action/Create', headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4214 4215 except(requests.exceptions.Timeout): 4216 return(connectionErrHandler(args.json, "Timeout", None)) 4217 except(requests.exceptions.ConnectionError) as err: 4218 return connectionErrHandler(args.json, "ConnectionError", err) 4219 return res.text 4220 4221 4222def createPrivilegeMapping(host, args, session): 4223 """ 4224 Called by the ldap function. Creates the group and the privilege mapping. 4225 4226 @param host: string, the hostname or IP address of the bmc 4227 @param args: contains additional arguments used by the ldap subcommand 4228 @param session: the active session to use 4229 @param args.json: boolean, if this flag is set to true, the output 4230 will be provided in json format for programmatic consumption 4231 """ 4232 4233 try: 4234 if (isRedfishSupport): 4235 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/' 4236 4237 #To maintain the interface compatibility between op930 and op940, the server type has been made 4238 #optional. If the server type is not specified, then create the role-mapper for the currently 4239 #enabled server type. 4240 serverType = args.serverType 4241 if (serverType is None): 4242 serverType = getLDAPTypeEnabled(host,session) 4243 if (serverType is None): 4244 return("LDAP server has not been enabled. Please specify LDAP serverType to proceed further...") 4245 4246 data = {"data": [args.groupName,args.privilege]} 4247 res = session.post(url + serverTypeMap[serverType] + '/action/Create', headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4248 4249 else: 4250 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/action/Create' 4251 data = {"data": [args.groupName,args.privilege]} 4252 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4253 4254 except(requests.exceptions.Timeout): 4255 return(connectionErrHandler(args.json, "Timeout", None)) 4256 except(requests.exceptions.ConnectionError) as err: 4257 return connectionErrHandler(args.json, "ConnectionError", err) 4258 return res.text 4259 4260def listPrivilegeMapping(host, args, session): 4261 """ 4262 Called by the ldap function. Lists the group and the privilege mapping. 4263 4264 @param host: string, the hostname or IP address of the bmc 4265 @param args: contains additional arguments used by the ldap subcommand 4266 @param session: the active session to use 4267 @param args.json: boolean, if this flag is set to true, the output 4268 will be provided in json format for programmatic consumption 4269 """ 4270 4271 if (isRedfishSupport): 4272 serverType = args.serverType 4273 if (serverType is None): 4274 serverType = getLDAPTypeEnabled(host,session) 4275 if (serverType is None): 4276 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4277 4278 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/'+serverTypeMap[serverType]+'/role_map/enumerate' 4279 4280 else: 4281 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/enumerate' 4282 4283 data = {"data": []} 4284 4285 try: 4286 res = session.get(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4287 except(requests.exceptions.Timeout): 4288 return(connectionErrHandler(args.json, "Timeout", None)) 4289 except(requests.exceptions.ConnectionError) as err: 4290 return connectionErrHandler(args.json, "ConnectionError", err) 4291 4292 return res.text 4293 4294def deletePrivilegeMapping(host, args, session): 4295 """ 4296 Called by the ldap function. Deletes the mapping associated with the group. 4297 4298 @param host: string, the hostname or IP address of the bmc 4299 @param args: contains additional arguments used by the ldap subcommand 4300 @param session: the active session to use 4301 @param args.json: boolean, if this flag is set to true, the output 4302 will be provided in json format for programmatic consumption 4303 """ 4304 4305 ldapNameSpaceObjects = listPrivilegeMapping(host, args, session) 4306 ldapNameSpaceObjects = json.loads(ldapNameSpaceObjects)["data"] 4307 path = '' 4308 data = {"data": []} 4309 4310 if (isRedfishSupport): 4311 if (args.serverType is None): 4312 serverType = getLDAPTypeEnabled(host,session) 4313 if (serverType is None): 4314 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4315 # search for the object having the mapping for the given group 4316 for key,value in ldapNameSpaceObjects.items(): 4317 if value['GroupName'] == args.groupName: 4318 path = key 4319 break 4320 4321 if path == '': 4322 return "No privilege mapping found for this group." 4323 4324 # delete the object 4325 url = 'https://'+host+path+'/action/Delete' 4326 4327 else: 4328 # not interested in the config objet 4329 ldapNameSpaceObjects.pop('/xyz/openbmc_project/user/ldap/config', None) 4330 4331 # search for the object having the mapping for the given group 4332 for key,value in ldapNameSpaceObjects.items(): 4333 if value['GroupName'] == args.groupName: 4334 path = key 4335 break 4336 4337 if path == '': 4338 return "No privilege mapping found for this group." 4339 4340 # delete the object 4341 url = 'https://'+host+path+'/action/delete' 4342 4343 try: 4344 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4345 except(requests.exceptions.Timeout): 4346 return(connectionErrHandler(args.json, "Timeout", None)) 4347 except(requests.exceptions.ConnectionError) as err: 4348 return connectionErrHandler(args.json, "ConnectionError", err) 4349 return res.text 4350 4351def deleteAllPrivilegeMapping(host, args, session): 4352 """ 4353 Called by the ldap function. Deletes all the privilege mapping and group defined. 4354 @param host: string, the hostname or IP address of the bmc 4355 @param args: contains additional arguments used by the ldap subcommand 4356 @param session: the active session to use 4357 @param args.json: boolean, if this flag is set to true, the output 4358 will be provided in json format for programmatic consumption 4359 """ 4360 4361 ldapNameSpaceObjects = listPrivilegeMapping(host, args, session) 4362 ldapNameSpaceObjects = json.loads(ldapNameSpaceObjects)["data"] 4363 path = '' 4364 data = {"data": []} 4365 4366 if (isRedfishSupport): 4367 if (args.serverType is None): 4368 serverType = getLDAPTypeEnabled(host,session) 4369 if (serverType is None): 4370 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4371 4372 else: 4373 # Remove the config object. 4374 ldapNameSpaceObjects.pop('/xyz/openbmc_project/user/ldap/config', None) 4375 4376 try: 4377 # search for GroupName property and delete if it is available. 4378 for path in ldapNameSpaceObjects.keys(): 4379 # delete the object 4380 url = 'https://'+host+path+'/action/Delete' 4381 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4382 4383 except(requests.exceptions.Timeout): 4384 return(connectionErrHandler(args.json, "Timeout", None)) 4385 except(requests.exceptions.ConnectionError) as err: 4386 return connectionErrHandler(args.json, "ConnectionError", err) 4387 return res.text 4388 4389def viewLDAPConfig(host, args, session): 4390 """ 4391 Called by the ldap function. Prints out active LDAP configuration properties 4392 4393 @param host: string, the hostname or IP address of the bmc 4394 @param args: contains additional arguments used by the ldap subcommand 4395 args.json: boolean, if this flag is set to true, the output 4396 will be provided in json format for programmatic consumption 4397 @param session: the active session to use 4398 @return returns LDAP's configured properties. 4399 """ 4400 4401 try: 4402 if (isRedfishSupport): 4403 4404 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 4405 4406 serverTypeEnabled = getLDAPTypeEnabled(host,session) 4407 4408 if (serverTypeEnabled is not None): 4409 data = {"data": []} 4410 res = session.get(url + serverTypeMap[serverTypeEnabled], headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4411 else: 4412 return("LDAP server has not been enabled...") 4413 4414 else : 4415 url = "https://"+host+"/xyz/openbmc_project/user/ldap/config" 4416 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4417 4418 except(requests.exceptions.Timeout): 4419 return(connectionErrHandler(args.json, "Timeout", None)) 4420 except(requests.exceptions.ConnectionError) as err: 4421 return connectionErrHandler(args.json, "ConnectionError", err) 4422 if res.status_code == 404: 4423 return "LDAP server config has not been created" 4424 return res.text 4425 4426def str2bool(v): 4427 if v.lower() in ('yes', 'true', 't', 'y', '1'): 4428 return True 4429 elif v.lower() in ('no', 'false', 'f', 'n', '0'): 4430 return False 4431 else: 4432 raise argparse.ArgumentTypeError('Boolean value expected.') 4433 4434def localUsers(host, args, session): 4435 """ 4436 Enables and disables local BMC users. 4437 4438 @param host: string, the hostname or IP address of the bmc 4439 @param args: contains additional arguments used by the logging sub command 4440 @param session: the active session to use 4441 """ 4442 4443 url="https://{hostname}/xyz/openbmc_project/user/enumerate".format(hostname=host) 4444 try: 4445 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4446 except(requests.exceptions.Timeout): 4447 return(connectionErrHandler(args.json, "Timeout", None)) 4448 usersDict = json.loads(res.text) 4449 4450 if not usersDict['data']: 4451 return "No users found" 4452 4453 output = "" 4454 for user in usersDict['data']: 4455 4456 # Skip LDAP and another non-local users 4457 if 'UserEnabled' not in usersDict['data'][user]: 4458 continue 4459 4460 name = user.split('/')[-1] 4461 url = "https://{hostname}{user}/attr/UserEnabled".format(hostname=host, user=user) 4462 4463 if args.local_users == "queryenabled": 4464 try: 4465 res = session.get(url, headers=jsonHeader,verify=False, timeout=baseTimeout) 4466 except(requests.exceptions.Timeout): 4467 return(connectionErrHandler(args.json, "Timeout", None)) 4468 4469 result = json.loads(res.text) 4470 output += ("User: {name} Enabled: {result}\n").format(name=name, result=result['data']) 4471 4472 elif args.local_users in ["enableall", "disableall"]: 4473 action = "" 4474 if args.local_users == "enableall": 4475 data = '{"data": true}' 4476 action = "Enabling" 4477 else: 4478 data = '{"data": false}' 4479 action = "Disabling" 4480 4481 output += "{action} {name}\n".format(action=action, name=name) 4482 4483 try: 4484 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 4485 except(requests.exceptions.Timeout): 4486 return connectionErrHandler(args.json, "Timeout", None) 4487 except(requests.exceptions.ConnectionError) as err: 4488 return connectionErrHandler(args.json, "ConnectionError", err) 4489 else: 4490 return "Invalid local users argument" 4491 4492 return output 4493 4494def setPassword(host, args, session): 4495 """ 4496 Set local user password 4497 @param host: string, the hostname or IP address of the bmc 4498 @param args: contains additional arguments used by the logging sub 4499 command 4500 @param session: the active session to use 4501 @param args.json: boolean, if this flag is set to true, the output 4502 will be provided in json format for programmatic consumption 4503 @return: Session object 4504 """ 4505 try: 4506 if(isRedfishSupport): 4507 url = "https://" + host + "/redfish/v1/AccountService/Accounts/"+ \ 4508 args.user 4509 data = {"Password":args.password} 4510 res = session.patch(url, headers=jsonHeader, json=data, 4511 verify=False, timeout=baseTimeout) 4512 else: 4513 url = "https://" + host + "/xyz/openbmc_project/user/" + args.user + \ 4514 "/action/SetPassword" 4515 res = session.post(url, headers=jsonHeader, 4516 json={"data": [args.password]}, verify=False, 4517 timeout=baseTimeout) 4518 except(requests.exceptions.Timeout): 4519 return(connectionErrHandler(args.json, "Timeout", None)) 4520 except(requests.exceptions.ConnectionError) as err: 4521 return connectionErrHandler(args.json, "ConnectionError", err) 4522 except(requests.exceptions.RequestException) as err: 4523 return connectionErrHandler(args.json, "RequestException", err) 4524 return res.status_code 4525 4526def getThermalZones(host, args, session): 4527 """ 4528 Get the available thermal control zones 4529 @param host: string, the hostname or IP address of the bmc 4530 @param args: contains additional arguments used to get the thermal 4531 control zones 4532 @param session: the active session to use 4533 @return: Session object 4534 """ 4535 url = "https://" + host + "/xyz/openbmc_project/control/thermal/enumerate" 4536 4537 try: 4538 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4539 except(requests.exceptions.Timeout): 4540 return(connectionErrHandler(args.json, "Timeout", None)) 4541 except(requests.exceptions.ConnectionError) as err: 4542 return connectionErrHandler(args.json, "ConnectionError", err) 4543 except(requests.exceptions.RequestException) as err: 4544 return connectionErrHandler(args.json, "RequestException", err) 4545 4546 if (res.status_code == 404): 4547 return "No thermal control zones found" 4548 4549 zonesDict = json.loads(res.text) 4550 if not zonesDict['data']: 4551 return "No thermal control zones found" 4552 for zone in zonesDict['data']: 4553 z = ",".join(str(zone.split('/')[-1]) for zone in zonesDict['data']) 4554 4555 return "Zones: [ " + z + " ]" 4556 4557 4558def getThermalMode(host, args, session): 4559 """ 4560 Get thermal control mode 4561 @param host: string, the hostname or IP address of the bmc 4562 @param args: contains additional arguments used to get the thermal 4563 control mode 4564 @param session: the active session to use 4565 @param args.zone: the zone to get the mode on 4566 @return: Session object 4567 """ 4568 url = "https://" + host + "/xyz/openbmc_project/control/thermal/" + \ 4569 args.zone 4570 4571 try: 4572 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4573 except(requests.exceptions.Timeout): 4574 return(connectionErrHandler(args.json, "Timeout", None)) 4575 except(requests.exceptions.ConnectionError) as err: 4576 return connectionErrHandler(args.json, "ConnectionError", err) 4577 except(requests.exceptions.RequestException) as err: 4578 return connectionErrHandler(args.json, "RequestException", err) 4579 4580 if (res.status_code == 404): 4581 return "Thermal control zone(" + args.zone + ") not found" 4582 4583 propsDict = json.loads(res.text) 4584 if not propsDict['data']: 4585 return "No thermal control properties found on zone(" + args.zone + ")" 4586 curMode = "Current" 4587 supModes = "Supported" 4588 result = "\n" 4589 for prop in propsDict['data']: 4590 if (prop.casefold() == curMode.casefold()): 4591 result += curMode + " Mode: " + propsDict['data'][curMode] + "\n" 4592 if (prop.casefold() == supModes.casefold()): 4593 s = ", ".join(str(sup) for sup in propsDict['data'][supModes]) 4594 result += supModes + " Modes: [ " + s + " ]\n" 4595 4596 return result 4597 4598def setThermalMode(host, args, session): 4599 """ 4600 Set thermal control mode 4601 @param host: string, the hostname or IP address of the bmc 4602 @param args: contains additional arguments used for setting the thermal 4603 control mode 4604 @param session: the active session to use 4605 @param args.zone: the zone to set the mode on 4606 @param args.mode: the mode to enable 4607 @return: Session object 4608 """ 4609 url = "https://" + host + "/xyz/openbmc_project/control/thermal/" + \ 4610 args.zone + "/attr/Current" 4611 4612 # Check args.mode against supported modes using `getThermalMode` output 4613 modes = getThermalMode(host, args, session) 4614 modes = os.linesep.join([m for m in modes.splitlines() if m]) 4615 modes = modes.replace("\n", ";").strip() 4616 modesDict = dict(m.split(': ') for m in modes.split(';')) 4617 sModes = ''.join(s for s in modesDict['Supported Modes'] if s not in '[ ]') 4618 if args.mode.casefold() not in \ 4619 (m.casefold() for m in sModes.split(',')) or not args.mode: 4620 result = ("Unsupported mode('" + args.mode + "') given, " + 4621 "select a supported mode: \n" + 4622 getThermalMode(host, args, session)) 4623 return result 4624 4625 data = '{"data":"' + args.mode + '"}' 4626 try: 4627 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4628 except(requests.exceptions.Timeout): 4629 return(connectionErrHandler(args.json, "Timeout", None)) 4630 except(requests.exceptions.ConnectionError) as err: 4631 return connectionErrHandler(args.json, "ConnectionError", err) 4632 except(requests.exceptions.RequestException) as err: 4633 return connectionErrHandler(args.json, "RequestException", err) 4634 4635 if (data and res.status_code != 404): 4636 try: 4637 res = session.put(url, headers=jsonHeader, 4638 data=data, verify=False, 4639 timeout=30) 4640 except(requests.exceptions.Timeout): 4641 return(connectionErrHandler(args.json, "Timeout", None)) 4642 except(requests.exceptions.ConnectionError) as err: 4643 return connectionErrHandler(args.json, "ConnectionError", err) 4644 except(requests.exceptions.RequestException) as err: 4645 return connectionErrHandler(args.json, "RequestException", err) 4646 4647 if res.status_code == 403: 4648 return "The specified thermal control zone(" + args.zone + ")" + \ 4649 " does not exist" 4650 4651 return res.text 4652 else: 4653 return "Setting thermal control mode(" + args.mode + ")" + \ 4654 " not supported or operation not available" 4655 4656 4657def createCommandParser(): 4658 """ 4659 creates the parser for the command line along with help for each command and subcommand 4660 4661 @return: returns the parser for the command line 4662 """ 4663 parser = argparse.ArgumentParser(description='Process arguments') 4664 parser.add_argument("-H", "--host", help='A hostname or IP for the BMC') 4665 parser.add_argument("-U", "--user", help='The username to login with') 4666 group = parser.add_mutually_exclusive_group() 4667 group.add_argument("-A", "--askpw", action='store_true', help='prompt for password') 4668 group.add_argument("-P", "--PW", help='Provide the password in-line') 4669 group.add_argument("-E", "--PWenvvar", action='store_true', help='Get password from envvar OPENBMCTOOL_PASSWORD') 4670 parser.add_argument('-j', '--json', action='store_true', help='output json data only') 4671 parser.add_argument('-t', '--policyTableLoc', help='The location of the policy table to parse alerts') 4672 parser.add_argument('-c', '--CerFormat', action='store_true', help=argparse.SUPPRESS) 4673 parser.add_argument('-T', '--procTime', action='store_true', help= argparse.SUPPRESS) 4674 parser.add_argument('-V', '--version', action='store_true', help='Display the version number of the openbmctool') 4675 subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4676 4677 #fru command 4678 parser_inv = subparsers.add_parser("fru", help='Work with platform inventory') 4679 inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions", dest='command') 4680 inv_subparser.required = True 4681 #fru print 4682 inv_print = inv_subparser.add_parser("print", help="prints out a list of all FRUs") 4683 inv_print.set_defaults(func=fruPrint) 4684 #fru list [0....n] 4685 inv_list = inv_subparser.add_parser("list", help="print out details on selected FRUs. Specifying no items will list the entire inventory") 4686 inv_list.add_argument('items', nargs='?', help="print out details on selected FRUs. Specifying no items will list the entire inventory") 4687 inv_list.set_defaults(func=fruList) 4688 #fru status 4689 inv_status = inv_subparser.add_parser("status", help="prints out the status of all FRUs") 4690 inv_status.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4691 inv_status.set_defaults(func=fruStatus) 4692 4693 #sensors command 4694 parser_sens = subparsers.add_parser("sensors", help="Work with platform sensors") 4695 sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions', dest='command') 4696 sens_subparser.required = True 4697 #sensor print 4698 sens_print= sens_subparser.add_parser('print', help="prints out a list of all Sensors.") 4699 sens_print.set_defaults(func=sensor) 4700 #sensor list[0...n] 4701 sens_list=sens_subparser.add_parser("list", help="Lists all Sensors in the platform. Specify a sensor for full details. ") 4702 sens_list.add_argument("sensNum", nargs='?', help="The Sensor number to get full details on" ) 4703 sens_list.set_defaults(func=sensor) 4704 4705 #thermal control commands 4706 parser_therm = subparsers.add_parser("thermal", help="Work with thermal control parameters") 4707 therm_subparser=parser_therm.add_subparsers(title='subcommands', description='Thermal control actions to work with', help='Valid thermal control actions to work with', dest='command') 4708 #thermal control zones 4709 parser_thermZones = therm_subparser.add_parser("zones", help="Get a list of available thermal control zones") 4710 parser_thermZones.set_defaults(func=getThermalZones) 4711 #thermal control modes 4712 parser_thermMode = therm_subparser.add_parser("modes", help="Work with thermal control modes") 4713 thermMode_sub = parser_thermMode.add_subparsers(title='subactions', description='Work with thermal control modes', help="Work with thermal control modes") 4714 #get thermal control mode 4715 parser_getThermMode = thermMode_sub.add_parser("get", help="Get current and supported thermal control modes") 4716 parser_getThermMode.add_argument('-z', '--zone', required=True, help='Thermal zone to work with') 4717 parser_getThermMode.set_defaults(func=getThermalMode) 4718 #set thermal control mode 4719 parser_setThermMode = thermMode_sub.add_parser("set", help="Set the thermal control mode") 4720 parser_setThermMode.add_argument('-z', '--zone', required=True, help='Thermal zone to work with') 4721 parser_setThermMode.add_argument('-m', '--mode', required=True, help='The supported thermal control mode') 4722 parser_setThermMode.set_defaults(func=setThermalMode) 4723 4724 #sel command 4725 parser_sel = subparsers.add_parser("sel", help="Work with platform alerts") 4726 sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions', dest='command') 4727 sel_subparser.required = True 4728 #sel print 4729 sel_print = sel_subparser.add_parser("print", help="prints out a list of all sels in a condensed list") 4730 sel_print.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS) 4731 sel_print.add_argument('-v', '--verbose', action='store_true', help="Changes the output to being very verbose") 4732 sel_print.add_argument('-f', '--fileloc', help='Parse a file instead of the BMC output') 4733 sel_print.set_defaults(func=selPrint) 4734 4735 #sel list 4736 sel_list = sel_subparser.add_parser("list", help="Lists all SELs in the platform. Specifying a specific number will pull all the details for that individual SEL") 4737 sel_list.add_argument("selNum", nargs='?', type=int, help="The SEL entry to get details on") 4738 sel_list.set_defaults(func=selList) 4739 4740 sel_get = sel_subparser.add_parser("get", help="Gets the verbose details of a specified SEL entry") 4741 sel_get.add_argument('selNum', type=int, help="the number of the SEL entry to get") 4742 sel_get.set_defaults(func=selList) 4743 4744 sel_clear = sel_subparser.add_parser("clear", help="Clears all entries from the SEL") 4745 sel_clear.set_defaults(func=selClear) 4746 4747 sel_setResolved = sel_subparser.add_parser("resolve", help="Sets the sel entry to resolved") 4748 sel_setResolved.add_argument('-n', '--selNum', type=int, help="the number of the SEL entry to resolve") 4749 sel_ResolveAll_sub = sel_setResolved.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4750 sel_ResolveAll = sel_ResolveAll_sub.add_parser('all', help='Resolve all SEL entries') 4751 sel_ResolveAll.set_defaults(func=selResolveAll) 4752 sel_setResolved.set_defaults(func=selSetResolved) 4753 4754 parser_chassis = subparsers.add_parser("chassis", help="Work with chassis power and status") 4755 chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4756 4757 parser_chassis.add_argument('status', action='store_true', help='Returns the current status of the platform') 4758 parser_chassis.set_defaults(func=chassis) 4759 4760 parser_chasPower = chas_sub.add_parser("power", help="Turn the chassis on or off, check the power state") 4761 parser_chasPower.add_argument('powcmd', choices=['on','softoff', 'hardoff', 'status'], help='The value for the power command. on, off, or status') 4762 parser_chasPower.set_defaults(func=chassisPower) 4763 4764 #control the chassis identify led 4765 parser_chasIdent = chas_sub.add_parser("identify", help="Control the chassis identify led") 4766 parser_chasIdent.add_argument('identcmd', choices=['on', 'off', 'status'], help='The control option for the led: on, off, blink, status') 4767 parser_chasIdent.set_defaults(func=chassisIdent) 4768 4769 #collect service data 4770 parser_servData = subparsers.add_parser("collect_service_data", help="Collect all bmc data needed for service") 4771 parser_servData.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS) 4772 parser_servData.set_defaults(func=collectServiceData) 4773 4774 #system quick health check 4775 parser_healthChk = subparsers.add_parser("health_check", help="Work with platform sensors") 4776 parser_healthChk.set_defaults(func=healthCheck) 4777 4778 #work with dumps 4779 parser_bmcdump = subparsers.add_parser("dump", help="Work with dumps") 4780 parser_bmcdump.add_argument("-t", "--dumpType", default='bmc', choices=['bmc','SystemDump'],help="Type of dump") 4781 bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4782 bmcDump_sub.required = True 4783 dump_Create = bmcDump_sub.add_parser('create', help="Create a dump of given type") 4784 dump_Create.set_defaults(func=dumpCreate) 4785 4786 dump_list = bmcDump_sub.add_parser('list', help="list all dumps") 4787 dump_list.set_defaults(func=dumpList) 4788 4789 parserdumpdelete = bmcDump_sub.add_parser('delete', help="Delete dump") 4790 parserdumpdelete.add_argument("-n", "--dumpNum", nargs='*', type=int, help="The Dump entry to delete") 4791 parserdumpdelete.set_defaults(func=dumpDelete) 4792 4793 bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4794 deleteAllDumps = bmcDumpDelsub.add_parser('all', help='Delete all dumps') 4795 deleteAllDumps.set_defaults(func=dumpDeleteAll) 4796 4797 parser_dumpretrieve = bmcDump_sub.add_parser('retrieve', help='Retrieve a dump file') 4798 parser_dumpretrieve.add_argument("-n,", "--dumpNum", help="The Dump entry to retrieve") 4799 parser_dumpretrieve.add_argument("-s", "--dumpSaveLoc", help="The location to save the bmc dump file or file path for system dump") 4800 parser_dumpretrieve.set_defaults(func=dumpRetrieve) 4801 4802 #bmc command for reseting the bmc 4803 parser_bmc = subparsers.add_parser('bmc', help="Work with the bmc") 4804 bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4805 parser_BMCReset = bmc_sub.add_parser('reset', help='Reset the bmc' ) 4806 parser_BMCReset.add_argument('type', choices=['warm','cold'], help="Warm: Reboot the BMC, Cold: CLEAR config and reboot bmc") 4807 parser_bmc.add_argument('info', action='store_true', help="Displays information about the BMC hardware, including device revision, firmware revision, IPMI version supported, manufacturer ID, and information on additional device support.") 4808 parser_bmc.set_defaults(func=bmc) 4809 4810 #add alias to the bmc command 4811 parser_mc = subparsers.add_parser('mc', help="Work with the management controller") 4812 mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4813 parser_MCReset = mc_sub.add_parser('reset', help='Reset the bmc' ) 4814 parser_MCReset.add_argument('type', choices=['warm','cold'], help="Reboot the BMC") 4815 #parser_MCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration") 4816 parser_mc.add_argument('info', action='store_true', help="Displays information about the BMC hardware, including device revision, firmware revision, IPMI version supported, manufacturer ID, and information on additional device support.") 4817 parser_MCReset.set_defaults(func=bmcReset) 4818 parser_mc.set_defaults(func=bmc) 4819 4820 #gard clear 4821 parser_gc = subparsers.add_parser("gardclear", help="Used to clear gard records") 4822 parser_gc.set_defaults(func=gardClear) 4823 4824 #firmware_flash 4825 parser_fw = subparsers.add_parser("firmware", help="Work with the system firmware") 4826 fwflash_subproc = parser_fw.add_subparsers(title='subcommands', description='valid firmware commands', help='sub-command help', dest='command') 4827 fwflash_subproc.required = True 4828 4829 fwflash = fwflash_subproc.add_parser('flash', help="Flash the system firmware") 4830 fwflash.add_argument('type', choices=['bmc', 'pnor'], help="image type to flash") 4831 fwflash.add_argument('-f', '--fileloc', required=True, help="The absolute path to the firmware image") 4832 fwflash.set_defaults(func=fwFlash) 4833 4834 fwActivate = fwflash_subproc.add_parser('activate', help="Activate existing image on the bmc") 4835 fwActivate.add_argument('imageID', help="The image ID to activate from the firmware list. Ex: 63c95399") 4836 fwActivate.set_defaults(func=activateFWImage) 4837 4838 fwActivateStatus = fwflash_subproc.add_parser('activation_status', help="Check Status of activations") 4839 fwActivateStatus.set_defaults(func=activateStatus) 4840 4841 fwList = fwflash_subproc.add_parser('list', help="List all of the installed firmware") 4842 fwList.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4843 fwList.set_defaults(func=firmwareList) 4844 4845 fwprint = fwflash_subproc.add_parser('print', help="List all of the installed firmware") 4846 fwprint.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4847 fwprint.set_defaults(func=firmwareList) 4848 4849 fwDelete = fwflash_subproc.add_parser('delete', help="Delete an existing firmware version") 4850 fwDelete.add_argument('versionID', help="The version ID to delete from the firmware list. Ex: 63c95399") 4851 fwDelete.set_defaults(func=deleteFWVersion) 4852 4853 #logging 4854 parser_logging = subparsers.add_parser("logging", help="logging controls") 4855 logging_sub = parser_logging.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4856 4857 #turn rest api logging on/off 4858 parser_rest_logging = logging_sub.add_parser("rest_api", help="turn rest api logging on/off") 4859 parser_rest_logging.add_argument('rest_logging', choices=['on', 'off'], help='The control option for rest logging: on, off') 4860 parser_rest_logging.set_defaults(func=restLogging) 4861 4862 #remote logging 4863 parser_remote_logging = logging_sub.add_parser("remote_logging", help="Remote logging (rsyslog) commands") 4864 parser_remote_logging.add_argument('remote_logging', choices=['view', 'disable'], help='Remote logging (rsyslog) commands') 4865 parser_remote_logging.set_defaults(func=remoteLogging) 4866 4867 #configure remote logging 4868 parser_remote_logging_config = logging_sub.add_parser("remote_logging_config", help="Configure remote logging (rsyslog)") 4869 parser_remote_logging_config.add_argument("-a", "--address", required=True, help="Set IP address of rsyslog server") 4870 parser_remote_logging_config.add_argument("-p", "--port", required=True, type=int, help="Set Port of rsyslog server") 4871 parser_remote_logging_config.set_defaults(func=remoteLoggingConfig) 4872 4873 #certificate management 4874 parser_cert = subparsers.add_parser("certificate", help="Certificate management") 4875 certMgmt_subproc = parser_cert.add_subparsers(title='subcommands', description='valid certificate commands', help='sub-command help', dest='command') 4876 4877 certUpdate = certMgmt_subproc.add_parser('update', help="Update the certificate") 4878 certUpdate.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to update") 4879 certUpdate.add_argument('service', choices=['https', 'ldap'], help="Service to update") 4880 certUpdate.add_argument('-f', '--fileloc', required=True, help="The absolute path to the certificate file") 4881 certUpdate.set_defaults(func=certificateUpdate) 4882 4883 certDelete = certMgmt_subproc.add_parser('delete', help="Delete the certificate") 4884 certDelete.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to delete") 4885 certDelete.add_argument('service', choices=['https', 'ldap'], help="Service to delete the certificate") 4886 certDelete.set_defaults(func=certificateDelete) 4887 4888 certReplace = certMgmt_subproc.add_parser('replace', 4889 help="Replace the certificate") 4890 certReplace.add_argument('type', choices=['server', 'client', 'authority'], 4891 help="certificate type to replace") 4892 certReplace.add_argument('service', choices=['https', 'ldap'], 4893 help="Service to replace the certificate") 4894 certReplace.add_argument('-f', '--fileloc', required=True, 4895 help="The absolute path to the certificate file") 4896 certReplace.set_defaults(func=certificateReplace) 4897 4898 certDisplay = certMgmt_subproc.add_parser('display', 4899 help="Print the certificate") 4900 certDisplay.add_argument('type', choices=['server', 'client', 'authority'], 4901 help="certificate type to display") 4902 certDisplay.set_defaults(func=certificateDisplay) 4903 4904 certList = certMgmt_subproc.add_parser('list', 4905 help="Certificate list") 4906 certList.set_defaults(func=certificateList) 4907 4908 certGenerateCSR = certMgmt_subproc.add_parser('generatecsr', help="Generate CSR") 4909 certGenerateCSR.add_argument('type', choices=['server', 'client', 'authority'], 4910 help="Generate CSR") 4911 certGenerateCSR.add_argument('city', 4912 help="The city or locality of the organization making the request") 4913 certGenerateCSR.add_argument('commonName', 4914 help="The fully qualified domain name of the component that is being secured.") 4915 certGenerateCSR.add_argument('country', 4916 help="The country of the organization making the request") 4917 certGenerateCSR.add_argument('organization', 4918 help="The name of the organization making the request.") 4919 certGenerateCSR.add_argument('organizationUnit', 4920 help="The name of the unit or division of the organization making the request.") 4921 certGenerateCSR.add_argument('state', 4922 help="The state, province, or region of the organization making the request.") 4923 certGenerateCSR.add_argument('keyPairAlgorithm', choices=['RSA', 'EC'], 4924 help="The type of key pair for use with signing algorithms.") 4925 certGenerateCSR.add_argument('keyCurveId', 4926 help="The curve ID to be used with the key, if needed based on the value of the 'KeyPairAlgorithm' parameter.") 4927 certGenerateCSR.add_argument('contactPerson', 4928 help="The name of the user making the request") 4929 certGenerateCSR.add_argument('email', 4930 help="The email address of the contact within the organization") 4931 certGenerateCSR.add_argument('alternativeNames', 4932 help="Additional hostnames of the component that is being secured") 4933 certGenerateCSR.add_argument('givenname', 4934 help="The given name of the user making the request") 4935 certGenerateCSR.add_argument('surname', 4936 help="The surname of the user making the request") 4937 certGenerateCSR.add_argument('unstructuredname', 4938 help="he unstructured name of the subject") 4939 certGenerateCSR.add_argument('initials', 4940 help="The initials of the user making the request") 4941 certGenerateCSR.set_defaults(func=certificateGenerateCSR) 4942 4943 # local users 4944 parser_users = subparsers.add_parser("local_users", help="Work with local users") 4945 parser_users.add_argument('local_users', choices=['disableall','enableall', 'queryenabled'], help="Disable, enable or query local user accounts") 4946 parser_users.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4947 parser_users.set_defaults(func=localUsers) 4948 4949 #LDAP 4950 parser_ldap = subparsers.add_parser("ldap", help="LDAP controls") 4951 ldap_sub = parser_ldap.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4952 4953 #configure and enable LDAP 4954 parser_ldap_config = ldap_sub.add_parser("enable", help="Configure and enables the LDAP") 4955 parser_ldap_config.add_argument("-a", "--uri", required=True, help="Set LDAP server URI") 4956 parser_ldap_config.add_argument("-B", "--bindDN", required=True, help="Set the bind DN of the LDAP server") 4957 parser_ldap_config.add_argument("-b", "--baseDN", required=True, help="Set the base DN of the LDAP server") 4958 parser_ldap_config.add_argument("-p", "--bindPassword", required=True, help="Set the bind password of the LDAP server") 4959 parser_ldap_config.add_argument("-S", "--scope", choices=['sub','one', 'base'], 4960 help='Specifies the search scope:subtree, one level or base object.') 4961 parser_ldap_config.add_argument("-t", "--serverType", required=True, choices=['ActiveDirectory','OpenLDAP'], 4962 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 4963 parser_ldap_config.add_argument("-g","--groupAttrName", required=False, default='', help="Group Attribute Name") 4964 parser_ldap_config.add_argument("-u","--userAttrName", required=False, default='', help="User Attribute Name") 4965 parser_ldap_config.set_defaults(func=enableLDAPConfig) 4966 4967 # disable LDAP 4968 parser_disable_ldap = ldap_sub.add_parser("disable", help="disables the LDAP") 4969 parser_disable_ldap.set_defaults(func=disableLDAP) 4970 # view-config 4971 parser_ldap_config = \ 4972 ldap_sub.add_parser("view-config", help="prints out a list of all \ 4973 LDAPS's configured properties") 4974 parser_ldap_config.set_defaults(func=viewLDAPConfig) 4975 4976 #create group privilege mapping 4977 parser_ldap_mapper = ldap_sub.add_parser("privilege-mapper", help="LDAP group privilege controls") 4978 parser_ldap_mapper_sub = parser_ldap_mapper.add_subparsers(title='subcommands', description='valid subcommands', 4979 help="sub-command help", dest='command') 4980 4981 parser_ldap_mapper_create = parser_ldap_mapper_sub.add_parser("create", help="Create mapping of ldap group and privilege") 4982 parser_ldap_mapper_create.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 4983 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 4984 parser_ldap_mapper_create.add_argument("-g","--groupName",required=True,help="Group Name") 4985 parser_ldap_mapper_create.add_argument("-p","--privilege",choices=['priv-admin','priv-operator','priv-user','priv-callback'],required=True,help="Privilege") 4986 parser_ldap_mapper_create.set_defaults(func=createPrivilegeMapping) 4987 4988 #list group privilege mapping 4989 parser_ldap_mapper_list = parser_ldap_mapper_sub.add_parser("list",help="List privilege mapping") 4990 parser_ldap_mapper_list.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 4991 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 4992 parser_ldap_mapper_list.set_defaults(func=listPrivilegeMapping) 4993 4994 #delete group privilege mapping 4995 parser_ldap_mapper_delete = parser_ldap_mapper_sub.add_parser("delete",help="Delete privilege mapping") 4996 parser_ldap_mapper_delete.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 4997 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 4998 parser_ldap_mapper_delete.add_argument("-g","--groupName",required=True,help="Group Name") 4999 parser_ldap_mapper_delete.set_defaults(func=deletePrivilegeMapping) 5000 5001 #deleteAll group privilege mapping 5002 parser_ldap_mapper_delete = parser_ldap_mapper_sub.add_parser("purge",help="Delete All privilege mapping") 5003 parser_ldap_mapper_delete.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 5004 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 5005 parser_ldap_mapper_delete.set_defaults(func=deleteAllPrivilegeMapping) 5006 5007 # set local user password 5008 parser_set_password = subparsers.add_parser("set_password", 5009 help="Set password of local user") 5010 parser_set_password.add_argument( "-p", "--password", required=True, 5011 help="Password of local user") 5012 parser_set_password.set_defaults(func=setPassword) 5013 5014 # network 5015 parser_nw = subparsers.add_parser("network", help="network controls") 5016 nw_sub = parser_nw.add_subparsers(title='subcommands', 5017 description='valid subcommands', 5018 help="sub-command help", 5019 dest='command') 5020 5021 # enable DHCP 5022 parser_enable_dhcp = nw_sub.add_parser("enableDHCP", 5023 help="enables the DHCP on given " 5024 "Interface") 5025 parser_enable_dhcp.add_argument("-I", "--Interface", required=True, 5026 help="Name of the ethernet interface(it can" 5027 "be obtained by the " 5028 "command:network view-config)" 5029 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5030 parser_enable_dhcp.set_defaults(func=enableDHCP) 5031 5032 # disable DHCP 5033 parser_disable_dhcp = nw_sub.add_parser("disableDHCP", 5034 help="disables the DHCP on given " 5035 "Interface") 5036 parser_disable_dhcp.add_argument("-I", "--Interface", required=True, 5037 help="Name of the ethernet interface(it can" 5038 "be obtained by the " 5039 "command:network view-config)" 5040 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5041 parser_disable_dhcp.set_defaults(func=disableDHCP) 5042 5043 # get HostName 5044 parser_gethostname = nw_sub.add_parser("getHostName", 5045 help="prints out HostName") 5046 parser_gethostname.set_defaults(func=getHostname) 5047 5048 # set HostName 5049 parser_sethostname = nw_sub.add_parser("setHostName", help="sets HostName") 5050 parser_sethostname.add_argument("-H", "--HostName", required=True, 5051 help="A HostName for the BMC") 5052 parser_sethostname.set_defaults(func=setHostname) 5053 5054 # get domainname 5055 parser_getdomainname = nw_sub.add_parser("getDomainName", 5056 help="prints out DomainName of " 5057 "given Interface") 5058 parser_getdomainname.add_argument("-I", "--Interface", required=True, 5059 help="Name of the ethernet interface(it " 5060 "can be obtained by the " 5061 "command:network view-config)" 5062 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5063 parser_getdomainname.set_defaults(func=getDomainName) 5064 5065 # set domainname 5066 parser_setdomainname = nw_sub.add_parser("setDomainName", 5067 help="sets DomainName of given " 5068 "Interface") 5069 parser_setdomainname.add_argument("-D", "--DomainName", required=True, 5070 help="Ex: DomainName=Domain1,Domain2,...") 5071 parser_setdomainname.add_argument("-I", "--Interface", required=True, 5072 help="Name of the ethernet interface(it " 5073 "can be obtained by the " 5074 "command:network view-config)" 5075 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5076 parser_setdomainname.set_defaults(func=setDomainName) 5077 5078 # get MACAddress 5079 parser_getmacaddress = nw_sub.add_parser("getMACAddress", 5080 help="prints out MACAddress the " 5081 "given Interface") 5082 parser_getmacaddress.add_argument("-I", "--Interface", required=True, 5083 help="Name of the ethernet interface(it " 5084 "can be obtained by the " 5085 "command:network view-config)" 5086 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5087 parser_getmacaddress.set_defaults(func=getMACAddress) 5088 5089 # set MACAddress 5090 parser_setmacaddress = nw_sub.add_parser("setMACAddress", 5091 help="sets MACAddress") 5092 parser_setmacaddress.add_argument("-MA", "--MACAddress", required=True, 5093 help="A MACAddress for the given " 5094 "Interface") 5095 parser_setmacaddress.add_argument("-I", "--Interface", required=True, 5096 help="Name of the ethernet interface(it can" 5097 "be obtained by the " 5098 "command:network view-config)" 5099 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5100 parser_setmacaddress.set_defaults(func=setMACAddress) 5101 5102 # get DefaultGW 5103 parser_getdefaultgw = nw_sub.add_parser("getDefaultGW", 5104 help="prints out DefaultGateway " 5105 "the BMC") 5106 parser_getdefaultgw.set_defaults(func=getDefaultGateway) 5107 5108 # set DefaultGW 5109 parser_setdefaultgw = nw_sub.add_parser("setDefaultGW", 5110 help="sets DefaultGW") 5111 parser_setdefaultgw.add_argument("-GW", "--DefaultGW", required=True, 5112 help="A DefaultGateway for the BMC") 5113 parser_setdefaultgw.set_defaults(func=setDefaultGateway) 5114 5115 # view network Config 5116 parser_ldap_config = nw_sub.add_parser("view-config", help="prints out a " 5117 "list of all network's configured " 5118 "properties") 5119 parser_ldap_config.set_defaults(func=viewNWConfig) 5120 5121 # get DNS 5122 parser_getDNS = nw_sub.add_parser("getDNS", 5123 help="prints out DNS servers on the " 5124 "given interface") 5125 parser_getDNS.add_argument("-I", "--Interface", required=True, 5126 help="Name of the ethernet interface(it can" 5127 "be obtained by the " 5128 "command:network view-config)" 5129 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5130 parser_getDNS.set_defaults(func=getDNS) 5131 5132 # set DNS 5133 parser_setDNS = nw_sub.add_parser("setDNS", 5134 help="sets DNS servers on the given " 5135 "interface") 5136 parser_setDNS.add_argument("-d", "--DNSServers", required=True, 5137 help="Ex: DNSSERVERS=DNS1,DNS2,...") 5138 parser_setDNS.add_argument("-I", "--Interface", required=True, 5139 help="Name of the ethernet interface(it can" 5140 "be obtained by the " 5141 "command:network view-config)" 5142 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5143 parser_setDNS.set_defaults(func=setDNS) 5144 5145 # get NTP 5146 parser_getNTP = nw_sub.add_parser("getNTP", 5147 help="prints out NTP servers on the " 5148 "given interface") 5149 parser_getNTP.add_argument("-I", "--Interface", required=True, 5150 help="Name of the ethernet interface(it can" 5151 "be obtained by the " 5152 "command:network view-config)" 5153 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5154 parser_getNTP.set_defaults(func=getNTP) 5155 5156 # set NTP 5157 parser_setNTP = nw_sub.add_parser("setNTP", 5158 help="sets NTP servers on the given " 5159 "interface") 5160 parser_setNTP.add_argument("-N", "--NTPServers", required=True, 5161 help="Ex: NTPSERVERS=NTP1,NTP2,...") 5162 parser_setNTP.add_argument("-I", "--Interface", required=True, 5163 help="Name of the ethernet interface(it can" 5164 "be obtained by the " 5165 "command:network view-config)" 5166 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5167 parser_setNTP.set_defaults(func=setNTP) 5168 5169 # configure IP 5170 parser_ip_config = nw_sub.add_parser("addIP", help="Sets IP address to" 5171 "given interface") 5172 parser_ip_config.add_argument("-a", "--address", required=True, 5173 help="IP address of given interface") 5174 parser_ip_config.add_argument("-gw", "--gateway", required=False, default='', 5175 help="The gateway for given interface") 5176 parser_ip_config.add_argument("-l", "--prefixLength", required=True, 5177 help="The prefixLength of IP address") 5178 parser_ip_config.add_argument("-p", "--type", required=True, 5179 choices=['ipv4', 'ipv6'], 5180 help="The protocol type of the given" 5181 "IP address") 5182 parser_ip_config.add_argument("-I", "--Interface", required=True, 5183 help="Name of the ethernet interface(it can" 5184 "be obtained by the " 5185 "command:network view-config)" 5186 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5187 parser_ip_config.set_defaults(func=addIP) 5188 5189 # getIP 5190 parser_getIP = nw_sub.add_parser("getIP", help="prints out IP address" 5191 "of given interface") 5192 parser_getIP.add_argument("-I", "--Interface", required=True, 5193 help="Name of the ethernet interface(it can" 5194 "be obtained by the command:network view-config)" 5195 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5196 parser_getIP.set_defaults(func=getIP) 5197 5198 # rmIP 5199 parser_rmIP = nw_sub.add_parser("rmIP", help="deletes IP address" 5200 "of given interface") 5201 parser_rmIP.add_argument("-a", "--address", required=True, 5202 help="IP address to remove form given Interface") 5203 parser_rmIP.add_argument("-I", "--Interface", required=True, 5204 help="Name of the ethernet interface(it can" 5205 "be obtained by the command:network view-config)" 5206 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5207 parser_rmIP.set_defaults(func=deleteIP) 5208 5209 # add VLAN 5210 parser_create_vlan = nw_sub.add_parser("addVLAN", help="enables VLAN " 5211 "on given interface with given " 5212 "VLAN Identifier") 5213 parser_create_vlan.add_argument("-I", "--Interface", required=True, 5214 choices=['eth0', 'eth1'], 5215 help="Name of the ethernet interface") 5216 parser_create_vlan.add_argument("-n", "--Identifier", required=True, 5217 help="VLAN Identifier") 5218 parser_create_vlan.set_defaults(func=addVLAN) 5219 5220 # delete VLAN 5221 parser_delete_vlan = nw_sub.add_parser("deleteVLAN", help="disables VLAN " 5222 "on given interface with given " 5223 "VLAN Identifier") 5224 parser_delete_vlan.add_argument("-I", "--Interface", required=True, 5225 help="Name of the ethernet interface(it can" 5226 "be obtained by the " 5227 "command:network view-config)" 5228 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5229 parser_delete_vlan.set_defaults(func=deleteVLAN) 5230 5231 # viewDHCPConfig 5232 parser_viewDHCPConfig = nw_sub.add_parser("viewDHCPConfig", 5233 help="Shows DHCP configured " 5234 "Properties") 5235 parser_viewDHCPConfig.set_defaults(func=viewDHCPConfig) 5236 5237 # configureDHCP 5238 parser_configDHCP = nw_sub.add_parser("configureDHCP", 5239 help="Configures/updates DHCP " 5240 "Properties") 5241 parser_configDHCP.add_argument("-d", "--DNSEnabled", type=str2bool, 5242 required=True, help="Sets DNSEnabled property") 5243 parser_configDHCP.add_argument("-n", "--HostNameEnabled", type=str2bool, 5244 required=True, 5245 help="Sets HostNameEnabled property") 5246 parser_configDHCP.add_argument("-t", "--NTPEnabled", type=str2bool, 5247 required=True, 5248 help="Sets NTPEnabled property") 5249 parser_configDHCP.add_argument("-s", "--SendHostNameEnabled", type=str2bool, 5250 required=True, 5251 help="Sets SendHostNameEnabled property") 5252 parser_configDHCP.set_defaults(func=configureDHCP) 5253 5254 # network factory reset 5255 parser_nw_reset = nw_sub.add_parser("nwReset", 5256 help="Resets networks setting to " 5257 "factory defaults. " 5258 "note:Reset settings will be applied " 5259 "after BMC reboot") 5260 parser_nw_reset.set_defaults(func=nwReset) 5261 5262 return parser 5263 5264def main(argv=None): 5265 """ 5266 main function for running the command line utility as a sub application 5267 """ 5268 global toolVersion 5269 toolVersion = "1.19" 5270 global isRedfishSupport 5271 5272 parser = createCommandParser() 5273 args = parser.parse_args(argv) 5274 5275 totTimeStart = int(round(time.time()*1000)) 5276 5277 if(sys.version_info < (3,0)): 5278 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 5279 if sys.version_info >= (3,0): 5280 requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) 5281 if (args.version): 5282 print("Version: "+ toolVersion) 5283 sys.exit(0) 5284 if (hasattr(args, 'fileloc') and args.fileloc is not None and 'print' in args.command): 5285 mysess = None 5286 print(selPrint('N/A', args, mysess)) 5287 else: 5288 if(hasattr(args, 'host') and hasattr(args,'user')): 5289 if (args.askpw): 5290 pw = getpass.getpass() 5291 elif(args.PW is not None): 5292 pw = args.PW 5293 elif(args.PWenvvar): 5294 pw = os.environ['OPENBMCTOOL_PASSWORD'] 5295 else: 5296 print("You must specify a password") 5297 sys.exit() 5298 logintimeStart = int(round(time.time()*1000)) 5299 mysess = login(args.host, args.user, pw, args.json, 5300 args.command == 'set_password') 5301 if(mysess == None): 5302 print("Login Failed!") 5303 sys.exit() 5304 if(sys.version_info < (3,0)): 5305 if isinstance(mysess, basestring): 5306 print(mysess) 5307 sys.exit(1) 5308 elif sys.version_info >= (3,0): 5309 if isinstance(mysess, str): 5310 print(mysess) 5311 sys.exit(1) 5312 logintimeStop = int(round(time.time()*1000)) 5313 isRedfishSupport = redfishSupportPresent(args.host,mysess) 5314 commandTimeStart = int(round(time.time()*1000)) 5315 output = args.func(args.host, args, mysess) 5316 commandTimeStop = int(round(time.time()*1000)) 5317 if isinstance(output, dict): 5318 print(json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 5319 else: 5320 print(output) 5321 if (mysess is not None): 5322 logout(args.host, args.user, pw, mysess, args.json) 5323 if(args.procTime): 5324 print("Total time: " + str(int(round(time.time()*1000))- totTimeStart)) 5325 print("loginTime: " + str(logintimeStop - logintimeStart)) 5326 print("command Time: " + str(commandTimeStop - commandTimeStart)) 5327 else: 5328 print("usage:\n" 5329 " OPENBMCTOOL_PASSWORD=secret # if using -E\n" 5330 " openbmctool.py [-h] -H HOST -U USER {-A | -P PW | -E} [-j]\n" + 5331 "\t[-t POLICYTABLELOC] [-V]\n" + 5332 "\t{fru,sensors,sel,chassis,collect_service_data, \ 5333 health_check,dump,bmc,mc,gardclear,firmware,logging}\n" + 5334 "\t...\n" + 5335 "openbmctool.py: error: the following arguments are required: -H/--host, -U/--user") 5336 sys.exit() 5337 5338if __name__ == '__main__': 5339 """ 5340 main function when called from the command line 5341 5342 """ 5343 import sys 5344 5345 isTTY = sys.stdout.isatty() 5346 assert sys.version_info >= (2,7) 5347 main() 5348