summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2011-01-22 15:14:10 -0800
committerVishvananda Ishaya <vishvananda@gmail.com>2011-01-22 15:14:10 -0800
commitf6c360947a56e0500e8326bf722c16ed5eceece9 (patch)
tree605014146ca297bcfc77afa8c288353192d90280 /plugins
parentd757a1a10f0cbc5a3c0f5b1427d1d526584298ce (diff)
parentb6d2d24e778eb6d4b6570246bfe09cba1897d254 (diff)
merged trunk
Diffstat (limited to 'plugins')
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/agent126
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance44
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py62
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