Merge lp:~cbehrens/nova/lp741246 into lp:~hudson-openstack/nova/trunk

Proposed by Chris Behrens
Status: Merged
Approved by: Josh Kearney
Approved revision: 925
Merged at revision: 937
Proposed branch: lp:~cbehrens/nova/lp741246
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 125 lines (+77/-6)
1 file modified
plugins/xenserver/xenapi/etc/xapi.d/plugins/agent (+77/-6)
To merge this branch: bzr merge lp:~cbehrens/nova/lp741246
Reviewer Review Type Date Requested Status
Josh Kearney (community) Approve
Ed Leafe (community) Approve
Review via email: mp+55627@code.launchpad.net

Description of the change

Fixes bug 741246. Ed Leafe's inject_file method for the agent plugin was mistakenly never committed after having to fix commits under wrong email address. vmops makes calls to this (previously) missing method.

To post a comment you must log in.
Revision history for this message
Ed Leafe (ed-leafe) wrote :

The uuid module is not available in Python 2.4, which is what plugins will run under. So change the 'import uuid' line (diff #16) to 'import commands', and then change diff line #96 from:

tmp_id = str(uuid.uuid4())

to:

tmp_id = commands.getoutput("uuidgen")

review: Needs Fixing
Revision history for this message
Ed Leafe (ed-leafe) wrote :

lgtm

review: Approve
Revision history for this message
Josh Kearney (jk0) wrote :

lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/xenserver/xenapi/etc/xapi.d/plugins/agent'
2--- plugins/xenserver/xenapi/etc/xapi.d/plugins/agent 2011-02-17 22:09:26 +0000
3+++ plugins/xenserver/xenapi/etc/xapi.d/plugins/agent 2011-03-31 16:03:33 +0000
4@@ -22,6 +22,8 @@
5 # XenAPI plugin for reading/writing information to xenstore
6 #
7
8+import base64
9+import commands
10 try:
11 import json
12 except ImportError:
13@@ -66,7 +68,7 @@
14 try:
15 resp = _wait_for_agent(self, request_id, arg_dict)
16 except TimeoutError, e:
17- raise PluginError("%s" % e)
18+ raise PluginError(e)
19 return resp
20
21
22@@ -87,7 +89,7 @@
23 try:
24 resp = _wait_for_agent(self, request_id, arg_dict)
25 except TimeoutError, e:
26- raise PluginError("%s" % e)
27+ raise PluginError(e)
28 return resp
29
30
31@@ -102,6 +104,75 @@
32 xenstore.write_record(self, arg_dict)
33
34
35+@jsonify
36+def inject_file(self, arg_dict):
37+ """Expects a file path and the contents of the file to be written. Both
38+ should be base64-encoded in order to eliminate errors as they are passed
39+ through the stack. Writes that information to xenstore for the agent,
40+ which will decode the file and intended path, and create it on the
41+ instance. The original agent munged both of these into a single entry;
42+ the new agent keeps them separate. We will need to test for the new agent,
43+ and write the xenstore records to match the agent version. We will also
44+ need to test to determine if the file injection method on the agent has
45+ been disabled, and raise a NotImplemented error if that is the case.
46+ """
47+ b64_path = arg_dict["b64_path"]
48+ b64_file = arg_dict["b64_file"]
49+ request_id = arg_dict["id"]
50+ if self._agent_has_method("file_inject"):
51+ # New version of the agent. Agent should receive a 'value'
52+ # key whose value is a dictionary containing 'b64_path' and
53+ # 'b64_file'. See old version below.
54+ arg_dict["value"] = json.dumps({"name": "file_inject",
55+ "value": {"b64_path": b64_path, "b64_file": b64_file}})
56+ elif self._agent_has_method("injectfile"):
57+ # Old agent requires file path and file contents to be
58+ # combined into one base64 value.
59+ raw_path = base64.b64decode(b64_path)
60+ raw_file = base64.b64decode(b64_file)
61+ new_b64 = base64.b64encode("%s,%s") % (raw_path, raw_file)
62+ arg_dict["value"] = json.dumps({"name": "injectfile",
63+ "value": new_b64})
64+ else:
65+ # Either the methods don't exist in the agent, or they
66+ # have been disabled.
67+ raise NotImplementedError(_("NOT IMPLEMENTED: Agent does not"
68+ " support file injection."))
69+ arg_dict["path"] = "data/host/%s" % request_id
70+ xenstore.write_record(self, arg_dict)
71+ try:
72+ resp = _wait_for_agent(self, request_id, arg_dict)
73+ except TimeoutError, e:
74+ raise PluginError(e)
75+ return resp
76+
77+
78+def _agent_has_method(self, method):
79+ """Check that the agent has a particular method by checking its
80+ features. Cache the features so we don't have to query the agent
81+ every time we need to check.
82+ """
83+ try:
84+ self._agent_methods
85+ except AttributeError:
86+ self._agent_methods = []
87+ if not self._agent_methods:
88+ # Haven't been defined
89+ tmp_id = commands.getoutput("uuidgen")
90+ dct = {}
91+ dct["value"] = json.dumps({"name": "features", "value": ""})
92+ dct["path"] = "data/host/%s" % tmp_id
93+ xenstore.write_record(self, dct)
94+ try:
95+ resp = _wait_for_agent(self, tmp_id, dct)
96+ except TimeoutError, e:
97+ raise PluginError(e)
98+ response = json.loads(resp)
99+ # The agent returns a comma-separated list of methods.
100+ self._agent_methods = response.split(",")
101+ return method in self._agent_methods
102+
103+
104 def _wait_for_agent(self, request_id, arg_dict):
105 """Periodically checks xenstore for a response from the agent.
106 The request is always written to 'data/host/{id}', and
107@@ -119,9 +190,8 @@
108 # First, delete the request record
109 arg_dict["path"] = "data/host/%s" % request_id
110 xenstore.delete_record(self, arg_dict)
111- raise TimeoutError(
112- "TIMEOUT: No response from agent within %s seconds." %
113- AGENT_TIMEOUT)
114+ raise TimeoutError(_("TIMEOUT: No response from agent within"
115+ " %s seconds.") % AGENT_TIMEOUT)
116 ret = xenstore.read_record(self, arg_dict)
117 # Note: the response for None with be a string that includes
118 # double quotes.
119@@ -136,4 +206,5 @@
120 XenAPIPlugin.dispatch(
121 {"key_init": key_init,
122 "password": password,
123- "resetnetwork": resetnetwork})
124+ "resetnetwork": resetnetwork,
125+ "inject_file": inject_file})