diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2009-08-25 14:56:54 +0100 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2009-08-25 14:56:54 +0100 |
commit | 1b14383998f80e4108167ef224a402a28e13f5bd (patch) | |
tree | 7e07831fbce7fbe2f8059b206535565fa0861a07 /tools | |
parent | 552cd79c66dabeafab6ea48d13dd028cb47e76f7 (diff) | |
download | xen-1b14383998f80e4108167ef224a402a28e13f5bd.tar.gz xen-1b14383998f80e4108167ef224a402a28e13f5bd.tar.bz2 xen-1b14383998f80e4108167ef224a402a28e13f5bd.zip |
xend: Add support for URI ('file:' and 'data:' scheme) for PV/kernel
and PV/ramdisk
Add support for 'file:' and 'data:' URI schemes for the parameters
'PV/kernel' and 'PV/ramdisk' in the VM.create() call. The 'data:'
scheme handling enables using a file which is stored inside the
management system (from where the XenAPI call is send) as kernel or
ramdisk.
Notes:
o all included: a detailed description can be found in the xenapi
documentation
o bumped up the version of the API document to 1.0.8 (because of
(minimal) interface extension)
o Future enhancements (like http:, ftp: schemes) fit seamlessly into
the current design / classes
o Unittest cases and xm-test case included
Signed-off-by: Andreas Florath <xen@flonatel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/python/xen/util/fileuri.py | 156 | ||||
-rw-r--r-- | tools/python/xen/xend/XendConfig.py | 15 | ||||
-rw-r--r-- | tools/python/xen/xend/XendDomainInfo.py | 6 | ||||
-rw-r--r-- | tools/python/xen/xend/image.py | 17 | ||||
-rw-r--r-- | tools/tests/run_tests.sh | 61 | ||||
-rw-r--r-- | tools/tests/utests/run_all_tests.py | 32 | ||||
-rw-r--r-- | tools/tests/utests/ut_util/ut_fileuri.py | 209 | ||||
-rw-r--r-- | tools/tests/utests/ut_xend/ut_XendConfig.py | 117 | ||||
-rw-r--r-- | tools/tests/utests/ut_xend/ut_image.py | 147 | ||||
-rw-r--r-- | tools/xm-test/tests/xapi/04_xapi-data_uri_handling.py | 65 | ||||
-rw-r--r-- | tools/xm-test/tests/xapi/Makefile.am | 3 |
11 files changed, 819 insertions, 9 deletions
diff --git a/tools/python/xen/util/fileuri.py b/tools/python/xen/util/fileuri.py new file mode 100644 index 0000000000..b4a79234fe --- /dev/null +++ b/tools/python/xen/util/fileuri.py @@ -0,0 +1,156 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ + +import logging +import os +import base64 +import tempfile +import stat +from xen.xend.XendLogging import log +from xen.util import mkdir + +# +# This functions and classes can be used where a filename is expected - +# especially in the xenapi.VM.create() for PV_kernel and PV_ramdisk. +# +# The functions have a backward compatibility mode, i.e. when there is +# no appropriate scheme detected, the data is seens as a path to a +# (local) file. +# + +class scheme_error(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +# Data scheme (as defined in RFC 2397): +# data:application/octet-stream;base64,<base64 encoded data> +# It looks that there is currently no general purpose implementation +# available (in python) for this URL scheme - so the very basic is +# done here. +# +# Limitations +# o Only base64 is currently supported +class scheme_data: + + def encode(data, mediatype = 'application/octet-stream', + encoding = 'base64'): + # XXX Limit this to base64 for current implementation + if encoding!='base64': + raise scheme_error("invalid encoding") + return 'data:' + mediatype + ";" + encoding \ + + "," + base64.b64encode(data) + encode = staticmethod(encode) + + # Private method: parse encoded data + def parse(encoded_data): + if not isinstance(encoded_data, str): + raise scheme_error("encoded data has wrong type") + if not encoded_data.startswith('data:'): + raise scheme_error("'data:' scheme declaration missing") + comma = encoded_data.find(',', 5) + if comma == -1: + raise scheme_error("data separator (comma) is missing") + # Cut off the media type and encoding + mtenc = encoded_data[5:comma] + if len(mtenc)==0: + raise scheme_error("encoding is empty") + # XXX Limit to base64 encoding + if not mtenc.endswith(';base64'): + raise scheme_error("encoding is not base64") + mediatype = mtenc[:-7] + return (mediatype, 'base64', comma+1) + parse = staticmethod(parse) + + # Stores the data in a local file and returns the filename + # and a flag if this file in temporary only and must be deleted + # after starting the VM. + def decode(encoded_data): + mkdir.parents("/var/run/xend/boot/", stat.S_IRWXU) + mediatype, encoding, data_start = scheme_data.parse(encoded_data) + fd, filename = tempfile.mkstemp( + prefix="data_uri_file.", dir="/var/run/xend/boot") + # Because of python 2.3 support, there is a need to nest these + # (see http://www.python.org/doc/2.3/ref/try.html) + try: + try: + os.write(fd, base64.b64decode(encoded_data[data_start:])) + except TypeError, se: + raise scheme_error("failed to decode as base64") + finally: + os.close(fd) + return filename, True + decode = staticmethod(decode) + + # Utility function which reads in the given (local) file and + # creates a data scheme from this. + def create_from_file(filename): + try: + f = open(filename, "r") + d = f.read() + f.close() + return scheme_data.encode(d) + except IOError: + raise scheme_error("file does not exists") + create_from_file = staticmethod(create_from_file) + + +# File Scheme +# This class supports absolut paths only. +class scheme_file: + + def encode(filename): + if len(filename) == 0: + raise scheme_error("filename is empty") + if filename[0] != '/': + raise scheme_error("filename is not absolut") + return 'file://' + filename + encode = staticmethod(encode) + + def decode(encoded_data): + if not encoded_data.startswith("file://"): + raise scheme_error("no file:// scheme found") + path = encoded_data[7:] + if len(path)==0: + raise scheme_error("path is empty") + if path[0]!='/': + raise scheme_error("path is not absolute") + return path, False + decode = staticmethod(decode) + + +class scheme_set: + + def __init__(self): + self.schemes = [scheme_data, scheme_file] + + # log_decode_exception flags whether a specific uri schema + # decoding exception should be logged or not (default: False - do + # not log). + def decode(self, uri, log_decode_exception=False): + for scheme in self.schemes: + try: + # If this passes, it is the correct scheme + return scheme.decode(uri) + except scheme_error, se: + if log_decode_exception: + log.debug("Decode throws an error: '%s'" % se) + return uri, False + +schemes = scheme_set() + diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index 9ef008c54e..20ecca8b01 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -41,6 +41,7 @@ from xen.util.pci import pci_opts_list_from_sxp, pci_convert_sxp_to_dict from xen.xend.XendSXPDev import dev_dict_to_sxp from xen.util import xsconstants from xen.util import auxbin +import xen.util.fileuri log = logging.getLogger("xend.XendConfig") log.setLevel(logging.WARN) @@ -338,6 +339,8 @@ class XendConfig(dict): # output from xc.domain_getinfo self._dominfo_to_xapi(dominfo, update_mem = True) + self.handle_fileuris() + log.debug('XendConfig.init: %s' % scrub_password(self)) # validators go here @@ -1999,10 +2002,14 @@ class XendConfig(dict): self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','') self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','') self['_temp_args'] = kernel_args + self['use_tmp_kernel'] = True + self['use_tmp_ramdisk'] = True else: self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','') self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','') self['PV_args'] = kernel_args + self['use_tmp_kernel'] = False + self['use_tmp_ramdisk'] = False self['superpages'] = sxp.child_value(image_sxp, 'superpages',0) @@ -2072,4 +2079,12 @@ class XendConfig(dict): opts = pci_opts_list_from_sxp(dev) pci.append([domain, bus, slot, func, vdevfn, opts]) self['platform']['pci'] = pci + + def handle_fileuris(self): + for arg in [('PV_kernel', 'use_tmp_kernel'), + ('PV_ramdisk', 'use_tmp_ramdisk')]: + if arg[0] in self and self[arg[0]]!='': + self[arg[0]], self[arg[1]] \ + = xen.util.fileuri.schemes.decode(self[arg[0]]) + log.debug("fileuri '%s' = '%s'" % (arg[0], self[arg[0]][:100])) diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index f15bcf13ef..5e634b3e2b 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -2662,7 +2662,7 @@ class XendDomainInfo: self._createDevices() - self.image.cleanupBootloading() + self.image.cleanupTmpImages() self.info['start_time'] = time.time() @@ -2670,12 +2670,12 @@ class XendDomainInfo: except VmError, exn: log.exception("XendDomainInfo.initDomain: exception occurred") if self.image: - self.image.cleanupBootloading() + self.image.cleanupTmpImages() raise exn except RuntimeError, exn: log.exception("XendDomainInfo.initDomain: exception occurred") if self.image: - self.image.cleanupBootloading() + self.image.cleanupTmpImages() raise VmError(str(exn)) diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index e5665578b4..0e9abd9726 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -89,6 +89,8 @@ class ImageHandler: self.vm = vm self.bootloader = False + self.use_tmp_kernel = False + self.use_tmp_ramdisk = False self.kernel = None self.ramdisk = None self.cmdline = None @@ -106,6 +108,12 @@ class ImageHandler: self.kernel = vmConfig['PV_kernel'] self.cmdline = vmConfig['PV_args'] self.ramdisk = vmConfig['PV_ramdisk'] + # There a code-paths where use_tmp_xxx is not set at all; but if + # this is set, the variable itself is a boolean. + if 'use_tmp_kernel' in vmConfig and vmConfig['use_tmp_kernel']: + self.use_tmp_kernel = True + if 'use_tmp_ramdisk' in vmConfig and vmConfig['use_tmp_ramdisk']: + self.use_tmp_ramdisk = True self.vm.storeVm(("image/ostype", self.ostype), ("image/kernel", self.kernel), ("image/cmdline", self.cmdline), @@ -135,12 +143,12 @@ class ImageHandler: if 'cpuid_check' in vmConfig: self.cpuid_check = vmConfig['cpuid_check'] - def cleanupBootloading(self): - if self.bootloader: + def cleanupTmpImages(self): + if self.use_tmp_kernel: self.unlink(self.kernel) + if self.use_tmp_ramdisk: self.unlink(self.ramdisk) - def unlink(self, f): if not f: return try: @@ -659,8 +667,6 @@ class ImageHandler: transformed[sinput] = t self.cpuid_check = transformed - - class LinuxImageHandler(ImageHandler): ostype = "linux" @@ -1028,3 +1034,4 @@ def findImageHandlerClass(image): return _handlers[arch.type][image_type] except KeyError: raise VmError('unknown image type: ' + image_type) + diff --git a/tools/tests/run_tests.sh b/tools/tests/run_tests.sh new file mode 100644 index 0000000000..c492876b4b --- /dev/null +++ b/tools/tests/run_tests.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# This runs the available unit-tests with all different supported +# python versions. +# +# To run this this must be 'cd'ed to the tests directory. +# + +ENABLE_UNSUPPORTED=0 + +function usage() +{ + printf "Usage: %s: [-u]\n" $0 + printf " -u: run test with unsupported python versions also\n" +} + +function run_one_test() +{ + PYTHON=$1 + PYTHON_EXECUTABLE=`echo $PYTHON | tr -d "-"` + echo "+++ Running tests with $PYTHON" + export LD_LIBRARY_PATH=./regression/installed/$PYTHON/lib + ./regression/installed/$PYTHON/bin/$PYTHON_EXECUTABLE \ + utests/run_all_tests.py + echo "--- Finished tests with $PYTHON" +} + +function run_all_tests() +{ + for PYTHON in $@; + do + run_one_test $PYTHON + done +} + +while getopts u name +do + case $name in + h) usage; exit 0;; + u) ENABLE_UNSUPPORTED=1;; + ?) usage; exit 2;; + esac +done + +# Build the different python versions +(cd regression && make -j4 runtime-environment) + +# Supported: when an unit test fails this should be seen as an error +PYTHON_SUPPORTED="python-2.4 python-2.5 python-2.6" +# Unsupported: failure should be seen as a hint +PYTHON_UNSUPPORTED="python-3.1" + +export PYTHONPATH=`echo $PWD/../python/build/lib.*`:$PWD + +set -e +run_all_tests $PYTHON_SUPPORTED + +if test $ENABLE_UNSUPPORTED -eq 1 +then + run_all_tests $PYTHON_UNSUPPORTED +fi diff --git a/tools/tests/utests/run_all_tests.py b/tools/tests/utests/run_all_tests.py new file mode 100644 index 0000000000..e36fa4d836 --- /dev/null +++ b/tools/tests/utests/run_all_tests.py @@ -0,0 +1,32 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ + +import unittest + +import utests.ut_util.ut_fileuri +import utests.ut_xend.ut_XendConfig +import utests.ut_xend.ut_image + +suite = unittest.TestSuite( + [utests.ut_util.ut_fileuri.suite(), + utests.ut_xend.ut_XendConfig.suite(), + utests.ut_xend.ut_image.suite(), + ]) + +if __name__ == "__main__": + testresult = unittest.TextTestRunner(verbosity=3).run(suite) + diff --git a/tools/tests/utests/ut_util/ut_fileuri.py b/tools/tests/utests/ut_util/ut_fileuri.py new file mode 100644 index 0000000000..cd64e7d05e --- /dev/null +++ b/tools/tests/utests/ut_util/ut_fileuri.py @@ -0,0 +1,209 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ + +import os +import unittest + +from xen.util.fileuri import scheme_error +from xen.util.fileuri import scheme_data +from xen.util.fileuri import scheme_file +from xen.util.fileuri import schemes + +class scheme_data_unit_tests(unittest.TestCase): + + def check_basic_encoding(self): + "util.fileuri.scheme_data - basic encoding" + sd = scheme_data.encode('Hello!') + self.assertEqual(sd, 'data:application/octet-stream;base64,SGVsbG8h') + + def check_encoding_with_given_mediatype(self): + "util.fileuri.scheme_data - encoding with given media name" + sd = scheme_data.encode('Hello!', 'application/x-my-linux-kernel') + self.assertEqual(sd, + 'data:application/x-my-linux-kernel;base64,SGVsbG8h') + + def check_parse_01(self): + "util.fileuri.scheme_data - parsing of None" + self.assertRaises(scheme_error, scheme_data.parse, None) + + def check_parse_02(self): + "util.fileuri.scheme_data - parsing of empty string" + self.assertRaises(scheme_error, scheme_data.parse, "") + + def check_parse_03(self): + "util.fileuri.scheme_data - parsing of unstructured data" + self.assertRaises(scheme_error, scheme_data.parse, "akskdjdfhezezu") + + def check_parse_04(self): + "util.fileuri.scheme_data - data: is not at the first place" + self.assertRaises(scheme_error, scheme_data.parse, 'ggdata:sossm') + + def check_parse_05(self): + "util.fileuri.scheme_data - no comma in data" + self.assertRaises(scheme_error, scheme_data.parse, 'data:sossm') + + def check_parse_06(self): + "util.fileuri.scheme_data - encoding is empty" + self.assertRaises(scheme_error, scheme_data.parse, 'data:,') + + def check_parse_07(self): + "util.fileuri.scheme_data - unknown encoding" + self.assertRaises(scheme_error, scheme_data.parse, + 'data:somemediatype;unknown,') + + def check_parse_08(self): + "util.fileuri.scheme_data - parse ok - empty data" + mediatype, encoding, data_start = scheme_data.parse( + 'data:somemedia;base64,') + self.assertEqual(mediatype, 'somemedia') + self.assertEqual(encoding, 'base64') + self.assertEqual(data_start, 22) + + def check_parse_09(self): + "util.fileuri.scheme_data - parse ok - some data" + mediatype, encoding, data_start = scheme_data.parse( + 'data:somemedia;base64,HereComesTheSun') + self.assertEqual(mediatype, 'somemedia') + self.assertEqual(encoding, 'base64') + self.assertEqual(data_start, 22) + + def check_parse_10(self): + "util.fileuri.scheme_data - header ok - data error" + self.assertRaises(scheme_error, scheme_data.decode, + 'data:application/octet-stream;base64,H!$ere"Co<mesT>heS_.un') + + def check_cff_file_does_not_exist(self): + "util.fileuri.scheme_data - create from file - non existent file" + self.assertRaises(scheme_error, scheme_data.create_from_file, + "/there/is/hopefully/no/file/like/this") + + def check_cff_ok(self): + "util.fileuri.scheme_data - create from file - ok" + tmppath = "/tmp/scheme_data_check_cff_ok" + f = open(tmppath, "w") + f.write("huhuhu") + f.close() + d = scheme_data.create_from_file(tmppath) + os.unlink(tmppath) + self.assertEqual(d, "data:application/octet-stream;base64,aHVodWh1") + + +class scheme_file_unit_tests(unittest.TestCase): + + def check_encode_empty_filename(self): + "util.fileuri.scheme_file - encode empty filename" + self.assertRaises(scheme_error, scheme_file.encode, "") + + def check_encode_relative_filename(self): + "util.fileuri.scheme_file - encode relative filename" + self.assertRaises(scheme_error, scheme_file.encode, "../there") + + def check_encode_absolut_filename(self): + "util.fileuri.scheme_file - encode absolut filename" + self.assertEqual( + scheme_file.encode("/here/and/there/again"), + 'file:///here/and/there/again') + + def check_decode_01(self): + "util.fileuri.scheme_file - decode empty data" + self.assertRaises(scheme_error, scheme_file.decode, "") + + def check_decode_02(self): + "util.fileuri.scheme_file - decode data with no file:// at the beginning (1)" + self.assertRaises(scheme_error, scheme_file.decode, + "phonehome://bbbb") + + def check_decode_03(self): + "util.fileuri.scheme_file - decode data with no file:// at the beginning (2)" + self.assertRaises(scheme_error, scheme_file.decode, + "file:/bbbb") + + def check_decode_04(self): + "util.fileuri.scheme_file - decode empty path" + self.assertRaises(scheme_error, scheme_file.decode, + "file://") + + def check_decode_05(self): + "util.fileuri.scheme_file - decode empty relative path" + self.assertRaises(scheme_error, scheme_file.decode, + "file://somewhere") + + def check_decode_06(self): + "util.fileuri.scheme_file - decode ok" + path, tmp_file = scheme_file.decode("file:///boot/vmlinuz") + self.assertEqual(path, "/boot/vmlinuz") + self.assertEqual(tmp_file, False) + +class scheme_set_unit_tests(unittest.TestCase): + + def check_data_01(self): + "util.fileuri.scheme_set - data with error in media type" + + u = "data:something_wrong,base64:swer" + uri, tmp_file = schemes.decode(u) + self.assertEqual(uri, u) + self.assertEqual(tmp_file, False) + + def check_data_02(self): + "util.fileuri.scheme_set - data with error in base64 data" + + u = "data:application/octet-stream;base64,S!VsbG8h" + uri, tmp_file = schemes.decode(u) + self.assertEqual(uri, u) + self.assertEqual(tmp_file, False) + + def check_data_03(self): + "util.fileuri.scheme_set - data ok" + + u = "data:application/octet-stream;base64,SGVsbG8h" + uri, tmp_file = schemes.decode(u) + + # Read file contents + f = open(uri, "r") + d = f.read() + f.close() + os.unlink(uri) + + self.assertEqual(d, "Hello!") + self.assertEqual(tmp_file, True) + + def check_file_01(self): + "util.fileuri.scheme_set - file ok" + + f = "/The/Path/To/The/File.txt" + uri, tmp_file = schemes.decode("file://" + f) + self.assertEqual(uri, f) + self.assertEqual(tmp_file, False) + + def check_without_scheme_01(self): + "util.fileuri.scheme_set - without scheme" + + f = "/The/Path/To/The/File.txt" + uri, tmp_file = schemes.decode(f) + self.assertEqual(uri, f) + self.assertEqual(tmp_file, False) + + +def suite(): + return unittest.TestSuite( + [unittest.makeSuite(scheme_data_unit_tests, 'check_'), + unittest.makeSuite(scheme_file_unit_tests, 'check_'), + unittest.makeSuite(scheme_set_unit_tests, 'check_'),]) + +if __name__ == "__main__": + testresult = unittest.TextTestRunner(verbosity=3).run(suite()) + diff --git a/tools/tests/utests/ut_xend/ut_XendConfig.py b/tools/tests/utests/ut_xend/ut_XendConfig.py new file mode 100644 index 0000000000..724ad084b3 --- /dev/null +++ b/tools/tests/utests/ut_xend/ut_XendConfig.py @@ -0,0 +1,117 @@ +#=========================================================================== +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ + +import os +import unittest + +# This does not work because of a cyclic import loop +#from xen.xend.XendConfig import XendConfig +import xen.xend.XendDomain + +class XendConfigUnitTest(unittest.TestCase): + + def minimal_vmconf(self): + return { + 'memory_dynamic_min': 64, + 'memory_dynamic_max': 128, + 'memory_static_max': 128, + } + + def check_hf_01(self): + "xend.XendConfig.handle_fileutils - PV_kernel/ramdisk not set" + vmconf = self.minimal_vmconf() + xc = xen.xend.XendConfig.XendConfig(xapi = vmconf) + + self.assert_(not xc.has_key('use_tmp_kernel')) + self.assert_(not xc.has_key('use_tmp_ramdisk')) + + def check_hf_02(self): + "xend.XendConfig.handle_fileutils - PV_kernel/ramdisk set to some path" + vmconf = self.minimal_vmconf() + vmconf['PV_kernel'] = '/some/where/under/the/rainbow-kernel' + vmconf['PV_ramdisk'] = '/some/where/under/the/rainbow-ramdisk' + xc = xen.xend.XendConfig.XendConfig(xapi = vmconf) + + self.assert_(xc.has_key('use_tmp_kernel')) + self.assert_(xc.has_key('use_tmp_ramdisk')) + + self.assert_(not xc['use_tmp_kernel']) + self.assert_(not xc['use_tmp_ramdisk']) + + def check_hf_03(self): + "xend.XendConfig.handle_fileutils - PV_kernel/ramdisk using file: scheme" + vmconf = self.minimal_vmconf() + vmconf['PV_kernel'] = 'file:///some/where/under/the/rainbow-kernel' + vmconf['PV_ramdisk'] = 'file:///some/where/under/the/rainbow-ramdisk' + xc = xen.xend.XendConfig.XendConfig(xapi = vmconf) + + self.assert_(xc.has_key('use_tmp_kernel')) + self.assert_(xc.has_key('use_tmp_ramdisk')) + + self.assert_(not xc['use_tmp_kernel']) + self.assert_(not xc['use_tmp_ramdisk']) + + self.assert_('PV_kernel' in xc) + self.assert_('PV_ramdisk' in xc) + + self.assertEqual("/some/where/under/the/rainbow-kernel", + xc['PV_kernel']) + self.assertEqual("/some/where/under/the/rainbow-ramdisk", + xc['PV_ramdisk']) + + def check_hf_04(self): + "xend.XendConfig.handle_fileutils - PV_kernel/ramdisk using data: scheme" + vmconf = self.minimal_vmconf() + vmconf['PV_kernel'] = 'data:application/octet-stream;base64,VGhpcyBpcyB0aGUga2VybmVsCg==' + vmconf['PV_ramdisk'] = 'data:application/octet-stream;base64,TXkgZ3JlYXQgcmFtZGlzawo=' + xc = xen.xend.XendConfig.XendConfig(xapi = vmconf) + + self.assert_(xc.has_key('use_tmp_kernel')) + self.assert_(xc.has_key('use_tmp_ramdisk')) + + self.assert_(xc['use_tmp_kernel']) + self.assert_(xc['use_tmp_ramdisk']) + + self.assert_('PV_kernel' in xc) + self.assert_('PV_ramdisk' in xc) + + self.assert_(xc['PV_kernel'].startswith( + "/var/run/xend/boot/data_uri_file.")) + self.assert_(xc['PV_ramdisk'].startswith( + "/var/run/xend/boot/data_uri_file.")) + + f = file(xc['PV_kernel']) + kc = f.read() + f.close() + + f = file(xc['PV_ramdisk']) + rc = f.read() + f.close() + + os.unlink(xc['PV_kernel']) + os.unlink(xc['PV_ramdisk']) + + self.assertEqual(kc, "This is the kernel\n") + self.assertEqual(rc, "My great ramdisk\n") + +def suite(): + return unittest.TestSuite( + [unittest.makeSuite(XendConfigUnitTest, 'check_'),]) + +if __name__ == "__main__": + testresult = unittest.TextTestRunner(verbosity=3).run(suite()) + diff --git a/tools/tests/utests/ut_xend/ut_image.py b/tools/tests/utests/ut_xend/ut_image.py new file mode 100644 index 0000000000..92ec6458f7 --- /dev/null +++ b/tools/tests/utests/ut_xend/ut_image.py @@ -0,0 +1,147 @@ +#=========================================================================== +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ + +import unittest +import tempfile +import os + +import xen.xend.image + +class ImageHandlerUnitTests(unittest.TestCase): + + class ImageHandlerUnitTestsVirtualMachine: + + def __init__(self): + self.info = { + 'name_label': 'ItsMyParty', + } + + def storeVm(self, *args): + pass + + def permissionsVm(self, *args): + pass + + def getDomid(self): + return 7 + + # Sets up a vm_config with no bootloader. + def vm_config_no_bootloader(self): + return { + 'PV_kernel': 'value_of_PV_kernel', + 'PV_args': 'value_of_PV_args', + 'PV_ramdisk': 'value_of_PV_ramdisk', + 'platform': {}, + 'console_refs': [], + } + + def check_configure_01(self): + # This retests the problem reported by Jun Koi on 24.07.2009 + # see http://lists.xensource.com/archives/html/xen-devel/2009-07/msg01006.html + "ImageHandler - call configure with mostly empty vmConfig" + + vmConfig = self.vm_config_no_bootloader() + vm = self.ImageHandlerUnitTestsVirtualMachine() + ih = xen.xend.image.ImageHandler(vm, vmConfig) + + self.assertEqual(ih.use_tmp_kernel, False) + self.assertEqual(ih.use_tmp_ramdisk, False) + + def check_configure_02(self): + "ImageHandler - call configure with use_tmp_xxx set to false" + + vmConfig = self.vm_config_no_bootloader() + vmConfig['use_tmp_kernel'] = False + vmConfig['use_tmp_ramdisk'] = False + vm = self.ImageHandlerUnitTestsVirtualMachine() + ih = xen.xend.image.ImageHandler(vm, vmConfig) + + self.assertEqual(ih.use_tmp_kernel, False) + self.assertEqual(ih.use_tmp_ramdisk, False) + + + def check_configure_03(self): + "ImageHandler - call configure with use_tmp_xxx set to true" + + vmConfig = self.vm_config_no_bootloader() + vmConfig['use_tmp_kernel'] = True + vmConfig['use_tmp_ramdisk'] = True + vm = self.ImageHandlerUnitTestsVirtualMachine() + ih = xen.xend.image.ImageHandler(vm, vmConfig) + + self.assertEqual(ih.use_tmp_kernel, True) + self.assertEqual(ih.use_tmp_ramdisk, True) + + def cleanup_tmp_images_base(self, vmConfig): + vm = self.ImageHandlerUnitTestsVirtualMachine() + ih = xen.xend.image.ImageHandler(vm, vmConfig) + + k, ih.kernel = tempfile.mkstemp( + prefix = "ImageHandler-cleanupTmpImages-k", dir = "/tmp") + r, ih.ramdisk = tempfile.mkstemp( + prefix = "ImageHandler-cleanupTmpImages-r", dir = "/tmp") + + ih.cleanupTmpImages() + + kres = os.path.exists(ih.kernel) + rres = os.path.exists(ih.ramdisk) + + if not ih.use_tmp_kernel: + os.unlink(ih.kernel) + if not ih.use_tmp_ramdisk: + os.unlink(ih.ramdisk) + + return kres, rres + + def check_cleanup_tmp_images_01(self): + "ImageHandler - cleanupTmpImages with use_tmp_xxx unset" + + vmConfig = self.vm_config_no_bootloader() + kres, rres = self.cleanup_tmp_images_base(vmConfig) + + self.assertEqual(kres, True) + self.assertEqual(rres, True) + + def check_cleanup_tmp_images_02(self): + "ImageHandler - cleanupTmpImages with use_tmp_xxx set to false" + + vmConfig = self.vm_config_no_bootloader() + vmConfig['use_tmp_kernel'] = False + vmConfig['use_tmp_ramdisk'] = False + kres, rres = self.cleanup_tmp_images_base(vmConfig) + + self.assertEqual(kres, True) + self.assertEqual(rres, True) + + def check_cleanup_tmp_images_03(self): + "ImageHandler - cleanupTmpImages with use_tmp_xxx set to true" + + vmConfig = self.vm_config_no_bootloader() + vmConfig['use_tmp_kernel'] = True + vmConfig['use_tmp_ramdisk'] = True + kres, rres = self.cleanup_tmp_images_base(vmConfig) + + self.assertEqual(kres, False) + self.assertEqual(rres, False) + +def suite(): + return unittest.TestSuite( + [unittest.makeSuite(ImageHandlerUnitTests, 'check_'),]) + +if __name__ == "__main__": + testresult = unittest.TextTestRunner(verbosity=3).run(suite()) + diff --git a/tools/xm-test/tests/xapi/04_xapi-data_uri_handling.py b/tools/xm-test/tests/xapi/04_xapi-data_uri_handling.py new file mode 100644 index 0000000000..7d1a39b503 --- /dev/null +++ b/tools/xm-test/tests/xapi/04_xapi-data_uri_handling.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2009 flonatel GmbH & Co. KG +#============================================================================ +# +# This file contains test cases for checking the data URI +# functionality: +# kernel and ramdisk are both checked with original uri, +# file uri and data uri (in every constallation) +# + +import copy + +from xen.util.fileuri import schemes, scheme_data, scheme_file + +from XmTestLib import * +from XmTestLib.network_utils import * +from XmTestLib.XenAPIDomain import XmTestAPIDomain + +kernel_orig_uri = arch.configDefaults['kernel'] +ramdisk_orig_uri = arch.configDefaults['ramdisk'] +kernel_data_uri = scheme_data.create_from_file(kernel_orig_uri) +ramdisk_data_uri = scheme_data.create_from_file(ramdisk_orig_uri) +kernel_file_uri = scheme_file.encode(kernel_orig_uri) +ramdisk_file_uri = scheme_file.encode(ramdisk_orig_uri) + +config = copy.copy(arch.configDefaults) + +for kernel in (kernel_orig_uri, kernel_data_uri, kernel_file_uri): + for ramdisk in (ramdisk_orig_uri, ramdisk_data_uri, ramdisk_file_uri): + config['kernel'] = kernel + config['ramdisk'] = ramdisk + print("Using kernel='%s' ramdisk='%s'" % (kernel[:100], ramdisk[:100])) + try: + guest = XmTestAPIDomain(baseConfig = config) + console = guest.start() + except DomainError, e: + if verbose: + print("Failed to create test domain because: %s" % e.extra) + FAIL(str(e)) + + try: + run = console.runCmd("ls /") + if run['return'] > 0: + FAIL("Could not start host") + except ConsoleError, e: + saveLog(console.getHistory()) + FAIL(str(e)) + + guest.closeConsole() + guest.stop() + diff --git a/tools/xm-test/tests/xapi/Makefile.am b/tools/xm-test/tests/xapi/Makefile.am index 3ae8afffb0..bfdf27c660 100644 --- a/tools/xm-test/tests/xapi/Makefile.am +++ b/tools/xm-test/tests/xapi/Makefile.am @@ -2,7 +2,8 @@ SUBDIRS = TESTS = 01_xapi-vm_basic.test \ 02_xapi-vbd_basic.test \ - 03_xapi-network_pos.test + 03_xapi-network_pos.test \ + 04_xapi-data_uri_handling.test XFAIL_TESTS = |