From a1992ba586acc7545a7edb37130727e19e4d1065 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 30 Mar 2011 13:13:04 -0700 Subject: [PATCH] Add ed leafe's code for the inject_file agent plugin method that somehow got lost (fixes bug 741246). Update TimeoutError string for i18n --- .../xenserver/xenapi/etc/xapi.d/plugins/agent | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 94eaabe73362..3522df49d5f0 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -22,6 +22,7 @@ # XenAPI plugin for reading/writing information to xenstore # +import base64 try: import json except ImportError: @@ -31,6 +32,7 @@ import random import subprocess import tempfile import time +import uuid import XenAPIPlugin @@ -102,6 +104,75 @@ def resetnetwork(self, arg_dict): xenstore.write_record(self, arg_dict) +@jsonify +def inject_file(self, arg_dict): + """Expects a file path and the contents of the file to be written. Both + should be base64-encoded in order to eliminate errors as they are passed + through the stack. Writes that information to xenstore for the agent, + which will decode the file and intended path, and create it on the + instance. The original agent munged both of these into a single entry; + the new agent keeps them separate. We will need to test for the new agent, + and write the xenstore records to match the agent version. We will also + need to test to determine if the file injection method on the agent has + been disabled, and raise a NotImplemented error if that is the case. + """ + b64_path = arg_dict["b64_path"] + b64_file = arg_dict["b64_file"] + request_id = arg_dict["id"] + if self._agent_has_method("file_inject"): + # New version of the agent. Agent should receive a 'value' + # key whose value is a dictionary containing 'b64_path' and + # 'b64_file'. See old version below. + arg_dict["value"] = json.dumps({"name": "file_inject", + "value": {"b64_path": b64_path, "b64_file": b64_file}}) + elif self._agent_has_method("injectfile"): + # Old agent requires file path and file contents to be + # combined into one base64 value. + raw_path = base64.b64decode(b64_path) + raw_file = base64.b64decode(b64_file) + new_b64 = base64.b64encode("%s,%s") % (raw_path, raw_file) + arg_dict["value"] = json.dumps({"name": "injectfile", + "value": new_b64}) + else: + # Either the methods don't exist in the agent, or they + # have been disabled. + raise NotImplementedError("NOT IMPLEMENTED: Agent does not support" + " file injection.") + arg_dict["path"] = "data/host/%s" % request_id + xenstore.write_record(self, arg_dict) + try: + resp = _wait_for_agent(self, request_id, arg_dict) + except TimeoutError, e: + raise PluginError("%s" % e) + return resp + + +def _agent_has_method(self, method): + """Check that the agent has a particular method by checking its + features. Cache the features so we don't have to query the agent + every time we need to check. + """ + try: + self._agent_methods + except AttributeError: + self._agent_methods = [] + if not self._agent_methods: + # Haven't been defined + tmp_id = str(uuid.uuid4()) + dct = {} + dct["value"] = json.dumps({"name": "features", "value": ""}) + dct["path"] = "data/host/%s" % tmp_id + xenstore.write_record(self, dct) + try: + resp = _wait_for_agent(self, tmp_id, dct) + except TimeoutError, e: + raise PluginError("%s" % e) + response = json.loads(resp) + # The agent returns a comma-separated list of methods. + self._agent_methods = response.split(",") + return method in self._agent_methods + + def _wait_for_agent(self, request_id, arg_dict): """Periodically checks xenstore for a response from the agent. The request is always written to 'data/host/{id}', and @@ -119,9 +190,8 @@ def _wait_for_agent(self, request_id, arg_dict): # First, delete the request record arg_dict["path"] = "data/host/%s" % request_id xenstore.delete_record(self, arg_dict) - raise TimeoutError( - "TIMEOUT: No response from agent within %s seconds." % - AGENT_TIMEOUT) + raise TimeoutError(_("TIMEOUT: No response from agent within" + " %s seconds.") % AGENT_TIMEOUT) ret = xenstore.read_record(self, arg_dict) # Note: the response for None with be a string that includes # double quotes. @@ -136,4 +206,5 @@ if __name__ == "__main__": XenAPIPlugin.dispatch( {"key_init": key_init, "password": password, - "resetnetwork": resetnetwork}) + "resetnetwork": resetnetwork, + "inject_file": inject_file})