~therealmik/+junk/python-ocfra

1 by Michael Samuel
Initial Import
1
###
2
# OCF RA Script Framework
3
# By Michael Samuel <michael.samuel@ipsystems.com.au>
4
# 
5
# OCF Resource Script Class
6
###
7
8
from action import Action
9
from parameter import Parameter, STRING, INTEGER, BOOLEAN
10
from errors import *
11
from xml.etree import ElementTree
12
import os
13
import sys
14
import traceback
5 by Michael Samuel
Use an ordered dict if possible for actions and parameters
15
try:
16
	from collections import OrderedDict as _odict
17
except ImportError:
18
	_odict = dict
1 by Michael Samuel
Initial Import
19
2 by Michael Samuel
Cleanup, added PyDummy resource agent
20
class _ocf_reskeys(dict):
21
	def __init__(self, params):
22
		self._params = params
23
24
	def __missing__(self, key):
25
		p = self._params[key]
1 by Michael Samuel
Initial Import
26
		try:
2 by Michael Samuel
Cleanup, added PyDummy resource agent
27
			return p.datatype.topython(os.environ[p.envname])
1 by Michael Samuel
Initial Import
28
		except KeyError:
2 by Michael Samuel
Cleanup, added PyDummy resource agent
29
			if p.default is not None:
8 by Michael Samuel
Fixed __missing__ paste error
30
				self[p.name] = p.default
31
				return p.default
2 by Michael Samuel
Cleanup, added PyDummy resource agent
32
			else:
33
				raise
34
		
35
	__getattr__ = dict.__getitem__
36
37
class OCFEnvironment(object):
38
	def __init__(self, params):
39
		self.instance = os.environ.get("OCF_RESOURCE_INSTANCE", "__noinstance__")
40
		self.tempdir = os.environ.get("HA_RSCTMP", "/tmp")
41
1 by Michael Samuel
Initial Import
42
		try:
43
			self.check_level = int(os.environ["OCF_CHECK_LEVEL"])
44
		except KeyError:
45
			pass
2 by Michael Samuel
Cleanup, added PyDummy resource agent
46
47
		self.params = _ocf_reskeys(params)
1 by Michael Samuel
Initial Import
48
49
class ResourceAgent(object):
2 by Michael Samuel
Cleanup, added PyDummy resource agent
50
	"""ResourceAgent is the main class for creating your OCF resource agent.
51
	@name The name of your resource agent (`basename $0` in the shell script wrapper)
52
	@version A version string (number?) for this agent.  1.0 would be a good start."""
7 by Michael Samuel
Resource agents need a long and short desc (even though the dtd omits this)
53
	def __init__(self, name, version, longdesc, shortdesc):
1 by Michael Samuel
Initial Import
54
		self.name = name
55
		self.version = version
7 by Michael Samuel
Resource agents need a long and short desc (even though the dtd omits this)
56
		self.longdesc = longdesc
57
		self.shortdesc = shortdesc
5 by Michael Samuel
Use an ordered dict if possible for actions and parameters
58
		self.actions = _odict()
59
		self.parameters = _odict()
1 by Michael Samuel
Initial Import
60
		self.validations = []
61
2 by Michael Samuel
Cleanup, added PyDummy resource agent
62
		self.add_action("meta-data", {"timeout": "10s"}, self.meta_data)
63
		self.add_action("validate-all", {"timeout": "10s"}, self.validate_all)
1 by Michael Samuel
Initial Import
64
		self.add_validation(self.validate_parameters)
65
66
	def add_action(self, *args, **kwargs):
2 by Michael Samuel
Cleanup, added PyDummy resource agent
67
		"""Add an action that the script supports
68
	@name The name of the action (eg. start) - Must have at-least start, stop. meta-data and validate-all are
69
	      provided for you
70
	@parameters A dict of default parameters for this action.  timeout is mandatory.  example: { "timeout": "10s" }
71
	@callback A callable (function or method) to be executed on this action.  Is always an OCFEnvironment object"""
72
		
1 by Michael Samuel
Initial Import
73
		a = Action(*args, **kwargs)
74
		self.actions[a.name] = a
75
76
	def add_parameter(self, *args, **kwargs):
77
		p = Parameter(*args, **kwargs)
2 by Michael Samuel
Cleanup, added PyDummy resource agent
78
		self.parameters[p.name] = p
1 by Michael Samuel
Initial Import
79
80
	def add_validation(self, callback):
81
		self.validations.append(callback)
82
83
	def usage(self):
84
		return "Usage: %s <%s>" % (self.name, "|".join(self.actions.keys()))
85
86
	def run(self, argv):
87
		if isinstance(argv, str):
88
			action = argv
89
		elif len(argv) != 2:
90
			print >>sys.stderr, self.usage()
91
			raise OCF_ERR_UNIMPLEMENTED(self.usage())
92
		else:
93
			action = sys.argv[1]
94
95
		try:
4 by Michael Samuel
Actually print usage info if invalid action called
96
			if not self.actions.has_key(action):
97
				raise OCF_ERR_UNIMPLEMENTED(self.usage())
98
			a = self.actions[action]
2 by Michael Samuel
Cleanup, added PyDummy resource agent
99
			a.callback(OCFEnvironment(self.parameters))
1 by Michael Samuel
Initial Import
100
		except OcfError, e:
101
			if e.message:
102
				print >>sys.stderr, e.message
103
			raise
104
		except Exception, e:
105
			traceback.print_exc(file=sys.stderr)
106
			sys.exit(1)
107
108
	def meta_data(self, env=None):
109
		eResourceAgent = ElementTree.Element("resource-agent", {"name": self.name, "version": self.version})
110
		ElementTree.SubElement(eResourceAgent, "version").text = "1.0"
111
7 by Michael Samuel
Resource agents need a long and short desc (even though the dtd omits this)
112
		ElementTree.SubElement(eResourceAgent, "longdesc", {"lang": "en"}).text = self.longdesc
113
		ElementTree.SubElement(eResourceAgent, "shortdesc", {"lang": "en"}).text = self.shortdesc
114
1 by Michael Samuel
Initial Import
115
		eParameters = ElementTree.SubElement(eResourceAgent, "parameters")
2 by Michael Samuel
Cleanup, added PyDummy resource agent
116
		for p in (p.getelement() for p in self.parameters.itervalues()):
1 by Michael Samuel
Initial Import
117
			eParameters.append(p)
118
119
		eActions = ElementTree.SubElement(eResourceAgent, "actions")
120
		for a in (a.getelement() for a in self.actions.itervalues()):
121
			eActions.append(a)
122
123
		sys.stdout.write("""<?xml version="1.0"?>
124
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
125
""")
126
		ElementTree.ElementTree(eResourceAgent).write(sys.stdout)
127
		sys.stdout.write("\n")
128
129
130
	def validate_all(self, env=None):
2 by Michael Samuel
Cleanup, added PyDummy resource agent
131
		for validator in self.validations:
1 by Michael Samuel
Initial Import
132
			validator(env)
133
134
	def validate_parameters(self, env=None):
2 by Michael Samuel
Cleanup, added PyDummy resource agent
135
		for parameter in self.parameters.itervalues():
1 by Michael Samuel
Initial Import
136
			if not os.environ.has_key(parameter.envname):
137
				if parameter.required:
138
					raise OCF_ERR_CONFIGURED("Required parameter \"%s\" missing" % parameter.name)
139
				continue
140
			try:
141
				value = parameter.datatype.topython(os.environ[parameter.envname])
142
				parameter.validate(value)
143
			except ValueError, e:
144
				raise ErrConfigured("Invalid value for parameter %s: %s", (parameter.name, e.message))
145