/* * Copyright (c) 2019 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 * * * * @file * Starling-X Common Bmc Utilities */ #include #include #include using namespace std; #include "nodeBase.h" /* for ... mtce-common node definitions */ #include "hostUtil.h" /* for ... mtce-common host definitions */ #include "bmcUtil.h" /* for ... mtce-common bmc utility header */ #include "nodeUtil.h" /* for ... tolowercase */ #include "jsonUtil.h" /* for ... jsonUtil_get_key_value_string */ /********************************************************************** * * Name : bmcUtil_getCmd_str * * Purpose : logging ; bmc request * * Description: return string representing command * * Construct : static array of bmc request strings * * bmcUtil_request_str_array * * Assumptions: initialized in module init * **********************************************************************/ static std::string bmcUtil_request_str_array [BMC_THREAD_CMD__LAST+1] ; string bmcUtil_getCmd_str ( int command ) { if ( command >= BMC_THREAD_CMD__LAST ) { slog ("Invalid thread command (%d)\n", command ); return (bmcUtil_request_str_array[BMC_THREAD_CMD__LAST]); } return (bmcUtil_request_str_array[command]); } /********************************************************************** * * Name : bmcUtil_getAction_str * * Purpose : logging ; bmc action * * Description: return string representing action * * Construct : static array of bmc action strings * * bmcUtil_action_str_array * * Assumptions: initialized in module init * **********************************************************************/ static std::string bmcUtil_action_str_array [BMC_THREAD_CMD__LAST+1] ; string bmcUtil_getAction_str ( int action ) { if ( action >= BMC_THREAD_CMD__LAST ) { slog ("Invalid thread action (%d)\n", action ); return (bmcUtil_action_str_array[BMC_THREAD_CMD__LAST]); } return (bmcUtil_action_str_array[action]); } /********************************************************************** * * Name : bmcUtil_getProtocol_str * * Purpose : logging ; bmc protocol name * * Description: return string representing bmc protocol name * **********************************************************************/ string bmcUtil_getProtocol_str ( bmc_protocol_enum protocol ) { switch (protocol) { case BMC_PROTOCOL__REDFISHTOOL: return(BMC_PROTOCOL__REDFISHTOOL_STR); case BMC_PROTOCOL__IPMITOOL: return(BMC_PROTOCOL__IPMITOOL_STR); default: return("unknown"); } } /********************************************************************** * * Name : bmcUtil_chop_system_req * * Purpose : logging ; reduce the length of the system call * request for logging purposes. * * Warning : Do not use the chopped string to make the system * call request. Only use it for logging. * * Description: return the chopped string. * **********************************************************************/ string bmcUtil_chop_system_req ( string request ) { string chopped_request = "" ; size_t found = request.find(" > ") ; if ( found != string::npos ) chopped_request = request.substr(9,found-9); else chopped_request = request.substr(9); return (chopped_request); } /************************************************************************* * * Name : bmcUtil_init * * Purpose : Initialize various common Board Management support * service functions and aspects. * * Description: Init support for IPMI and Redfish * * Returns : Initialization result ; always PASS (for now) * *************************************************************************/ int bmcUtil_init ( void ) { if ( daemon_is_file_present ( BMC_OUTPUT_DIR ) == false ) daemon_make_dir(BMC_OUTPUT_DIR) ; if ( daemon_is_file_present ( BMC_HWMON_TMP_DIR ) == false ) daemon_make_dir(BMC_HWMON_TMP_DIR) ; ipmiUtil_init (); redfishUtil_init (); #ifdef WANT_FIT_TESTING daemon_make_dir(FIT__INFO_FILEPATH); #endif /* init static strings */ bmcUtil_request_str_array[BMC_THREAD_CMD__POWER_RESET] = "Reset"; bmcUtil_request_str_array[BMC_THREAD_CMD__POWER_ON] = "Power-On"; bmcUtil_request_str_array[BMC_THREAD_CMD__POWER_OFF] = "Power-Off"; bmcUtil_request_str_array[BMC_THREAD_CMD__POWER_CYCLE] = "Power-Cycle"; bmcUtil_request_str_array[BMC_THREAD_CMD__BMC_QUERY] = "Query BMC Root"; bmcUtil_request_str_array[BMC_THREAD_CMD__BMC_INFO] = "Query BMC Info"; bmcUtil_request_str_array[BMC_THREAD_CMD__POWER_STATUS] = "Query Power Status"; bmcUtil_request_str_array[BMC_THREAD_CMD__RESTART_CAUSE] = "Query Reset Reason"; bmcUtil_request_str_array[BMC_THREAD_CMD__BOOTDEV_PXE] = "Netboot"; bmcUtil_request_str_array[BMC_THREAD_CMD__READ_SENSORS] = "Read Sensors"; bmcUtil_request_str_array[BMC_THREAD_CMD__LAST] = "unknown"; bmcUtil_action_str_array[BMC_THREAD_CMD__POWER_RESET] = "resetting"; bmcUtil_action_str_array[BMC_THREAD_CMD__POWER_ON] = "powering on"; bmcUtil_action_str_array[BMC_THREAD_CMD__POWER_OFF] = "powering off"; bmcUtil_action_str_array[BMC_THREAD_CMD__POWER_CYCLE] = "power cycling"; bmcUtil_action_str_array[BMC_THREAD_CMD__BMC_QUERY] = "querying bmc root"; bmcUtil_action_str_array[BMC_THREAD_CMD__BMC_INFO] = "querying bmc info"; bmcUtil_action_str_array[BMC_THREAD_CMD__POWER_STATUS] = "querying power status"; bmcUtil_action_str_array[BMC_THREAD_CMD__RESTART_CAUSE] = "querying reset cause"; bmcUtil_action_str_array[BMC_THREAD_CMD__BOOTDEV_PXE] = "setting next boot dev"; bmcUtil_action_str_array[BMC_THREAD_CMD__READ_SENSORS] = "reading sensors"; bmcUtil_action_str_array[BMC_THREAD_CMD__LAST] = "unknown"; return (PASS); } /************************************************************************* * * Name : bmcUtil_info_init * * Purpose : Initialize the BMC information struct. * * Returns : nothing * *************************************************************************/ void bmcUtil_info_init ( bmc_info_type & bmc_info ) { bmc_info.manufacturer.clear(); bmc_info.manufacturer_id.clear(); bmc_info.product_name.clear(); bmc_info.product_id.clear(); bmc_info.device_id.clear(); bmc_info.fw_version.clear(); bmc_info.hw_version.clear(); bmc_info.power_on = false ; bmc_info.restart_cause.clear() ; /* clear the supported actions lists */ bmc_info.reset_action_list.clear(); bmc_info.power_on_action_list.clear(); bmc_info.power_off_action_list.clear(); } /************************************************************************* * * Name : bmcUtil_hwmon_info * * Purpose : Creates the hardware monitor info file and content. * * Description: The hardware monitor learns the hosts power state and * current bmc protocol being used. * * Future : An extra string is passed in but currently unused. * * Returns : nothing * *************************************************************************/ void bmcUtil_hwmon_info ( string hostname, bmc_protocol_enum proto, bool power_on, string extra ) { /* default the bmc info file */ string bmc_info_path_n_filename = BMC_OUTPUT_DIR + hostname ; /* remove the old BMC info file if present */ daemon_remove_file ( bmc_info_path_n_filename.data() ); /* add the 'protocol' key:val pair */ string info_str = "{\"protocol\":\"" ; if ( proto == BMC_PROTOCOL__REDFISHTOOL ) info_str.append(BMC_PROTOCOL__REDFISHTOOL_STR); else info_str.append(BMC_PROTOCOL__IPMITOOL_STR); /* add the 'power' state key:val pair */ if ( power_on ) info_str.append("\",\"power_state\":\"on\""); else info_str.append("\",\"power_state\":\"off\""); /* add the extra data if it exists */ if ( ! extra.empty () ) info_str.append(extra); /* terminate */ info_str.append ("}"); blog ("%s hwmon info: %s", hostname.c_str(), info_str.c_str()); /* write the data to the file */ daemon_log ( bmc_info_path_n_filename.data(), info_str.data() ); } /***************************************************************************** * * Name : bmcUtil_read_bmc_info * Description : Read power status and protocol from bmc info file * Parameters : hostname - host name power_state - read from file protocol - read from file * Return : true - file exist false - file not exist * *****************************************************************************/ bool bmcUtil_read_bmc_info( string hostname, string & power_state, bmc_protocol_enum & protocol ) { struct json_object *json_obj = NULL; string bmc_info_path_n_filename = BMC_OUTPUT_DIR + hostname ; if ( ! daemon_is_file_present ( bmc_info_path_n_filename.data() )) return (false); string filedata = daemon_read_file (bmc_info_path_n_filename.data()) ; blog ("%s data:%s\n", hostname.c_str(), filedata.data()); json_obj = json_tokener_parse ( (char *)filedata.data() ); if ( json_obj ) { power_state = jsonUtil_get_key_value_string ( json_obj, "power_state" ); if ( strcmp (power_state.data(), BMC_POWER_ON_STATUS) ) power_state = BMC_POWER_OFF_STATUS ; string protocol_str = jsonUtil_get_key_value_string ( json_obj, "protocol" ); if ( strcmp (protocol_str.data(), BMC_PROTOCOL__REDFISHTOOL_STR) ) protocol = BMC_PROTOCOL__IPMITOOL ; else protocol = BMC_PROTOCOL__REDFISHTOOL ; json_object_put(json_obj); ilog ("%s power is %s with bmc communication using %s", hostname.c_str(), power_state.c_str(), bmcUtil_getProtocol_str(protocol).c_str()); return (true); } else { /* Set to default value for power state and protocol */ power_state = BMC_POWER_OFF_STATUS ; protocol = BMC_PROTOCOL__IPMITOOL ; blog ("%s failed to parse bmc info! set to ipmitool by default!\n", hostname.c_str()); return (false); } return (true); } /***************************************************************************** * * Name : bmcUtil_read_hwmond_protocol * Description : Read hwmon protocol from hwmon_hostname_protocol file * Parameters : hostname - host name * Return : bmc protocol * *****************************************************************************/ bmc_protocol_enum bmcUtil_read_hwmond_protocol ( string hostname ) { bmc_protocol_enum protocol = BMC_PROTOCOL__IPMITOOL ; string hwmond_proto_filename = BMC_HWMON_TMP_DIR + hostname ; string proto_str = daemon_read_file ( hwmond_proto_filename.data() ) ; if ( strcmp (proto_str.data(), BMC_PROTOCOL__REDFISHTOOL_STR) ) protocol = BMC_PROTOCOL__REDFISHTOOL ; return protocol; } /***************************************************************************** * * Name : bmcUtil_write_hwmond_protocol * Description : Write hwmon protocol to hwmon_hostname_protocol file * Parameters : hostname - host name protocol - protocol stored to the file * *****************************************************************************/ void bmcUtil_write_hwmond_protocol ( string hostname, bmc_protocol_enum protocol ) { string hwmond_proto_filename = BMC_HWMON_TMP_DIR + hostname ; /* remove old file if present and write current protocol to the file*/ daemon_remove_file ( hwmond_proto_filename.data() ); string proto_str = bmcUtil_getProtocol_str ( protocol ) ; daemon_log ( hwmond_proto_filename.data(), proto_str.data() ); } /************************************************************************* * * Name : bmcUtil_create_pw_file * * Purpose : Create a randomly named password filename * * Description: Create based on protocol. * Add the password info to the file. * Attach filename to thread info. * * Returns : Error status passed through thread info status * and status string * *************************************************************************/ void bmcUtil_create_pw_file ( thread_info_type * info_ptr, string pw_file_content, bmc_protocol_enum protocol ) { string password_tempfile ; info_ptr->password_file.clear (); /* protocol specific output dir */ if ( protocol == BMC_PROTOCOL__REDFISHTOOL ) password_tempfile = REDFISHTOOL_OUTPUT_DIR ; else password_tempfile = IPMITOOL_OUTPUT_DIR ; password_tempfile.append(".") ; password_tempfile.append(program_invocation_short_name); password_tempfile.append("-"); password_tempfile.append(info_ptr->hostname); password_tempfile.append("-"); info_ptr->pw_file_fd = hostUtil_mktmpfile ( info_ptr->hostname, password_tempfile, info_ptr->password_file, pw_file_content ); if ( info_ptr->pw_file_fd <= 0 ) { info_ptr->status_string = "failed to get an open temporary password filedesc" ; info_ptr->status = FAIL_FILE_CREATE ; info_ptr->password_file.clear(); } else { /* clean-up */ if ( info_ptr->pw_file_fd > 0 ) close(info_ptr->pw_file_fd); info_ptr->pw_file_fd = 0 ; info_ptr->status_string = "" ; info_ptr->status = PASS ; } } /************************************************************************* * * Name : bmcUtil_create_data_fn * * Purpose : Create a outout data filename * * Description: Create based on protocol. * * Returns : datafile name as a string * *************************************************************************/ string bmcUtil_create_data_fn ( string & hostname, string file_suffix, bmc_protocol_enum protocol ) { /* create the output filename */ string datafile ; /* protocol specific output dir */ if ( protocol == BMC_PROTOCOL__REDFISHTOOL ) datafile = REDFISHTOOL_OUTPUT_DIR ; else datafile = IPMITOOL_OUTPUT_DIR ; datafile.append(program_invocation_short_name); datafile.append("_"); datafile.append(hostname); /* add the sensor list command */ datafile.append(file_suffix); return ( datafile ); } /************************************************************************* * * Name : bmcUtil_is_power_on * * Purpose : Get power state from query response data. * * Description: Parse a BMC protocol specific response for current * power state. * * Assumptions: supplied power state is not changed on failure. * * Parameters : hostname - string * protocol - BMC_PROTOCOL__REDFISHTOOL | BMC_PROTOCOL__IPMITOOL * response - protocol specific power query response data * * Updates : power_on - updated if response is queried ok * set true if power is on * set false if power is off * * Returns : PASS or * FAIL_NO_DATA , FAIL_JSON_PARSE * *************************************************************************/ int bmcUtil_is_power_on ( string hostname, bmc_protocol_enum protocol, string & response, bool & power_on) { if ( response.empty() ) { wlog ("%s bmc power status query response empty", hostname.c_str()); return (FAIL_NO_DATA); } else if ( protocol == BMC_PROTOCOL__REDFISHTOOL ) { struct json_object *json_obj = json_tokener_parse((char*)response.data()); if ( !json_obj ) { wlog ("%s failed to tokenize bmc info", hostname.c_str()); return (FAIL_JSON_PARSE) ; } else if (tolowercase(jsonUtil_get_key_value_string(json_obj,REDFISH_LABEL__POWER_STATE)) == "on" ) power_on = true ; else power_on = false ; /* free the json object */ json_object_put(json_obj ); } else /* IPMI */ { if ( response.find (IPMITOOL_POWER_ON_STATUS) != std::string::npos ) power_on = true ; else power_on = false ; } return (PASS); } /**************************************************************************** * * Name : bmcUtil_remove_files * * Purpose : cleanup temp files. * * Description: Called during de-provision to remove temporary files created * by host provisioning and command output. * * Function detects which process is calling it and removes * only the temp files that daemon created for a specific host. * * Assumptions: Keeps the temp dirs clean and current. * ****************************************************************************/ extern char *program_invocation_short_name; void bmcUtil_remove_files ( string hostname, bmc_protocol_enum protocol ) { /* Read in the list of config files and their contents */ std::list filelist ; std::list::iterator file_ptr ; string dir = BMC_OUTPUT_DIR ; dir.append(bmcUtil_getProtocol_str(protocol)); int rc = load_filenames_in_dir ( dir.data(), filelist ) ; if ( rc ) { ilog ("%s failed to load files (rc:%d)", hostname.c_str(), rc ); return ; } /* files exist as __ */ if ( !strcmp(MTC_SERVICE_MTCAGENT_NAME, program_invocation_short_name )) { for ( file_ptr = filelist.begin(); file_ptr != filelist.end() ; file_ptr++ ) { if ( file_ptr->find (program_invocation_short_name) != string::npos ) { if ( file_ptr->find (hostname) != string::npos ) { daemon_remove_file ( file_ptr->data() ); blog2 ("%s %s removed", hostname.c_str(), file_ptr->c_str()); } } } } else if ( !strcmp(MTC_SERVICE_HWMOND_NAME, program_invocation_short_name )) { for ( file_ptr = filelist.begin(); file_ptr != filelist.end() ; file_ptr++ ) { if ( file_ptr->find (program_invocation_short_name) != string::npos ) { if ( file_ptr->find (hostname) != string::npos ) { daemon_remove_file ( file_ptr->data() ); blog2 ("%s %s removed", hostname.c_str(), file_ptr->c_str()); } } } /* remove the static file that specified the protocol that was used to create this host's sensor model */ string hwmond_proto_filename = BMC_HWMON_TMP_DIR ; hwmond_proto_filename.append("/") ; hwmond_proto_filename.append(hostname); daemon_remove_file ( hwmond_proto_filename.data() ); } }