correct message
[monitor.git] / monitor / wrapper / plc.py
index 783efbc..a78541e 100644 (file)
@@ -12,19 +12,37 @@ import xml, xmlrpclib
 import logging
 import time
 import traceback
+from monitor import database
+
+# note: this needs to be consistent with the value in PLEWWW/planetlab/includes/plc_functions.php
+PENDING_CONSORTIUM_ID = 0
+# not used in monitor
+#APPROVED_CONSORTIUM_ID = 999999
+
 try:
-       import config
+       from monitor import config
        debug = config.debug
+       XMLRPC_SERVER=config.API_SERVER
 except:
        debug = False
+       # NOTE: this host is used by default when there are no auth files.
+       XMLRPC_SERVER="https://boot.planet-lab.org/PLCAPI/"
+
 logger = logging.getLogger("monitor")
        
 class Auth:
-       def __init__(self):
-               self.auth = {'AuthMethod': "anonymous"}
+       def __init__(self, username=None, password=None, **kwargs):
+               if 'session' in kwargs:
+                       self.auth= { 'AuthMethod' : 'session',
+                                       'session' : kwargs['session'] }
+               else:
+                       if username==None and password==None:
+                               self.auth = {'AuthMethod': "anonymous"}
+                       else:
+                               self.auth = {'Username' : username,
+                                                       'AuthMethod' : 'password',
+                                                       'AuthString' : password}
 
-# NOTE: this host is used by default when there are no auth files.
-XMLRPC_SERVER="https://boot.planet-lab.org/PLCAPI/"
 
 # NOTE: by default, use anonymous access, but if auth files are 
 #       configured, use them, with their auth definitions.
@@ -43,7 +61,7 @@ except:
                auth = Auth()
                auth.server = XMLRPC_SERVER
 
-api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
+global_error_count = 0
 
 class PLC:
        def __init__(self, auth, url):
@@ -56,16 +74,137 @@ class PLC:
                if method is None:
                        raise AssertionError("method does not exist")
 
-               return lambda *params : method(self.auth, *params)
+               try:
+                       return lambda *params : method(self.auth, *params)
+               except xmlrpclib.ProtocolError:
+                       traceback.print_exc()
+                       global_error_count += 1
+                       if global_error_count >= 10:
+                               print "maximum error count exceeded; exiting..."
+                               sys.exit(1)
+                       else:
+                               print "%s errors have occurred" % global_error_count
+                       raise Exception("ProtocolError continuing")
 
        def __repr__(self):
                return self.api.__repr__()
 
+
+class CachedPLC(PLC):
+
+       def _param_to_str(self, name, *params):
+               fields = len(params)
+               retstr = ""
+               retstr += "%s-" % name
+               for x in params:
+                       retstr += "%s-" % x
+               return retstr[:-1]
+
+       def __getattr__(self, name):
+               method = getattr(self.api, name)
+               if method is None:
+                       raise AssertionError("method does not exist")
+
+               def run_or_returncached(*params):
+                       cachename = self._param_to_str(name, *params)
+                       #print "cachename is %s" % cachename
+                       if hasattr(config, 'refresh'):
+                               refresh = config.refresh
+                       else:
+                               refresh = False
+
+                       if 'Get' in name:
+                               if not database.cachedRecently(cachename):
+                                       load_old_cache = False
+                                       try:
+                                               values = method(self.auth, *params)
+                                       except:
+                                               print "Call %s FAILED: Using old cached data" % cachename
+                                               load_old_cache = True
+
+                                       if load_old_cache:
+                                               values = database.dbLoad(cachename)
+                                       else:
+                                               database.dbDump(cachename, values)
+
+                                       return values
+                               else:
+                                       values = database.dbLoad(cachename)
+                                       return values
+                       else:
+                               return method(self.auth, *params)
+
+               return run_or_returncached
+
+api = PLC(auth.auth, auth.server)
+cacheapi = CachedPLC(auth.auth, auth.server)
+
+
 def getAPI(url):
        return xmlrpclib.Server(url, verbose=False, allow_none=True)
 
-def getAuthAPI():
-       return PLC(auth.auth, auth.server)
+def getNodeAPI(session):
+       nodeauth = Auth(session=session)
+       return PLC(nodeauth.auth, auth.server)
+
+def getAuthAPI(url=None):
+       if url:
+               return PLC(auth.auth, url)
+       else:
+               return PLC(auth.auth, auth.server)
+
+def getCachedAuthAPI():
+       return CachedPLC(auth.auth, auth.server)
+
+def getSessionAPI(session, server):
+       nodeauth = Auth(session=session)
+       return PLC(nodeauth.auth, server)
+def getUserAPI(username, password, server):
+       auth = Auth(username,password)
+       return PLC(auth.auth, server)
+
+def getTechEmails(loginbase):
+       """
+               For the given site, return all user email addresses that have the 'tech' role.
+       """
+       api = getAuthAPI()
+       # get site details.
+       s = api.GetSites(loginbase)[0]
+       # get people at site
+       p = api.GetPersons(s['person_ids'])
+       # pull out those with the right role.
+       emails = [ person['email'] for person in filter(lambda x: 'tech' in x['roles'], p) ]
+       return emails
+
+def getPIEmails(loginbase):
+       """
+               For the given site, return all user email addresses that have the 'tech' role.
+       """
+       api = getAuthAPI()
+       # get site details.
+       s = api.GetSites(loginbase)[0]
+       # get people at site
+       p = api.GetPersons(s['person_ids'])
+       # pull out those with the right role.
+       emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], p) ]
+       return emails
+
+def getSliceUserEmails(loginbase):
+       """
+               For the given site, return all user email addresses that have the 'tech' role.
+       """
+       api = getAuthAPI()
+       # get site details.
+       s = api.GetSites(loginbase)[0]
+       # get people at site
+       slices = api.GetSlices(s['slice_ids'])
+       people = []
+       for slice in slices:
+               people += api.GetPersons(slice['person_ids'])
+       # pull out those with the right role.
+       emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], people) ]
+       unique_emails = [ x for x in set(emails) ]
+       return unique_emails
 
 '''
 Returns list of nodes in dbg as reported by PLC
@@ -84,19 +223,20 @@ def nodesDbg():
 Returns loginbase for given nodename
 '''
 def siteId(nodename):
-       api = xmlrpclib.Server(auth.server, verbose=False)
-       anon = {'AuthMethod': "anonymous"}
-       site_id = api.GetNodes (anon, {"hostname": nodename}, ['site_id'])
+       api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
+       site_id = api.GetNodes (auth.auth, {"hostname": nodename}, ['site_id'])
        if len(site_id) == 1:
-               loginbase = api.GetSites (anon, site_id[0], ["login_base"])
+               loginbase = api.GetSites (auth.auth, site_id[0], ["login_base"])
                return loginbase[0]['login_base']
+       else:
+               print "Not nodes returned!!!!"
 
 '''
 Returns list of slices for a site.
 '''
 def slices(loginbase):
        siteslices = []
-       api = xmlrpclib.Server(auth.server, verbose=False)
+       api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        sliceids = api.GetSites (auth.auth, {"login_base" : loginbase}, ["slice_ids"])[0]['slice_ids']
        for slice in api.GetSlices(auth.auth, {"slice_id" : sliceids}, ["name"]):
                siteslices.append(slice['name'])
@@ -106,11 +246,14 @@ def slices(loginbase):
 Returns dict of PCU info of a given node.
 '''
 def getpcu(nodename):
-       api = xmlrpclib.Server(auth.server, verbose=False)
+       api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        anon = {'AuthMethod': "anonymous"}
        nodeinfo = api.GetNodes(auth.auth, {"hostname": nodename}, ["pcu_ids", "ports"])[0]
        if nodeinfo['pcu_ids']:
+               print nodeinfo
                sitepcu = api.GetPCUs(auth.auth, nodeinfo['pcu_ids'])[0]
+               print sitepcu
+               print nodeinfo["ports"]
                sitepcu[nodename] = nodeinfo["ports"][0]
                return sitepcu
        else:
@@ -138,6 +281,7 @@ def getSiteNodes(loginbase, fields=None):
                print "getSiteNodes:  %s" % exc
        return nodelist
 
+
 def getPersons(filter=None, fields=None):
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        persons = []
@@ -174,17 +318,33 @@ def getSiteNodes2(loginbase):
 
 def getNodeNetworks(filter=None):
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
-       nodenetworks = api.GetNodeNetworks(auth.auth, filter, None)
+       nodenetworks = api.GetInterfaces(auth.auth, filter, None)
        return nodenetworks
 
 def getNodes(filter=None, fields=None):
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        nodes = api.GetNodes(auth.auth, filter, fields) 
                        #['boot_state', 'hostname', 
-                       #'site_id', 'date_created', 'node_id', 'version', 'nodenetwork_ids',
+                       #'site_id', 'date_created', 'node_id', 'version', 'interface_ids',
                        #'last_updated', 'peer_node_id', 'ssh_rsa_key' ])
        return nodes
 
+
+# Check if the site is a pending site that needs to be approved.
+def isPendingSite(loginbase):
+        api = xmlrpclib.Server(auth.server, verbose=False)
+        try:
+                site = api.GetSites(auth.auth, loginbase)[0]
+        except Exception, exc:
+                login.info("ERROR: No site %s" % loginbase)
+                return False
+
+        if not site['enabled'] and site['ext_consortium_id'] == PENDING_CONSORTIUM_ID:
+                return True
+
+        return False
+
+
 '''
 Sets boot state of a node.
 '''
@@ -217,9 +377,15 @@ def nodePOD(nodename):
 '''
 Freeze all site slices.
 '''
-def suspendSlices(nodename):
+def suspendSiteSlices(loginbase):
+        if isPendingSite(loginbase):
+                msg = "INFO: suspendSiteSlices: Pending Site (%s)" % loginbase
+                print msg
+                logger.info(msg)
+                return
+
        api = xmlrpclib.Server(auth.server, verbose=False)
-       for slice in slices(siteId(nodename)):
+       for slice in slices(loginbase):
                logger.info("Suspending slice %s" % slice)
                try:
                        if not debug:
@@ -227,9 +393,23 @@ def suspendSlices(nodename):
                except Exception, exc:
                        logger.info("suspendSlices:  %s" % exc)
 
-def enableSlices(nodename):
+'''
+Freeze all site slices.
+'''
+def suspendSlices(nodename):
+        loginbase = siteId(nodename)
+        suspendSiteSlices(loginbase)
+
+
+def enableSiteSlices(loginbase):
+        if isPendingSite(loginbase):
+                msg = "INFO: enableSiteSlices: Pending Site (%s)" % loginbase
+                print msg
+                logger.info(msg)
+                return
+
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
-       for slice in slices(siteId(nodename)):
+       for slice in slices(loginbase):
                logger.info("Enabling slices %s" % slice)
                try:
                        if not debug:
@@ -243,9 +423,14 @@ def enableSlices(nodename):
                                                logger.info("Deleted enable=0 attribute from slice %s" % slice)
                                                api.DeleteSliceAttribute(auth.auth, attr['slice_attribute_id'])
                except Exception, exc:
-                       logger.info("enableSlices: %s" % exc)
+                       logger.info("enableSiteSlices: %s" % exc)
                        print "exception: %s" % exc
 
+def enableSlices(nodename):
+        loginbase = siteId(nodename)
+        enableSiteSlices(loginbase)
+
+
 #I'm commenting this because this really should be a manual process.  
 #'''
 #Enable suspended site slices.
@@ -256,34 +441,54 @@ def enableSlices(nodename):
 #              logger.info("Suspending slice %s" % slice)
 #              api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
 #
-def enableSliceCreation(nodename):
+def enableSiteSliceCreation(loginbase):
+        if isPendingSite(loginbase):
+                msg = "INFO: enableSiteSliceCreation: Pending Site (%s)" % loginbase
+                print msg
+                logger.info(msg)
+                return
+
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        try:
-               loginbase = siteId(nodename)
                logger.info("Enabling slice creation for site %s" % loginbase)
                if not debug:
                        logger.info("\tcalling UpdateSite(%s, enabled=True)" % loginbase)
                        api.UpdateSite(auth.auth, loginbase, {'enabled': True})
        except Exception, exc:
-               print "ERROR: enableSliceCreation:  %s" % exc
-               logger.info("ERROR: enableSliceCreation:  %s" % exc)
+               print "ERROR: enableSiteSliceCreation:  %s" % exc
+               logger.info("ERROR: enableSiteSliceCreation:  %s" % exc)
+
+def enableSliceCreation(nodename):
+        loginbase = siteId(nodename)
+        enableSiteSliceCreation(loginbase)
 
 '''
-Removes ability to create slices. Returns previous max_slices
+Removes site's ability to create slices. Returns previous max_slices
 '''
-def removeSliceCreation(nodename):
-       print "removeSliceCreation(%s)" % nodename
+def removeSiteSliceCreation(loginbase):
+        print "removeSiteSliceCreation(%s)" % loginbase
+
+        if isPendingSite(loginbase):
+                msg = "INFO: removeSiteSliceCreation: Pending Site (%s)" % loginbase
+                print msg
+                logger.info(msg)
+                return
+
        api = xmlrpclib.Server(auth.server, verbose=False)
        try:
-               loginbase = siteId(nodename)
-               #numslices = api.GetSites(auth.auth, {"login_base": loginbase}, 
-               #               ["max_slices"])[0]['max_slices']
                logger.info("Removing slice creation for site %s" % loginbase)
                if not debug:
-                       #api.UpdateSite(auth.auth, loginbase, {'max_slices': 0})
                        api.UpdateSite(auth.auth, loginbase, {'enabled': False})
        except Exception, exc:
-               logger.info("removeSliceCreation:  %s" % exc)
+               logger.info("removeSiteSliceCreation:  %s" % exc)
+
+'''
+Removes ability to create slices. Returns previous max_slices
+'''
+def removeSliceCreation(nodename):
+        loginbase = siteId(nodename)
+        removeSiteSliceCreation(loginbase)
+
 
 '''
 QED