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 |