I wrote a funny little python script I thought I'd share with the community.

It takes your current init scripts and sets up heartbeat to manage all those services that are started after 'network'. So you can get heartbeat to monitor the services on your machine.

It's not at all well tested, but it is kinda fun, and it worked for me ;-).

If you run it, it produces XML which you can then feed to cibadmin like this:
        1node2heartbeat >output-file-from-the-funny-little-script
        cibadmin -U -x output-file-from-the-funny-little-script

        (you should run the script as root).

Then heartbeat will monitor all your services for you.

It leaves out any that don't seem to support status, or which don't seem to be running at the moment.

At the moment, it only looks at those services started in run level 3.

I've only tested it with SUSE 10.0, but it tries to make only LSB assumptions (and a few other common ones).

It doesn't (yet) stop init from starting them - although that's easy.

There are probably all kinds of things it could do better. But, it's fun, and maybe it's useful.

Then you can use crm_mon and the GUI to watch and control all your local services.

The only caveat you should probably be aware of is that if you stop heartbeat, it will stop all your services. Maybe that's not quite what you had in mind...

Let me know what your reaction to and experience with it is.

        Enjoy!


--
    Alan Robertson <[EMAIL PROTECTED]>

"Openness is the foundation and preservative of friendship... Let me claim from you at all times your undisguised opinions." - William Wilberforce
#!/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  <[EMAIL PROTECTED]>
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'
  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 += '</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'
  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)
_______________________________________________________
Linux-HA-Dev: [email protected]
http://lists.linux-ha.org/mailman/listinfo/linux-ha-dev
Home Page: http://linux-ha.org/

Reply via email to