File: 1node2heartbeat

package info (click to toggle)
pacemaker 1.0.9.1%2Bhg15626-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 34,668 kB
  • ctags: 5,645
  • sloc: xml: 87,444; ansic: 57,853; python: 11,479; sh: 5,255; makefile: 663; perl: 387; sed: 262
file content (326 lines) | stat: -rwxr-xr-x 10,555 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#!/usr/bin/python
#
#	Program to determine current list of enabled services for init state 3
#	and create heartbeat CRM configuration for heartbeat to manage them
#
__copyright__='''
Author: Alan Robertson	<alanr@unix.sh>
Copyright (C) 2006 International Business Machines
'''

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
import os,re
#
#	Here's the plan:
#	Find out the default run level
#	Find out what (additional?) services are enabled in that run level
#	Figure out which of them start after the network (or heartbeat?)
#	Ignore heartbeat :-)
#	Figure out which services supply the $services
#		Look to see if the SUSE /etc/insserv.conf file exists
#			If so, then scan it for who provides the $services
#			defined by the LSB
#		If we're on Red Hat, then make some Red Hat type assumptions
#			(whatever those might be)
#		If we're not, then make some generic assumptions...
#	Scan the init scripts for their dependencies...
#	Eliminate anything at or before 'network'.
#	Create resources corresponding to all active services
#	Include monitor actions for those services
#	that can be started after 'network'
#	Add the start-after dependencies
#
#	Things to consider doing in the future:
#	Constrain them to only run on the local system?
#	Put them all in a convenience group (no colocation, no ordering)
#	Add start and stop timeouts

ServiceKeywords = {}
ServiceMap = {}
ProvidesMap = {}
RequiresMap = {}
SkipMap = {'heartbeat': None,  'random': None}
NoMonitor = {'microcode': None}
PreReqs = ['network']
IgnoreList = []
sysname = os.uname()[1]
InitDir = "/etc/init.d"

def service_is_hb_compatible(service):
  scriptname = os.path.join(InitDir, service)
  command=scriptname + " status >/dev/null 2>&1";
  rc = os.system(command)
  return rc == 0

def find_ordered_services(dir):
  allscripts = os.listdir(dir)
  allscripts.sort()
  services = []
  for entry in allscripts:
    matchobj = re.match("S[0-9]+(.*)", entry)
    if not matchobj:
      continue
    service = matchobj.group(1)
    if SkipMap.has_key(service):
      continue
    if service_is_hb_compatible(service):
      services.append(service)
    else:
      IgnoreList.append(service)
  return services


def register_services(initdir, services):
  for service in services:
    if not ServiceMap.has_key(service):
       ServiceMap[service] = os.path.join(initdir, service)
  for service in services:
    script_dependency_scan(service, os.path.join(initdir, service), ServiceMap)
  
#
#	From the LSB version 3.1: "Comment Conventions for Init Scripts"
#
### BEGIN INIT INFO
### END INIT INFO
#
# The delimiter lines may contain trailing whitespace, which shall be ignored.
# All lines inside the block shall begin with a hash character '#' in the
# first column, so the shell interprets them as comment lines which do not
# affect operation of the script. The lines shall be of the form:
# {keyword}: arg1 [arg2...]
# with exactly one space character between the '#' and the keyword, with a
# single exception. In lines following a line containing the Description
# keyword, and until the next keyword or block ending delimiter is seen,
# a line where the '#' is followed by more than one space or a tab
# character shall be treated as a continuation of the previous line.
#

# Make this a class to avoid recompiling it for each script we scan.
class pats:
  begin=re.compile("###\s+BEGIN\s+INIT\s+INFO")
  end=re.compile("###\s+END\s+INIT\s+INFO")
  desc=re.compile("# Description:\s*(.*)", re.IGNORECASE)
  desc_continue=re.compile("#(  +|\t)\s*(.*)")
  keyword=re.compile("# ([^\s:]+):\s*(.*)\s*\Z")

def script_keyword_scan(filename, servicename):
  keywords = {}
  ST_START=0
  ST_INITINFO=1
  ST_DESCRIPTION=1
  description=""
  state=ST_START

  try:
    fd = open(filename)
  except IOError:
    return keywords

  while 1:
    line = fd.readline()
    if not line:
      break

    if state == ST_START:
       if pats.begin.match(line):
          state = ST_INITINFO
       continue
    if pats.end.match(line):
      break

    if state == ST_DESCRIPTION:
      match = pats.desc_continue.match(line)
      if match:
        description += ("\n" + match.group(2))
        continue
      state = ST_INITINFO

    match = pats.desc.match(line)
    if match:
      state = ST_DESCRIPTION
      description = match.group(1)
      continue

    match = pats.keyword.match(line)
    if match:
      keywords[match.group(1)] = match.group(2)

  # Clean up and return
  fd.close()
  if description != "":
    keywords["Description"] = description
  keywords["_PATHNAME_"] = filename
  keywords["_RESOURCENAME_"] = "R_" + sysname + "_" + servicename
  return keywords

def script_dependency_scan(service, script, servicemap):
  keywords=script_keyword_scan(script, service)
  ServiceKeywords[service] = keywords

SysServiceGuesses = {
  '$local_fs':		['boot.localfs'],
  '$network':		['network'],
  '$named':		['named'],
  '$portmap':		['portmap'],
  '$remote_fs':		['nfs'],
  '$syslog':		['syslog'],
  '$netdaemons':	['portmap', 'inetd'],
  '$time':		['ntp'],
}

#
#	For specific versions of Linux, there are often better ways
#	to do this...
#
#	(e.g., for SUSE Linux, one should look at /etc/insserv.conf file)
#
def map_sys_services(servicemap):
  sysservicemap = {}
  for sysserv in SysServiceGuesses.keys():
    servlist = SysServiceGuesses[sysserv]
    result = []
    for service in servlist:
      if servicemap.has_key(service):
        result.append(service)

    sysservicemap[sysserv] = result
  return sysservicemap

#
#
#
def create_service_dependencies(servicekeywords, systemservicemap):
  dependencies = {}
  for service in servicekeywords.keys():
    if not dependencies.has_key(service):
      dependencies[service] = {}
    for key in ('Required-Start', 'Should-Start'):
      if not servicekeywords[service].has_key(key):
        continue
      for depserv in servicekeywords[service][key].split():
        if systemservicemap.has_key(depserv):
          sysserv = systemservicemap[depserv]
          for serv in sysserv:
            dependencies[service][serv] = None
        else:
          if servicekeywords.has_key(depserv):
            dependencies[service][depserv] = None
    if len(dependencies[service]) == 0:
       del dependencies[service]
  return dependencies

#
#	Modify the service name map to include all the mappings from
#	'Provides' services to real service script names...
#
def map_script_services(sysservmap, servicekeywords):
  for service in servicekeywords.keys():
    if not servicekeywords[service].has_key('Provides'):
      continue
    for provided in servicekeywords[service]['Provides'].split():
      if not sysservmap.has_key(provided):
        sysservmap[provided] = []
      sysservmap[provided].append(service)
  return sysservmap

def create_cib_update(keywords, depmap):
  services =  keywords.keys()
  services.sort()
  result = ""
  # Create the XML for the resources
  result += '<cib>\n'
  result += '<configuration>\n'
  result += '<crm_config/>\n'
  result += '<nodes/>\n'
  result += '<resources>\n'
  groupname="G_" + sysname + "_localinit"
  result += ' <group id="'+groupname+'" ordered="0" collocated="0">\n'
  for service in services:
    rid = keywords[service]["_RESOURCENAME_"]
    monid = "OPmon_" + sysname + '_' + service
    result += \
        '  <primitive id="' + rid + '" class="lsb" type="'+ service +	\
        '">\n' + 							\
        '   <instance_attributes/>\n' +					\
        '   <operations>\n'
    if  not NoMonitor.has_key(service):
      result += \
        '    <op id="' + monid + '" name="monitor" interval="30s" timeout="30s"/>\n'
    result += \
        '   </operations>\n'						\
        '  </primitive>\n'
  result += ' </group>\n'
  result += '</resources>\n'
  services = depmap.keys()
  services.sort()
  result += '<constraints>\n'
  for service in services:
    rid = keywords[service]["_RESOURCENAME_"]
    deps = depmap[service].keys()
    deps.sort()
    for dep in deps:
      if not keywords.has_key(dep):
        continue
      depid = keywords[dep]["_RESOURCENAME_"]
      orderid='O_' + sysname + '_' + service + '_' + dep
      result += ' <rsc_order id="' + orderid + '" from="' + rid + \
		'" to="' + depid + '" type="after"/>\n'
  loc_id="Loc_" + sysname + "_localinit"
  rule_id="LocRule_" + sysname + "_localinit"
  expr_id="LocExp_" + sysname + "_localinit"

  result += ' <rsc_location id="' + loc_id + '" rsc="' + groupname + '">\n'
  result += '  <rule id="' + rule_id + '" score="-INFINITY">\n'
  result += '   <expression attribute="#uname" id="' + expr_id +	\
			'" operation="ne" value="' + sysname + '"/>\n'
  result += '   </rule>\n'
  result += ' </rsc_location>\n'
  result += '</constraints>\n'
  result += '</configuration>\n'
  result += '<status/>\n'
  result += '</cib>\n'
  return result



def remove_a_prereq(service, servicemap, keywords, deps):
  if deps.has_key(service):
    parents = deps[service].keys()
    del deps[service]
  else:
    parents = []
  if servicemap.has_key(service):
    del servicemap[service]
  if keywords.has_key(service):
    del keywords[service]
  for parent in parents:
    if not deps.has_key(parent):
      continue
    remove_a_prereq(parent, servicemap, keywords, deps)
    

def remove_important_prereqs(prereqs, servicemap, keywords, deps):
  # Find everything these important prereqs need and get rid of them...
  for service in prereqs:
    remove_a_prereq(service, servicemap, keywords, deps)

ServiceList = find_ordered_services(os.path.join(InitDir, "rc3.d"))
register_services(InitDir, ServiceList)
SysServiceMap = map_sys_services(ServiceMap)
map_script_services(SysServiceMap, ServiceKeywords)
ServiceDependencies = create_service_dependencies(ServiceKeywords,SysServiceMap)
remove_important_prereqs(PreReqs, SysServiceMap, ServiceKeywords, ServiceDependencies)

print create_cib_update(ServiceKeywords, ServiceDependencies)