Lazy wrote:
Sorry about my previous post, probably gmail did something nasty to it.

Hi,
I'm trying to get my head around OpenIPMI python bindings. And it
isn't going well so far. I want to monitor servers threw lan.
openipmigui.py is complicated, and I failed
to google any other python examples.

Maybe someone here wrote something similar and can share a peace of code ?
Any help will be appreciated.

Yeah, I really need some examples. I'm attaching a small program that plugs into a management system; the plugin resets and controls power on IPMI targets. It should be useful for you as an example, though it doesn't work stand-alone. You still have to know how the basic library works, the Python binding are a relatively thin binding over C. It also has a bunch of daemon-handling that is useful.

If you get a working program, I would love it if it was something that could be included as an example in the OpenIPMI package.

-corey
"""mvtrm_ipmi_resetter - External resource manager plugin for resetting
via IPMI

:version: 1.0.0
:author: Corey Minyard <[EMAIL PROTECTED]>
:organization: MontaVista Software
:copyright: Copyright 2007 MontaVista Software, Inc. All rights reserved.
:requires:
  -	`Python 2.2+ <http://www.python.org>`__
"""

import traceback
from StringIO import StringIO
import mvtrm
import mvtrm.client
import OpenIPMI
import sys
import selector
import threading

ipmi_to_log_level_map = {
	"INFO" : mvtrm.LOG_INFO,
	"WARN" : mvtrm.LOG_WARNING,
	"SEVR" : mvtrm.LOG_SEVERE,
	"FATL" : mvtrm.LOG_FATAL,
	"EINF" : mvtrm.LOG_INFO,
	"DEBG" : mvtrm.LOG_DEBUG
}

domains = { }

class DomainInfo:
	def __init__(self, name, args, handler):
		self.name = name
		self.args = args
		# Turn off stuff we don't need
		self.args.insert(0, '-nofrus')
		self.args.insert(0, '-nosel')
		self.args.insert(0, '-nosetseltime')
		self.first_connect = True
		self.controls = { }
		self.control_handlers = [ handler ]
		self.domain_id = OpenIPMI.open_domain2(name, self.args, self)
		if self.domain_id == None:
			raise RuntimeError("Invalid domain arguments")
		domains[name] = self
		return

	def AddControlHandler(self, handler):
		self.control_handlers.append(handler)
		for c,i in self.controls.items():
			handler.NewControl(c, i)
			pass
		return

	def IterateControls(self, handler):
		for c,i in self.controls.items():
			handler.NewControl(c, i)
			pass
		return

	def RemoveControlHandler(self, handler):
		self.control_handler.remove(handler)
		if len(self.control_handlers) == 0:
			del domains[self.name]
			self.domain_id.to_domain(self)
			pass
		return

	def domain_cb(self, domain):
		domain.close(self)
		return

	def domain_close_done_cb(self):
		return

	def entity_update_cb(self, op, domain, entity):
		if (op == "added"):
			entity.add_control_update_handler(self)
			for c in self.control_handlers:
				c.NewEntity(entity.get_name(),
					    entity.get_id())
				pass
			pass
		elif op == "deleted":
			for c in self.control_handlers:
				c.RemoveEntity(entity.get_name(),
					       entity.get_id())
				pass
			pass
		return

	def entity_control_update_cb(self, op, entity, control):
		if (op == "added"):
			self.controls[control.get_name()] = control.get_id()
			for c in self.control_handlers:
				c.NewControl(control.get_name(),
					     control.get_id())
				pass
			pass
		elif (op == "deleted"):
			del self.controls[control.get_name()]
			for c in self.control_handlers:
				c.RemoveControl(control.get_name(),
						control.get_id())
				pass
			pass
		return

	def conn_change_cb(self, domain, err, conn_num, port_num, connected):
		if self.first_connect:
			self.first_connect = False
			domain.add_entity_update_handler(self)
			pass
		return

	pass

class IPMIResetInfo:
	def __init__(self, client, resource, resetinfo):
		self.client = client
		self.resource = resource
		args = mvtrm.client.lex_split(resetinfo)
		self.domain = args[0]
		del args[0]
		self.__setvalues(args)
		if self.domain not in domains:
			DomainInfo(self.domain, args, self)
			pass
		else:
			domains[self.domain].AddControlHandler(self)
			pass
		return

	def __setvalues(self, args):
		self.reset_control = None
		self.power_control = None
		self.power_entity = None
		i = args[0].find(":")
		while i >= 0:
			t,v = args[0].split(":", 1)
			if (t == "reset"):
				self.reset_control = v
				pass
			elif (t == "power"):
				self.power_control = v
				pass
			elif (t == "powent"):
				self.power_entity = v
				pass
			else:
				raise ValueError("Invalid control: " + t)
			del args[0]
			if len(args) == 0:
				break
			i = args[0].find(":")
			pass
		self.reset_control_id = None
		self.power_control_id = None
		self.power_entity_id = None
		return

	def close(self):
		domains[self.domain].RemoveControlHandler(self)
		return

	def NewControl(self, name, id):
		if self.reset_control == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Found reset control " + name)
			self.reset_control_id = id
			pass
		elif self.power_control == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Found power control " + name)
			self.power_control_id = id
			pass
		return

	def RemoveControl(self, name, id):
		if self.reset_control == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Removed reset control "
						 + name)
			self.reset_control_id = None
			pass
		elif self.power_control == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Removed power control "
						 + name)
			self.power_control_id = None
			pass
		return

	def NewEntity(self, name, id):
		if self.power_entity == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Found power entity " + name)
			self.power_entity_id = id
			pass
		return

	def RemoveEntity(self, name, id):
		if self.power_entity == name:
			self.client.ErrorHandler(mvtrm.LOG_INFO,
						 "Removed power entity "
						 + name)
			self.power_entity_id = None
			pass
		return

	def UpdateResetInfo(self, resetinfo):
		args = mvtrm.client.lex_split(resetinfo)
		domain = args[0]
		del args[0]
		if domain != self.domain:
			domains[self.domain].RemoveControlHandler(self)
			self.domain = domain
			self.__setvalues(args)
			if self.domain not in domains:
				DomainInfo(self.domain, args, self)
				pass
			else:
				domains[self.domain].AddControlHandler(self)
				pass
			pass
		else:
			self.__setvalues(args)
			domains[self.domain].IterateControls(self)
			pass
		return

	def HandleMsg(self, msg, response):
		try:
			mt = msg.GetAttr("msgtype")
			if mt == "IPMIReset":
				self.Reset()
			else:
				v = int(msg['value'])
				self.Power(v)
				pass
			pass
		except Exception, err:
			so = StringIO()
			traceback.print_exc(None, so)
			response.SetAttr('error', so.getvalue())
			pass
		self.client.send(response)
		return

	def Reset(self):
		if self.reset_control_id is None:
			raise RuntimeError("Control %s has not been found"
					   % (str(self.reset_control)))
			return
		self.op = "reset"
		self.rv = 0
		rv = self.reset_control_id.to_control(self)
		if rv == 0:
			rv = self.rv
		if rv != 0:
			raise RuntimeError("Error resetting device: "
					   + OpenIPMI.get_error_string(rv))
		return

	def Power(self, value=0):
		if self.power_control_id is not None:
			self.value = int(value)
			self.op = "power"
			self.rv = 0
			rv = self.power_control_id.to_control(self)
			if rv == 0:
				rv = self.rv
				pass
			pass
		elif self.power_entity_id is not None:
			self.value = int(value)
			self.op = "power"
			self.rv = 0
			rv = self.power_entity_id.to_entity(self)
			if rv == 0:
				rv = self.rv
				pass
			pass
		elif self.power_control:
			raise RuntimeError("Control %s has not been found"
					   % (self.power_control))
		else:
			raise RuntimeError("Entity %s has not been found"
					   % (self.power_entity))
			return
		if rv != 0:
			raise RuntimeError("Error powering device: "
					   + OpenIPMI.get_error_string(rv))
		elif self.value == 0:
			self.client.ResourceAttributeSet(self.resource,
							 { "State" : "off" })
			pass
		else:
			self.client.ResourceAttributeSet(self.resource,
							 { "State" : "on" })
			pass
		return

	def control_cb(self, control):
		if self.op == "reset":
			self.client.ErrorHandler(
				mvtrm.LOG_INFO,
				"reset " + control.get_name())
			self.rv = control.set_val([1])
		elif self.op == "power":
			self.client.ErrorHandler(
				mvtrm.LOG_INFO,
				"power(%s) %s" % (str(self.value),
						  control.get_name()))
			self.rv = control.set_val([self.value])
		return

	def entity_cb(self, entity):
		if self.value:
			# power on
			self.client.ErrorHandler(
				mvtrm.LOG_INFO,
				"activate " + entity.get_name())
			self.rv = entity.set_activation_requested()
			if self.rv:
				self.rv = entity.activate()
			pass
		else:
			# power off
			self.client.ErrorHandler(
				mvtrm.LOG_INFO,
				"deactivate " + entity.get_name())
			self.rv = entity.deactivate()
			pass
		return

	pass

class IPMIResetClient(mvtrm.client.Client):
	def __init__(self, host='localhost', port=None, adminpw="",
		     log=None, sel=None):
		mvtrm.client.Client.__init__(self, host=host, port=port,
					     log=log, sel=sel)

		OpenIPMI.set_log_handler(self)
		OpenIPMI.init()

		self.infos = { }

		self.AdminEnable(adminpw)
		self.initializing = True
		self.initinfoq = [ ]
		self.initmsgq = [ ]

		self.PluginAdd("IPMIReset")
		self.PluginAdd("IPMIPower")

		# Find all the resources that have IPMIResetInfo set in
		# them and configure them.
		mylist = self.ResourceList(('re',
					    ("$", ("string", "IPMIResetInfo")),
					    ("string", ".*")))
		for resource in mylist:
			a = self.ResourceAttributeGet(resource,
						      ["IPMIResetInfo"])
			self.__new_rsrc(resource, a['IPMIResetInfo'])
			pass

		# Play back everything that we received while initializing.
		self.initializing = False
		for m in self.initinfoq:
			self.InfoHandler(m)
			pass
		for m in self.initmsgq:
			self.RequestHandler(m)
		del self.initinfoq
		del self.initmsgq
		return

	# OpenIPMI log handler
	def log(self, level, log):
		self.ErrorHandler(ipmi_to_log_level_map[level], log)
		return

	def __new_rsrc(self, resource, attrs):
		try:
			self.infos[resource] = IPMIResetInfo(self, resource, attrs)
			pass
		except Exception, err:
			so = StringIO()
			traceback.print_exc(None, so)
			self.ErrorHandler(mvtrm.LOG_SEVERE,
					  "Error setting up reset for "
					  + resource + " (" + str(attrs)
					  + "): " + so.getvalue())

			pass
		return

	def __rsrc_change(self, resource, attrs):
		try:
			self.infos[resource].UpdateResetInfo(attrs)
			pass
		except Exception, err:
			so = StringIO()
			traceback.print_exc(None, so)
			self.ErrorHandler(mvtrm.LOG_SEVERE,
					  "Error changing reset info for"
					  + resource + " (" + str(attrs)
					  + "): " + so.getvalue())
			pass
		return

	def InfoHandler(self, msg):
		if self.initializing:
			self.initinfoq.append(msg)
			return

		# Watch for messages that modify IPMIResetInfo
		mt = msg.GetAttr("msgtype")
		if mt == "ResourceRemove":
			r = msg['resource']
			if r in self.infos:
				v = self.infos[r]
				del self.infos[r]
				v.close()
				pass
			pass
		elif mt == "ResourceAdd":
			a = msg['initattr']
			if "IPMIResetInfo" in a:
				r = msg['resource']
				v = a['IPMIResetInfo']
				self.__new_rsrc(r, v)
				pass
			pass
		elif mt == "ResourceAttributeSet":
			a = msg['attributes']
			if "IPMIResetInfo" in a:
				r = msg['resource']
				v = a['IPMIResetInfo']
				if r in self.infos:
					try:
						self.__rsrc_change(r, v)
						pass
					except Exception, err:
						pass
					pass
				else:
					self.__new_rsrc(r, v)
					pass
				pass
			pass
		# Ignore everything else.
		return

	def RequestHandler(self, msg):
		if self.initializing:
			self.initmsgq.append(msg)
			return

		mt = msg.GetAttr("msgtype")

		response = mvtrm.GetRMMsg(msgclass="Response", msgtype=mt)
		response.SetAttr('msgid', msg.GetAttr('msgid'))
		response['plugin_seq'] = msg['plugin_seq']

		try:
			if ((mt == "IPMIReset") or (mt == "IPMIPower")):
				rs = msg["resources"]
				o = msg['plugin_resources_isowner']
				r = rs[0]
				if not msg['plugin_isadmin'] and not o[r]:
					raise RuntimeError(
						("resource %s not "
						 + "owned by client.")
						% (r))
				if r not in self.infos:
					raise RuntimeError(
						("resource %s does "
						 + "not support IPMI "
						 + "reset/power.")
						% (r))

				t = threading.Thread(
					target=self.infos[r].HandleMsg,
					args=(msg, response))
				t.start()
				pass
			else:
				raise ValueError("Invalid message type: " + mt)
			pass
		except Exception, err:
			so = StringIO()
			traceback.print_exc(None, so)
			response.SetAttr('error', so.getvalue())
			self.send(response)
			pass
		return

	pass


DEFAULT_HOST = "localhost"
DEFAULT_PIDFILE = "/var/run/mvtrm_ipmi_resetter.pid"
DEFAULT_PWFILE = "/etc/mvtrm/adminpw"

def usage(argv):
	print ("%s [--port <port>] [--host <host>] [--daemon]"
	       % argv[0])
	print "        [--pwf <file> ] [--user <user>] [--group <group>]"
	print "        [--pidfile <file>] [--syslog] [--savepid]"
	print "  Start the IPMI reset daemon.  This connects to the"
	print "  resource manager, reads information about IPMI resetting"
	print "  from the targets, and sets up to reset/power control the"
	print "  targets.  Options are:"
	print "  --port <port> - Connect to the resource maanger using the"
	print ("    given port.  If the port is not specified use %d."
	       % (mvtrm.RESMGR_PORT))
	print "  --host <host> - Connect to the resource manager on the given"
	print ("    host.  If the host is not specified, use %s."
	       % (DEFAULT_HOST))
	print "  --daemon - run the process as a daemon, double forking,"
	print "    sending the logs to syslog, and storing the pid in the"
	print "    pidfile."
	print "  --pwf <file> - Read the admin password from the given file."
	print "    Only the first line is read.  If not specified, use"
	print ("    %s.  If that file does not exist, use an empty password."
	       % (DEFAULT_PWFILE))
	print "  --user <user> - Switch to the given user after startup."
	print "  --group <group> - Switch to the given group after startup."
	print "  --syslog - Send logs to the syslog."
	print "  --savepid - Save the pid in the pidfile."
	print "  --pidfile <file> - Use the given pidfile instead of"
	print "     %s." % (DEFAULT_PIDFILE)
	return

import os, errno, signal

gotsig = None

def sighandler(sig, frame):
	global gotsig
	gotsig = sig
	return

def idfromname(fn, name):
	"""Read a name's id from a group or password file."""
	rid = None
	try:
		f = open(fn, "r")
		s = f.readline()
		while s:
			try:
				vname, x, vid, rest = s.split(":", 3)
				if name == vname:
					rid = int(vid)
					break
				pass
			except Exception, err:
				pass
			s = f.readline()
			pass
		pass
	except:
		pass
	return rid

def startup(argv):
	global gotsig
	i = 1
	port = None
	host = DEFAULT_HOST
	daemon = False
	log = None
	user = None
	group = None
	savepid = False
	pidfile = DEFAULT_PIDFILE
	pwfile = DEFAULT_PWFILE
	pw = ""
	do_syslog = False
	while i < len(argv):
		if argv[i] == "--port" or argv[i] == '-p':
			i += 1
			if i >= len(argv):
				print "port not given with " + argv[i-1]
				usage(argv)
				return 1
			try:
				port = int(argv[i])
			except:
				print "Port not an integer: " + argv[i]
				usage(argv)
				return 1
			pass
		elif argv[i] == "--host" or argv[i] == '-h':
			i += 1
			if i >= len(argv):
				print "host not given with " + argv[i-1]
				usage(argv)
				return 1
			host = argv[i]
			pass
		elif argv[i] == "--pwf" or argv[i] == '-a':
			i += 1
			if i >= len(argv):
				print "file not given with " + argv[i-1]
				usage(argv)
				return 1
			f = open(argv[1], "r")
			pw = f.readline()
			f.close()
			pw = pw.rstrip("\n")
			pwfile = None
			pass
		elif argv[i] == "--user" or argv[i] == '-u':
			i += 1
			if i >= len(argv):
				print "user not given with " + argv[i-1]
				usage(argv)
				return 1
			user = argv[i]
			pass
		elif argv[i] == "--group" or argv[i] == '-g':
			i += 1
			if i >= len(argv):
				print "group not given with " + argv[i-1]
				usage(argv)
				return 1
			group = argv[i]
			pass
		elif argv[i] == "--pidfile" or argv[i] == '-m':
			i += 1
			if i >= len(argv):
				print "file not given with " + argv[i-1]
				usage(argv)
				return 1
			pidfile = argv[i]
			pass
		elif argv[i] == "--savepid":
			savepid = True
		elif argv[i] == "--syslog":
			do_syslog = True
		elif argv[i] == "--daemon":
			daemon = True
			do_syslog = True
			savepid = True
		elif argv[i] == "--help" or argv[i] == '-?':
			usage(argv)
			return 0
		else:
			print "Invalid parameter: " + argv[i]
			usage(argv)
			return 1
			pass
		i += 1
		pass

	if pwfile:
		# No password file specified, try to read it from the
		# default file.  Don't care if it fails.
		try:
			f = open(pwfile, "r")
			try:
				pw = f.readline()
			except:
				pass
			f.close()
			pw = pw.rstrip("\n")
			pass
		except:
			pass
		pass

	pf = None
	if savepid:
		pid = None
		try:
			pf = open(pidfile, "r")
			try:
				pid = int(pf.readline())
			except ValueError:
				print ("Warning, pidfile has invalid data,"
				       + " ignoring")
				pass
			pf.close()
		except IOError, err:
			if err.errno != errno.ENOENT:
				raise
			pass
		if pid is not None:
			# Pulled the pid from the pidfile, make sure it is not
			# already running.
			try:
				os.kill(pid, 0)
				print ("Daemon is already running as pid %d"
				       % (pid))
				sys.exit(2)
				pass
			except OSError, err:
				if err.errno != errno.ESRCH:
					raise
				print "Warning, process appears to have died."
				pass
			pass

		pf = open(pidfile, "w")
		pass

	sel_logger = None
	if do_syslog:
		import syslog
		class logger:
			def __init__(self):
				self.logmap = { }
				self.logmap[mvtrm.LOG_FATAL] = syslog.LOG_CRIT
				self.logmap[mvtrm.LOG_SEVERE] = syslog.LOG_ERR
				self.logmap[mvtrm.LOG_WARNING] = syslog.LOG_WARNING
				self.logmap[mvtrm.LOG_INFO] = syslog.LOG_INFO
				return

			def log(self, level, err):
				level = self.logmap[level]
				syslog.syslog(level, str(err))
				return

			def selector_log(self, err):
				syslog.syslog(syslog.LOG_CRIT, err)
				return
			pass
		syslog.openlog("mvtrm_ipmi_resetter", 0, syslog.LOG_DAEMON)
		log = logger()
		sel_logger = log.selector_log
		pass

	# Here we have all the files that root would need, go ahead and
	# set the user and group

	orig_uid = None
	if user:
		orig_uid = os.getuid()
		uid = idfromname("/etc/passwd", user)
		if uid is None:
			try:
				uid = int(user)
			except:
				print "Invalid user specified: " + user
				sys.exit(1)
				pass
			pass
		os.setreuid(uid, orig_uid)
		pass

	orig_gid = None
	if group:
		orig_gid = os.getgid()
		gid = idfromname("/etc/group", group)
		if gid is None:
			try:
				gid = int(group)
			except:
				print "Invalid group specified: " + group
				sys.exit(1)
				pass
			pass
		os.setregid(gid, orig_gid)
		pass

	sel = selector.Selector(logger=sel_logger)

	client = IPMIResetClient(host=host, port=port, log=log, adminpw=pw,
				 sel=sel)
	client.ErrorHandler(mvtrm.LOG_INFO,
			    "Starting resource manager IPMI resetter")

	# Don't daemonize until we get evertying started up.
	if daemon:
		# First stop the selector, multiple threads do not survive
		# a fork.
		sel.stop()

		# double-fork magic
		pid = os.fork()
		if pid > 0:
			# parent, exit
			sys.exit(0)
			pass
		os.chdir('/')
		# create a new SID
		os.setsid()
		# do second fork to not be session leader
		pid = os.fork()
		if pid > 0:
			sys.exit(0)
			pass
		# redirect standard pipes
		if hasattr(os,'devnull'):
			redirect=os.devnull
		else:
			redirect='/dev/null'
			pass
		# first close underlining C streams also
		os.close(0)
		os.close(1)
		os.close(2)
		# open with the lowest descriptor, which is 0 (stdin),
		# closed above
		os.open(redirect, os.O_RDWR)
		# now duplicate to stdout and stderr
		os.dup2(0,1)
		os.dup2(0,2)

		sel.start() # Fork is done, restart the selector thread.
		pass

	if pf:
		pf.write(str(os.getpid()))
		pf.close()
		pass

	signal.signal(signal.SIGTERM, sighandler)

	# Wait until time to stop.
	try:
		while not gotsig:
			OpenIPMI.wait_io(1000)
			pass
		pass
	except KeyboardInterrupt:
		pass
	except StandardError, err:
		so = StringIO()
		traceback.print_exc(None, so)
		client.ErrorHandler(mvtrm.LOG_FATAL, so.getvalue())
	except Exception:
		# Let the exit go through
		pass

	if savepid:
		# We may not be able to remove the pidfile as the original
		# user.  So set the user back.
		if orig_uid is not None:
			os.setuid(orig_uid)
			pass
		if orig_gid is not None:
			os.setuid(orig_gid)
			pass

		try:
			os.remove(pidfile)
		except:
			pass
		pass

	client.ErrorHandler(mvtrm.LOG_INFO,
			    "Quitting resource manager IPMI resetter")
	return

if __name__ == "__main__":
	startup(sys.argv)
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Openipmi-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openipmi-developer

Reply via email to