diff options
| author | Vishvananda Ishaya <vishvananda@gmail.com> | 2011-01-22 15:14:10 -0800 |
|---|---|---|
| committer | Vishvananda Ishaya <vishvananda@gmail.com> | 2011-01-22 15:14:10 -0800 |
| commit | f6c360947a56e0500e8326bf722c16ed5eceece9 (patch) | |
| tree | 605014146ca297bcfc77afa8c288353192d90280 /plugins | |
| parent | d757a1a10f0cbc5a3c0f5b1427d1d526584298ce (diff) | |
| parent | b6d2d24e778eb6d4b6570246bfe09cba1897d254 (diff) | |
merged trunk
Diffstat (limited to 'plugins')
| -rwxr-xr-x | plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 126 | ||||
| -rw-r--r-- | plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 44 | ||||
| -rwxr-xr-x | plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 62 |
3 files changed, 201 insertions, 31 deletions
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent new file mode 100755 index 000000000..12c3a19c8 --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Citrix Systems, Inc. +# Copyright 2011 OpenStack LLC. +# Copyright 2011 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# +# XenAPI plugin for reading/writing information to xenstore +# + +try: + import json +except ImportError: + import simplejson as json +import os +import random +import subprocess +import tempfile +import time + +import XenAPIPlugin + +from pluginlib_nova import * +configure_logging("xenstore") +import xenstore + +AGENT_TIMEOUT = 30 + + +def jsonify(fnc): + def wrapper(*args, **kwargs): + return json.dumps(fnc(*args, **kwargs)) + return wrapper + + +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 + info to be passed, such as passwords. Returns the shared + secret key value. + """ + pub = int(arg_dict["pub"]) + arg_dict["value"] = json.dumps({"name": "keyinit", "value": pub}) + request_id = arg_dict["id"] + 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 + + +@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 + encrypted using the shared secret key that was returned by a + 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"] + 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 _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 + the agent's response for that request will be in 'data/guest/{id}'. + If no value appears from the agent within the time specified by + AGENT_TIMEOUT, the original request is deleted and a TimeoutError + is returned. + """ + arg_dict["path"] = "data/guest/%s" % request_id + arg_dict["ignore_missing_path"] = True + start = time.time() + while True: + if time.time() - start > AGENT_TIMEOUT: + # No response within the timeout period; bail out + # 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) + ret = xenstore.read_record(self, arg_dict) + # Note: the response for None with be a string that includes + # double quotes. + if ret != '"None"': + # The agent responded + return ret + else: + time.sleep(3) + + +if __name__ == "__main__": + XenAPIPlugin.dispatch( + {"key_init": key_init, + "password": password}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 5e648b970..aadacce57 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -18,7 +18,7 @@ # under the License. # -# XenAPI plugin for putting images into glance +# XenAPI plugin for managing glance images # import base64 @@ -40,29 +40,57 @@ from pluginlib_nova import * configure_logging('glance') CHUNK_SIZE = 8192 +KERNEL_DIR = '/boot/guest' FILE_SR_PATH = '/var/run/sr-mount' +def copy_kernel_vdi(session,args): + vdi = exists(args, 'vdi-ref') + size = exists(args,'image-size') + #Use the uuid as a filename + vdi_uuid=session.xenapi.VDI.get_uuid(vdi) + copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)} + filename=with_vdi_in_dom0(session, vdi, False, + lambda dev: + _copy_kernel_vdi('/dev/%s' % dev,copy_args)) + return filename + +def _copy_kernel_vdi(dest,copy_args): + vdi_uuid=copy_args['vdi_uuid'] + vdi_size=copy_args['vdi_size'] + logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s",dest,vdi_uuid) + filename=KERNEL_DIR + '/' + vdi_uuid + #read data from /dev/ and write into a file on /boot/guest + of=open(filename,'wb') + f=open(dest,'rb') + #copy only vdi_size bytes + data=f.read(vdi_size) + of.write(data) + f.close() + of.close() + logging.debug("Done. Filename: %s",filename) + return filename + def put_vdis(session, args): params = pickle.loads(exists(args, 'params')) vdi_uuids = params["vdi_uuids"] - image_name = params["image_name"] + image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] sr_path = get_sr_path(session) #FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs - tmp_file = "%s.tar.gz" % os.path.join('/tmp', image_name) + tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id)) tar_cmd = ['tar', '-zcf', tmp_file, '--directory=%s' % sr_path] paths = [ "%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids ] tar_cmd.extend(paths) logging.debug("Bundling image with cmd: %s", tar_cmd) subprocess.call(tar_cmd) logging.debug("Writing to test file %s", tmp_file) - put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port) + put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port) return "" # FIXME(sirp): return anything useful here? -def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): +def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): size = os.path.getsize(tmp_file) basename = os.path.basename(tmp_file) @@ -72,7 +100,6 @@ def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): 'x-image-meta-store': 'file', 'x-image-meta-is_public': 'True', 'x-image-meta-type': 'raw', - 'x-image-meta-name': image_name, 'x-image-meta-size': size, 'content-length': size, 'content-type': 'application/octet-stream', @@ -80,7 +107,7 @@ def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): conn = httplib.HTTPConnection(glance_host, glance_port) #NOTE(sirp): httplib under python2.4 won't accept a file-like object # to request - conn.putrequest('POST', '/images') + conn.putrequest('PUT', '/images/%s' % image_id) for header, value in headers.iteritems(): conn.putheader(header, value) @@ -129,4 +156,5 @@ def find_sr(session): if __name__ == '__main__': - XenAPIPlugin.dispatch({'put_vdis': put_vdis}) + XenAPIPlugin.dispatch({'put_vdis': put_vdis, + 'copy_kernel_vdi': copy_kernel_vdi}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py index 8e7a829d5..7fea1136d 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py @@ -19,6 +19,8 @@ # that we need. # +import gettext +gettext.install('nova', unicode=1) import httplib import logging import logging.handlers @@ -60,7 +62,7 @@ def ignore_failure(func, *args, **kwargs): try: return func(*args, **kwargs) except XenAPI.Failure, e: - logging.error('Ignoring XenAPI.Failure %s', e) + logging.error(_('Ignoring XenAPI.Failure %s'), e) return None @@ -78,19 +80,25 @@ def validate_exists(args, key, default=None): """ if key in args: if len(args[key]) == 0: - raise ArgumentError('Argument %r value %r is too short.' % - (key, args[key])) + raise ArgumentError(_('Argument %(key)s value %(value)s is too ' + 'short.') % + {'key': key, + 'value': args[key]}) if not ARGUMENT_PATTERN.match(args[key]): - raise ArgumentError('Argument %r value %r contains invalid ' - 'characters.' % (key, args[key])) + raise ArgumentError(_('Argument %(key)s value %(value)s contains ' + 'invalid characters.') % + {'key': key, + 'value': args[key]}) if args[key][0] == '-': - raise ArgumentError('Argument %r value %r starts with a hyphen.' - % (key, args[key])) + raise ArgumentError(_('Argument %(key)s value %(value)s starts ' + 'with a hyphen.') % + {'key': key, + 'value': args[key]}) return args[key] elif default is not None: return default else: - raise ArgumentError('Argument %s is required.' % key) + raise ArgumentError(_('Argument %s is required.') % key) def validate_bool(args, key, default=None): @@ -105,8 +113,10 @@ def validate_bool(args, key, default=None): elif value.lower() == 'false': return False else: - raise ArgumentError("Argument %s may not take value %r. " - "Valid values are ['true', 'false']." % (key, value)) + raise ArgumentError(_("Argument %(key)s may not take value %(value)s. " + "Valid values are ['true', 'false'].") + % {'key': key, + 'value': value}) def exists(args, key): @@ -116,7 +126,7 @@ def exists(args, key): if key in args: return args[key] else: - raise ArgumentError('Argument %s is required.' % key) + raise ArgumentError(_('Argument %s is required.') % key) def optional(args, key): @@ -149,8 +159,13 @@ def create_vdi(session, sr_ref, name_label, virtual_size, read_only): 'other_config': {}, 'sm_config': {}, 'tags': []}) - logging.debug('Created VDI %s (%s, %s, %s) on %s.', vdi_ref, name_label, - virtual_size, read_only, sr_ref) + logging.debug(_('Created VDI %(vdi_ref)s (%(label)s, %(size)s, ' + '%(read_only)s) on %(sr_ref)s.') % + {'vdi_ref': vdi_ref, + 'label': name_label, + 'size': virtual_size, + 'read_only': read_only, + 'sr_ref': sr_ref}) return vdi_ref @@ -169,19 +184,19 @@ def with_vdi_in_dom0(session, vdi, read_only, f): vbd_rec['qos_algorithm_type'] = '' vbd_rec['qos_algorithm_params'] = {} vbd_rec['qos_supported_algorithms'] = [] - logging.debug('Creating VBD for VDI %s ... ', vdi) + logging.debug(_('Creating VBD for VDI %s ... '), vdi) vbd = session.xenapi.VBD.create(vbd_rec) - logging.debug('Creating VBD for VDI %s done.', vdi) + logging.debug(_('Creating VBD for VDI %s done.'), vdi) try: - logging.debug('Plugging VBD %s ... ', vbd) + logging.debug(_('Plugging VBD %s ... '), vbd) session.xenapi.VBD.plug(vbd) - logging.debug('Plugging VBD %s done.', vbd) + logging.debug(_('Plugging VBD %s done.'), vbd) return f(session.xenapi.VBD.get_device(vbd)) finally: - logging.debug('Destroying VBD for VDI %s ... ', vdi) + logging.debug(_('Destroying VBD for VDI %s ... '), vdi) vbd_unplug_with_retry(session, vbd) ignore_failure(session.xenapi.VBD.destroy, vbd) - logging.debug('Destroying VBD for VDI %s done.', vdi) + logging.debug(_('Destroying VBD for VDI %s done.'), vdi) def vbd_unplug_with_retry(session, vbd): @@ -192,19 +207,20 @@ def vbd_unplug_with_retry(session, vbd): while True: try: session.xenapi.VBD.unplug(vbd) - logging.debug('VBD.unplug successful first time.') + logging.debug(_('VBD.unplug successful first time.')) return except XenAPI.Failure, e: if (len(e.details) > 0 and e.details[0] == 'DEVICE_DETACH_REJECTED'): - logging.debug('VBD.unplug rejected: retrying...') + logging.debug(_('VBD.unplug rejected: retrying...')) time.sleep(1) elif (len(e.details) > 0 and e.details[0] == 'DEVICE_ALREADY_DETACHED'): - logging.debug('VBD.unplug successful eventually.') + logging.debug(_('VBD.unplug successful eventually.')) return else: - logging.error('Ignoring XenAPI.Failure in VBD.unplug: %s', e) + logging.error(_('Ignoring XenAPI.Failure in VBD.unplug: %s'), + e) return |
