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()['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 getTask(host, args, session): 1485 """ 1486 Get operation on the Task Monitor URI 1487 1488 @param host: string, the hostname or IP address of the bmc 1489 @param args: contains additional arguments used by the task 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 if args.taskURI is not None: 1494 url ='https://'+host+str(args.taskURI) 1495 try: 1496 r = session.post(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1497 if (r.status_code == 200 and not args.json): 1498 return r.text 1499 elif (r.status_code == 200 and args.json): 1500 return r.json() 1501 else: 1502 return ('Failed to retrieve the data on Task Monitor URI') 1503 except(requests.exceptions.Timeout): 1504 return connectionErrHandler(args.json, "Timeout", None) 1505 except(requests.exceptions.ConnectionError) as err: 1506 return connectionErrHandler(args.json, "ConnectionError", err) 1507 else: 1508 return 'You must specify the Task Monitor URI' 1509 1510 1511def dumpRetrieve(host, args, session): 1512 """ 1513 Downloads dump of given dump type 1514 1515 @param host: string, the hostname or IP address of the bmc 1516 @param args: contains additional arguments used by the collectServiceData sub command 1517 @param session: the active session to use 1518 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1519 """ 1520 dumpType = args.dumpType 1521 if (args.dumpType=="SystemDump"): 1522 dumpResp=systemDumpRetrieve(host,args,session) 1523 elif(args.dumpType=="bmc"): 1524 dumpResp=bmcDumpRetrieve(host,args,session) 1525 return dumpResp 1526 1527def dumpList(host, args, session): 1528 """ 1529 Lists dump of the given dump type 1530 1531 @param host: string, the hostname or IP address of the bmc 1532 @param args: contains additional arguments used by the collectServiceData sub command 1533 @param session: the active session to use 1534 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1535 """ 1536 if (args.dumpType=="SystemDump"): 1537 dumpResp=systemDumpList(host,args,session) 1538 elif(args.dumpType=="bmc"): 1539 dumpResp=bmcDumpList(host,args,session) 1540 return dumpResp 1541 1542def dumpDelete(host, args, session): 1543 """ 1544 Deletes dump of the given dump type 1545 1546 @param host: string, the hostname or IP address of the bmc 1547 @param args: contains additional arguments used by the collectServiceData sub command 1548 @param session: the active session to use 1549 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1550 """ 1551 if (args.dumpType=="SystemDump"): 1552 dumpResp=systemDumpDelete(host,args,session) 1553 elif(args.dumpType=="bmc"): 1554 dumpResp=bmcDumpDelete(host,args,session) 1555 return dumpResp 1556 1557def dumpDeleteAll(host, args, session): 1558 """ 1559 Deletes all dumps of the given dump type 1560 1561 @param host: string, the hostname or IP address of the bmc 1562 @param args: contains additional arguments used by the collectServiceData sub command 1563 @param session: the active session to use 1564 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1565 """ 1566 if (args.dumpType=="SystemDump"): 1567 dumpResp=systemDumpDeleteAll(host,args,session) 1568 elif(args.dumpType=="bmc"): 1569 dumpResp=bmcDumpDeleteAll(host,args,session) 1570 return dumpResp 1571 1572def dumpCreate(host, args, session): 1573 """ 1574 Creates dump for the given dump type 1575 1576 @param host: string, the hostname or IP address of the bmc 1577 @param args: contains additional arguments used by the collectServiceData sub command 1578 @param session: the active session to use 1579 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1580 """ 1581 if (args.dumpType=="SystemDump"): 1582 dumpResp=systemDumpCreate(host,args,session) 1583 elif(args.dumpType=="bmc"): 1584 dumpResp=bmcDumpCreate(host,args,session) 1585 return dumpResp 1586 1587 1588def bmcDumpRetrieve(host, args, session): 1589 """ 1590 Downloads a dump file from the bmc 1591 1592 @param host: string, the hostname or IP address of the bmc 1593 @param args: contains additional arguments used by the collectServiceData sub command 1594 @param session: the active session to use 1595 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1596 """ 1597 dumpNum = args.dumpNum 1598 if (args.dumpSaveLoc is not None): 1599 saveLoc = args.dumpSaveLoc 1600 else: 1601 saveLoc = tempfile.gettempdir() 1602 url ='https://'+host+'/download/dump/' + str(dumpNum) 1603 try: 1604 r = session.get(url, headers=jsonHeader, stream=True, verify=False, timeout=baseTimeout) 1605 if (args.dumpSaveLoc is not None): 1606 if os.path.exists(saveLoc): 1607 if saveLoc[-1] != os.path.sep: 1608 saveLoc = saveLoc + os.path.sep 1609 filename = saveLoc + host+'-dump' + str(dumpNum) + '.tar.xz' 1610 1611 else: 1612 return 'Invalid save location specified' 1613 else: 1614 filename = tempfile.gettempdir()+os.sep + host+'-dump' + str(dumpNum) + '.tar.xz' 1615 1616 with open(filename, 'wb') as f: 1617 for chunk in r.iter_content(chunk_size =1024): 1618 if chunk: 1619 f.write(chunk) 1620 return 'Saved as ' + filename 1621 1622 except(requests.exceptions.Timeout): 1623 return connectionErrHandler(args.json, "Timeout", None) 1624 1625 except(requests.exceptions.ConnectionError) as err: 1626 return connectionErrHandler(args.json, "ConnectionError", err) 1627 1628def bmcDumpList(host, args, session): 1629 """ 1630 Lists the number of dump files on the bmc 1631 1632 @param host: string, the hostname or IP address of the bmc 1633 @param args: contains additional arguments used by the collectServiceData sub command 1634 @param session: the active session to use 1635 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1636 """ 1637 url ='https://'+host+'/xyz/openbmc_project/dump/list' 1638 try: 1639 r = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1640 dumpList = r.json() 1641 formattedList = [] 1642 #remove items that aren't dump entries 'entry, internal, manager endpoints' 1643 if 'data' in dumpList: 1644 for entry in dumpList['data']: 1645 if 'entry' in entry: 1646 if entry.split('/')[-1].isnumeric(): 1647 formattedList.append(entry) 1648 dumpList['data']= formattedList 1649 return dumpList 1650 except(requests.exceptions.Timeout): 1651 return connectionErrHandler(args.json, "Timeout", None) 1652 1653 except(requests.exceptions.ConnectionError) as err: 1654 return connectionErrHandler(args.json, "ConnectionError", err) 1655 1656def bmcDumpDelete(host, args, session): 1657 """ 1658 Deletes BMC dump files from the bmc 1659 1660 @param host: string, the hostname or IP address of the bmc 1661 @param args: contains additional arguments used by the collectServiceData sub command 1662 @param session: the active session to use 1663 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1664 """ 1665 dumpList = [] 1666 successList = [] 1667 failedList = [] 1668 if args.dumpNum is not None: 1669 if isinstance(args.dumpNum, list): 1670 dumpList = args.dumpNum 1671 else: 1672 dumpList.append(args.dumpNum) 1673 for dumpNum in dumpList: 1674 url ='https://'+host+'/xyz/openbmc_project/dump/entry/'+str(dumpNum)+'/action/Delete' 1675 try: 1676 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1677 if r.status_code == 200: 1678 successList.append(str(dumpNum)) 1679 else: 1680 failedList.append(str(dumpNum)) 1681 except(requests.exceptions.Timeout): 1682 return connectionErrHandler(args.json, "Timeout", None) 1683 except(requests.exceptions.ConnectionError) as err: 1684 return connectionErrHandler(args.json, "ConnectionError", err) 1685 output = "Successfully deleted dumps: " + ', '.join(successList) 1686 if(len(failedList)>0): 1687 output+= '\nFailed to delete dumps: ' + ', '.join(failedList) 1688 return output 1689 else: 1690 return 'You must specify an entry number to delete' 1691 1692def bmcDumpDeleteAll(host, args, session): 1693 """ 1694 Deletes All BMC dump files from the bmc 1695 1696 @param host: string, the hostname or IP address of the bmc 1697 @param args: contains additional arguments used by the collectServiceData sub command 1698 @param session: the active session to use 1699 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1700 """ 1701 dumpResp = bmcDumpList(host, args, session) 1702 if 'FQPSPIN0000M' in dumpResp or 'FQPSPIN0001M'in dumpResp: 1703 return dumpResp 1704 dumpList = dumpResp['data'] 1705 d = vars(args) 1706 dumpNums = [] 1707 for dump in dumpList: 1708 dumpNum = dump.strip().split('/')[-1] 1709 if dumpNum.isdigit(): 1710 dumpNums.append(int(dumpNum)) 1711 d['dumpNum'] = dumpNums 1712 1713 return bmcDumpDelete(host, args, session) 1714 1715 1716def bmcDumpCreate(host, args, session): 1717 """ 1718 Creates a bmc dump file 1719 1720 @param host: string, the hostname or IP address of the bmc 1721 @param args: contains additional arguments used by the collectServiceData sub command 1722 @param session: the active session to use 1723 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1724 """ 1725 url = 'https://'+host+'/xyz/openbmc_project/dump/action/CreateDump' 1726 try: 1727 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1728 info = r.json() 1729 if(r.status_code == 200 and not args.json): 1730 return ('Dump successfully created') 1731 elif(args.json): 1732 return info 1733 elif 'data' in info: 1734 if 'QuotaExceeded' in info['data']['description']: 1735 return 'BMC dump space is full. Please delete at least one existing dump entry and try again.' 1736 else: 1737 return "Failed to create a BMC dump. BMC Response:\n {resp}".format(resp=info) 1738 else: 1739 return "Failed to create a BMC dump. BMC Response:\n {resp}".format(resp=info) 1740 except(requests.exceptions.Timeout): 1741 return connectionErrHandler(args.json, "Timeout", None) 1742 except(requests.exceptions.ConnectionError) as err: 1743 return connectionErrHandler(args.json, "ConnectionError", err) 1744 1745 1746def systemDumpRetrieve(host, args, session): 1747 """ 1748 Downloads system dump 1749 1750 @param host: string, the hostname or IP address of the bmc 1751 @param args: contains additional arguments used by the collectServiceData sub command 1752 @param session: the active session to use 1753 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1754 """ 1755 NBDSetup(host,args,session) 1756 pipe = NBDPipe() 1757 pipe.openHTTPSocket(args) 1758 pipe.openTCPSocket() 1759 pipe.waitformessage() 1760 1761def systemDumpList(host, args, session): 1762 """ 1763 Lists system dumps 1764 1765 @param host: string, the hostname or IP address of the bmc 1766 @param args: contains additional arguments used by the collectServiceData sub command 1767 @param session: the active session to use 1768 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1769 """ 1770 url = "https://"+host+"/redfish/v1/Systems/system/LogServices/Dump/Entries" 1771 try: 1772 r = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 1773 dumpList = r.json() 1774 return dumpList 1775 except(requests.exceptions.Timeout): 1776 return connectionErrHandler(args.json, "Timeout", None) 1777 1778 except(requests.exceptions.ConnectionError) as err: 1779 return connectionErrHandler(args.json, "ConnectionError", err) 1780 1781 1782def systemDumpDelete(host, args, session): 1783 """ 1784 Deletes system dump 1785 1786 @param host: string, the hostname or IP address of the bmc 1787 @param args: contains additional arguments used by the collectServiceData sub command 1788 @param session: the active session to use 1789 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1790 """ 1791 dumpList = [] 1792 successList = [] 1793 failedList = [] 1794 if args.dumpNum is not None: 1795 if isinstance(args.dumpNum, list): 1796 dumpList = args.dumpNum 1797 else: 1798 dumpList.append(args.dumpNum) 1799 for dumpNum in dumpList: 1800 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Entries/'+ str(dumpNum) 1801 try: 1802 r = session.delete(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1803 if r.status_code == 200: 1804 successList.append(str(dumpNum)) 1805 else: 1806 failedList.append(str(dumpNum)) 1807 except(requests.exceptions.Timeout): 1808 return connectionErrHandler(args.json, "Timeout", None) 1809 except(requests.exceptions.ConnectionError) as err: 1810 return connectionErrHandler(args.json, "ConnectionError", err) 1811 output = "Successfully deleted dumps: " + ', '.join(successList) 1812 if(len(failedList)>0): 1813 output+= '\nFailed to delete dumps: ' + ', '.join(failedList) 1814 return output 1815 else: 1816 return 'You must specify an entry number to delete' 1817 1818def systemDumpDeleteAll(host, args, session): 1819 """ 1820 Deletes All system dumps 1821 1822 @param host: string, the hostname or IP address of the bmc 1823 @param args: contains additional arguments used by the collectServiceData sub command 1824 @param session: the active session to use 1825 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1826 """ 1827 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog' 1828 try: 1829 r = session.post(url, headers=jsonHeader, json = {"data": []}, verify=False, timeout=baseTimeout) 1830 if(r.status_code == 200 and not args.json): 1831 return ('Dumps successfully cleared') 1832 elif(args.json): 1833 return r.json() 1834 else: 1835 return ('Failed to clear dumps') 1836 except(requests.exceptions.Timeout): 1837 return connectionErrHandler(args.json, "Timeout", None) 1838 except(requests.exceptions.ConnectionError) as err: 1839 return connectionErrHandler(args.json, "ConnectionError", err) 1840 1841def systemDumpCreate(host, args, session): 1842 """ 1843 Creates a system dump 1844 1845 @param host: string, the hostname or IP address of the bmc 1846 @param args: contains additional arguments used by the collectServiceData sub command 1847 @param session: the active session to use 1848 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1849 """ 1850 url = 'https://'+host+'/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData' 1851 params = {'DiagnosticDataType':'OEM', 'OEMDiagnosticDataType':'System'} 1852 try: 1853 r = session.post(url, headers=jsonHeader, params=params, data = json.dumps(params), verify=False, timeout=baseTimeout) 1854 if(r.status_code == 200): 1855 return r.json() 1856 else: 1857 return ('Failed to create dump') 1858 except(requests.exceptions.Timeout): 1859 return connectionErrHandler(args.json, "Timeout", None) 1860 except(requests.exceptions.ConnectionError) as err: 1861 return connectionErrHandler(args.json, "ConnectionError", err) 1862 1863def csdDumpInitiate(host, args, session): 1864 """ 1865 Starts the process of getting the current list of dumps then initiates the creation of one. 1866 1867 @param host: string, the hostname or IP address of the bmc 1868 @param args: contains additional arguments used by the collectServiceData sub command 1869 @param session: the active session to use 1870 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1871 """ 1872 errorInfo = "" 1873 dumpcount = 0 1874 try: 1875 d = vars(args) 1876 d['json'] = True 1877 except Exception as e: 1878 errorInfo += "Failed to set the json flag to True \n Exception: {eInfo}\n".format(eInfo=e) 1879 exc_type, exc_obj, exc_tb = sys.exc_info() 1880 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1881 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1882 errorInfo += traceback.format_exc() 1883 1884 try: 1885 for i in range(3): 1886 dumpInfo = bmcDumpList(host, args, session) 1887 if 'data' in dumpInfo: 1888 dumpcount = len(dumpInfo['data']) 1889 break 1890 else: 1891 errorInfo+= "Dump List Message returned: " + json.dumps(dumpInfo,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1892 except Exception as e: 1893 errorInfo+= "Failed to collect the list of dumps.\nException: {eInfo}\n".format(eInfo=e) 1894 exc_type, exc_obj, exc_tb = sys.exc_info() 1895 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1896 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1897 errorInfo += traceback.format_exc() 1898 1899 #Create a user initiated dump 1900 dumpFailure = True 1901 try: 1902 for i in range(3): 1903 dumpcreated = bmcDumpCreate(host, args, session) 1904 if 'message' in dumpcreated: 1905 if 'ok' in dumpcreated['message'].lower(): 1906 dumpFailure = False 1907 break 1908 elif 'data' in dumpcreated: 1909 if 'QuotaExceeded' in dumpcreated['data']['description']: 1910 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.') 1911 errorInfo+='Dump Space is full. No new dump was created with this collection' 1912 break 1913 else: 1914 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1915 else: 1916 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1917 else: 1918 errorInfo+= "Dump create message returned: " + json.dumps(dumpcreated,indent=0, separators=(',', ':')).replace('\n','') +"\n" 1919 except Exception as e: 1920 errorInfo+= "Dump create exception encountered: {eInfo}\n".format(eInfo=e) 1921 exc_type, exc_obj, exc_tb = sys.exc_info() 1922 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1923 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1924 errorInfo += traceback.format_exc() 1925 1926 output = {} 1927 output['errors'] = errorInfo 1928 output['dumpcount'] = dumpcount 1929 if dumpFailure: output['dumpFailure'] = True 1930 return output 1931 1932def csdInventory(host, args,session, fileDir): 1933 """ 1934 Collects the BMC inventory, retrying if necessary 1935 1936 @param host: string, the hostname or IP address of the bmc 1937 @param args: contains additional arguments used by the collectServiceData sub command 1938 @param session: the active session to use 1939 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1940 @param fileDir: string representation of the path to use for putting files created 1941 """ 1942 errorInfo = "===========Inventory =============\n" 1943 output={} 1944 inventoryCollected = False 1945 try: 1946 for i in range(3): 1947 frulist = fruPrint(host, args, session) 1948 if 'Hardware' in frulist: 1949 inventoryCollected = True 1950 break 1951 else: 1952 errorInfo += json.dumps(frulist, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 1953 except Exception as e: 1954 errorInfo += "Inventory collection exception: {eInfo}\n".format(eInfo=e) 1955 exc_type, exc_obj, exc_tb = sys.exc_info() 1956 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1957 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1958 errorInfo += traceback.format_exc() 1959 if inventoryCollected: 1960 try: 1961 with open(fileDir +os.sep+'inventory.txt', 'w') as f: 1962 f.write(json.dumps(frulist, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 1963 print("Inventory collected and stored in " + fileDir + os.sep + "inventory.txt") 1964 output['fileLoc'] = fileDir+os.sep+'inventory.txt' 1965 except Exception as e: 1966 print("Failed to write inventory to file.") 1967 errorInfo += "Error writing inventory to the file. Exception: {eInfo}\n".format(eInfo=e) 1968 exc_type, exc_obj, exc_tb = sys.exc_info() 1969 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 1970 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 1971 errorInfo += traceback.format_exc() 1972 1973 output['errors'] = errorInfo 1974 1975 return output 1976 1977def csdSensors(host, args,session, fileDir): 1978 """ 1979 Collects the BMC sensor readings, retrying if necessary 1980 1981 @param host: string, the hostname or IP address of the bmc 1982 @param args: contains additional arguments used by the collectServiceData sub command 1983 @param session: the active session to use 1984 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 1985 @param fileDir: string representation of the path to use for putting files created 1986 """ 1987 errorInfo = "===========Sensors =============\n" 1988 sensorsCollected = False 1989 output={} 1990 try: 1991 d = vars(args) 1992 d['json'] = False 1993 except Exception as e: 1994 errorInfo += "Failed to set the json flag to False \n 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 try: 2001 for i in range(3): 2002 sensorReadings = sensor(host, args, session) 2003 if 'OCC0' in sensorReadings: 2004 sensorsCollected = True 2005 break 2006 else: 2007 errorInfo += sensorReadings 2008 except Exception as e: 2009 errorInfo += "Sensor reading collection exception: {eInfo}\n".format(eInfo=e) 2010 exc_type, exc_obj, exc_tb = sys.exc_info() 2011 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2012 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2013 errorInfo += traceback.format_exc() 2014 if sensorsCollected: 2015 try: 2016 with open(fileDir +os.sep+'sensorReadings.txt', 'w') as f: 2017 f.write(sensorReadings) 2018 print("Sensor readings collected and stored in " + fileDir + os.sep+ "sensorReadings.txt") 2019 output['fileLoc'] = fileDir+os.sep+'sensorReadings.txt' 2020 except Exception as e: 2021 print("Failed to write sensor readings to file system.") 2022 errorInfo += "Error writing sensor readings to the file. Exception: {eInfo}\n".format(eInfo=e) 2023 exc_type, exc_obj, exc_tb = sys.exc_info() 2024 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2025 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2026 errorInfo += traceback.format_exc() 2027 2028 output['errors'] = errorInfo 2029 return output 2030 2031def csdLEDs(host,args, session, fileDir): 2032 """ 2033 Collects the BMC LED status, retrying if necessary 2034 2035 @param host: string, the hostname or IP address of the bmc 2036 @param args: contains additional arguments used by the collectServiceData sub command 2037 @param session: the active session to use 2038 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2039 @param fileDir: string representation of the path to use for putting files created 2040 """ 2041 errorInfo = "===========LEDs =============\n" 2042 ledsCollected = False 2043 output={} 2044 try: 2045 d = vars(args) 2046 d['json'] = True 2047 except Exception as e: 2048 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2049 exc_type, exc_obj, exc_tb = sys.exc_info() 2050 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2051 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2052 errorInfo += traceback.format_exc() 2053 try: 2054 url="https://"+host+"/xyz/openbmc_project/led/enumerate" 2055 httpHeader = {'Content-Type':'application/json'} 2056 for i in range(3): 2057 try: 2058 ledRes = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2059 if ledRes.status_code == 200: 2060 ledsCollected = True 2061 leds = ledRes.json()['data'] 2062 break 2063 else: 2064 errorInfo += ledRes.text 2065 except(requests.exceptions.Timeout): 2066 errorInfo+=json.dumps( connectionErrHandler(args.json, "Timeout", None), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2067 except(requests.exceptions.ConnectionError) as err: 2068 errorInfo += json.dumps(connectionErrHandler(args.json, "ConnectionError", err), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2069 exc_type, exc_obj, exc_tb = sys.exc_info() 2070 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2071 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2072 errorInfo += traceback.format_exc() 2073 except Exception as e: 2074 errorInfo += "LED status collection exception: {eInfo}\n".format(eInfo=e) 2075 exc_type, exc_obj, exc_tb = sys.exc_info() 2076 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2077 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2078 errorInfo += traceback.format_exc() 2079 2080 if ledsCollected: 2081 try: 2082 with open(fileDir +os.sep+'ledStatus.txt', 'w') as f: 2083 f.write(json.dumps(leds, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 2084 print("LED status collected and stored in " + fileDir + os.sep+ "ledStatus.txt") 2085 output['fileLoc'] = fileDir+os.sep+'ledStatus.txt' 2086 except Exception as e: 2087 print("Failed to write LED status to file system.") 2088 errorInfo += "Error writing LED status to the file. Exception: {eInfo}\n".format(eInfo=e) 2089 exc_type, exc_obj, exc_tb = sys.exc_info() 2090 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2091 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2092 errorInfo += traceback.format_exc() 2093 2094 output['errors'] = errorInfo 2095 return output 2096 2097def csdSelShortList(host, args, session, fileDir): 2098 """ 2099 Collects the BMC log entries, retrying if necessary 2100 2101 @param host: string, the hostname or IP address of the bmc 2102 @param args: contains additional arguments used by the collectServiceData sub command 2103 @param session: the active session to use 2104 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2105 @param fileDir: string representation of the path to use for putting files created 2106 """ 2107 errorInfo = "===========SEL Short List =============\n" 2108 selsCollected = False 2109 output={} 2110 try: 2111 d = vars(args) 2112 d['json'] = False 2113 except Exception as e: 2114 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2115 exc_type, exc_obj, exc_tb = sys.exc_info() 2116 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2117 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2118 errorInfo += traceback.format_exc() 2119 2120 try: 2121 for i in range(3): 2122 sels = selPrint(host,args,session) 2123 if '----Active Alerts----' in sels or 'No log entries found' in sels or '----Historical Alerts----' in sels: 2124 selsCollected = True 2125 break 2126 else: 2127 errorInfo += sels + '\n' 2128 except Exception as e: 2129 errorInfo += "SEL short list collection exception: {eInfo}\n".format(eInfo=e) 2130 exc_type, exc_obj, exc_tb = sys.exc_info() 2131 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2132 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2133 errorInfo += traceback.format_exc() 2134 2135 if selsCollected: 2136 try: 2137 with open(fileDir +os.sep+'SELshortlist.txt', 'w') as f: 2138 f.write(sels) 2139 print("SEL short list collected and stored in " + fileDir + os.sep+ "SELshortlist.txt") 2140 output['fileLoc'] = fileDir+os.sep+'SELshortlist.txt' 2141 except Exception as e: 2142 print("Failed to write SEL short list to file system.") 2143 errorInfo += "Error writing SEL short list to the file. Exception: {eInfo}\n".format(eInfo=e) 2144 exc_type, exc_obj, exc_tb = sys.exc_info() 2145 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2146 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2147 errorInfo += traceback.format_exc() 2148 2149 output['errors'] = errorInfo 2150 return output 2151 2152def csdParsedSels(host, args, session, fileDir): 2153 """ 2154 Collects the BMC log entries, retrying if necessary 2155 2156 @param host: string, the hostname or IP address of the bmc 2157 @param args: contains additional arguments used by the collectServiceData sub command 2158 @param session: the active session to use 2159 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2160 @param fileDir: string representation of the path to use for putting files created 2161 """ 2162 errorInfo = "===========SEL Parsed List =============\n" 2163 selsCollected = False 2164 output={} 2165 try: 2166 d = vars(args) 2167 d['json'] = True 2168 d['fullEsel'] = True 2169 except Exception as e: 2170 errorInfo += "Failed to set the json flag to True \n Exception: {eInfo}\n".format(eInfo=e) 2171 exc_type, exc_obj, exc_tb = sys.exc_info() 2172 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2173 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2174 errorInfo += traceback.format_exc() 2175 2176 try: 2177 for i in range(3): 2178 parsedfullsels = json.loads(selPrint(host,args,session)) 2179 if 'numAlerts' in parsedfullsels: 2180 selsCollected = True 2181 break 2182 else: 2183 errorInfo += parsedfullsels + '\n' 2184 except Exception as e: 2185 errorInfo += "Parsed full SELs collection exception: {eInfo}\n".format(eInfo=e) 2186 exc_type, exc_obj, exc_tb = sys.exc_info() 2187 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2188 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2189 errorInfo += traceback.format_exc() 2190 2191 if selsCollected: 2192 try: 2193 sortedSELs = sortSELs(parsedfullsels) 2194 with open(fileDir +os.sep+'parsedSELs.txt', 'w') as f: 2195 for log in sortedSELs[0]: 2196 esel = "" 2197 parsedfullsels[sortedSELs[1][str(log)]]['timestamp'] = datetime.datetime.fromtimestamp(int(parsedfullsels[sortedSELs[1][str(log)]]['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S") 2198 if ('raweSEL' in parsedfullsels[sortedSELs[1][str(log)]] and args.devdebug): 2199 esel = parsedfullsels[sortedSELs[1][str(log)]]['raweSEL'] 2200 del parsedfullsels[sortedSELs[1][str(log)]]['raweSEL'] 2201 f.write(json.dumps(parsedfullsels[sortedSELs[1][str(log)]],sort_keys=True, indent=4, separators=(',', ': '))) 2202 if(args.devdebug and esel != ""): 2203 f.write(parseESEL(args, esel)) 2204 print("Parsed SELs collected and stored in " + fileDir + os.sep+ "parsedSELs.txt") 2205 output['fileLoc'] = fileDir+os.sep+'parsedSELs.txt' 2206 except Exception as e: 2207 print("Failed to write fully parsed SELs to file system.") 2208 errorInfo += "Error writing fully parsed SELs to the file. Exception: {eInfo}\n".format(eInfo=e) 2209 exc_type, exc_obj, exc_tb = sys.exc_info() 2210 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2211 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2212 errorInfo += traceback.format_exc() 2213 2214 output['errors'] = errorInfo 2215 return output 2216 2217def csdFullEnumeration(host, args, session, fileDir): 2218 """ 2219 Collects a full enumeration of /xyz/openbmc_project/, retrying if necessary 2220 2221 @param host: string, the hostname or IP address of the bmc 2222 @param args: contains additional arguments used by the collectServiceData sub command 2223 @param session: the active session to use 2224 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2225 @param fileDir: string representation of the path to use for putting files created 2226 """ 2227 errorInfo = "===========BMC Full Enumeration =============\n" 2228 bmcFullCollected = False 2229 output={} 2230 try: 2231 d = vars(args) 2232 d['json'] = True 2233 except Exception as e: 2234 errorInfo += "Failed to set the json flag to False \n Exception: {eInfo}\n".format(eInfo=e) 2235 exc_type, exc_obj, exc_tb = sys.exc_info() 2236 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2237 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2238 errorInfo += traceback.format_exc() 2239 try: 2240 print("Attempting to get a full BMC enumeration") 2241 url="https://"+host+"/xyz/openbmc_project/enumerate" 2242 httpHeader = {'Content-Type':'application/json'} 2243 for i in range(3): 2244 try: 2245 bmcRes = session.get(url, headers=jsonHeader, verify=False, timeout=180) 2246 if bmcRes.status_code == 200: 2247 bmcFullCollected = True 2248 fullEnumeration = bmcRes.json() 2249 break 2250 else: 2251 errorInfo += bmcRes.text 2252 except(requests.exceptions.Timeout): 2253 errorInfo+=json.dumps( connectionErrHandler(args.json, "Timeout", None), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2254 except(requests.exceptions.ConnectionError) as err: 2255 errorInfo += json.dumps(connectionErrHandler(args.json, "ConnectionError", err), sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n' 2256 exc_type, exc_obj, exc_tb = sys.exc_info() 2257 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2258 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2259 errorInfo += traceback.format_exc() 2260 except Exception as e: 2261 errorInfo += "RAW BMC data collection exception: {eInfo}\n".format(eInfo=e) 2262 exc_type, exc_obj, exc_tb = sys.exc_info() 2263 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2264 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2265 errorInfo += traceback.format_exc() 2266 2267 if bmcFullCollected: 2268 try: 2269 with open(fileDir +os.sep+'bmcFullRaw.txt', 'w') as f: 2270 f.write(json.dumps(fullEnumeration, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) + '\n') 2271 print("RAW BMC data collected and saved into " + fileDir + os.sep+ "bmcFullRaw.txt") 2272 output['fileLoc'] = fileDir+os.sep+'bmcFullRaw.txt' 2273 except Exception as e: 2274 print("Failed to write RAW BMC data to file system.") 2275 errorInfo += "Error writing RAW BMC data collection to the file. Exception: {eInfo}\n".format(eInfo=e) 2276 exc_type, exc_obj, exc_tb = sys.exc_info() 2277 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2278 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2279 errorInfo += traceback.format_exc() 2280 2281 output['errors'] = errorInfo 2282 return output 2283 2284def csdCollectAllDumps(host, args, session, fileDir): 2285 """ 2286 Collects all of the bmc dump files and stores them in fileDir 2287 2288 @param host: string, the hostname or IP address of the bmc 2289 @param args: contains additional arguments used by the collectServiceData sub command 2290 @param session: the active session to use 2291 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2292 @param fileDir: string representation of the path to use for putting files created 2293 """ 2294 2295 errorInfo = "===========BMC Dump Collection =============\n" 2296 dumpListCollected = False 2297 output={} 2298 dumpList = {} 2299 try: 2300 d = vars(args) 2301 d['json'] = True 2302 d['dumpSaveLoc'] = fileDir 2303 except Exception as e: 2304 errorInfo += "Failed to set the json flag to True, or failed to set the dumpSave Location \n Exception: {eInfo}\n".format(eInfo=e) 2305 exc_type, exc_obj, exc_tb = sys.exc_info() 2306 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2307 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2308 errorInfo += traceback.format_exc() 2309 2310 print('Collecting bmc dump files') 2311 2312 try: 2313 for i in range(3): 2314 dumpResp = bmcDumpList(host, args, session) 2315 if 'message' in dumpResp: 2316 if 'ok' in dumpResp['message'].lower(): 2317 dumpList = dumpResp['data'] 2318 dumpListCollected = True 2319 break 2320 else: 2321 errorInfo += "Status was not OK when retrieving the list of dumps available. \n Response: \n{resp}\n".format(resp=dumpResp) 2322 else: 2323 errorInfo += "Invalid response received from the BMC while retrieving the list of dumps available.\n {resp}\n".format(resp=dumpResp) 2324 except Exception as e: 2325 errorInfo += "BMC dump list exception: {eInfo}\n".format(eInfo=e) 2326 exc_type, exc_obj, exc_tb = sys.exc_info() 2327 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2328 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2329 errorInfo += traceback.format_exc() 2330 2331 if dumpListCollected: 2332 output['fileList'] = [] 2333 for dump in dumpList: 2334 try: 2335 if '/xyz/openbmc_project/dump/internal/manager' not in dump: 2336 d['dumpNum'] = int(dump.strip().split('/')[-1]) 2337 print('retrieving dump file ' + str(d['dumpNum'])) 2338 filename = bmcDumpRetrieve(host, args, session).split('Saved as ')[-1] 2339 output['fileList'].append(filename) 2340 except Exception as e: 2341 print("Unable to collect dump: {dumpInfo}".format(dumpInfo=dump)) 2342 errorInfo += "Exception collecting a bmc dump {dumpInfo}\n {eInfo}\n".format(dumpInfo=dump, eInfo=e) 2343 exc_type, exc_obj, exc_tb = sys.exc_info() 2344 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2345 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2346 errorInfo += traceback.format_exc() 2347 output['errors'] = errorInfo 2348 return output 2349 2350def collectServiceData(host, args, session): 2351 """ 2352 Collects all data needed for service from the BMC 2353 2354 @param host: string, the hostname or IP address of the bmc 2355 @param args: contains additional arguments used by the collectServiceData sub command 2356 @param session: the active session to use 2357 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2358 """ 2359 2360 global toolVersion 2361 filelist = [] 2362 errorInfo = "" 2363 2364 #get current number of bmc dumps and create a new bmc dump 2365 dumpInitdata = csdDumpInitiate(host, args, session) 2366 if 'dumpFailure' in dumpInitdata: 2367 return 'Collect service data is stopping due to not being able to create a new dump. No service data was collected.' 2368 dumpcount = dumpInitdata['dumpcount'] 2369 errorInfo += dumpInitdata['errors'] 2370 #create the directory to put files 2371 try: 2372 args.silent = True 2373 myDir = tempfile.gettempdir()+os.sep + host + "--" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S") 2374 os.makedirs(myDir) 2375 2376 except Exception as e: 2377 print('Unable to create the temporary directory for data collection. Ensure sufficient privileges to create temporary directory. Aborting.') 2378 exc_type, exc_obj, exc_tb = sys.exc_info() 2379 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 2380 errorInfo += "Exception: Error: {err}, Details: {etype}, {fname}, {lineno}\n".format(err=e, etype=exc_type, fname=fname, lineno=exc_tb.tb_lineno) 2381 errorInfo += traceback.format_exc() 2382 return("Python exception: {eInfo}".format(eInfo = e)) 2383 2384 #Collect Inventory 2385 inventoryData = csdInventory(host, args, session, myDir) 2386 if 'fileLoc' in inventoryData: 2387 filelist.append(inventoryData['fileLoc']) 2388 errorInfo += inventoryData['errors'] 2389 #Read all the sensor and OCC status 2390 sensorData = csdSensors(host,args,session,myDir) 2391 if 'fileLoc' in sensorData: 2392 filelist.append(sensorData['fileLoc']) 2393 errorInfo += sensorData['errors'] 2394 #Collect all of the LEDs status 2395 ledStatus = csdLEDs(host, args, session, myDir) 2396 if 'fileLoc' in ledStatus: 2397 filelist.append(ledStatus['fileLoc']) 2398 errorInfo += ledStatus['errors'] 2399 2400 #Collect the bmc logs 2401 selShort = csdSelShortList(host, args, session, myDir) 2402 if 'fileLoc' in selShort: 2403 filelist.append(selShort['fileLoc']) 2404 errorInfo += selShort['errors'] 2405 2406 parsedSELs = csdParsedSels(host, args, session, myDir) 2407 if 'fileLoc' in parsedSELs: 2408 filelist.append(parsedSELs['fileLoc']) 2409 errorInfo += parsedSELs['errors'] 2410 2411 #collect RAW bmc enumeration 2412 bmcRaw = csdFullEnumeration(host, args, session, myDir) 2413 if 'fileLoc' in bmcRaw: 2414 filelist.append(bmcRaw['fileLoc']) 2415 errorInfo += bmcRaw['errors'] 2416 2417 #wait for new dump to finish being created 2418 waitingForNewDump = True 2419 count = 0; 2420 print("Waiting for new BMC dump to finish being created. Wait time could be up to 5 minutes") 2421 while(waitingForNewDump): 2422 dumpList = bmcDumpList(host, args, session)['data'] 2423 if len(dumpList) > dumpcount: 2424 waitingForNewDump = False 2425 break; 2426 elif(count>150): 2427 print("Timed out waiting for bmc to make a new dump file. Continuing without it.") 2428 break; 2429 else: 2430 time.sleep(2) 2431 count += 1 2432 2433 #collect all of the dump files 2434 getBMCDumps = csdCollectAllDumps(host, args, session, myDir) 2435 if 'fileList' in getBMCDumps: 2436 filelist+= getBMCDumps['fileList'] 2437 errorInfo += getBMCDumps['errors'] 2438 2439 #write the runtime errors to a file 2440 try: 2441 with open(myDir +os.sep+'openbmctoolRuntimeErrors.txt', 'w') as f: 2442 f.write(errorInfo) 2443 print("OpenBMC tool runtime errors collected and stored in " + myDir + os.sep+ "openbmctoolRuntimeErrors.txt") 2444 filelist.append(myDir+os.sep+'openbmctoolRuntimeErrors.txt') 2445 except Exception as e: 2446 print("Failed to write OpenBMC tool runtime errors to file system.") 2447 2448 #create the zip file 2449 try: 2450 filename = myDir.split(tempfile.gettempdir()+os.sep)[-1] + "_" + toolVersion + '_openbmc.zip' 2451 zf = zipfile.ZipFile(myDir+os.sep + filename, 'w') 2452 for myfile in filelist: 2453 zf.write(myfile, os.path.basename(myfile)) 2454 zf.close() 2455 print("Zip file with all collected data created and stored in: {fileInfo}".format(fileInfo=myDir+os.sep+filename)) 2456 except Exception as e: 2457 print("Failed to create zip file with collected information") 2458 return "data collection finished" 2459 2460 2461def healthCheck(host, args, session): 2462 """ 2463 runs a health check on the platform 2464 2465 @param host: string, the hostname or IP address of the bmc 2466 @param args: contains additional arguments used by the bmc sub command 2467 @param session: the active session to use 2468 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2469 """ 2470 #check fru status and get as json to easily work through 2471 d = vars(args) 2472 useJson = d['json'] 2473 d['json'] = True 2474 d['verbose']= False 2475 2476 frus = json.loads(fruStatus(host, args, session)) 2477 2478 hwStatus= "OK" 2479 performanceStatus = "OK" 2480 for key in frus: 2481 if frus[key]["Functional"] == "No" and frus[key]["Present"] == "Yes": 2482 hwStatus= "Degraded" 2483 if("power_supply" in key or "powersupply" in key): 2484 gpuCount =0 2485 for comp in frus: 2486 if "gv100card" in comp: 2487 gpuCount +=1 2488 if gpuCount > 4: 2489 hwStatus = "Critical" 2490 performanceStatus="Degraded" 2491 break; 2492 elif("fan" in key): 2493 hwStatus = "Degraded" 2494 else: 2495 performanceStatus = "Degraded" 2496 if useJson: 2497 output = {"Hardware Status": hwStatus, "Performance": performanceStatus} 2498 output = json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) 2499 else: 2500 output = ("Hardware Status: " + hwStatus + 2501 "\nPerformance: " +performanceStatus ) 2502 2503 2504 #SW407886: Clear the duplicate entries 2505 #collect the dups 2506 d['devdebug'] = False 2507 sels = json.loads(selPrint(host, args, session)) 2508 logNums2Clr = [] 2509 oldestLogNum={"logNum": "bogus" ,"key" : ""} 2510 count = 0 2511 if sels['numAlerts'] > 0: 2512 for key in sels: 2513 if "numAlerts" in key: 2514 continue 2515 try: 2516 if "slave@00:00/00:00:00:06/sbefifo1-dev0/occ1-dev0" in sels[key]['Message']: 2517 count += 1 2518 if count > 1: 2519 #preserve first occurrence 2520 if sels[key]['timestamp'] < sels[oldestLogNum['key']]['timestamp']: 2521 oldestLogNum['key']=key 2522 oldestLogNum['logNum'] = sels[key]['logNum'] 2523 else: 2524 oldestLogNum['key']=key 2525 oldestLogNum['logNum'] = sels[key]['logNum'] 2526 logNums2Clr.append(sels[key]['logNum']) 2527 except KeyError: 2528 continue 2529 if(count >0): 2530 logNums2Clr.remove(oldestLogNum['logNum']) 2531 #delete the dups 2532 if count >1: 2533 data = "{\"data\": [] }" 2534 for logNum in logNums2Clr: 2535 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete" 2536 try: 2537 session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2538 except(requests.exceptions.Timeout): 2539 deleteFailed = True 2540 except(requests.exceptions.ConnectionError) as err: 2541 deleteFailed = True 2542 #End of defect resolve code 2543 d['json'] = useJson 2544 return output 2545 2546 2547 2548def bmc(host, args, session): 2549 """ 2550 handles various bmc level commands, currently bmc rebooting 2551 2552 @param host: string, the hostname or IP address of the bmc 2553 @param args: contains additional arguments used by the bmc sub command 2554 @param session: the active session to use 2555 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2556 """ 2557 if(args.type is not None): 2558 return bmcReset(host, args, session) 2559 if(args.info): 2560 return "Not implemented at this time" 2561 2562 2563 2564def bmcReset(host, args, session): 2565 """ 2566 controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots. 2567 2568 @param host: string, the hostname or IP address of the bmc 2569 @param args: contains additional arguments used by the bmcReset sub command 2570 @param session: the active session to use 2571 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2572 """ 2573 if checkFWactivation(host, args, session): 2574 return ("BMC reset control disabled during firmware activation") 2575 if(args.type == "warm"): 2576 print("\nAttempting to reboot the BMC...:") 2577 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition" 2578 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}' 2579 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2580 return res.text 2581 elif(args.type =="cold"): 2582 print("\nAttempting to reboot the BMC...:") 2583 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition" 2584 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}' 2585 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2586 return res.text 2587 else: 2588 return "invalid command" 2589 2590def gardClear(host, args, session): 2591 """ 2592 clears the gard records from the bmc 2593 2594 @param host: string, the hostname or IP address of the bmc 2595 @param args: contains additional arguments used by the gardClear sub command 2596 @param session: the active session to use 2597 """ 2598 url="https://"+host+"/org/open_power/control/gard/action/Reset" 2599 data = '{"data":[]}' 2600 try: 2601 2602 res = session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2603 if res.status_code == 404: 2604 return "Command not supported by this firmware version" 2605 else: 2606 return res.text 2607 except(requests.exceptions.Timeout): 2608 return connectionErrHandler(args.json, "Timeout", None) 2609 except(requests.exceptions.ConnectionError) as err: 2610 return connectionErrHandler(args.json, "ConnectionError", err) 2611 2612def activateFWImage(host, args, session): 2613 """ 2614 activates a firmware image on the bmc 2615 2616 @param host: string, the hostname or IP address of the bmc 2617 @param args: contains additional arguments used by the fwflash sub command 2618 @param session: the active session to use 2619 @param fwID: the unique ID of the fw image to activate 2620 """ 2621 fwID = args.imageID 2622 2623 #determine the existing versions 2624 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2625 try: 2626 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2627 except(requests.exceptions.Timeout): 2628 return connectionErrHandler(args.json, "Timeout", None) 2629 except(requests.exceptions.ConnectionError) as err: 2630 return connectionErrHandler(args.json, "ConnectionError", err) 2631 existingSoftware = json.loads(resp.text)['data'] 2632 altVersionID = '' 2633 versionType = '' 2634 imageKey = '/xyz/openbmc_project/software/'+fwID 2635 if imageKey in existingSoftware: 2636 versionType = existingSoftware[imageKey]['Purpose'] 2637 for key in existingSoftware: 2638 if imageKey == key: 2639 continue 2640 if 'Purpose' in existingSoftware[key]: 2641 if versionType == existingSoftware[key]['Purpose']: 2642 altVersionID = key.split('/')[-1] 2643 2644 2645 2646 2647 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/attr/Priority" 2648 url1="https://"+host+"/xyz/openbmc_project/software/"+ altVersionID + "/attr/Priority" 2649 data = "{\"data\": 0}" 2650 data1 = "{\"data\": 1 }" 2651 try: 2652 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2653 resp1 = session.put(url1, headers=jsonHeader, data=data1, verify=False, timeout=baseTimeout) 2654 except(requests.exceptions.Timeout): 2655 return connectionErrHandler(args.json, "Timeout", None) 2656 except(requests.exceptions.ConnectionError) as err: 2657 return connectionErrHandler(args.json, "ConnectionError", err) 2658 if(not args.json): 2659 if resp.status_code == 200 and resp1.status_code == 200: 2660 return 'Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. ' 2661 else: 2662 return "Firmware activation failed." 2663 else: 2664 return resp.text + resp1.text 2665 2666def activateStatus(host, args, session): 2667 if checkFWactivation(host, args, session): 2668 return("Firmware is currently being activated. Do not reboot the BMC or start the Host OS") 2669 else: 2670 return("No firmware activations are pending") 2671 2672def extractFWimage(path, imageType): 2673 """ 2674 extracts the bmc image and returns information about the package 2675 2676 @param path: the path and file name of the firmware image 2677 @param imageType: The type of image the user is trying to flash. Host or BMC 2678 @return: the image id associated with the package. returns an empty string on error. 2679 """ 2680 f = tempfile.TemporaryFile() 2681 tmpDir = tempfile.gettempdir() 2682 newImageID = "" 2683 if os.path.exists(path): 2684 try: 2685 imageFile = tarfile.open(path,'r') 2686 contents = imageFile.getmembers() 2687 for tf in contents: 2688 if 'MANIFEST' in tf.name: 2689 imageFile.extract(tf.name, path=tmpDir) 2690 with open(tempfile.gettempdir() +os.sep+ tf.name, 'r') as imageInfo: 2691 for line in imageInfo: 2692 if 'purpose' in line: 2693 purpose = line.split('=')[1] 2694 if imageType not in purpose.split('.')[-1]: 2695 print('The specified image is not for ' + imageType) 2696 print('Please try again with the image for ' + imageType) 2697 return "" 2698 if 'version' == line.split('=')[0]: 2699 version = line.split('=')[1].strip().encode('utf-8') 2700 m = hashlib.sha512() 2701 m.update(version) 2702 newImageID = m.hexdigest()[:8] 2703 break 2704 try: 2705 os.remove(tempfile.gettempdir() +os.sep+ tf.name) 2706 except OSError: 2707 pass 2708 return newImageID 2709 except tarfile.ExtractError as e: 2710 print('Unable to extract information from the firmware file.') 2711 print('Ensure you have write access to the directory: ' + tmpDir) 2712 return newImageID 2713 except tarfile.TarError as e: 2714 print('This is not a valid firmware file.') 2715 return newImageID 2716 print("This is not a valid firmware file.") 2717 return newImageID 2718 else: 2719 print('The filename and path provided are not valid.') 2720 return newImageID 2721 2722def getAllFWImageIDs(fwInvDict): 2723 """ 2724 gets a list of all the firmware image IDs 2725 2726 @param fwInvDict: the dictionary to search for FW image IDs 2727 @return: list containing string representation of the found image ids 2728 """ 2729 idList = [] 2730 for key in fwInvDict: 2731 if 'Version' in fwInvDict[key]: 2732 idList.append(key.split('/')[-1]) 2733 return idList 2734 2735def fwFlash(host, args, session): 2736 """ 2737 updates the bmc firmware and pnor firmware 2738 2739 @param host: string, the hostname or IP address of the bmc 2740 @param args: contains additional arguments used by the fwflash sub command 2741 @param session: the active session to use 2742 """ 2743 d = vars(args) 2744 if(args.type == 'bmc'): 2745 purp = 'BMC' 2746 else: 2747 purp = 'Host' 2748 2749 #check power state of the machine. No concurrent FW updates allowed 2750 d['powcmd'] = 'status' 2751 powerstate = chassisPower(host, args, session) 2752 if 'Chassis Power State: On' in powerstate: 2753 return("Aborting firmware update. Host is powered on. Please turn off the host and try again.") 2754 2755 #determine the existing images on the bmc 2756 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2757 try: 2758 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2759 except(requests.exceptions.Timeout): 2760 return connectionErrHandler(args.json, "Timeout", None) 2761 except(requests.exceptions.ConnectionError) as err: 2762 return connectionErrHandler(args.json, "ConnectionError", err) 2763 oldsoftware = json.loads(resp.text)['data'] 2764 2765 #Extract the tar and get information from the manifest file 2766 newversionID = extractFWimage(args.fileloc, purp) 2767 if newversionID == "": 2768 return "Unable to verify FW image." 2769 2770 2771 #check if the new image is already on the bmc 2772 if newversionID not in getAllFWImageIDs(oldsoftware): 2773 2774 #upload the file 2775 httpHeader = {'Content-Type':'application/octet-stream'} 2776 httpHeader.update(xAuthHeader) 2777 url="https://"+host+"/upload/image" 2778 data=open(args.fileloc,'rb').read() 2779 print("Uploading file to BMC") 2780 try: 2781 resp = session.post(url, headers=httpHeader, data=data, verify=False) 2782 except(requests.exceptions.Timeout): 2783 return connectionErrHandler(args.json, "Timeout", None) 2784 except(requests.exceptions.ConnectionError) as err: 2785 return connectionErrHandler(args.json, "ConnectionError", err) 2786 if resp.status_code != 200: 2787 return "Failed to upload the file to the bmc" 2788 else: 2789 print("Upload complete.") 2790 2791 #verify bmc processed the image 2792 software ={} 2793 for i in range(0, 5): 2794 url="https://"+host+"/xyz/openbmc_project/software/enumerate" 2795 try: 2796 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2797 except(requests.exceptions.Timeout): 2798 return connectionErrHandler(args.json, "Timeout", None) 2799 except(requests.exceptions.ConnectionError) as err: 2800 return connectionErrHandler(args.json, "ConnectionError", err) 2801 software = json.loads(resp.text)['data'] 2802 #check if bmc is done processing the new image 2803 if (newversionID in getAllFWImageIDs(software)): 2804 break 2805 else: 2806 time.sleep(15) 2807 2808 #activate the new image 2809 print("Activating new image: "+newversionID) 2810 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID + "/attr/RequestedActivation" 2811 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}' 2812 try: 2813 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2814 except(requests.exceptions.Timeout): 2815 return connectionErrHandler(args.json, "Timeout", None) 2816 except(requests.exceptions.ConnectionError) as err: 2817 return connectionErrHandler(args.json, "ConnectionError", err) 2818 2819 #wait for the activation to complete, timeout after ~1 hour 2820 i=0 2821 while i < 360: 2822 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID 2823 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}' 2824 try: 2825 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2826 except(requests.exceptions.Timeout): 2827 return connectionErrHandler(args.json, "Timeout", None) 2828 except(requests.exceptions.ConnectionError) as err: 2829 return connectionErrHandler(args.json, "ConnectionError", err) 2830 fwInfo = json.loads(resp.text)['data'] 2831 if 'Activating' not in fwInfo['Activation'] and 'Activating' not in fwInfo['RequestedActivation']: 2832 print('') 2833 break 2834 else: 2835 sys.stdout.write('.') 2836 sys.stdout.flush() 2837 time.sleep(10) #check every 10 seconds 2838 return "Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. " 2839 else: 2840 print("This image has been found on the bmc. Activating image: " + newversionID) 2841 2842 d['imageID'] = newversionID 2843 return activateFWImage(host, args, session) 2844 2845def getFWInventoryAttributes(rawFWInvItem, ID): 2846 """ 2847 gets and lists all of the firmware in the system. 2848 2849 @return: returns a dictionary containing the image attributes 2850 """ 2851 reqActivation = rawFWInvItem["RequestedActivation"].split('.')[-1] 2852 pendingActivation = "" 2853 if reqActivation == "None": 2854 pendingActivation = "No" 2855 else: 2856 pendingActivation = "Yes" 2857 firmwareAttr = {ID: { 2858 "Purpose": rawFWInvItem["Purpose"].split('.')[-1], 2859 "Version": rawFWInvItem["Version"], 2860 "RequestedActivation": pendingActivation, 2861 "ID": ID}} 2862 2863 if "ExtendedVersion" in rawFWInvItem: 2864 firmwareAttr[ID]['ExtendedVersion'] = rawFWInvItem['ExtendedVersion'].split(',') 2865 else: 2866 firmwareAttr[ID]['ExtendedVersion'] = "" 2867 return firmwareAttr 2868 2869def parseFWdata(firmwareDict): 2870 """ 2871 creates a dictionary with parsed firmware data 2872 2873 @return: returns a dictionary containing the image attributes 2874 """ 2875 firmwareInfoDict = {"Functional": {}, "Activated":{}, "NeedsActivated":{}} 2876 for key in firmwareDict['data']: 2877 #check for valid endpoint 2878 if "Purpose" in firmwareDict['data'][key]: 2879 id = key.split('/')[-1] 2880 if firmwareDict['data'][key]['Activation'].split('.')[-1] == "Active": 2881 fwActivated = True 2882 else: 2883 fwActivated = False 2884 if 'Priority' in firmwareDict['data'][key]: 2885 if firmwareDict['data'][key]['Priority'] == 0: 2886 firmwareInfoDict['Functional'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2887 elif firmwareDict['data'][key]['Priority'] >= 0 and fwActivated: 2888 firmwareInfoDict['Activated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2889 else: 2890 firmwareInfoDict['NeedsActivated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2891 else: 2892 firmwareInfoDict['NeedsActivated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id)) 2893 emptySections = [] 2894 for key in firmwareInfoDict: 2895 if len(firmwareInfoDict[key])<=0: 2896 emptySections.append(key) 2897 for key in emptySections: 2898 del firmwareInfoDict[key] 2899 return firmwareInfoDict 2900 2901def displayFWInvenory(firmwareInfoDict, args): 2902 """ 2903 gets and lists all of the firmware in the system. 2904 2905 @return: returns a string containing all of the firmware information 2906 """ 2907 output = "" 2908 if not args.json: 2909 for key in firmwareInfoDict: 2910 for subkey in firmwareInfoDict[key]: 2911 firmwareInfoDict[key][subkey]['ExtendedVersion'] = str(firmwareInfoDict[key][subkey]['ExtendedVersion']) 2912 if not args.verbose: 2913 output = "---Running Images---\n" 2914 colNames = ["Purpose", "Version", "ID"] 2915 keylist = ["Purpose", "Version", "ID"] 2916 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"]) 2917 if "Activated" in firmwareInfoDict: 2918 output += "\n---Available Images---\n" 2919 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"]) 2920 if "NeedsActivated" in firmwareInfoDict: 2921 output += "\n---Needs Activated Images---\n" 2922 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"]) 2923 2924 else: 2925 output = "---Running Images---\n" 2926 colNames = ["Purpose", "Version", "ID", "Pending Activation", "Extended Version"] 2927 keylist = ["Purpose", "Version", "ID", "RequestedActivation", "ExtendedVersion"] 2928 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"]) 2929 if "Activated" in firmwareInfoDict: 2930 output += "\n---Available Images---\n" 2931 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"]) 2932 if "NeedsActivated" in firmwareInfoDict: 2933 output += "\n---Needs Activated Images---\n" 2934 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"]) 2935 return output 2936 else: 2937 return str(json.dumps(firmwareInfoDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 2938 2939def firmwareList(host, args, session): 2940 """ 2941 gets and lists all of the firmware in the system. 2942 2943 @return: returns a string containing all of the firmware information 2944 """ 2945 url="https://{hostname}/xyz/openbmc_project/software/enumerate".format(hostname=host) 2946 try: 2947 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 2948 except(requests.exceptions.Timeout): 2949 return(connectionErrHandler(args.json, "Timeout", None)) 2950 firmwareDict = json.loads(res.text) 2951 2952 #sort the received information 2953 firmwareInfoDict = parseFWdata(firmwareDict) 2954 2955 #display the information 2956 return displayFWInvenory(firmwareInfoDict, args) 2957 2958 2959def deleteFWVersion(host, args, session): 2960 """ 2961 deletes a firmware version on the BMC 2962 2963 @param host: string, the hostname or IP address of the BMC 2964 @param args: contains additional arguments used by the fwflash sub command 2965 @param session: the active session to use 2966 @param fwID: the unique ID of the fw version to delete 2967 """ 2968 fwID = args.versionID 2969 2970 print("Deleting version: "+fwID) 2971 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/action/Delete" 2972 data = "{\"data\": [] }" 2973 2974 try: 2975 res = session.post(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 2976 except(requests.exceptions.Timeout): 2977 return(connectionErrHandler(args.json, "Timeout", None)) 2978 if res.status_code == 200: 2979 return ('The firmware version has been deleted') 2980 else: 2981 return ('Unable to delete the specified firmware version') 2982 2983 2984def restLogging(host, args, session): 2985 """ 2986 Called by the logging function. Turns REST API logging on/off. 2987 2988 @param host: string, the hostname or IP address of the bmc 2989 @param args: contains additional arguments used by the logging sub command 2990 @param session: the active session to use 2991 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 2992 """ 2993 url="https://"+host+"/xyz/openbmc_project/logging/rest_api_logs/attr/Enabled" 2994 2995 if(args.rest_logging == 'on'): 2996 data = '{"data": 1}' 2997 elif(args.rest_logging == 'off'): 2998 data = '{"data": 0}' 2999 else: 3000 return "Invalid logging rest_api command" 3001 3002 try: 3003 res = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3004 except(requests.exceptions.Timeout): 3005 return(connectionErrHandler(args.json, "Timeout", None)) 3006 return res.text 3007 3008 3009def remoteLogging(host, args, session): 3010 """ 3011 Called by the logging function. View config information for/disable remote logging (rsyslog). 3012 3013 @param host: string, the hostname or IP address of the bmc 3014 @param args: contains additional arguments used by the logging sub command 3015 @param session: the active session to use 3016 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 3017 """ 3018 3019 url="https://"+host+"/xyz/openbmc_project/logging/config/remote" 3020 3021 try: 3022 if(args.remote_logging == 'view'): 3023 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3024 elif(args.remote_logging == 'disable'): 3025 res = session.put(url + '/attr/Port', headers=jsonHeader, json = {"data": 0}, verify=False, timeout=baseTimeout) 3026 res = session.put(url + '/attr/Address', headers=jsonHeader, json = {"data": ""}, verify=False, timeout=baseTimeout) 3027 else: 3028 return "Invalid logging remote_logging command" 3029 except(requests.exceptions.Timeout): 3030 return(connectionErrHandler(args.json, "Timeout", None)) 3031 return res.text 3032 3033 3034def remoteLoggingConfig(host, args, session): 3035 """ 3036 Called by the logging function. Configures remote logging (rsyslog). 3037 3038 @param host: string, the hostname or IP address of the bmc 3039 @param args: contains additional arguments used by the logging sub command 3040 @param session: the active session to use 3041 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption 3042 """ 3043 3044 url="https://"+host+"/xyz/openbmc_project/logging/config/remote" 3045 3046 try: 3047 res = session.put(url + '/attr/Port', headers=jsonHeader, json = {"data": args.port}, verify=False, timeout=baseTimeout) 3048 res = session.put(url + '/attr/Address', headers=jsonHeader, json = {"data": args.address}, verify=False, timeout=baseTimeout) 3049 except(requests.exceptions.Timeout): 3050 return(connectionErrHandler(args.json, "Timeout", None)) 3051 return res.text 3052 3053def redfishSupportPresent(host, session): 3054 url = "https://" + host + "/redfish/v1" 3055 try: 3056 resp = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3057 except(requests.exceptions.Timeout): 3058 return False 3059 except(requests.exceptions.ConnectionError) as err: 3060 return False 3061 if resp.status_code != 200: 3062 return False 3063 else: 3064 return True 3065 3066def certificateUpdate(host, args, session): 3067 """ 3068 Called by certificate management function. update server/client/authority certificates 3069 Example: 3070 certificate update server https -f cert.pem 3071 certificate update authority ldap -f Root-CA.pem 3072 certificate update client ldap -f cert.pem 3073 @param host: string, the hostname or IP address of the bmc 3074 @param args: contains additional arguments used by the certificate update sub command 3075 @param session: the active session to use 3076 """ 3077 httpHeader = {'Content-Type': 'application/octet-stream'} 3078 httpHeader.update(xAuthHeader) 3079 data = open(args.fileloc, 'r').read() 3080 try: 3081 if redfishSupportPresent(host, session): 3082 if(args.type.lower() == 'server' and args.service.lower() != "https"): 3083 return "Invalid service type" 3084 if(args.type.lower() == 'client' and args.service.lower() != "ldap"): 3085 return "Invalid service type" 3086 if(args.type.lower() == 'authority' and args.service.lower() != "ldap"): 3087 return "Invalid service type" 3088 url = ""; 3089 if(args.type.lower() == 'server'): 3090 url = "https://" + host + \ 3091 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates" 3092 elif(args.type.lower() == 'client'): 3093 url = "https://" + host + \ 3094 "/redfish/v1/AccountService/LDAP/Certificates" 3095 elif(args.type.lower() == 'authority'): 3096 url = "https://" + host + \ 3097 "/redfish/v1/Managers/bmc/Truststore/Certificates" 3098 else: 3099 return "Unsupported certificate type" 3100 resp = session.post(url, headers=httpHeader, data=data, 3101 verify=False) 3102 else: 3103 url = "https://" + host + "/xyz/openbmc_project/certs/" + \ 3104 args.type.lower() + "/" + args.service.lower() 3105 resp = session.put(url, headers=httpHeader, data=data, verify=False) 3106 except(requests.exceptions.Timeout): 3107 return(connectionErrHandler(args.json, "Timeout", None)) 3108 except(requests.exceptions.ConnectionError) as err: 3109 return connectionErrHandler(args.json, "ConnectionError", err) 3110 if resp.status_code != 200: 3111 print(resp.text) 3112 return "Failed to update the certificate" 3113 else: 3114 print("Update complete.") 3115 3116def certificateDelete(host, args, session): 3117 """ 3118 Called by certificate management function to delete certificate 3119 Example: 3120 certificate delete server https 3121 certificate delete authority ldap 3122 certificate delete client ldap 3123 @param host: string, the hostname or IP address of the bmc 3124 @param args: contains additional arguments used by the certificate delete sub command 3125 @param session: the active session to use 3126 """ 3127 if redfishSupportPresent(host, session): 3128 return "Not supported, please use certificate replace instead"; 3129 httpHeader = {'Content-Type': 'multipart/form-data'} 3130 httpHeader.update(xAuthHeader) 3131 url = "https://" + host + "/xyz/openbmc_project/certs/" + args.type.lower() + "/" + args.service.lower() 3132 print("Deleting certificate url=" + url) 3133 try: 3134 resp = session.delete(url, headers=httpHeader) 3135 except(requests.exceptions.Timeout): 3136 return(connectionErrHandler(args.json, "Timeout", None)) 3137 except(requests.exceptions.ConnectionError) as err: 3138 return connectionErrHandler(args.json, "ConnectionError", err) 3139 if resp.status_code != 200: 3140 print(resp.text) 3141 return "Failed to delete the certificate" 3142 else: 3143 print("Delete complete.") 3144 3145def certificateReplace(host, args, session): 3146 """ 3147 Called by certificate management function. replace server/client/ 3148 authority certificates 3149 Example: 3150 certificate replace server https -f cert.pem 3151 certificate replace authority ldap -f Root-CA.pem 3152 certificate replace client ldap -f cert.pem 3153 @param host: string, the hostname or IP address of the bmc 3154 @param args: contains additional arguments used by the certificate 3155 replace sub command 3156 @param session: the active session to use 3157 """ 3158 cert = open(args.fileloc, 'r').read() 3159 try: 3160 if redfishSupportPresent(host, session): 3161 httpHeader = {'Content-Type': 'application/json'} 3162 httpHeader.update(xAuthHeader) 3163 url = ""; 3164 if(args.type.lower() == 'server' and args.service.lower() != "https"): 3165 return "Invalid service type" 3166 if(args.type.lower() == 'client' and args.service.lower() != "ldap"): 3167 return "Invalid service type" 3168 if(args.type.lower() == 'authority' and args.service.lower() != "ldap"): 3169 return "Invalid service type" 3170 if(args.type.lower() == 'server'): 3171 url = "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1" 3172 elif(args.type.lower() == 'client'): 3173 url = "/redfish/v1/AccountService/LDAP/Certificates/1" 3174 elif(args.type.lower() == 'authority'): 3175 url = "/redfish/v1/Managers/bmc/Truststore/Certificates/1" 3176 replaceUrl = "https://" + host + \ 3177 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate" 3178 data ={"CertificateUri":{"@odata.id":url}, "CertificateType":"PEM", 3179 "CertificateString":cert} 3180 resp = session.post(replaceUrl, headers=httpHeader, json=data, verify=False) 3181 else: 3182 httpHeader = {'Content-Type': 'application/octet-stream'} 3183 httpHeader.update(xAuthHeader) 3184 url = "https://" + host + "/xyz/openbmc_project/certs/" + \ 3185 args.type.lower() + "/" + args.service.lower() 3186 resp = session.delete(url, headers=httpHeader) 3187 resp = session.put(url, headers=httpHeader, data=cert, verify=False) 3188 except(requests.exceptions.Timeout): 3189 return(connectionErrHandler(args.json, "Timeout", None)) 3190 except(requests.exceptions.ConnectionError) as err: 3191 return connectionErrHandler(args.json, "ConnectionError", err) 3192 if resp.status_code != 200: 3193 print(resp.text) 3194 return "Failed to replace the certificate" 3195 else: 3196 print("Replace complete.") 3197 return resp.text 3198 3199def certificateDisplay(host, args, session): 3200 """ 3201 Called by certificate management function. display server/client/ 3202 authority certificates 3203 Example: 3204 certificate display server 3205 certificate display authority 3206 certificate display client 3207 @param host: string, the hostname or IP address of the bmc 3208 @param args: contains additional arguments used by the certificate 3209 display sub command 3210 @param session: the active session to use 3211 """ 3212 if not redfishSupportPresent(host, session): 3213 return "Not supported"; 3214 3215 httpHeader = {'Content-Type': 'application/octet-stream'} 3216 httpHeader.update(xAuthHeader) 3217 if(args.type.lower() == 'server'): 3218 url = "https://" + host + \ 3219 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1" 3220 elif(args.type.lower() == 'client'): 3221 url = "https://" + host + \ 3222 "/redfish/v1/AccountService/LDAP/Certificates/1" 3223 elif(args.type.lower() == 'authority'): 3224 url = "https://" + host + \ 3225 "/redfish/v1/Managers/bmc/Truststore/Certificates/1" 3226 try: 3227 resp = session.get(url, headers=httpHeader, verify=False) 3228 except(requests.exceptions.Timeout): 3229 return(connectionErrHandler(args.json, "Timeout", None)) 3230 except(requests.exceptions.ConnectionError) as err: 3231 return connectionErrHandler(args.json, "ConnectionError", err) 3232 if resp.status_code != 200: 3233 print(resp.text) 3234 return "Failed to display the certificate" 3235 else: 3236 print("Display complete.") 3237 return resp.text 3238 3239def certificateList(host, args, session): 3240 """ 3241 Called by certificate management function. 3242 Example: 3243 certificate list 3244 @param host: string, the hostname or IP address of the bmc 3245 @param args: contains additional arguments used by the certificate 3246 list sub command 3247 @param session: the active session to use 3248 """ 3249 if not redfishSupportPresent(host, session): 3250 return "Not supported"; 3251 3252 httpHeader = {'Content-Type': 'application/octet-stream'} 3253 httpHeader.update(xAuthHeader) 3254 url = "https://" + host + \ 3255 "/redfish/v1/CertificateService/CertificateLocations/" 3256 try: 3257 resp = session.get(url, headers=httpHeader, verify=False) 3258 except(requests.exceptions.Timeout): 3259 return(connectionErrHandler(args.json, "Timeout", None)) 3260 except(requests.exceptions.ConnectionError) as err: 3261 return connectionErrHandler(args.json, "ConnectionError", err) 3262 if resp.status_code != 200: 3263 print(resp.text) 3264 return "Failed to list certificates" 3265 else: 3266 print("List certificates complete.") 3267 return resp.text 3268 3269def certificateGenerateCSR(host, args, session): 3270 """ 3271 Called by certificate management function. Generate CSR for server/ 3272 client certificates 3273 Example: 3274 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 3275 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 3276 @param host: string, the hostname or IP address of the bmc 3277 @param args: contains additional arguments used by the certificate replace sub command 3278 @param session: the active session to use 3279 """ 3280 if not redfishSupportPresent(host, session): 3281 return "Not supported"; 3282 3283 httpHeader = {'Content-Type': 'application/octet-stream'} 3284 httpHeader.update(xAuthHeader) 3285 url = ""; 3286 if(args.type.lower() == 'server'): 3287 url = "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" 3288 usage_list = ["ServerAuthentication"] 3289 elif(args.type.lower() == 'client'): 3290 url = "/redfish/v1/AccountService/LDAP/Certificates/" 3291 usage_list = ["ClientAuthentication"] 3292 elif(args.type.lower() == 'authority'): 3293 url = "/redfish/v1/Managers/bmc/Truststore/Certificates/" 3294 print("Generating CSR url=" + url) 3295 generateCSRUrl = "https://" + host + \ 3296 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR" 3297 try: 3298 alt_name_list = args.alternativeNames.split(",") 3299 data ={"CertificateCollection":{"@odata.id":url}, 3300 "CommonName":args.commonName, "City":args.city, 3301 "Country":args.country, "Organization":args.organization, 3302 "OrganizationalUnit":args.organizationUnit, "State":args.state, 3303 "KeyPairAlgorithm":args.keyPairAlgorithm, "KeyCurveId":args.keyCurveId, 3304 "AlternativeNames":alt_name_list, "ContactPerson":args.contactPerson, 3305 "Email":args.email, "GivenName":args.givenname, "Initials":args.initials, 3306 "KeyUsage":usage_list, "Surname":args.surname, 3307 "UnstructuredName":args.unstructuredname} 3308 resp = session.post(generateCSRUrl, headers=httpHeader, 3309 json=data, verify=False) 3310 except(requests.exceptions.Timeout): 3311 return(connectionErrHandler(args.json, "Timeout", None)) 3312 except(requests.exceptions.ConnectionError) as err: 3313 return connectionErrHandler(args.json, "ConnectionError", err) 3314 if resp.status_code != 200: 3315 print(resp.text) 3316 return "Failed to generate CSR" 3317 else: 3318 print("GenerateCSR complete.") 3319 return resp.text 3320 3321def enableLDAPConfig(host, args, session): 3322 """ 3323 Called by the ldap function. Configures LDAP. 3324 3325 @param host: string, the hostname or IP address of the bmc 3326 @param args: contains additional arguments used by the ldap subcommand 3327 @param session: the active session to use 3328 @param args.json: boolean, if this flag is set to true, the output will 3329 be provided in json format for programmatic consumption 3330 """ 3331 3332 if(isRedfishSupport): 3333 return enableLDAP(host, args, session) 3334 else: 3335 return enableLegacyLDAP(host, args, session) 3336 3337def enableLegacyLDAP(host, args, session): 3338 """ 3339 Called by the ldap function. Configures LDAP on Lagecy systems. 3340 3341 @param host: string, the hostname or IP address of the bmc 3342 @param args: contains additional arguments used by the ldap subcommand 3343 @param session: the active session to use 3344 @param args.json: boolean, if this flag is set to true, the output will 3345 be provided in json format for programmatic consumption 3346 """ 3347 3348 url='https://'+host+'/xyz/openbmc_project/user/ldap/action/CreateConfig' 3349 scope = { 3350 'sub' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.sub', 3351 'one' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.one', 3352 'base': 'xyz.openbmc_project.User.Ldap.Create.SearchScope.base' 3353 } 3354 3355 serverType = { 3356 'ActiveDirectory' : 'xyz.openbmc_project.User.Ldap.Create.Type.ActiveDirectory', 3357 'OpenLDAP' : 'xyz.openbmc_project.User.Ldap.Create.Type.OpenLdap' 3358 } 3359 3360 data = {"data": [args.uri, args.bindDN, args.baseDN, args.bindPassword, scope[args.scope], serverType[args.serverType]]} 3361 3362 try: 3363 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3364 except(requests.exceptions.Timeout): 3365 return(connectionErrHandler(args.json, "Timeout", None)) 3366 except(requests.exceptions.ConnectionError) as err: 3367 return connectionErrHandler(args.json, "ConnectionError", err) 3368 3369 return res.text 3370 3371def enableLDAP(host, args, session): 3372 """ 3373 Called by the ldap function. Configures LDAP for systems with latest user-manager design changes 3374 3375 @param host: string, the hostname or IP address of the bmc 3376 @param args: contains additional arguments used by the ldap subcommand 3377 @param session: the active session to use 3378 @param args.json: boolean, if this flag is set to true, the output will 3379 be provided in json format for programmatic consumption 3380 """ 3381 3382 scope = { 3383 'sub' : 'xyz.openbmc_project.User.Ldap.Config.SearchScope.sub', 3384 'one' : 'xyz.openbmc_project.User.Ldap.Config.SearchScope.one', 3385 'base': 'xyz.openbmc_project.User.Ldap.Config.SearchScope.base' 3386 } 3387 3388 serverType = { 3389 'ActiveDirectory' : 'xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory', 3390 'OpenLDAP' : 'xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap' 3391 } 3392 3393 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 3394 3395 serverTypeEnabled = getLDAPTypeEnabled(host,session) 3396 serverTypeToBeEnabled = args.serverType 3397 3398 #If the given LDAP type is already enabled, then return 3399 if (serverTypeToBeEnabled == serverTypeEnabled): 3400 return("Server type " + serverTypeToBeEnabled + " is already enabled...") 3401 3402 try: 3403 3404 # Copy the role map from the currently enabled LDAP server type 3405 # to the newly enabled server type 3406 # Disable the currently enabled LDAP server type. Unless 3407 # it is disabled, we cannot enable a new LDAP server type 3408 if (serverTypeEnabled is not None): 3409 3410 if (serverTypeToBeEnabled != serverTypeEnabled): 3411 res = syncRoleMap(host,args,session,serverTypeEnabled,serverTypeToBeEnabled) 3412 3413 data = "{\"data\": 0 }" 3414 res = session.put(url + serverTypeMap[serverTypeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3415 3416 data = {"data": args.baseDN} 3417 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBaseDN', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3418 if (res.status_code != requests.codes.ok): 3419 print("Updates to the property LDAPBaseDN failed...") 3420 return(res.text) 3421 3422 data = {"data": args.bindDN} 3423 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBindDN', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3424 if (res.status_code != requests.codes.ok): 3425 print("Updates to the property LDAPBindDN failed...") 3426 return(res.text) 3427 3428 data = {"data": args.bindPassword} 3429 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPBindDNPassword', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3430 if (res.status_code != requests.codes.ok): 3431 print("Updates to the property LDAPBindDNPassword failed...") 3432 return(res.text) 3433 3434 data = {"data": scope[args.scope]} 3435 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPSearchScope', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3436 if (res.status_code != requests.codes.ok): 3437 print("Updates to the property LDAPSearchScope failed...") 3438 return(res.text) 3439 3440 data = {"data": args.uri} 3441 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/LDAPServerURI', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3442 if (res.status_code != requests.codes.ok): 3443 print("Updates to the property LDAPServerURI failed...") 3444 return(res.text) 3445 3446 data = {"data": args.groupAttrName} 3447 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/GroupNameAttribute', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3448 if (res.status_code != requests.codes.ok): 3449 print("Updates to the property GroupNameAttribute failed...") 3450 return(res.text) 3451 3452 data = {"data": args.userAttrName} 3453 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/UserNameAttribute', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3454 if (res.status_code != requests.codes.ok): 3455 print("Updates to the property UserNameAttribute failed...") 3456 return(res.text) 3457 3458 #After updating the properties, enable the new server type 3459 data = "{\"data\": 1 }" 3460 res = session.put(url + serverTypeMap[serverTypeToBeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3461 3462 except(requests.exceptions.Timeout): 3463 return(connectionErrHandler(args.json, "Timeout", None)) 3464 except(requests.exceptions.ConnectionError) as err: 3465 return connectionErrHandler(args.json, "ConnectionError", err) 3466 return res.text 3467 3468def disableLDAP(host, args, session): 3469 """ 3470 Called by the ldap function. Deletes the LDAP Configuration. 3471 3472 @param host: string, the hostname or IP address of the bmc 3473 @param args: contains additional arguments used by the ldap subcommand 3474 @param session: the active session to use 3475 @param args.json: boolean, if this flag is set to true, the output 3476 will be provided in json format for programmatic consumption 3477 """ 3478 3479 try: 3480 if (isRedfishSupport) : 3481 3482 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 3483 3484 serverTypeEnabled = getLDAPTypeEnabled(host,session) 3485 3486 if (serverTypeEnabled is not None): 3487 #To keep the role map in sync, 3488 #If the server type being disabled has role map, then 3489 # - copy the role map to the other server type(s) 3490 for serverType in serverTypeMap.keys(): 3491 if (serverType != serverTypeEnabled): 3492 res = syncRoleMap(host,args,session,serverTypeEnabled,serverType) 3493 3494 #Disable the currently enabled LDAP server type 3495 data = "{\"data\": 0 }" 3496 res = session.put(url + serverTypeMap[serverTypeEnabled] + '/attr/Enabled', headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 3497 3498 else: 3499 return("LDAP server has not been enabled...") 3500 3501 else : 3502 url='https://'+host+'/xyz/openbmc_project/user/ldap/config/action/delete' 3503 data = {"data": []} 3504 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 3505 3506 except(requests.exceptions.Timeout): 3507 return(connectionErrHandler(args.json, "Timeout", None)) 3508 except(requests.exceptions.ConnectionError) as err: 3509 return connectionErrHandler(args.json, "ConnectionError", err) 3510 3511 return res.text 3512 3513def enableDHCP(host, args, session): 3514 3515 """ 3516 Called by the network function. Enables DHCP. 3517 3518 @param host: string, the hostname or IP address of the bmc 3519 @param args: contains additional arguments used by the ldap subcommand 3520 args.json: boolean, if this flag is set to true, the output 3521 will be provided in json format for programmatic consumption 3522 @param session: the active session to use 3523 """ 3524 3525 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3526 "/attr/DHCPEnabled" 3527 data = "{\"data\": 1 }" 3528 try: 3529 res = session.put(url, headers=jsonHeader, data=data, verify=False, 3530 timeout=baseTimeout) 3531 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 3540 return res.text 3541 3542 3543def disableDHCP(host, args, session): 3544 """ 3545 Called by the network function. Disables DHCP. 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/"+args.Interface+\ 3555 "/attr/DHCPEnabled" 3556 data = "{\"data\": 0 }" 3557 try: 3558 res = session.put(url, headers=jsonHeader, data=data, verify=False, 3559 timeout=baseTimeout) 3560 except(requests.exceptions.Timeout): 3561 return(connectionErrHandler(args.json, "Timeout", None)) 3562 except(requests.exceptions.ConnectionError) as err: 3563 return connectionErrHandler(args.json, "ConnectionError", err) 3564 if res.status_code == 403: 3565 return "The specified Interface"+"("+args.Interface+")"+\ 3566 " doesn't exist" 3567 return res.text 3568 3569 3570def getHostname(host, args, session): 3571 3572 """ 3573 Called by the network function. Prints out the Hostname. 3574 3575 @param host: string, the hostname or IP address of the bmc 3576 @param args: contains additional arguments used by the ldap subcommand 3577 args.json: boolean, if this flag is set to true, the output 3578 will be provided in json format for programmatic consumption 3579 @param session: the active session to use 3580 """ 3581 3582 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/HostName" 3583 3584 try: 3585 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3586 except(requests.exceptions.Timeout): 3587 return(connectionErrHandler(args.json, "Timeout", None)) 3588 except(requests.exceptions.ConnectionError) as err: 3589 return connectionErrHandler(args.json, "ConnectionError", err) 3590 3591 return res.text 3592 3593 3594def setHostname(host, args, session): 3595 """ 3596 Called by the network function. Sets the Hostname. 3597 3598 @param host: string, the hostname or IP address of the bmc 3599 @param args: contains additional arguments used by the ldap subcommand 3600 args.json: boolean, if this flag is set to true, the output 3601 will be provided in json format for programmatic consumption 3602 @param session: the active session to use 3603 """ 3604 3605 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/HostName" 3606 3607 data = {"data": args.HostName} 3608 3609 try: 3610 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3611 timeout=baseTimeout) 3612 except(requests.exceptions.Timeout): 3613 return(connectionErrHandler(args.json, "Timeout", None)) 3614 except(requests.exceptions.ConnectionError) as err: 3615 return connectionErrHandler(args.json, "ConnectionError", err) 3616 3617 return res.text 3618 3619 3620def getDomainName(host, args, session): 3621 3622 """ 3623 Called by the network function. Prints out the DomainName. 3624 3625 @param host: string, the hostname or IP address of the bmc 3626 @param args: contains additional arguments used by the ldap subcommand 3627 args.json: boolean, if this flag is set to true, the output 3628 will be provided in json format for programmatic consumption 3629 @param session: the active session to use 3630 """ 3631 3632 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3633 "/attr/DomainName" 3634 3635 try: 3636 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3637 except(requests.exceptions.Timeout): 3638 return(connectionErrHandler(args.json, "Timeout", None)) 3639 except(requests.exceptions.ConnectionError) as err: 3640 return connectionErrHandler(args.json, "ConnectionError", err) 3641 if res.status_code == 404: 3642 return "The DomainName is not configured on Interface"+"("+args.Interface+")" 3643 3644 return res.text 3645 3646 3647def setDomainName(host, args, session): 3648 """ 3649 Called by the network function. Sets the DomainName. 3650 3651 @param host: string, the hostname or IP address of the bmc 3652 @param args: contains additional arguments used by the ldap subcommand 3653 args.json: boolean, if this flag is set to true, the output 3654 will be provided in json format for programmatic consumption 3655 @param session: the active session to use 3656 """ 3657 3658 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3659 "/attr/DomainName" 3660 3661 data = {"data": args.DomainName.split(",")} 3662 3663 try: 3664 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3665 timeout=baseTimeout) 3666 except(requests.exceptions.Timeout): 3667 return(connectionErrHandler(args.json, "Timeout", None)) 3668 except(requests.exceptions.ConnectionError) as err: 3669 return connectionErrHandler(args.json, "ConnectionError", err) 3670 if res.status_code == 403: 3671 return "Failed to set Domain Name" 3672 3673 return res.text 3674 3675 3676def getMACAddress(host, args, session): 3677 3678 """ 3679 Called by the network function. Prints out the MACAddress. 3680 3681 @param host: string, the hostname or IP address of the bmc 3682 @param args: contains additional arguments used by the ldap subcommand 3683 args.json: boolean, if this flag is set to true, the output 3684 will be provided in json format for programmatic consumption 3685 @param session: the active session to use 3686 """ 3687 3688 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3689 "/attr/MACAddress" 3690 3691 try: 3692 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3693 except(requests.exceptions.Timeout): 3694 return(connectionErrHandler(args.json, "Timeout", None)) 3695 except(requests.exceptions.ConnectionError) as err: 3696 return connectionErrHandler(args.json, "ConnectionError", err) 3697 if res.status_code == 404: 3698 return "Failed to get MACAddress" 3699 3700 return res.text 3701 3702 3703def setMACAddress(host, args, session): 3704 """ 3705 Called by the network function. Sets the MACAddress. 3706 3707 @param host: string, the hostname or IP address of the bmc 3708 @param args: contains additional arguments used by the ldap subcommand 3709 args.json: boolean, if this flag is set to true, the output 3710 will be provided in json format for programmatic consumption 3711 @param session: the active session to use 3712 """ 3713 3714 url = "https://"+host+"/xyz/openbmc_project/network/"+args.Interface+\ 3715 "/attr/MACAddress" 3716 3717 data = {"data": args.MACAddress} 3718 3719 try: 3720 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3721 timeout=baseTimeout) 3722 except(requests.exceptions.Timeout): 3723 return(connectionErrHandler(args.json, "Timeout", None)) 3724 except(requests.exceptions.ConnectionError) as err: 3725 return connectionErrHandler(args.json, "ConnectionError", err) 3726 if res.status_code == 403: 3727 return "Failed to set MACAddress" 3728 3729 return res.text 3730 3731 3732def getDefaultGateway(host, args, session): 3733 3734 """ 3735 Called by the network function. Prints out the DefaultGateway. 3736 3737 @param host: string, the hostname or IP address of the bmc 3738 @param args: contains additional arguments used by the ldap subcommand 3739 args.json: boolean, if this flag is set to true, the output 3740 will be provided in json format for programmatic consumption 3741 @param session: the active session to use 3742 """ 3743 3744 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/DefaultGateway" 3745 3746 try: 3747 res = session.get(url, headers=jsonHeader, verify=False, 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 == 404: 3753 return "Failed to get Default Gateway info" 3754 3755 return res.text 3756 3757 3758def setDefaultGateway(host, args, session): 3759 """ 3760 Called by the network function. Sets the DefaultGateway. 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 """ 3768 3769 url = "https://"+host+"/xyz/openbmc_project/network/config/attr/DefaultGateway" 3770 3771 data = {"data": args.DefaultGW} 3772 3773 try: 3774 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3775 timeout=baseTimeout) 3776 except(requests.exceptions.Timeout): 3777 return(connectionErrHandler(args.json, "Timeout", None)) 3778 except(requests.exceptions.ConnectionError) as err: 3779 return connectionErrHandler(args.json, "ConnectionError", err) 3780 if res.status_code == 403: 3781 return "Failed to set Default Gateway" 3782 3783 return res.text 3784 3785 3786def viewNWConfig(host, args, session): 3787 """ 3788 Called by the ldap function. Prints out network configured properties 3789 3790 @param host: string, the hostname or IP address of the bmc 3791 @param args: contains additional arguments used by the ldap subcommand 3792 args.json: boolean, if this flag is set to true, the output 3793 will be provided in json format for programmatic consumption 3794 @param session: the active session to use 3795 @return returns LDAP's configured properties. 3796 """ 3797 url = "https://"+host+"/xyz/openbmc_project/network/enumerate" 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 except(requests.exceptions.RequestException) as err: 3805 return connectionErrHandler(args.json, "RequestException", err) 3806 if res.status_code == 404: 3807 return "LDAP server config has not been created" 3808 return res.text 3809 3810 3811def getDNS(host, args, session): 3812 3813 """ 3814 Called by the network function. Prints out DNS servers on the interface 3815 3816 @param host: string, the hostname or IP address of the bmc 3817 @param args: contains additional arguments used by the ldap subcommand 3818 args.json: boolean, if this flag is set to true, the output 3819 will be provided in json format for programmatic consumption 3820 @param session: the active session to use 3821 """ 3822 3823 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3824 + "/attr/Nameservers" 3825 3826 try: 3827 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3828 except(requests.exceptions.Timeout): 3829 return(connectionErrHandler(args.json, "Timeout", None)) 3830 except(requests.exceptions.ConnectionError) as err: 3831 return connectionErrHandler(args.json, "ConnectionError", err) 3832 if res.status_code == 404: 3833 return "The NameServer is not configured on Interface"+"("+args.Interface+")" 3834 3835 return res.text 3836 3837 3838def setDNS(host, args, session): 3839 """ 3840 Called by the network function. Sets DNS servers on the interface. 3841 3842 @param host: string, the hostname or IP address of the bmc 3843 @param args: contains additional arguments used by the ldap subcommand 3844 args.json: boolean, if this flag is set to true, the output 3845 will be provided in json format for programmatic consumption 3846 @param session: the active session to use 3847 """ 3848 3849 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3850 + "/attr/Nameservers" 3851 3852 data = {"data": args.DNSServers.split(",")} 3853 3854 try: 3855 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3856 timeout=baseTimeout) 3857 except(requests.exceptions.Timeout): 3858 return(connectionErrHandler(args.json, "Timeout", None)) 3859 except(requests.exceptions.ConnectionError) as err: 3860 return connectionErrHandler(args.json, "ConnectionError", err) 3861 if res.status_code == 403: 3862 return "Failed to set DNS" 3863 3864 return res.text 3865 3866 3867def getNTP(host, args, session): 3868 3869 """ 3870 Called by the network function. Prints out NTP servers on the interface 3871 3872 @param host: string, the hostname or IP address of the bmc 3873 @param args: contains additional arguments used by the ldap subcommand 3874 args.json: boolean, if this flag is set to true, the output 3875 will be provided in json format for programmatic consumption 3876 @param session: the active session to use 3877 """ 3878 3879 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3880 + "/attr/NTPServers" 3881 try: 3882 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3883 except(requests.exceptions.Timeout): 3884 return(connectionErrHandler(args.json, "Timeout", None)) 3885 except(requests.exceptions.ConnectionError) as err: 3886 return connectionErrHandler(args.json, "ConnectionError", err) 3887 if res.status_code == 404: 3888 return "The NTPServer is not configured on Interface"+"("+args.Interface+")" 3889 3890 return res.text 3891 3892 3893def setNTP(host, args, session): 3894 """ 3895 Called by the network function. Sets NTP servers on the interface. 3896 3897 @param host: string, the hostname or IP address of the bmc 3898 @param args: contains additional arguments used by the ldap subcommand 3899 args.json: boolean, if this flag is set to true, the output 3900 will be provided in json format for programmatic consumption 3901 @param session: the active session to use 3902 """ 3903 3904 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3905 + "/attr/NTPServers" 3906 3907 data = {"data": args.NTPServers.split(",")} 3908 3909 try: 3910 res = session.put(url, headers=jsonHeader, json=data, verify=False, 3911 timeout=baseTimeout) 3912 except(requests.exceptions.Timeout): 3913 return(connectionErrHandler(args.json, "Timeout", None)) 3914 except(requests.exceptions.ConnectionError) as err: 3915 return connectionErrHandler(args.json, "ConnectionError", err) 3916 if res.status_code == 403: 3917 return "Failed to set NTP" 3918 3919 return res.text 3920 3921 3922def addIP(host, args, session): 3923 """ 3924 Called by the network function. Configures IP address on given interface 3925 3926 @param host: string, the hostname or IP address of the bmc 3927 @param args: contains additional arguments used by the ldap subcommand 3928 args.json: boolean, if this flag is set to true, the output 3929 will be provided in json format for programmatic consumption 3930 @param session: the active session to use 3931 """ 3932 3933 url = "https://" + host + "/xyz/openbmc_project/network/" + args.Interface\ 3934 + "/action/IP" 3935 protocol = { 3936 'ipv4': 'xyz.openbmc_project.Network.IP.Protocol.IPv4', 3937 'ipv6': 'xyz.openbmc_project.Network.IP.Protocol.IPv6' 3938 } 3939 3940 data = {"data": [protocol[args.type], args.address, int(args.prefixLength), 3941 args.gateway]} 3942 3943 try: 3944 res = session.post(url, headers=jsonHeader, json=data, verify=False, 3945 timeout=baseTimeout) 3946 except(requests.exceptions.Timeout): 3947 return(connectionErrHandler(args.json, "Timeout", None)) 3948 except(requests.exceptions.ConnectionError) as err: 3949 return connectionErrHandler(args.json, "ConnectionError", err) 3950 if res.status_code == 404: 3951 return "The specified Interface" + "(" + args.Interface + ")" +\ 3952 " doesn't exist" 3953 3954 return res.text 3955 3956 3957def getIP(host, args, session): 3958 """ 3959 Called by the network function. Prints out IP address of given interface 3960 3961 @param host: string, the hostname or IP address of the bmc 3962 @param args: contains additional arguments used by the ldap subcommand 3963 args.json: boolean, if this flag is set to true, the output 3964 will be provided in json format for programmatic consumption 3965 @param session: the active session to use 3966 """ 3967 3968 url = "https://" + host+"/xyz/openbmc_project/network/" + args.Interface +\ 3969 "/enumerate" 3970 try: 3971 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3972 except(requests.exceptions.Timeout): 3973 return(connectionErrHandler(args.json, "Timeout", None)) 3974 except(requests.exceptions.ConnectionError) as err: 3975 return connectionErrHandler(args.json, "ConnectionError", err) 3976 if res.status_code == 404: 3977 return "The specified Interface" + "(" + args.Interface + ")" +\ 3978 " doesn't exist" 3979 3980 return res.text 3981 3982 3983def deleteIP(host, args, session): 3984 """ 3985 Called by the network function. Deletes the IP address from given Interface 3986 3987 @param host: string, the hostname or IP address of the bmc 3988 @param args: contains additional arguments used by the ldap subcommand 3989 @param session: the active session to use 3990 @param args.json: boolean, if this flag is set to true, the output 3991 will be provided in json format for programmatic consumption 3992 """ 3993 3994 url = "https://"+host+"/xyz/openbmc_project/network/" + args.Interface+\ 3995 "/enumerate" 3996 data = {"data": []} 3997 try: 3998 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 3999 except(requests.exceptions.Timeout): 4000 return(connectionErrHandler(args.json, "Timeout", None)) 4001 except(requests.exceptions.ConnectionError) as err: 4002 return connectionErrHandler(args.json, "ConnectionError", err) 4003 if res.status_code == 404: 4004 return "The specified Interface" + "(" + args.Interface + ")" +\ 4005 " doesn't exist" 4006 objDict = json.loads(res.text) 4007 if not objDict['data']: 4008 return "No object found for given address on given Interface" 4009 for obj in objDict['data']: 4010 try: 4011 if args.address in objDict['data'][obj]['Address']: 4012 url = "https://"+host+obj+"/action/Delete" 4013 try: 4014 res = session.post(url, headers=jsonHeader, json=data, 4015 verify=False, timeout=baseTimeout) 4016 except(requests.exceptions.Timeout): 4017 return(connectionErrHandler(args.json, "Timeout", None)) 4018 except(requests.exceptions.ConnectionError) as err: 4019 return connectionErrHandler(args.json, "ConnectionError", err) 4020 return res.text 4021 else: 4022 continue 4023 except KeyError: 4024 continue 4025 return "No object found for address " + args.address + \ 4026 " on Interface(" + args.Interface + ")" 4027 4028 4029def addVLAN(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/action/VLAN" 4041 4042 data = {"data": [args.Interface,int(args.Identifier)]} 4043 try: 4044 res = session.post(url, headers=jsonHeader, json=data, verify=False, 4045 timeout=baseTimeout) 4046 except(requests.exceptions.Timeout): 4047 return(connectionErrHandler(args.json, "Timeout", None)) 4048 except(requests.exceptions.ConnectionError) as err: 4049 return connectionErrHandler(args.json, "ConnectionError", err) 4050 if res.status_code == 400: 4051 return "Adding VLAN to interface" + "(" + args.Interface + ")" +\ 4052 " failed" 4053 4054 return res.text 4055 4056 4057def deleteVLAN(host, args, session): 4058 """ 4059 Called by the network function. Creates VLAN on given interface. 4060 4061 @param host: string, the hostname or IP address of the bmc 4062 @param args: contains additional arguments used by the ldap subcommand 4063 args.json: boolean, if this flag is set to true, the output 4064 will be provided in json format for programmatic consumption 4065 @param session: the active session to use 4066 """ 4067 4068 url = "https://" + host+"/xyz/openbmc_project/network/"+args.Interface+"/action/Delete" 4069 data = {"data": []} 4070 4071 try: 4072 res = session.post(url, headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4073 except(requests.exceptions.Timeout): 4074 return(connectionErrHandler(args.json, "Timeout", None)) 4075 except(requests.exceptions.ConnectionError) as err: 4076 return connectionErrHandler(args.json, "ConnectionError", err) 4077 if res.status_code == 404: 4078 return "The specified VLAN"+"("+args.Interface+")" +" doesn't exist" 4079 4080 return res.text 4081 4082 4083def viewDHCPConfig(host, args, session): 4084 """ 4085 Called by the network function. Shows DHCP configured Properties. 4086 4087 @param host: string, the hostname or IP address of the bmc 4088 @param args: contains additional arguments used by the ldap subcommand 4089 args.json: boolean, if this flag is set to true, the output 4090 will be provided in json format for programmatic consumption 4091 @param session: the active session to use 4092 """ 4093 4094 url="https://"+host+"/xyz/openbmc_project/network/config/dhcp" 4095 4096 try: 4097 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4098 except(requests.exceptions.Timeout): 4099 return(connectionErrHandler(args.json, "Timeout", None)) 4100 except(requests.exceptions.ConnectionError) as err: 4101 return connectionErrHandler(args.json, "ConnectionError", err) 4102 4103 return res.text 4104 4105 4106def configureDHCP(host, args, session): 4107 """ 4108 Called by the network function. Configures/updates DHCP Properties. 4109 4110 @param host: string, the hostname or IP address of the bmc 4111 @param args: contains additional arguments used by the ldap subcommand 4112 args.json: boolean, if this flag is set to true, the output 4113 will be provided in json format for programmatic consumption 4114 @param session: the active session to use 4115 """ 4116 4117 4118 try: 4119 url="https://"+host+"/xyz/openbmc_project/network/config/dhcp" 4120 if(args.DNSEnabled == True): 4121 data = '{"data": 1}' 4122 else: 4123 data = '{"data": 0}' 4124 res = session.put(url + '/attr/DNSEnabled', headers=jsonHeader, 4125 data=data, verify=False, timeout=baseTimeout) 4126 if(args.HostNameEnabled == True): 4127 data = '{"data": 1}' 4128 else: 4129 data = '{"data": 0}' 4130 res = session.put(url + '/attr/HostNameEnabled', headers=jsonHeader, 4131 data=data, verify=False, timeout=baseTimeout) 4132 if(args.NTPEnabled == True): 4133 data = '{"data": 1}' 4134 else: 4135 data = '{"data": 0}' 4136 res = session.put(url + '/attr/NTPEnabled', headers=jsonHeader, 4137 data=data, verify=False, timeout=baseTimeout) 4138 if(args.SendHostNameEnabled == True): 4139 data = '{"data": 1}' 4140 else: 4141 data = '{"data": 0}' 4142 res = session.put(url + '/attr/SendHostNameEnabled', headers=jsonHeader, 4143 data=data, verify=False, timeout=baseTimeout) 4144 except(requests.exceptions.Timeout): 4145 return(connectionErrHandler(args.json, "Timeout", None)) 4146 except(requests.exceptions.ConnectionError) as err: 4147 return connectionErrHandler(args.json, "ConnectionError", err) 4148 4149 return res.text 4150 4151 4152def nwReset(host, args, session): 4153 4154 """ 4155 Called by the network function. Resets networks setting to factory defaults. 4156 4157 @param host: string, the hostname or IP address of the bmc 4158 @param args: contains additional arguments used by the ldap subcommand 4159 args.json: boolean, if this flag is set to true, the output 4160 will be provided in json format for programmatic consumption 4161 @param session: the active session to use 4162 """ 4163 4164 url = "https://"+host+"/xyz/openbmc_project/network/action/Reset" 4165 data = '{"data":[] }' 4166 try: 4167 res = session.post(url, headers=jsonHeader, data=data, verify=False, 4168 timeout=baseTimeout) 4169 4170 except(requests.exceptions.Timeout): 4171 return(connectionErrHandler(args.json, "Timeout", None)) 4172 except(requests.exceptions.ConnectionError) as err: 4173 return connectionErrHandler(args.json, "ConnectionError", err) 4174 4175 return res.text 4176 4177def getLDAPTypeEnabled(host,session): 4178 4179 """ 4180 Called by LDAP related functions to find the LDAP server type that has been enabled. 4181 Returns None if LDAP has not been configured. 4182 4183 @param host: string, the hostname or IP address of the bmc 4184 @param session: the active session to use 4185 """ 4186 4187 enabled = False 4188 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/' 4189 for key,value in serverTypeMap.items(): 4190 data = {"data": []} 4191 try: 4192 res = session.get(url + value + '/attr/Enabled', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4193 except(requests.exceptions.Timeout): 4194 print(connectionErrHandler(args.json, "Timeout", None)) 4195 return 4196 except(requests.exceptions.ConnectionError) as err: 4197 print(connectionErrHandler(args.json, "ConnectionError", err)) 4198 return 4199 4200 enabled = res.json()['data'] 4201 if (enabled): 4202 return key 4203 4204def syncRoleMap(host,args,session,fromServerType,toServerType): 4205 4206 """ 4207 Called by LDAP related functions to sync the role maps 4208 Returns False if LDAP has not been configured. 4209 4210 @param host: string, the hostname or IP address of the bmc 4211 @param session: the active session to use 4212 @param fromServerType : Server type whose role map has to be copied 4213 @param toServerType : Server type to which role map has to be copied 4214 """ 4215 4216 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 4217 4218 try: 4219 #Note: If the fromServerType has no role map, then 4220 #the toServerType will not have any role map. 4221 4222 #delete the privilege mapping from the toServerType and 4223 #then copy the privilege mapping from fromServerType to 4224 #toServerType. 4225 args.serverType = toServerType 4226 res = deleteAllPrivilegeMapping(host, args, session) 4227 4228 data = {"data": []} 4229 res = session.get(url + serverTypeMap[fromServerType] + '/role_map/enumerate', headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4230 #Previously enabled server type has no role map 4231 if (res.status_code != requests.codes.ok): 4232 4233 #fromServerType has no role map; So, no need to copy 4234 #role map to toServerType. 4235 return 4236 4237 objDict = json.loads(res.text) 4238 dataDict = objDict['data'] 4239 for key,value in dataDict.items(): 4240 data = {"data": [value["GroupName"], value["Privilege"]]} 4241 res = session.post(url + serverTypeMap[toServerType] + '/action/Create', headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4242 4243 except(requests.exceptions.Timeout): 4244 return(connectionErrHandler(args.json, "Timeout", None)) 4245 except(requests.exceptions.ConnectionError) as err: 4246 return connectionErrHandler(args.json, "ConnectionError", err) 4247 return res.text 4248 4249 4250def createPrivilegeMapping(host, args, session): 4251 """ 4252 Called by the ldap function. Creates the group and the privilege mapping. 4253 4254 @param host: string, the hostname or IP address of the bmc 4255 @param args: contains additional arguments used by the ldap subcommand 4256 @param session: the active session to use 4257 @param args.json: boolean, if this flag is set to true, the output 4258 will be provided in json format for programmatic consumption 4259 """ 4260 4261 try: 4262 if (isRedfishSupport): 4263 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/' 4264 4265 #To maintain the interface compatibility between op930 and op940, the server type has been made 4266 #optional. If the server type is not specified, then create the role-mapper for the currently 4267 #enabled server type. 4268 serverType = args.serverType 4269 if (serverType is None): 4270 serverType = getLDAPTypeEnabled(host,session) 4271 if (serverType is None): 4272 return("LDAP server has not been enabled. Please specify LDAP serverType to proceed further...") 4273 4274 data = {"data": [args.groupName,args.privilege]} 4275 res = session.post(url + serverTypeMap[serverType] + '/action/Create', headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4276 4277 else: 4278 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/action/Create' 4279 data = {"data": [args.groupName,args.privilege]} 4280 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4281 4282 except(requests.exceptions.Timeout): 4283 return(connectionErrHandler(args.json, "Timeout", None)) 4284 except(requests.exceptions.ConnectionError) as err: 4285 return connectionErrHandler(args.json, "ConnectionError", err) 4286 return res.text 4287 4288def listPrivilegeMapping(host, args, session): 4289 """ 4290 Called by the ldap function. Lists the group and the privilege mapping. 4291 4292 @param host: string, the hostname or IP address of the bmc 4293 @param args: contains additional arguments used by the ldap subcommand 4294 @param session: the active session to use 4295 @param args.json: boolean, if this flag is set to true, the output 4296 will be provided in json format for programmatic consumption 4297 """ 4298 4299 if (isRedfishSupport): 4300 serverType = args.serverType 4301 if (serverType is None): 4302 serverType = getLDAPTypeEnabled(host,session) 4303 if (serverType is None): 4304 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4305 4306 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/'+serverTypeMap[serverType]+'/role_map/enumerate' 4307 4308 else: 4309 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/enumerate' 4310 4311 data = {"data": []} 4312 4313 try: 4314 res = session.get(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4315 except(requests.exceptions.Timeout): 4316 return(connectionErrHandler(args.json, "Timeout", None)) 4317 except(requests.exceptions.ConnectionError) as err: 4318 return connectionErrHandler(args.json, "ConnectionError", err) 4319 4320 return res.text 4321 4322def deletePrivilegeMapping(host, args, session): 4323 """ 4324 Called by the ldap function. Deletes the mapping associated with the group. 4325 4326 @param host: string, the hostname or IP address of the bmc 4327 @param args: contains additional arguments used by the ldap subcommand 4328 @param session: the active session to use 4329 @param args.json: boolean, if this flag is set to true, the output 4330 will be provided in json format for programmatic consumption 4331 """ 4332 4333 ldapNameSpaceObjects = listPrivilegeMapping(host, args, session) 4334 ldapNameSpaceObjects = json.loads(ldapNameSpaceObjects)["data"] 4335 path = '' 4336 data = {"data": []} 4337 4338 if (isRedfishSupport): 4339 if (args.serverType is None): 4340 serverType = getLDAPTypeEnabled(host,session) 4341 if (serverType is None): 4342 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4343 # search for the object having the mapping for the given group 4344 for key,value in ldapNameSpaceObjects.items(): 4345 if value['GroupName'] == args.groupName: 4346 path = key 4347 break 4348 4349 if path == '': 4350 return "No privilege mapping found for this group." 4351 4352 # delete the object 4353 url = 'https://'+host+path+'/action/Delete' 4354 4355 else: 4356 # not interested in the config objet 4357 ldapNameSpaceObjects.pop('/xyz/openbmc_project/user/ldap/config', None) 4358 4359 # search for the object having the mapping for the given group 4360 for key,value in ldapNameSpaceObjects.items(): 4361 if value['GroupName'] == args.groupName: 4362 path = key 4363 break 4364 4365 if path == '': 4366 return "No privilege mapping found for this group." 4367 4368 # delete the object 4369 url = 'https://'+host+path+'/action/delete' 4370 4371 try: 4372 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4373 except(requests.exceptions.Timeout): 4374 return(connectionErrHandler(args.json, "Timeout", None)) 4375 except(requests.exceptions.ConnectionError) as err: 4376 return connectionErrHandler(args.json, "ConnectionError", err) 4377 return res.text 4378 4379def deleteAllPrivilegeMapping(host, args, session): 4380 """ 4381 Called by the ldap function. Deletes all the privilege mapping and group defined. 4382 @param host: string, the hostname or IP address of the bmc 4383 @param args: contains additional arguments used by the ldap subcommand 4384 @param session: the active session to use 4385 @param args.json: boolean, if this flag is set to true, the output 4386 will be provided in json format for programmatic consumption 4387 """ 4388 4389 ldapNameSpaceObjects = listPrivilegeMapping(host, args, session) 4390 ldapNameSpaceObjects = json.loads(ldapNameSpaceObjects)["data"] 4391 path = '' 4392 data = {"data": []} 4393 4394 if (isRedfishSupport): 4395 if (args.serverType is None): 4396 serverType = getLDAPTypeEnabled(host,session) 4397 if (serverType is None): 4398 return("LDAP has not been enabled. Please specify LDAP serverType to proceed further...") 4399 4400 else: 4401 # Remove the config object. 4402 ldapNameSpaceObjects.pop('/xyz/openbmc_project/user/ldap/config', None) 4403 4404 try: 4405 # search for GroupName property and delete if it is available. 4406 for path in ldapNameSpaceObjects.keys(): 4407 # delete the object 4408 url = 'https://'+host+path+'/action/Delete' 4409 res = session.post(url, headers=jsonHeader, json = data, verify=False, timeout=baseTimeout) 4410 4411 except(requests.exceptions.Timeout): 4412 return(connectionErrHandler(args.json, "Timeout", None)) 4413 except(requests.exceptions.ConnectionError) as err: 4414 return connectionErrHandler(args.json, "ConnectionError", err) 4415 return res.text 4416 4417def viewLDAPConfig(host, args, session): 4418 """ 4419 Called by the ldap function. Prints out active LDAP configuration properties 4420 4421 @param host: string, the hostname or IP address of the bmc 4422 @param args: contains additional arguments used by the ldap subcommand 4423 args.json: boolean, if this flag is set to true, the output 4424 will be provided in json format for programmatic consumption 4425 @param session: the active session to use 4426 @return returns LDAP's configured properties. 4427 """ 4428 4429 try: 4430 if (isRedfishSupport): 4431 4432 url = "https://"+host+"/xyz/openbmc_project/user/ldap/" 4433 4434 serverTypeEnabled = getLDAPTypeEnabled(host,session) 4435 4436 if (serverTypeEnabled is not None): 4437 data = {"data": []} 4438 res = session.get(url + serverTypeMap[serverTypeEnabled], headers=jsonHeader, json=data, verify=False, timeout=baseTimeout) 4439 else: 4440 return("LDAP server has not been enabled...") 4441 4442 else : 4443 url = "https://"+host+"/xyz/openbmc_project/user/ldap/config" 4444 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4445 4446 except(requests.exceptions.Timeout): 4447 return(connectionErrHandler(args.json, "Timeout", None)) 4448 except(requests.exceptions.ConnectionError) as err: 4449 return connectionErrHandler(args.json, "ConnectionError", err) 4450 if res.status_code == 404: 4451 return "LDAP server config has not been created" 4452 return res.text 4453 4454def str2bool(v): 4455 if v.lower() in ('yes', 'true', 't', 'y', '1'): 4456 return True 4457 elif v.lower() in ('no', 'false', 'f', 'n', '0'): 4458 return False 4459 else: 4460 raise argparse.ArgumentTypeError('Boolean value expected.') 4461 4462def localUsers(host, args, session): 4463 """ 4464 Enables and disables local BMC users. 4465 4466 @param host: string, the hostname or IP address of the bmc 4467 @param args: contains additional arguments used by the logging sub command 4468 @param session: the active session to use 4469 """ 4470 4471 url="https://{hostname}/xyz/openbmc_project/user/enumerate".format(hostname=host) 4472 try: 4473 res = session.get(url, headers=jsonHeader, verify=False, timeout=baseTimeout) 4474 except(requests.exceptions.Timeout): 4475 return(connectionErrHandler(args.json, "Timeout", None)) 4476 usersDict = json.loads(res.text) 4477 4478 if not usersDict['data']: 4479 return "No users found" 4480 4481 output = "" 4482 for user in usersDict['data']: 4483 4484 # Skip LDAP and another non-local users 4485 if 'UserEnabled' not in usersDict['data'][user]: 4486 continue 4487 4488 name = user.split('/')[-1] 4489 url = "https://{hostname}{user}/attr/UserEnabled".format(hostname=host, user=user) 4490 4491 if args.local_users == "queryenabled": 4492 try: 4493 res = session.get(url, headers=jsonHeader,verify=False, timeout=baseTimeout) 4494 except(requests.exceptions.Timeout): 4495 return(connectionErrHandler(args.json, "Timeout", None)) 4496 4497 result = json.loads(res.text) 4498 output += ("User: {name} Enabled: {result}\n").format(name=name, result=result['data']) 4499 4500 elif args.local_users in ["enableall", "disableall"]: 4501 action = "" 4502 if args.local_users == "enableall": 4503 data = '{"data": true}' 4504 action = "Enabling" 4505 else: 4506 data = '{"data": false}' 4507 action = "Disabling" 4508 4509 output += "{action} {name}\n".format(action=action, name=name) 4510 4511 try: 4512 resp = session.put(url, headers=jsonHeader, data=data, verify=False, timeout=baseTimeout) 4513 except(requests.exceptions.Timeout): 4514 return connectionErrHandler(args.json, "Timeout", None) 4515 except(requests.exceptions.ConnectionError) as err: 4516 return connectionErrHandler(args.json, "ConnectionError", err) 4517 else: 4518 return "Invalid local users argument" 4519 4520 return output 4521 4522def setPassword(host, args, session): 4523 """ 4524 Set local user password 4525 @param host: string, the hostname or IP address of the bmc 4526 @param args: contains additional arguments used by the logging sub 4527 command 4528 @param session: the active session to use 4529 @param args.json: boolean, if this flag is set to true, the output 4530 will be provided in json format for programmatic consumption 4531 @return: Session object 4532 """ 4533 try: 4534 if(isRedfishSupport): 4535 url = "https://" + host + "/redfish/v1/AccountService/Accounts/"+ \ 4536 args.user 4537 data = {"Password":args.password} 4538 res = session.patch(url, headers=jsonHeader, json=data, 4539 verify=False, timeout=baseTimeout) 4540 else: 4541 url = "https://" + host + "/xyz/openbmc_project/user/" + args.user + \ 4542 "/action/SetPassword" 4543 res = session.post(url, headers=jsonHeader, 4544 json={"data": [args.password]}, verify=False, 4545 timeout=baseTimeout) 4546 except(requests.exceptions.Timeout): 4547 return(connectionErrHandler(args.json, "Timeout", None)) 4548 except(requests.exceptions.ConnectionError) as err: 4549 return connectionErrHandler(args.json, "ConnectionError", err) 4550 except(requests.exceptions.RequestException) as err: 4551 return connectionErrHandler(args.json, "RequestException", err) 4552 return res.status_code 4553 4554def getThermalZones(host, args, session): 4555 """ 4556 Get the available thermal control zones 4557 @param host: string, the hostname or IP address of the bmc 4558 @param args: contains additional arguments used to get the thermal 4559 control zones 4560 @param session: the active session to use 4561 @return: Session object 4562 """ 4563 url = "https://" + host + "/xyz/openbmc_project/control/thermal/enumerate" 4564 4565 try: 4566 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4567 except(requests.exceptions.Timeout): 4568 return(connectionErrHandler(args.json, "Timeout", None)) 4569 except(requests.exceptions.ConnectionError) as err: 4570 return connectionErrHandler(args.json, "ConnectionError", err) 4571 except(requests.exceptions.RequestException) as err: 4572 return connectionErrHandler(args.json, "RequestException", err) 4573 4574 if (res.status_code == 404): 4575 return "No thermal control zones found" 4576 4577 zonesDict = json.loads(res.text) 4578 if not zonesDict['data']: 4579 return "No thermal control zones found" 4580 for zone in zonesDict['data']: 4581 z = ",".join(str(zone.split('/')[-1]) for zone in zonesDict['data']) 4582 4583 return "Zones: [ " + z + " ]" 4584 4585 4586def getThermalMode(host, args, session): 4587 """ 4588 Get thermal control mode 4589 @param host: string, the hostname or IP address of the bmc 4590 @param args: contains additional arguments used to get the thermal 4591 control mode 4592 @param session: the active session to use 4593 @param args.zone: the zone to get the mode on 4594 @return: Session object 4595 """ 4596 url = "https://" + host + "/xyz/openbmc_project/control/thermal/" + \ 4597 args.zone 4598 4599 try: 4600 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4601 except(requests.exceptions.Timeout): 4602 return(connectionErrHandler(args.json, "Timeout", None)) 4603 except(requests.exceptions.ConnectionError) as err: 4604 return connectionErrHandler(args.json, "ConnectionError", err) 4605 except(requests.exceptions.RequestException) as err: 4606 return connectionErrHandler(args.json, "RequestException", err) 4607 4608 if (res.status_code == 404): 4609 return "Thermal control zone(" + args.zone + ") not found" 4610 4611 propsDict = json.loads(res.text) 4612 if not propsDict['data']: 4613 return "No thermal control properties found on zone(" + args.zone + ")" 4614 curMode = "Current" 4615 supModes = "Supported" 4616 result = "\n" 4617 for prop in propsDict['data']: 4618 if (prop.casefold() == curMode.casefold()): 4619 result += curMode + " Mode: " + propsDict['data'][curMode] + "\n" 4620 if (prop.casefold() == supModes.casefold()): 4621 s = ", ".join(str(sup) for sup in propsDict['data'][supModes]) 4622 result += supModes + " Modes: [ " + s + " ]\n" 4623 4624 return result 4625 4626def setThermalMode(host, args, session): 4627 """ 4628 Set thermal control mode 4629 @param host: string, the hostname or IP address of the bmc 4630 @param args: contains additional arguments used for setting the thermal 4631 control mode 4632 @param session: the active session to use 4633 @param args.zone: the zone to set the mode on 4634 @param args.mode: the mode to enable 4635 @return: Session object 4636 """ 4637 url = "https://" + host + "/xyz/openbmc_project/control/thermal/" + \ 4638 args.zone + "/attr/Current" 4639 4640 # Check args.mode against supported modes using `getThermalMode` output 4641 modes = getThermalMode(host, args, session) 4642 modes = os.linesep.join([m for m in modes.splitlines() if m]) 4643 modes = modes.replace("\n", ";").strip() 4644 modesDict = dict(m.split(': ') for m in modes.split(';')) 4645 sModes = ''.join(s for s in modesDict['Supported Modes'] if s not in '[ ]') 4646 if args.mode.casefold() not in \ 4647 (m.casefold() for m in sModes.split(',')) or not args.mode: 4648 result = ("Unsupported mode('" + args.mode + "') given, " + 4649 "select a supported mode: \n" + 4650 getThermalMode(host, args, session)) 4651 return result 4652 4653 data = '{"data":"' + args.mode + '"}' 4654 try: 4655 res = session.get(url, headers=jsonHeader, verify=False, timeout=30) 4656 except(requests.exceptions.Timeout): 4657 return(connectionErrHandler(args.json, "Timeout", None)) 4658 except(requests.exceptions.ConnectionError) as err: 4659 return connectionErrHandler(args.json, "ConnectionError", err) 4660 except(requests.exceptions.RequestException) as err: 4661 return connectionErrHandler(args.json, "RequestException", err) 4662 4663 if (data and res.status_code != 404): 4664 try: 4665 res = session.put(url, headers=jsonHeader, 4666 data=data, verify=False, 4667 timeout=30) 4668 except(requests.exceptions.Timeout): 4669 return(connectionErrHandler(args.json, "Timeout", None)) 4670 except(requests.exceptions.ConnectionError) as err: 4671 return connectionErrHandler(args.json, "ConnectionError", err) 4672 except(requests.exceptions.RequestException) as err: 4673 return connectionErrHandler(args.json, "RequestException", err) 4674 4675 if res.status_code == 403: 4676 return "The specified thermal control zone(" + args.zone + ")" + \ 4677 " does not exist" 4678 4679 return res.text 4680 else: 4681 return "Setting thermal control mode(" + args.mode + ")" + \ 4682 " not supported or operation not available" 4683 4684 4685def createCommandParser(): 4686 """ 4687 creates the parser for the command line along with help for each command and subcommand 4688 4689 @return: returns the parser for the command line 4690 """ 4691 parser = argparse.ArgumentParser(description='Process arguments') 4692 parser.add_argument("-H", "--host", help='A hostname or IP for the BMC') 4693 parser.add_argument("-U", "--user", help='The username to login with') 4694 group = parser.add_mutually_exclusive_group() 4695 group.add_argument("-A", "--askpw", action='store_true', help='prompt for password') 4696 group.add_argument("-P", "--PW", help='Provide the password in-line') 4697 group.add_argument("-E", "--PWenvvar", action='store_true', help='Get password from envvar OPENBMCTOOL_PASSWORD') 4698 parser.add_argument('-j', '--json', action='store_true', help='output json data only') 4699 parser.add_argument('-t', '--policyTableLoc', help='The location of the policy table to parse alerts') 4700 parser.add_argument('-c', '--CerFormat', action='store_true', help=argparse.SUPPRESS) 4701 parser.add_argument('-T', '--procTime', action='store_true', help= argparse.SUPPRESS) 4702 parser.add_argument('-V', '--version', action='store_true', help='Display the version number of the openbmctool') 4703 subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4704 4705 #fru command 4706 parser_inv = subparsers.add_parser("fru", help='Work with platform inventory') 4707 inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions", dest='command') 4708 inv_subparser.required = True 4709 #fru print 4710 inv_print = inv_subparser.add_parser("print", help="prints out a list of all FRUs") 4711 inv_print.set_defaults(func=fruPrint) 4712 #fru list [0....n] 4713 inv_list = inv_subparser.add_parser("list", help="print out details on selected FRUs. Specifying no items will list the entire inventory") 4714 inv_list.add_argument('items', nargs='?', help="print out details on selected FRUs. Specifying no items will list the entire inventory") 4715 inv_list.set_defaults(func=fruList) 4716 #fru status 4717 inv_status = inv_subparser.add_parser("status", help="prints out the status of all FRUs") 4718 inv_status.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4719 inv_status.set_defaults(func=fruStatus) 4720 4721 #sensors command 4722 parser_sens = subparsers.add_parser("sensors", help="Work with platform sensors") 4723 sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions', dest='command') 4724 sens_subparser.required = True 4725 #sensor print 4726 sens_print= sens_subparser.add_parser('print', help="prints out a list of all Sensors.") 4727 sens_print.set_defaults(func=sensor) 4728 #sensor list[0...n] 4729 sens_list=sens_subparser.add_parser("list", help="Lists all Sensors in the platform. Specify a sensor for full details. ") 4730 sens_list.add_argument("sensNum", nargs='?', help="The Sensor number to get full details on" ) 4731 sens_list.set_defaults(func=sensor) 4732 4733 #thermal control commands 4734 parser_therm = subparsers.add_parser("thermal", help="Work with thermal control parameters") 4735 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') 4736 #thermal control zones 4737 parser_thermZones = therm_subparser.add_parser("zones", help="Get a list of available thermal control zones") 4738 parser_thermZones.set_defaults(func=getThermalZones) 4739 #thermal control modes 4740 parser_thermMode = therm_subparser.add_parser("modes", help="Work with thermal control modes") 4741 thermMode_sub = parser_thermMode.add_subparsers(title='subactions', description='Work with thermal control modes', help="Work with thermal control modes") 4742 #get thermal control mode 4743 parser_getThermMode = thermMode_sub.add_parser("get", help="Get current and supported thermal control modes") 4744 parser_getThermMode.add_argument('-z', '--zone', required=True, help='Thermal zone to work with') 4745 parser_getThermMode.set_defaults(func=getThermalMode) 4746 #set thermal control mode 4747 parser_setThermMode = thermMode_sub.add_parser("set", help="Set the thermal control mode") 4748 parser_setThermMode.add_argument('-z', '--zone', required=True, help='Thermal zone to work with') 4749 parser_setThermMode.add_argument('-m', '--mode', required=True, help='The supported thermal control mode') 4750 parser_setThermMode.set_defaults(func=setThermalMode) 4751 4752 #sel command 4753 parser_sel = subparsers.add_parser("sel", help="Work with platform alerts") 4754 sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions', dest='command') 4755 sel_subparser.required = True 4756 #sel print 4757 sel_print = sel_subparser.add_parser("print", help="prints out a list of all sels in a condensed list") 4758 sel_print.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS) 4759 sel_print.add_argument('-v', '--verbose', action='store_true', help="Changes the output to being very verbose") 4760 sel_print.add_argument('-f', '--fileloc', help='Parse a file instead of the BMC output') 4761 sel_print.set_defaults(func=selPrint) 4762 4763 #sel list 4764 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") 4765 sel_list.add_argument("selNum", nargs='?', type=int, help="The SEL entry to get details on") 4766 sel_list.set_defaults(func=selList) 4767 4768 sel_get = sel_subparser.add_parser("get", help="Gets the verbose details of a specified SEL entry") 4769 sel_get.add_argument('selNum', type=int, help="the number of the SEL entry to get") 4770 sel_get.set_defaults(func=selList) 4771 4772 sel_clear = sel_subparser.add_parser("clear", help="Clears all entries from the SEL") 4773 sel_clear.set_defaults(func=selClear) 4774 4775 sel_setResolved = sel_subparser.add_parser("resolve", help="Sets the sel entry to resolved") 4776 sel_setResolved.add_argument('-n', '--selNum', type=int, help="the number of the SEL entry to resolve") 4777 sel_ResolveAll_sub = sel_setResolved.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4778 sel_ResolveAll = sel_ResolveAll_sub.add_parser('all', help='Resolve all SEL entries') 4779 sel_ResolveAll.set_defaults(func=selResolveAll) 4780 sel_setResolved.set_defaults(func=selSetResolved) 4781 4782 parser_chassis = subparsers.add_parser("chassis", help="Work with chassis power and status") 4783 chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4784 4785 parser_chassis.add_argument('status', action='store_true', help='Returns the current status of the platform') 4786 parser_chassis.set_defaults(func=chassis) 4787 4788 parser_chasPower = chas_sub.add_parser("power", help="Turn the chassis on or off, check the power state") 4789 parser_chasPower.add_argument('powcmd', choices=['on','softoff', 'hardoff', 'status'], help='The value for the power command. on, off, or status') 4790 parser_chasPower.set_defaults(func=chassisPower) 4791 4792 #control the chassis identify led 4793 parser_chasIdent = chas_sub.add_parser("identify", help="Control the chassis identify led") 4794 parser_chasIdent.add_argument('identcmd', choices=['on', 'off', 'status'], help='The control option for the led: on, off, blink, status') 4795 parser_chasIdent.set_defaults(func=chassisIdent) 4796 4797 #collect service data 4798 parser_servData = subparsers.add_parser("collect_service_data", help="Collect all bmc data needed for service") 4799 parser_servData.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS) 4800 parser_servData.set_defaults(func=collectServiceData) 4801 4802 #system quick health check 4803 parser_healthChk = subparsers.add_parser("health_check", help="Work with platform sensors") 4804 parser_healthChk.set_defaults(func=healthCheck) 4805 4806 #tasks 4807 parser_tasks = subparsers.add_parser("task", help="Work with tasks") 4808 tasks_sub = parser_tasks.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4809 tasks_sub.required = True 4810 get_Task = tasks_sub.add_parser('get', help="Get on Task Monitor URL") 4811 get_Task.add_argument("-u", "--taskURI", help="Task Monitor URI") 4812 get_Task.set_defaults(func=getTask) 4813 4814 #work with dumps 4815 parser_bmcdump = subparsers.add_parser("dump", help="Work with dumps") 4816 parser_bmcdump.add_argument("-t", "--dumpType", default='bmc', choices=['bmc','SystemDump'],help="Type of dump") 4817 bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4818 bmcDump_sub.required = True 4819 dump_Create = bmcDump_sub.add_parser('create', help="Create a dump of given type") 4820 dump_Create.set_defaults(func=dumpCreate) 4821 4822 dump_list = bmcDump_sub.add_parser('list', help="list all dumps") 4823 dump_list.set_defaults(func=dumpList) 4824 4825 parserdumpdelete = bmcDump_sub.add_parser('delete', help="Delete dump") 4826 parserdumpdelete.add_argument("-n", "--dumpNum", nargs='*', type=int, help="The Dump entry to delete") 4827 parserdumpdelete.set_defaults(func=dumpDelete) 4828 4829 bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4830 deleteAllDumps = bmcDumpDelsub.add_parser('all', help='Delete all dumps') 4831 deleteAllDumps.set_defaults(func=dumpDeleteAll) 4832 4833 parser_dumpretrieve = bmcDump_sub.add_parser('retrieve', help='Retrieve a dump file') 4834 parser_dumpretrieve.add_argument("-n,", "--dumpNum", help="The Dump entry to retrieve") 4835 parser_dumpretrieve.add_argument("-s", "--dumpSaveLoc", help="The location to save the bmc dump file or file path for system dump") 4836 parser_dumpretrieve.set_defaults(func=dumpRetrieve) 4837 4838 #bmc command for reseting the bmc 4839 parser_bmc = subparsers.add_parser('bmc', help="Work with the bmc") 4840 bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4841 parser_BMCReset = bmc_sub.add_parser('reset', help='Reset the bmc' ) 4842 parser_BMCReset.add_argument('type', choices=['warm','cold'], help="Warm: Reboot the BMC, Cold: CLEAR config and reboot bmc") 4843 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.") 4844 parser_bmc.set_defaults(func=bmc) 4845 4846 #add alias to the bmc command 4847 parser_mc = subparsers.add_parser('mc', help="Work with the management controller") 4848 mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4849 parser_MCReset = mc_sub.add_parser('reset', help='Reset the bmc' ) 4850 parser_MCReset.add_argument('type', choices=['warm','cold'], help="Reboot the BMC") 4851 #parser_MCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration") 4852 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.") 4853 parser_MCReset.set_defaults(func=bmcReset) 4854 parser_mc.set_defaults(func=bmc) 4855 4856 #gard clear 4857 parser_gc = subparsers.add_parser("gardclear", help="Used to clear gard records") 4858 parser_gc.set_defaults(func=gardClear) 4859 4860 #firmware_flash 4861 parser_fw = subparsers.add_parser("firmware", help="Work with the system firmware") 4862 fwflash_subproc = parser_fw.add_subparsers(title='subcommands', description='valid firmware commands', help='sub-command help', dest='command') 4863 fwflash_subproc.required = True 4864 4865 fwflash = fwflash_subproc.add_parser('flash', help="Flash the system firmware") 4866 fwflash.add_argument('type', choices=['bmc', 'pnor'], help="image type to flash") 4867 fwflash.add_argument('-f', '--fileloc', required=True, help="The absolute path to the firmware image") 4868 fwflash.set_defaults(func=fwFlash) 4869 4870 fwActivate = fwflash_subproc.add_parser('activate', help="Activate existing image on the bmc") 4871 fwActivate.add_argument('imageID', help="The image ID to activate from the firmware list. Ex: 63c95399") 4872 fwActivate.set_defaults(func=activateFWImage) 4873 4874 fwActivateStatus = fwflash_subproc.add_parser('activation_status', help="Check Status of activations") 4875 fwActivateStatus.set_defaults(func=activateStatus) 4876 4877 fwList = fwflash_subproc.add_parser('list', help="List all of the installed firmware") 4878 fwList.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4879 fwList.set_defaults(func=firmwareList) 4880 4881 fwprint = fwflash_subproc.add_parser('print', help="List all of the installed firmware") 4882 fwprint.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4883 fwprint.set_defaults(func=firmwareList) 4884 4885 fwDelete = fwflash_subproc.add_parser('delete', help="Delete an existing firmware version") 4886 fwDelete.add_argument('versionID', help="The version ID to delete from the firmware list. Ex: 63c95399") 4887 fwDelete.set_defaults(func=deleteFWVersion) 4888 4889 #logging 4890 parser_logging = subparsers.add_parser("logging", help="logging controls") 4891 logging_sub = parser_logging.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4892 4893 #turn rest api logging on/off 4894 parser_rest_logging = logging_sub.add_parser("rest_api", help="turn rest api logging on/off") 4895 parser_rest_logging.add_argument('rest_logging', choices=['on', 'off'], help='The control option for rest logging: on, off') 4896 parser_rest_logging.set_defaults(func=restLogging) 4897 4898 #remote logging 4899 parser_remote_logging = logging_sub.add_parser("remote_logging", help="Remote logging (rsyslog) commands") 4900 parser_remote_logging.add_argument('remote_logging', choices=['view', 'disable'], help='Remote logging (rsyslog) commands') 4901 parser_remote_logging.set_defaults(func=remoteLogging) 4902 4903 #configure remote logging 4904 parser_remote_logging_config = logging_sub.add_parser("remote_logging_config", help="Configure remote logging (rsyslog)") 4905 parser_remote_logging_config.add_argument("-a", "--address", required=True, help="Set IP address of rsyslog server") 4906 parser_remote_logging_config.add_argument("-p", "--port", required=True, type=int, help="Set Port of rsyslog server") 4907 parser_remote_logging_config.set_defaults(func=remoteLoggingConfig) 4908 4909 #certificate management 4910 parser_cert = subparsers.add_parser("certificate", help="Certificate management") 4911 certMgmt_subproc = parser_cert.add_subparsers(title='subcommands', description='valid certificate commands', help='sub-command help', dest='command') 4912 4913 certUpdate = certMgmt_subproc.add_parser('update', help="Update the certificate") 4914 certUpdate.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to update") 4915 certUpdate.add_argument('service', choices=['https', 'ldap'], help="Service to update") 4916 certUpdate.add_argument('-f', '--fileloc', required=True, help="The absolute path to the certificate file") 4917 certUpdate.set_defaults(func=certificateUpdate) 4918 4919 certDelete = certMgmt_subproc.add_parser('delete', help="Delete the certificate") 4920 certDelete.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to delete") 4921 certDelete.add_argument('service', choices=['https', 'ldap'], help="Service to delete the certificate") 4922 certDelete.set_defaults(func=certificateDelete) 4923 4924 certReplace = certMgmt_subproc.add_parser('replace', 4925 help="Replace the certificate") 4926 certReplace.add_argument('type', choices=['server', 'client', 'authority'], 4927 help="certificate type to replace") 4928 certReplace.add_argument('service', choices=['https', 'ldap'], 4929 help="Service to replace the certificate") 4930 certReplace.add_argument('-f', '--fileloc', required=True, 4931 help="The absolute path to the certificate file") 4932 certReplace.set_defaults(func=certificateReplace) 4933 4934 certDisplay = certMgmt_subproc.add_parser('display', 4935 help="Print the certificate") 4936 certDisplay.add_argument('type', choices=['server', 'client', 'authority'], 4937 help="certificate type to display") 4938 certDisplay.set_defaults(func=certificateDisplay) 4939 4940 certList = certMgmt_subproc.add_parser('list', 4941 help="Certificate list") 4942 certList.set_defaults(func=certificateList) 4943 4944 certGenerateCSR = certMgmt_subproc.add_parser('generatecsr', help="Generate CSR") 4945 certGenerateCSR.add_argument('type', choices=['server', 'client', 'authority'], 4946 help="Generate CSR") 4947 certGenerateCSR.add_argument('city', 4948 help="The city or locality of the organization making the request") 4949 certGenerateCSR.add_argument('commonName', 4950 help="The fully qualified domain name of the component that is being secured.") 4951 certGenerateCSR.add_argument('country', 4952 help="The country of the organization making the request") 4953 certGenerateCSR.add_argument('organization', 4954 help="The name of the organization making the request.") 4955 certGenerateCSR.add_argument('organizationUnit', 4956 help="The name of the unit or division of the organization making the request.") 4957 certGenerateCSR.add_argument('state', 4958 help="The state, province, or region of the organization making the request.") 4959 certGenerateCSR.add_argument('keyPairAlgorithm', choices=['RSA', 'EC'], 4960 help="The type of key pair for use with signing algorithms.") 4961 certGenerateCSR.add_argument('keyCurveId', 4962 help="The curve ID to be used with the key, if needed based on the value of the 'KeyPairAlgorithm' parameter.") 4963 certGenerateCSR.add_argument('contactPerson', 4964 help="The name of the user making the request") 4965 certGenerateCSR.add_argument('email', 4966 help="The email address of the contact within the organization") 4967 certGenerateCSR.add_argument('alternativeNames', 4968 help="Additional hostnames of the component that is being secured") 4969 certGenerateCSR.add_argument('givenname', 4970 help="The given name of the user making the request") 4971 certGenerateCSR.add_argument('surname', 4972 help="The surname of the user making the request") 4973 certGenerateCSR.add_argument('unstructuredname', 4974 help="he unstructured name of the subject") 4975 certGenerateCSR.add_argument('initials', 4976 help="The initials of the user making the request") 4977 certGenerateCSR.set_defaults(func=certificateGenerateCSR) 4978 4979 # local users 4980 parser_users = subparsers.add_parser("local_users", help="Work with local users") 4981 parser_users.add_argument('local_users', choices=['disableall','enableall', 'queryenabled'], help="Disable, enable or query local user accounts") 4982 parser_users.add_argument('-v', '--verbose', action='store_true', help='Verbose output') 4983 parser_users.set_defaults(func=localUsers) 4984 4985 #LDAP 4986 parser_ldap = subparsers.add_parser("ldap", help="LDAP controls") 4987 ldap_sub = parser_ldap.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command') 4988 4989 #configure and enable LDAP 4990 parser_ldap_config = ldap_sub.add_parser("enable", help="Configure and enables the LDAP") 4991 parser_ldap_config.add_argument("-a", "--uri", required=True, help="Set LDAP server URI") 4992 parser_ldap_config.add_argument("-B", "--bindDN", required=True, help="Set the bind DN of the LDAP server") 4993 parser_ldap_config.add_argument("-b", "--baseDN", required=True, help="Set the base DN of the LDAP server") 4994 parser_ldap_config.add_argument("-p", "--bindPassword", required=True, help="Set the bind password of the LDAP server") 4995 parser_ldap_config.add_argument("-S", "--scope", choices=['sub','one', 'base'], 4996 help='Specifies the search scope:subtree, one level or base object.') 4997 parser_ldap_config.add_argument("-t", "--serverType", required=True, choices=['ActiveDirectory','OpenLDAP'], 4998 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 4999 parser_ldap_config.add_argument("-g","--groupAttrName", required=False, default='', help="Group Attribute Name") 5000 parser_ldap_config.add_argument("-u","--userAttrName", required=False, default='', help="User Attribute Name") 5001 parser_ldap_config.set_defaults(func=enableLDAPConfig) 5002 5003 # disable LDAP 5004 parser_disable_ldap = ldap_sub.add_parser("disable", help="disables the LDAP") 5005 parser_disable_ldap.set_defaults(func=disableLDAP) 5006 # view-config 5007 parser_ldap_config = \ 5008 ldap_sub.add_parser("view-config", help="prints out a list of all \ 5009 LDAPS's configured properties") 5010 parser_ldap_config.set_defaults(func=viewLDAPConfig) 5011 5012 #create group privilege mapping 5013 parser_ldap_mapper = ldap_sub.add_parser("privilege-mapper", help="LDAP group privilege controls") 5014 parser_ldap_mapper_sub = parser_ldap_mapper.add_subparsers(title='subcommands', description='valid subcommands', 5015 help="sub-command help", dest='command') 5016 5017 parser_ldap_mapper_create = parser_ldap_mapper_sub.add_parser("create", help="Create mapping of ldap group and privilege") 5018 parser_ldap_mapper_create.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 5019 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 5020 parser_ldap_mapper_create.add_argument("-g","--groupName",required=True,help="Group Name") 5021 parser_ldap_mapper_create.add_argument("-p","--privilege",choices=['priv-admin','priv-operator','priv-user','priv-callback'],required=True,help="Privilege") 5022 parser_ldap_mapper_create.set_defaults(func=createPrivilegeMapping) 5023 5024 #list group privilege mapping 5025 parser_ldap_mapper_list = parser_ldap_mapper_sub.add_parser("list",help="List privilege mapping") 5026 parser_ldap_mapper_list.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 5027 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 5028 parser_ldap_mapper_list.set_defaults(func=listPrivilegeMapping) 5029 5030 #delete group privilege mapping 5031 parser_ldap_mapper_delete = parser_ldap_mapper_sub.add_parser("delete",help="Delete privilege mapping") 5032 parser_ldap_mapper_delete.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 5033 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 5034 parser_ldap_mapper_delete.add_argument("-g","--groupName",required=True,help="Group Name") 5035 parser_ldap_mapper_delete.set_defaults(func=deletePrivilegeMapping) 5036 5037 #deleteAll group privilege mapping 5038 parser_ldap_mapper_delete = parser_ldap_mapper_sub.add_parser("purge",help="Delete All privilege mapping") 5039 parser_ldap_mapper_delete.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'], 5040 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap') 5041 parser_ldap_mapper_delete.set_defaults(func=deleteAllPrivilegeMapping) 5042 5043 # set local user password 5044 parser_set_password = subparsers.add_parser("set_password", 5045 help="Set password of local user") 5046 parser_set_password.add_argument( "-p", "--password", required=True, 5047 help="Password of local user") 5048 parser_set_password.set_defaults(func=setPassword) 5049 5050 # network 5051 parser_nw = subparsers.add_parser("network", help="network controls") 5052 nw_sub = parser_nw.add_subparsers(title='subcommands', 5053 description='valid subcommands', 5054 help="sub-command help", 5055 dest='command') 5056 5057 # enable DHCP 5058 parser_enable_dhcp = nw_sub.add_parser("enableDHCP", 5059 help="enables the DHCP on given " 5060 "Interface") 5061 parser_enable_dhcp.add_argument("-I", "--Interface", required=True, 5062 help="Name of the ethernet interface(it can" 5063 "be obtained by the " 5064 "command:network view-config)" 5065 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5066 parser_enable_dhcp.set_defaults(func=enableDHCP) 5067 5068 # disable DHCP 5069 parser_disable_dhcp = nw_sub.add_parser("disableDHCP", 5070 help="disables the DHCP on given " 5071 "Interface") 5072 parser_disable_dhcp.add_argument("-I", "--Interface", required=True, 5073 help="Name of the ethernet interface(it can" 5074 "be obtained by the " 5075 "command:network view-config)" 5076 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5077 parser_disable_dhcp.set_defaults(func=disableDHCP) 5078 5079 # get HostName 5080 parser_gethostname = nw_sub.add_parser("getHostName", 5081 help="prints out HostName") 5082 parser_gethostname.set_defaults(func=getHostname) 5083 5084 # set HostName 5085 parser_sethostname = nw_sub.add_parser("setHostName", help="sets HostName") 5086 parser_sethostname.add_argument("-H", "--HostName", required=True, 5087 help="A HostName for the BMC") 5088 parser_sethostname.set_defaults(func=setHostname) 5089 5090 # get domainname 5091 parser_getdomainname = nw_sub.add_parser("getDomainName", 5092 help="prints out DomainName of " 5093 "given Interface") 5094 parser_getdomainname.add_argument("-I", "--Interface", required=True, 5095 help="Name of the ethernet interface(it " 5096 "can be obtained by the " 5097 "command:network view-config)" 5098 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5099 parser_getdomainname.set_defaults(func=getDomainName) 5100 5101 # set domainname 5102 parser_setdomainname = nw_sub.add_parser("setDomainName", 5103 help="sets DomainName of given " 5104 "Interface") 5105 parser_setdomainname.add_argument("-D", "--DomainName", required=True, 5106 help="Ex: DomainName=Domain1,Domain2,...") 5107 parser_setdomainname.add_argument("-I", "--Interface", required=True, 5108 help="Name of the ethernet interface(it " 5109 "can be obtained by the " 5110 "command:network view-config)" 5111 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5112 parser_setdomainname.set_defaults(func=setDomainName) 5113 5114 # get MACAddress 5115 parser_getmacaddress = nw_sub.add_parser("getMACAddress", 5116 help="prints out MACAddress the " 5117 "given Interface") 5118 parser_getmacaddress.add_argument("-I", "--Interface", required=True, 5119 help="Name of the ethernet interface(it " 5120 "can be obtained by the " 5121 "command:network view-config)" 5122 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5123 parser_getmacaddress.set_defaults(func=getMACAddress) 5124 5125 # set MACAddress 5126 parser_setmacaddress = nw_sub.add_parser("setMACAddress", 5127 help="sets MACAddress") 5128 parser_setmacaddress.add_argument("-MA", "--MACAddress", required=True, 5129 help="A MACAddress for the given " 5130 "Interface") 5131 parser_setmacaddress.add_argument("-I", "--Interface", required=True, 5132 help="Name of the ethernet interface(it can" 5133 "be obtained by the " 5134 "command:network view-config)" 5135 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5136 parser_setmacaddress.set_defaults(func=setMACAddress) 5137 5138 # get DefaultGW 5139 parser_getdefaultgw = nw_sub.add_parser("getDefaultGW", 5140 help="prints out DefaultGateway " 5141 "the BMC") 5142 parser_getdefaultgw.set_defaults(func=getDefaultGateway) 5143 5144 # set DefaultGW 5145 parser_setdefaultgw = nw_sub.add_parser("setDefaultGW", 5146 help="sets DefaultGW") 5147 parser_setdefaultgw.add_argument("-GW", "--DefaultGW", required=True, 5148 help="A DefaultGateway for the BMC") 5149 parser_setdefaultgw.set_defaults(func=setDefaultGateway) 5150 5151 # view network Config 5152 parser_ldap_config = nw_sub.add_parser("view-config", help="prints out a " 5153 "list of all network's configured " 5154 "properties") 5155 parser_ldap_config.set_defaults(func=viewNWConfig) 5156 5157 # get DNS 5158 parser_getDNS = nw_sub.add_parser("getDNS", 5159 help="prints out DNS servers on the " 5160 "given interface") 5161 parser_getDNS.add_argument("-I", "--Interface", required=True, 5162 help="Name of the ethernet interface(it can" 5163 "be obtained by the " 5164 "command:network view-config)" 5165 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5166 parser_getDNS.set_defaults(func=getDNS) 5167 5168 # set DNS 5169 parser_setDNS = nw_sub.add_parser("setDNS", 5170 help="sets DNS servers on the given " 5171 "interface") 5172 parser_setDNS.add_argument("-d", "--DNSServers", required=True, 5173 help="Ex: DNSSERVERS=DNS1,DNS2,...") 5174 parser_setDNS.add_argument("-I", "--Interface", required=True, 5175 help="Name of the ethernet interface(it can" 5176 "be obtained by the " 5177 "command:network view-config)" 5178 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5179 parser_setDNS.set_defaults(func=setDNS) 5180 5181 # get NTP 5182 parser_getNTP = nw_sub.add_parser("getNTP", 5183 help="prints out NTP servers on the " 5184 "given interface") 5185 parser_getNTP.add_argument("-I", "--Interface", required=True, 5186 help="Name of the ethernet interface(it can" 5187 "be obtained by the " 5188 "command:network view-config)" 5189 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5190 parser_getNTP.set_defaults(func=getNTP) 5191 5192 # set NTP 5193 parser_setNTP = nw_sub.add_parser("setNTP", 5194 help="sets NTP servers on the given " 5195 "interface") 5196 parser_setNTP.add_argument("-N", "--NTPServers", required=True, 5197 help="Ex: NTPSERVERS=NTP1,NTP2,...") 5198 parser_setNTP.add_argument("-I", "--Interface", required=True, 5199 help="Name of the ethernet interface(it can" 5200 "be obtained by the " 5201 "command:network view-config)" 5202 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5203 parser_setNTP.set_defaults(func=setNTP) 5204 5205 # configure IP 5206 parser_ip_config = nw_sub.add_parser("addIP", help="Sets IP address to" 5207 "given interface") 5208 parser_ip_config.add_argument("-a", "--address", required=True, 5209 help="IP address of given interface") 5210 parser_ip_config.add_argument("-gw", "--gateway", required=False, default='', 5211 help="The gateway for given interface") 5212 parser_ip_config.add_argument("-l", "--prefixLength", required=True, 5213 help="The prefixLength of IP address") 5214 parser_ip_config.add_argument("-p", "--type", required=True, 5215 choices=['ipv4', 'ipv6'], 5216 help="The protocol type of the given" 5217 "IP address") 5218 parser_ip_config.add_argument("-I", "--Interface", required=True, 5219 help="Name of the ethernet interface(it can" 5220 "be obtained by the " 5221 "command:network view-config)" 5222 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5223 parser_ip_config.set_defaults(func=addIP) 5224 5225 # getIP 5226 parser_getIP = nw_sub.add_parser("getIP", help="prints out IP address" 5227 "of given interface") 5228 parser_getIP.add_argument("-I", "--Interface", required=True, 5229 help="Name of the ethernet interface(it can" 5230 "be obtained by the command:network view-config)" 5231 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5232 parser_getIP.set_defaults(func=getIP) 5233 5234 # rmIP 5235 parser_rmIP = nw_sub.add_parser("rmIP", help="deletes IP address" 5236 "of given interface") 5237 parser_rmIP.add_argument("-a", "--address", required=True, 5238 help="IP address to remove form given Interface") 5239 parser_rmIP.add_argument("-I", "--Interface", required=True, 5240 help="Name of the ethernet interface(it can" 5241 "be obtained by the command:network view-config)" 5242 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5243 parser_rmIP.set_defaults(func=deleteIP) 5244 5245 # add VLAN 5246 parser_create_vlan = nw_sub.add_parser("addVLAN", help="enables VLAN " 5247 "on given interface with given " 5248 "VLAN Identifier") 5249 parser_create_vlan.add_argument("-I", "--Interface", required=True, 5250 choices=['eth0', 'eth1'], 5251 help="Name of the ethernet interface") 5252 parser_create_vlan.add_argument("-n", "--Identifier", required=True, 5253 help="VLAN Identifier") 5254 parser_create_vlan.set_defaults(func=addVLAN) 5255 5256 # delete VLAN 5257 parser_delete_vlan = nw_sub.add_parser("deleteVLAN", help="disables VLAN " 5258 "on given interface with given " 5259 "VLAN Identifier") 5260 parser_delete_vlan.add_argument("-I", "--Interface", required=True, 5261 help="Name of the ethernet interface(it can" 5262 "be obtained by the " 5263 "command:network view-config)" 5264 "Ex: eth0 or eth1 or VLAN(VLAN=eth0_50 etc)") 5265 parser_delete_vlan.set_defaults(func=deleteVLAN) 5266 5267 # viewDHCPConfig 5268 parser_viewDHCPConfig = nw_sub.add_parser("viewDHCPConfig", 5269 help="Shows DHCP configured " 5270 "Properties") 5271 parser_viewDHCPConfig.set_defaults(func=viewDHCPConfig) 5272 5273 # configureDHCP 5274 parser_configDHCP = nw_sub.add_parser("configureDHCP", 5275 help="Configures/updates DHCP " 5276 "Properties") 5277 parser_configDHCP.add_argument("-d", "--DNSEnabled", type=str2bool, 5278 required=True, help="Sets DNSEnabled property") 5279 parser_configDHCP.add_argument("-n", "--HostNameEnabled", type=str2bool, 5280 required=True, 5281 help="Sets HostNameEnabled property") 5282 parser_configDHCP.add_argument("-t", "--NTPEnabled", type=str2bool, 5283 required=True, 5284 help="Sets NTPEnabled property") 5285 parser_configDHCP.add_argument("-s", "--SendHostNameEnabled", type=str2bool, 5286 required=True, 5287 help="Sets SendHostNameEnabled property") 5288 parser_configDHCP.set_defaults(func=configureDHCP) 5289 5290 # network factory reset 5291 parser_nw_reset = nw_sub.add_parser("nwReset", 5292 help="Resets networks setting to " 5293 "factory defaults. " 5294 "note:Reset settings will be applied " 5295 "after BMC reboot") 5296 parser_nw_reset.set_defaults(func=nwReset) 5297 5298 return parser 5299 5300def main(argv=None): 5301 """ 5302 main function for running the command line utility as a sub application 5303 """ 5304 global toolVersion 5305 toolVersion = "1.19" 5306 global isRedfishSupport 5307 5308 parser = createCommandParser() 5309 args = parser.parse_args(argv) 5310 5311 totTimeStart = int(round(time.time()*1000)) 5312 5313 if(sys.version_info < (3,0)): 5314 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 5315 if sys.version_info >= (3,0): 5316 requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) 5317 if (args.version): 5318 print("Version: "+ toolVersion) 5319 sys.exit(0) 5320 if (hasattr(args, 'fileloc') and args.fileloc is not None and 'print' in args.command): 5321 mysess = None 5322 print(selPrint('N/A', args, mysess)) 5323 else: 5324 if(hasattr(args, 'host') and hasattr(args,'user')): 5325 if (args.askpw): 5326 pw = getpass.getpass() 5327 elif(args.PW is not None): 5328 pw = args.PW 5329 elif(args.PWenvvar): 5330 pw = os.environ['OPENBMCTOOL_PASSWORD'] 5331 else: 5332 print("You must specify a password") 5333 sys.exit() 5334 logintimeStart = int(round(time.time()*1000)) 5335 mysess = login(args.host, args.user, pw, args.json, 5336 args.command == 'set_password') 5337 if(mysess == None): 5338 print("Login Failed!") 5339 sys.exit() 5340 if(sys.version_info < (3,0)): 5341 if isinstance(mysess, basestring): 5342 print(mysess) 5343 sys.exit(1) 5344 elif sys.version_info >= (3,0): 5345 if isinstance(mysess, str): 5346 print(mysess) 5347 sys.exit(1) 5348 logintimeStop = int(round(time.time()*1000)) 5349 isRedfishSupport = redfishSupportPresent(args.host,mysess) 5350 commandTimeStart = int(round(time.time()*1000)) 5351 output = args.func(args.host, args, mysess) 5352 commandTimeStop = int(round(time.time()*1000)) 5353 if isinstance(output, dict): 5354 print(json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)) 5355 else: 5356 print(output) 5357 if (mysess is not None): 5358 logout(args.host, args.user, pw, mysess, args.json) 5359 if(args.procTime): 5360 print("Total time: " + str(int(round(time.time()*1000))- totTimeStart)) 5361 print("loginTime: " + str(logintimeStop - logintimeStart)) 5362 print("command Time: " + str(commandTimeStop - commandTimeStart)) 5363 else: 5364 print("usage:\n" 5365 " OPENBMCTOOL_PASSWORD=secret # if using -E\n" 5366 " openbmctool.py [-h] -H HOST -U USER {-A | -P PW | -E} [-j]\n" + 5367 "\t[-t POLICYTABLELOC] [-V]\n" + 5368 "\t{fru,sensors,sel,chassis,collect_service_data, \ 5369 health_check,dump,bmc,mc,gardclear,firmware,logging}\n" + 5370 "\t...\n" + 5371 "openbmctool.py: error: the following arguments are required: -H/--host, -U/--user") 5372 sys.exit() 5373 5374if __name__ == '__main__': 5375 """ 5376 main function when called from the command line 5377 5378 """ 5379 import sys 5380 5381 isTTY = sys.stdout.isatty() 5382 assert sys.version_info >= (2,7) 5383 main() 5384