Many small updates and fixes:
[monitor.git] / monitor / wrapper / plc.py
index 7d9af2d..00632bf 100644 (file)
@@ -12,10 +12,12 @@ import xml, xmlrpclib
 import logging
 import time
 import traceback
-from monitor import database
+from datetime import datetime
 
+# note: this needs to be consistent with the value in PLEWWW/planetlab/includes/plc_functions.php
 PENDING_CONSORTIUM_ID = 0
-APPROVED_CONSORTIUM_ID = 999999
+# not used in monitor
+#APPROVED_CONSORTIUM_ID = 999999
 
 try:
        from monitor import config
@@ -26,6 +28,21 @@ except:
        # NOTE: this host is used by default when there are no auth files.
        XMLRPC_SERVER="https://boot.planet-lab.org/PLCAPI/"
 
+global_log_api = True
+logging.basicConfig(level=logging.DEBUG,
+                    format='%(asctime)s %(levelname)s %(name)s : %(message)s',
+                    datefmt='%s %Y-%m-%dT%H:%M:%S',
+                    filename='/usr/share/monitor/myops-api-log.log',
+                    filemode='a')
+apilog = logging.getLogger("api")
+
+def log_api_call(name, *params):
+    logstr = "%s(" %name
+    for x in params:
+        logstr += "%s," % x
+    logstr = logstr[:-1] + ")"
+    if global_log_api: apilog.debug(logstr)
+
 logger = logging.getLogger("monitor")
        
 class Auth:
@@ -73,7 +90,11 @@ class PLC:
                        raise AssertionError("method does not exist")
 
                try:
-                       return lambda *params : method(self.auth, *params)
+                       def call_method(aut, *params):
+                               if global_log_api: log_api_call(name, *params)
+                               return method(aut, *params)
+                       return lambda *params : call_method(self.auth, *params)
+                       #return lambda *params : method(self.auth, *params)
                except xmlrpclib.ProtocolError:
                        traceback.print_exc()
                        global_error_count += 1
@@ -88,54 +109,7 @@ class PLC:
                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):
@@ -171,7 +145,11 @@ def getTechEmails(loginbase):
        # 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) ]
+       emails = []
+       for person in filter(lambda x: 'tech' in x['roles'], p):
+               if not isPersonExempt(person['email']):
+                       emails.append(person['email'])
+       #emails = [ person['email'] for person in filter(lambda x: 'tech' in x['roles'], p) ]
        return emails
 
 def getPIEmails(loginbase):
@@ -184,7 +162,11 @@ def getPIEmails(loginbase):
        # 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) ]
+       #emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], p) ]
+       emails = []
+       for person in filter(lambda x: 'pi' in x['roles'], p):
+               if not isPersonExempt(person['email']):
+                       emails.append(person['email'])
        return emails
 
 def getSliceUserEmails(loginbase):
@@ -200,7 +182,13 @@ def getSliceUserEmails(loginbase):
        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) ]
+       #emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], people) ]
+
+       emails = []
+       for person in people:
+               if not isPersonExempt(person['email']):
+                       emails.append(person['email'])
+
        unique_emails = [ x for x in set(emails) ]
        return unique_emails
 
@@ -246,7 +234,11 @@ Returns dict of PCU info of a given node.
 def getpcu(nodename):
        api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
        anon = {'AuthMethod': "anonymous"}
-       nodeinfo = api.GetNodes(auth.auth, {"hostname": nodename}, ["pcu_ids", "ports"])[0]
+        try:
+                nodeinfo = api.GetNodes(auth.auth, {"hostname": nodename}, ["pcu_ids", "ports"])[0]
+        except IndexError:
+                logger.info("Can not find node: %s" % nodename)
+                return False
        if nodeinfo['pcu_ids']:
                print nodeinfo
                sitepcu = api.GetPCUs(auth.auth, nodeinfo['pcu_ids'])[0]
@@ -334,7 +326,7 @@ def isPendingSite(loginbase):
         try:
                 site = api.GetSites(auth.auth, loginbase)[0]
         except Exception, exc:
-                login.info("ERROR: No site %s" % loginbase)
+                logger.info("ERROR: No site %s" % loginbase)
                 return False
 
         if not site['enabled'] and site['ext_consortium_id'] == PENDING_CONSORTIUM_ID:
@@ -376,12 +368,19 @@ def nodePOD(nodename):
 Freeze all site slices.
 '''
 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(loginbase):
                logger.info("Suspending slice %s" % slice)
                try:
                        if not debug:
-                               api.AddSliceAttribute(auth.auth, slice, "enabled", "0")
+                           if not isSliceExempt(slice):
+                                   api.AddSliceTag(auth.auth, slice, "enabled", "0")
                except Exception, exc:
                        logger.info("suspendSlices:  %s" % exc)
 
@@ -389,16 +388,17 @@ def suspendSiteSlices(loginbase):
 Freeze all site slices.
 '''
 def suspendSlices(nodename):
-       api = xmlrpclib.Server(auth.server, verbose=False)
-       for slice in slices(siteId(nodename)):
-               logger.info("Suspending slice %s" % slice)
-               try:
-                       if not debug:
-                               api.AddSliceAttribute(auth.auth, slice, "enabled", "0")
-               except Exception, exc:
-                       logger.info("suspendSlices:  %s" % exc)
+        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(loginbase):
                logger.info("Enabling slices %s" % slice)
@@ -408,34 +408,18 @@ def enableSiteSlices(loginbase):
                                if len(slice_list) == 0:
                                        return
                                slice_id = slice_list[0]['slice_id']
-                               l_attr = api.GetSliceAttributes(auth.auth, {'slice_id': slice_id}, None)
+                               l_attr = api.GetSliceTags(auth.auth, {'slice_id': slice_id}, None)
                                for attr in l_attr:
-                                       if "enabled" == attr['name'] and attr['value'] == "0":
+                                       if "enabled" == attr['tagname'] and attr['value'] == "0":
                                                logger.info("Deleted enable=0 attribute from slice %s" % slice)
-                                               api.DeleteSliceAttribute(auth.auth, attr['slice_attribute_id'])
+                                               api.DeleteSliceTag(auth.auth, attr['slice_tag_id'])
                except Exception, exc:
                        logger.info("enableSiteSlices: %s" % exc)
                        print "exception: %s" % exc
 
 def enableSlices(nodename):
-       api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
-
-       for slice in slices(siteId(nodename)):
-               logger.info("Enabling slices %s" % slice)
-               try:
-                       if not debug:
-                               slice_list = api.GetSlices(auth.auth, {'name': slice}, None)
-                               if len(slice_list) == 0:
-                                       return
-                               slice_id = slice_list[0]['slice_id']
-                               l_attr = api.GetSliceAttributes(auth.auth, {'slice_id': slice_id}, None)
-                               for attr in l_attr:
-                                       if "enabled" == attr['name'] and attr['value'] == "0":
-                                               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)
-                       print "exception: %s" % exc
+        loginbase = siteId(nodename)
+        enableSiteSlices(loginbase)
 
 
 #I'm commenting this because this really should be a manual process.  
@@ -446,51 +430,104 @@ def enableSlices(nodename):
 #      api = xmlrpclib.Server(auth.server, verbose=False)
 #      for slice in  slices(siteId(nodename)):
 #              logger.info("Suspending slice %s" % slice)
-#              api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
+#              api.SliceTagAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
 #
 def enableSiteSliceCreation(loginbase):
-        if isPendingSite(loginbase):
-                msg = "INFO: enableSiteSliceCreation: Pending Site (%s)" % loginbase
-                print msg
-                logger.info(msg)
-                return
+       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:
                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})
+                       site = api.GetSites(auth.auth, loginbase)[0]
+                       if site['enabled'] == False:
+                               logger.info("\tcalling UpdateSite(%s, enabled=True)" % loginbase)
+                               if not isSiteExempt(loginbase):
+                                       api.UpdateSite(auth.auth, loginbase, {'enabled': True})
        except Exception, exc:
                print "ERROR: enableSiteSliceCreation:  %s" % exc
                logger.info("ERROR: enableSiteSliceCreation:  %s" % exc)
 
 def enableSliceCreation(nodename):
-       api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
+        loginbase = siteId(nodename)
+        enableSiteSliceCreation(loginbase)
+
+def areSlicesEnabled(site):
+
        try:
-               loginbase = siteId(nodename)
-                enableSiteSliceCreation(loginbase)
+               slice_list = api.GetSlices(slices(site))
+               if len(slice_list) == 0:
+                       return None
+               for slice in slice_list:
+                       slice_id = slice['slice_id']
+                       l_attr = api.GetSliceTags({'slice_id': slice_id})
+                       for attr in l_attr:
+                               if "enabled" == attr['tagname'] and attr['value'] == "0":
+                                       return False
+
        except Exception, exc:
-               print "ERROR: enableSliceCreation:  %s" % exc
-               logger.info("ERROR: enableSliceCreation:  %s" % exc)
+               pass
+
+       return True
+       
+
+def isSiteEnabled(site):
+    try:
+        site = api.GetSites(site)[0]
+        return site['enabled']
+    except:
+        pass
+
+    return True
+    
+
+def isTagCurrent(tags):
+    if len(tags) > 0:
+        for tag in tags:
+            until = tag['value']
+            if datetime.strptime(until, "%Y%m%d") > datetime.now():
+                # NOTE: the 'exempt_until' time is beyond current time
+                return True
+    return False
+
+def isPersonExempt(email):
+    tags = api.GetPersonTags({'email' : email, 'tagname' : 'exempt_person_until'})
+    return isTagCurrent(tags)
+
+def isNodeExempt(hostname):
+    tags = api.GetNodeTags({'hostname' : hostname, 'tagname' : 'exempt_node_until'})
+    return isTagCurrent(tags)
+
+def isSliceExempt(slicename):
+    tags = api.GetSliceTags({'name' : slicename, 'tagname' : 'exempt_slice_until'})
+    return isTagCurrent(tags)
+
+def isSiteExempt(loginbase):
+    tags = api.GetSiteTags({'login_base' : loginbase, 'tagname' : 'exempt_site_until'})
+    return isTagCurrent(tags)
 
 '''
 Removes site's ability to create slices. Returns previous max_slices
 '''
 def removeSiteSliceCreation(loginbase):
-        print "removeSiteSliceCreation(%s)" % loginbase
+       #print "removeSiteSliceCreation(%s)" % loginbase
 
-        if isPendingSite(loginbase):
-                msg = "INFO: removeSiteSliceCreation: Pending Site (%s)" % loginbase
-                print msg
-                logger.info(msg)
-                return
+       if isPendingSite(loginbase):
+               msg = "INFO: removeSiteSliceCreation: Pending Site (%s)" % loginbase
+               print msg
+               logger.info(msg)
+               return
 
        api = xmlrpclib.Server(auth.server, verbose=False)
        try:
                logger.info("Removing slice creation for site %s" % loginbase)
                if not debug:
-                       api.UpdateSite(auth.auth, loginbase, {'enabled': False})
+                       if not isSiteExempt(loginbase):
+                           api.UpdateSite(auth.auth, loginbase, {'enabled': False})
        except Exception, exc:
                logger.info("removeSiteSliceCreation:  %s" % exc)
 
@@ -498,13 +535,9 @@ def removeSiteSliceCreation(loginbase):
 Removes ability to create slices. Returns previous max_slices
 '''
 def removeSliceCreation(nodename):
-       print "removeSliceCreation(%s)" % nodename
-       api = xmlrpclib.Server(auth.server, verbose=False)
-       try:
-               loginbase = siteId(nodename)
-                removeSiteSliceCreation(loginbase)
-       except Exception, exc:
-               logger.info("removeSliceCreation:  %s" % exc)
+        loginbase = siteId(nodename)
+        removeSiteSliceCreation(loginbase)
+
 
 '''
 QED