blob: d61a35c66b668045d7eacbe90e523436c431ec7d [file] [log] [blame]
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001#!/usr/bin/python3
Justin Thalere412dc22018-01-12 16:28:24 -06002"""
3 Copyright 2017 IBM Corporation
Justin Thalerf9aee3e2017-12-05 12:11:09 -06004
Justin Thalere412dc22018-01-12 16:28:24 -06005 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"""
Justin Thalerf9aee3e2017-12-05 12:11:09 -060017import argparse
18import requests
19import getpass
20import json
21import os
22import urllib3
23import time, datetime
Justin Thalerf9aee3e2017-12-05 12:11:09 -060024import binascii
25import subprocess
26import platform
27import zipfile
28
Justin Thalerf9aee3e2017-12-05 12:11:09 -060029def hilight(textToColor, color, bold):
Justin Thalere412dc22018-01-12 16:28:24 -060030 """
31 Used to add highlights to various text for displaying in a terminal
32
33 @param textToColor: string, the text to be colored
34 @param color: string, used to color the text red or green
35 @param bold: boolean, used to bold the textToColor
36 @return: Buffered reader containing the modified string.
37 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -060038 if(sys.platform.__contains__("win")):
39 if(color == "red"):
40 os.system('color 04')
41 elif(color == "green"):
42 os.system('color 02')
43 else:
44 os.system('color') #reset to default
45 return textToColor
46 else:
47 attr = []
48 if(color == "red"):
49 attr.append('31')
50 elif(color == "green"):
51 attr.append('32')
52 else:
53 attr.append('0')
54 if bold:
55 attr.append('1')
56 else:
57 attr.append('0')
58 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr),textToColor)
59
Justin Thalere412dc22018-01-12 16:28:24 -060060
Justin Thalerf9aee3e2017-12-05 12:11:09 -060061def connectionErrHandler(jsonFormat, errorStr, err):
Justin Thalere412dc22018-01-12 16:28:24 -060062 """
63 Error handler various connection errors to bmcs
64
65 @param jsonFormat: boolean, used to output in json format with an error code.
66 @param errorStr: string, used to color the text red or green
67 @param err: string, the text from the exception
68 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -060069 if errorStr == "Timeout":
70 if not jsonFormat:
71 return("FQPSPIN0000M: Connection timed out. Ensure you have network connectivity to the bmc")
72 else:
73 errorMessageStr = ("{\n\t\"event0\":{\n" +
74 "\t\t\"CommonEventID\": \"FQPSPIN0000M\",\n"+
75 "\t\t\"sensor\": \"N/A\",\n"+
76 "\t\t\"state\": \"N/A\",\n" +
77 "\t\t\"additionalDetails\": \"N/A\",\n" +
78 "\t\t\"Message\": \"Connection timed out. Ensure you have network connectivity to the BMC\",\n" +
79 "\t\t\"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.\",\n" +
80 "\t\t\"Serviceable\": \"Yes\",\n" +
81 "\t\t\"CallHomeCandidate\": \"No\",\n" +
82 "\t\t\"Severity\": \"Critical\",\n" +
83 "\t\t\"EventType\": \"Communication Failure/Timeout\",\n" +
84 "\t\t\"VMMigrationFlag\": \"Yes\",\n" +
85 "\t\t\"AffectedSubsystem\": \"Interconnect (Networking)\",\n" +
86 "\t\t\"timestamp\": \""+str(int(time.time()))+"\",\n" +
87 "\t\t\"UserAction\": \"Verify network connectivity between the two systems and the bmc is functional.\"" +
88 "\t\n}, \n" +
89 "\t\"numAlerts\": \"1\" \n}");
90 return(errorMessageStr)
91 elif errorStr == "ConnectionError":
92 if not jsonFormat:
93 return("FQPSPIN0001M: " + str(err))
94 else:
95 errorMessageStr = ("{\n\t\"event0\":{\n" +
96 "\t\t\"CommonEventID\": \"FQPSPIN0001M\",\n"+
97 "\t\t\"sensor\": \"N/A\",\n"+
98 "\t\t\"state\": \"N/A\",\n" +
99 "\t\t\"additionalDetails\": \"" + str(err)+"\",\n" +
100 "\t\t\"Message\": \"Connection Error. View additional details for more information\",\n" +
101 "\t\t\"LengthyDescription\": \"A connection error to the specified BMC occurred and additional details are provided. Review these details to resolve the issue.\",\n" +
102 "\t\t\"Serviceable\": \"Yes\",\n" +
103 "\t\t\"CallHomeCandidate\": \"No\",\n" +
104 "\t\t\"Severity\": \"Critical\",\n" +
105 "\t\t\"EventType\": \"Communication Failure/Timeout\",\n" +
106 "\t\t\"VMMigrationFlag\": \"Yes\",\n" +
107 "\t\t\"AffectedSubsystem\": \"Interconnect (Networking)\",\n" +
108 "\t\t\"timestamp\": \""+str(int(time.time()))+"\",\n" +
109 "\t\t\"UserAction\": \"Correct the issue highlighted in additional details and try again\"" +
110 "\t\n}, \n" +
111 "\t\"numAlerts\": \"1\" \n}");
112 return(errorMessageStr)
113 else:
114 return("Unknown Error: "+ str(err))
115
Justin Thalere412dc22018-01-12 16:28:24 -0600116
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600117def setColWidth(keylist, numCols, dictForOutput, colNames):
Justin Thalere412dc22018-01-12 16:28:24 -0600118 """
119 Sets the output width of the columns to display
120
121 @param keylist: list, list of strings representing the keys for the dictForOutput
122 @param numcols: the total number of columns in the final output
123 @param dictForOutput: dictionary, contains the information to print to the screen
124 @param colNames: list, The strings to use for the column headings, in order of the keylist
125 @return: A list of the column widths for each respective column.
126 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600127 colWidths = []
128 for x in range(0, numCols):
129 colWidths.append(0)
130 for key in dictForOutput:
131 for x in range(0, numCols):
132 colWidths[x] = max(colWidths[x], len(str(dictForOutput[key][keylist[x]])))
133
134 for x in range(0, numCols):
135 colWidths[x] = max(colWidths[x], len(colNames[x])) +2
136
137 return colWidths
138
139def loadPolicyTable(pathToPolicyTable):
Justin Thalere412dc22018-01-12 16:28:24 -0600140 """
141 loads a json based policy table into a dictionary
142
143 @param value: boolean, the value to convert
144 @return: A string of "Yes" or "No"
145 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600146 policyTable = {}
147 if(os.path.exists(pathToPolicyTable)):
148 with open(pathToPolicyTable, 'r') as stream:
149 try:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600150 contents =json.load(stream)
151 policyTable = contents['events']
Justin Thalere412dc22018-01-12 16:28:24 -0600152 except Exception as err:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600153 print(err)
154 return policyTable
155
Justin Thalere412dc22018-01-12 16:28:24 -0600156
157def boolToString(value):
158 """
159 converts a boolean value to a human readable string value
160
161 @param value: boolean, the value to convert
162 @return: A string of "Yes" or "No"
163 """
164 if(value):
165 return "Yes"
166 else:
167 return "No"
168
169
170def tableDisplay(keylist, colNames, output):
171 """
172 Logs into the BMC and creates a session
173
174 @param keylist: list, keys for the output dictionary, ordered by colNames
175 @param colNames: Names for the Table of the columns
176 @param output: The dictionary of data to display
177 @return: Session object
178 """
179 colWidth = setColWidth(keylist, len(colNames), output, colNames)
180 row = ""
181 outputText = ""
182 for i in range(len(colNames)):
183 if (i != 0): row = row + "| "
184 row = row + colNames[i].ljust(colWidth[i])
185 outputText += row + "\n"
186
187 for key in sorted(output.keys()):
188 row = ""
189 for i in range(len(output[key])):
190 if (i != 0): row = row + "| "
191 row = row + output[key][keylist[i]].ljust(colWidth[i])
192 outputText += row + "\n"
193
194 return outputText
195
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600196def login(host, username, pw,jsonFormat):
Justin Thalere412dc22018-01-12 16:28:24 -0600197 """
198 Logs into the BMC and creates a session
199
200 @param host: string, the hostname or IP address of the bmc to log into
201 @param username: The user name for the bmc to log into
202 @param pw: The password for the BMC to log into
203 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
204 @return: Session object
205 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600206 if(jsonFormat==False):
207 print("Attempting login...")
208 httpHeader = {'Content-Type':'application/json'}
209 mysess = requests.session()
210 try:
211 r = mysess.post('https://'+host+'/login', headers=httpHeader, json = {"data": [username, pw]}, verify=False, timeout=30)
212 loginMessage = json.loads(r.text)
213 if (loginMessage['status'] != "ok"):
214 print(loginMessage["data"]["description"].encode('utf-8'))
215 sys.exit(1)
216# if(sys.version_info < (3,0)):
217# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
218# if sys.version_info >= (3,0):
219# requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
220 return mysess
221 except(requests.exceptions.Timeout):
222 print(connectionErrHandler(jsonFormat, "Timeout", None))
223 sys.exit(1)
224 except(requests.exceptions.ConnectionError) as err:
225 print(connectionErrHandler(jsonFormat, "ConnectionError", err))
226 sys.exit(1)
227
Justin Thalere412dc22018-01-12 16:28:24 -0600228
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600229def logout(host, username, pw, session, jsonFormat):
Justin Thalere412dc22018-01-12 16:28:24 -0600230 """
231 Logs out of the bmc and terminates the session
232
233 @param host: string, the hostname or IP address of the bmc to log out of
234 @param username: The user name for the bmc to log out of
235 @param pw: The password for the BMC to log out of
236 @param session: the active session to use
237 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
238 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600239 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600240 try:
241 r = session.post('https://'+host+'/logout', headers=httpHeader,json = {"data": [username, pw]}, verify=False, timeout=10)
242 except(requests.exceptions.Timeout):
243 print(connectionErrHandler(jsonFormat, "Timeout", None))
244
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600245 if(jsonFormat==False):
246 if('"message": "200 OK"' in r.text):
247 print('User ' +username + ' has been logged out')
248
Justin Thalere412dc22018-01-12 16:28:24 -0600249
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600250def fru(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600251 """
252 prints out the system inventory. deprecated see fruPrint and fruList
253
254 @param host: string, the hostname or IP address of the bmc
255 @param args: contains additional arguments used by the fru sub command
256 @param session: the active session to use
257 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
258 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600259 #url="https://"+host+"/org/openbmc/inventory/system/chassis/enumerate"
260
261 #print(url)
262 #res = session.get(url, headers=httpHeader, verify=False)
263 #print(res.text)
264 #sample = res.text
265
266 #inv_list = json.loads(sample)["data"]
267
268 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
269 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600270 try:
271 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
272 except(requests.exceptions.Timeout):
273 return(connectionErrHandler(args.json, "Timeout", None))
274
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600275 sample = res.text
276# inv_list.update(json.loads(sample)["data"])
277#
278# #determine column width's
279# colNames = ["FRU Name", "FRU Type", "Has Fault", "Is FRU", "Present", "Version"]
280# colWidths = setColWidth(["FRU Name", "fru_type", "fault", "is_fru", "present", "version"], 6, inv_list, colNames)
281#
282# print("FRU Name".ljust(colWidths[0])+ "FRU Type".ljust(colWidths[1]) + "Has Fault".ljust(colWidths[2]) + "Is FRU".ljust(colWidths[3])+
283# "Present".ljust(colWidths[4]) + "Version".ljust(colWidths[5]))
284# format the output
285# for key in sorted(inv_list.keys()):
286# keyParts = key.split("/")
287# isFRU = "True" if (inv_list[key]["is_fru"]==1) else "False"
288#
289# fruEntry = (keyParts[len(keyParts) - 1].ljust(colWidths[0]) + inv_list[key]["fru_type"].ljust(colWidths[1])+
290# inv_list[key]["fault"].ljust(colWidths[2])+isFRU.ljust(colWidths[3])+
291# inv_list[key]["present"].ljust(colWidths[4])+ inv_list[key]["version"].ljust(colWidths[5]))
292# if(isTTY):
293# if(inv_list[key]["is_fru"] == 1):
294# color = "green"
295# bold = True
296# else:
297# color='black'
298# bold = False
299# fruEntry = hilight(fruEntry, color, bold)
300# print (fruEntry)
301 return sample
Justin Thalere412dc22018-01-12 16:28:24 -0600302
303def fruPrint(host, args, session):
304 """
305 prints out all inventory
306
307 @param host: string, the hostname or IP address of the bmc
308 @param args: contains additional arguments used by the fru sub command
309 @param session: the active session to use
310 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
311 @return returns the total fru list.
312 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600313 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
314 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600315 try:
316 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
317 except(requests.exceptions.Timeout):
318 return(connectionErrHandler(args.json, "Timeout", None))
319
320
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600321# print(res.text)
322 frulist = res.text
323 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600324 try:
325 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
326 except(requests.exceptions.Timeout):
327 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600328# print(res.text)
329 frulist = frulist +"\n" + res.text
330
331 return frulist
332
Justin Thalere412dc22018-01-12 16:28:24 -0600333
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600334def fruList(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600335 """
336 prints out all inventory or only a specific specified item
337
338 @param host: string, the hostname or IP address of the bmc
339 @param args: contains additional arguments used by the fru sub command
340 @param session: the active session to use
341 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
342 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600343 if(args.items==True):
344 return fruPrint(host, args, session)
345 else:
Justin Thalere412dc22018-01-12 16:28:24 -0600346 return fruPrint(host, args, session)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600347
348
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600349
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600350def fruStatus(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600351 """
352 prints out the status of all FRUs
353
354 @param host: string, the hostname or IP address of the bmc
355 @param args: contains additional arguments used by the fru sub command
356 @param session: the active session to use
357 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
358 """
359 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600360 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600361 try:
362 res = session.get(url, headers=httpHeader, verify=False)
363 except(requests.exceptions.Timeout):
364 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600365# print(res.text)
Justin Thalere412dc22018-01-12 16:28:24 -0600366 frulist = json.loads(res.text)['data']
367 frus = {}
368 for key in frulist:
369 component = frulist[key]
370 isFru = False
371 present = False
372 func = False
373 hasSels = False
374 keyPieces = key.split('/')
375 fruName = keyPieces[-1]
376 if 'core' in fruName: #associate cores to cpus
377 fruName = keyPieces[-2] + '-' + keyPieces[-1]
378 if 'Functional' in component:
379 if('Present' in component):
380
381 if 'FieldReplaceable' in component:
382 if component['FieldReplaceable'] == 1:
383 isFru = True
384 if "fan" in fruName:
385 isFru = True;
386 if component['Present'] == 1:
387 present = True
388 if component['Functional'] == 1:
389 func = True
390 if ((key + "/fault") in frulist):
391 hasSels = True;
392 if args.verbose:
393 if hasSels:
394 loglist = []
395 faults = frulist[key+"/fault"]['endpoints']
396 for item in faults:
397 loglist.append(item.split('/')[-1])
398 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
399 else:
400 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
401 else:
402 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
403 elif "power_supply" in fruName:
404 if component['Present'] ==1:
405 present = True
406 isFru = True
407 if ((key + "/fault") in frulist):
408 hasSels = True;
409 if args.verbose:
410 if hasSels:
411 loglist = []
412 faults = frulist[key+"/fault"]['endpoints']
413 for key in faults:
414 loglist.append(faults[key].split('/')[-1])
415 frus[fruName] = {"compName": fruName, "Functional": "No", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
416 else:
417 frus[fruName] = {"compName": fruName, "Functional": "Yes", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
418 else:
419 frus[fruName] = {"compName": fruName, "Functional": boolToString(not hasSels), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
420 if not args.json:
421 if not args.verbose:
422 colNames = ["Component", "Is a FRU", "Present", "Functional", "Has Logs"]
423 keylist = ["compName", "IsFru", "Present", "Functional", "hasSEL"]
424 else:
425 colNames = ["Component", "Is a FRU", "Present", "Functional", "Assoc. Log Number(s)"]
426 keylist = ["compName", "IsFru", "Present", "Functional", "selList"]
427 return tableDisplay(keylist, colNames, frus)
428 else:
429 return str(json.dumps(frus, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
430
431def sensor(host, args, session):
432 """
433 prints out all sensors
434
435 @param host: string, the hostname or IP address of the bmc
436 @param args: contains additional arguments used by the sensor sub command
437 @param session: the active session to use
438 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
439 """
440 httpHeader = {'Content-Type':'application/json'}
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600441 url="https://"+host+"/xyz/openbmc_project/sensors/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600442 try:
443 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
444 except(requests.exceptions.Timeout):
445 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600446
447 #Get OCC status
448 url="https://"+host+"/org/open_power/control/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600449 try:
450 occres = session.get(url, headers=httpHeader, verify=False, timeout=30)
451 except(requests.exceptions.Timeout):
452 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600453 if not args.json:
454 colNames = ['sensor', 'type', 'units', 'value', 'target']
455 sensors = json.loads(res.text)["data"]
456 output = {}
457 for key in sensors:
458 senDict = {}
459 keyparts = key.split("/")
460 senDict['sensorName'] = keyparts[-1]
461 senDict['type'] = keyparts[-2]
Justin Thalere412dc22018-01-12 16:28:24 -0600462 try:
463 senDict['units'] = sensors[key]['Unit'].split('.')[-1]
464 except KeyError:
465 print('Key Error: '+ key)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600466 if('Scale' in sensors[key]):
467 scale = 10 ** sensors[key]['Scale']
468 else:
469 scale = 1
470 senDict['value'] = str(sensors[key]['Value'] * scale)
471 if 'Target' in sensors[key]:
472 senDict['target'] = str(sensors[key]['Target'])
473 else:
474 senDict['target'] = 'N/A'
475 output[senDict['sensorName']] = senDict
476
477 occstatus = json.loads(occres.text)["data"]
478 if '/org/open_power/control/occ0' in occstatus:
479 occ0 = occstatus["/org/open_power/control/occ0"]['OccActive']
480 if occ0 == 1:
481 occ0 = 'Active'
482 else:
483 occ0 = 'Inactive'
484 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'}
485 occ1 = occstatus["/org/open_power/control/occ1"]['OccActive']
486 if occ1 == 1:
487 occ1 = 'Active'
488 else:
489 occ1 = 'Inactive'
490 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'}
491 else:
492 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
493 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
494 keylist = ['sensorName', 'type', 'units', 'value', 'target']
Justin Thalere412dc22018-01-12 16:28:24 -0600495
496 return tableDisplay(keylist, colNames, output)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600497 else:
498 return res.text + occres.text
Justin Thalere412dc22018-01-12 16:28:24 -0600499
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600500def sel(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600501 """
502 prints out the bmc alerts
503
504 @param host: string, the hostname or IP address of the bmc
505 @param args: contains additional arguments used by the sel sub command
506 @param session: the active session to use
507 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
508 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600509
510 url="https://"+host+"/xyz/openbmc_project/logging/entry/enumerate"
511 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600512 try:
513 res = session.get(url, headers=httpHeader, verify=False, timeout=60)
514 except(requests.exceptions.Timeout):
515 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600516 return res.text
Justin Thalere412dc22018-01-12 16:28:24 -0600517
518
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600519def parseESEL(args, eselRAW):
Justin Thalere412dc22018-01-12 16:28:24 -0600520 """
521 parses the esel data and gets predetermined search terms
522
523 @param eselRAW: string, the raw esel string from the bmc
524 @return: A dictionary containing the quick snapshot data unless args.fullEsel is listed then a full PEL log is returned
525 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600526 eselParts = {}
527 esel_bin = binascii.unhexlify(''.join(eselRAW.split()[16:]))
528 #search terms contains the search term as the key and the return dictionary key as it's value
529 searchTerms = { 'Signature Description':'signatureDescription', 'devdesc':'devdesc',
530 'Callout type': 'calloutType', 'Procedure':'procedure'}
531
532 with open('/tmp/esel.bin', 'wb') as f:
533 f.write(esel_bin)
534 errlPath = ""
535 #use the right errl file for the machine architecture
536 arch = platform.machine()
537 if(arch =='x86_64' or arch =='AMD64'):
538 if os.path.exists('/opt/ibm/ras/bin/x86_64/errl'):
539 errlPath = '/opt/ibm/ras/bin/x86_64/errl'
540 elif os.path.exists('errl/x86_64/errl'):
541 errlPath = 'errl/x86_64/errl'
542 else:
543 errlPath = 'x86_64/errl'
544 elif (platform.machine()=='ppc64le'):
545 if os.path.exists('/opt/ibm/ras/bin/ppc64le/errl'):
546 errlPath = '/opt/ibm/ras/bin/ppc64le/errl'
547 elif os.path.exists('errl/ppc64le/errl'):
548 errlPath = 'errl/ppc64le/errl'
549 else:
550 errlPath = 'ppc64le/errl'
551 else:
552 print("machine architecture not supported for parsing eSELs")
553 return eselParts
554
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600555 if(os.path.exists(errlPath)):
556 output= subprocess.check_output([errlPath, '-d', '--file=/tmp/esel.bin']).decode('utf-8')
557# output = proc.communicate()[0]
558 lines = output.split('\n')
559
560 if(hasattr(args, 'fullEsel')):
561 return output
562
563 for i in range(0, len(lines)):
564 lineParts = lines[i].split(':')
565 if(len(lineParts)>1): #ignore multi lines, output formatting lines, and other information
566 for term in searchTerms:
567 if(term in lineParts[0]):
568 temp = lines[i][lines[i].find(':')+1:].strip()[:-1].strip()
569 if lines[i+1].find(':') != -1:
570 if (len(lines[i+1].split(':')[0][1:].strip())==0):
571 while(len(lines[i][:lines[i].find(':')].strip())>2):
572 if((i+1) <= len(lines)):
573 i+=1
574 else:
575 i=i-1
576 break
577 temp = temp + lines[i][lines[i].find(':'):].strip()[:-1].strip()[:-1].strip()
578 eselParts[searchTerms[term]] = temp
579 os.remove('/tmp/esel.bin')
580 else:
581 print("errl file cannot be found")
582
583 return eselParts
584
Justin Thalere412dc22018-01-12 16:28:24 -0600585
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600586def sortSELs(events):
Justin Thalere412dc22018-01-12 16:28:24 -0600587 """
588 sorts the sels by timestamp, then log entry number
589
590 @param events: Dictionary containing events
591 @return: list containing a list of the ordered log entries, and dictionary of keys
592 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600593 logNumList = []
594 timestampList = []
595 eventKeyDict = {}
596 eventsWithTimestamp = {}
597 logNum2events = {}
598 for key in events:
599 if key == 'numAlerts': continue
600 if 'callout' in key: continue
601 timestamp = (events[key]['timestamp'])
602 if timestamp not in timestampList:
603 eventsWithTimestamp[timestamp] = [events[key]['logNum']]
604 else:
605 eventsWithTimestamp[timestamp].append(events[key]['logNum'])
606 #map logNumbers to the event dictionary keys
607 eventKeyDict[str(events[key]['logNum'])] = key
608
609 timestampList = list(eventsWithTimestamp.keys())
610 timestampList.sort()
611 for ts in timestampList:
612 if len(eventsWithTimestamp[ts]) > 1:
613 tmplist = eventsWithTimestamp[ts]
614 tmplist.sort()
615 logNumList = logNumList + tmplist
616 else:
617 logNumList = logNumList + eventsWithTimestamp[ts]
618
619 return [logNumList, eventKeyDict]
620
Justin Thalere412dc22018-01-12 16:28:24 -0600621
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600622def parseAlerts(policyTable, selEntries, args):
Justin Thalere412dc22018-01-12 16:28:24 -0600623 """
624 parses alerts in the IBM CER format, using an IBM policy Table
625
626 @param policyTable: dictionary, the policy table entries
627 @param selEntries: dictionary, the alerts retrieved from the bmc
628 @return: A dictionary of the parsed entries, in chronological order
629 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600630 eventDict = {}
631 eventNum =""
632 count = 0
633 esel = ""
634 eselParts = {}
635 i2cdevice= ""
636
637 'prepare and sort the event entries'
638 for key in selEntries:
639 if 'callout' not in key:
640 selEntries[key]['logNum'] = key.split('/')[-1]
641 selEntries[key]['timestamp'] = selEntries[key]['Timestamp']
642 sortedEntries = sortSELs(selEntries)
643 logNumList = sortedEntries[0]
644 eventKeyDict = sortedEntries[1]
645
646 for logNum in logNumList:
647 key = eventKeyDict[logNum]
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600648 hasEsel=False
649 i2creadFail = False
650 if 'callout' in key:
651 continue
652 else:
653 messageID = str(selEntries[key]['Message'])
654 addDataPiece = selEntries[key]['AdditionalData']
655 calloutIndex = 0
656 calloutFound = False
657 for i in range(len(addDataPiece)):
658 if("CALLOUT_INVENTORY_PATH" in addDataPiece[i]):
659 calloutIndex = i
660 calloutFound = True
661 fruCallout = str(addDataPiece[calloutIndex]).split('=')[1]
662 if("CALLOUT_DEVICE_PATH" in addDataPiece[i]):
663 i2creadFail = True
664 i2cdevice = str(addDataPiece[i]).strip().split('=')[1]
665 i2cdevice = '/'.join(i2cdevice.split('/')[-4:])
666 fruCallout = 'I2C'
667 calloutFound = True
668 if("ESEL" in addDataPiece[i]):
669 esel = str(addDataPiece[i]).strip().split('=')[1]
670 if args.devdebug:
671 eselParts = parseESEL(args, esel)
672 hasEsel=True
673 if("GPU" in addDataPiece[i]):
674 fruCallout = '/xyz/openbmc_project/inventory/system/chassis/motherboard/gpu' + str(addDataPiece[i]).strip()[-1]
675 calloutFound = True
676 if("PROCEDURE" in addDataPiece[i]):
677 fruCallout = str(hex(int(str(addDataPiece[i]).split('=')[1])))[2:]
678 calloutFound = True
Justin Thalere412dc22018-01-12 16:28:24 -0600679 if("RAIL_NAME" in addDataPiece[i]):
680 calloutFound=True
681 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
682 if("INPUT_NAME" in addDataPiece[i]):
683 calloutFound=True
684 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
685 if("SENSOR_TYPE" in addDataPiece[i]):
686 calloutFound=True
687 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600688
689 if(calloutFound):
690 policyKey = messageID +"||" + fruCallout
691 else:
692 policyKey = messageID
693 event = {}
694 eventNum = str(count)
695 if policyKey in policyTable:
696 for pkey in policyTable[policyKey]:
697 if(type(policyTable[policyKey][pkey])== bool):
698 event[pkey] = boolToString(policyTable[policyKey][pkey])
699 else:
700 if (i2creadFail and pkey == 'Message'):
701 event[pkey] = policyTable[policyKey][pkey] + ' ' +i2cdevice
702 else:
703 event[pkey] = policyTable[policyKey][pkey]
704 event['timestamp'] = selEntries[key]['Timestamp']
705 event['resolved'] = bool(selEntries[key]['Resolved'])
706 if(hasEsel):
707 if args.devdebug:
708 event['eselParts'] = eselParts
709 event['raweSEL'] = esel
710 event['logNum'] = key.split('/')[-1]
711 eventDict['event' + eventNum] = event
712
713 else:
714 severity = str(selEntries[key]['Severity']).split('.')[-1]
715 if severity == 'Error':
716 severity = 'Critical'
717 eventDict['event'+eventNum] = {}
718 eventDict['event' + eventNum]['error'] = "error: Not found in policy table: " + policyKey
719 eventDict['event' + eventNum]['timestamp'] = selEntries[key]['Timestamp']
720 eventDict['event' + eventNum]['Severity'] = severity
721 if(hasEsel):
722 if args.devdebug:
723 eventDict['event' +eventNum]['eselParts'] = eselParts
724 eventDict['event' +eventNum]['raweSEL'] = esel
725 eventDict['event' +eventNum]['logNum'] = key.split('/')[-1]
726 eventDict['event' +eventNum]['resolved'] = bool(selEntries[key]['Resolved'])
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600727 count += 1
728 return eventDict
729
730
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600731def selDisplay(events, args):
Justin Thalere412dc22018-01-12 16:28:24 -0600732 """
733 displays alerts in human readable format
734
735 @param events: Dictionary containing events
736 @return:
737 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600738 activeAlerts = []
739 historyAlerts = []
740 sortedEntries = sortSELs(events)
741 logNumList = sortedEntries[0]
742 eventKeyDict = sortedEntries[1]
743 keylist = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message']
744 if(args.devdebug):
745 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message', 'eSEL contents']
746 keylist.append('eSEL')
747 else:
748 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity', 'Message']
749 for log in logNumList:
750 selDict = {}
751 alert = events[eventKeyDict[str(log)]]
752 if('error' in alert):
753 selDict['Entry'] = alert['logNum']
754 selDict['ID'] = 'Unknown'
755 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
756 msg = alert['error']
757 polMsg = msg.split("policy table:")[0]
758 msg = msg.split("policy table:")[1]
759 msgPieces = msg.split("||")
760 err = msgPieces[0]
761 if(err.find("org.open_power.")!=-1):
762 err = err.split("org.open_power.")[1]
763 elif(err.find("xyz.openbmc_project.")!=-1):
764 err = err.split("xyz.openbmc_project.")[1]
765 else:
766 err = msgPieces[0]
767 callout = ""
768 if len(msgPieces) >1:
769 callout = msgPieces[1]
770 if(callout.find("/org/open_power/")!=-1):
771 callout = callout.split("/org/open_power/")[1]
772 elif(callout.find("/xyz/openbmc_project/")!=-1):
773 callout = callout.split("/xyz/openbmc_project/")[1]
774 else:
775 callout = msgPieces[1]
776 selDict['Message'] = polMsg +"policy table: "+ err + "||" + callout
777 selDict['Serviceable'] = 'Unknown'
778 selDict['Severity'] = alert['Severity']
779 else:
780 selDict['Entry'] = alert['logNum']
781 selDict['ID'] = alert['CommonEventID']
782 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
783 selDict['Message'] = alert['Message']
784 selDict['Serviceable'] = alert['Serviceable']
785 selDict['Severity'] = alert['Severity']
786
787
788 eselOrder = ['refCode','signatureDescription', 'eselType', 'devdesc', 'calloutType', 'procedure']
789 if ('eselParts' in alert and args.devdebug):
790 eselOutput = ""
791 for item in eselOrder:
792 if item in alert['eselParts']:
793 eselOutput = eselOutput + item + ": " + alert['eselParts'][item] + " | "
794 selDict['eSEL'] = eselOutput
795 else:
796 if args.devdebug:
797 selDict['eSEL'] = "None"
798
799 if not alert['resolved']:
800 activeAlerts.append(selDict)
801 else:
802 historyAlerts.append(selDict)
803 mergedOutput = activeAlerts + historyAlerts
804 colWidth = setColWidth(keylist, len(colNames), dict(enumerate(mergedOutput)), colNames)
805
806 output = ""
807 if(len(activeAlerts)>0):
808 row = ""
809 output +="----Active Alerts----\n"
810 for i in range(0, len(colNames)):
811 if i!=0: row =row + "| "
812 row = row + colNames[i].ljust(colWidth[i])
813 output += row + "\n"
814
815 for i in range(0,len(activeAlerts)):
816 row = ""
817 for j in range(len(activeAlerts[i])):
818 if (j != 0): row = row + "| "
819 row = row + activeAlerts[i][keylist[j]].ljust(colWidth[j])
820 output += row + "\n"
821
822 if(len(historyAlerts)>0):
823 row = ""
824 output+= "----Historical Alerts----\n"
825 for i in range(len(colNames)):
826 if i!=0: row =row + "| "
827 row = row + colNames[i].ljust(colWidth[i])
828 output += row + "\n"
829
830 for i in range(0, len(historyAlerts)):
831 row = ""
832 for j in range(len(historyAlerts[i])):
833 if (j != 0): row = row + "| "
834 row = row + historyAlerts[i][keylist[j]].ljust(colWidth[j])
835 output += row + "\n"
836# print(events[eventKeyDict[str(log)]])
837 return output
838
Justin Thalere412dc22018-01-12 16:28:24 -0600839
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600840def selPrint(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600841 """
842 prints out all bmc alerts
843
844 @param host: string, the hostname or IP address of the bmc
845 @param args: contains additional arguments used by the fru sub command
846 @param session: the active session to use
847 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
848 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600849 if(args.policyTableLoc is None):
850 if os.path.exists('policyTable.json'):
851 ptableLoc = "policyTable.json"
852 elif os.path.exists('/opt/ibm/ras/lib/policyTable.json'):
853 ptableLoc = '/opt/ibm/ras/lib/policyTable.json'
854 else:
855 ptableLoc = 'lib/policyTable.json'
856 else:
857 ptableLoc = args.policyTableLoc
858 policyTable = loadPolicyTable(ptableLoc)
859 rawselEntries = ""
860 if(hasattr(args, 'fileloc') and args.fileloc is not None):
861 if os.path.exists(args.fileloc):
862 with open(args.fileloc, 'r') as selFile:
863 selLines = selFile.readlines()
864 rawselEntries = ''.join(selLines)
865 else:
866 print("Error: File not found")
867 sys.exit(1)
868 else:
869 rawselEntries = sel(host, args, session)
870 loadFailed = False
871 try:
872 selEntries = json.loads(rawselEntries)
873 except ValueError:
874 loadFailed = True
875 if loadFailed:
876 cleanSels = json.dumps(rawselEntries).replace('\\n', '')
877 #need to load json twice as original content was string escaped a second time
878 selEntries = json.loads(json.loads(cleanSels))
879 selEntries = selEntries['data']
Justin Thalere412dc22018-01-12 16:28:24 -0600880
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600881 if 'description' in selEntries:
882 if(args.json):
883 return("{\n\t\"numAlerts\": 0\n}")
884 else:
885 return("No log entries found")
886
887 else:
888 if(len(policyTable)>0):
889 events = parseAlerts(policyTable, selEntries, args)
890 if(args.json):
891 events["numAlerts"] = len(events)
892 retValue = str(json.dumps(events, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
893 return retValue
894 elif(hasattr(args, 'fullSel')):
895 return events
896 else:
897 #get log numbers to order event entries sequentially
898 return selDisplay(events, args)
899 else:
900 if(args.json):
901 return selEntries
902 else:
903 print("error: Policy Table not found.")
904 return selEntries
Justin Thalere412dc22018-01-12 16:28:24 -0600905
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600906def selList(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600907 """
908 prints out all all bmc alerts, or only prints out the specified alerts
909
910 @param host: string, the hostname or IP address of the bmc
911 @param args: contains additional arguments used by the fru sub command
912 @param session: the active session to use
913 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
914 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600915 return(sel(host, args, session))
916
Justin Thalere412dc22018-01-12 16:28:24 -0600917
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600918def selClear(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600919 """
920 clears all alerts
921
922 @param host: string, the hostname or IP address of the bmc
923 @param args: contains additional arguments used by the fru sub command
924 @param session: the active session to use
925 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
926 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600927 url="https://"+host+"/xyz/openbmc_project/logging/action/deleteAll"
928 httpHeader = {'Content-Type':'application/json'}
929 data = "{\"data\": [] }"
Justin Thalere412dc22018-01-12 16:28:24 -0600930
931 try:
932 res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
933 except(requests.exceptions.Timeout):
934 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600935 if res.status_code == 200:
936 return "The Alert Log has been cleared. Please allow a few minutes for the action to complete."
937 else:
938 print("Unable to clear the logs, trying to clear 1 at a time")
939 sels = json.loads(sel(host, args, session))['data']
940 for key in sels:
941 if 'callout' not in key:
942 logNum = key.split('/')[-1]
943 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete"
944 try:
945 session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
946 except(requests.exceptions.Timeout):
947 return connectionErrHandler(args.json, "Timeout", None)
948 sys.exit(1)
949 except(requests.exceptions.ConnectionError) as err:
950 return connectionErrHandler(args.json, "ConnectionError", err)
951 sys.exit(1)
952 return ('Sel clearing complete')
953
954def selSetResolved(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600955 """
956 sets a sel entry to resolved
957
958 @param host: string, the hostname or IP address of the bmc
959 @param args: contains additional arguments used by the fru sub command
960 @param session: the active session to use
961 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
962 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600963 url="https://"+host+"/xyz/openbmc_project/logging/entry/" + str(args.selNum) + "/attr/Resolved"
964 httpHeader = {'Content-Type':'application/json'}
965 data = "{\"data\": 1 }"
Justin Thalere412dc22018-01-12 16:28:24 -0600966 try:
967 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
968 except(requests.exceptions.Timeout):
969 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600970 if res.status_code == 200:
971 return "Sel entry "+ str(args.selNum) +" is now set to resolved"
972 else:
973 return "Unable to set the alert to resolved"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600974
Justin Thalere412dc22018-01-12 16:28:24 -0600975def selResolveAll(host, args, session):
976 """
977 sets a sel entry to resolved
978
979 @param host: string, the hostname or IP address of the bmc
980 @param args: contains additional arguments used by the fru sub command
981 @param session: the active session to use
982 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
983 """
984 rawselEntries = sel(host, args, session)
985 loadFailed = False
986 try:
987 selEntries = json.loads(rawselEntries)
988 except ValueError:
989 loadFailed = True
990 if loadFailed:
991 cleanSels = json.dumps(rawselEntries).replace('\\n', '')
992 #need to load json twice as original content was string escaped a second time
993 selEntries = json.loads(json.loads(cleanSels))
994 selEntries = selEntries['data']
995
996 if 'description' in selEntries:
997 if(args.json):
998 return("{\n\t\"selsResolved\": 0\n}")
999 else:
1000 return("No log entries found")
1001 else:
1002 d = vars(args)
1003 successlist = []
1004 failedlist = []
1005 for key in selEntries:
1006 if 'callout' not in key:
1007 d['selNum'] = key.split('/')[-1]
1008 resolved = selSetResolved(host,args,session)
1009 if 'Sel entry' in resolved:
1010 successlist.append(d['selNum'])
1011 else:
1012 failedlist.append(d['selNum'])
1013 output = ""
1014 successlist.sort()
1015 failedlist.sort()
1016 if len(successlist)>0:
1017 output = "Successfully resolved: " +', '.join(successlist) +"\n"
1018 if len(failedlist)>0:
1019 output += "Failed to resolve: " + ', '.join(failedlist) + "\n"
1020 return output
1021
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001022def chassisPower(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001023 """
1024 called by the chassis function. Controls the power state of the chassis, or gets the status
1025
1026 @param host: string, the hostname or IP address of the bmc
1027 @param args: contains additional arguments used by the fru sub command
1028 @param session: the active session to use
1029 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1030 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001031 if(args.powcmd == 'on'):
1032 print("Attempting to Power on...:")
1033 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
1034 httpHeader = {'Content-Type':'application/json',}
1035 data = '{"data":"xyz.openbmc_project.State.Host.Transition.On"}'
Justin Thalere412dc22018-01-12 16:28:24 -06001036 try:
1037 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1038 except(requests.exceptions.Timeout):
1039 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001040 return res.text
Justin Thalere412dc22018-01-12 16:28:24 -06001041 elif(args.powcmd == 'softoff'):
1042 print("Attempting to Power off gracefully...:")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001043 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
1044 httpHeader = {'Content-Type':'application/json'}
1045 data = '{"data":"xyz.openbmc_project.State.Host.Transition.Off"}'
Justin Thalere412dc22018-01-12 16:28:24 -06001046 try:
1047 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1048 except(requests.exceptions.Timeout):
1049 return(connectionErrHandler(args.json, "Timeout", None))
1050 return res.text
1051 elif(args.powcmd == 'hardoff'):
1052 print("Attempting to Power off immediately...:")
1053 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/RequestedPowerTransition"
1054 httpHeader = {'Content-Type':'application/json'}
1055 data = '{"data":"xyz.openbmc_project.State.Chassis.Transition.Off"}'
1056 try:
1057 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1058 except(requests.exceptions.Timeout):
1059 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001060 return res.text
1061 elif(args.powcmd == 'status'):
1062 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState"
1063 httpHeader = {'Content-Type':'application/json'}
1064# print(url)
Justin Thalere412dc22018-01-12 16:28:24 -06001065 try:
1066 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1067 except(requests.exceptions.Timeout):
1068 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001069 chassisState = json.loads(res.text)['data'].split('.')[-1]
1070 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/CurrentHostState"
Justin Thalere412dc22018-01-12 16:28:24 -06001071 try:
1072 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1073 except(requests.exceptions.Timeout):
1074 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001075 hostState = json.loads(res.text)['data'].split('.')[-1]
1076 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/CurrentBMCState"
Justin Thalere412dc22018-01-12 16:28:24 -06001077 try:
1078 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1079 except(requests.exceptions.Timeout):
1080 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001081 bmcState = json.loads(res.text)['data'].split('.')[-1]
1082 if(args.json):
1083 outDict = {"Chassis Power State" : chassisState, "Host Power State" : hostState, "BMC Power State":bmcState}
1084 return json.dumps(outDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
1085 else:
1086 return "Chassis Power State: " +chassisState + "\nHost Power State: " + hostState + "\nBMC Power State: " + bmcState
1087 else:
1088 return "Invalid chassis power command"
1089
Justin Thalere412dc22018-01-12 16:28:24 -06001090
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001091def chassisIdent(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001092 """
1093 called by the chassis function. Controls the identify led of the chassis. Sets or gets the state
1094
1095 @param host: string, the hostname or IP address of the bmc
1096 @param args: contains additional arguments used by the fru sub command
1097 @param session: the active session to use
1098 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1099 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001100 if(args.identcmd == 'on'):
1101 print("Attempting to turn identify light on...:")
1102 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
1103 httpHeader = {'Content-Type':'application/json',}
1104 data = '{"data":true}'
Justin Thalere412dc22018-01-12 16:28:24 -06001105 try:
1106 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1107 except(requests.exceptions.Timeout):
1108 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001109 return res.text
1110 elif(args.identcmd == 'off'):
1111 print("Attempting to turn identify light off...:")
1112 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
1113 httpHeader = {'Content-Type':'application/json'}
1114 data = '{"data":false}'
Justin Thalere412dc22018-01-12 16:28:24 -06001115 try:
1116 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1117 except(requests.exceptions.Timeout):
1118 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001119 return res.text
1120 elif(args.identcmd == 'status'):
1121 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify"
1122 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -06001123 try:
1124 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1125 except(requests.exceptions.Timeout):
1126 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001127 status = json.loads(res.text)['data']
1128 if(args.json):
1129 return status
1130 else:
1131 if status['Asserted'] == 0:
1132 return "Identify light is off"
1133 else:
1134 return "Identify light is blinking"
1135 else:
1136 return "Invalid chassis identify command"
1137
Justin Thalere412dc22018-01-12 16:28:24 -06001138
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001139def chassis(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001140 """
1141 controls the different chassis commands
1142
1143 @param host: string, the hostname or IP address of the bmc
1144 @param args: contains additional arguments used by the fru sub command
1145 @param session: the active session to use
1146 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1147 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001148 if(hasattr(args, 'powcmd')):
1149 result = chassisPower(host,args,session)
1150 elif(hasattr(args, 'identcmd')):
1151 result = chassisIdent(host, args, session)
1152 else:
1153 return "to be completed"
1154 return result
Justin Thalere412dc22018-01-12 16:28:24 -06001155
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001156def bmcDumpRetrieve(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001157 """
1158 Downloads a dump file from the bmc
1159
1160 @param host: string, the hostname or IP address of the bmc
1161 @param args: contains additional arguments used by the collectServiceData sub command
1162 @param session: the active session to use
1163 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1164 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001165 httpHeader = {'Content-Type':'application/json'}
1166 dumpNum = args.dumpNum
1167 if (args.dumpSaveLoc is not None):
1168 saveLoc = args.dumpSaveLoc
1169 else:
1170 saveLoc = '/tmp'
1171 url ='https://'+host+'/download/dump/' + str(dumpNum)
1172 try:
Justin Thalere412dc22018-01-12 16:28:24 -06001173 r = session.get(url, headers=httpHeader, stream=True, verify=False, timeout=30)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001174 if (args.dumpSaveLoc is not None):
1175 if os.path.exists(saveLoc):
1176 if saveLoc[-1] != os.path.sep:
1177 saveLoc = saveLoc + os.path.sep
1178 filename = saveLoc + host+'-dump' + str(dumpNum) + '.tar.xz'
1179
1180 else:
1181 return 'Invalid save location specified'
1182 else:
1183 filename = '/tmp/' + host+'-dump' + str(dumpNum) + '.tar.xz'
1184
1185 with open(filename, 'wb') as f:
1186 for chunk in r.iter_content(chunk_size =1024):
1187 if chunk:
1188 f.write(chunk)
1189 return 'Saved as ' + filename
1190
1191 except(requests.exceptions.Timeout):
1192 return connectionErrHandler(args.json, "Timeout", None)
Justin Thalere412dc22018-01-12 16:28:24 -06001193
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001194 except(requests.exceptions.ConnectionError) as err:
1195 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001196
Justin Thalere412dc22018-01-12 16:28:24 -06001197def bmcDumpList(host, args, session):
1198 """
1199 Lists the number of dump files on the bmc
1200
1201 @param host: string, the hostname or IP address of the bmc
1202 @param args: contains additional arguments used by the collectServiceData sub command
1203 @param session: the active session to use
1204 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1205 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001206 httpHeader = {'Content-Type':'application/json'}
1207 url ='https://'+host+'/xyz/openbmc_project/dump/list'
1208 try:
1209 r = session.get(url, headers=httpHeader, verify=False, timeout=20)
1210 dumpList = json.loads(r.text)
1211 return r.text
1212 except(requests.exceptions.Timeout):
1213 return connectionErrHandler(args.json, "Timeout", None)
Justin Thalere412dc22018-01-12 16:28:24 -06001214
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001215 except(requests.exceptions.ConnectionError) as err:
Justin Thalere412dc22018-01-12 16:28:24 -06001216 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001217
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001218def bmcDumpDelete(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001219 """
1220 Deletes BMC dump files from the bmc
1221
1222 @param host: string, the hostname or IP address of the bmc
1223 @param args: contains additional arguments used by the collectServiceData sub command
1224 @param session: the active session to use
1225 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1226 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001227 httpHeader = {'Content-Type':'application/json'}
1228 dumpList = []
1229 successList = []
1230 failedList = []
1231 if args.dumpNum is not None:
1232 if isinstance(args.dumpNum, list):
1233 dumpList = args.dumpNum
1234 else:
1235 dumpList.append(args.dumpNum)
1236 for dumpNum in dumpList:
1237 url ='https://'+host+'/xyz/openbmc_project/dump/entry/'+str(dumpNum)+'/action/Delete'
1238 try:
1239 r = session.post(url, headers=httpHeader, json = {"data": []}, verify=False, timeout=30)
1240 if r.status_code == 200:
1241 successList.append(str(dumpNum))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001242 else:
1243 failedList.append(str(dumpNum))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001244 except(requests.exceptions.Timeout):
1245 return connectionErrHandler(args.json, "Timeout", None)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001246 except(requests.exceptions.ConnectionError) as err:
1247 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001248 output = "Successfully deleted dumps: " + ', '.join(successList)
1249 if(len(failedList)>0):
1250 output+= '\nFailed to delete dumps: ' + ', '.join(failedList)
1251 return output
1252 else:
1253 return 'You must specify an entry number to delete'
1254
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001255def bmcDumpDeleteAll(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001256 """
1257 Deletes All BMC dump files from the bmc
1258
1259 @param host: string, the hostname or IP address of the bmc
1260 @param args: contains additional arguments used by the collectServiceData sub command
1261 @param session: the active session to use
1262 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1263 """
1264 dumpResp = bmcDumpList(host, args, session)
1265 if 'FQPSPIN0000M' in dumpResp or 'FQPSPIN0001M'in dumpResp:
1266 return dumpResp
1267 dumpList = json.loads(dumpResp)['data']
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001268 d = vars(args)
1269 dumpNums = []
1270 for dump in dumpList:
1271 if '/xyz/openbmc_project/dump/internal/manager' not in dump:
1272 dumpNums.append(int(dump.strip().split('/')[-1]))
1273 d['dumpNum'] = dumpNums
1274
1275 return bmcDumpDelete(host, args, session)
1276
Justin Thalere412dc22018-01-12 16:28:24 -06001277
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001278def bmcDumpCreate(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001279 """
1280 Creates a bmc dump file
1281
1282 @param host: string, the hostname or IP address of the bmc
1283 @param args: contains additional arguments used by the collectServiceData sub command
1284 @param session: the active session to use
1285 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1286 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001287 httpHeader = {'Content-Type':'application/json'}
1288 url = 'https://'+host+'/xyz/openbmc_project/dump/action/CreateDump'
1289 try:
1290 r = session.post(url, headers=httpHeader, json = {"data": []}, verify=False, timeout=30)
1291 if('"message": "200 OK"' in r.text and not args.json):
1292 return ('Dump successfully created')
1293 else:
1294 return ('Failed to create dump')
1295 except(requests.exceptions.Timeout):
1296 return connectionErrHandler(args.json, "Timeout", None)
1297 except(requests.exceptions.ConnectionError) as err:
1298 return connectionErrHandler(args.json, "ConnectionError", err)
1299
1300
1301
Justin Thalere412dc22018-01-12 16:28:24 -06001302
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001303def collectServiceData(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001304 """
1305 Collects all data needed for service from the BMC
1306
1307 @param host: string, the hostname or IP address of the bmc
1308 @param args: contains additional arguments used by the collectServiceData sub command
1309 @param session: the active session to use
1310 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1311 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001312 #create a bmc dump
1313 dumpcount = len(json.loads(bmcDumpList(host, args, session))['data'])
1314 try:
1315 dumpcreated = bmcDumpCreate(host, args, session)
1316 except Exception as e:
1317 print('failed to create a bmc dump')
1318
1319
1320 #Collect Inventory
1321 try:
1322 args.silent = True
1323 myDir = '/tmp/' + host + "--" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S")
1324 os.makedirs(myDir)
1325 filelist = []
1326 frulist = fruPrint(host, args, session)
1327 with open(myDir +'/inventory.txt', 'w') as f:
1328 f.write(frulist)
1329 print("Inventory collected and stored in " + myDir + "/inventory.txt")
1330 filelist.append(myDir+'/inventory.txt')
1331 except Exception as e:
1332 print("Failed to collect inventory")
1333
1334 #Read all the sensor and OCC status
1335 try:
1336 sensorReadings = sensor(host, args, session)
1337 with open(myDir +'/sensorReadings.txt', 'w') as f:
1338 f.write(sensorReadings)
1339 print("Sensor readings collected and stored in " +myDir + "/sensorReadings.txt")
1340 filelist.append(myDir+'/sensorReadings.txt')
1341 except Exception as e:
1342 print("Failed to collect sensor readings")
1343
1344 #Collect all of the LEDs status
1345 try:
1346 url="https://"+host+"/xyz/openbmc_project/led/enumerate"
1347 httpHeader = {'Content-Type':'application/json'}
1348 leds = session.get(url, headers=httpHeader, verify=False, timeout=20)
1349 with open(myDir +'/ledStatus.txt', 'w') as f:
1350 f.write(leds.text)
1351 print("System LED status collected and stored in "+myDir +"/ledStatus.txt")
1352 filelist.append(myDir+'/ledStatus.txt')
1353 except Exception as e:
1354 print("Failed to collect LED status")
1355
1356 #Collect the bmc logs
1357 try:
1358 sels = selPrint(host,args,session)
1359 with open(myDir +'/SELshortlist.txt', 'w') as f:
1360 f.write(str(sels))
1361 print("sel short list collected and stored in "+myDir +"/SELshortlist.txt")
1362 filelist.append(myDir+'/SELshortlist.txt')
1363 time.sleep(2)
1364
1365 d = vars(args)
1366 d['json'] = True
1367 d['fullSel'] = True
1368 parsedfullsels = json.loads(selPrint(host, args, session))
1369 d['fullEsel'] = True
1370 sortedSELs = sortSELs(parsedfullsels)
1371 with open(myDir +'/parsedSELs.txt', 'w') as f:
1372 for log in sortedSELs[0]:
1373 esel = ""
1374 parsedfullsels[sortedSELs[1][str(log)]]['timestamp'] = datetime.datetime.fromtimestamp(int(parsedfullsels[sortedSELs[1][str(log)]]['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
1375 if ('raweSEL' in parsedfullsels[sortedSELs[1][str(log)]] and args.devdebug):
1376 esel = parsedfullsels[sortedSELs[1][str(log)]]['raweSEL']
1377 del parsedfullsels[sortedSELs[1][str(log)]]['raweSEL']
1378 f.write(json.dumps(parsedfullsels[sortedSELs[1][str(log)]],sort_keys=True, indent=4, separators=(',', ': ')))
1379 if(args.devdebug and esel != ""):
1380 f.write(parseESEL(args, esel))
1381 print("fully parsed sels collected and stored in "+myDir +"/parsedSELs.txt")
1382 filelist.append(myDir+'/parsedSELs.txt')
1383 except Exception as e:
1384 print("Failed to collect system event logs")
1385 print(e)
1386
1387 #collect RAW bmc enumeration
1388 try:
1389 url="https://"+host+"/xyz/openbmc_project/enumerate"
1390 print("Attempting to get a full BMC enumeration")
1391 fullDump = session.get(url, headers=httpHeader, verify=False, timeout=120)
1392 with open(myDir +'/bmcFullRaw.txt', 'w') as f:
1393 f.write(fullDump.text)
1394 print("RAW BMC data collected and saved into "+myDir +"/bmcFullRaw.txt")
1395 filelist.append(myDir+'/bmcFullRaw.txt')
1396 except Exception as e:
1397 print("Failed to collect bmc full enumeration")
1398
1399 #collect the dump files
1400 waitingForNewDump = True
1401 count = 0;
1402 while(waitingForNewDump):
1403 dumpList = json.loads(bmcDumpList(host, args, session))['data']
1404 if len(dumpList) > dumpcount:
1405 waitingForNewDump = False
1406 break;
1407 elif(count>30):
1408 print("Timed out waiting for bmc to make a new dump file. Dump space may be full.")
1409 break;
1410 else:
1411 time.sleep(2)
1412 count += 1
1413 try:
1414 print('Collecting bmc dump files')
1415 d['dumpSaveLoc'] = myDir
1416 dumpList = json.loads(bmcDumpList(host, args, session))['data']
1417 for dump in dumpList:
1418 if '/xyz/openbmc_project/dump/internal/manager' not in dump:
1419 d['dumpNum'] = int(dump.strip().split('/')[-1])
1420 print('retrieving dump file ' + str(d['dumpNum']))
1421 filename = bmcDumpRetrieve(host, args, session).split('Saved as ')[-1]
1422 filelist.append(filename)
1423 time.sleep(2)
1424 except Exception as e:
1425 print("Failed to collect bmc dump files")
1426 print(e)
1427
1428 #create the zip file
1429 try:
1430 filename = myDir.split('/tmp/')[-1] + '.zip'
1431 zf = zipfile.ZipFile(myDir+'/' + filename, 'w')
1432 for myfile in filelist:
1433 zf.write(myfile, os.path.basename(myfile))
1434 zf.close()
1435 except Exception as e:
1436 print("Failed to create zip file with collected information")
1437 return "data collection complete"
1438
Justin Thalere412dc22018-01-12 16:28:24 -06001439
1440def healthCheck(host, args, session):
1441 """
1442 runs a health check on the platform
1443
1444 @param host: string, the hostname or IP address of the bmc
1445 @param args: contains additional arguments used by the bmc sub command
1446 @param session: the active session to use
1447 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1448 """
1449 #check fru status and get as json to easily work through
1450 d = vars(args)
1451 useJson = d['json']
1452 d['json'] = True
1453 d['verbose']= False
1454
1455 frus = json.loads(fruStatus(host, args, session))
1456
1457 hwStatus= "OK"
1458 performanceStatus = "OK"
1459 for key in frus:
1460 if frus[key]["Functional"] == "No" and frus[key]["Present"] == "Yes":
1461 hwStatus= "Degraded"
1462 if("power_supply" in key):
1463 gpuCount =0;
1464 frulist = json.loads(fruList(host, args, session))
1465 for comp in frulist:
1466 if "gv100card" in comp:
1467 gpuCount +=1
1468 if gpuCount > 4:
1469 hwStatus = "Critical"
1470 performanceStatus="Degraded"
1471 break;
1472 elif("fan" in key):
1473 hwStatus = "Degraded"
1474 else:
1475 performanceStatus = "Degraded"
1476 if useJson:
1477 output = {"Hardware Status": hwStatus, "Performance": performanceStatus}
1478 output = json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
1479 else:
1480 output = ("Hardware Status: " + hwStatus +
1481 "\nPerformance: " +performanceStatus )
1482
1483
1484 #SW407886: Clear the duplicate entries
1485 #collect the dups
1486 d['devdebug'] = False
1487 sels = json.loads(selPrint(host, args, session))
1488 logNums2Clr = []
1489 oldestLogNum={"logNum": "bogus" ,"key" : ""}
1490 count = 0
1491 if sels['numAlerts'] > 0:
1492 for key in sels:
1493 if "numAlerts" in key:
1494 continue
1495 try:
1496 if "slave@00:00/00:00:00:06/sbefifo1-dev0/occ1-dev0" in sels[key]['Message']:
1497 count += 1
1498 if count > 1:
1499 #preserve first occurrence
1500 if sels[key]['timestamp'] < sels[oldestLogNum['key']]['timestamp']:
1501 oldestLogNum['key']=key
1502 oldestLogNum['logNum'] = sels[key]['logNum']
1503 else:
1504 oldestLogNum['key']=key
1505 oldestLogNum['logNum'] = sels[key]['logNum']
1506 logNums2Clr.append(sels[key]['logNum'])
1507 except KeyError:
1508 continue
1509 if(count >0):
1510 logNums2Clr.remove(oldestLogNum['logNum'])
1511 #delete the dups
1512 if count >1:
1513 httpHeader = {'Content-Type':'application/json'}
1514 data = "{\"data\": [] }"
1515 for logNum in logNums2Clr:
1516 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete"
1517 try:
1518 session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1519 except(requests.exceptions.Timeout):
1520 deleteFailed = True
1521 except(requests.exceptions.ConnectionError) as err:
1522 deleteFailed = True
1523 #End of defect resolve code
1524 d['json'] = useJson
1525 return output
1526
1527
1528
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001529def bmc(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001530 """
1531 handles various bmc level commands, currently bmc rebooting
1532
1533 @param host: string, the hostname or IP address of the bmc
1534 @param args: contains additional arguments used by the bmc sub command
1535 @param session: the active session to use
1536 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1537 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001538 if(args.type is not None):
1539 return bmcReset(host, args, session)
Justin Thalere412dc22018-01-12 16:28:24 -06001540 if(args.info):
1541 return "Not implemented at this time"
1542
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001543
Justin Thalere412dc22018-01-12 16:28:24 -06001544
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001545def bmcReset(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001546 """
1547 controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots.
1548
1549 @param host: string, the hostname or IP address of the bmc
1550 @param args: contains additional arguments used by the bmcReset sub command
1551 @param session: the active session to use
1552 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1553 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001554 if(args.type == "warm"):
1555 print("\nAttempting to reboot the BMC...:")
1556 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
1557 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -06001558 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
1559 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001560 return res.text
1561 elif(args.type =="cold"):
Justin Thalere412dc22018-01-12 16:28:24 -06001562 print("\nAttempting to reboot the BMC...:")
1563 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
1564 httpHeader = {'Content-Type':'application/json'}
1565 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
1566 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
1567 return res.text
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001568 else:
1569 return "invalid command"
Justin Thalere412dc22018-01-12 16:28:24 -06001570
1571def gardClear(host, args, session):
1572 """
1573 clears the gard records from the bmc
1574
1575 @param host: string, the hostname or IP address of the bmc
1576 @param args: contains additional arguments used by the gardClear sub command
1577 @param session: the active session to use
1578 """
1579 url="https://"+host+"/org/open_power/control/gard/action/Reset"
1580 httpHeader = {'Content-Type':'application/json'}
1581 data = '{"data":[]}'
1582 try:
1583
1584 res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1585 if res.status_code == 404:
1586 return "Command not supported by this firmware version"
1587 else:
1588 return res.text
1589 except(requests.exceptions.Timeout):
1590 return connectionErrHandler(args.json, "Timeout", None)
1591 except(requests.exceptions.ConnectionError) as err:
1592 return connectionErrHandler(args.json, "ConnectionError", err)
1593
1594def activateFWImage(host, args, session):
1595 """
1596 activates a firmware image on the bmc
1597
1598 @param host: string, the hostname or IP address of the bmc
1599 @param args: contains additional arguments used by the fwflash sub command
1600 @param session: the active session to use
1601 @param fwID: the unique ID of the fw image to activate
1602 """
1603 fwID = args.imageID
1604
1605 #determine the existing versions
1606 httpHeader = {'Content-Type':'application/json'}
1607 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1608 try:
1609 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1610 except(requests.exceptions.Timeout):
1611 return connectionErrHandler(args.json, "Timeout", None)
1612 except(requests.exceptions.ConnectionError) as err:
1613 return connectionErrHandler(args.json, "ConnectionError", err)
1614 existingSoftware = json.loads(resp.text)['data']
1615 altVersionID = ''
1616 versionType = ''
1617 imageKey = '/xyz/openbmc_project/software/'+fwID
1618 if imageKey in existingSoftware:
1619 versionType = existingSoftware[imageKey]['Purpose']
1620 for key in existingSoftware:
1621 if imageKey == key:
1622 continue
1623 if 'Purpose' in existingSoftware[key]:
1624 if versionType == existingSoftware[key]['Purpose']:
1625 altVersionID = key.split('/')[-1]
1626
1627
1628
1629
1630 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/attr/Priority"
1631 url1="https://"+host+"/xyz/openbmc_project/software/"+ altVersionID + "/attr/Priority"
1632 data = "{\"data\": 0}"
1633 data1 = "{\"data\": 1 }"
1634 try:
1635 resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1636 resp1 = session.put(url1, headers=httpHeader, data=data1, verify=False, timeout=30)
1637 except(requests.exceptions.Timeout):
1638 return connectionErrHandler(args.json, "Timeout", None)
1639 except(requests.exceptions.ConnectionError) as err:
1640 return connectionErrHandler(args.json, "ConnectionError", err)
1641 if(not args.json):
1642 if resp.status_code == 200 and resp1.status_code == 200:
1643 return 'Firmware activation completed. Please reboot the BMC for the changes to take effect.'
1644 else:
1645 return "Firmware activation failed."
1646 else:
1647 return resp.text + resp1.text
1648
1649def fwFlash(host, args, session):
1650 """
1651 updates the bmc firmware and pnor firmware
1652
1653 @param host: string, the hostname or IP address of the bmc
1654 @param args: contains additional arguments used by the fwflash sub command
1655 @param session: the active session to use
1656 """
1657
1658 if(args.type == 'bmc'):
1659 purp = 'BMC'
1660 else:
1661 purp = 'Host'
1662 #determine the existing versions
1663 httpHeader = {'Content-Type':'application/json'}
1664 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1665 try:
1666 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1667 except(requests.exceptions.Timeout):
1668 return connectionErrHandler(args.json, "Timeout", None)
1669 except(requests.exceptions.ConnectionError) as err:
1670 return connectionErrHandler(args.json, "ConnectionError", err)
1671 oldsoftware = json.loads(resp.text)['data']
1672
1673 #upload the file
1674 httpHeader = {'Content-Type':'application/octet-stream'}
1675 url="https://"+host+"/upload/image"
1676 data=open(args.fileloc,'rb').read()
1677 print("Uploading file to BMC")
1678 try:
1679 resp = session.post(url, headers=httpHeader, data=data, verify=False)
1680 except(requests.exceptions.Timeout):
1681 return connectionErrHandler(args.json, "Timeout", None)
1682 except(requests.exceptions.ConnectionError) as err:
1683 return connectionErrHandler(args.json, "ConnectionError", err)
1684 if resp.status_code != 200:
1685 return "Failed to upload the file to the bmc"
1686 else:
1687 print("Upload complete.")
1688
1689 #determine the version number
1690 software ={}
1691 for i in range(0, 5):
1692 httpHeader = {'Content-Type':'application/json'}
1693 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1694 try:
1695 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1696 except(requests.exceptions.Timeout):
1697 return connectionErrHandler(args.json, "Timeout", None)
1698 except(requests.exceptions.ConnectionError) as err:
1699 return connectionErrHandler(args.json, "ConnectionError", err)
1700 software = json.loads(resp.text)['data']
1701 #check if bmc is done processing the new image
1702 if (len(software.keys()) > len(oldsoftware.keys())):
1703 break
1704 else:
1705 time.sleep(15)
1706 newversionID = ''
1707 for key in software:
1708 if key not in oldsoftware:
1709 idPart = key.split('/')[-1]
1710 if idPart == 'inventory':
1711 continue
1712 softPurpose = software['/xyz/openbmc_project/software/' +idPart]['Purpose'].split('.')[-1]
1713 if(purp in softPurpose):
1714 newversionID = idPart
1715 break
1716 if newversionID == '':
1717 return('Could not find the new version of the firmware on the bmc, it may already exist.' +
1718 "\nRun fru print command and check for the version number. If found, use the firmware activate command to change to using that image.\n"
1719 "If you are reapplying the same image, reboot the bmc to complete the update. ")
1720
1721 #activate the new image
1722 print("Activating new image")
1723 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID + "/attr/RequestedActivation"
1724 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}'
1725 try:
1726 resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1727 except(requests.exceptions.Timeout):
1728 return connectionErrHandler(args.json, "Timeout", None)
1729 except(requests.exceptions.ConnectionError) as err:
1730 return connectionErrHandler(args.json, "ConnectionError", err)
1731
1732 return "Firmware flash completed. Please allow a few minutes for the activation to complete. After the activation is complete you will need to reboot the bmc and the host OS for the changes to take effect. "
1733
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001734def createCommandParser():
Justin Thalere412dc22018-01-12 16:28:24 -06001735 """
1736 creates the parser for the command line along with help for each command and subcommand
1737
1738 @return: returns the parser for the command line
1739 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001740 parser = argparse.ArgumentParser(description='Process arguments')
Justin Thalere412dc22018-01-12 16:28:24 -06001741 parser.add_argument("-H", "--host", help='A hostname or IP for the BMC')
1742 parser.add_argument("-U", "--user", help='The username to login with')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001743 group = parser.add_mutually_exclusive_group()
1744 group.add_argument("-A", "--askpw", action='store_true', help='prompt for password')
1745 group.add_argument("-P", "--PW", help='Provide the password in-line')
1746 parser.add_argument('-j', '--json', action='store_true', help='output json data only')
1747 parser.add_argument('-t', '--policyTableLoc', help='The location of the policy table to parse alerts')
1748 parser.add_argument('-c', '--CerFormat', action='store_true', help=argparse.SUPPRESS)
1749 parser.add_argument('-T', '--procTime', action='store_true', help= argparse.SUPPRESS)
Justin Thalere412dc22018-01-12 16:28:24 -06001750 parser.add_argument('-V', '--version', action='store_true', help='Display the version number of the openbmctool')
1751 subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001752
1753 #fru command
1754 parser_inv = subparsers.add_parser("fru", help='Work with platform inventory')
1755 #fru print
Justin Thalere412dc22018-01-12 16:28:24 -06001756 inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001757 inv_print = inv_subparser.add_parser("print", help="prints out a list of all FRUs")
1758 inv_print.set_defaults(func=fruPrint)
1759 #fru list [0....n]
1760 inv_list = inv_subparser.add_parser("list", help="print out details on selected FRUs. Specifying no items will list the entire inventory")
1761 inv_list.add_argument('items', nargs='?', help="print out details on selected FRUs. Specifying no items will list the entire inventory")
1762 inv_list.set_defaults(func=fruList)
1763 #fru status
1764 inv_status = inv_subparser.add_parser("status", help="prints out the status of all FRUs")
Justin Thalere412dc22018-01-12 16:28:24 -06001765 inv_status.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001766 inv_status.set_defaults(func=fruStatus)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001767
1768 #sensors command
1769 parser_sens = subparsers.add_parser("sensors", help="Work with platform sensors")
Justin Thalere412dc22018-01-12 16:28:24 -06001770 sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions', dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001771 #sensor print
1772 sens_print= sens_subparser.add_parser('print', help="prints out a list of all Sensors.")
1773 sens_print.set_defaults(func=sensor)
1774 #sensor list[0...n]
1775 sens_list=sens_subparser.add_parser("list", help="Lists all Sensors in the platform. Specify a sensor for full details. ")
1776 sens_list.add_argument("sensNum", nargs='?', help="The Sensor number to get full details on" )
1777 sens_list.set_defaults(func=sensor)
1778
1779
1780 #sel command
1781 parser_sel = subparsers.add_parser("sel", help="Work with platform alerts")
Justin Thalere412dc22018-01-12 16:28:24 -06001782 sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions', dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001783
1784 #sel print
1785 sel_print = sel_subparser.add_parser("print", help="prints out a list of all sels in a condensed list")
1786 sel_print.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS)
1787 sel_print.add_argument('-v', '--verbose', action='store_true', help="Changes the output to being very verbose")
1788 sel_print.add_argument('-f', '--fileloc', help='Parse a file instead of the BMC output')
1789 sel_print.set_defaults(func=selPrint)
1790 #sel list
1791 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")
1792 sel_list.add_argument("selNum", nargs='?', type=int, help="The SEL entry to get details on")
1793 sel_list.set_defaults(func=selList)
1794
1795 sel_get = sel_subparser.add_parser("get", help="Gets the verbose details of a specified SEL entry")
1796 sel_get.add_argument('selNum', type=int, help="the number of the SEL entry to get")
1797 sel_get.set_defaults(func=selList)
1798
1799 sel_clear = sel_subparser.add_parser("clear", help="Clears all entries from the SEL")
1800 sel_clear.set_defaults(func=selClear)
1801
1802 sel_setResolved = sel_subparser.add_parser("resolve", help="Sets the sel entry to resolved")
Justin Thalere412dc22018-01-12 16:28:24 -06001803 sel_setResolved.add_argument('-n', '--selNum', type=int, help="the number of the SEL entry to resolve")
1804 sel_ResolveAll_sub = sel_setResolved.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
1805 sel_ResolveAll = sel_ResolveAll_sub.add_parser('all', help='Resolve all SEL entries')
1806 sel_ResolveAll.set_defaults(func=selResolveAll)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001807 sel_setResolved.set_defaults(func=selSetResolved)
1808
1809 parser_chassis = subparsers.add_parser("chassis", help="Work with chassis power and status")
Justin Thalere412dc22018-01-12 16:28:24 -06001810 chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001811
1812 parser_chassis.add_argument('status', action='store_true', help='Returns the current status of the platform')
1813 parser_chassis.set_defaults(func=chassis)
1814
1815 parser_chasPower = chas_sub.add_parser("power", help="Turn the chassis on or off, check the power state")
Justin Thalere412dc22018-01-12 16:28:24 -06001816 parser_chasPower.add_argument('powcmd', choices=['on','softoff', 'hardoff', 'status'], help='The value for the power command. on, off, or status')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001817 parser_chasPower.set_defaults(func=chassisPower)
1818
1819 #control the chassis identify led
1820 parser_chasIdent = chas_sub.add_parser("identify", help="Control the chassis identify led")
1821 parser_chasIdent.add_argument('identcmd', choices=['on', 'off', 'status'], help='The control option for the led: on, off, blink, status')
1822 parser_chasIdent.set_defaults(func=chassisIdent)
1823
1824 #collect service data
1825 parser_servData = subparsers.add_parser("collect_service_data", help="Collect all bmc data needed for service")
1826 parser_servData.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS)
1827 parser_servData.set_defaults(func=collectServiceData)
1828
Justin Thalere412dc22018-01-12 16:28:24 -06001829 #system quick health check
1830 parser_healthChk = subparsers.add_parser("health_check", help="Work with platform sensors")
1831 parser_healthChk.set_defaults(func=healthCheck)
1832
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001833 #work with bmc dumps
1834 parser_bmcdump = subparsers.add_parser("dump", help="Work with bmc dump files")
Justin Thalere412dc22018-01-12 16:28:24 -06001835 bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001836 dump_Create = bmcDump_sub.add_parser('create', help="Create a bmc dump")
1837 dump_Create.set_defaults(func=bmcDumpCreate)
1838
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001839 dump_list = bmcDump_sub.add_parser('list', help="list all bmc dump files")
1840 dump_list.set_defaults(func=bmcDumpList)
1841
1842 parserdumpdelete = bmcDump_sub.add_parser('delete', help="Delete bmc dump files")
1843 parserdumpdelete.add_argument("-n", "--dumpNum", nargs='*', type=int, help="The Dump entry to delete")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001844 parserdumpdelete.set_defaults(func=bmcDumpDelete)
1845
Justin Thalere412dc22018-01-12 16:28:24 -06001846 bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001847 deleteAllDumps = bmcDumpDelsub.add_parser('all', help='Delete all bmc dump files')
1848 deleteAllDumps.set_defaults(func=bmcDumpDeleteAll)
1849
1850 parser_dumpretrieve = bmcDump_sub.add_parser('retrieve', help='Retrieve a dump file')
1851 parser_dumpretrieve.add_argument("dumpNum", type=int, help="The Dump entry to delete")
1852 parser_dumpretrieve.add_argument("-s", "--dumpSaveLoc", help="The location to save the bmc dump file")
1853 parser_dumpretrieve.set_defaults(func=bmcDumpRetrieve)
1854
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001855 parser_bmc = subparsers.add_parser('bmc', help="Work with the bmc")
Justin Thalere412dc22018-01-12 16:28:24 -06001856 bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001857 parser_BMCReset = bmc_sub.add_parser('reset', help='Reset the bmc' )
1858 parser_BMCReset.add_argument('type', choices=['warm','cold'], help="Warm: Reboot the BMC, Cold: CLEAR config and reboot bmc")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001859 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.")
1860 parser_bmc.set_defaults(func=bmc)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001861
1862 #add alias to the bmc command
1863 parser_mc = subparsers.add_parser('mc', help="Work with the management controller")
Justin Thalere412dc22018-01-12 16:28:24 -06001864 mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001865 parser_MCReset = mc_sub.add_parser('reset', help='Reset the bmc' )
1866 parser_MCReset.add_argument('type', choices=['warm','cold'], help="Reboot the BMC")
1867 #parser_MCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration")
1868 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.")
Justin Thalere412dc22018-01-12 16:28:24 -06001869 parser_MCReset.set_defaults(func=bmcReset)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001870 parser_mc.set_defaults(func=bmc)
Justin Thalere412dc22018-01-12 16:28:24 -06001871
1872 #gard clear
1873 parser_gc = subparsers.add_parser("gardclear", help="Used to clear gard records")
1874 parser_gc.set_defaults(func=gardClear)
1875
1876 #firmware_flash
1877 parser_fw = subparsers.add_parser("firmware", help="Work with the system firmware")
1878 fwflash_subproc = parser_fw.add_subparsers(title='subcommands', description='valid firmware commands', help='sub-command help', dest='command')
1879 fwflash = fwflash_subproc.add_parser('flash', help="Flash the system firmware")
1880 fwflash.add_argument('type', choices=['bmc', 'pnor'], help="image type to flash")
1881 fwflash.add_argument('-f', '--fileloc', required=True, help="The absolute path to the firmware image")
1882 fwflash.set_defaults(func=fwFlash)
1883
1884 fwActivate = fwflash_subproc.add_parser('activate', help="Active existing image on the bmc")
1885 fwActivate.add_argument('imageID', help="The image ID to activate from the firmware list. Ex: 63c95399")
1886 fwActivate.set_defaults(func=activateFWImage)
1887
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001888 return parser
1889
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001890def main(argv=None):
Justin Thalere412dc22018-01-12 16:28:24 -06001891 """
1892 main function for running the command line utility as a sub application
1893 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001894 parser = createCommandParser()
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001895 args = parser.parse_args(argv)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001896
1897 totTimeStart = int(round(time.time()*1000))
1898
1899 if(sys.version_info < (3,0)):
1900 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
1901 if sys.version_info >= (3,0):
1902 requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
Justin Thalere412dc22018-01-12 16:28:24 -06001903 if (args.version):
1904 print("Version: 1.0")
1905 sys.exit(0)
1906 if (hasattr(args, 'fileloc') and args.fileloc is not None and 'print' in args.command):
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001907 mysess = None
Justin Thalere412dc22018-01-12 16:28:24 -06001908 print(selPrint('N/A', args, mysess))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001909 else:
Justin Thalere412dc22018-01-12 16:28:24 -06001910 if(hasattr(args, 'host') and hasattr(args,'user')):
1911 if (args.askpw):
1912 pw = getpass.getpass()
1913 elif(args.PW is not None):
1914 pw = args.PW
1915 else:
1916 print("You must specify a password")
1917 sys.exit()
1918 logintimeStart = int(round(time.time()*1000))
1919 mysess = login(args.host, args.user, pw, args.json)
1920 logintimeStop = int(round(time.time()*1000))
1921
1922 commandTimeStart = int(round(time.time()*1000))
1923 output = args.func(args.host, args, mysess)
1924 commandTimeStop = int(round(time.time()*1000))
1925 print(output)
1926 if (mysess is not None):
1927 logout(args.host, args.user, pw, mysess, args.json)
1928 if(args.procTime):
1929 print("Total time: " + str(int(round(time.time()*1000))- totTimeStart))
1930 print("loginTime: " + str(logintimeStop - logintimeStart))
1931 print("command Time: " + str(commandTimeStop - commandTimeStart))
1932 else:
1933 print("usage: openbmctool.py [-h] -H HOST -U USER [-A | -P PW] [-j]\n" +
1934 "\t[-t POLICYTABLELOC] [-V]\n" +
1935 "\t{fru,sensors,sel,chassis,collect_service_data,health_check,dump,bmc,mc,gardclear,firmware}\n" +
1936 "\t...\n" +
1937 "openbmctool.py: error: the following arguments are required: -H/--host, -U/--user")
1938 sys.exit()
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001939
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001940if __name__ == '__main__':
Justin Thalere412dc22018-01-12 16:28:24 -06001941 """
1942 main function when called from the command line
1943
1944 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001945 import sys
1946
1947 isTTY = sys.stdout.isatty()
1948 assert sys.version_info >= (2,7)
1949 main()