diff options
| author | John Tran <jtran@attinteractive.com> | 2011-05-12 14:29:41 -0700 |
|---|---|---|
| committer | John Tran <jtran@attinteractive.com> | 2011-05-12 14:29:41 -0700 |
| commit | cbe89f150f6c1e209405da6cbba4c3cf9163fd2e (patch) | |
| tree | 3d3f3415257b2f2d266137cd06c29b62e97b96d1 /plugins | |
| parent | 7cd6e9f1cf62ff5628ae4680aa66ada676c8c288 (diff) | |
| parent | 0576766cdf3480ad02159671d2dfc0bdcb154934 (diff) | |
| download | nova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.tar.gz nova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.tar.xz nova-cbe89f150f6c1e209405da6cbba4c3cf9163fd2e.zip | |
merged from trunk
Diffstat (limited to 'plugins')
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__": |
