Merge branch 'master' of ssh://git.onelab.eu/git/nodemanager
[nodemanager.git] / plugins / omf_resctl.py
index 0ee1da0..a5f4c68 100644 (file)
@@ -1,8 +1,6 @@
 #
 #
-# $Id$
-# $URL$
-#
 # NodeManager plugin - first step of handling omf_controlled slices
 # NodeManager plugin - first step of handling omf_controlled slices
+#
 
 """
 Overwrites the 'resctl' tag of slivers controlled by OMF so slivermanager.py does the right thing
 
 """
 Overwrites the 'resctl' tag of slivers controlled by OMF so slivermanager.py does the right thing
@@ -15,12 +13,56 @@ import subprocess
 import tools
 import logger
 
 import tools
 import logger
 
-priority = 50
+# we need this to run after sliverauth
+priority = 150
 
 def start():
 
 def start():
-    logger.log("omf_resctl: plugin starting up...")
+    pass
+
+### the new template for OMF v6
+# hard-wire this for now
+# once the variables are expanded, this is expected to go into
+config_ple_template="""---
+# slicename = _slicename_
+# hostname = _hostname_
+# xmpp_server = _xmpp_server_
+# we extract expires time here, even in a comment so that the
+# trigger script gets called whenever this changes
+# expires: _expires_
+:uid: _slicename_@_hostname_
+:uri: xmpp://_slicename_-_hostname_-<%= "#{Process.pid}" %>:_slicename_-_hostname_-<%= "#{Process.pid}" %>@_xmpp_server_
+:environment: production
+:debug: false
+:auth:
+  :root_cert_dir: /home/_slicename_/root_certs
+  :entity_cert: /home/_slicename_/entity.crt
+  :entity_key: /home/_slicename_/.ssh/id_rsa
+"""
+
+# the path where the config is expected from within the sliver
+yaml_slice_path="/etc/omf_rc/config.yml"
+# the path for the script that we call when a change occurs
+# given that we're now responsible for fetching this one, I have to
+# decide on an actual path - not jsut a name to search for in PATH
+omf_rc_trigger_script="/usr/bin/plc_trigger_omf_rc"
+
+# hopefully temporary: when trigger script is missing, fetch it at the url here
+omf_rc_trigger_url="http://git.mytestbed.net/?p=omf.git;a=blob_plain;f=omf_rc/bin/plc_trigger_omf_rc;hb=HEAD"
+def fetch_trigger_script_if_missing (slicename):
+    full_path="/vservers/%s/%s"%(slicename,omf_rc_trigger_script)
+    if not os.path.isfile (full_path):
+        retcod=subprocess.call (['curl','--silent','-o',full_path,omf_rc_trigger_url])
+        if retcod!=0:
+            logger.log("Could not fetch %s"%omf_rc_trigger_url)
+        else:
+            subprocess.call(['chmod','+x',full_path])
+            logger.log("omf_resctl: fetched %s"%(full_path))
+            logger.log("omf_resctl: from %s"%(omf_rc_trigger_url))
 
 def GetSlivers(data, conf = None, plc = None):
 
 def GetSlivers(data, conf = None, plc = None):
+    logger.log("omf_resctl.GetSlivers")
     if 'accounts' not in data:
         logger.log_missing_data("omf_resctl.GetSlivers",'accounts')
         return
     if 'accounts' not in data:
         logger.log_missing_data("omf_resctl.GetSlivers",'accounts')
         return
@@ -32,54 +74,58 @@ def GetSlivers(data, conf = None, plc = None):
             raise Exception
     except:
         # disabled feature - bailing out
             raise Exception
     except:
         # disabled feature - bailing out
-        # xxx might need to clean up more deeply..
-        logger.log("PLC_OMF_ENABLED is not set -- plugin exiting")
+        logger.log("omf_resctl: PLC_OMF config unsufficient (not enabled, or no server set), -- plugin exiting")
         return
 
         return
 
-    # as hrn is set only at AddNode-time, upgraded myplcs might still miss this
-    # clue: just overwrite the hostname of all nodes
-    # for node in GetNodes(): UpdateNode(node['node_id'],{'hostname':node['hostname']})
-    try:
-        node_hrn = data['hrn']
-        if not node_hrn: raise Exception
-    except:
-        logger.log("Failed to read hrn from GetSlivers, using 'default' - *please upgrade PLCAPI*")
-        node_hrn='default   # Failed to read hrn from GetSlivers, please upgrade PLCAPI'
+    hostname = data['hostname']
 
 
-    for sliver in data['slivers']:
-        name=sliver['name']
-        sliver_pub_key_dir=os.path.join("/home", name, ".ssh/")
-        sliver_private_key=os.path.join(sliver_pub_key_dir, "id_rsa")
+    def is_omf_friendly (sliver):
         for chunk in sliver['attributes']:
         for chunk in sliver['attributes']:
-            if chunk['tagname']=='omf_control':
-                # scan all versions of omf-resctl
-                etc_path="/vservers/%s/etc/"%name
-                pattern = etc_path + "omf-resctl-*/omf-resctl.yaml.in"
-                templates = glob.glob (pattern)
-                if not templates:
-                    logger.log("WARNING: omf_resctl plugin, no template found for slice %s using pattern %s"\
-                                   %(name,pattern))
-                    continue
-                for template in templates:
-                    # remove the .in extension
-                    yaml=template[:-3]
-                    # figure service name as subdir under etc/
-                    service_name=os.path.split(template.replace(etc_path,''))[0]
-                    # read template and replace
-                    template_contents=file(template).read()
-                    yaml_contents=template_contents\
-                        .replace('@XMPP_SERVER@',xmpp_server)\
-                        .replace('@NODE_HRN@',node_hrn)\
-                        .replace('@SLICE_NAME@',name)\
-                        .replace('@SLIVER_PRIVATE_KEY@',sliver_private_key)\
-                        .replace('@SLIVER_PUB_KEY_DIR@',sliver_pub_key_dir)
-                    changes=tools.replace_file_with_string(yaml,yaml_contents)
-                    logger.log("yaml_contents length=%d, changes=%r"%(len(yaml_contents),changes))
-                    if changes:
-                        sp=subprocess.Popen(['vserver',name,'exec','service',service_name,'restart'],
-                                            stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
-                        (output,retcod)=sp.communicate()
-                        logger.log("omf_resctl: %s: restarted resource controller (retcod=%r)"%(name,retcod))
-                        logger.log("omf_resctl: got output\n%s"%output)
-                    else:
-                        logger.log("omf_resctl: %s: omf_control'ed sliver has no change" % name)
+            if chunk['tagname']=='omf_control': return True
+
+    for sliver in data['slivers']:
+        # skip non OMF-friendly slices
+        if not is_omf_friendly (sliver): continue
+        slicename=sliver['name']
+        expires=str(sliver['expires'])
+        yaml_template = config_ple_template
+        yaml_contents = yaml_template\
+            .replace('_xmpp_server_',xmpp_server)\
+            .replace('_slicename_',slicename)\
+            .replace('_hostname_',hostname)\
+            .replace('_expires_',expires)
+        yaml_full_path="/vservers/%s/%s"%(slicename,yaml_slice_path)
+        yaml_full_dir=os.path.dirname(yaml_full_path)
+        if not os.path.isdir(yaml_full_dir):
+            try: os.makedirs(yaml_full_dir)
+            except OSError: pass
+
+        config_changes=tools.replace_file_with_string(yaml_full_path,yaml_contents)
+        logger.log("yaml_contents length=%d, config_changes=%r"%(len(yaml_contents),config_changes))
+        # would make sense to also check for changes to authorized_keys 
+        # would require saving a copy of that some place for comparison
+        # xxx todo
+        keys_changes = False
+        if config_changes or keys_changes:
+            # instead of restarting the service we call a companion script
+            try:
+                fetch_trigger_script_if_missing (slicename)
+                # the trigger script actually needs to be run in the slice context of course
+                slice_command = [ "sudo", "-i",  omf_rc_trigger_script ]
+                to_run = tools.command_in_slice (slicename, slice_command)
+                logger.log("command_in_slice: %s"%to_run)
+                sp=subprocess.Popen(to_run, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
+                (out,err)=sp.communicate()
+                retcod=sp.returncode
+                # we don't wait for that, try to display a retcod for info purpose only
+                # might be None if that config script lasts or hangs whatever
+                logger.log("omf_resctl: %s: called OMF rc control script (imm. retcod=%r)"%(slicename,retcod))
+                logger.log("omf_resctl: got stdout\n%s"%out)
+                logger.log("omf_resctl: got stderr\n%s"%err)
+            except:
+                import traceback
+                traceback.print_exc()
+                logger.log_exc("omf_resctl: WARNING: Could not call trigger script %s"%\
+                                   omf_rc_trigger_script, name=slicename)
+        else:
+            logger.log("omf_resctl: %s: omf_control'ed sliver has no change" % slicename)