X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=slivermanager.py;h=624853e72bc8ad59ce4e9123f38c8f2d2b184717;hb=9e6b9c1ea9e020c55c85b433bac47231d63e9ffd;hp=56f2c48d3bc4ee1ecc85e2d2ce22e1df85191ffb;hpb=164e7fc96baccd6ae5caa57b794fb9966167eca2;p=nodemanager.git diff --git a/slivermanager.py b/slivermanager.py index 56f2c48..624853e 100644 --- a/slivermanager.py +++ b/slivermanager.py @@ -1,6 +1,3 @@ -# $Id$ -# $URL$ - """Sliver manager. The sliver manager has several functions. It is responsible for @@ -10,19 +7,37 @@ also to make inter-sliver resource loans. The sliver manager is also responsible for handling delegation accounts. """ -priority=10 - import string,re +import time import logger -import accounts import api, api_calls import database +import account import controller -import sliver_vs -try: from bwlimit import bwmin, bwmax -except ImportError: bwmin, bwmax = 8, 1000*1000*1000 +try: + import sliver_lxc + implementation='lxc' + sliver_default_type='sliver.LXC' + sliver_class_to_register = sliver_lxc.Sliver_LXC + sliver_password_shell = sliver_lxc.Sliver_LXC.SHELL +except: + try: + import sliver_vs + implementation='vs' + sliver_default_type='sliver.VServer' + sliver_class_to_register = sliver_vs.Sliver_VS + sliver_password_shell = sliver_vs.Sliver_VS.SHELL + except: + logger.log("Could not import either sliver_lxc or sliver_vs - bailing out") + exit(1) + +# just being safe +try : from plnode.bwlimit import bwmin, bwmax +except: bwmin, bwmax = 8, 1000*1000*1000 + +priority=10 DEFAULT_ALLOCATION = { @@ -30,6 +45,8 @@ DEFAULT_ALLOCATION = { # CPU parameters 'cpu_pct': 0, # percent CPU reserved 'cpu_share': 1, # proportional share + 'cpu_cores': "0b", # reserved cpu cores [b] + 'cpu_freezable': 0, # freeze processes if cpu_cores is 0 # bandwidth parameters 'net_min_rate': bwmin / 1000, # kbps 'net_max_rate': bwmax / 1000, # kbps @@ -57,6 +74,53 @@ DEFAULT_ALLOCATION = { start_requested = False # set to True in order to request that all slivers be started +# check leases and adjust the 'reservation_alive' field in slivers +# this is not expected to be saved as it will change for the next round +def adjustReservedSlivers (data): + """ + On reservable nodes, tweak the 'reservation_alive' field to instruct cyclic loop + about what to do with slivers. + """ + # only impacts reservable nodes + if 'reservation_policy' not in data: return + policy=data['reservation_policy'] + if policy not in ['lease_or_idle', 'lease_or_shared']: + if policy is not None: + logger.log ("unexpected reservation_policy %(policy)s"%locals()) + return + + logger.log("slivermanager.adjustReservedSlivers") + now=int(time.time()) + # scan leases that are expected to show in ascending order + active_lease=None + for lease in data['leases']: + if lease['t_from'] <= now and now <= lease['t_until']: + active_lease=lease + break + + def is_system_sliver (sliver): + for d in sliver['attributes']: + if d['tagname']=='system' and d['value']: + return True + return False + + # mark slivers as appropriate + for sliver in data['slivers']: + # system slivers must be kept alive + if is_system_sliver(sliver): + sliver['reservation_alive']=True + continue + + # regular slivers + if not active_lease: + # with 'idle_or_shared', just let the field out, behave like a shared node + # otherwise, mark all slivers as being turned down + if policy == 'lease_or_idle': + sliver['reservation_alive']=False + else: + # there is an active lease, mark it alive and the other not + sliver['reservation_alive'] = sliver['name']==active_lease['name'] + @database.synchronized def GetSlivers(data, config = None, plc=None, fullupdate=True): """This function has two purposes. One, convert GetSlivers() data @@ -82,24 +146,25 @@ def GetSlivers(data, config = None, plc=None, fullupdate=True): if network['is_primary'] and network['bwlimit'] is not None: DEFAULT_ALLOCATION['net_max_rate'] = network['bwlimit'] / 1000 - # Take intscripts (global) returned by API, make dict + # Take initscripts (global) returned by API, build a hash scriptname->code + iscripts_hash = {} if 'initscripts' not in data: logger.log_missing_data("slivermanager.GetSlivers",'initscripts') return - initscripts = {} - for is_rec in data['initscripts']: - logger.verbose("slivermanager: initscript: %s" % is_rec['name']) - initscripts[str(is_rec['name'])] = is_rec['script'] + for initscript_rec in data['initscripts']: + logger.verbose("slivermanager: initscript: %s" % initscript_rec['name']) + iscripts_hash[str(initscript_rec['name'])] = initscript_rec['script'] + adjustReservedSlivers (data) for sliver in data['slivers']: logger.verbose("slivermanager: %s: slivermanager.GetSlivers in slivers loop"%sliver['name']) rec = sliver.copy() rec.setdefault('timestamp', data['timestamp']) # convert attributes field to a proper dict - attr_dict = {} - for attr in rec.pop('attributes'): attr_dict[attr['tagname']] = attr['value'] - rec.setdefault("attributes", attr_dict) + attributes = {} + for attr in rec.pop('attributes'): attributes[attr['tagname']] = attr['value'] + rec.setdefault("attributes", attributes) # squash keys keys = rec.pop('keys') @@ -109,54 +174,80 @@ def GetSlivers(data, config = None, plc=None, fullupdate=True): ## instantiation here, but i suppose its the same thing when you think about it. -FA # Handle nm-controller here if rec['instantiation'].lower() == 'nm-controller': - rec.setdefault('type', attr_dict.get('type', 'controller.Controller')) + rec.setdefault('type', attributes.get('type', 'controller.Controller')) else: - rec.setdefault('type', attr_dict.get('type', 'sliver.VServer')) + rec.setdefault('type', attributes.get('type', sliver_default_type)) # set the vserver reference. If none, set to default. - rec.setdefault('vref', attr_dict.get('vref', 'default')) + rec.setdefault('vref', attributes.get('vref', 'default')) - # set initscripts. first check if exists, if not, leave empty. - is_name = attr_dict.get('initscript') - if is_name is not None and is_name in initscripts: - rec['initscript'] = initscripts[is_name] + ### set initscripts; set empty rec['initscript'] if not + # if tag 'initscript_code' is set, that's what we use + iscode = attributes.get('initscript_code','') + if iscode: + rec['initscript']=iscode else: - rec['initscript'] = '' + isname = attributes.get('initscript') + if isname is not None and isname in iscripts_hash: + rec['initscript'] = iscripts_hash[isname] + else: + rec['initscript'] = '' # set delegations, if none, set empty - rec.setdefault('delegations', attr_dict.get("delegations", [])) + rec.setdefault('delegations', attributes.get("delegations", [])) # extract the implied rspec rspec = {} rec['rspec'] = rspec - for resname, default_amt in DEFAULT_ALLOCATION.iteritems(): + for resname, default_amount in DEFAULT_ALLOCATION.iteritems(): try: - t = type(default_amt) - amt = t.__new__(t, attr_dict[resname]) - except (KeyError, ValueError): amt = default_amt - rspec[resname] = amt + t = type(default_amount) + amount = t.__new__(t, attributes[resname]) + except (KeyError, ValueError): amount = default_amount + rspec[resname] = amount # add in sysctl attributes into the rspec - for key in attr_dict.keys(): + for key in attributes.keys(): if key.find("sysctl.") == 0: - rspec[key] = attr_dict[key] + rspec[key] = attributes[key] + + # also export tags in rspec so they make it to the sliver_vs.start call + rspec['tags']=attributes database.db.deliver_record(rec) if fullupdate: database.db.set_min_timestamp(data['timestamp']) # slivers are created here. database.db.sync() - accounts.Startingup = False -def deliver_ticket(data): +def deliver_ticket(data): return GetSlivers(data, fullupdate=False) -def start(options, config): - for resname, default_amt in sliver_vs.DEFAULT_ALLOCATION.iteritems(): - DEFAULT_ALLOCATION[resname]=default_amt - - accounts.register_class(sliver_vs.Sliver_VS) - accounts.register_class(controller.Controller) - accounts.Startingup = options.startup +def start(): + # No default allocation values for LXC yet, think if its necessary given + # that they are also default allocation values in this module + if implementation == 'vs': + for resname, default_amount in sliver_vs.DEFAULT_ALLOCATION.iteritems(): + DEFAULT_ALLOCATION[resname]=default_amount + + account.register_class(sliver_class_to_register) + account.register_class(controller.Controller) database.start() api_calls.deliver_ticket = deliver_ticket api.start() + +### check if a sliver is running +### a first step to a unified code for codemux +def is_running (name): + if implementation=='vs': + import vserver + return vserver.VServer(name).is_running() + else: + import libvirt + running = False + try: + conn = libvirt.open('lxc://') + dom = conn.lookupByName(name) + running = dom.info()[0] == libvirt.VIR_DOMAIN_RUNNING + finally: + conn.close() + return running