reguire gnupg1 on f>=31; sense the system to use gpg1 when installed
[nodemanager.git] / coresched_lxc.py
index ff2443e..c6b7f4d 100644 (file)
@@ -1,25 +1,28 @@
-"""Whole core scheduling
-
+"""
+Whole-core scheduling
 """
 
 import logger
 import os
+import os.path
 import cgroups
+from functools import reduce
 
 glo_coresched_simulate = False
 joinpath = os.path.join
 
 class CoreSched:
-    """ Whole-core scheduler
+    """ 
+    Whole-core scheduler
 
-        The main entrypoint is adjustCores(self, slivers) which takes a
-        dictionary of sliver records. The cpu_cores field is pulled from the
-        effective rspec (rec["_rspec"]) for each sliver.
+    The main entrypoint is adjustCores(self, slivers) which takes a
+    dictionary of sliver records. The cpu_cores field is pulled from the
+    effective rspec (rec["_rspec"]) for each sliver.
 
-        If cpu_cores > 0 for a sliver, then that sliver will reserve one or
-        more of the cpu_cores on the machine.
+    If cpu_cores > 0 for a sliver, then that sliver will reserve one or
+    more of the cpu_cores on the machine.
 
-        One core is always left unreserved for system slices.
+    One core is always left unreserved for system slices.
     """
 
     def __init__(self, cgroup_var_name="cpuset.cpus", slice_attr_name="cpu_cores"):
@@ -32,8 +35,9 @@ class CoreSched:
         self.cpu_siblings={}
 
     def get_cgroup_var(self, name=None, subsys=None, filename=None):
-        """ decode cpuset.cpus or cpuset.mems into a list of units that can
-            be reserved.
+        """ 
+        decode cpuset.cpus or cpuset.mems into a list of units that can
+        be reserved.
         """
 
         assert(filename!=None or name!=None)
@@ -64,7 +68,8 @@ class CoreSched:
         return units
 
     def get_cpus(self):
-        """ return a list of available cpu identifiers: [0,1,2,3...]
+        """ 
+        return a list of available cpu identifiers: [0,1,2,3...]
         """
 
         # the cpus never change, so if it's already been computed then don't
@@ -110,9 +115,10 @@ class CoreSched:
         return self.find_cpu_mostsiblings(cpus)
 
     def get_cgroups (self):
-        """ return a list of cgroups
-            this might change as vservers are instantiated, so always compute
-            it dynamically.
+        """ 
+        return a list of cgroups
+        this might change as vservers are instantiated, 
+        so always compute it dynamically.
         """
         return cgroups.get_cgroups()
         #cgroups = []
@@ -123,9 +129,10 @@ class CoreSched:
         #return cgroups
 
     def decodeCoreSpec (self, cores):
-        """ Decode the value of the core attribute. It's a number, followed by
-            an optional letter "b" to indicate besteffort cores should also
-            be supplied.
+        """ 
+        Decode the value of the core attribute. 
+        It's a number, followed by an optional letter "b" to indicate besteffort
+        cores should also be supplied.
         """
         bestEffort = False
 
@@ -141,9 +148,10 @@ class CoreSched:
         return (cores, bestEffort)
 
     def adjustCores (self, slivers):
-        """ slivers is a dict of {sliver_name: rec}
-                rec is a dict of attributes
-                    rec['_rspec'] is the effective rspec
+        """ 
+        slivers is a dict of {sliver_name: rec}
+           rec is a dict of attributes
+           rec['_rspec'] is the effective rspec
         """
 
         cpus = self.get_cpus()[:]
@@ -161,7 +169,7 @@ class CoreSched:
 
         # allocate the cores to the slivers that have them reserved
         # TODO: Need to sort this from biggest cpu_cores to smallest
-        for name, rec in slivers.iteritems():
+        for name, rec in slivers.items():
             rspec = rec["_rspec"]
             cores = rspec.get(self.slice_attr_name, 0)
             (cores, bestEffort) = self.decodeCoreSpec(cores)
@@ -178,7 +186,7 @@ class CoreSched:
                     lastCpu = cpu
 
                     logger.log("CoreSched: allocating unit " + str(cpu) + " to slice " + name)
-                    reservations[name] = reservations.get(name,[]) + [cpu]
+                    reservations[name] = reservations.get(name, []) + [cpu]
 
                     # now find a memory node to go with the cpu
                     if memSchedule:
@@ -186,7 +194,7 @@ class CoreSched:
                         if mem != None:
                             mems.remove(mem)
                             logger.log("CoreSched: allocating memory node " + str(mem) + " to slice " + name)
-                            mem_reservations[name] = mem_reservations.get(name,[]) + [mem]
+                            mem_reservations[name] = mem_reservations.get(name, []) + [mem]
                         else:
                             logger.log("CoreSched: failed to find memory node for cpu" + str(cpu))
 
@@ -197,13 +205,21 @@ class CoreSched:
         reservations["_default"] = cpus[:]
         mem_reservations["_default"] = mems[:]
 
+        freezeList = {}
+
         # now check and see if any of our slices had the besteffort flag
         # set
-        for name, rec in slivers.iteritems():
+        for name, rec in slivers.items():
             rspec = rec["_rspec"]
             cores = rspec.get(self.slice_attr_name, 0)
             (cores, bestEffort) = self.decodeCoreSpec(cores)
 
+            freezable = rspec.get("cpu_freezable", 0)
+            if (cores==0) and (freezable == 1):
+               freezeList[name] = "FROZEN"
+            else:
+               freezeList[name] = "THAWED"
+
             # if the bestEffort flag isn't set then we have nothing to do
             if not bestEffort:
                 continue
@@ -211,22 +227,45 @@ class CoreSched:
             # note that if a reservation is [], then we don't need to add
             # bestEffort cores to it, since it is bestEffort by default.
 
-            if reservations.get(name,[]) != []:
+            if reservations.get(name, []) != []:
                 reservations[name] = reservations[name] + reservations["_default"]
-                mem_reservations[name] = mem_reservations.get(name,[]) + mem_reservations["_default"]
+                mem_reservations[name] = mem_reservations.get(name, []) + mem_reservations["_default"]
                 logger.log("CoreSched: adding besteffort units to " + name + ". new units = " + str(reservations[name]))
 
         self.reserveUnits(self.cgroup_var_name, reservations)
 
         self.reserveUnits(self.cgroup_mem_name, mem_reservations)
 
+        self.freezeUnits("freezer.state", freezeList)
+
+    def freezeUnits (self, var_name, freezeList):
+        for (slicename, freeze) in list(freezeList.items()):
+            try:
+                cgroup_path = cgroups.get_cgroup_path(slicename, 'freezer')
+                logger.verbose("CoreSched: setting freezer for {} to {} - path={} var={}"
+                               .format(slicename,freeze, cgroup_path, var_name))
+                cgroup = os.path.join(cgroup_path, var_name)
+                if not cgroup:
+                    logger.log("Warning: Could not spot 'freezer' cgroup file for slice {} - ignored".format(slicename))
+                    break
+
+                if glo_coresched_simulate:
+                        print("F", cgroup)
+                else:
+                    with open(cgroup, "w") as f:
+                        f.write(freeze)
+            except Exception as e:
+                # the cgroup probably didn't exit...
+                logger.log("CoreSched: exception while setting freeze for {} ({})".format(slicename, e))
+
     def reserveUnits (self, var_name, reservations):
-        """ give a set of reservations (dictionary of slicename:cpuid_list),
-            write those reservations to the appropriate cgroup files.
+        """ 
+        give a set of reservations (dictionary of slicename:cpuid_list),
+        write those reservations to the appropriate cgroup files.
 
-            reservations["_default"] is assumed to be the default reservation
-            for slices that do not reserve cores. It's essentially the leftover
-            cpu cores.
+        reservations["_default"] is assumed to be the default reservation
+        for slices that do not reserve cores. It's essentially the leftover
+        cpu cores.
         """
 
         default = reservations["_default"]
@@ -245,10 +284,9 @@ class CoreSched:
                 cpus = default
 
             if glo_coresched_simulate:
-                print "R", "/dev/cgroup/" + cgroup + "/" + var_name, self.listToRange(cpus)
+                print("R", cgroup + "/" + var_name, self.listToRange(cpus))
             else:
                 cgroups.write(cgroup, var_name, self.listToRange(cpus))
-                #file("/dev/cgroup/" + cgroup + "/" + var_name, "w").write( self.listToRange(cpus) + "\n" )
 
     def reserveDefault (self, var_name, cpus):
         #if not os.path.exists("/etc/vservers/.defaults/cgroup"):
@@ -261,13 +299,15 @@ class CoreSched:
         pass
 
     def listToRange (self, list):
-        """ take a list of items [1,2,3,5,...] and return it as a range: "1-3,5"
-            for now, just comma-separate
+        """ 
+        take a list of items [1,2,3,5,...] and return it as a range: "1-3,5"
+        for now, just comma-separate
         """
         return ",".join( [str(i) for i in list] )
 
     def get_mems(self):
-        """ return a list of available cpu identifiers: [0,1,2,3...]
+        """ 
+        return a list of available cpu identifiers: [0,1,2,3...]
         """
 
         # the cpus never change, so if it's already been computed then don't
@@ -310,8 +350,9 @@ class CoreSched:
         return self.mems
 
     def find_associated_memnode(self, mems, cpu):
-        """ Given a list of memory nodes and a cpu, see if one of the nodes in
-            the list can be used with that cpu.
+        """ 
+        Given a list of memory nodes and a cpu, see if one of the nodes in
+        the list can be used with that cpu.
         """
         for item in mems:
             if cpu in self.mems_map[item]:
@@ -319,8 +360,8 @@ class CoreSched:
         return None
 
     def get_memnode_cpus(self, index):
-        """ for a given memory node, return the CPUs that it is associated
-            with.
+        """
+        for a given memory node, return the CPUs that it is associated with.
         """
         fn = "/sys/devices/system/node/node" + str(index) + "/cpulist"
         if not os.path.exists(fn):
@@ -356,17 +397,17 @@ if __name__=="__main__":
 
     x = CoreSched()
 
-    print "cgroups:", ",".join(x.get_cgroups())
+    print("cgroups:", ",".join(x.get_cgroups()))
 
-    print "cpus:", x.listToRange(x.get_cpus())
-    print "sibling map:"
+    print("cpus:", x.listToRange(x.get_cpus()))
+    print("sibling map:")
     for item in x.get_cpus():
-        print " ", item, ",".join([str(y) for y in x.cpu_siblings.get(item,[])])
+        print(" ", item, ",".join([str(y) for y in x.cpu_siblings.get(item, [])]))
 
-    print "mems:", x.listToRange(x.get_mems())
-    print "cpu to memory map:"
+    print("mems:", x.listToRange(x.get_mems()))
+    print("cpu to memory map:")
     for item in x.get_mems():
-        print " ", item, ",".join([str(y) for y in x.mems_map.get(item,[])])
+        print(" ", item, ",".join([str(y) for y in x.mems_map.get(item, [])]))
 
     rspec_sl_test1 = {"cpu_cores": "1"}
     rec_sl_test1 = {"_rspec": rspec_sl_test1}