blob: d0d1571a4fb064dae9143fd08020bb7572f87dc8 [file] [log] [blame]
Justin Thalerb8807ce2018-05-25 19:16:20 -05001#!/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
Justin Thaler22b1bb52018-03-15 13:31:32 -050028import tarfile
29import tempfile
30import hashlib
Justin Thalera6b5df72018-07-16 11:10:07 -050031import re
Justin Thalerf9aee3e2017-12-05 12:11:09 -060032
Justin Thalerf9aee3e2017-12-05 12:11:09 -060033def hilight(textToColor, color, bold):
Justin Thalere412dc22018-01-12 16:28:24 -060034 """
35 Used to add highlights to various text for displaying in a terminal
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060036
Justin Thalere412dc22018-01-12 16:28:24 -060037 @param textToColor: string, the text to be colored
38 @param color: string, used to color the text red or green
39 @param bold: boolean, used to bold the textToColor
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060040 @return: Buffered reader containing the modified string.
41 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -060042 if(sys.platform.__contains__("win")):
43 if(color == "red"):
44 os.system('color 04')
45 elif(color == "green"):
46 os.system('color 02')
47 else:
48 os.system('color') #reset to default
49 return textToColor
50 else:
51 attr = []
52 if(color == "red"):
53 attr.append('31')
54 elif(color == "green"):
55 attr.append('32')
56 else:
57 attr.append('0')
58 if bold:
59 attr.append('1')
60 else:
61 attr.append('0')
62 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr),textToColor)
63
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060064
Justin Thalerf9aee3e2017-12-05 12:11:09 -060065def connectionErrHandler(jsonFormat, errorStr, err):
Justin Thalere412dc22018-01-12 16:28:24 -060066 """
67 Error handler various connection errors to bmcs
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060068
69 @param jsonFormat: boolean, used to output in json format with an error code.
Justin Thalere412dc22018-01-12 16:28:24 -060070 @param errorStr: string, used to color the text red or green
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060071 @param err: string, the text from the exception
72 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -060073 if errorStr == "Timeout":
74 if not jsonFormat:
75 return("FQPSPIN0000M: Connection timed out. Ensure you have network connectivity to the bmc")
76 else:
Justin Thaler115bca72018-05-25 19:29:08 -050077 conerror = {}
78 conerror['CommonEventID'] = 'FQPSPIN0000M'
79 conerror['sensor']="N/A"
80 conerror['state']="N/A"
81 conerror['additionalDetails'] = "N/A"
82 conerror['Message']="Connection timed out. Ensure you have network connectivity to the BMC"
83 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."
84 conerror['Serviceable']="Yes"
85 conerror['CallHomeCandidate']= "No"
86 conerror['Severity'] = "Critical"
87 conerror['EventType'] = "Communication Failure/Timeout"
88 conerror['VMMigrationFlag'] = "Yes"
89 conerror["AffectedSubsystem"] = "Interconnect (Networking)"
90 conerror["timestamp"] = str(int(time.time()))
91 conerror["UserAction"] = "Verify network connectivity between the two systems and the bmc is functional."
92 eventdict = {}
93 eventdict['event0'] = conerror
94 eventdict['numAlerts'] = '1'
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -060095
Justin Thaler115bca72018-05-25 19:29:08 -050096 errorMessageStr = errorMessageStr = json.dumps(eventdict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
Justin Thalerf9aee3e2017-12-05 12:11:09 -060097 return(errorMessageStr)
98 elif errorStr == "ConnectionError":
99 if not jsonFormat:
100 return("FQPSPIN0001M: " + str(err))
101 else:
Justin Thaler115bca72018-05-25 19:29:08 -0500102 conerror = {}
103 conerror['CommonEventID'] = 'FQPSPIN0001M'
104 conerror['sensor']="N/A"
105 conerror['state']="N/A"
106 conerror['additionalDetails'] = str(err)
107 conerror['Message']="Connection Error. View additional details for more information"
108 conerror['LengthyDescription'] = "A connection error to the specified BMC occurred and additional details are provided. Review these details to resolve the issue."
109 conerror['Serviceable']="Yes"
110 conerror['CallHomeCandidate']= "No"
111 conerror['Severity'] = "Critical"
112 conerror['EventType'] = "Communication Failure/Timeout"
113 conerror['VMMigrationFlag'] = "Yes"
114 conerror["AffectedSubsystem"] = "Interconnect (Networking)"
115 conerror["timestamp"] = str(int(time.time()))
116 conerror["UserAction"] = "Correct the issue highlighted in additional details and try again"
117 eventdict = {}
118 eventdict['event0'] = conerror
119 eventdict['numAlerts'] = '1'
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600120
Justin Thaler115bca72018-05-25 19:29:08 -0500121 errorMessageStr = json.dumps(eventdict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600122 return(errorMessageStr)
Justin Thaler115bca72018-05-25 19:29:08 -0500123
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600124 else:
125 return("Unknown Error: "+ str(err))
126
Justin Thalere412dc22018-01-12 16:28:24 -0600127
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600128def setColWidth(keylist, numCols, dictForOutput, colNames):
Justin Thalere412dc22018-01-12 16:28:24 -0600129 """
130 Sets the output width of the columns to display
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600131
132 @param keylist: list, list of strings representing the keys for the dictForOutput
Justin Thalere412dc22018-01-12 16:28:24 -0600133 @param numcols: the total number of columns in the final output
134 @param dictForOutput: dictionary, contains the information to print to the screen
135 @param colNames: list, The strings to use for the column headings, in order of the keylist
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600136 @return: A list of the column widths for each respective column.
Justin Thalere412dc22018-01-12 16:28:24 -0600137 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600138 colWidths = []
139 for x in range(0, numCols):
140 colWidths.append(0)
141 for key in dictForOutput:
142 for x in range(0, numCols):
143 colWidths[x] = max(colWidths[x], len(str(dictForOutput[key][keylist[x]])))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600144
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600145 for x in range(0, numCols):
146 colWidths[x] = max(colWidths[x], len(colNames[x])) +2
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600147
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600148 return colWidths
149
150def loadPolicyTable(pathToPolicyTable):
Justin Thalere412dc22018-01-12 16:28:24 -0600151 """
152 loads a json based policy table into a dictionary
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600153
Justin Thalere412dc22018-01-12 16:28:24 -0600154 @param value: boolean, the value to convert
155 @return: A string of "Yes" or "No"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600156 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600157 policyTable = {}
158 if(os.path.exists(pathToPolicyTable)):
159 with open(pathToPolicyTable, 'r') as stream:
160 try:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600161 contents =json.load(stream)
162 policyTable = contents['events']
Justin Thalere412dc22018-01-12 16:28:24 -0600163 except Exception as err:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600164 print(err)
165 return policyTable
166
Justin Thalere412dc22018-01-12 16:28:24 -0600167
168def boolToString(value):
169 """
170 converts a boolean value to a human readable string value
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600171
Justin Thalere412dc22018-01-12 16:28:24 -0600172 @param value: boolean, the value to convert
173 @return: A string of "Yes" or "No"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600174 """
Justin Thalere412dc22018-01-12 16:28:24 -0600175 if(value):
176 return "Yes"
177 else:
178 return "No"
179
Justin Thalera6b5df72018-07-16 11:10:07 -0500180def stringToInt(text):
181 """
182 returns an integer if the string can be converted, otherwise returns the string
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600183
Justin Thalera6b5df72018-07-16 11:10:07 -0500184 @param text: the string to try to convert to an integer
185 """
186 if text.isdigit():
187 return int(text)
188 else:
189 return text
Justin Thalere412dc22018-01-12 16:28:24 -0600190
Justin Thalera6b5df72018-07-16 11:10:07 -0500191def naturalSort(text):
192 """
193 provides a way to naturally sort a list
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600194
Justin Thalera6b5df72018-07-16 11:10:07 -0500195 @param text: the key to convert for sorting
196 @return list containing the broken up string parts by integers and strings
197 """
198 stringPartList = []
199 for c in re.split('(\d+)', text):
200 stringPartList.append(stringToInt(c))
201 return stringPartList
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600202
Justin Thalere412dc22018-01-12 16:28:24 -0600203def tableDisplay(keylist, colNames, output):
204 """
205 Logs into the BMC and creates a session
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600206
Justin Thalere412dc22018-01-12 16:28:24 -0600207 @param keylist: list, keys for the output dictionary, ordered by colNames
208 @param colNames: Names for the Table of the columns
209 @param output: The dictionary of data to display
210 @return: Session object
211 """
212 colWidth = setColWidth(keylist, len(colNames), output, colNames)
213 row = ""
214 outputText = ""
215 for i in range(len(colNames)):
216 if (i != 0): row = row + "| "
217 row = row + colNames[i].ljust(colWidth[i])
218 outputText += row + "\n"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600219
Justin Thalera6b5df72018-07-16 11:10:07 -0500220 output_keys = list(output.keys())
221 output_keys.sort(key=naturalSort)
222 for key in output_keys:
Justin Thalere412dc22018-01-12 16:28:24 -0600223 row = ""
Justin Thaler8fe0c732018-07-24 14:32:35 -0500224 for i in range(len(keylist)):
Justin Thalere412dc22018-01-12 16:28:24 -0600225 if (i != 0): row = row + "| "
226 row = row + output[key][keylist[i]].ljust(colWidth[i])
227 outputText += row + "\n"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600228
Justin Thalere412dc22018-01-12 16:28:24 -0600229 return outputText
230
Justin Thaler22b1bb52018-03-15 13:31:32 -0500231def checkFWactivation(host, args, session):
232 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600233 Checks the software inventory for an image that is being activated.
234
Justin Thaler22b1bb52018-03-15 13:31:32 -0500235 @return: True if an image is being activated, false is no activations are happening
236 """
237 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600238 httpHeader = {'Content-Type':'application/json'}
Justin Thaler22b1bb52018-03-15 13:31:32 -0500239 try:
240 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
241 except(requests.exceptions.Timeout):
242 print(connectionErrHandler(args.json, "Timeout", None))
243 return(True)
244 except(requests.exceptions.ConnectionError) as err:
245 print( connectionErrHandler(args.json, "ConnectionError", err))
246 return True
247 fwInfo = json.loads(resp.text)['data']
248 for key in fwInfo:
249 if 'Activation' in fwInfo[key]:
250 if 'Activating' in fwInfo[key]['Activation'] or 'Activating' in fwInfo[key]['RequestedActivation']:
251 return True
252 return False
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600253
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600254def login(host, username, pw,jsonFormat):
Justin Thalere412dc22018-01-12 16:28:24 -0600255 """
256 Logs into the BMC and creates a session
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600257
Justin Thalere412dc22018-01-12 16:28:24 -0600258 @param host: string, the hostname or IP address of the bmc to log into
259 @param username: The user name for the bmc to log into
260 @param pw: The password for the BMC to log into
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600261 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
Justin Thalere412dc22018-01-12 16:28:24 -0600262 @return: Session object
263 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600264 if(jsonFormat==False):
265 print("Attempting login...")
266 httpHeader = {'Content-Type':'application/json'}
267 mysess = requests.session()
268 try:
269 r = mysess.post('https://'+host+'/login', headers=httpHeader, json = {"data": [username, pw]}, verify=False, timeout=30)
270 loginMessage = json.loads(r.text)
271 if (loginMessage['status'] != "ok"):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600272 print(loginMessage["data"]["description"].encode('utf-8'))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600273 sys.exit(1)
274# if(sys.version_info < (3,0)):
275# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
276# if sys.version_info >= (3,0):
277# requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
278 return mysess
279 except(requests.exceptions.Timeout):
Justin Thaler115bca72018-05-25 19:29:08 -0500280 return (connectionErrHandler(jsonFormat, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600281 except(requests.exceptions.ConnectionError) as err:
Justin Thaler115bca72018-05-25 19:29:08 -0500282 return (connectionErrHandler(jsonFormat, "ConnectionError", err))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600283
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600284
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600285def logout(host, username, pw, session, jsonFormat):
Justin Thalere412dc22018-01-12 16:28:24 -0600286 """
287 Logs out of the bmc and terminates the session
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600288
Justin Thalere412dc22018-01-12 16:28:24 -0600289 @param host: string, the hostname or IP address of the bmc to log out of
290 @param username: The user name for the bmc to log out of
291 @param pw: The password for the BMC to log out of
292 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600293 @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
294 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600295 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600296 try:
297 r = session.post('https://'+host+'/logout', headers=httpHeader,json = {"data": [username, pw]}, verify=False, timeout=10)
298 except(requests.exceptions.Timeout):
299 print(connectionErrHandler(jsonFormat, "Timeout", None))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600300
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600301 if(jsonFormat==False):
302 if('"message": "200 OK"' in r.text):
303 print('User ' +username + ' has been logged out')
304
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600305
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600306def fru(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600307 """
308 prints out the system inventory. deprecated see fruPrint and fruList
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600309
Justin Thalere412dc22018-01-12 16:28:24 -0600310 @param host: string, the hostname or IP address of the bmc
311 @param args: contains additional arguments used by the fru sub command
312 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600313 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
314 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600315 #url="https://"+host+"/org/openbmc/inventory/system/chassis/enumerate"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600316
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600317 #print(url)
318 #res = session.get(url, headers=httpHeader, verify=False)
319 #print(res.text)
320 #sample = res.text
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600321
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600322 #inv_list = json.loads(sample)["data"]
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600323
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600324 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
325 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600326 try:
327 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
328 except(requests.exceptions.Timeout):
329 return(connectionErrHandler(args.json, "Timeout", None))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600330
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600331 sample = res.text
332# inv_list.update(json.loads(sample)["data"])
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600333#
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600334# #determine column width's
335# colNames = ["FRU Name", "FRU Type", "Has Fault", "Is FRU", "Present", "Version"]
336# colWidths = setColWidth(["FRU Name", "fru_type", "fault", "is_fru", "present", "version"], 6, inv_list, colNames)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600337#
338# print("FRU Name".ljust(colWidths[0])+ "FRU Type".ljust(colWidths[1]) + "Has Fault".ljust(colWidths[2]) + "Is FRU".ljust(colWidths[3])+
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600339# "Present".ljust(colWidths[4]) + "Version".ljust(colWidths[5]))
340# format the output
341# for key in sorted(inv_list.keys()):
342# keyParts = key.split("/")
343# isFRU = "True" if (inv_list[key]["is_fru"]==1) else "False"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600344#
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600345# fruEntry = (keyParts[len(keyParts) - 1].ljust(colWidths[0]) + inv_list[key]["fru_type"].ljust(colWidths[1])+
346# inv_list[key]["fault"].ljust(colWidths[2])+isFRU.ljust(colWidths[3])+
347# inv_list[key]["present"].ljust(colWidths[4])+ inv_list[key]["version"].ljust(colWidths[5]))
348# if(isTTY):
349# if(inv_list[key]["is_fru"] == 1):
350# color = "green"
351# bold = True
352# else:
353# color='black'
354# bold = False
355# fruEntry = hilight(fruEntry, color, bold)
356# print (fruEntry)
357 return sample
Justin Thalere412dc22018-01-12 16:28:24 -0600358
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600359def fruPrint(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600360 """
361 prints out all inventory
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600362
Justin Thalere412dc22018-01-12 16:28:24 -0600363 @param host: string, the hostname or IP address of the bmc
364 @param args: contains additional arguments used by the fru sub command
365 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600366 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
367 @return returns the total fru list.
368 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600369 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
370 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600371 try:
372 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
373 except(requests.exceptions.Timeout):
374 return(connectionErrHandler(args.json, "Timeout", None))
375
376
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600377# print(res.text)
378 frulist = res.text
379 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600380 try:
381 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
382 except(requests.exceptions.Timeout):
383 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600384# print(res.text)
385 frulist = frulist +"\n" + res.text
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600386
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600387 return frulist
388
Justin Thalere412dc22018-01-12 16:28:24 -0600389
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600390def fruList(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600391 """
392 prints out all inventory or only a specific specified item
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600393
Justin Thalere412dc22018-01-12 16:28:24 -0600394 @param host: string, the hostname or IP address of the bmc
395 @param args: contains additional arguments used by the fru sub command
396 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600397 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
398 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600399 if(args.items==True):
400 return fruPrint(host, args, session)
401 else:
Justin Thalere412dc22018-01-12 16:28:24 -0600402 return fruPrint(host, args, session)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600403
404
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600405
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600406def fruStatus(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600407 """
408 prints out the status of all FRUs
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600409
Justin Thalere412dc22018-01-12 16:28:24 -0600410 @param host: string, the hostname or IP address of the bmc
411 @param args: contains additional arguments used by the fru sub command
412 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600413 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
414 """
Justin Thalere412dc22018-01-12 16:28:24 -0600415 url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600416 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600417 try:
418 res = session.get(url, headers=httpHeader, verify=False)
419 except(requests.exceptions.Timeout):
420 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600421# print(res.text)
Justin Thalere412dc22018-01-12 16:28:24 -0600422 frulist = json.loads(res.text)['data']
423 frus = {}
424 for key in frulist:
425 component = frulist[key]
426 isFru = False
427 present = False
428 func = False
429 hasSels = False
430 keyPieces = key.split('/')
431 fruName = keyPieces[-1]
432 if 'core' in fruName: #associate cores to cpus
433 fruName = keyPieces[-2] + '-' + keyPieces[-1]
434 if 'Functional' in component:
435 if('Present' in component):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600436
Justin Thalere412dc22018-01-12 16:28:24 -0600437 if 'FieldReplaceable' in component:
438 if component['FieldReplaceable'] == 1:
439 isFru = True
440 if "fan" in fruName:
441 isFru = True;
442 if component['Present'] == 1:
443 present = True
444 if component['Functional'] == 1:
445 func = True
446 if ((key + "/fault") in frulist):
447 hasSels = True;
448 if args.verbose:
449 if hasSels:
450 loglist = []
451 faults = frulist[key+"/fault"]['endpoints']
452 for item in faults:
453 loglist.append(item.split('/')[-1])
454 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
455 else:
456 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
457 else:
458 frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
Justin Thalerfb9c81c2018-07-16 11:14:37 -0500459 elif "power_supply" in fruName or "powersupply" in fruName:
Justin Thalere412dc22018-01-12 16:28:24 -0600460 if component['Present'] ==1:
461 present = True
462 isFru = True
463 if ((key + "/fault") in frulist):
464 hasSels = True;
465 if args.verbose:
466 if hasSels:
467 loglist = []
468 faults = frulist[key+"/fault"]['endpoints']
469 for key in faults:
470 loglist.append(faults[key].split('/')[-1])
471 frus[fruName] = {"compName": fruName, "Functional": "No", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
472 else:
473 frus[fruName] = {"compName": fruName, "Functional": "Yes", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
474 else:
475 frus[fruName] = {"compName": fruName, "Functional": boolToString(not hasSels), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
476 if not args.json:
477 if not args.verbose:
478 colNames = ["Component", "Is a FRU", "Present", "Functional", "Has Logs"]
479 keylist = ["compName", "IsFru", "Present", "Functional", "hasSEL"]
480 else:
481 colNames = ["Component", "Is a FRU", "Present", "Functional", "Assoc. Log Number(s)"]
482 keylist = ["compName", "IsFru", "Present", "Functional", "selList"]
483 return tableDisplay(keylist, colNames, frus)
484 else:
485 return str(json.dumps(frus, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600486
Justin Thalere412dc22018-01-12 16:28:24 -0600487def sensor(host, args, session):
488 """
489 prints out all sensors
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600490
Justin Thalere412dc22018-01-12 16:28:24 -0600491 @param host: string, the hostname or IP address of the bmc
492 @param args: contains additional arguments used by the sensor sub command
493 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600494 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
495 """
Justin Thalere412dc22018-01-12 16:28:24 -0600496 httpHeader = {'Content-Type':'application/json'}
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600497 url="https://"+host+"/xyz/openbmc_project/sensors/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600498 try:
499 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
500 except(requests.exceptions.Timeout):
501 return(connectionErrHandler(args.json, "Timeout", None))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600502
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600503 #Get OCC status
504 url="https://"+host+"/org/open_power/control/enumerate"
Justin Thalere412dc22018-01-12 16:28:24 -0600505 try:
506 occres = session.get(url, headers=httpHeader, verify=False, timeout=30)
507 except(requests.exceptions.Timeout):
508 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600509 if not args.json:
510 colNames = ['sensor', 'type', 'units', 'value', 'target']
511 sensors = json.loads(res.text)["data"]
512 output = {}
513 for key in sensors:
514 senDict = {}
515 keyparts = key.split("/")
516 senDict['sensorName'] = keyparts[-1]
517 senDict['type'] = keyparts[-2]
Justin Thalere412dc22018-01-12 16:28:24 -0600518 try:
519 senDict['units'] = sensors[key]['Unit'].split('.')[-1]
520 except KeyError:
Justin Thaler22b1bb52018-03-15 13:31:32 -0500521 senDict['units'] = "N/A"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600522 if('Scale' in sensors[key]):
523 scale = 10 ** sensors[key]['Scale']
524 else:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600525 scale = 1
Justin Thaler22b1bb52018-03-15 13:31:32 -0500526 try:
527 senDict['value'] = str(sensors[key]['Value'] * scale)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600528 except KeyError:
Justin Thaler22b1bb52018-03-15 13:31:32 -0500529 if 'value' in sensors[key]:
530 senDict['value'] = sensors[key]['value']
531 else:
532 senDict['value'] = "N/A"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600533 if 'Target' in sensors[key]:
534 senDict['target'] = str(sensors[key]['Target'])
535 else:
536 senDict['target'] = 'N/A'
537 output[senDict['sensorName']] = senDict
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600538
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600539 occstatus = json.loads(occres.text)["data"]
540 if '/org/open_power/control/occ0' in occstatus:
541 occ0 = occstatus["/org/open_power/control/occ0"]['OccActive']
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600542 if occ0 == 1:
543 occ0 = 'Active'
544 else:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600545 occ0 = 'Inactive'
546 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'}
547 occ1 = occstatus["/org/open_power/control/occ1"]['OccActive']
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600548 if occ1 == 1:
549 occ1 = 'Active'
550 else:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600551 occ1 = 'Inactive'
552 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': occ0, 'target': 'Active'}
553 else:
554 output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
555 output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
556 keylist = ['sensorName', 'type', 'units', 'value', 'target']
Justin Thalere412dc22018-01-12 16:28:24 -0600557
558 return tableDisplay(keylist, colNames, output)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600559 else:
560 return res.text + occres.text
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600561
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600562def sel(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600563 """
564 prints out the bmc alerts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600565
Justin Thalere412dc22018-01-12 16:28:24 -0600566 @param host: string, the hostname or IP address of the bmc
567 @param args: contains additional arguments used by the sel sub command
568 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600569 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
570 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600571
572 url="https://"+host+"/xyz/openbmc_project/logging/entry/enumerate"
573 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -0600574 try:
575 res = session.get(url, headers=httpHeader, verify=False, timeout=60)
576 except(requests.exceptions.Timeout):
577 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600578 return res.text
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600579
580
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600581def parseESEL(args, eselRAW):
Justin Thalere412dc22018-01-12 16:28:24 -0600582 """
583 parses the esel data and gets predetermined search terms
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600584
Justin Thalere412dc22018-01-12 16:28:24 -0600585 @param eselRAW: string, the raw esel string from the bmc
586 @return: A dictionary containing the quick snapshot data unless args.fullEsel is listed then a full PEL log is returned
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600587 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600588 eselParts = {}
589 esel_bin = binascii.unhexlify(''.join(eselRAW.split()[16:]))
590 #search terms contains the search term as the key and the return dictionary key as it's value
591 searchTerms = { 'Signature Description':'signatureDescription', 'devdesc':'devdesc',
Justin Thaler22b1bb52018-03-15 13:31:32 -0500592 'Callout type': 'calloutType', 'Procedure':'procedure', 'Sensor Type': 'sensorType'}
Justin Thalercf1deae2018-05-25 19:35:21 -0500593 eselBinPath = tempfile.gettempdir() + os.sep + 'esel.bin'
594 with open(eselBinPath, 'wb') as f:
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600595 f.write(esel_bin)
596 errlPath = ""
597 #use the right errl file for the machine architecture
598 arch = platform.machine()
599 if(arch =='x86_64' or arch =='AMD64'):
600 if os.path.exists('/opt/ibm/ras/bin/x86_64/errl'):
601 errlPath = '/opt/ibm/ras/bin/x86_64/errl'
602 elif os.path.exists('errl/x86_64/errl'):
603 errlPath = 'errl/x86_64/errl'
604 else:
605 errlPath = 'x86_64/errl'
606 elif (platform.machine()=='ppc64le'):
607 if os.path.exists('/opt/ibm/ras/bin/ppc64le/errl'):
608 errlPath = '/opt/ibm/ras/bin/ppc64le/errl'
609 elif os.path.exists('errl/ppc64le/errl'):
610 errlPath = 'errl/ppc64le/errl'
611 else:
612 errlPath = 'ppc64le/errl'
613 else:
614 print("machine architecture not supported for parsing eSELs")
615 return eselParts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600616
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600617 if(os.path.exists(errlPath)):
Justin Thalercf1deae2018-05-25 19:35:21 -0500618 output= subprocess.check_output([errlPath, '-d', '--file='+eselBinPath]).decode('utf-8')
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600619# output = proc.communicate()[0]
620 lines = output.split('\n')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600621
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600622 if(hasattr(args, 'fullEsel')):
623 return output
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600624
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600625 for i in range(0, len(lines)):
626 lineParts = lines[i].split(':')
627 if(len(lineParts)>1): #ignore multi lines, output formatting lines, and other information
628 for term in searchTerms:
629 if(term in lineParts[0]):
630 temp = lines[i][lines[i].find(':')+1:].strip()[:-1].strip()
631 if lines[i+1].find(':') != -1:
632 if (len(lines[i+1].split(':')[0][1:].strip())==0):
633 while(len(lines[i][:lines[i].find(':')].strip())>2):
634 if((i+1) <= len(lines)):
635 i+=1
636 else:
637 i=i-1
638 break
639 temp = temp + lines[i][lines[i].find(':'):].strip()[:-1].strip()[:-1].strip()
Justin Thaler22b1bb52018-03-15 13:31:32 -0500640 if(searchTerms[term] in eselParts):
641 eselParts[searchTerms[term]] = eselParts[searchTerms[term]] + ", " + temp
642 else:
643 eselParts[searchTerms[term]] = temp
Justin Thalercf1deae2018-05-25 19:35:21 -0500644 os.remove(eselBinPath)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600645 else:
646 print("errl file cannot be found")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600647
648 return eselParts
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600649
Justin Thalere412dc22018-01-12 16:28:24 -0600650
Matt Spinler02d0dff2018-08-29 13:19:25 -0500651def getESELSeverity(esel):
652 """
653 Finds the severity type in an eSEL from the User Header section.
654 @param esel - the eSEL data
655 @return severity - e.g. 'Critical'
656 """
657
658 # everything but 1 and 2 are Critical
659 # '1': 'recovered',
660 # '2': 'predictive',
661 # '4': 'unrecoverable',
662 # '5': 'critical',
663 # '6': 'diagnostic',
664 # '7': 'symptom'
665 severities = {
666 '1': 'Informational',
667 '2': 'Warning'
668 }
669
670 try:
671 headerPosition = esel.index('55 48') # 'UH'
672 # The severity is the last byte in the 8 byte section (a byte is ' bb')
673 severity = esel[headerPosition:headerPosition+32].split(' ')[-1]
674 type = severity[0]
675 except ValueError:
676 print("Could not find severity value in UH section in eSEL")
677 type = 'x';
678
679 return severities.get(type, 'Critical')
680
681
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600682def sortSELs(events):
Justin Thalere412dc22018-01-12 16:28:24 -0600683 """
684 sorts the sels by timestamp, then log entry number
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600685
Justin Thalere412dc22018-01-12 16:28:24 -0600686 @param events: Dictionary containing events
687 @return: list containing a list of the ordered log entries, and dictionary of keys
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600688 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600689 logNumList = []
690 timestampList = []
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600691 eventKeyDict = {}
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600692 eventsWithTimestamp = {}
693 logNum2events = {}
694 for key in events:
695 if key == 'numAlerts': continue
696 if 'callout' in key: continue
697 timestamp = (events[key]['timestamp'])
698 if timestamp not in timestampList:
699 eventsWithTimestamp[timestamp] = [events[key]['logNum']]
700 else:
701 eventsWithTimestamp[timestamp].append(events[key]['logNum'])
702 #map logNumbers to the event dictionary keys
703 eventKeyDict[str(events[key]['logNum'])] = key
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600704
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600705 timestampList = list(eventsWithTimestamp.keys())
706 timestampList.sort()
707 for ts in timestampList:
708 if len(eventsWithTimestamp[ts]) > 1:
709 tmplist = eventsWithTimestamp[ts]
710 tmplist.sort()
711 logNumList = logNumList + tmplist
712 else:
713 logNumList = logNumList + eventsWithTimestamp[ts]
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600714
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600715 return [logNumList, eventKeyDict]
716
Justin Thalere412dc22018-01-12 16:28:24 -0600717
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600718def parseAlerts(policyTable, selEntries, args):
Justin Thalere412dc22018-01-12 16:28:24 -0600719 """
720 parses alerts in the IBM CER format, using an IBM policy Table
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600721
Justin Thalere412dc22018-01-12 16:28:24 -0600722 @param policyTable: dictionary, the policy table entries
723 @param selEntries: dictionary, the alerts retrieved from the bmc
724 @return: A dictionary of the parsed entries, in chronological order
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600725 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600726 eventDict = {}
727 eventNum =""
728 count = 0
729 esel = ""
730 eselParts = {}
731 i2cdevice= ""
Matt Spinler02d0dff2018-08-29 13:19:25 -0500732 eselSeverity = None
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600733
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600734 'prepare and sort the event entries'
735 for key in selEntries:
736 if 'callout' not in key:
737 selEntries[key]['logNum'] = key.split('/')[-1]
738 selEntries[key]['timestamp'] = selEntries[key]['Timestamp']
739 sortedEntries = sortSELs(selEntries)
740 logNumList = sortedEntries[0]
741 eventKeyDict = sortedEntries[1]
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600742
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600743 for logNum in logNumList:
744 key = eventKeyDict[logNum]
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600745 hasEsel=False
746 i2creadFail = False
747 if 'callout' in key:
748 continue
749 else:
750 messageID = str(selEntries[key]['Message'])
751 addDataPiece = selEntries[key]['AdditionalData']
752 calloutIndex = 0
753 calloutFound = False
754 for i in range(len(addDataPiece)):
755 if("CALLOUT_INVENTORY_PATH" in addDataPiece[i]):
756 calloutIndex = i
757 calloutFound = True
758 fruCallout = str(addDataPiece[calloutIndex]).split('=')[1]
759 if("CALLOUT_DEVICE_PATH" in addDataPiece[i]):
760 i2creadFail = True
Matt Spinlerd178a472018-08-31 09:48:52 -0500761
762 fruCallout = str(addDataPiece[calloutIndex]).split('=')[1]
763
764 # Fall back to "I2C"/"FSI" if dev path isn't in policy table
765 if (messageID + '||' + fruCallout) not in policyTable:
766 i2cdevice = str(addDataPiece[i]).strip().split('=')[1]
767 i2cdevice = '/'.join(i2cdevice.split('/')[-4:])
768 if 'fsi' in str(addDataPiece[calloutIndex]).split('=')[1]:
769 fruCallout = 'FSI'
770 else:
771 fruCallout = 'I2C'
Justin Thalere34c43a2018-05-25 19:37:55 -0500772 calloutFound = True
773 if("CALLOUT_GPIO_NUM" in addDataPiece[i]):
774 if not calloutFound:
775 fruCallout = 'GPIO'
776 calloutFound = True
777 if("CALLOUT_IIC_BUS" in addDataPiece[i]):
778 if not calloutFound:
779 fruCallout = "I2C"
780 calloutFound = True
781 if("CALLOUT_IPMI_SENSOR_NUM" in addDataPiece[i]):
782 if not calloutFound:
783 fruCallout = "IPMI"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600784 calloutFound = True
785 if("ESEL" in addDataPiece[i]):
786 esel = str(addDataPiece[i]).strip().split('=')[1]
Matt Spinler02d0dff2018-08-29 13:19:25 -0500787 eselSeverity = getESELSeverity(esel)
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600788 if args.devdebug:
789 eselParts = parseESEL(args, esel)
790 hasEsel=True
791 if("GPU" in addDataPiece[i]):
792 fruCallout = '/xyz/openbmc_project/inventory/system/chassis/motherboard/gpu' + str(addDataPiece[i]).strip()[-1]
793 calloutFound = True
794 if("PROCEDURE" in addDataPiece[i]):
795 fruCallout = str(hex(int(str(addDataPiece[i]).split('=')[1])))[2:]
796 calloutFound = True
Justin Thalere412dc22018-01-12 16:28:24 -0600797 if("RAIL_NAME" in addDataPiece[i]):
798 calloutFound=True
799 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
800 if("INPUT_NAME" in addDataPiece[i]):
801 calloutFound=True
802 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
803 if("SENSOR_TYPE" in addDataPiece[i]):
804 calloutFound=True
805 fruCallout = str(addDataPiece[i]).split('=')[1].strip()
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600806
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600807 if(calloutFound):
Justin Thaler22b1bb52018-03-15 13:31:32 -0500808 if fruCallout != "":
809 policyKey = messageID +"||" + fruCallout
Matt Spinler02d0dff2018-08-29 13:19:25 -0500810
811 # Also use the severity for hostboot errors
812 if eselSeverity and messageID == 'org.open_power.Host.Error.Event':
813 policyKey += '||' + eselSeverity
814
815 # if not in the table, fall back to the original key
816 if policyKey not in policyTable:
817 policyKey = policyKey.replace('||'+eselSeverity, '')
818
Justin Thalere34c43a2018-05-25 19:37:55 -0500819 if policyKey not in policyTable:
820 policyKey = messageID
Justin Thaler22b1bb52018-03-15 13:31:32 -0500821 else:
822 policyKey = messageID
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600823 else:
824 policyKey = messageID
825 event = {}
826 eventNum = str(count)
827 if policyKey in policyTable:
828 for pkey in policyTable[policyKey]:
829 if(type(policyTable[policyKey][pkey])== bool):
830 event[pkey] = boolToString(policyTable[policyKey][pkey])
831 else:
832 if (i2creadFail and pkey == 'Message'):
833 event[pkey] = policyTable[policyKey][pkey] + ' ' +i2cdevice
834 else:
835 event[pkey] = policyTable[policyKey][pkey]
836 event['timestamp'] = selEntries[key]['Timestamp']
837 event['resolved'] = bool(selEntries[key]['Resolved'])
838 if(hasEsel):
839 if args.devdebug:
840 event['eselParts'] = eselParts
841 event['raweSEL'] = esel
842 event['logNum'] = key.split('/')[-1]
843 eventDict['event' + eventNum] = event
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600844
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600845 else:
846 severity = str(selEntries[key]['Severity']).split('.')[-1]
847 if severity == 'Error':
848 severity = 'Critical'
849 eventDict['event'+eventNum] = {}
850 eventDict['event' + eventNum]['error'] = "error: Not found in policy table: " + policyKey
851 eventDict['event' + eventNum]['timestamp'] = selEntries[key]['Timestamp']
852 eventDict['event' + eventNum]['Severity'] = severity
853 if(hasEsel):
854 if args.devdebug:
855 eventDict['event' +eventNum]['eselParts'] = eselParts
856 eventDict['event' +eventNum]['raweSEL'] = esel
857 eventDict['event' +eventNum]['logNum'] = key.split('/')[-1]
858 eventDict['event' +eventNum]['resolved'] = bool(selEntries[key]['Resolved'])
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600859 count += 1
860 return eventDict
861
862
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600863def selDisplay(events, args):
Justin Thalere412dc22018-01-12 16:28:24 -0600864 """
865 displays alerts in human readable format
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600866
Justin Thalere412dc22018-01-12 16:28:24 -0600867 @param events: Dictionary containing events
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600868 @return:
869 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600870 activeAlerts = []
871 historyAlerts = []
872 sortedEntries = sortSELs(events)
873 logNumList = sortedEntries[0]
874 eventKeyDict = sortedEntries[1]
875 keylist = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message']
876 if(args.devdebug):
877 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity','Message', 'eSEL contents']
878 keylist.append('eSEL')
879 else:
880 colNames = ['Entry', 'ID', 'Timestamp', 'Serviceable', 'Severity', 'Message']
881 for log in logNumList:
882 selDict = {}
883 alert = events[eventKeyDict[str(log)]]
884 if('error' in alert):
885 selDict['Entry'] = alert['logNum']
886 selDict['ID'] = 'Unknown'
887 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
888 msg = alert['error']
889 polMsg = msg.split("policy table:")[0]
890 msg = msg.split("policy table:")[1]
891 msgPieces = msg.split("||")
892 err = msgPieces[0]
893 if(err.find("org.open_power.")!=-1):
894 err = err.split("org.open_power.")[1]
895 elif(err.find("xyz.openbmc_project.")!=-1):
896 err = err.split("xyz.openbmc_project.")[1]
897 else:
898 err = msgPieces[0]
899 callout = ""
900 if len(msgPieces) >1:
901 callout = msgPieces[1]
902 if(callout.find("/org/open_power/")!=-1):
903 callout = callout.split("/org/open_power/")[1]
904 elif(callout.find("/xyz/openbmc_project/")!=-1):
905 callout = callout.split("/xyz/openbmc_project/")[1]
906 else:
907 callout = msgPieces[1]
908 selDict['Message'] = polMsg +"policy table: "+ err + "||" + callout
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600909 selDict['Serviceable'] = 'Unknown'
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600910 selDict['Severity'] = alert['Severity']
911 else:
912 selDict['Entry'] = alert['logNum']
913 selDict['ID'] = alert['CommonEventID']
914 selDict['Timestamp'] = datetime.datetime.fromtimestamp(int(alert['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600915 selDict['Message'] = alert['Message']
916 selDict['Serviceable'] = alert['Serviceable']
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600917 selDict['Severity'] = alert['Severity']
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600918
919
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600920 eselOrder = ['refCode','signatureDescription', 'eselType', 'devdesc', 'calloutType', 'procedure']
921 if ('eselParts' in alert and args.devdebug):
922 eselOutput = ""
923 for item in eselOrder:
924 if item in alert['eselParts']:
925 eselOutput = eselOutput + item + ": " + alert['eselParts'][item] + " | "
926 selDict['eSEL'] = eselOutput
927 else:
928 if args.devdebug:
929 selDict['eSEL'] = "None"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600930
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600931 if not alert['resolved']:
932 activeAlerts.append(selDict)
933 else:
934 historyAlerts.append(selDict)
935 mergedOutput = activeAlerts + historyAlerts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600936 colWidth = setColWidth(keylist, len(colNames), dict(enumerate(mergedOutput)), colNames)
937
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600938 output = ""
939 if(len(activeAlerts)>0):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600940 row = ""
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600941 output +="----Active Alerts----\n"
942 for i in range(0, len(colNames)):
943 if i!=0: row =row + "| "
944 row = row + colNames[i].ljust(colWidth[i])
945 output += row + "\n"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600946
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600947 for i in range(0,len(activeAlerts)):
948 row = ""
949 for j in range(len(activeAlerts[i])):
950 if (j != 0): row = row + "| "
951 row = row + activeAlerts[i][keylist[j]].ljust(colWidth[j])
952 output += row + "\n"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600953
954 if(len(historyAlerts)>0):
955 row = ""
956 output+= "----Historical Alerts----\n"
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600957 for i in range(len(colNames)):
958 if i!=0: row =row + "| "
959 row = row + colNames[i].ljust(colWidth[i])
960 output += row + "\n"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600961
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600962 for i in range(0, len(historyAlerts)):
963 row = ""
964 for j in range(len(historyAlerts[i])):
965 if (j != 0): row = row + "| "
966 row = row + historyAlerts[i][keylist[j]].ljust(colWidth[j])
967 output += row + "\n"
968# print(events[eventKeyDict[str(log)]])
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600969 return output
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600970
Justin Thalere412dc22018-01-12 16:28:24 -0600971
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600972def selPrint(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -0600973 """
974 prints out all bmc alerts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600975
Justin Thalere412dc22018-01-12 16:28:24 -0600976 @param host: string, the hostname or IP address of the bmc
977 @param args: contains additional arguments used by the fru sub command
978 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600979 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
980 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -0600981 if(args.policyTableLoc is None):
982 if os.path.exists('policyTable.json'):
983 ptableLoc = "policyTable.json"
984 elif os.path.exists('/opt/ibm/ras/lib/policyTable.json'):
985 ptableLoc = '/opt/ibm/ras/lib/policyTable.json'
986 else:
987 ptableLoc = 'lib/policyTable.json'
988 else:
989 ptableLoc = args.policyTableLoc
990 policyTable = loadPolicyTable(ptableLoc)
991 rawselEntries = ""
992 if(hasattr(args, 'fileloc') and args.fileloc is not None):
993 if os.path.exists(args.fileloc):
994 with open(args.fileloc, 'r') as selFile:
995 selLines = selFile.readlines()
996 rawselEntries = ''.join(selLines)
997 else:
998 print("Error: File not found")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -0600999 sys.exit(1)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001000 else:
1001 rawselEntries = sel(host, args, session)
1002 loadFailed = False
1003 try:
1004 selEntries = json.loads(rawselEntries)
1005 except ValueError:
1006 loadFailed = True
1007 if loadFailed:
1008 cleanSels = json.dumps(rawselEntries).replace('\\n', '')
1009 #need to load json twice as original content was string escaped a second time
1010 selEntries = json.loads(json.loads(cleanSels))
1011 selEntries = selEntries['data']
Justin Thalere412dc22018-01-12 16:28:24 -06001012
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001013 if 'description' in selEntries:
1014 if(args.json):
1015 return("{\n\t\"numAlerts\": 0\n}")
1016 else:
1017 return("No log entries found")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001018
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001019 else:
1020 if(len(policyTable)>0):
1021 events = parseAlerts(policyTable, selEntries, args)
1022 if(args.json):
1023 events["numAlerts"] = len(events)
1024 retValue = str(json.dumps(events, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
1025 return retValue
1026 elif(hasattr(args, 'fullSel')):
1027 return events
1028 else:
1029 #get log numbers to order event entries sequentially
1030 return selDisplay(events, args)
1031 else:
1032 if(args.json):
1033 return selEntries
1034 else:
1035 print("error: Policy Table not found.")
1036 return selEntries
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001037
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001038def selList(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001039 """
1040 prints out all all bmc alerts, or only prints out the specified alerts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001041
Justin Thalere412dc22018-01-12 16:28:24 -06001042 @param host: string, the hostname or IP address of the bmc
1043 @param args: contains additional arguments used by the fru sub command
1044 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001045 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1046 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001047 return(sel(host, args, session))
1048
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001049
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001050def selClear(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001051 """
1052 clears all alerts
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001053
Justin Thalere412dc22018-01-12 16:28:24 -06001054 @param host: string, the hostname or IP address of the bmc
1055 @param args: contains additional arguments used by the fru sub command
1056 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001057 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1058 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001059 url="https://"+host+"/xyz/openbmc_project/logging/action/deleteAll"
1060 httpHeader = {'Content-Type':'application/json'}
1061 data = "{\"data\": [] }"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001062
Justin Thalere412dc22018-01-12 16:28:24 -06001063 try:
1064 res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1065 except(requests.exceptions.Timeout):
1066 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001067 if res.status_code == 200:
1068 return "The Alert Log has been cleared. Please allow a few minutes for the action to complete."
1069 else:
1070 print("Unable to clear the logs, trying to clear 1 at a time")
1071 sels = json.loads(sel(host, args, session))['data']
1072 for key in sels:
1073 if 'callout' not in key:
1074 logNum = key.split('/')[-1]
1075 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete"
1076 try:
1077 session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1078 except(requests.exceptions.Timeout):
1079 return connectionErrHandler(args.json, "Timeout", None)
1080 sys.exit(1)
1081 except(requests.exceptions.ConnectionError) as err:
1082 return connectionErrHandler(args.json, "ConnectionError", err)
1083 sys.exit(1)
1084 return ('Sel clearing complete')
1085
1086def selSetResolved(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001087 """
1088 sets a sel entry to resolved
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001089
Justin Thalere412dc22018-01-12 16:28:24 -06001090 @param host: string, the hostname or IP address of the bmc
1091 @param args: contains additional arguments used by the fru sub command
1092 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001093 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1094 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001095 url="https://"+host+"/xyz/openbmc_project/logging/entry/" + str(args.selNum) + "/attr/Resolved"
1096 httpHeader = {'Content-Type':'application/json'}
1097 data = "{\"data\": 1 }"
Justin Thalere412dc22018-01-12 16:28:24 -06001098 try:
1099 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1100 except(requests.exceptions.Timeout):
1101 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001102 if res.status_code == 200:
1103 return "Sel entry "+ str(args.selNum) +" is now set to resolved"
1104 else:
1105 return "Unable to set the alert to resolved"
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001106
Justin Thalere412dc22018-01-12 16:28:24 -06001107def selResolveAll(host, args, session):
1108 """
1109 sets a sel entry to resolved
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001110
Justin Thalere412dc22018-01-12 16:28:24 -06001111 @param host: string, the hostname or IP address of the bmc
1112 @param args: contains additional arguments used by the fru sub command
1113 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001114 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1115 """
Justin Thalere412dc22018-01-12 16:28:24 -06001116 rawselEntries = sel(host, args, session)
1117 loadFailed = False
1118 try:
1119 selEntries = json.loads(rawselEntries)
1120 except ValueError:
1121 loadFailed = True
1122 if loadFailed:
1123 cleanSels = json.dumps(rawselEntries).replace('\\n', '')
1124 #need to load json twice as original content was string escaped a second time
1125 selEntries = json.loads(json.loads(cleanSels))
1126 selEntries = selEntries['data']
1127
1128 if 'description' in selEntries:
1129 if(args.json):
1130 return("{\n\t\"selsResolved\": 0\n}")
1131 else:
1132 return("No log entries found")
1133 else:
1134 d = vars(args)
1135 successlist = []
1136 failedlist = []
1137 for key in selEntries:
1138 if 'callout' not in key:
1139 d['selNum'] = key.split('/')[-1]
1140 resolved = selSetResolved(host,args,session)
1141 if 'Sel entry' in resolved:
1142 successlist.append(d['selNum'])
1143 else:
1144 failedlist.append(d['selNum'])
1145 output = ""
1146 successlist.sort()
1147 failedlist.sort()
1148 if len(successlist)>0:
1149 output = "Successfully resolved: " +', '.join(successlist) +"\n"
1150 if len(failedlist)>0:
1151 output += "Failed to resolve: " + ', '.join(failedlist) + "\n"
1152 return output
1153
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001154def chassisPower(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001155 """
1156 called by the chassis function. Controls the power state of the chassis, or gets the status
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001157
Justin Thalere412dc22018-01-12 16:28:24 -06001158 @param host: string, the hostname or IP address of the bmc
1159 @param args: contains additional arguments used by the fru sub command
1160 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001161 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1162 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001163 if(args.powcmd == 'on'):
Justin Thaler22b1bb52018-03-15 13:31:32 -05001164 if checkFWactivation(host, args, session):
1165 return ("Chassis Power control disabled during firmware activation")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001166 print("Attempting to Power on...:")
1167 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
1168 httpHeader = {'Content-Type':'application/json',}
1169 data = '{"data":"xyz.openbmc_project.State.Host.Transition.On"}'
Justin Thalere412dc22018-01-12 16:28:24 -06001170 try:
1171 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1172 except(requests.exceptions.Timeout):
1173 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001174 return res.text
Justin Thalere412dc22018-01-12 16:28:24 -06001175 elif(args.powcmd == 'softoff'):
Justin Thaler22b1bb52018-03-15 13:31:32 -05001176 if checkFWactivation(host, args, session):
1177 return ("Chassis Power control disabled during firmware activation")
Justin Thalere412dc22018-01-12 16:28:24 -06001178 print("Attempting to Power off gracefully...:")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001179 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
1180 httpHeader = {'Content-Type':'application/json'}
1181 data = '{"data":"xyz.openbmc_project.State.Host.Transition.Off"}'
Justin Thalere412dc22018-01-12 16:28:24 -06001182 try:
1183 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1184 except(requests.exceptions.Timeout):
1185 return(connectionErrHandler(args.json, "Timeout", None))
1186 return res.text
1187 elif(args.powcmd == 'hardoff'):
Justin Thaler22b1bb52018-03-15 13:31:32 -05001188 if checkFWactivation(host, args, session):
1189 return ("Chassis Power control disabled during firmware activation")
Justin Thalere412dc22018-01-12 16:28:24 -06001190 print("Attempting to Power off immediately...:")
1191 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/RequestedPowerTransition"
1192 httpHeader = {'Content-Type':'application/json'}
1193 data = '{"data":"xyz.openbmc_project.State.Chassis.Transition.Off"}'
1194 try:
1195 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1196 except(requests.exceptions.Timeout):
1197 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001198 return res.text
1199 elif(args.powcmd == 'status'):
1200 url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState"
1201 httpHeader = {'Content-Type':'application/json'}
1202# print(url)
Justin Thalere412dc22018-01-12 16:28:24 -06001203 try:
1204 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1205 except(requests.exceptions.Timeout):
1206 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001207 chassisState = json.loads(res.text)['data'].split('.')[-1]
1208 url="https://"+host+"/xyz/openbmc_project/state/host0/attr/CurrentHostState"
Justin Thalere412dc22018-01-12 16:28:24 -06001209 try:
1210 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1211 except(requests.exceptions.Timeout):
1212 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001213 hostState = json.loads(res.text)['data'].split('.')[-1]
1214 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/CurrentBMCState"
Justin Thalere412dc22018-01-12 16:28:24 -06001215 try:
1216 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1217 except(requests.exceptions.Timeout):
1218 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001219 bmcState = json.loads(res.text)['data'].split('.')[-1]
1220 if(args.json):
1221 outDict = {"Chassis Power State" : chassisState, "Host Power State" : hostState, "BMC Power State":bmcState}
1222 return json.dumps(outDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
1223 else:
1224 return "Chassis Power State: " +chassisState + "\nHost Power State: " + hostState + "\nBMC Power State: " + bmcState
1225 else:
1226 return "Invalid chassis power command"
1227
Justin Thalere412dc22018-01-12 16:28:24 -06001228
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001229def chassisIdent(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001230 """
1231 called by the chassis function. Controls the identify led of the chassis. Sets or gets the state
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001232
Justin Thalere412dc22018-01-12 16:28:24 -06001233 @param host: string, the hostname or IP address of the bmc
1234 @param args: contains additional arguments used by the fru sub command
1235 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001236 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001237 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001238 if(args.identcmd == 'on'):
1239 print("Attempting to turn identify light on...:")
1240 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
1241 httpHeader = {'Content-Type':'application/json',}
1242 data = '{"data":true}'
Justin Thalere412dc22018-01-12 16:28:24 -06001243 try:
1244 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1245 except(requests.exceptions.Timeout):
1246 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001247 return res.text
1248 elif(args.identcmd == 'off'):
1249 print("Attempting to turn identify light off...:")
1250 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
1251 httpHeader = {'Content-Type':'application/json'}
1252 data = '{"data":false}'
Justin Thalere412dc22018-01-12 16:28:24 -06001253 try:
1254 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1255 except(requests.exceptions.Timeout):
1256 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001257 return res.text
1258 elif(args.identcmd == 'status'):
1259 url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify"
1260 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -06001261 try:
1262 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
1263 except(requests.exceptions.Timeout):
1264 return(connectionErrHandler(args.json, "Timeout", None))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001265 status = json.loads(res.text)['data']
1266 if(args.json):
1267 return status
1268 else:
1269 if status['Asserted'] == 0:
1270 return "Identify light is off"
1271 else:
1272 return "Identify light is blinking"
1273 else:
1274 return "Invalid chassis identify command"
1275
Justin Thalere412dc22018-01-12 16:28:24 -06001276
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001277def chassis(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001278 """
1279 controls the different chassis commands
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001280
Justin Thalere412dc22018-01-12 16:28:24 -06001281 @param host: string, the hostname or IP address of the bmc
1282 @param args: contains additional arguments used by the fru sub command
1283 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001284 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1285 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001286 if(hasattr(args, 'powcmd')):
1287 result = chassisPower(host,args,session)
1288 elif(hasattr(args, 'identcmd')):
1289 result = chassisIdent(host, args, session)
1290 else:
Justin Thaler22b1bb52018-03-15 13:31:32 -05001291 return "This feature is not yet implemented"
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001292 return result
Justin Thalere412dc22018-01-12 16:28:24 -06001293
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001294def bmcDumpRetrieve(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001295 """
1296 Downloads a dump file from the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001297
Justin Thalere412dc22018-01-12 16:28:24 -06001298 @param host: string, the hostname or IP address of the bmc
1299 @param args: contains additional arguments used by the collectServiceData sub command
1300 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001301 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001302 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001303 httpHeader = {'Content-Type':'application/json'}
1304 dumpNum = args.dumpNum
1305 if (args.dumpSaveLoc is not None):
1306 saveLoc = args.dumpSaveLoc
1307 else:
Justin Thalercf1deae2018-05-25 19:35:21 -05001308 saveLoc = tempfile.gettempdir()
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001309 url ='https://'+host+'/download/dump/' + str(dumpNum)
1310 try:
Justin Thalere412dc22018-01-12 16:28:24 -06001311 r = session.get(url, headers=httpHeader, stream=True, verify=False, timeout=30)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001312 if (args.dumpSaveLoc is not None):
1313 if os.path.exists(saveLoc):
1314 if saveLoc[-1] != os.path.sep:
1315 saveLoc = saveLoc + os.path.sep
1316 filename = saveLoc + host+'-dump' + str(dumpNum) + '.tar.xz'
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001317
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001318 else:
1319 return 'Invalid save location specified'
1320 else:
Justin Thalercf1deae2018-05-25 19:35:21 -05001321 filename = tempfile.gettempdir()+os.sep + host+'-dump' + str(dumpNum) + '.tar.xz'
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001322
1323 with open(filename, 'wb') as f:
1324 for chunk in r.iter_content(chunk_size =1024):
1325 if chunk:
1326 f.write(chunk)
1327 return 'Saved as ' + filename
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001328
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001329 except(requests.exceptions.Timeout):
1330 return connectionErrHandler(args.json, "Timeout", None)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001331
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001332 except(requests.exceptions.ConnectionError) as err:
1333 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001334
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001335def bmcDumpList(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001336 """
1337 Lists the number of dump files on the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001338
Justin Thalere412dc22018-01-12 16:28:24 -06001339 @param host: string, the hostname or IP address of the bmc
1340 @param args: contains additional arguments used by the collectServiceData sub command
1341 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001342 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1343 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001344 httpHeader = {'Content-Type':'application/json'}
1345 url ='https://'+host+'/xyz/openbmc_project/dump/list'
1346 try:
1347 r = session.get(url, headers=httpHeader, verify=False, timeout=20)
1348 dumpList = json.loads(r.text)
1349 return r.text
1350 except(requests.exceptions.Timeout):
1351 return connectionErrHandler(args.json, "Timeout", None)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001352
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001353 except(requests.exceptions.ConnectionError) as err:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001354 return connectionErrHandler(args.json, "ConnectionError", err)
1355
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001356def bmcDumpDelete(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001357 """
1358 Deletes BMC dump files from the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001359
Justin Thalere412dc22018-01-12 16:28:24 -06001360 @param host: string, the hostname or IP address of the bmc
1361 @param args: contains additional arguments used by the collectServiceData sub command
1362 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001363 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001364 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001365 httpHeader = {'Content-Type':'application/json'}
1366 dumpList = []
1367 successList = []
1368 failedList = []
1369 if args.dumpNum is not None:
1370 if isinstance(args.dumpNum, list):
1371 dumpList = args.dumpNum
1372 else:
1373 dumpList.append(args.dumpNum)
1374 for dumpNum in dumpList:
1375 url ='https://'+host+'/xyz/openbmc_project/dump/entry/'+str(dumpNum)+'/action/Delete'
1376 try:
1377 r = session.post(url, headers=httpHeader, json = {"data": []}, verify=False, timeout=30)
1378 if r.status_code == 200:
1379 successList.append(str(dumpNum))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001380 else:
1381 failedList.append(str(dumpNum))
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001382 except(requests.exceptions.Timeout):
1383 return connectionErrHandler(args.json, "Timeout", None)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001384 except(requests.exceptions.ConnectionError) as err:
1385 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001386 output = "Successfully deleted dumps: " + ', '.join(successList)
1387 if(len(failedList)>0):
1388 output+= '\nFailed to delete dumps: ' + ', '.join(failedList)
1389 return output
1390 else:
1391 return 'You must specify an entry number to delete'
1392
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001393def bmcDumpDeleteAll(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001394 """
1395 Deletes All BMC dump files from the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001396
Justin Thalere412dc22018-01-12 16:28:24 -06001397 @param host: string, the hostname or IP address of the bmc
1398 @param args: contains additional arguments used by the collectServiceData sub command
1399 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001400 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001401 """
1402 dumpResp = bmcDumpList(host, args, session)
1403 if 'FQPSPIN0000M' in dumpResp or 'FQPSPIN0001M'in dumpResp:
1404 return dumpResp
1405 dumpList = json.loads(dumpResp)['data']
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001406 d = vars(args)
1407 dumpNums = []
1408 for dump in dumpList:
1409 if '/xyz/openbmc_project/dump/internal/manager' not in dump:
1410 dumpNums.append(int(dump.strip().split('/')[-1]))
1411 d['dumpNum'] = dumpNums
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001412
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001413 return bmcDumpDelete(host, args, session)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001414
Justin Thalere412dc22018-01-12 16:28:24 -06001415
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001416def bmcDumpCreate(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001417 """
1418 Creates a bmc dump file
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001419
Justin Thalere412dc22018-01-12 16:28:24 -06001420 @param host: string, the hostname or IP address of the bmc
1421 @param args: contains additional arguments used by the collectServiceData sub command
1422 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001423 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001424 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001425 httpHeader = {'Content-Type':'application/json'}
1426 url = 'https://'+host+'/xyz/openbmc_project/dump/action/CreateDump'
1427 try:
1428 r = session.post(url, headers=httpHeader, json = {"data": []}, verify=False, timeout=30)
1429 if('"message": "200 OK"' in r.text and not args.json):
1430 return ('Dump successfully created')
1431 else:
1432 return ('Failed to create dump')
1433 except(requests.exceptions.Timeout):
1434 return connectionErrHandler(args.json, "Timeout", None)
1435 except(requests.exceptions.ConnectionError) as err:
1436 return connectionErrHandler(args.json, "ConnectionError", err)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001437
1438
1439
Justin Thalere412dc22018-01-12 16:28:24 -06001440
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001441def collectServiceData(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001442 """
1443 Collects all data needed for service from the BMC
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001444
Justin Thalere412dc22018-01-12 16:28:24 -06001445 @param host: string, the hostname or IP address of the bmc
1446 @param args: contains additional arguments used by the collectServiceData sub command
1447 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001448 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Justin Thalere412dc22018-01-12 16:28:24 -06001449 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001450
Justin Thaler22b1bb52018-03-15 13:31:32 -05001451 global toolVersion
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001452 #create a bmc dump
1453 dumpcount = len(json.loads(bmcDumpList(host, args, session))['data'])
1454 try:
1455 dumpcreated = bmcDumpCreate(host, args, session)
1456 except Exception as e:
1457 print('failed to create a bmc dump')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001458
1459
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001460 #Collect Inventory
1461 try:
1462 args.silent = True
Justin Thalercf1deae2018-05-25 19:35:21 -05001463 myDir = tempfile.gettempdir()+os.sep + host + "--" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001464 os.makedirs(myDir)
1465 filelist = []
1466 frulist = fruPrint(host, args, session)
1467 with open(myDir +'/inventory.txt', 'w') as f:
1468 f.write(frulist)
1469 print("Inventory collected and stored in " + myDir + "/inventory.txt")
1470 filelist.append(myDir+'/inventory.txt')
1471 except Exception as e:
1472 print("Failed to collect inventory")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001473
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001474 #Read all the sensor and OCC status
1475 try:
1476 sensorReadings = sensor(host, args, session)
1477 with open(myDir +'/sensorReadings.txt', 'w') as f:
1478 f.write(sensorReadings)
1479 print("Sensor readings collected and stored in " +myDir + "/sensorReadings.txt")
1480 filelist.append(myDir+'/sensorReadings.txt')
1481 except Exception as e:
1482 print("Failed to collect sensor readings")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001483
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001484 #Collect all of the LEDs status
1485 try:
1486 url="https://"+host+"/xyz/openbmc_project/led/enumerate"
1487 httpHeader = {'Content-Type':'application/json'}
1488 leds = session.get(url, headers=httpHeader, verify=False, timeout=20)
1489 with open(myDir +'/ledStatus.txt', 'w') as f:
1490 f.write(leds.text)
1491 print("System LED status collected and stored in "+myDir +"/ledStatus.txt")
1492 filelist.append(myDir+'/ledStatus.txt')
1493 except Exception as e:
1494 print("Failed to collect LED status")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001495
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001496 #Collect the bmc logs
1497 try:
1498 sels = selPrint(host,args,session)
1499 with open(myDir +'/SELshortlist.txt', 'w') as f:
1500 f.write(str(sels))
1501 print("sel short list collected and stored in "+myDir +"/SELshortlist.txt")
1502 filelist.append(myDir+'/SELshortlist.txt')
1503 time.sleep(2)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001504
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001505 d = vars(args)
1506 d['json'] = True
1507 d['fullSel'] = True
1508 parsedfullsels = json.loads(selPrint(host, args, session))
1509 d['fullEsel'] = True
1510 sortedSELs = sortSELs(parsedfullsels)
1511 with open(myDir +'/parsedSELs.txt', 'w') as f:
1512 for log in sortedSELs[0]:
1513 esel = ""
1514 parsedfullsels[sortedSELs[1][str(log)]]['timestamp'] = datetime.datetime.fromtimestamp(int(parsedfullsels[sortedSELs[1][str(log)]]['timestamp']/1000)).strftime("%Y-%m-%d %H:%M:%S")
1515 if ('raweSEL' in parsedfullsels[sortedSELs[1][str(log)]] and args.devdebug):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001516 esel = parsedfullsels[sortedSELs[1][str(log)]]['raweSEL']
1517 del parsedfullsels[sortedSELs[1][str(log)]]['raweSEL']
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001518 f.write(json.dumps(parsedfullsels[sortedSELs[1][str(log)]],sort_keys=True, indent=4, separators=(',', ': ')))
1519 if(args.devdebug and esel != ""):
1520 f.write(parseESEL(args, esel))
1521 print("fully parsed sels collected and stored in "+myDir +"/parsedSELs.txt")
1522 filelist.append(myDir+'/parsedSELs.txt')
1523 except Exception as e:
1524 print("Failed to collect system event logs")
1525 print(e)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001526
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001527 #collect RAW bmc enumeration
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001528 try:
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001529 url="https://"+host+"/xyz/openbmc_project/enumerate"
1530 print("Attempting to get a full BMC enumeration")
1531 fullDump = session.get(url, headers=httpHeader, verify=False, timeout=120)
1532 with open(myDir +'/bmcFullRaw.txt', 'w') as f:
1533 f.write(fullDump.text)
1534 print("RAW BMC data collected and saved into "+myDir +"/bmcFullRaw.txt")
1535 filelist.append(myDir+'/bmcFullRaw.txt')
1536 except Exception as e:
1537 print("Failed to collect bmc full enumeration")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001538
1539 #collect the dump files
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001540 waitingForNewDump = True
1541 count = 0;
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001542 while(waitingForNewDump):
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001543 dumpList = json.loads(bmcDumpList(host, args, session))['data']
1544 if len(dumpList) > dumpcount:
1545 waitingForNewDump = False
1546 break;
1547 elif(count>30):
1548 print("Timed out waiting for bmc to make a new dump file. Dump space may be full.")
1549 break;
1550 else:
1551 time.sleep(2)
1552 count += 1
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001553 try:
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001554 print('Collecting bmc dump files')
1555 d['dumpSaveLoc'] = myDir
1556 dumpList = json.loads(bmcDumpList(host, args, session))['data']
1557 for dump in dumpList:
1558 if '/xyz/openbmc_project/dump/internal/manager' not in dump:
1559 d['dumpNum'] = int(dump.strip().split('/')[-1])
1560 print('retrieving dump file ' + str(d['dumpNum']))
1561 filename = bmcDumpRetrieve(host, args, session).split('Saved as ')[-1]
1562 filelist.append(filename)
1563 time.sleep(2)
1564 except Exception as e:
1565 print("Failed to collect bmc dump files")
1566 print(e)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001567
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001568 #create the zip file
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001569 try:
Justin Thalercf1deae2018-05-25 19:35:21 -05001570 filename = myDir.split(tempfile.gettempdir()+os.sep)[-1] + "_" + toolVersion + '_openbmc.zip'
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001571 zf = zipfile.ZipFile(myDir+'/' + filename, 'w')
1572 for myfile in filelist:
1573 zf.write(myfile, os.path.basename(myfile))
1574 zf.close()
1575 except Exception as e:
1576 print("Failed to create zip file with collected information")
1577 return "data collection complete"
1578
Justin Thalere412dc22018-01-12 16:28:24 -06001579
1580def healthCheck(host, args, session):
1581 """
1582 runs a health check on the platform
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001583
Justin Thalere412dc22018-01-12 16:28:24 -06001584 @param host: string, the hostname or IP address of the bmc
1585 @param args: contains additional arguments used by the bmc sub command
1586 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001587 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1588 """
Justin Thalere412dc22018-01-12 16:28:24 -06001589 #check fru status and get as json to easily work through
1590 d = vars(args)
1591 useJson = d['json']
1592 d['json'] = True
1593 d['verbose']= False
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001594
Justin Thalere412dc22018-01-12 16:28:24 -06001595 frus = json.loads(fruStatus(host, args, session))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001596
Justin Thalere412dc22018-01-12 16:28:24 -06001597 hwStatus= "OK"
1598 performanceStatus = "OK"
1599 for key in frus:
1600 if frus[key]["Functional"] == "No" and frus[key]["Present"] == "Yes":
1601 hwStatus= "Degraded"
Justin Thalerfb9c81c2018-07-16 11:14:37 -05001602 if("power_supply" in key or "powersupply" in key):
1603 gpuCount =0
1604 for comp in frus:
Justin Thalere412dc22018-01-12 16:28:24 -06001605 if "gv100card" in comp:
1606 gpuCount +=1
1607 if gpuCount > 4:
1608 hwStatus = "Critical"
1609 performanceStatus="Degraded"
1610 break;
1611 elif("fan" in key):
1612 hwStatus = "Degraded"
1613 else:
1614 performanceStatus = "Degraded"
1615 if useJson:
1616 output = {"Hardware Status": hwStatus, "Performance": performanceStatus}
1617 output = json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
1618 else:
1619 output = ("Hardware Status: " + hwStatus +
1620 "\nPerformance: " +performanceStatus )
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001621
1622
Justin Thalere412dc22018-01-12 16:28:24 -06001623 #SW407886: Clear the duplicate entries
1624 #collect the dups
1625 d['devdebug'] = False
1626 sels = json.loads(selPrint(host, args, session))
1627 logNums2Clr = []
1628 oldestLogNum={"logNum": "bogus" ,"key" : ""}
1629 count = 0
1630 if sels['numAlerts'] > 0:
1631 for key in sels:
1632 if "numAlerts" in key:
1633 continue
1634 try:
1635 if "slave@00:00/00:00:00:06/sbefifo1-dev0/occ1-dev0" in sels[key]['Message']:
1636 count += 1
1637 if count > 1:
1638 #preserve first occurrence
1639 if sels[key]['timestamp'] < sels[oldestLogNum['key']]['timestamp']:
1640 oldestLogNum['key']=key
1641 oldestLogNum['logNum'] = sels[key]['logNum']
1642 else:
1643 oldestLogNum['key']=key
1644 oldestLogNum['logNum'] = sels[key]['logNum']
1645 logNums2Clr.append(sels[key]['logNum'])
1646 except KeyError:
1647 continue
1648 if(count >0):
1649 logNums2Clr.remove(oldestLogNum['logNum'])
1650 #delete the dups
1651 if count >1:
1652 httpHeader = {'Content-Type':'application/json'}
1653 data = "{\"data\": [] }"
1654 for logNum in logNums2Clr:
1655 url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete"
1656 try:
1657 session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1658 except(requests.exceptions.Timeout):
1659 deleteFailed = True
1660 except(requests.exceptions.ConnectionError) as err:
1661 deleteFailed = True
1662 #End of defect resolve code
1663 d['json'] = useJson
1664 return output
1665
1666
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001667
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001668def bmc(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001669 """
1670 handles various bmc level commands, currently bmc rebooting
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001671
Justin Thalere412dc22018-01-12 16:28:24 -06001672 @param host: string, the hostname or IP address of the bmc
1673 @param args: contains additional arguments used by the bmc sub command
1674 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001675 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1676 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001677 if(args.type is not None):
1678 return bmcReset(host, args, session)
Justin Thalere412dc22018-01-12 16:28:24 -06001679 if(args.info):
1680 return "Not implemented at this time"
1681
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001682
Justin Thalere412dc22018-01-12 16:28:24 -06001683
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001684def bmcReset(host, args, session):
Justin Thalere412dc22018-01-12 16:28:24 -06001685 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001686 controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots.
1687
Justin Thalere412dc22018-01-12 16:28:24 -06001688 @param host: string, the hostname or IP address of the bmc
1689 @param args: contains additional arguments used by the bmcReset sub command
1690 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001691 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
1692 """
Justin Thaler22b1bb52018-03-15 13:31:32 -05001693 if checkFWactivation(host, args, session):
1694 return ("BMC reset control disabled during firmware activation")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001695 if(args.type == "warm"):
1696 print("\nAttempting to reboot the BMC...:")
1697 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
1698 httpHeader = {'Content-Type':'application/json'}
Justin Thalere412dc22018-01-12 16:28:24 -06001699 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
1700 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001701 return res.text
1702 elif(args.type =="cold"):
Justin Thalere412dc22018-01-12 16:28:24 -06001703 print("\nAttempting to reboot the BMC...:")
1704 url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
1705 httpHeader = {'Content-Type':'application/json'}
1706 data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
1707 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
1708 return res.text
Justin Thalerf9aee3e2017-12-05 12:11:09 -06001709 else:
1710 return "invalid command"
Justin Thalere412dc22018-01-12 16:28:24 -06001711
1712def gardClear(host, args, session):
1713 """
1714 clears the gard records from the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001715
Justin Thalere412dc22018-01-12 16:28:24 -06001716 @param host: string, the hostname or IP address of the bmc
1717 @param args: contains additional arguments used by the gardClear sub command
1718 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001719 """
Justin Thalere412dc22018-01-12 16:28:24 -06001720 url="https://"+host+"/org/open_power/control/gard/action/Reset"
1721 httpHeader = {'Content-Type':'application/json'}
1722 data = '{"data":[]}'
1723 try:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001724
Justin Thalere412dc22018-01-12 16:28:24 -06001725 res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
1726 if res.status_code == 404:
1727 return "Command not supported by this firmware version"
1728 else:
1729 return res.text
1730 except(requests.exceptions.Timeout):
1731 return connectionErrHandler(args.json, "Timeout", None)
1732 except(requests.exceptions.ConnectionError) as err:
1733 return connectionErrHandler(args.json, "ConnectionError", err)
1734
1735def activateFWImage(host, args, session):
1736 """
1737 activates a firmware image on the bmc
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001738
Justin Thalere412dc22018-01-12 16:28:24 -06001739 @param host: string, the hostname or IP address of the bmc
1740 @param args: contains additional arguments used by the fwflash sub command
1741 @param session: the active session to use
1742 @param fwID: the unique ID of the fw image to activate
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001743 """
Justin Thalere412dc22018-01-12 16:28:24 -06001744 fwID = args.imageID
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001745
Justin Thalere412dc22018-01-12 16:28:24 -06001746 #determine the existing versions
1747 httpHeader = {'Content-Type':'application/json'}
1748 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1749 try:
1750 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1751 except(requests.exceptions.Timeout):
1752 return connectionErrHandler(args.json, "Timeout", None)
1753 except(requests.exceptions.ConnectionError) as err:
1754 return connectionErrHandler(args.json, "ConnectionError", err)
1755 existingSoftware = json.loads(resp.text)['data']
1756 altVersionID = ''
1757 versionType = ''
1758 imageKey = '/xyz/openbmc_project/software/'+fwID
1759 if imageKey in existingSoftware:
1760 versionType = existingSoftware[imageKey]['Purpose']
1761 for key in existingSoftware:
1762 if imageKey == key:
1763 continue
1764 if 'Purpose' in existingSoftware[key]:
1765 if versionType == existingSoftware[key]['Purpose']:
1766 altVersionID = key.split('/')[-1]
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001767
1768
1769
1770
Justin Thalere412dc22018-01-12 16:28:24 -06001771 url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/attr/Priority"
1772 url1="https://"+host+"/xyz/openbmc_project/software/"+ altVersionID + "/attr/Priority"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001773 data = "{\"data\": 0}"
Justin Thalere412dc22018-01-12 16:28:24 -06001774 data1 = "{\"data\": 1 }"
1775 try:
1776 resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1777 resp1 = session.put(url1, headers=httpHeader, data=data1, verify=False, timeout=30)
1778 except(requests.exceptions.Timeout):
1779 return connectionErrHandler(args.json, "Timeout", None)
1780 except(requests.exceptions.ConnectionError) as err:
1781 return connectionErrHandler(args.json, "ConnectionError", err)
1782 if(not args.json):
1783 if resp.status_code == 200 and resp1.status_code == 200:
Justin Thaler22b1bb52018-03-15 13:31:32 -05001784 return 'Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. '
Justin Thalere412dc22018-01-12 16:28:24 -06001785 else:
1786 return "Firmware activation failed."
1787 else:
1788 return resp.text + resp1.text
Justin Thaler22b1bb52018-03-15 13:31:32 -05001789
1790def activateStatus(host, args, session):
1791 if checkFWactivation(host, args, session):
1792 return("Firmware is currently being activated. Do not reboot the BMC or start the Host OS")
1793 else:
1794 return("No firmware activations are pending")
1795
1796def extractFWimage(path, imageType):
1797 """
1798 extracts the bmc image and returns information about the package
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001799
Justin Thaler22b1bb52018-03-15 13:31:32 -05001800 @param path: the path and file name of the firmware image
1801 @param imageType: The type of image the user is trying to flash. Host or BMC
1802 @return: the image id associated with the package. returns an empty string on error.
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001803 """
Justin Thaler22b1bb52018-03-15 13:31:32 -05001804 f = tempfile.TemporaryFile()
1805 tmpDir = tempfile.gettempdir()
1806 newImageID = ""
1807 if os.path.exists(path):
1808 try:
1809 imageFile = tarfile.open(path,'r')
1810 contents = imageFile.getmembers()
1811 for tf in contents:
1812 if 'MANIFEST' in tf.name:
1813 imageFile.extract(tf.name, path=tmpDir)
1814 with open(tempfile.gettempdir() +os.sep+ tf.name, 'r') as imageInfo:
1815 for line in imageInfo:
1816 if 'purpose' in line:
1817 purpose = line.split('=')[1]
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001818 if imageType not in purpose.split('.')[-1]:
Justin Thaler22b1bb52018-03-15 13:31:32 -05001819 print('The specified image is not for ' + imageType)
1820 print('Please try again with the image for ' + imageType)
1821 return ""
1822 if 'version' == line.split('=')[0]:
1823 version = line.split('=')[1].strip().encode('utf-8')
1824 m = hashlib.sha512()
1825 m.update(version)
1826 newImageID = m.hexdigest()[:8]
1827 break
1828 try:
1829 os.remove(tempfile.gettempdir() +os.sep+ tf.name)
1830 except OSError:
1831 pass
1832 return newImageID
1833 except tarfile.ExtractError as e:
1834 print('Unable to extract information from the firmware file.')
1835 print('Ensure you have write access to the directory: ' + tmpDir)
1836 return newImageID
1837 except tarfile.TarError as e:
1838 print('This is not a valid firmware file.')
1839 return newImageID
1840 print("This is not a valid firmware file.")
1841 return newImageID
1842 else:
1843 print('The filename and path provided are not valid.')
1844 return newImageID
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001845
Justin Thaler22b1bb52018-03-15 13:31:32 -05001846def getAllFWImageIDs(fwInvDict):
1847 """
1848 gets a list of all the firmware image IDs
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001849
Justin Thaler22b1bb52018-03-15 13:31:32 -05001850 @param fwInvDict: the dictionary to search for FW image IDs
1851 @return: list containing string representation of the found image ids
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001852 """
Justin Thaler22b1bb52018-03-15 13:31:32 -05001853 idList = []
1854 for key in fwInvDict:
1855 if 'Version' in fwInvDict[key]:
1856 idList.append(key.split('/')[-1])
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001857 return idList
1858
Justin Thalere412dc22018-01-12 16:28:24 -06001859def fwFlash(host, args, session):
1860 """
1861 updates the bmc firmware and pnor firmware
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001862
Justin Thalere412dc22018-01-12 16:28:24 -06001863 @param host: string, the hostname or IP address of the bmc
1864 @param args: contains additional arguments used by the fwflash sub command
1865 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001866 """
Justin Thaler22b1bb52018-03-15 13:31:32 -05001867 d = vars(args)
Justin Thalere412dc22018-01-12 16:28:24 -06001868 if(args.type == 'bmc'):
1869 purp = 'BMC'
1870 else:
1871 purp = 'Host'
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001872
1873 #check power state of the machine. No concurrent FW updates allowed
Justin Thaler22b1bb52018-03-15 13:31:32 -05001874 d['powcmd'] = 'status'
1875 powerstate = chassisPower(host, args, session)
1876 if 'Chassis Power State: On' in powerstate:
1877 return("Aborting firmware update. Host is powered on. Please turn off the host and try again.")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001878
Justin Thaler22b1bb52018-03-15 13:31:32 -05001879 #determine the existing images on the bmc
Justin Thalere412dc22018-01-12 16:28:24 -06001880 httpHeader = {'Content-Type':'application/json'}
1881 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1882 try:
1883 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1884 except(requests.exceptions.Timeout):
1885 return connectionErrHandler(args.json, "Timeout", None)
1886 except(requests.exceptions.ConnectionError) as err:
1887 return connectionErrHandler(args.json, "ConnectionError", err)
1888 oldsoftware = json.loads(resp.text)['data']
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001889
Justin Thaler22b1bb52018-03-15 13:31:32 -05001890 #Extract the tar and get information from the manifest file
1891 newversionID = extractFWimage(args.fileloc, purp)
1892 if newversionID == "":
1893 return "Unable to verify FW image."
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001894
1895
Justin Thaler22b1bb52018-03-15 13:31:32 -05001896 #check if the new image is already on the bmc
1897 if newversionID not in getAllFWImageIDs(oldsoftware):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001898
Justin Thaler22b1bb52018-03-15 13:31:32 -05001899 #upload the file
1900 httpHeader = {'Content-Type':'application/octet-stream'}
1901 url="https://"+host+"/upload/image"
1902 data=open(args.fileloc,'rb').read()
1903 print("Uploading file to BMC")
Justin Thalere412dc22018-01-12 16:28:24 -06001904 try:
Justin Thaler22b1bb52018-03-15 13:31:32 -05001905 resp = session.post(url, headers=httpHeader, data=data, verify=False)
Justin Thalere412dc22018-01-12 16:28:24 -06001906 except(requests.exceptions.Timeout):
1907 return connectionErrHandler(args.json, "Timeout", None)
1908 except(requests.exceptions.ConnectionError) as err:
1909 return connectionErrHandler(args.json, "ConnectionError", err)
Justin Thaler22b1bb52018-03-15 13:31:32 -05001910 if resp.status_code != 200:
1911 return "Failed to upload the file to the bmc"
Justin Thalere412dc22018-01-12 16:28:24 -06001912 else:
Justin Thaler22b1bb52018-03-15 13:31:32 -05001913 print("Upload complete.")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001914
Justin Thaler22b1bb52018-03-15 13:31:32 -05001915 #verify bmc processed the image
1916 software ={}
1917 for i in range(0, 5):
1918 httpHeader = {'Content-Type':'application/json'}
1919 url="https://"+host+"/xyz/openbmc_project/software/enumerate"
1920 try:
1921 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1922 except(requests.exceptions.Timeout):
1923 return connectionErrHandler(args.json, "Timeout", None)
1924 except(requests.exceptions.ConnectionError) as err:
1925 return connectionErrHandler(args.json, "ConnectionError", err)
1926 software = json.loads(resp.text)['data']
1927 #check if bmc is done processing the new image
1928 if (newversionID in getAllFWImageIDs(software)):
Justin Thalere412dc22018-01-12 16:28:24 -06001929 break
Justin Thaler22b1bb52018-03-15 13:31:32 -05001930 else:
1931 time.sleep(15)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001932
Justin Thaler22b1bb52018-03-15 13:31:32 -05001933 #activate the new image
1934 print("Activating new image: "+newversionID)
1935 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID + "/attr/RequestedActivation"
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001936 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}'
Justin Thaler22b1bb52018-03-15 13:31:32 -05001937 try:
1938 resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
1939 except(requests.exceptions.Timeout):
1940 return connectionErrHandler(args.json, "Timeout", None)
1941 except(requests.exceptions.ConnectionError) as err:
1942 return connectionErrHandler(args.json, "ConnectionError", err)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001943
Justin Thaler22b1bb52018-03-15 13:31:32 -05001944 #wait for the activation to complete, timeout after ~1 hour
1945 i=0
1946 while i < 360:
1947 url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001948 data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}'
Justin Thaler22b1bb52018-03-15 13:31:32 -05001949 try:
1950 resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
1951 except(requests.exceptions.Timeout):
1952 return connectionErrHandler(args.json, "Timeout", None)
1953 except(requests.exceptions.ConnectionError) as err:
1954 return connectionErrHandler(args.json, "ConnectionError", err)
1955 fwInfo = json.loads(resp.text)['data']
1956 if 'Activating' not in fwInfo['Activation'] and 'Activating' not in fwInfo['RequestedActivation']:
1957 print('')
1958 break
1959 else:
1960 sys.stdout.write('.')
1961 sys.stdout.flush()
1962 time.sleep(10) #check every 10 seconds
1963 return "Firmware flash and activation completed. Please reboot the bmc and then boot the host OS for the changes to take effect. "
1964 else:
1965 print("This image has been found on the bmc. Activating image: " + newversionID)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001966
Justin Thaler22b1bb52018-03-15 13:31:32 -05001967 d['imageID'] = newversionID
1968 return activateFWImage(host, args, session)
Justin Thalere412dc22018-01-12 16:28:24 -06001969
Justin Thaler3d71d402018-07-24 14:35:39 -05001970def getFWInventoryAttributes(rawFWInvItem, ID):
1971 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001972 gets and lists all of the firmware in the system.
1973
Justin Thaler3d71d402018-07-24 14:35:39 -05001974 @return: returns a dictionary containing the image attributes
1975 """
1976 reqActivation = rawFWInvItem["RequestedActivation"].split('.')[-1]
1977 pendingActivation = ""
1978 if reqActivation == "None":
1979 pendingActivation = "No"
1980 else:
1981 pendingActivation = "Yes"
1982 firmwareAttr = {ID: {
1983 "Purpose": rawFWInvItem["Purpose"].split('.')[-1],
1984 "Version": rawFWInvItem["Version"],
1985 "RequestedActivation": pendingActivation,
1986 "ID": ID}}
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001987
Justin Thaler3d71d402018-07-24 14:35:39 -05001988 if "ExtendedVersion" in rawFWInvItem:
1989 firmwareAttr[ID]['ExtendedVersion'] = rawFWInvItem['ExtendedVersion'].split(',')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001990 else:
Justin Thaler3d71d402018-07-24 14:35:39 -05001991 firmwareAttr[ID]['ExtendedVersion'] = ""
1992 return firmwareAttr
1993
1994def parseFWdata(firmwareDict):
1995 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06001996 creates a dictionary with parsed firmware data
1997
Justin Thaler3d71d402018-07-24 14:35:39 -05001998 @return: returns a dictionary containing the image attributes
1999 """
2000 firmwareInfoDict = {"Functional": {}, "Activated":{}, "NeedsActivated":{}}
2001 for key in firmwareDict['data']:
2002 #check for valid endpoint
2003 if "Purpose" in firmwareDict['data'][key]:
2004 id = key.split('/')[-1]
2005 if firmwareDict['data'][key]['Activation'].split('.')[-1] == "Active":
2006 fwActivated = True
2007 else:
2008 fwActivated = False
2009 if firmwareDict['data'][key]['Priority'] == 0:
2010 firmwareInfoDict['Functional'].update(getFWInventoryAttributes(firmwareDict['data'][key], id))
2011 elif firmwareDict['data'][key]['Priority'] >= 0 and fwActivated:
2012 firmwareInfoDict['Activated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id))
2013 else:
2014 firmwareInfoDict['Activated'].update(getFWInventoryAttributes(firmwareDict['data'][key], id))
2015 emptySections = []
2016 for key in firmwareInfoDict:
2017 if len(firmwareInfoDict[key])<=0:
2018 emptySections.append(key)
2019 for key in emptySections:
2020 del firmwareInfoDict[key]
2021 return firmwareInfoDict
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002022
Justin Thaler3d71d402018-07-24 14:35:39 -05002023def displayFWInvenory(firmwareInfoDict, args):
2024 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002025 gets and lists all of the firmware in the system.
2026
Justin Thaler3d71d402018-07-24 14:35:39 -05002027 @return: returns a string containing all of the firmware information
2028 """
2029 output = ""
2030 if not args.json:
2031 for key in firmwareInfoDict:
2032 for subkey in firmwareInfoDict[key]:
2033 firmwareInfoDict[key][subkey]['ExtendedVersion'] = str(firmwareInfoDict[key][subkey]['ExtendedVersion'])
2034 if not args.verbose:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002035 output = "---Running Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002036 colNames = ["Purpose", "Version", "ID"]
2037 keylist = ["Purpose", "Version", "ID"]
2038 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"])
2039 if "Activated" in firmwareInfoDict:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002040 output += "\n---Available Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002041 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"])
2042 if "NeedsActivated" in firmwareInfoDict:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002043 output += "\n---Needs Activated Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002044 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"])
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002045
Justin Thaler3d71d402018-07-24 14:35:39 -05002046 else:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002047 output = "---Running Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002048 colNames = ["Purpose", "Version", "ID", "Pending Activation", "Extended Version"]
2049 keylist = ["Purpose", "Version", "ID", "RequestedActivation", "ExtendedVersion"]
2050 output += tableDisplay(keylist, colNames, firmwareInfoDict["Functional"])
2051 if "Activated" in firmwareInfoDict:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002052 output += "\n---Available Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002053 output += tableDisplay(keylist, colNames, firmwareInfoDict["Activated"])
2054 if "NeedsActivated" in firmwareInfoDict:
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002055 output += "\n---Needs Activated Images---\n"
Justin Thaler3d71d402018-07-24 14:35:39 -05002056 output += tableDisplay(keylist, colNames, firmwareInfoDict["NeedsActivated"])
2057 return output
2058 else:
2059 return str(json.dumps(firmwareInfoDict, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
2060
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002061def firmwareList(host, args, session):
Justin Thaler3d71d402018-07-24 14:35:39 -05002062 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002063 gets and lists all of the firmware in the system.
2064
Justin Thaler3d71d402018-07-24 14:35:39 -05002065 @return: returns a string containing all of the firmware information
2066 """
2067 httpHeader = {'Content-Type':'application/json'}
2068 url="https://{hostname}/xyz/openbmc_project/software/enumerate".format(hostname=host)
2069 try:
2070 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
2071 except(requests.exceptions.Timeout):
2072 return(connectionErrHandler(args.json, "Timeout", None))
2073 firmwareDict = json.loads(res.text)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002074
Justin Thaler3d71d402018-07-24 14:35:39 -05002075 #sort the received information
2076 firmwareInfoDict = parseFWdata(firmwareDict)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002077
Justin Thaler3d71d402018-07-24 14:35:39 -05002078 #display the information
2079 return displayFWInvenory(firmwareInfoDict, args)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002080
2081
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002082def restLogging(host, args, session):
2083 """
2084 Called by the logging function. Turns REST API logging on/off.
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002085
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002086 @param host: string, the hostname or IP address of the bmc
2087 @param args: contains additional arguments used by the logging sub command
2088 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002089 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002090 """
2091
2092 url="https://"+host+"/xyz/openbmc_project/logging/rest_api_logs/attr/Enabled"
2093 httpHeader = {'Content-Type':'application/json'}
2094
2095 if(args.rest_logging == 'on'):
2096 data = '{"data": 1}'
2097 elif(args.rest_logging == 'off'):
2098 data = '{"data": 0}'
2099 else:
2100 return "Invalid logging rest_api command"
2101
2102 try:
2103 res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
2104 except(requests.exceptions.Timeout):
2105 return(connectionErrHandler(args.json, "Timeout", None))
2106 return res.text
2107
2108
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002109def remoteLogging(host, args, session):
2110 """
2111 Called by the logging function. View config information for/disable remote logging (rsyslog).
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002112
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002113 @param host: string, the hostname or IP address of the bmc
2114 @param args: contains additional arguments used by the logging sub command
2115 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002116 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002117 """
2118
2119 url="https://"+host+"/xyz/openbmc_project/logging/config/remote"
2120 httpHeader = {'Content-Type':'application/json'}
2121
2122 try:
2123 if(args.remote_logging == 'view'):
2124 res = session.get(url, headers=httpHeader, verify=False, timeout=30)
2125 elif(args.remote_logging == 'disable'):
2126 res = session.put(url + '/attr/Port', headers=httpHeader, json = {"data": 0}, verify=False, timeout=30)
2127 res = session.put(url + '/attr/Address', headers=httpHeader, json = {"data": ""}, verify=False, timeout=30)
2128 else:
2129 return "Invalid logging remote_logging command"
2130 except(requests.exceptions.Timeout):
2131 return(connectionErrHandler(args.json, "Timeout", None))
2132 return res.text
2133
2134
2135def remoteLoggingConfig(host, args, session):
2136 """
2137 Called by the logging function. Configures remote logging (rsyslog).
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002138
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002139 @param host: string, the hostname or IP address of the bmc
2140 @param args: contains additional arguments used by the logging sub command
2141 @param session: the active session to use
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002142 @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002143 """
2144
2145 url="https://"+host+"/xyz/openbmc_project/logging/config/remote"
2146 httpHeader = {'Content-Type':'application/json'}
2147
2148 try:
2149 res = session.put(url + '/attr/Port', headers=httpHeader, json = {"data": args.port}, verify=False, timeout=30)
2150 res = session.put(url + '/attr/Address', headers=httpHeader, json = {"data": args.address}, verify=False, timeout=30)
2151 except(requests.exceptions.Timeout):
2152 return(connectionErrHandler(args.json, "Timeout", None))
2153 return res.text
2154
Ratan Gupta9166cd22018-10-01 18:09:40 +05302155
Dhruvaraj Subhashchandran64e7f6f2018-10-02 03:42:14 -05002156def certificateUpdate(host, args, session):
2157 """
2158 Called by certificate management function. update server/client/authority certificates
2159 Example:
2160 certificate update server https -f cert.pem
2161 certificate update authority ldap -f Root-CA.pem
2162 certificate update client ldap -f cert.pem
2163 @param host: string, the hostname or IP address of the bmc
2164 @param args: contains additional arguments used by the certificate update sub command
2165 @param session: the active session to use
2166 """
2167
2168 httpHeader = {'Content-Type': 'application/octet-stream'}
2169 url = "https://" + host + "/xyz/openbmc_project/certs/" + args.type.lower() + "/" + args.service.lower()
2170 data = open(args.fileloc, 'rb').read()
2171 print("Updating certificate url=" + url)
2172 try:
2173 resp = session.put(url, headers=httpHeader, data=data, verify=False)
2174 except(requests.exceptions.Timeout):
2175 return(connectionErrHandler(args.json, "Timeout", None))
2176 except(requests.exceptions.ConnectionError) as err:
2177 return connectionErrHandler(args.json, "ConnectionError", err)
2178 if resp.status_code != 200:
2179 print(resp.text)
2180 return "Failed to update the certificate"
2181 else:
2182 print("Update complete.")
2183
2184
2185def certificateDelete(host, args, session):
2186 """
2187 Called by certificate management function to delete certificate
2188 Example:
2189 certificate delete server https
2190 certificate delete authority ldap
2191 certificate delete client ldap
2192 @param host: string, the hostname or IP address of the bmc
2193 @param args: contains additional arguments used by the certificate delete sub command
2194 @param session: the active session to use
2195 """
2196
2197 httpHeader = {'Content-Type': 'multipart/form-data'}
2198 url = "https://" + host + "/xyz/openbmc_project/certs/" + args.type.lower() + "/" + args.service.lower()
2199 print("Deleting certificate url=" + url)
2200 try:
2201 resp = session.delete(url, headers=httpHeader)
2202 except(requests.exceptions.Timeout):
2203 return(connectionErrHandler(args.json, "Timeout", None))
2204 except(requests.exceptions.ConnectionError) as err:
2205 return connectionErrHandler(args.json, "ConnectionError", err)
2206 if resp.status_code != 200:
2207 print(resp.text)
2208 return "Failed to delete the certificate"
2209 else:
2210 print("Delete complete.")
2211
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002212
Ratan Gupta9166cd22018-10-01 18:09:40 +05302213def enableLDAP(host, args, session):
2214 """
2215 Called by the ldap function. Configures LDAP.
2216
2217 @param host: string, the hostname or IP address of the bmc
2218 @param args: contains additional arguments used by the ldap subcommand
2219 @param session: the active session to use
2220 @param args.json: boolean, if this flag is set to true, the output will
2221 be provided in json format for programmatic consumption
2222 """
2223
2224 url='https://'+host+'/xyz/openbmc_project/user/ldap/action/CreateConfig'
2225 httpHeader = {'Content-Type':'application/json'}
2226 scope = {
2227 'sub' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.sub',
2228 'one' : 'xyz.openbmc_project.User.Ldap.Create.SearchScope.one',
2229 'base': 'xyz.openbmc_project.User.Ldap.Create.SearchScope.base'
2230 }
2231
2232 serverType = {
2233 'ActiveDirectory' : 'xyz.openbmc_project.User.Ldap.Create.Type.ActiveDirectory',
2234 'OpenLDAP' : 'xyz.openbmc_project.User.Ldap.Create.Type.OpenLdap'
2235 }
2236
2237 data = {"data": [args.uri, args.bindDN, args.baseDN, args.bindPassword, scope[args.scope], serverType[args.serverType]]}
2238
2239 try:
2240 res = session.post(url, headers=httpHeader, json=data, verify=False, timeout=30)
2241 except(requests.exceptions.Timeout):
2242 return(connectionErrHandler(args.json, "Timeout", None))
2243 except(requests.exceptions.ConnectionError) as err:
2244 return connectionErrHandler(args.json, "ConnectionError", err)
2245
2246 return res.text
2247
2248
2249def disableLDAP(host, args, session):
2250 """
2251 Called by the ldap function. Deletes the LDAP Configuration.
2252
2253 @param host: string, the hostname or IP address of the bmc
2254 @param args: contains additional arguments used by the ldap subcommand
2255 @param session: the active session to use
2256 @param args.json: boolean, if this flag is set to true, the output
2257 will be provided in json format for programmatic consumption
2258 """
2259
2260 url='https://'+host+'/xyz/openbmc_project/user/ldap/config/action/delete'
2261 httpHeader = {'Content-Type':'application/json'}
2262 data = {"data": []}
2263
2264 try:
2265 res = session.post(url, headers=httpHeader, json=data, verify=False, timeout=30)
2266 except(requests.exceptions.Timeout):
2267 return(connectionErrHandler(args.json, "Timeout", None))
2268 except(requests.exceptions.ConnectionError) as err:
2269 return connectionErrHandler(args.json, "ConnectionError", err)
2270
2271 return res.text
2272
Ratan Guptafeee6372018-10-17 23:25:51 +05302273def createPrivilegeMapping(host, args, session):
2274 """
2275 Called by the ldap function. Creates the group and the privilege mapping.
2276
2277 @param host: string, the hostname or IP address of the bmc
2278 @param args: contains additional arguments used by the ldap subcommand
2279 @param session: the active session to use
2280 @param args.json: boolean, if this flag is set to true, the output
2281 will be provided in json format for programmatic consumption
2282 """
2283
2284 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/action/Create'
2285 httpHeader = {'Content-Type':'application/json'}
2286
2287 data = {"data": [args.groupName,args.privilege]}
2288
2289 try:
2290 res = session.post(url, headers=httpHeader, json = data, verify=False, timeout=30)
2291 except(requests.exceptions.Timeout):
2292 return(connectionErrHandler(args.json, "Timeout", None))
2293 except(requests.exceptions.ConnectionError) as err:
2294 return connectionErrHandler(args.json, "ConnectionError", err)
2295 return res.text
2296
2297def listPrivilegeMapping(host, args, session):
2298 """
2299 Called by the ldap function. Lists the group and the privilege mapping.
2300
2301 @param host: string, the hostname or IP address of the bmc
2302 @param args: contains additional arguments used by the ldap subcommand
2303 @param session: the active session to use
2304 @param args.json: boolean, if this flag is set to true, the output
2305 will be provided in json format for programmatic consumption
2306 """
2307 url = 'https://'+host+'/xyz/openbmc_project/user/ldap/enumerate'
2308 httpHeader = {'Content-Type': 'application/json'}
2309 data = {"data": []}
2310
2311 try:
2312 res = session.get(url, headers=httpHeader, json = data, verify=False, timeout=30)
2313 except(requests.exceptions.Timeout):
2314 return(connectionErrHandler(args.json, "Timeout", None))
2315 except(requests.exceptions.ConnectionError) as err:
2316 return connectionErrHandler(args.json, "ConnectionError", err)
2317 return res.text
2318
2319def deletePrivilegeMapping(host, args, session):
2320 """
2321 Called by the ldap function. Deletes the mapping associated with the group.
2322
2323 @param host: string, the hostname or IP address of the bmc
2324 @param args: contains additional arguments used by the ldap subcommand
2325 @param session: the active session to use
2326 @param args.json: boolean, if this flag is set to true, the output
2327 will be provided in json format for programmatic consumption
2328 """
2329 (ldapNameSpaceObjects) = listPrivilegeMapping(host, args, session)
2330 ldapNameSpaceObjects = json.loads(ldapNameSpaceObjects)["data"]
2331 path = ''
2332
2333 # not interested in the config objet
2334 ldapNameSpaceObjects.pop('/xyz/openbmc_project/user/ldap/config', None)
2335
2336 # search for the object having the mapping for the given group
2337 for key,value in ldapNameSpaceObjects.items():
2338 if value['GroupName'] == args.groupName:
2339 path = key
2340 break
2341
2342 if path == '':
2343 return "No privilege mapping found for this group."
2344
2345 # delete the object
2346 url = 'https://'+host+path+'/action/delete'
2347 httpHeader = {'Content-Type': 'application/json'}
2348 data = {"data": []}
2349
2350 try:
2351 res = session.post(url, headers=httpHeader, json = data, verify=False, timeout=30)
2352 except(requests.exceptions.Timeout):
2353 return(connectionErrHandler(args.json, "Timeout", None))
2354 except(requests.exceptions.ConnectionError) as err:
2355 return connectionErrHandler(args.json, "ConnectionError", err)
2356 return res.text
Ratan Gupta9166cd22018-10-01 18:09:40 +05302357
Nagaraju Goruganti7d1fe172018-11-13 06:09:29 -06002358def viewLDAPConfig(host, args, session):
2359 """
2360 Called by the ldap function. Prints out LDAP's configured properties
2361
2362 @param host: string, the hostname or IP address of the bmc
2363 @param args: contains additional arguments used by the ldap subcommand
2364 args.json: boolean, if this flag is set to true, the output
2365 will be provided in json format for programmatic consumption
2366 @param session: the active session to use
2367 @return returns LDAP's configured properties.
2368 """
2369 url = "https://"+host+"/xyz/openbmc_project/user/ldap/config"
2370 httpHeader = {'Content-Type': 'application/json'}
2371 try:
2372 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
2373 except(requests.exceptions.Timeout):
2374 return(connectionErrHandler(args.json, "Timeout", None))
2375 except(requests.exceptions.ConnectionError) as err:
2376 return connectionErrHandler(args.json, "ConnectionError", err)
2377 except(requests.exceptions.RequestException) as err:
2378 return connectionErrHandler(args.json, "RequestException", err)
2379 if res.status_code == 404:
2380 return "LDAP server config has not been created"
2381 return res.text
2382
2383
Matt Spinler7d426c22018-09-24 14:42:07 -05002384def localUsers(host, args, session):
2385 """
2386 Enables and disables local BMC users.
2387
2388 @param host: string, the hostname or IP address of the bmc
2389 @param args: contains additional arguments used by the logging sub command
2390 @param session: the active session to use
2391 """
2392
2393 httpHeader = {'Content-Type':'application/json'}
2394 url="https://{hostname}/xyz/openbmc_project/user/enumerate".format(hostname=host)
2395 try:
2396 res = session.get(url, headers=httpHeader, verify=False, timeout=40)
2397 except(requests.exceptions.Timeout):
2398 return(connectionErrHandler(args.json, "Timeout", None))
2399 usersDict = json.loads(res.text)
2400
2401 if not usersDict['data']:
2402 return "No users found"
2403
2404 output = ""
2405 for user in usersDict['data']:
Matt Spinler015adc22018-10-23 14:30:19 -05002406
2407 # Skip LDAP and another non-local users
2408 if 'UserEnabled' not in usersDict['data'][user]:
2409 continue
2410
Matt Spinler7d426c22018-09-24 14:42:07 -05002411 name = user.split('/')[-1]
2412 url = "https://{hostname}{user}/attr/UserEnabled".format(hostname=host, user=user)
2413
2414 if args.local_users == "queryenabled":
2415 try:
2416 res = session.get(url, headers=httpHeader,verify=False, timeout=30)
2417 except(requests.exceptions.Timeout):
2418 return(connectionErrHandler(args.json, "Timeout", None))
2419
2420 result = json.loads(res.text)
2421 output += ("User: {name} Enabled: {result}\n").format(name=name, result=result['data'])
2422
2423 elif args.local_users in ["enableall", "disableall"]:
2424 action = ""
2425 if args.local_users == "enableall":
2426 data = '{"data": true}'
2427 action = "Enabling"
2428 else:
2429 data = '{"data": false}'
2430 action = "Disabling"
2431
2432 output += "{action} {name}\n".format(action=action, name=name)
2433
2434 try:
2435 resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
2436 except(requests.exceptions.Timeout):
2437 return connectionErrHandler(args.json, "Timeout", None)
2438 except(requests.exceptions.ConnectionError) as err:
2439 return connectionErrHandler(args.json, "ConnectionError", err)
2440 else:
2441 return "Invalid local users argument"
2442
2443 return output
2444
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002445def createCommandParser():
Justin Thalere412dc22018-01-12 16:28:24 -06002446 """
2447 creates the parser for the command line along with help for each command and subcommand
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002448
Justin Thalere412dc22018-01-12 16:28:24 -06002449 @return: returns the parser for the command line
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002450 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002451 parser = argparse.ArgumentParser(description='Process arguments')
Justin Thalere412dc22018-01-12 16:28:24 -06002452 parser.add_argument("-H", "--host", help='A hostname or IP for the BMC')
2453 parser.add_argument("-U", "--user", help='The username to login with')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002454 group = parser.add_mutually_exclusive_group()
2455 group.add_argument("-A", "--askpw", action='store_true', help='prompt for password')
2456 group.add_argument("-P", "--PW", help='Provide the password in-line')
2457 parser.add_argument('-j', '--json', action='store_true', help='output json data only')
2458 parser.add_argument('-t', '--policyTableLoc', help='The location of the policy table to parse alerts')
2459 parser.add_argument('-c', '--CerFormat', action='store_true', help=argparse.SUPPRESS)
2460 parser.add_argument('-T', '--procTime', action='store_true', help= argparse.SUPPRESS)
Justin Thalere412dc22018-01-12 16:28:24 -06002461 parser.add_argument('-V', '--version', action='store_true', help='Display the version number of the openbmctool')
2462 subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002463
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002464 #fru command
2465 parser_inv = subparsers.add_parser("fru", help='Work with platform inventory')
Justin Thalere412dc22018-01-12 16:28:24 -06002466 inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions", dest='command')
Justin Thaler53bf2f12018-07-16 14:05:32 -05002467 inv_subparser.required = True
2468 #fru print
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002469 inv_print = inv_subparser.add_parser("print", help="prints out a list of all FRUs")
2470 inv_print.set_defaults(func=fruPrint)
2471 #fru list [0....n]
2472 inv_list = inv_subparser.add_parser("list", help="print out details on selected FRUs. Specifying no items will list the entire inventory")
2473 inv_list.add_argument('items', nargs='?', help="print out details on selected FRUs. Specifying no items will list the entire inventory")
2474 inv_list.set_defaults(func=fruList)
2475 #fru status
2476 inv_status = inv_subparser.add_parser("status", help="prints out the status of all FRUs")
Justin Thalere412dc22018-01-12 16:28:24 -06002477 inv_status.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002478 inv_status.set_defaults(func=fruStatus)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002479
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002480 #sensors command
2481 parser_sens = subparsers.add_parser("sensors", help="Work with platform sensors")
Justin Thalere412dc22018-01-12 16:28:24 -06002482 sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions', dest='command')
Justin Thaler53bf2f12018-07-16 14:05:32 -05002483 sens_subparser.required = True
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002484 #sensor print
2485 sens_print= sens_subparser.add_parser('print', help="prints out a list of all Sensors.")
2486 sens_print.set_defaults(func=sensor)
2487 #sensor list[0...n]
2488 sens_list=sens_subparser.add_parser("list", help="Lists all Sensors in the platform. Specify a sensor for full details. ")
2489 sens_list.add_argument("sensNum", nargs='?', help="The Sensor number to get full details on" )
2490 sens_list.set_defaults(func=sensor)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002491
2492
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002493 #sel command
2494 parser_sel = subparsers.add_parser("sel", help="Work with platform alerts")
Justin Thalere412dc22018-01-12 16:28:24 -06002495 sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions', dest='command')
Justin Thaler53bf2f12018-07-16 14:05:32 -05002496 sel_subparser.required = True
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002497 #sel print
2498 sel_print = sel_subparser.add_parser("print", help="prints out a list of all sels in a condensed list")
2499 sel_print.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS)
2500 sel_print.add_argument('-v', '--verbose', action='store_true', help="Changes the output to being very verbose")
2501 sel_print.add_argument('-f', '--fileloc', help='Parse a file instead of the BMC output')
2502 sel_print.set_defaults(func=selPrint)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002503
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002504 #sel list
2505 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")
2506 sel_list.add_argument("selNum", nargs='?', type=int, help="The SEL entry to get details on")
2507 sel_list.set_defaults(func=selList)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002508
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002509 sel_get = sel_subparser.add_parser("get", help="Gets the verbose details of a specified SEL entry")
2510 sel_get.add_argument('selNum', type=int, help="the number of the SEL entry to get")
2511 sel_get.set_defaults(func=selList)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002512
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002513 sel_clear = sel_subparser.add_parser("clear", help="Clears all entries from the SEL")
2514 sel_clear.set_defaults(func=selClear)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002515
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002516 sel_setResolved = sel_subparser.add_parser("resolve", help="Sets the sel entry to resolved")
Justin Thalere412dc22018-01-12 16:28:24 -06002517 sel_setResolved.add_argument('-n', '--selNum', type=int, help="the number of the SEL entry to resolve")
2518 sel_ResolveAll_sub = sel_setResolved.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
2519 sel_ResolveAll = sel_ResolveAll_sub.add_parser('all', help='Resolve all SEL entries')
2520 sel_ResolveAll.set_defaults(func=selResolveAll)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002521 sel_setResolved.set_defaults(func=selSetResolved)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002522
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002523 parser_chassis = subparsers.add_parser("chassis", help="Work with chassis power and status")
Justin Thalere412dc22018-01-12 16:28:24 -06002524 chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002525
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002526 parser_chassis.add_argument('status', action='store_true', help='Returns the current status of the platform')
2527 parser_chassis.set_defaults(func=chassis)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002528
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002529 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 -06002530 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 -06002531 parser_chasPower.set_defaults(func=chassisPower)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002532
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002533 #control the chassis identify led
2534 parser_chasIdent = chas_sub.add_parser("identify", help="Control the chassis identify led")
2535 parser_chasIdent.add_argument('identcmd', choices=['on', 'off', 'status'], help='The control option for the led: on, off, blink, status')
2536 parser_chasIdent.set_defaults(func=chassisIdent)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002537
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002538 #collect service data
2539 parser_servData = subparsers.add_parser("collect_service_data", help="Collect all bmc data needed for service")
2540 parser_servData.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS)
2541 parser_servData.set_defaults(func=collectServiceData)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002542
Justin Thalere412dc22018-01-12 16:28:24 -06002543 #system quick health check
2544 parser_healthChk = subparsers.add_parser("health_check", help="Work with platform sensors")
2545 parser_healthChk.set_defaults(func=healthCheck)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002546
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002547 #work with bmc dumps
2548 parser_bmcdump = subparsers.add_parser("dump", help="Work with bmc dump files")
Justin Thalere412dc22018-01-12 16:28:24 -06002549 bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thaler53bf2f12018-07-16 14:05:32 -05002550 bmcDump_sub.required = True
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002551 dump_Create = bmcDump_sub.add_parser('create', help="Create a bmc dump")
2552 dump_Create.set_defaults(func=bmcDumpCreate)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002553
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002554 dump_list = bmcDump_sub.add_parser('list', help="list all bmc dump files")
2555 dump_list.set_defaults(func=bmcDumpList)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002556
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002557 parserdumpdelete = bmcDump_sub.add_parser('delete', help="Delete bmc dump files")
2558 parserdumpdelete.add_argument("-n", "--dumpNum", nargs='*', type=int, help="The Dump entry to delete")
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002559 parserdumpdelete.set_defaults(func=bmcDumpDelete)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002560
Justin Thalere412dc22018-01-12 16:28:24 -06002561 bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002562 deleteAllDumps = bmcDumpDelsub.add_parser('all', help='Delete all bmc dump files')
2563 deleteAllDumps.set_defaults(func=bmcDumpDeleteAll)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002564
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002565 parser_dumpretrieve = bmcDump_sub.add_parser('retrieve', help='Retrieve a dump file')
2566 parser_dumpretrieve.add_argument("dumpNum", type=int, help="The Dump entry to delete")
2567 parser_dumpretrieve.add_argument("-s", "--dumpSaveLoc", help="The location to save the bmc dump file")
2568 parser_dumpretrieve.set_defaults(func=bmcDumpRetrieve)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002569
Justin Thaler22b1bb52018-03-15 13:31:32 -05002570 #bmc command for reseting the bmc
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002571 parser_bmc = subparsers.add_parser('bmc', help="Work with the bmc")
Justin Thalere412dc22018-01-12 16:28:24 -06002572 bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002573 parser_BMCReset = bmc_sub.add_parser('reset', help='Reset the bmc' )
2574 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 -06002575 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.")
2576 parser_bmc.set_defaults(func=bmc)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002577
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002578 #add alias to the bmc command
2579 parser_mc = subparsers.add_parser('mc', help="Work with the management controller")
Justin Thalere412dc22018-01-12 16:28:24 -06002580 mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002581 parser_MCReset = mc_sub.add_parser('reset', help='Reset the bmc' )
2582 parser_MCReset.add_argument('type', choices=['warm','cold'], help="Reboot the BMC")
2583 #parser_MCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration")
2584 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 -06002585 parser_MCReset.set_defaults(func=bmcReset)
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002586 parser_mc.set_defaults(func=bmc)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002587
Justin Thalere412dc22018-01-12 16:28:24 -06002588 #gard clear
2589 parser_gc = subparsers.add_parser("gardclear", help="Used to clear gard records")
2590 parser_gc.set_defaults(func=gardClear)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002591
Justin Thalere412dc22018-01-12 16:28:24 -06002592 #firmware_flash
2593 parser_fw = subparsers.add_parser("firmware", help="Work with the system firmware")
2594 fwflash_subproc = parser_fw.add_subparsers(title='subcommands', description='valid firmware commands', help='sub-command help', dest='command')
Justin Thaler53bf2f12018-07-16 14:05:32 -05002595 fwflash_subproc.required = True
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002596
Justin Thalere412dc22018-01-12 16:28:24 -06002597 fwflash = fwflash_subproc.add_parser('flash', help="Flash the system firmware")
2598 fwflash.add_argument('type', choices=['bmc', 'pnor'], help="image type to flash")
2599 fwflash.add_argument('-f', '--fileloc', required=True, help="The absolute path to the firmware image")
2600 fwflash.set_defaults(func=fwFlash)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002601
Justin Thaler22b1bb52018-03-15 13:31:32 -05002602 fwActivate = fwflash_subproc.add_parser('activate', help="Activate existing image on the bmc")
Justin Thalere412dc22018-01-12 16:28:24 -06002603 fwActivate.add_argument('imageID', help="The image ID to activate from the firmware list. Ex: 63c95399")
2604 fwActivate.set_defaults(func=activateFWImage)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002605
Justin Thaler22b1bb52018-03-15 13:31:32 -05002606 fwActivateStatus = fwflash_subproc.add_parser('activation_status', help="Check Status of activations")
2607 fwActivateStatus.set_defaults(func=activateStatus)
2608
Justin Thaler3d71d402018-07-24 14:35:39 -05002609 fwList = fwflash_subproc.add_parser('list', help="List all of the installed firmware")
2610 fwList.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
2611 fwList.set_defaults(func=firmwareList)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002612
Justin Thaler3d71d402018-07-24 14:35:39 -05002613 fwprint = fwflash_subproc.add_parser('print', help="List all of the installed firmware")
2614 fwprint.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
2615 fwprint.set_defaults(func=firmwareList)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002616
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002617 #logging
2618 parser_logging = subparsers.add_parser("logging", help="logging controls")
2619 logging_sub = parser_logging.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002620
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002621 #turn rest api logging on/off
2622 parser_rest_logging = logging_sub.add_parser("rest_api", help="turn rest api logging on/off")
2623 parser_rest_logging.add_argument('rest_logging', choices=['on', 'off'], help='The control option for rest logging: on, off')
2624 parser_rest_logging.set_defaults(func=restLogging)
Deepak Kodihalli02d53282018-09-18 06:53:31 -05002625
2626 #remote logging
2627 parser_remote_logging = logging_sub.add_parser("remote_logging", help="Remote logging (rsyslog) commands")
2628 parser_remote_logging.add_argument('remote_logging', choices=['view', 'disable'], help='Remote logging (rsyslog) commands')
2629 parser_remote_logging.set_defaults(func=remoteLogging)
2630
2631 #configure remote logging
2632 parser_remote_logging_config = logging_sub.add_parser("remote_logging_config", help="Configure remote logging (rsyslog)")
2633 parser_remote_logging_config.add_argument("-a", "--address", required=True, help="Set IP address of rsyslog server")
2634 parser_remote_logging_config.add_argument("-p", "--port", required=True, type=int, help="Set Port of rsyslog server")
2635 parser_remote_logging_config.set_defaults(func=remoteLoggingConfig)
Dhruvaraj Subhashchandran64e7f6f2018-10-02 03:42:14 -05002636
2637 #certificate management
2638 parser_cert = subparsers.add_parser("certificate", help="Certificate management")
2639 certMgmt_subproc = parser_cert.add_subparsers(title='subcommands', description='valid certificate commands', help='sub-command help', dest='command')
2640
2641 certUpdate = certMgmt_subproc.add_parser('update', help="Update the certificate")
2642 certUpdate.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to update")
2643 certUpdate.add_argument('service', choices=['https', 'ldap'], help="Service to update")
2644 certUpdate.add_argument('-f', '--fileloc', required=True, help="The absolute path to the certificate file")
2645 certUpdate.set_defaults(func=certificateUpdate)
2646
2647 certDelete = certMgmt_subproc.add_parser('delete', help="Delete the certificate")
2648 certDelete.add_argument('type', choices=['server', 'client', 'authority'], help="certificate type to delete")
2649 certDelete.add_argument('service', choices=['https', 'ldap'], help="Service to delete the certificate")
2650 certDelete.set_defaults(func=certificateDelete)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002651
Matt Spinler7d426c22018-09-24 14:42:07 -05002652 # local users
2653 parser_users = subparsers.add_parser("local_users", help="Work with local users")
2654 parser_users.add_argument('local_users', choices=['disableall','enableall', 'queryenabled'], help="Disable, enable or query local user accounts")
2655 parser_users.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
2656 parser_users.set_defaults(func=localUsers)
2657
Ratan Gupta9166cd22018-10-01 18:09:40 +05302658 #LDAP
2659 parser_ldap = subparsers.add_parser("ldap", help="LDAP controls")
2660 ldap_sub = parser_ldap.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
2661
2662 #configure and enable LDAP
2663 parser_ldap_config = ldap_sub.add_parser("enable", help="Configure and enables the LDAP")
2664 parser_ldap_config.add_argument("-a", "--uri", required=True, help="Set LDAP server URI")
2665 parser_ldap_config.add_argument("-B", "--bindDN", required=True, help="Set the bind DN of the LDAP server")
2666 parser_ldap_config.add_argument("-b", "--baseDN", required=True, help="Set the base DN of the LDAP server")
2667 parser_ldap_config.add_argument("-p", "--bindPassword", required=True, help="Set the bind password of the LDAP server")
2668 parser_ldap_config.add_argument("-S", "--scope", choices=['sub','one', 'base'],
2669 help='Specifies the search scope:subtree, one level or base object.')
2670 parser_ldap_config.add_argument("-t", "--serverType", choices=['ActiveDirectory','OpenLDAP'],
2671 help='Specifies the configured server is ActiveDirectory(AD) or OpenLdap')
2672 parser_ldap_config.set_defaults(func=enableLDAP)
2673
2674 # disable LDAP
2675 parser_disable_ldap = ldap_sub.add_parser("disable", help="disables the LDAP")
2676 parser_disable_ldap.set_defaults(func=disableLDAP)
Nagaraju Goruganti7d1fe172018-11-13 06:09:29 -06002677 # view-config
2678 parser_ldap_config = \
2679 ldap_sub.add_parser("view-config", help="prints out a list of all \
2680 LDAPS's configured properties")
2681 parser_ldap_config.set_defaults(func=viewLDAPConfig)
Ratan Gupta9166cd22018-10-01 18:09:40 +05302682
Ratan Guptafeee6372018-10-17 23:25:51 +05302683 #create group privilege mapping
2684 parser_ldap_mapper = ldap_sub.add_parser("privilege-mapper", help="LDAP group privilege controls")
2685 parser_ldap_mapper_sub = parser_ldap_mapper.add_subparsers(title='subcommands', description='valid subcommands',
2686 help="sub-command help", dest='command')
2687
2688 parser_ldap_mapper_create = parser_ldap_mapper_sub.add_parser("create", help="Create mapping of ldap group and privilege")
2689 parser_ldap_mapper_create.add_argument("-g","--groupName",required=True,help="Group Name")
2690 parser_ldap_mapper_create.add_argument("-p","--privilege",choices=['priv-admin','priv-user'],required=True,help="Privilege")
2691 parser_ldap_mapper_create.set_defaults(func=createPrivilegeMapping)
2692
2693 #list group privilege mapping
2694 parser_ldap_mapper_list = parser_ldap_mapper_sub.add_parser("list",help="List privilege mapping")
2695 parser_ldap_mapper_list.set_defaults(func=listPrivilegeMapping)
2696
2697 #delete group privilege mapping
2698 parser_ldap_mapper_delete = parser_ldap_mapper_sub.add_parser("delete",help="Delete privilege mapping")
2699 parser_ldap_mapper_delete.add_argument("-g","--groupName",required=True,help="Group Name")
2700 parser_ldap_mapper_delete.set_defaults(func=deletePrivilegeMapping)
2701
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002702 return parser
2703
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002704def main(argv=None):
Justin Thalere412dc22018-01-12 16:28:24 -06002705 """
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002706 main function for running the command line utility as a sub application
2707 """
2708 global toolVersion
Matt Spinler7d426c22018-09-24 14:42:07 -05002709 toolVersion = "1.08"
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002710 parser = createCommandParser()
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002711 args = parser.parse_args(argv)
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002712
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002713 totTimeStart = int(round(time.time()*1000))
2714
2715 if(sys.version_info < (3,0)):
2716 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
2717 if sys.version_info >= (3,0):
2718 requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
Justin Thalere412dc22018-01-12 16:28:24 -06002719 if (args.version):
Justin Thaler22b1bb52018-03-15 13:31:32 -05002720 print("Version: "+ toolVersion)
Justin Thalere412dc22018-01-12 16:28:24 -06002721 sys.exit(0)
2722 if (hasattr(args, 'fileloc') and args.fileloc is not None and 'print' in args.command):
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002723 mysess = None
Justin Thalere412dc22018-01-12 16:28:24 -06002724 print(selPrint('N/A', args, mysess))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002725 else:
Justin Thalere412dc22018-01-12 16:28:24 -06002726 if(hasattr(args, 'host') and hasattr(args,'user')):
2727 if (args.askpw):
2728 pw = getpass.getpass()
2729 elif(args.PW is not None):
2730 pw = args.PW
2731 else:
2732 print("You must specify a password")
2733 sys.exit()
2734 logintimeStart = int(round(time.time()*1000))
2735 mysess = login(args.host, args.user, pw, args.json)
Justin Thalera9415b42018-05-25 19:40:13 -05002736 if(sys.version_info < (3,0)):
2737 if isinstance(mysess, basestring):
2738 print(mysess)
2739 sys.exit(1)
2740 elif sys.version_info >= (3,0):
2741 if isinstance(mysess, str):
2742 print(mysess)
2743 sys.exit(1)
Justin Thalere412dc22018-01-12 16:28:24 -06002744 logintimeStop = int(round(time.time()*1000))
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002745
2746 commandTimeStart = int(round(time.time()*1000))
Justin Thalere412dc22018-01-12 16:28:24 -06002747 output = args.func(args.host, args, mysess)
2748 commandTimeStop = int(round(time.time()*1000))
2749 print(output)
2750 if (mysess is not None):
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002751 logout(args.host, args.user, pw, mysess, args.json)
2752 if(args.procTime):
Justin Thalere412dc22018-01-12 16:28:24 -06002753 print("Total time: " + str(int(round(time.time()*1000))- totTimeStart))
2754 print("loginTime: " + str(logintimeStop - logintimeStart))
2755 print("command Time: " + str(commandTimeStop - commandTimeStart))
2756 else:
2757 print("usage: openbmctool.py [-h] -H HOST -U USER [-A | -P PW] [-j]\n" +
2758 "\t[-t POLICYTABLELOC] [-V]\n" +
Deepak Kodihalli22d4df02018-09-18 06:52:43 -05002759 "\t{fru,sensors,sel,chassis,collect_service_data, \
2760 health_check,dump,bmc,mc,gardclear,firmware,logging}\n" +
Justin Thalere412dc22018-01-12 16:28:24 -06002761 "\t...\n" +
2762 "openbmctool.py: error: the following arguments are required: -H/--host, -U/--user")
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002763 sys.exit()
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002764
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002765if __name__ == '__main__':
Justin Thalere412dc22018-01-12 16:28:24 -06002766 """
2767 main function when called from the command line
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002768
2769 """
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002770 import sys
Nagaraju Gorugantic1a00af2018-11-07 00:52:11 -06002771
Justin Thalerf9aee3e2017-12-05 12:11:09 -06002772 isTTY = sys.stdout.isatty()
2773 assert sys.version_info >= (2,7)
2774 main()