mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1043 lines
25 KiB
1043 lines
25 KiB
/*
|
|
* Stonith module for BladeCenter via OpenHPI, an implementation of Service
|
|
* Availability Forum's Hardware Platfrom Interface
|
|
*
|
|
* Author: Dave Blaschke <debltc@us.ibm.com>
|
|
*
|
|
* Copyright (c) 2005 International Business Machines
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <lha_internal.h>
|
|
|
|
#define DEVICE "IBM BladeCenter (OpenHPI)"
|
|
|
|
#include "stonith_plugin_common.h"
|
|
|
|
#define PIL_PLUGIN bladehpi
|
|
#define PIL_PLUGIN_S "bladehpi"
|
|
#define PIL_PLUGINLICENSE LICENSE_LGPL
|
|
#define PIL_PLUGINLICENSEURL URL_LGPL
|
|
#include <pils/plugin.h>
|
|
|
|
#include <openhpi/SaHpi.h>
|
|
|
|
/* Maximum number of seconds to wait for host to power off */
|
|
#define MAX_POWEROFF_WAIT 60
|
|
|
|
/* entity_root, the one required plugin parameter */
|
|
#define ST_ENTITYROOT "entity_root"
|
|
|
|
/* String format of entity_root */
|
|
#define SYSTEM_CHASSIS_FMT "{SYSTEM_CHASSIS,%d}"
|
|
|
|
/* soft_reset, the one optional plugin parameter */
|
|
#define ST_SOFTRESET "soft_reset"
|
|
|
|
#define OPENHPIURL "http://www.openhpi.org/"
|
|
|
|
/* OpenHPI resource types of interest to this plugin */
|
|
#define OHRES_NONE 0
|
|
#define OHRES_BLADECENT 1
|
|
#define OHRES_MGMTMOD 2
|
|
#define OHRES_BLADE 3
|
|
|
|
/* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that
|
|
saHpiResourcePowerStateSet can return before the desired state has been
|
|
achieved by the blade. In the SAHPI_POWER_OFF case this is not good,
|
|
as whoever calls this plugin assumes that the power is actually off
|
|
when the plugin returns with a successful return code. Define this
|
|
constant to build code that loops in one second intervals after calling
|
|
saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is
|
|
really off.
|
|
#define IBMBC_WAIT_FOR_OFF */
|
|
|
|
static StonithPlugin * bladehpi_new(const char *);
|
|
static void bladehpi_destroy(StonithPlugin *);
|
|
static const char * bladehpi_getinfo(StonithPlugin *, int);
|
|
static const char ** bladehpi_get_confignames(StonithPlugin *);
|
|
static int bladehpi_status(StonithPlugin *);
|
|
static int bladehpi_reset_req(StonithPlugin *, int, const char *);
|
|
static char ** bladehpi_hostlist(StonithPlugin *);
|
|
static int bladehpi_set_config(StonithPlugin *, StonithNVpair *);
|
|
|
|
static struct stonith_ops bladehpiOps = {
|
|
bladehpi_new, /* Create new STONITH object */
|
|
bladehpi_destroy, /* Destroy STONITH object */
|
|
bladehpi_getinfo, /* Return STONITH info string */
|
|
bladehpi_get_confignames, /* Return configuration parameters */
|
|
bladehpi_set_config, /* Set configuration */
|
|
bladehpi_status, /* Return STONITH device status */
|
|
bladehpi_reset_req, /* Request a reset */
|
|
bladehpi_hostlist, /* Return list of supported hosts */
|
|
};
|
|
|
|
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
|
|
|
|
static const PILPluginImports * PluginImports;
|
|
static PILPlugin * OurPlugin;
|
|
static PILInterface * OurInterface;
|
|
static StonithImports * OurImports;
|
|
static void * interfprivate;
|
|
|
|
|
|
PIL_rc
|
|
PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports);
|
|
|
|
PIL_rc
|
|
PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports)
|
|
{
|
|
/* Force the compiler to do a little type checking */
|
|
(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
|
|
|
|
PluginImports = imports;
|
|
OurPlugin = us;
|
|
|
|
/* Register ourself as a plugin */
|
|
imports->register_plugin(us, &OurPIExports);
|
|
|
|
/* Register our interface implementation */
|
|
return imports->register_interface(us
|
|
, PIL_PLUGINTYPE_S
|
|
, PIL_PLUGIN_S
|
|
, &bladehpiOps
|
|
, NULL /* close */
|
|
, &OurInterface
|
|
, (void *)&OurImports
|
|
, &interfprivate);
|
|
}
|
|
|
|
struct pluginDevice {
|
|
StonithPlugin sp;
|
|
const char * pluginid;
|
|
char * idinfo;
|
|
char * device;
|
|
int softreset;
|
|
GList * hostlist;
|
|
SaHpiVersionT ohver; /* OpenHPI interface version */
|
|
SaHpiSessionIdT ohsession; /* session ID */
|
|
SaHpiUint32T ohrptcnt; /* RPT count for hostlist */
|
|
SaHpiResourceIdT ohdevid; /* device resource ID */
|
|
SaHpiResourceIdT ohsensid; /* sensor resource ID */
|
|
SaHpiSensorNumT ohsensnum; /* sensor number */
|
|
};
|
|
|
|
static const char *pluginid = "BladeCenterDevice-Stonith";
|
|
static const char *NOTpluginID = "IBM BladeCenter device has been destroyed";
|
|
|
|
#include "stonith_config_xml.h"
|
|
|
|
#define XML_ENTITYROOT_SHORTDESC \
|
|
XML_PARM_SHORTDESC_BEGIN("en") \
|
|
ST_ENTITYROOT \
|
|
XML_PARM_SHORTDESC_END
|
|
|
|
#define XML_ENTITYROOT_LONGDESC \
|
|
XML_PARM_LONGDESC_BEGIN("en") \
|
|
"The entity_root of the STONITH device from the OpenHPI config file" \
|
|
XML_PARM_LONGDESC_END
|
|
|
|
#define XML_ENTITYROOT_PARM \
|
|
XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1") \
|
|
XML_ENTITYROOT_SHORTDESC \
|
|
XML_ENTITYROOT_LONGDESC \
|
|
XML_PARAMETER_END
|
|
|
|
#define XML_SOFTRESET_SHORTDESC \
|
|
XML_PARM_SHORTDESC_BEGIN("en") \
|
|
ST_SOFTRESET \
|
|
XML_PARM_SHORTDESC_END
|
|
|
|
#define XML_SOFTRESET_LONGDESC \
|
|
XML_PARM_LONGDESC_BEGIN("en") \
|
|
"Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \
|
|
XML_PARM_LONGDESC_END
|
|
|
|
#define XML_SOFTRESET_PARM \
|
|
XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0") \
|
|
XML_SOFTRESET_SHORTDESC \
|
|
XML_SOFTRESET_LONGDESC \
|
|
XML_PARAMETER_END
|
|
|
|
static const char *bladehpiXML =
|
|
XML_PARAMETERS_BEGIN
|
|
XML_ENTITYROOT_PARM
|
|
XML_SOFTRESET_PARM
|
|
XML_PARAMETERS_END;
|
|
|
|
static int get_resource_type(char *, SaHpiRptEntryT *);
|
|
static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT);
|
|
static int get_bladehpi_hostlist(struct pluginDevice *);
|
|
static void free_bladehpi_hostlist(struct pluginDevice *);
|
|
static int get_num_tokens(char *str);
|
|
|
|
struct blade_info {
|
|
char * name; /* blade name */
|
|
SaHpiResourceIdT resourceId; /* blade resource ID */
|
|
SaHpiCapabilitiesT resourceCaps; /* blade capabilities */
|
|
};
|
|
|
|
|
|
static int
|
|
bladehpi_status(StonithPlugin *s)
|
|
{
|
|
struct pluginDevice * dev;
|
|
SaErrorT ohrc;
|
|
SaHpiDomainInfoT ohdi;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
ERRIFWRONGDEV(s, S_OOPS);
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
/* Refresh the hostlist only if RPTs updated */
|
|
ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
return S_BADCONFIG;
|
|
}
|
|
if (dev->ohrptcnt != ohdi.RptUpdateCount) {
|
|
free_bladehpi_hostlist(dev);
|
|
if (get_bladehpi_hostlist(dev) != S_OK) {
|
|
LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
|
|
, __FUNCTION__);
|
|
return S_BADCONFIG;
|
|
}
|
|
}
|
|
|
|
/* At this point, hostlist is up to date */
|
|
if (dev->ohsensid && dev->ohsensnum) {
|
|
/*
|
|
* For accurate status, need to make a call that goes out to
|
|
* BladeCenter MM because the calls made so far by this
|
|
* function (and perhaps get_bladehpi_hostlist) only retrieve
|
|
* information from memory cached by OpenHPI
|
|
*/
|
|
ohrc = saHpiSensorReadingGet(dev->ohsession
|
|
, dev->ohsensid, dev->ohsensnum, NULL, NULL);
|
|
if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) {
|
|
LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s"
|
|
, __FUNCTION__);
|
|
return S_OOPS;
|
|
}
|
|
}
|
|
|
|
return dev->ohdevid ? S_OK : S_OOPS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the list of hosts configured for this HMC device
|
|
*/
|
|
|
|
static char **
|
|
bladehpi_hostlist(StonithPlugin *s)
|
|
{
|
|
struct pluginDevice * dev;
|
|
int numnames = 0, j;
|
|
char ** ret = NULL;
|
|
GList * node = NULL;
|
|
SaErrorT ohrc;
|
|
SaHpiDomainInfoT ohdi;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
ERRIFWRONGDEV(s, NULL);
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
/* Refresh the hostlist only if RPTs updated */
|
|
ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
return NULL;
|
|
}
|
|
if (dev->ohrptcnt != ohdi.RptUpdateCount) {
|
|
free_bladehpi_hostlist(dev);
|
|
if (get_bladehpi_hostlist(dev) != S_OK) {
|
|
LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
|
|
, __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* At this point, hostlist is up to date */
|
|
numnames = g_list_length(dev->hostlist);
|
|
if (numnames < 0) {
|
|
LOG(PIL_CRIT, "Unconfigured stonith object in %s"
|
|
, __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
|
|
ret = (char **)MALLOC((numnames+1) * sizeof(char *));
|
|
if (ret == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
memset(ret, 0, (numnames+1) * sizeof(char *));
|
|
for (node = g_list_first(dev->hostlist), j = 0
|
|
; NULL != node
|
|
; j++, node = g_list_next(node)) {
|
|
ret[j] = STRDUP(((struct blade_info *)node->data)->name);
|
|
if (ret[j] == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory for strdup in %s"
|
|
, __FUNCTION__);
|
|
stonith_free_hostlist(ret);
|
|
return NULL;
|
|
}
|
|
g_strdown(ret[j]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const char **
|
|
bladehpi_get_confignames(StonithPlugin *s)
|
|
{
|
|
static const char * names[] = {ST_ENTITYROOT, NULL};
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the given host, and obey the request type.
|
|
*/
|
|
|
|
static int
|
|
bladehpi_reset_req(StonithPlugin *s, int request, const char *host)
|
|
{
|
|
GList * node = NULL;
|
|
struct pluginDevice * dev = NULL;
|
|
struct blade_info * bi = NULL;
|
|
SaHpiPowerStateT ohcurstate, ohnewstate;
|
|
SaErrorT ohrc;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called, request=%d, host=%s"
|
|
, __FUNCTION__, request, host);
|
|
}
|
|
|
|
ERRIFWRONGDEV(s, S_OOPS);
|
|
|
|
if (host == NULL) {
|
|
LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__);
|
|
return S_OOPS;
|
|
}
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
for (node = g_list_first(dev->hostlist)
|
|
; node != NULL
|
|
; node = g_list_next(node)) {
|
|
bi = ((struct blade_info *)node->data);
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name);
|
|
}
|
|
|
|
if (!strcasecmp(bi->name, host)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!node || !bi) {
|
|
LOG(PIL_CRIT
|
|
, "Host %s is not configured in this STONITH module, "
|
|
"please check your configuration information", host);
|
|
return S_OOPS;
|
|
}
|
|
|
|
/* Make sure host has proper capabilities for get */
|
|
if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) {
|
|
LOG(PIL_CRIT
|
|
, "Host %s does not have power capability", host);
|
|
return S_OOPS;
|
|
}
|
|
|
|
ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId
|
|
, &ohcurstate);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get host %s power state (%d)"
|
|
, host, ohrc);
|
|
return S_OOPS;
|
|
}
|
|
|
|
switch (request) {
|
|
case ST_POWERON:
|
|
if (ohcurstate == SAHPI_POWER_ON) {
|
|
LOG(PIL_INFO, "Host %s already on", host);
|
|
return S_OK;
|
|
}
|
|
ohnewstate = SAHPI_POWER_ON;
|
|
|
|
break;
|
|
|
|
case ST_POWEROFF:
|
|
if (ohcurstate == SAHPI_POWER_OFF) {
|
|
LOG(PIL_INFO, "Host %s already off", host);
|
|
return S_OK;
|
|
}
|
|
ohnewstate = SAHPI_POWER_OFF;
|
|
|
|
break;
|
|
|
|
case ST_GENERIC_RESET:
|
|
if (ohcurstate == SAHPI_POWER_OFF) {
|
|
ohnewstate = SAHPI_POWER_ON;
|
|
} else {
|
|
ohnewstate = SAHPI_POWER_CYCLE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(PIL_CRIT, "Invalid request argument to %s"
|
|
, __FUNCTION__);
|
|
return S_INVAL;
|
|
}
|
|
|
|
if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) {
|
|
int maxwait;
|
|
|
|
ohrc = saHpiResourcePowerStateSet(dev->ohsession
|
|
, bi->resourceId, SAHPI_POWER_OFF);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to set host %s power state to"
|
|
" OFF (%d)", host, ohrc);
|
|
return S_OOPS;
|
|
}
|
|
|
|
/*
|
|
* Must wait for power off here or subsequent power on request
|
|
* may take place while power is still on and thus ignored
|
|
*/
|
|
maxwait = MAX_POWEROFF_WAIT;
|
|
do {
|
|
maxwait--;
|
|
sleep(1);
|
|
ohrc = saHpiResourcePowerStateGet(dev->ohsession
|
|
, bi->resourceId, &ohcurstate);
|
|
} while ((ohrc == SA_OK)
|
|
&& (ohcurstate != SAHPI_POWER_OFF)
|
|
&& (maxwait > 0));
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "Waited %d seconds for power off"
|
|
, MAX_POWEROFF_WAIT - maxwait);
|
|
}
|
|
|
|
ohrc = saHpiResourcePowerStateSet(dev->ohsession
|
|
, bi->resourceId, SAHPI_POWER_ON);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to set host %s power state to"
|
|
" ON (%d)", host, ohrc);
|
|
return S_OOPS;
|
|
}
|
|
} else {
|
|
/* Make sure host has proper capabilities to reset */
|
|
if ((ohnewstate == SAHPI_POWER_CYCLE) &&
|
|
(!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) {
|
|
LOG(PIL_CRIT
|
|
, "Host %s does not have reset capability"
|
|
, host);
|
|
return S_OOPS;
|
|
}
|
|
|
|
if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession
|
|
, bi->resourceId, ohnewstate)) != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to set host %s power state (%d)"
|
|
, host, ohrc);
|
|
return S_OOPS;
|
|
}
|
|
}
|
|
|
|
#ifdef IBMBC_WAIT_FOR_OFF
|
|
if (ohnewstate == SAHPI_POWER_OFF) {
|
|
int maxwait = MAX_POWEROFF_WAIT;
|
|
|
|
do {
|
|
maxwait--;
|
|
sleep(1);
|
|
ohrc = saHpiResourcePowerStateGet(dev->ohsession
|
|
, bi->resourceId, &ohcurstate);
|
|
} while ((ohrc == SA_OK)
|
|
&& (ohcurstate != SAHPI_POWER_OFF)
|
|
&& (maxwait > 0));
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "Waited %d seconds for power off"
|
|
, MAX_POWEROFF_WAIT - maxwait);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse the information in the given configuration file,
|
|
* and stash it away...
|
|
*/
|
|
|
|
static int
|
|
bladehpi_set_config(StonithPlugin *s, StonithNVpair *list)
|
|
{
|
|
struct pluginDevice * dev = NULL;
|
|
StonithNamesToGet namestocopy [] =
|
|
{ {ST_ENTITYROOT, NULL}
|
|
, {NULL, NULL}
|
|
};
|
|
int rc, i;
|
|
SaErrorT ohrc;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
ERRIFWRONGDEV(s, S_OOPS);
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s conditionally compiled with:"
|
|
#ifdef IBMBC_WAIT_FOR_OFF
|
|
" IBMBC_WAIT_FOR_OFF"
|
|
#endif
|
|
, dev->pluginid);
|
|
}
|
|
|
|
if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
|
|
return rc;
|
|
}
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT
|
|
, namestocopy[0].s_value);
|
|
}
|
|
|
|
if (get_num_tokens(namestocopy[0].s_value) == 1) {
|
|
/* name=value pairs on command line, look for soft_reset */
|
|
const char *softreset =
|
|
OurImports->GetValue(list, ST_SOFTRESET);
|
|
if (softreset != NULL) {
|
|
if (!strcasecmp(softreset, "true") ||
|
|
!strcmp(softreset, "1")) {
|
|
dev->softreset = 1;
|
|
} else if (!strcasecmp(softreset, "false") ||
|
|
!strcmp(softreset, "0")) {
|
|
dev->softreset = 0;
|
|
} else {
|
|
LOG(PIL_CRIT, "Invalid %s %s, must be "
|
|
"true, 1, false or 0"
|
|
, ST_SOFTRESET, softreset);
|
|
FREE(namestocopy[0].s_value);
|
|
return S_OOPS;
|
|
}
|
|
}
|
|
} else {
|
|
/* -p or -F option with args "entity_root [soft_reset]..." */
|
|
char *pch = namestocopy[0].s_value;
|
|
|
|
/* skip over entity_root and null-terminate */
|
|
pch += strcspn(pch, WHITESPACE);
|
|
*pch = EOS;
|
|
|
|
/* skip over white-space up to next token */
|
|
pch++;
|
|
pch += strspn(pch, WHITESPACE);
|
|
if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) {
|
|
dev->softreset = 1;
|
|
} else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) {
|
|
dev->softreset = 0;
|
|
} else {
|
|
LOG(PIL_CRIT, "Invalid %s %s, must be "
|
|
"true, 1, false or 0"
|
|
, ST_SOFTRESET, pch);
|
|
FREE(namestocopy[0].s_value);
|
|
return S_OOPS;
|
|
}
|
|
}
|
|
|
|
dev->device = STRDUP(namestocopy[0].s_value);
|
|
FREE(namestocopy[0].s_value);
|
|
if (dev->device == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__);
|
|
return S_OOPS;
|
|
}
|
|
|
|
if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) ||
|
|
sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) {
|
|
LOG(PIL_CRIT, "Invalid %s %s, must be of format %s"
|
|
, ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
dev->ohver = saHpiVersionGet();
|
|
if (dev->ohver > SAHPI_INTERFACE_VERSION) {
|
|
LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than "
|
|
"one used by plugin (%x), incompatibilites may exist"
|
|
, dev->ohver, SAHPI_INTERFACE_VERSION);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID
|
|
, &dev->ohsession, NULL);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
ohrc = saHpiDiscover(dev->ohsession);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
if (get_bladehpi_hostlist(dev) != S_OK) {
|
|
LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
|
|
, __FUNCTION__);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
static const char *
|
|
bladehpi_getinfo(StonithPlugin *s, int reqtype)
|
|
{
|
|
struct pluginDevice * dev;
|
|
const char * ret;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called, reqtype=%d"
|
|
, __FUNCTION__, reqtype);
|
|
}
|
|
|
|
ERRIFWRONGDEV(s, NULL);
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
switch (reqtype) {
|
|
case ST_DEVICEID:
|
|
ret = dev->idinfo;
|
|
break;
|
|
|
|
case ST_DEVICENAME:
|
|
ret = dev->device;
|
|
break;
|
|
|
|
case ST_DEVICEDESCR:
|
|
ret = "IBM BladeCenter via OpenHPI\n"
|
|
"Use for IBM xSeries systems managed by BladeCenter\n"
|
|
" Required parameter name " ST_ENTITYROOT " is "
|
|
"a string (no white-space) of\n"
|
|
"the format \""SYSTEM_CHASSIS_FMT"\" "
|
|
"which is entity_root of BladeCenter\n"
|
|
"from OpenHPI config file, where %d is a positive "
|
|
"integer\n"
|
|
" Optional parameter name " ST_SOFTRESET " is "
|
|
"true|1 if STONITH device should\n"
|
|
"use soft reset (power cycle) to reset nodes or "
|
|
"false|0 if device should\n"
|
|
"use hard reset (power off, wait, power on); "
|
|
"default is false";
|
|
break;
|
|
|
|
case ST_DEVICEURL:
|
|
ret = OPENHPIURL;
|
|
break;
|
|
|
|
case ST_CONF_XML: /* XML metadata */
|
|
ret = bladehpiXML;
|
|
break;
|
|
|
|
default:
|
|
ret = NULL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* HMC Stonith destructor...
|
|
*/
|
|
|
|
static void
|
|
bladehpi_destroy(StonithPlugin *s)
|
|
{
|
|
struct pluginDevice * dev;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
VOIDERRIFWRONGDEV(s);
|
|
|
|
dev = (struct pluginDevice *)s;
|
|
|
|
dev->pluginid = NOTpluginID;
|
|
if (dev->device) {
|
|
FREE(dev->device);
|
|
dev->device = NULL;
|
|
}
|
|
if (dev->idinfo) {
|
|
FREE(dev->idinfo);
|
|
dev->idinfo = NULL;
|
|
}
|
|
free_bladehpi_hostlist(dev);
|
|
|
|
if (dev->ohsession) {
|
|
saHpiSessionClose(dev->ohsession);
|
|
dev->ohsession = 0;
|
|
}
|
|
|
|
FREE(dev);
|
|
}
|
|
|
|
|
|
static StonithPlugin *
|
|
bladehpi_new(const char *subplugin)
|
|
{
|
|
struct pluginDevice * dev = ST_MALLOCT(struct pluginDevice);
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
|
|
}
|
|
|
|
if (dev == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
dev->pluginid = pluginid;
|
|
dev->device = NULL;
|
|
dev->hostlist = NULL;
|
|
REPLSTR(dev->idinfo, DEVICE);
|
|
if (dev->idinfo == NULL) {
|
|
FREE(dev);
|
|
return NULL;
|
|
}
|
|
dev->sp.s_ops = &bladehpiOps;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__);
|
|
}
|
|
|
|
return ((void *)dev);
|
|
}
|
|
|
|
|
|
static int
|
|
get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT)
|
|
{
|
|
int i, rc = OHRES_NONE;
|
|
int foundBlade = 0, foundExp = 0, foundMgmt = 0;
|
|
int foundRoot = 0, foundOther = 0;
|
|
char rootName[64];
|
|
SaHpiEntityPathT * ohep = &ohRPT->ResourceEntity;
|
|
|
|
if (ohep == NULL || entityRoot == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* First find root of entity path, which is last entity in entry */
|
|
for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) {
|
|
if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Then back up through entries looking for specific entity */
|
|
for (i--; i >= 0; i--) {
|
|
switch (ohep->Entry[i].EntityType) {
|
|
case SAHPI_ENT_SBC_BLADE:
|
|
foundBlade = 1;
|
|
break;
|
|
|
|
case SAHPI_ENT_SYS_EXPANSION_BOARD:
|
|
foundExp = 1;
|
|
break;
|
|
|
|
case SAHPI_ENT_SYS_MGMNT_MODULE:
|
|
if (ohep->Entry[i].EntityLocation == 0) {
|
|
foundMgmt = 1;
|
|
}
|
|
break;
|
|
|
|
case SAHPI_ENT_SYSTEM_CHASSIS:
|
|
snprintf(rootName, sizeof(rootName)
|
|
, SYSTEM_CHASSIS_FMT
|
|
, ohep->Entry[i].EntityLocation);
|
|
if (!strcmp(entityRoot, rootName)) {
|
|
foundRoot = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
foundOther = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We are only interested in specific entities on specific device */
|
|
if (foundRoot) {
|
|
if (foundMgmt && !(foundBlade||foundExp||foundOther)) {
|
|
rc = OHRES_MGMTMOD;
|
|
} else if (!(foundMgmt||foundBlade||foundExp||foundOther)) {
|
|
rc = OHRES_BLADECENT;
|
|
} else if (foundBlade && !foundExp) {
|
|
rc = OHRES_BLADE;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int
|
|
get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid)
|
|
{
|
|
SaErrorT ohrc = SA_OK;
|
|
SaHpiEntryIdT ohnextid;
|
|
SaHpiRdrT ohRDR;
|
|
|
|
ohnextid = SAHPI_FIRST_ENTRY;
|
|
do {
|
|
ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid
|
|
, &ohnextid, &ohRDR);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
} else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) {
|
|
return ohRDR.RdrTypeUnion.SensorRec.Num;
|
|
}
|
|
} while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get RPT update count
|
|
* Loop through all RPT entries
|
|
* If entry is BladeCenter, save resource ID in dev->ohdevid
|
|
* If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid
|
|
* and sensor number in dev->ohsensnum
|
|
* If entry is blade, save blade_info and add to dev->hostlist
|
|
* Get RPT update count
|
|
* If RPT update count changed since start of loop, repeat loop
|
|
* Save RPT update count in dev->ohrptcnt
|
|
*
|
|
* Note that not only does this function update hostlist, it also
|
|
* updates ohrptcnt, ohdevid, ohsensid and ohsensnum. However, with
|
|
* this logic it does not need to be called again until the RPT update
|
|
* count changes.
|
|
*/
|
|
|
|
static int
|
|
get_bladehpi_hostlist(struct pluginDevice *dev)
|
|
{
|
|
struct blade_info * bi;
|
|
SaErrorT ohrc;
|
|
SaHpiEntryIdT ohnextid;
|
|
SaHpiRptEntryT ohRPT;
|
|
SaHpiDomainInfoT ohdi;
|
|
SaHpiUint32T ohupdate;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: called, dev->device=%s"
|
|
, __FUNCTION__, dev->device);
|
|
}
|
|
|
|
if (dev->device == NULL || *dev->device == 0) {
|
|
LOG(PIL_CRIT, "Unconfigured stonith object in %s"
|
|
, __FUNCTION__);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
try_again:
|
|
ohupdate = ohdi.RptUpdateCount;
|
|
dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
|
|
ohnextid = SAHPI_FIRST_ENTRY;
|
|
do {
|
|
char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH];
|
|
int blnum;
|
|
|
|
ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid
|
|
, &ohnextid, &ohRPT);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
free_bladehpi_hostlist(dev);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
switch (get_resource_type(dev->device, &ohRPT)) {
|
|
case OHRES_BLADECENT:
|
|
dev->ohdevid = ohRPT.ResourceId;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "BladeCenter '%s' has id %d"
|
|
, (char*)ohRPT.ResourceTag.Data
|
|
, dev->ohdevid);
|
|
}
|
|
break;
|
|
|
|
case OHRES_MGMTMOD:
|
|
if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){
|
|
dev->ohsensnum = get_sensor_num(dev->ohsession
|
|
, ohRPT.ResourceId);
|
|
|
|
if (dev->ohsensnum) {
|
|
dev->ohsensid = ohRPT.ResourceId;
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG
|
|
, "MgmtModule '%s' has id %d "
|
|
"with sensor #%d"
|
|
, (char*)ohRPT.ResourceTag.Data
|
|
, dev->ohsensid
|
|
, dev->ohsensnum);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OHRES_BLADE:
|
|
if ((bi = (struct blade_info *)
|
|
MALLOC(sizeof(struct blade_info))) == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory in %s"
|
|
, __FUNCTION__);
|
|
free_bladehpi_hostlist(dev);
|
|
return S_OOPS;
|
|
}
|
|
|
|
/*
|
|
* New format consists of "Blade N - name" while older
|
|
* format consists only of "name"; we only need to
|
|
* stash name because ResourceID is the important info
|
|
*/
|
|
if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s"
|
|
, &blnum, blname) == 2) {
|
|
bi->name = STRDUP(blname);
|
|
} else {
|
|
bi->name = STRDUP((char*)ohRPT.ResourceTag.Data);
|
|
}
|
|
if (bi->name == NULL) {
|
|
LOG(PIL_CRIT, "Out of memory for strdup in %s"
|
|
, __FUNCTION__);
|
|
free_bladehpi_hostlist(dev);
|
|
return S_OOPS;
|
|
}
|
|
|
|
bi->resourceId = ohRPT.ResourceId;
|
|
bi->resourceCaps = ohRPT.ResourceCapabilities;
|
|
dev->hostlist = g_list_append(dev->hostlist, bi);
|
|
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "Blade '%s' has id %d, caps %x"
|
|
, bi->name, bi->resourceId, bi->resourceCaps);
|
|
}
|
|
break;
|
|
}
|
|
} while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
|
|
|
|
ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
|
|
if (ohrc != SA_OK) {
|
|
LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
|
|
, __FUNCTION__, ohrc);
|
|
free_bladehpi_hostlist(dev);
|
|
return S_BADCONFIG;
|
|
}
|
|
|
|
if (ohupdate != ohdi.RptUpdateCount) {
|
|
free_bladehpi_hostlist(dev);
|
|
if(Debug){
|
|
LOG(PIL_DEBUG, "Looping through entries again,"
|
|
" count changed from %d to %d"
|
|
, ohupdate, ohdi.RptUpdateCount);
|
|
}
|
|
goto try_again;
|
|
}
|
|
|
|
dev->ohrptcnt = ohupdate;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
free_bladehpi_hostlist(struct pluginDevice *dev)
|
|
{
|
|
if (dev->hostlist) {
|
|
GList *node;
|
|
while (NULL != (node = g_list_first(dev->hostlist))) {
|
|
dev->hostlist =
|
|
g_list_remove_link(dev->hostlist, node);
|
|
FREE(((struct blade_info *)node->data)->name);
|
|
FREE(node->data);
|
|
g_list_free(node);
|
|
}
|
|
dev->hostlist = NULL;
|
|
}
|
|
dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
|
|
}
|
|
|
|
|
|
static int
|
|
get_num_tokens(char *str)
|
|
{
|
|
int namecount = 0;
|
|
|
|
while (*str != EOS) {
|
|
str += strspn(str, WHITESPACE);
|
|
if (*str == EOS)
|
|
break;
|
|
str += strcspn(str, WHITESPACE);
|
|
namecount++;
|
|
}
|
|
return namecount;
|
|
}
|