-"""Whole core scheduling
-
+"""
+Whole-core scheduling
"""
import logger
import os
+import os.path
import cgroups
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"):
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)
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
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 = []
#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
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()[:]
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:
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))
(cores, bestEffort) = self.decodeCoreSpec(cores)
freezable = rspec.get("cpu_freezable", 0)
- logger.log("CoreSched: " + str(name) + " " + str(freezable) + " " + str(type(freezable)) + " " + str(cores) + " " + str(type(cores)))
if (cores==0) and (freezable == 1):
freezeList[name] = "FROZEN"
else:
# 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.freezeUnits("freezer.state", freezeList)
def freezeUnits (self, var_name, freezeList):
- for (cgroup, freeze) in freezeList.items():
+ for (slicename, freeze) in freezeList.items():
try:
- logger.log("CoreSched: setting freezer for " + cgroup + " to " + freeze)
+ 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", "/dev/cgroup/" + cgroup + "/" + var_name, freeze
+ print "F", cgroup
else:
- #file("/dev/cgroup/" + cgroup + "/" + var_name, "w").write(freeze)
- file("/sys/fs/cgroup/freezer/libvirt/lxc/" + cgroup + "/" + var_name, "w").write(freeze)
- except:
+ 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 " + cgroup)
+ 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"]
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"):
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
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]:
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):
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:"
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}