summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorJohn Tran <jtran@attinteractive.com>2011-05-12 14:29:41 -0700
committerJohn Tran <jtran@attinteractive.com>2011-05-12 14:29:41 -0700
commitcbe89f150f6c1e209405da6cbba4c3cf9163fd2e (patch)
tree3d3f3415257b2f2d266137cd06c29b62e97b96d1 /plugins
parent7cd6e9f1cf62ff5628ae4680aa66ada676c8c288 (diff)
parent0576766cdf3480ad02159671d2dfc0bdcb154934 (diff)
downloadnova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.tar.gz
nova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.tar.xz
nova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.zip
merged from trunk
Diffstat (limited to 'plugins')
-rwxr-xr-xplugins/xenserver/networking/etc/xensource/scripts/vif_rules.py34
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/agent86
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance3
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/migration3
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py39
5 files changed, 118 insertions, 47 deletions
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
index d2b2d61e6..48122e6d6 100755
--- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
+++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
@@ -54,6 +54,7 @@ def main(dom_id, command, only_this_vif=None):
def execute(*command, return_stdout=False):
devnull = open(os.devnull, 'w')
+ command = map(str, command)
proc = subprocess.Popen(command, close_fds=True,
stdout=subprocess.PIPE, stderr=devnull)
devnull.close()
@@ -71,13 +72,13 @@ def apply_iptables_rules(command, params):
iptables = lambda *rule: execute('/sbin/iptables', *rule)
iptables('-D', 'FORWARD', '-m', 'physdev',
- '--physdev-in', '%(VIF)s' % params,
- '-s', '%(IP)s' % params,
+ '--physdev-in', params['VIF'],
+ '-s', params['IP'],
'-j', 'ACCEPT')
if command == 'online':
iptables('-A', 'FORWARD', '-m', 'physdev',
- '--physdev-in', '%(VIF)s' % params,
- '-s', '%(IP)s' % params,
+ '--physdev-in', params['VIF'],
+ '-s', params['IP'],
'-j', 'ACCEPT')
@@ -85,25 +86,24 @@ def apply_arptables_rules(command, params):
arptables = lambda *rule: execute('/sbin/arptables', *rule)
arptables('-D', 'FORWARD', '--opcode', 'Request',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
arptables('-D', 'FORWARD', '--opcode', 'Reply',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
if command == 'online':
arptables('-A', 'FORWARD', '--opcode', 'Request',
- '--in-interface', '%(VIF)s' % params
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
arptables('-A', 'FORWARD', '--opcode', 'Reply',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
@@ -130,7 +130,7 @@ def apply_ebtables_rules(command, params):
'-i', params['VIF'], '-j', 'DROP')
if command == 'online':
ebtables('-I', 'FORWARD', '1', '-s', '!', params['MAC'],
- '-i', '%(VIF)s', '-j', 'DROP')
+ '-i', params['VIF'], '-j', 'DROP')
if __name__ == "__main__":
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index 94eaabe73..9e761f264 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -22,6 +22,8 @@
# XenAPI plugin for reading/writing information to xenstore
#
+import base64
+import commands
try:
import json
except ImportError:
@@ -51,7 +53,6 @@ class TimeoutError(StandardError):
pass
-@jsonify
def key_init(self, arg_dict):
"""Handles the Diffie-Hellman key exchange with the agent to
establish the shared secret key used to encrypt/decrypt sensitive
@@ -66,11 +67,10 @@ def key_init(self, arg_dict):
try:
resp = _wait_for_agent(self, request_id, arg_dict)
except TimeoutError, e:
- raise PluginError("%s" % e)
+ raise PluginError(e)
return resp
-@jsonify
def password(self, arg_dict):
"""Writes a request to xenstore that tells the agent to set
the root password for the given VM. The password should be
@@ -78,7 +78,6 @@ def password(self, arg_dict):
previous call to key_init. The encrypted password value should
be passed as the value for the 'enc_pass' key in arg_dict.
"""
- pub = int(arg_dict["pub"])
enc_pass = arg_dict["enc_pass"]
arg_dict["value"] = json.dumps({"name": "password", "value": enc_pass})
request_id = arg_dict["id"]
@@ -87,7 +86,7 @@ def password(self, arg_dict):
try:
resp = _wait_for_agent(self, request_id, arg_dict)
except TimeoutError, e:
- raise PluginError("%s" % e)
+ raise PluginError(e)
return resp
@@ -102,6 +101,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(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 = commands.getoutput("uuidgen")
+ 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(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 +187,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 +203,5 @@ if __name__ == "__main__":
XenAPIPlugin.dispatch(
{"key_init": key_init,
"password": password,
- "resetnetwork": resetnetwork})
+ "resetnetwork": resetnetwork,
+ "inject_file": inject_file})
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index c996f6ef4..0a45f3873 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -216,8 +216,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type):
'x-image-meta-status': 'queued',
'x-image-meta-disk-format': 'vhd',
'x-image-meta-container-format': 'ovf',
- 'x-image-meta-property-os-type': os_type
- }
+ 'x-image-meta-property-os-type': os_type}
for header, value in headers.iteritems():
conn.putheader(header, value)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration
index 4aa89863a..75c653408 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration
@@ -22,6 +22,7 @@ XenAPI Plugin for transfering data between host nodes
import os
import os.path
import pickle
+import shlex
import shutil
import subprocess
@@ -97,7 +98,7 @@ def transfer_vhd(session, args):
logging.debug("Preparing to transmit %s to %s" % (source_path,
dest_path))
- ssh_cmd = 'ssh -o StrictHostKeyChecking=no'
+ ssh_cmd = '\"ssh -o StrictHostKeyChecking=no\"'
rsync_args = shlex.split('nohup /usr/bin/rsync -av --progress -e %s %s %s'
% (ssh_cmd, source_path, dest_path))
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py
index a35ccd6ab..6c589ed29 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py
@@ -56,16 +56,17 @@ def read_record(self, arg_dict):
and boolean True, attempting to read a non-existent path will return
the string 'None' instead of raising an exception.
"""
- cmd = "xenstore-read /local/domain/%(dom_id)s/%(path)s" % arg_dict
+ cmd = ["xenstore-read", "/local/domain/%(dom_id)s/%(path)s" % arg_dict]
try:
- return _run_command(cmd).rstrip("\n")
+ ret, result = _run_command(cmd)
+ return result.strip()
except pluginlib.PluginError, e:
if arg_dict.get("ignore_missing_path", False):
- cmd = "xenstore-exists /local/domain/%(dom_id)s/%(path)s; echo $?"
- cmd = cmd % arg_dict
- ret = _run_command(cmd).strip()
+ cmd = ["xenstore-exists",
+ "/local/domain/%(dom_id)s/%(path)s" % arg_dict]
+ ret, result = _run_command(cmd)
# If the path exists, the cmd should return "0"
- if ret != "0":
+ if ret != 0:
# No such path, so ignore the error and return the
# string 'None', since None can't be marshalled
# over RPC.
@@ -83,8 +84,9 @@ def write_record(self, arg_dict):
you must specify a 'value' key, whose value must be a string. Typically,
you can json-ify more complex values and store the json output.
"""
- cmd = "xenstore-write /local/domain/%(dom_id)s/%(path)s '%(value)s'"
- cmd = cmd % arg_dict
+ cmd = ["xenstore-write",
+ "/local/domain/%(dom_id)s/%(path)s" % arg_dict,
+ arg_dict["value"]]
_run_command(cmd)
return arg_dict["value"]
@@ -96,10 +98,10 @@ def list_records(self, arg_dict):
path as the key and the stored value as the value. If the path
doesn't exist, an empty dict is returned.
"""
- cmd = "xenstore-ls /local/domain/%(dom_id)s/%(path)s" % arg_dict
- cmd = cmd.rstrip("/")
+ dirpath = "/local/domain/%(dom_id)s/%(path)s" % arg_dict
+ cmd = ["xenstore-ls", dirpath.rstrip("/")]
try:
- recs = _run_command(cmd)
+ ret, recs = _run_command(cmd)
except pluginlib.PluginError, e:
if "No such file or directory" in "%s" % e:
# Path doesn't exist.
@@ -128,8 +130,9 @@ def delete_record(self, arg_dict):
"""Just like it sounds: it removes the record for the specified
VM and the specified path from xenstore.
"""
- cmd = "xenstore-rm /local/domain/%(dom_id)s/%(path)s" % arg_dict
- return _run_command(cmd)
+ cmd = ["xenstore-rm", "/local/domain/%(dom_id)s/%(path)s" % arg_dict]
+ ret, result = _run_command(cmd)
+ return result
def _paths_from_ls(recs):
@@ -168,16 +171,16 @@ def _paths_from_ls(recs):
def _run_command(cmd):
"""Abstracts out the basics of issuing system commands. If the command
returns anything in stderr, a PluginError is raised with that information.
- Otherwise, the output from stdout is returned.
+ Otherwise, a tuple of (return code, stdout data) is returned.
"""
pipe = subprocess.PIPE
- proc = subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe,
- stderr=pipe, close_fds=True)
- proc.wait()
+ proc = subprocess.Popen(cmd, stdin=pipe, stdout=pipe, stderr=pipe,
+ close_fds=True)
+ ret = proc.wait()
err = proc.stderr.read()
if err:
raise pluginlib.PluginError(err)
- return proc.stdout.read()
+ return (ret, proc.stdout.read())
if __name__ == "__main__":