Updating code to version 1.0. Added capabilities for firmware updates, checking tool version, clearing gard, and some minor code readability changes.
diff --git a/thalerj/openbmctool.py b/thalerj/openbmctool.py
index 551c43c..d61a35c 100644
--- a/thalerj/openbmctool.py
+++ b/thalerj/openbmctool.py
@@ -1,21 +1,19 @@
#!/usr/bin/python3
+"""
+ Copyright 2017 IBM Corporation
-'''
-#================================================================================
-#
-# openbmctool.py
-#
-# Copyright IBM Corporation 2015-2017. All Rights Reserved
-#
-# This program is licensed under the terms of the Eclipse Public License
-# v1.0 as published by the Eclipse Foundation and available at
-# http://www.eclipse.org/legal/epl-v10.html
-#
-# U.S. Government Users Restricted Rights: Use, duplication or disclosure
-# restricted by GSA ADP Schedule Contract with IBM Corp.
-#
-#================================================================================
-'''
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
import argparse
import requests
import getpass
@@ -23,23 +21,20 @@
import os
import urllib3
import time, datetime
-# import yaml
import binascii
import subprocess
import platform
import zipfile
-#isTTY = sys.stdout.isatty()
-
-"""
- Used to add highlights to various text for displaying in a terminal
-
- @param textToColor: string, the text to be colored
- @param color: string, used to color the text red or green
- @param bold: boolean, used to bold the textToColor
- @return: Buffered reader containing the modified string.
-"""
def hilight(textToColor, color, bold):
+ """
+ Used to add highlights to various text for displaying in a terminal
+
+ @param textToColor: string, the text to be colored
+ @param color: string, used to color the text red or green
+ @param bold: boolean, used to bold the textToColor
+ @return: Buffered reader containing the modified string.
+ """
if(sys.platform.__contains__("win")):
if(color == "red"):
os.system('color 04')
@@ -62,14 +57,15 @@
attr.append('0')
return '\x1b[%sm%s\x1b[0m' % (';'.join(attr),textToColor)
-"""
- Error handler various connection errors to bmcs
-
- @param jsonFormat: boolean, used to output in json format with an error code.
- @param errorStr: string, used to color the text red or green
- @param err: string, the text from the exception
-"""
+
def connectionErrHandler(jsonFormat, errorStr, err):
+ """
+ Error handler various connection errors to bmcs
+
+ @param jsonFormat: boolean, used to output in json format with an error code.
+ @param errorStr: string, used to color the text red or green
+ @param err: string, the text from the exception
+ """
if errorStr == "Timeout":
if not jsonFormat:
return("FQPSPIN0000M: Connection timed out. Ensure you have network connectivity to the bmc")
@@ -117,16 +113,17 @@
else:
return("Unknown Error: "+ str(err))
-"""
- Sets the output width of the columns to display
-
- @param keylist: list, list of strings representing the keys for the dictForOutput
- @param numcols: the total number of columns in the final output
- @param dictForOutput: dictionary, contains the information to print to the screen
- @param colNames: list, The strings to use for the column headings, in order of the keylist
- @return: A list of the column widths for each respective column.
-"""
+
def setColWidth(keylist, numCols, dictForOutput, colNames):
+ """
+ Sets the output width of the columns to display
+
+ @param keylist: list, list of strings representing the keys for the dictForOutput
+ @param numcols: the total number of columns in the final output
+ @param dictForOutput: dictionary, contains the information to print to the screen
+ @param colNames: list, The strings to use for the column headings, in order of the keylist
+ @return: A list of the column widths for each respective column.
+ """
colWidths = []
for x in range(0, numCols):
colWidths.append(0)
@@ -140,27 +137,72 @@
return colWidths
def loadPolicyTable(pathToPolicyTable):
+ """
+ loads a json based policy table into a dictionary
+
+ @param value: boolean, the value to convert
+ @return: A string of "Yes" or "No"
+ """
policyTable = {}
if(os.path.exists(pathToPolicyTable)):
with open(pathToPolicyTable, 'r') as stream:
try:
-# contents = yaml.load(stream)
contents =json.load(stream)
policyTable = contents['events']
- except yaml.YAMLError as err:
+ except Exception as err:
print(err)
return policyTable
-"""
- Logs into the BMC and creates a session
-
- @param host: string, the hostname or IP address of the bmc to log into
- @param username: The user name for the bmc to log into
- @param pw: The password for the BMC to log into
- @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
- @return: Session object
-"""
+
+def boolToString(value):
+ """
+ converts a boolean value to a human readable string value
+
+ @param value: boolean, the value to convert
+ @return: A string of "Yes" or "No"
+ """
+ if(value):
+ return "Yes"
+ else:
+ return "No"
+
+
+def tableDisplay(keylist, colNames, output):
+ """
+ Logs into the BMC and creates a session
+
+ @param keylist: list, keys for the output dictionary, ordered by colNames
+ @param colNames: Names for the Table of the columns
+ @param output: The dictionary of data to display
+ @return: Session object
+ """
+ colWidth = setColWidth(keylist, len(colNames), output, colNames)
+ row = ""
+ outputText = ""
+ for i in range(len(colNames)):
+ if (i != 0): row = row + "| "
+ row = row + colNames[i].ljust(colWidth[i])
+ outputText += row + "\n"
+
+ for key in sorted(output.keys()):
+ row = ""
+ for i in range(len(output[key])):
+ if (i != 0): row = row + "| "
+ row = row + output[key][keylist[i]].ljust(colWidth[i])
+ outputText += row + "\n"
+
+ return outputText
+
def login(host, username, pw,jsonFormat):
+ """
+ Logs into the BMC and creates a session
+
+ @param host: string, the hostname or IP address of the bmc to log into
+ @param username: The user name for the bmc to log into
+ @param pw: The password for the BMC to log into
+ @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
+ @return: Session object
+ """
if(jsonFormat==False):
print("Attempting login...")
httpHeader = {'Content-Type':'application/json'}
@@ -183,31 +225,37 @@
print(connectionErrHandler(jsonFormat, "ConnectionError", err))
sys.exit(1)
-"""
- Logs out of the bmc and terminates the session
-
- @param host: string, the hostname or IP address of the bmc to log out of
- @param username: The user name for the bmc to log out of
- @param pw: The password for the BMC to log out of
- @param session: the active session to use
- @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
-"""
+
def logout(host, username, pw, session, jsonFormat):
+ """
+ Logs out of the bmc and terminates the session
+
+ @param host: string, the hostname or IP address of the bmc to log out of
+ @param username: The user name for the bmc to log out of
+ @param pw: The password for the BMC to log out of
+ @param session: the active session to use
+ @param jsonFormat: boolean, flag that will only allow relevant data from user command to be display. This function becomes silent when set to true.
+ """
httpHeader = {'Content-Type':'application/json'}
- r = session.post('https://'+host+'/logout', headers=httpHeader,json = {"data": [username, pw]}, verify=False, timeout=10)
+ try:
+ r = session.post('https://'+host+'/logout', headers=httpHeader,json = {"data": [username, pw]}, verify=False, timeout=10)
+ except(requests.exceptions.Timeout):
+ print(connectionErrHandler(jsonFormat, "Timeout", None))
+
if(jsonFormat==False):
if('"message": "200 OK"' in r.text):
print('User ' +username + ' has been logged out')
-"""
- prints out the system inventory. deprecated see fruPrint and fruList
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def fru(host, args, session):
+ """
+ prints out the system inventory. deprecated see fruPrint and fruList
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
#url="https://"+host+"/org/openbmc/inventory/system/chassis/enumerate"
#print(url)
@@ -219,7 +267,11 @@
url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
httpHeader = {'Content-Type':'application/json'}
- res = session.get(url, headers=httpHeader, verify=False)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=40)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
+
sample = res.text
# inv_list.update(json.loads(sample)["data"])
#
@@ -247,74 +299,157 @@
# fruEntry = hilight(fruEntry, color, bold)
# print (fruEntry)
return sample
-"""
- prints out all inventory
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
- @return returns the total fru list.
-"""
-def fruPrint(host, args, session):
+
+def fruPrint(host, args, session):
+ """
+ prints out all inventory
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ @return returns the total fru list.
+ """
url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
httpHeader = {'Content-Type':'application/json'}
- res = session.get(url, headers=httpHeader, verify=False)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=40)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
+
+
# print(res.text)
frulist = res.text
url="https://"+host+"/xyz/openbmc_project/software/enumerate"
- res = session.get(url, headers=httpHeader, verify=False)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=40)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
# print(res.text)
frulist = frulist +"\n" + res.text
return frulist
-"""
- prints out all inventory or only a specific specified item
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def fruList(host, args, session):
+ """
+ prints out all inventory or only a specific specified item
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.items==True):
return fruPrint(host, args, session)
else:
- return "not implemented at this time"
+ return fruPrint(host, args, session)
-"""
- prints out the status of all FRUs
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
def fruStatus(host, args, session):
- print("fru status to be implemented")
-
-"""
- prints out all sensors
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the sensor sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
-def sensor(host, args, session):
-# url="https://"+host+"/org/openbmc/sensors/enumerate"
+ """
+ prints out the status of all FRUs
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
+ url="https://"+host+"/xyz/openbmc_project/inventory/enumerate"
httpHeader = {'Content-Type':'application/json'}
-# print(url)
-# res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
# print(res.text)
+ frulist = json.loads(res.text)['data']
+ frus = {}
+ for key in frulist:
+ component = frulist[key]
+ isFru = False
+ present = False
+ func = False
+ hasSels = False
+ keyPieces = key.split('/')
+ fruName = keyPieces[-1]
+ if 'core' in fruName: #associate cores to cpus
+ fruName = keyPieces[-2] + '-' + keyPieces[-1]
+ if 'Functional' in component:
+ if('Present' in component):
+
+ if 'FieldReplaceable' in component:
+ if component['FieldReplaceable'] == 1:
+ isFru = True
+ if "fan" in fruName:
+ isFru = True;
+ if component['Present'] == 1:
+ present = True
+ if component['Functional'] == 1:
+ func = True
+ if ((key + "/fault") in frulist):
+ hasSels = True;
+ if args.verbose:
+ if hasSels:
+ loglist = []
+ faults = frulist[key+"/fault"]['endpoints']
+ for item in faults:
+ loglist.append(item.split('/')[-1])
+ frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
+ else:
+ frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
+ else:
+ frus[fruName] = {"compName": fruName, "Functional": boolToString(func), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
+ elif "power_supply" in fruName:
+ if component['Present'] ==1:
+ present = True
+ isFru = True
+ if ((key + "/fault") in frulist):
+ hasSels = True;
+ if args.verbose:
+ if hasSels:
+ loglist = []
+ faults = frulist[key+"/fault"]['endpoints']
+ for key in faults:
+ loglist.append(faults[key].split('/')[-1])
+ frus[fruName] = {"compName": fruName, "Functional": "No", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": ', '.join(loglist).strip() }
+ else:
+ frus[fruName] = {"compName": fruName, "Functional": "Yes", "Present":boolToString(present), "IsFru": boolToString(isFru), "selList": "None" }
+ else:
+ frus[fruName] = {"compName": fruName, "Functional": boolToString(not hasSels), "Present":boolToString(present), "IsFru": boolToString(isFru), "hasSEL": boolToString(hasSels) }
+ if not args.json:
+ if not args.verbose:
+ colNames = ["Component", "Is a FRU", "Present", "Functional", "Has Logs"]
+ keylist = ["compName", "IsFru", "Present", "Functional", "hasSEL"]
+ else:
+ colNames = ["Component", "Is a FRU", "Present", "Functional", "Assoc. Log Number(s)"]
+ keylist = ["compName", "IsFru", "Present", "Functional", "selList"]
+ return tableDisplay(keylist, colNames, frus)
+ else:
+ return str(json.dumps(frus, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False))
+
+def sensor(host, args, session):
+ """
+ prints out all sensors
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the sensor sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
+ httpHeader = {'Content-Type':'application/json'}
url="https://"+host+"/xyz/openbmc_project/sensors/enumerate"
- res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
#Get OCC status
url="https://"+host+"/org/open_power/control/enumerate"
- occres = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ occres = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
if not args.json:
colNames = ['sensor', 'type', 'units', 'value', 'target']
sensors = json.loads(res.text)["data"]
@@ -324,7 +459,10 @@
keyparts = key.split("/")
senDict['sensorName'] = keyparts[-1]
senDict['type'] = keyparts[-2]
- senDict['units'] = sensors[key]['Unit'].split('.')[-1]
+ try:
+ senDict['units'] = sensors[key]['Unit'].split('.')[-1]
+ except KeyError:
+ print('Key Error: '+ key)
if('Scale' in sensors[key]):
scale = 10 ** sensors[key]['Scale']
else:
@@ -354,59 +492,37 @@
output['OCC0'] = {'sensorName':'OCC0', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
output['OCC1'] = {'sensorName':'OCC1', 'type': 'Discrete', 'units': 'N/A', 'value': 'Inactive', 'target': 'Inactive'}
keylist = ['sensorName', 'type', 'units', 'value', 'target']
- colWidth = setColWidth(keylist, len(colNames), output, colNames)
- row = ""
- outputText = ""
- for i in range(len(colNames)):
- if (i != 0): row = row + "| "
- row = row + colNames[i].ljust(colWidth[i])
- outputText += row + "\n"
- sortedKeys = list(output.keys()).sort
- for key in sorted(output.keys()):
- row = ""
- for i in range(len(output[key])):
- if (i != 0): row = row + "| "
- row = row + output[key][keylist[i]].ljust(colWidth[i])
- outputText += row + "\n"
- return outputText
+
+ return tableDisplay(keylist, colNames, output)
else:
return res.text + occres.text
-"""
- prints out the bmc alerts
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the sel sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def sel(host, args, session):
+ """
+ prints out the bmc alerts
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the sel sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
url="https://"+host+"/xyz/openbmc_project/logging/entry/enumerate"
httpHeader = {'Content-Type':'application/json'}
- #print(url)
- res = session.get(url, headers=httpHeader, verify=False, timeout=60)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=60)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
return res.text
-
-
-"""
- converts a boolean value to a human readable string value
-
- @param value: boolean, the value to convert
- @return: A string of "Yes" or "No"
-"""
-def boolToString(value):
- if(value):
- return "Yes"
- else:
- return "No"
-
-"""
- parses the esel data and gets predetermined search terms
-
- @param eselRAW: string, the raw esel string from the bmc
- @return: A dictionary containing the quick snapshot data unless args.fullEsel is listed then a full PEL log is returned
-"""
+
+
def parseESEL(args, eselRAW):
+ """
+ parses the esel data and gets predetermined search terms
+
+ @param eselRAW: string, the raw esel string from the bmc
+ @return: A dictionary containing the quick snapshot data unless args.fullEsel is listed then a full PEL log is returned
+ """
eselParts = {}
esel_bin = binascii.unhexlify(''.join(eselRAW.split()[16:]))
#search terms contains the search term as the key and the return dictionary key as it's value
@@ -436,8 +552,6 @@
print("machine architecture not supported for parsing eSELs")
return eselParts
-
-
if(os.path.exists(errlPath)):
output= subprocess.check_output([errlPath, '-d', '--file=/tmp/esel.bin']).decode('utf-8')
# output = proc.communicate()[0]
@@ -468,13 +582,14 @@
return eselParts
-"""
- sorts the sels by timestamp, then log entry number
-
- @param events: Dictionary containing events
- @return: list containing a list of the ordered log entries, and dictionary of keys
-"""
+
def sortSELs(events):
+ """
+ sorts the sels by timestamp, then log entry number
+
+ @param events: Dictionary containing events
+ @return: list containing a list of the ordered log entries, and dictionary of keys
+ """
logNumList = []
timestampList = []
eventKeyDict = {}
@@ -503,14 +618,15 @@
return [logNumList, eventKeyDict]
-"""
- parses alerts in the IBM CER format, using an IBM policy Table
-
- @param policyTable: dictionary, the policy table entries
- @param selEntries: dictionary, the alerts retrieved from the bmc
- @return: A dictionary of the parsed entries, in chronological order
-"""
+
def parseAlerts(policyTable, selEntries, args):
+ """
+ parses alerts in the IBM CER format, using an IBM policy Table
+
+ @param policyTable: dictionary, the policy table entries
+ @param selEntries: dictionary, the alerts retrieved from the bmc
+ @return: A dictionary of the parsed entries, in chronological order
+ """
eventDict = {}
eventNum =""
count = 0
@@ -529,7 +645,6 @@
for logNum in logNumList:
key = eventKeyDict[logNum]
-# for key in selEntries:
hasEsel=False
i2creadFail = False
if 'callout' in key:
@@ -561,6 +676,15 @@
if("PROCEDURE" in addDataPiece[i]):
fruCallout = str(hex(int(str(addDataPiece[i]).split('=')[1])))[2:]
calloutFound = True
+ if("RAIL_NAME" in addDataPiece[i]):
+ calloutFound=True
+ fruCallout = str(addDataPiece[i]).split('=')[1].strip()
+ if("INPUT_NAME" in addDataPiece[i]):
+ calloutFound=True
+ fruCallout = str(addDataPiece[i]).split('=')[1].strip()
+ if("SENSOR_TYPE" in addDataPiece[i]):
+ calloutFound=True
+ fruCallout = str(addDataPiece[i]).split('=')[1].strip()
if(calloutFound):
policyKey = messageID +"||" + fruCallout
@@ -600,18 +724,17 @@
eventDict['event' +eventNum]['raweSEL'] = esel
eventDict['event' +eventNum]['logNum'] = key.split('/')[-1]
eventDict['event' +eventNum]['resolved'] = bool(selEntries[key]['Resolved'])
- #print("Event entry "+eventNum+": not found in policy table: " + policyKey)
count += 1
return eventDict
-"""
- displays alerts in human readable format
-
- @param events: Dictionary containing events
- @return:
-"""
def selDisplay(events, args):
+ """
+ displays alerts in human readable format
+
+ @param events: Dictionary containing events
+ @return:
+ """
activeAlerts = []
historyAlerts = []
sortedEntries = sortSELs(events)
@@ -713,15 +836,16 @@
# print(events[eventKeyDict[str(log)]])
return output
-"""
- prints out all bmc alerts
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def selPrint(host, args, session):
+ """
+ prints out all bmc alerts
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.policyTableLoc is None):
if os.path.exists('policyTable.json'):
ptableLoc = "policyTable.json"
@@ -753,7 +877,7 @@
#need to load json twice as original content was string escaped a second time
selEntries = json.loads(json.loads(cleanSels))
selEntries = selEntries['data']
- cerList = {}
+
if 'description' in selEntries:
if(args.json):
return("{\n\t\"numAlerts\": 0\n}")
@@ -778,31 +902,36 @@
else:
print("error: Policy Table not found.")
return selEntries
-"""
- prints out all all bmc alerts, or only prints out the specified alerts
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def selList(host, args, session):
+ """
+ prints out all all bmc alerts, or only prints out the specified alerts
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
return(sel(host, args, session))
-"""
- clears all alerts
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def selClear(host, args, session):
+ """
+ clears all alerts
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
url="https://"+host+"/xyz/openbmc_project/logging/action/deleteAll"
httpHeader = {'Content-Type':'application/json'}
data = "{\"data\": [] }"
- #print(url)
- res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
+
+ try:
+ res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
if res.status_code == 200:
return "The Alert Log has been cleared. Please allow a few minutes for the action to complete."
else:
@@ -823,81 +952,132 @@
return ('Sel clearing complete')
def selSetResolved(host, args, session):
+ """
+ sets a sel entry to resolved
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
url="https://"+host+"/xyz/openbmc_project/logging/entry/" + str(args.selNum) + "/attr/Resolved"
httpHeader = {'Content-Type':'application/json'}
data = "{\"data\": 1 }"
- #print(url)
- res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
if res.status_code == 200:
return "Sel entry "+ str(args.selNum) +" is now set to resolved"
else:
return "Unable to set the alert to resolved"
-
-# """
-# gathers the esels. deprecated
-#
-# @param host: string, the hostname or IP address of the bmc
-# @param args: contains additional arguments used by the fru sub command
-# @param session: the active session to use
-# @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-# """
-# def getESEL(host, args, session):
-# selentry= args.selNum
-# url="https://"+host+"/xyz/openbmc_project/logging/entry" + str(selentry)
-# httpHeader = {'Content-Type':'application/json'}
-# print(url)
-# res = session.get(url, headers=httpHeader, verify=False, timeout=20)
-# e = res.json()
-# if e['Message'] != 'org.open_power.Error.Host.Event' and\
-# e['Message'] != 'org.open_power.Error.Host.Event.Event':
-# raise Exception("Event is not from Host: " + e['Message'])
-# for d in e['AdditionalData']:
-# data = d.split("=")
-# tag = data.pop(0)
-# if tag != 'ESEL':
-# continue
-# data = "=".join(data)
-# if args.binary:
-# data = data.split(" ")
-# if '' == data[-1]:
-# data.pop()
-# data = "".join(map(lambda x: chr(int(x, 16)), data))
-# print(data)
-"""
- called by the chassis function. Controls the power state of the chassis, or gets the status
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+def selResolveAll(host, args, session):
+ """
+ sets a sel entry to resolved
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
+ rawselEntries = sel(host, args, session)
+ loadFailed = False
+ try:
+ selEntries = json.loads(rawselEntries)
+ except ValueError:
+ loadFailed = True
+ if loadFailed:
+ cleanSels = json.dumps(rawselEntries).replace('\\n', '')
+ #need to load json twice as original content was string escaped a second time
+ selEntries = json.loads(json.loads(cleanSels))
+ selEntries = selEntries['data']
+
+ if 'description' in selEntries:
+ if(args.json):
+ return("{\n\t\"selsResolved\": 0\n}")
+ else:
+ return("No log entries found")
+ else:
+ d = vars(args)
+ successlist = []
+ failedlist = []
+ for key in selEntries:
+ if 'callout' not in key:
+ d['selNum'] = key.split('/')[-1]
+ resolved = selSetResolved(host,args,session)
+ if 'Sel entry' in resolved:
+ successlist.append(d['selNum'])
+ else:
+ failedlist.append(d['selNum'])
+ output = ""
+ successlist.sort()
+ failedlist.sort()
+ if len(successlist)>0:
+ output = "Successfully resolved: " +', '.join(successlist) +"\n"
+ if len(failedlist)>0:
+ output += "Failed to resolve: " + ', '.join(failedlist) + "\n"
+ return output
+
def chassisPower(host, args, session):
+ """
+ called by the chassis function. Controls the power state of the chassis, or gets the status
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.powcmd == 'on'):
print("Attempting to Power on...:")
url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
httpHeader = {'Content-Type':'application/json',}
data = '{"data":"xyz.openbmc_project.State.Host.Transition.On"}'
- res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
return res.text
- elif(args.powcmd == 'off'):
- print("Attempting to Power off...:")
+ elif(args.powcmd == 'softoff'):
+ print("Attempting to Power off gracefully...:")
url="https://"+host+"/xyz/openbmc_project/state/host0/attr/RequestedHostTransition"
httpHeader = {'Content-Type':'application/json'}
data = '{"data":"xyz.openbmc_project.State.Host.Transition.Off"}'
- res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
+ return res.text
+ elif(args.powcmd == 'hardoff'):
+ print("Attempting to Power off immediately...:")
+ url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/RequestedPowerTransition"
+ httpHeader = {'Content-Type':'application/json'}
+ data = '{"data":"xyz.openbmc_project.State.Chassis.Transition.Off"}'
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
return res.text
elif(args.powcmd == 'status'):
url="https://"+host+"/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState"
httpHeader = {'Content-Type':'application/json'}
# print(url)
- res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
chassisState = json.loads(res.text)['data'].split('.')[-1]
url="https://"+host+"/xyz/openbmc_project/state/host0/attr/CurrentHostState"
- res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
hostState = json.loads(res.text)['data'].split('.')[-1]
url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/CurrentBMCState"
- res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
bmcState = json.loads(res.text)['data'].split('.')[-1]
if(args.json):
outDict = {"Chassis Power State" : chassisState, "Host Power State" : hostState, "BMC Power State":bmcState}
@@ -907,34 +1087,43 @@
else:
return "Invalid chassis power command"
-"""
- called by the chassis function. Controls the identify led of the chassis. Sets or gets the state
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def chassisIdent(host, args, session):
+ """
+ called by the chassis function. Controls the identify led of the chassis. Sets or gets the state
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.identcmd == 'on'):
print("Attempting to turn identify light on...:")
url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
httpHeader = {'Content-Type':'application/json',}
data = '{"data":true}'
- res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
return res.text
elif(args.identcmd == 'off'):
print("Attempting to turn identify light off...:")
url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted"
httpHeader = {'Content-Type':'application/json'}
data = '{"data":false}'
- res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ try:
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
return res.text
elif(args.identcmd == 'status'):
url="https://"+host+"/xyz/openbmc_project/led/groups/enclosure_identify"
httpHeader = {'Content-Type':'application/json'}
-# print(url)
- res = session.get(url, headers=httpHeader, verify=False, timeout=20)
+ try:
+ res = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return(connectionErrHandler(args.json, "Timeout", None))
status = json.loads(res.text)['data']
if(args.json):
return status
@@ -946,15 +1135,16 @@
else:
return "Invalid chassis identify command"
-"""
- controls the different chassis commands
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the fru sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def chassis(host, args, session):
+ """
+ controls the different chassis commands
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fru sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(hasattr(args, 'powcmd')):
result = chassisPower(host,args,session)
elif(hasattr(args, 'identcmd')):
@@ -962,15 +1152,16 @@
else:
return "to be completed"
return result
-"""
- Downloads a dump file from the bmc
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def bmcDumpRetrieve(host, args, session):
+ """
+ Downloads a dump file from the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
httpHeader = {'Content-Type':'application/json'}
dumpNum = args.dumpNum
if (args.dumpSaveLoc is not None):
@@ -979,7 +1170,7 @@
saveLoc = '/tmp'
url ='https://'+host+'/download/dump/' + str(dumpNum)
try:
- r = session.get(url, headers=httpHeader, stream=True, verify=False, timeout=20)
+ r = session.get(url, headers=httpHeader, stream=True, verify=False, timeout=30)
if (args.dumpSaveLoc is not None):
if os.path.exists(saveLoc):
if saveLoc[-1] != os.path.sep:
@@ -999,20 +1190,19 @@
except(requests.exceptions.Timeout):
return connectionErrHandler(args.json, "Timeout", None)
- sys.exit(1)
+
except(requests.exceptions.ConnectionError) as err:
return connectionErrHandler(args.json, "ConnectionError", err)
- sys.exit(1)
-"""
- Lists the number of dump files on the bmc
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
-def bmcDumpList(host, args, session):
+def bmcDumpList(host, args, session):
+ """
+ Lists the number of dump files on the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
httpHeader = {'Content-Type':'application/json'}
url ='https://'+host+'/xyz/openbmc_project/dump/list'
try:
@@ -1021,21 +1211,19 @@
return r.text
except(requests.exceptions.Timeout):
return connectionErrHandler(args.json, "Timeout", None)
- sys.exit(1)
+
except(requests.exceptions.ConnectionError) as err:
- return connectionErrHandler(args.json, "ConnectionError", err)
- sys.exit(1)
+ return connectionErrHandler(args.json, "ConnectionError", err)
-
-"""
- Deletes BMC dump files from the bmc
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
def bmcDumpDelete(host, args, session):
+ """
+ Deletes BMC dump files from the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
httpHeader = {'Content-Type':'application/json'}
dumpList = []
successList = []
@@ -1051,16 +1239,12 @@
r = session.post(url, headers=httpHeader, json = {"data": []}, verify=False, timeout=30)
if r.status_code == 200:
successList.append(str(dumpNum))
- #return('Dump ' + str(dumpNum) + ' deleted')
else:
failedList.append(str(dumpNum))
- #return('Unable to delete dump ' + str(dumpNum))
except(requests.exceptions.Timeout):
return connectionErrHandler(args.json, "Timeout", None)
-# sys.exit(1)
except(requests.exceptions.ConnectionError) as err:
return connectionErrHandler(args.json, "ConnectionError", err)
-# sys.exit(1)
output = "Successfully deleted dumps: " + ', '.join(successList)
if(len(failedList)>0):
output+= '\nFailed to delete dumps: ' + ', '.join(failedList)
@@ -1068,16 +1252,19 @@
else:
return 'You must specify an entry number to delete'
-"""
- Deletes All BMC dump files from the bmc
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
def bmcDumpDeleteAll(host, args, session):
- dumpList = json.loads(bmcDumpList(host, args, session))['data']
+ """
+ Deletes All BMC dump files from the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
+ dumpResp = bmcDumpList(host, args, session)
+ if 'FQPSPIN0000M' in dumpResp or 'FQPSPIN0001M'in dumpResp:
+ return dumpResp
+ dumpList = json.loads(dumpResp)['data']
d = vars(args)
dumpNums = []
for dump in dumpList:
@@ -1087,15 +1274,16 @@
return bmcDumpDelete(host, args, session)
-"""
- Creates a bmc dump file
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def bmcDumpCreate(host, args, session):
+ """
+ Creates a bmc dump file
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
httpHeader = {'Content-Type':'application/json'}
url = 'https://'+host+'/xyz/openbmc_project/dump/action/CreateDump'
try:
@@ -1111,15 +1299,16 @@
-"""
- Collects all data needed for service from the BMC
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the collectServiceData sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def collectServiceData(host, args, session):
+ """
+ Collects all data needed for service from the BMC
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the collectServiceData sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
#create a bmc dump
dumpcount = len(json.loads(bmcDumpList(host, args, session))['data'])
try:
@@ -1247,50 +1436,310 @@
print("Failed to create zip file with collected information")
return "data collection complete"
-"""
- handles various bmc level commands, currently bmc rebooting
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the bmc sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
+def healthCheck(host, args, session):
+ """
+ runs a health check on the platform
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the bmc sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
+ #check fru status and get as json to easily work through
+ d = vars(args)
+ useJson = d['json']
+ d['json'] = True
+ d['verbose']= False
+
+ frus = json.loads(fruStatus(host, args, session))
+
+ hwStatus= "OK"
+ performanceStatus = "OK"
+ for key in frus:
+ if frus[key]["Functional"] == "No" and frus[key]["Present"] == "Yes":
+ hwStatus= "Degraded"
+ if("power_supply" in key):
+ gpuCount =0;
+ frulist = json.loads(fruList(host, args, session))
+ for comp in frulist:
+ if "gv100card" in comp:
+ gpuCount +=1
+ if gpuCount > 4:
+ hwStatus = "Critical"
+ performanceStatus="Degraded"
+ break;
+ elif("fan" in key):
+ hwStatus = "Degraded"
+ else:
+ performanceStatus = "Degraded"
+ if useJson:
+ output = {"Hardware Status": hwStatus, "Performance": performanceStatus}
+ output = json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
+ else:
+ output = ("Hardware Status: " + hwStatus +
+ "\nPerformance: " +performanceStatus )
+
+
+ #SW407886: Clear the duplicate entries
+ #collect the dups
+ d['devdebug'] = False
+ sels = json.loads(selPrint(host, args, session))
+ logNums2Clr = []
+ oldestLogNum={"logNum": "bogus" ,"key" : ""}
+ count = 0
+ if sels['numAlerts'] > 0:
+ for key in sels:
+ if "numAlerts" in key:
+ continue
+ try:
+ if "slave@00:00/00:00:00:06/sbefifo1-dev0/occ1-dev0" in sels[key]['Message']:
+ count += 1
+ if count > 1:
+ #preserve first occurrence
+ if sels[key]['timestamp'] < sels[oldestLogNum['key']]['timestamp']:
+ oldestLogNum['key']=key
+ oldestLogNum['logNum'] = sels[key]['logNum']
+ else:
+ oldestLogNum['key']=key
+ oldestLogNum['logNum'] = sels[key]['logNum']
+ logNums2Clr.append(sels[key]['logNum'])
+ except KeyError:
+ continue
+ if(count >0):
+ logNums2Clr.remove(oldestLogNum['logNum'])
+ #delete the dups
+ if count >1:
+ httpHeader = {'Content-Type':'application/json'}
+ data = "{\"data\": [] }"
+ for logNum in logNums2Clr:
+ url = "https://"+ host+ "/xyz/openbmc_project/logging/entry/"+logNum+"/action/Delete"
+ try:
+ session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ deleteFailed = True
+ except(requests.exceptions.ConnectionError) as err:
+ deleteFailed = True
+ #End of defect resolve code
+ d['json'] = useJson
+ return output
+
+
+
def bmc(host, args, session):
- if(args.info):
- return "To be completed"
+ """
+ handles various bmc level commands, currently bmc rebooting
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the bmc sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.type is not None):
return bmcReset(host, args, session)
+ if(args.info):
+ return "Not implemented at this time"
+
-"""
- controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots.
-
- @param host: string, the hostname or IP address of the bmc
- @param args: contains additional arguments used by the bmcReset sub command
- @param session: the active session to use
- @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
-"""
+
def bmcReset(host, args, session):
+ """
+ controls resetting the bmc. warm reset reboots the bmc, cold reset removes the configuration and reboots.
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the bmcReset sub command
+ @param session: the active session to use
+ @param args.json: boolean, if this flag is set to true, the output will be provided in json format for programmatic consumption
+ """
if(args.type == "warm"):
print("\nAttempting to reboot the BMC...:")
url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
httpHeader = {'Content-Type':'application/json'}
- data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"'
- res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
return res.text
elif(args.type =="cold"):
- return "cold reset not available at this time."
+ print("\nAttempting to reboot the BMC...:")
+ url="https://"+host+"/xyz/openbmc_project/state/bmc0/attr/RequestedBMCTransition"
+ httpHeader = {'Content-Type':'application/json'}
+ data = '{"data":"xyz.openbmc_project.State.BMC.Transition.Reboot"}'
+ res = session.put(url, headers=httpHeader, data=data, verify=False, timeout=20)
+ return res.text
else:
return "invalid command"
-"""
- creates the parser for the command line along with help for each command and subcommand
-
- @return: returns the parser for the command line
-"""
+
+def gardClear(host, args, session):
+ """
+ clears the gard records from the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the gardClear sub command
+ @param session: the active session to use
+ """
+ url="https://"+host+"/org/open_power/control/gard/action/Reset"
+ httpHeader = {'Content-Type':'application/json'}
+ data = '{"data":[]}'
+ try:
+
+ res = session.post(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ if res.status_code == 404:
+ return "Command not supported by this firmware version"
+ else:
+ return res.text
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+
+def activateFWImage(host, args, session):
+ """
+ activates a firmware image on the bmc
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fwflash sub command
+ @param session: the active session to use
+ @param fwID: the unique ID of the fw image to activate
+ """
+ fwID = args.imageID
+
+ #determine the existing versions
+ httpHeader = {'Content-Type':'application/json'}
+ url="https://"+host+"/xyz/openbmc_project/software/enumerate"
+ try:
+ resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+ existingSoftware = json.loads(resp.text)['data']
+ altVersionID = ''
+ versionType = ''
+ imageKey = '/xyz/openbmc_project/software/'+fwID
+ if imageKey in existingSoftware:
+ versionType = existingSoftware[imageKey]['Purpose']
+ for key in existingSoftware:
+ if imageKey == key:
+ continue
+ if 'Purpose' in existingSoftware[key]:
+ if versionType == existingSoftware[key]['Purpose']:
+ altVersionID = key.split('/')[-1]
+
+
+
+
+ url="https://"+host+"/xyz/openbmc_project/software/"+ fwID + "/attr/Priority"
+ url1="https://"+host+"/xyz/openbmc_project/software/"+ altVersionID + "/attr/Priority"
+ data = "{\"data\": 0}"
+ data1 = "{\"data\": 1 }"
+ try:
+ resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ resp1 = session.put(url1, headers=httpHeader, data=data1, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+ if(not args.json):
+ if resp.status_code == 200 and resp1.status_code == 200:
+ return 'Firmware activation completed. Please reboot the BMC for the changes to take effect.'
+ else:
+ return "Firmware activation failed."
+ else:
+ return resp.text + resp1.text
+
+def fwFlash(host, args, session):
+ """
+ updates the bmc firmware and pnor firmware
+
+ @param host: string, the hostname or IP address of the bmc
+ @param args: contains additional arguments used by the fwflash sub command
+ @param session: the active session to use
+ """
+
+ if(args.type == 'bmc'):
+ purp = 'BMC'
+ else:
+ purp = 'Host'
+ #determine the existing versions
+ httpHeader = {'Content-Type':'application/json'}
+ url="https://"+host+"/xyz/openbmc_project/software/enumerate"
+ try:
+ resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+ oldsoftware = json.loads(resp.text)['data']
+
+ #upload the file
+ httpHeader = {'Content-Type':'application/octet-stream'}
+ url="https://"+host+"/upload/image"
+ data=open(args.fileloc,'rb').read()
+ print("Uploading file to BMC")
+ try:
+ resp = session.post(url, headers=httpHeader, data=data, verify=False)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+ if resp.status_code != 200:
+ return "Failed to upload the file to the bmc"
+ else:
+ print("Upload complete.")
+
+ #determine the version number
+ software ={}
+ for i in range(0, 5):
+ httpHeader = {'Content-Type':'application/json'}
+ url="https://"+host+"/xyz/openbmc_project/software/enumerate"
+ try:
+ resp = session.get(url, headers=httpHeader, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+ software = json.loads(resp.text)['data']
+ #check if bmc is done processing the new image
+ if (len(software.keys()) > len(oldsoftware.keys())):
+ break
+ else:
+ time.sleep(15)
+ newversionID = ''
+ for key in software:
+ if key not in oldsoftware:
+ idPart = key.split('/')[-1]
+ if idPart == 'inventory':
+ continue
+ softPurpose = software['/xyz/openbmc_project/software/' +idPart]['Purpose'].split('.')[-1]
+ if(purp in softPurpose):
+ newversionID = idPart
+ break
+ if newversionID == '':
+ return('Could not find the new version of the firmware on the bmc, it may already exist.' +
+ "\nRun fru print command and check for the version number. If found, use the firmware activate command to change to using that image.\n"
+ "If you are reapplying the same image, reboot the bmc to complete the update. ")
+
+ #activate the new image
+ print("Activating new image")
+ url="https://"+host+"/xyz/openbmc_project/software/"+ newversionID + "/attr/RequestedActivation"
+ data = '{"data":"xyz.openbmc_project.Software.Activation.RequestedActivations.Active"}'
+ try:
+ resp = session.put(url, headers=httpHeader, data=data, verify=False, timeout=30)
+ except(requests.exceptions.Timeout):
+ return connectionErrHandler(args.json, "Timeout", None)
+ except(requests.exceptions.ConnectionError) as err:
+ return connectionErrHandler(args.json, "ConnectionError", err)
+
+ return "Firmware flash completed. Please allow a few minutes for the activation to complete. After the activation is complete you will need to reboot the bmc and the host OS for the changes to take effect. "
+
def createCommandParser():
+ """
+ creates the parser for the command line along with help for each command and subcommand
+
+ @return: returns the parser for the command line
+ """
parser = argparse.ArgumentParser(description='Process arguments')
- parser.add_argument("-H", "--host", required=True, help='A hostname or IP for the BMC')
- parser.add_argument("-U", "--user", required=True, help='The username to login with')
- #parser.add_argument("-v", "--verbose", help='provides more detail')
+ parser.add_argument("-H", "--host", help='A hostname or IP for the BMC')
+ parser.add_argument("-U", "--user", help='The username to login with')
group = parser.add_mutually_exclusive_group()
group.add_argument("-A", "--askpw", action='store_true', help='prompt for password')
group.add_argument("-P", "--PW", help='Provide the password in-line')
@@ -1298,12 +1747,13 @@
parser.add_argument('-t', '--policyTableLoc', help='The location of the policy table to parse alerts')
parser.add_argument('-c', '--CerFormat', action='store_true', help=argparse.SUPPRESS)
parser.add_argument('-T', '--procTime', action='store_true', help= argparse.SUPPRESS)
- subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ parser.add_argument('-V', '--version', action='store_true', help='Display the version number of the openbmctool')
+ subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
#fru command
parser_inv = subparsers.add_parser("fru", help='Work with platform inventory')
#fru print
- inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions")
+ inv_subparser = parser_inv.add_subparsers(title='subcommands', description='valid inventory actions', help="valid inventory actions", dest='command')
inv_print = inv_subparser.add_parser("print", help="prints out a list of all FRUs")
inv_print.set_defaults(func=fruPrint)
#fru list [0....n]
@@ -1312,14 +1762,12 @@
inv_list.set_defaults(func=fruList)
#fru status
inv_status = inv_subparser.add_parser("status", help="prints out the status of all FRUs")
+ inv_status.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
inv_status.set_defaults(func=fruStatus)
- #parser_inv.add_argument('status', action='store_true', help="Lists the status of all BMC detected components")
- #parser_inv.add_argument('num', type=int, action='store_true', help="The number of the FRU to list")
- #inv_status.set_defaults(func=fruStatus)
#sensors command
parser_sens = subparsers.add_parser("sensors", help="Work with platform sensors")
- sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions')
+ sens_subparser=parser_sens.add_subparsers(title='subcommands', description='valid sensor actions', help='valid sensor actions', dest='command')
#sensor print
sens_print= sens_subparser.add_parser('print', help="prints out a list of all Sensors.")
sens_print.set_defaults(func=sensor)
@@ -1331,7 +1779,7 @@
#sel command
parser_sel = subparsers.add_parser("sel", help="Work with platform alerts")
- sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions')
+ sel_subparser = parser_sel.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions', dest='command')
#sel print
sel_print = sel_subparser.add_parser("print", help="prints out a list of all sels in a condensed list")
@@ -1352,17 +1800,20 @@
sel_clear.set_defaults(func=selClear)
sel_setResolved = sel_subparser.add_parser("resolve", help="Sets the sel entry to resolved")
- sel_setResolved.add_argument('selNum', type=int, help="the number of the SEL entry to resolve")
+ sel_setResolved.add_argument('-n', '--selNum', type=int, help="the number of the SEL entry to resolve")
+ sel_ResolveAll_sub = sel_setResolved.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
+ sel_ResolveAll = sel_ResolveAll_sub.add_parser('all', help='Resolve all SEL entries')
+ sel_ResolveAll.set_defaults(func=selResolveAll)
sel_setResolved.set_defaults(func=selSetResolved)
parser_chassis = subparsers.add_parser("chassis", help="Work with chassis power and status")
- chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ chas_sub = parser_chassis.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
parser_chassis.add_argument('status', action='store_true', help='Returns the current status of the platform')
parser_chassis.set_defaults(func=chassis)
parser_chasPower = chas_sub.add_parser("power", help="Turn the chassis on or off, check the power state")
- parser_chasPower.add_argument('powcmd', choices=['on','off', 'status'], help='The value for the power command. on, off, or status')
+ parser_chasPower.add_argument('powcmd', choices=['on','softoff', 'hardoff', 'status'], help='The value for the power command. on, off, or status')
parser_chasPower.set_defaults(func=chassisPower)
#control the chassis identify led
@@ -1375,22 +1826,24 @@
parser_servData.add_argument('-d', '--devdebug', action='store_true', help=argparse.SUPPRESS)
parser_servData.set_defaults(func=collectServiceData)
+ #system quick health check
+ parser_healthChk = subparsers.add_parser("health_check", help="Work with platform sensors")
+ parser_healthChk.set_defaults(func=healthCheck)
+
#work with bmc dumps
parser_bmcdump = subparsers.add_parser("dump", help="Work with bmc dump files")
- bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ bmcDump_sub = parser_bmcdump.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
dump_Create = bmcDump_sub.add_parser('create', help="Create a bmc dump")
dump_Create.set_defaults(func=bmcDumpCreate)
-
dump_list = bmcDump_sub.add_parser('list', help="list all bmc dump files")
dump_list.set_defaults(func=bmcDumpList)
parserdumpdelete = bmcDump_sub.add_parser('delete', help="Delete bmc dump files")
parserdumpdelete.add_argument("-n", "--dumpNum", nargs='*', type=int, help="The Dump entry to delete")
-# parserdumpdelete.add_argument('deleteAll', choices=['all'], help="Delete all entries")
parserdumpdelete.set_defaults(func=bmcDumpDelete)
- bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ bmcDumpDelsub = parserdumpdelete.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
deleteAllDumps = bmcDumpDelsub.add_parser('all', help='Delete all bmc dump files')
deleteAllDumps.set_defaults(func=bmcDumpDeleteAll)
@@ -1399,60 +1852,47 @@
parser_dumpretrieve.add_argument("-s", "--dumpSaveLoc", help="The location to save the bmc dump file")
parser_dumpretrieve.set_defaults(func=bmcDumpRetrieve)
-
- #esel command ###notused###
-# esel_parser = subparsers.add_parser("esel", help ="Work with an ESEL entry")
-# esel_subparser = esel_parser.add_subparsers(title='subcommands', description='valid SEL actions', help = 'valid SEL actions')
-# esel_get = esel_subparser.add_parser("get", help="Gets the details of an ESEL entry")
-# esel_get.add_argument('selNum', type=int, help="the number of the SEL entry to get")
-# esel_get.set_defaults(func=getESEL)
-
-
parser_bmc = subparsers.add_parser('bmc', help="Work with the bmc")
- bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ bmc_sub = parser_bmc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
parser_BMCReset = bmc_sub.add_parser('reset', help='Reset the bmc' )
parser_BMCReset.add_argument('type', choices=['warm','cold'], help="Warm: Reboot the BMC, Cold: CLEAR config and reboot bmc")
-# parser_BMCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration")
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.")
parser_bmc.set_defaults(func=bmc)
-# parser_BMCReset.set_defaults(func=bmcReset)
#add alias to the bmc command
parser_mc = subparsers.add_parser('mc', help="Work with the management controller")
- mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help")
+ mc_sub = parser_mc.add_subparsers(title='subcommands', description='valid subcommands',help="sub-command help", dest='command')
parser_MCReset = mc_sub.add_parser('reset', help='Reset the bmc' )
parser_MCReset.add_argument('type', choices=['warm','cold'], help="Reboot the BMC")
#parser_MCReset.add_argument('cold', action='store_true', help="Reboot the BMC and CLEAR the configuration")
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.")
+ parser_MCReset.set_defaults(func=bmcReset)
parser_mc.set_defaults(func=bmc)
- #parser_MCReset.set_defaults(func=bmcReset)
+
+ #gard clear
+ parser_gc = subparsers.add_parser("gardclear", help="Used to clear gard records")
+ parser_gc.set_defaults(func=gardClear)
+
+ #firmware_flash
+ parser_fw = subparsers.add_parser("firmware", help="Work with the system firmware")
+ fwflash_subproc = parser_fw.add_subparsers(title='subcommands', description='valid firmware commands', help='sub-command help', dest='command')
+ fwflash = fwflash_subproc.add_parser('flash', help="Flash the system firmware")
+ fwflash.add_argument('type', choices=['bmc', 'pnor'], help="image type to flash")
+ fwflash.add_argument('-f', '--fileloc', required=True, help="The absolute path to the firmware image")
+ fwflash.set_defaults(func=fwFlash)
+
+ fwActivate = fwflash_subproc.add_parser('activate', help="Active existing image on the bmc")
+ fwActivate.add_argument('imageID', help="The image ID to activate from the firmware list. Ex: 63c95399")
+ fwActivate.set_defaults(func=activateFWImage)
+
return parser
-"""
- main function for running the command line utility as a sub application
-
-"""
def main(argv=None):
-
-
+ """
+ main function for running the command line utility as a sub application
+ """
parser = createCommandParser()
-
- #host power on/off
- #reboot bmc
- #host current state
- #chassis power
- #error collection - nonipmi
- #clear logs
- #bmc state
-
args = parser.parse_args(argv)
- if (args.askpw):
- pw = getpass.getpass()
- elif(args.PW is not None):
- pw = args.PW
- else:
- print("You must specify a password")
- sys.exit()
totTimeStart = int(round(time.time()*1000))
@@ -1460,29 +1900,48 @@
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if sys.version_info >= (3,0):
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
- if (hasattr(args, 'fileloc') and args.fileloc is not None):
+ if (args.version):
+ print("Version: 1.0")
+ sys.exit(0)
+ if (hasattr(args, 'fileloc') and args.fileloc is not None and 'print' in args.command):
mysess = None
+ print(selPrint('N/A', args, mysess))
else:
- logintimeStart = int(round(time.time()*1000))
- mysess = login(args.host, args.user, pw, args.json)
- logintimeStop = int(round(time.time()*1000))
-
- commandTimeStart = int(round(time.time()*1000))
- output = args.func(args.host, args, mysess)
- commandTimeStop = int(round(time.time()*1000))
- print(output)
- if (mysess is not None):
- logout(args.host, args.user, pw, mysess, args.json)
- if(args.procTime):
- print("Total time: " + str(int(round(time.time()*1000))- totTimeStart))
- print("loginTime: " + str(logintimeStop - logintimeStart))
- print("command Time: " + str(commandTimeStop - commandTimeStart))
+ if(hasattr(args, 'host') and hasattr(args,'user')):
+ if (args.askpw):
+ pw = getpass.getpass()
+ elif(args.PW is not None):
+ pw = args.PW
+ else:
+ print("You must specify a password")
+ sys.exit()
+ logintimeStart = int(round(time.time()*1000))
+ mysess = login(args.host, args.user, pw, args.json)
+ logintimeStop = int(round(time.time()*1000))
+
+ commandTimeStart = int(round(time.time()*1000))
+ output = args.func(args.host, args, mysess)
+ commandTimeStop = int(round(time.time()*1000))
+ print(output)
+ if (mysess is not None):
+ logout(args.host, args.user, pw, mysess, args.json)
+ if(args.procTime):
+ print("Total time: " + str(int(round(time.time()*1000))- totTimeStart))
+ print("loginTime: " + str(logintimeStop - logintimeStart))
+ print("command Time: " + str(commandTimeStop - commandTimeStart))
+ else:
+ print("usage: openbmctool.py [-h] -H HOST -U USER [-A | -P PW] [-j]\n" +
+ "\t[-t POLICYTABLELOC] [-V]\n" +
+ "\t{fru,sensors,sel,chassis,collect_service_data,health_check,dump,bmc,mc,gardclear,firmware}\n" +
+ "\t...\n" +
+ "openbmctool.py: error: the following arguments are required: -H/--host, -U/--user")
+ sys.exit()
-"""
- main function when called from the command line
-
-"""
if __name__ == '__main__':
+ """
+ main function when called from the command line
+
+ """
import sys
isTTY = sys.stdout.isatty()