command_in_slice moved to tools.py
[nodemanager.git] / plugins / omf_resctl.py
index 87629aa..85824e1 100644 (file)
@@ -1,14 +1,12 @@
 #
 #
-# $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
 """
 
-import os
+import os, os.path
 import glob
 import subprocess
 
 import glob
 import subprocess
 
@@ -17,59 +15,97 @@ import logger
 
 priority = 50
 
 
 priority = 50
 
-def start(options, conf):
-    logger.log("omf_resctl: plugin starting up...")
+def start():
+    pass
+
+### the new template for v6
+# hard-wire this for now
+# once the variables are expanded, this is expected to go into
+config_ple_template="""---
+# Example:
+# _slicename_ = nicta_ruby
+# _hostname_ = planetlab1.research.nicta.com.au
+# _xmpp_server_ = xmpp.planet-lab.eu
+: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
+omf_rc_trigger_script="plc_trigger_omf_rc"
 
 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
 
     try:
         xmpp_server=data['xmpp']['server']
     if 'accounts' not in data:
         logger.log_missing_data("omf_resctl.GetSlivers",'accounts')
         return
 
     try:
         xmpp_server=data['xmpp']['server']
+        if not xmpp_server: 
+            # we have the key but no value, just as bad
+            raise Exception
     except:
         # disabled feature - bailing out
     except:
         # disabled feature - bailing out
-        # xxx might need to clean up more deeply..
+        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 hrn: raise Exception,"Empty hrn"
-    except:
-        node_hrn='default   # Failed to read hrn from GetSlivers, please upgrade PLCAPI'
+    hostname = data['hostname']
 
 
-    for sliver in data['slivers']:
-        name=sliver['name']
+    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)
-                    changes=tools.replace_file_with_string(yaml,yaml_contents)
-                    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']
+        yaml_template = config_ple_template
+        yaml_contents = yaml_template\
+            .replace('_xmpp_server_',xmpp_server)\
+            .replace('_slicename_',slicename)\
+            .replace('_hostname_',hostname)
+        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:
+                # the trigger script actually needs to be run in the slice context of course
+                # xxx we might need to use
+                # slice_command=['bash','-l','-c',omf_rc_trigger_script] 
+                slice_command = [omf_rc_trigger_script]
+                to_run = tools.command_in_slice (slicename, slice_command)
+                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)