creation : creates nagios config from a set of sites
[infrastructure.git] / nagios / configurator / NagiosConfig.py
diff --git a/nagios/configurator/NagiosConfig.py b/nagios/configurator/NagiosConfig.py
new file mode 100644 (file)
index 0000000..d159279
--- /dev/null
@@ -0,0 +1,450 @@
+import os
+import sys
+import xmlrpclib
+
+### downloaded makeHTML from:
+### article
+### http://www.hoboes.com/Mimsy/?ART=128
+### download \
+### http://www.hoboes.com/Mimsy/library/downloads/makeHTML.py.gz
+import makeHTML
+
+# utilities for building comon queries
+import comon_query
+    
+
+class NagiosConfig:
+
+    # static
+    _admin_role=10
+    _pi_role=20
+    # person emails to exclude
+    _exclude_admins={'maint@localhost.localdomain':True}
+
+    # set of services to check on each node
+    # these refer to generic services you must have in generic.cfg
+    _full_node_services = ['ssh',
+                           'ssh-root',
+                           'ssh-pl_conf',
+                           ]
+    _restricted_node_services = ['ssh',
+                                 'planetlab',
+                                 ]
+
+    _verbose = False
+# WARNING : some site names come with wierd unicode chars
+# in this case verbose mode might fail
+#    _verbose = True
+
+    plchost=None
+    xmlrpcserver = None
+    auth=None
+
+    dest_email = None
+
+    # a tree of sites, for access to nodes and contacts
+    siteTree = None
+    # list of admin people
+    allAdmins = {}
+
+    ### record arguments
+    def __init__ (self, plchost, plcid, plcpasswd, plcrole):
+        self.plchost = plchost
+
+        self.auth={}
+        self.auth['Username'] = plcid
+        self.auth['AuthMethod'] = "password"
+        self.auth['AuthString'] = plcpasswd
+        self.auth['Role']=plcrole
+
+    def Auth (self):
+        return self.auth
+
+    ### connect to xml rpc server
+    def Connect (self):
+            
+        try:
+            self.xmlrpcserver = xmlrpclib.Server("https://%s/PLCAPI/"
+                                                 % (self.plchost))
+        except:
+            self.xmlrpcserver = None
+            
+        return self.xmlrpcserver
+
+    def set_verbose (self):
+       self._verbose =  True
+
+    def verbose (self,arg):
+        if (self._verbose):
+            print str(arg)
+
+    def GatherData (self, email, sitefile):
+
+        # if email is None, we NEED to have admin role
+        # otherwise let's dont wait 10 minutes before we fail
+        AdminRoleRequired = "admin role required, or provide -o/-O"
+        if not email:
+            if self.auth['Role'] != 'admin':
+                raise AdminRoleRequired
+
+        self.dest_email = email
+
+        self.verbose ("Gathering")
+        server = self.Connect()
+        auth = self.Auth ()
+
+        if sitefile:
+            # open and parse the file, let's gather site_ids in the process
+            self.verbose("Opening site file %s"%sitefile)
+            siteF=open(sitefile,'r')
+            selected_ids=[]
+            while 1:
+                siteline=siteF.readline()
+                if not siteline:
+                    break
+                selected_ids += [int(siteline.split(' ')[0])]
+            siteF.close()
+            sitesList = server.GetSites(auth,selected_ids)
+        else:
+            # get list of all sites
+            sitesList = server.GetSites(auth)
+        self.verbose("Working on a total of %d sites"%len(sitesList))
+
+        selectedNodeIds = [];
+        for site in sitesList:
+            selectedNodeIds += site['node_ids']
+
+       # get all nodenetworks
+       allNodeNetworks = server.GetNodeNetworks (auth)
+       allNodeNetworksIndex = dict ( [ ( nn['nodenetwork_id'],nn) for nn in allNodeNetworks ] )
+
+        # get list of nodes
+        # might wish to restrict the returned attributes
+        selectedNodes = server.GetNodes(auth,selectedNodeIds,['node_id','hostname','nodenetwork_ids'])
+        self.verbose("Got a total of %d nodes"%len(selectedNodes))
+
+        for node in selectedNodes:
+           if not node['nodenetwork_ids']:
+               print 'WARNING : Node %s has no nodenetwork thus no IP'%node['hostname']
+           nn = allNodeNetworksIndex[node['nodenetwork_ids'][0]]
+           node['ip'] = nn['ip']
+       
+
+        # index it by node_id
+        selectedNodesIndex = dict( [ (node['node_id'],node) for node in selectedNodes ] )
+
+        self.siteTree = {}
+        for site in sitesList :
+            self.verbose( "SITE = %s" % site['name'])
+            site_id=site['site_id']
+            siteHash = {}
+            self.siteTree[site_id] = siteHash
+           node_ids = site['node_ids']
+            self.verbose( "NODES IDS = " + str(node_ids))
+           siteNodes=[ selectedNodesIndex[node_id] for node_id in node_ids ]
+           nodesHash = dict ( [ (node['node_id'],node) for node in siteNodes ] )
+            personsHash = {}
+            siteHash['site'] = site
+            siteHash['nodes'] = nodesHash
+            siteHash['persons'] = personsHash
+        
+        # We are provided a hard-wired e-mail :
+        # dont scan all people (maybe we cannot)
+        if not email:
+            
+            # get list of people
+            allPersons = server.GetPersons (auth, [],['person_id','email',
+                                                     'first_name','last_name'])
+            for person in allPersons:
+                person_id = person['person_id']
+                # keep only PIs
+                roles = server.AdmGetPersonRoles (auth,person_id)
+                is_admin = roles.has_key(str(self._admin_role))
+                is_pi = roles.has_key(str(self._pi_role))
+                if is_admin:
+                    ### handle exclude list
+                    if not self._exclude_admins.has_key(person['email']):
+                        self.allAdmins[person_id]=person
+                elif is_pi:
+                    person_sites=server.AdmGetPersonSites(auth,person_id)
+                    for site_id in person_sites:
+                        personsHash = self.siteTree[site_id]['persons']
+                        personsHash[person_id]=person
+                        person['full_name']=person['first_name']+person['last_name']
+                        self.verbose("Added PI %s" % person['full_name'])
+
+    _define_host="""define host{
+        use                             generic-host
+        host_name                       %s
+        alias                           %s
+        address                         %s
+        contact_groups                  %s_pis
+        }\n"""
+    _define_hostgroup="""define hostgroup{
+        hostgroup_name                  %s
+        alias                           %s
+        members                         %s
+        }\n"""
+    # as of nagios-2.5-2.fc4 - apparently cannot use genericity in contacts
+    _define_contact="""define contact{
+        contact_name                    %s
+        alias                           %s %s
+        email                           %s
+        service_notification_period     24x7
+        host_notification_period        24x7
+        service_notification_options    w,u,c,r
+        host_notification_options       d,r
+        service_notification_commands   notify-by-email
+        host_notification_commands      host-notify-by-email
+        }\n"""
+    _define_contactgroup="""define contactgroup{
+        contactgroup_name               %s_pis
+        alias                           %s
+        members                         %s
+        }\n"""
+    _define_service="""define service{
+        use                             generic-%s
+        host_name                       %s
+        contact_groups                  %s_pis
+        servicegroups                   %s
+        }\n"""
+    _define_servicegroup="""define servicegroup{
+        servicegroup_name               %s
+        alias                           all %s
+        }\n"""
+
+    def WriteNagiosConfig (self, dirname):
+
+        site_ids=self.siteTree.keys()
+        site_ids.sort()
+
+
+        # by default use all avail services
+        node_services = self._full_node_services
+        # but in restricted mode some wont work out
+        if self.dest_email:
+            node_services = self._restricted_node_services
+
+        # generate contact file
+        # with people with admin roles, and also
+        # centraladmin if email was provided 
+        # located in main dir so we can have control on load order
+        filename = "%s/%s.cfg"%(dirname,"planetlab")
+        print ( "Opening %s for writing" % filename)
+        file = open (filename,'w')
+        for admin_id in self.allAdmins.keys():
+            admin=self.allAdmins[admin_id]
+            file.write(self._define_contact%
+                       ('rootadmin','root','admin',admin['email']))
+        if self.dest_email:
+            file.write(self._define_contact%
+                       (self.dest_email,'central','admin',self.dest_email))
+
+        for service in node_services:
+            file.write(self._define_servicegroup
+                       %(service,service))
+        
+        file.close()
+
+        ### manage planetlab subdir
+        planetlabdir="%s/planetlab/"%dirname
+        import os.path
+        if not os.path.isdir (planetlabdir):
+            try:
+                print "Trying to create subdir %s"%planetlabdir
+                os.mkdir (planetlabdir)
+            except Exception,e:
+                print "Failed -- exiting"
+                sys.exit(1)
+
+        import dircache
+        ls = dircache.listdir(planetlabdir)
+        import re
+        m=re.compile('^.*\.cfg$')
+        cfgs=filter(lambda x: m.match(x),ls)
+        if len(cfgs) != 0:
+            print "Warning : you have %d nagios configs left in %s"%(
+                len(cfgs),planetlabdir)
+            print "It's OK to keep them, if that's what you're trying to do"
+
+        ### scan over sites
+        for site_id in site_ids:
+
+            siteHash=self.siteTree[site_id]
+            site = siteHash['site']
+            sitename = site['name']
+            sitebase = site['login_base']
+
+            nodes = siteHash['nodes']
+            node_ids = nodes.keys()
+            node_ids.sort()
+            if len(node_ids) != 0:
+
+                filename = "%s/%s.cfg"%(planetlabdir,sitebase)
+                print ( "Opening %s for writing" % filename)
+                file = open (filename,'w')
+
+                ### without email / use real people
+                if not self.dest_email:
+                    persons = siteHash ['persons']
+
+                    self.verbose("retrieved PIs = %s"%str(persons))
+                        
+                    if len(persons) != 0:
+                        personnames=''
+                        for person_id in persons.keys():
+                            person=persons[person_id]
+                            personname=person['full_name']
+                            if len(personnames) == 0:
+                                personnames = personname
+                            else:
+                                personnames = "%s,%s"%(personnames,personname)
+                            file.write(self._define_contact
+                                       %(personname,
+                                         person['first_name'],
+                                         person['last_name'],
+                                         person['email']))
+
+                        file.write(self._define_contactgroup
+                                   %(sitebase,sitename,personnames))
+                else:
+                    file.write(self._define_contactgroup
+                               %(sitebase,sitename,self.dest_email))
+                    
+
+                nodenames=""
+                for node_id in node_ids:
+                    node = nodes[node_id]
+                    if len(nodenames) == 0:
+                        nodenames = node['hostname']
+                    else:
+                        nodenames = "%s,%s"%(nodenames,node['hostname'])
+                    file.write(self._define_host
+                               %(node['hostname'],node['hostname'],
+                                 node['ip'],sitebase))
+
+                    for service in node_services:
+                        file.write(self._define_service
+                                   %(service,node['hostname'],sitebase,service))
+
+            
+                file.write(self._define_hostgroup
+                           %(sitebase,sitename,nodenames))
+
+                file.close()
+
+#################### static html file with bookmarks to comon site-centric queries
+# a first draft tried to create a column for easying the bookmarking process
+# I did not completely succeed, because
+# the javascript interface for firefox seems to only allow
+# the creation of bookmarks in the sidebar
+# in fact you can bookmark the link, but when you use that bookmark
+# the target url is opened in the sidebar
+# you can easily fix this by editing the bookmark and uncheck 
+# a dialob box, but that's not what we want
+# it's much easier to directly use the 'Bookmark this link'
+# from the firefox menu
+# so let's forget about this altogether
+
+#    ### bookmarks
+#    def ManualBookmark (self, url, text, bookmark_name):
+#        bookmark_link="""<a href='javascript:addToFavorites("%s","%s")'>
+#        <font color='#0000FF' face="Verdana">%s</font></a>"""
+#        # the single quotes in the URL need to be html-encoded 
+#        cleanurl = url.replace ("'","%27")
+#        return bookmark_link%(makeHTML.encode(cleanurl),bookmark_name,text)
+#
+#    bookmark_code="""<script language="JavaScript" type="Text/Javascript">
+#<!-- // Hide script from older browsers
+#     // script by http://www.hypergurl.com
+#     function addToFavorites(url,title) {
+#      if (window.sidebar) { // Mozilla Firefox Bookmark
+#         window.sidebar.addPanel(title, url,"");
+#        } else if (window.external) { // IE Favorite
+#           window.external.AddFavorite(url,title) 
+#        } else {
+#           alert("Sorry! Your browser doesn't support this function."); 
+#        }
+#     }
+#// -->
+#</script>""" ### ' for fooling emacs
+
+    def ComonNodesSelect (self,nodes):
+        names = map(lambda node: node['hostname'],nodes)
+        filters = map (comon_query.filter_node,names)
+        server = comon_query.SERVER
+        return comon_query.full_url(server,'||'.join(filters))
+
+    ### could not find the makeHTML tool for that
+    def ManualAnchor (self,name,value):
+        return '<a name="%s">%s</a>' % (name,value)
+
+    def SiteRow (self,site_id):
+    ### returns a (makeHTML) row for the given site
+
+        siteHash = self.siteTree[site_id]
+        site = siteHash['site']
+        sitename = site['name']
+        sitebase = site['login_base']
+
+        nodes = siteHash['nodes'].values()
+        if len(nodes) == 0 :
+            return None
+        else:
+            row = makeHTML.tableRow()
+            query=self.ComonNodesSelect(nodes)
+            row.addCells([makeHTML.link(content=sitename, url=query),
+                          self.ManualAnchor(sitebase,sitebase),
+#                          self.ManualBookmark(query,
+#                                              "Bookmark it",
+#                                              "Comon on %s"%sitename),
+                          ])
+            return row
+
+    def MakeSiteTable (self, sites):
+        ### create table / sitename
+        table = makeHTML.table ()
+        ### first row
+        head_row = makeHTML.tableRow()
+        head_row.addCells ( ['Site name',
+                             'Site login',
+#                             'Comon query',
+                             ])
+        table.addRow(rowList=[head_row])
+
+        for site in sites: 
+            table.addRow ( rowList=[self.SiteRow(site['site_id'])])
+        return table
+
+    ### Write a single index page that contains one bookmark per site
+    def WriteComonLinks (self, indexname):
+
+        pageHead = makeHTML.part('head')
+        pageHead.addPart('title',content='Comon links for all sites')
+#        pageHead.addPiece(bookmark_code)
+        
+        pageBody = makeHTML.part('body')
+        pageBody.addPart('h1',content='Comon links, per site')
+
+        sites = map(lambda s: s['site'],self.siteTree.values())
+
+        pageBody.addPart('h2',content='By site name')
+
+        sites.sort (lambda s1,s2: cmp(s1['name'],s2['name']))
+        pageBody.addPart('p',self.MakeSiteTable(sites))
+
+        pageBody.addPart('h2',content='By site login')
+
+        sites.sort (lambda s1,s2: cmp(s1['login_base'],s2['login_base']))
+        pageBody.addPart('p',self.MakeSiteTable(sites))
+
+        fullPage = makeHTML.part('html')
+        fullPage.addPiece(pageHead)
+        fullPage.addPiece(pageBody)
+
+        text=fullPage.make()
+
+        print ( "Opening %s for writing" % indexname)
+        index=open (indexname,"w")
+        index.write(text+"\n")
+        index.close()