From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andrew Cooper Subject: [PATCH v4 13/29] tools/python: Libxl migration v2 infrastructure Date: Tue, 14 Jul 2015 11:59:28 +0100 Message-ID: <1436871584-6522-14-git-send-email-andrew.cooper3@citrix.com> References: <1436871584-6522-1-git-send-email-andrew.cooper3@citrix.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1436871584-6522-1-git-send-email-andrew.cooper3@citrix.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: Xen-devel Cc: Andrew Cooper , Ian Jackson , Wei Liu List-Id: xen-devel@lists.xenproject.org Contains: * Python implementation of the libxl migration v2 records * Verification code for spec compliance * Unit tests Signed-off-by: Andrew Cooper Acked-by: Ian Campbell CC: Ian Jackson CC: Wei Liu --- tools/python/xen/migration/libxl.py | 188 +++++++++++++++++++++++++++++++++++ tools/python/xen/migration/tests.py | 13 +++ 2 files changed, 201 insertions(+) create mode 100644 tools/python/xen/migration/libxl.py diff --git a/tools/python/xen/migration/libxl.py b/tools/python/xen/migration/libxl.py new file mode 100644 index 0000000..4e1f4f8 --- /dev/null +++ b/tools/python/xen/migration/libxl.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Libxl Migration v2 streams + +Record structures as per docs/specs/libxl-migration-stream.pandoc, and +verification routines. +""" + +import sys + +from struct import calcsize, unpack +from xen.migration.verify import StreamError, RecordError, VerifyBase +from xen.migration.libxc import VerifyLibxc + +# Header +HDR_FORMAT = "!QII" + +HDR_IDENT = 0x4c6962786c466d74 # "LibxlFmt" in ASCII +HDR_VERSION = 2 + +HDR_OPT_BIT_ENDIAN = 0 +HDR_OPT_BIT_LEGACY = 1 + +HDR_OPT_LE = (0 << HDR_OPT_BIT_ENDIAN) +HDR_OPT_BE = (1 << HDR_OPT_BIT_ENDIAN) +HDR_OPT_LEGACY = (1 << HDR_OPT_BIT_LEGACY) + +HDR_OPT_RESZ_MASK = 0xfffc + +# Records +RH_FORMAT = "II" + +REC_TYPE_end = 0x00000000 +REC_TYPE_libxc_context = 0x00000001 +REC_TYPE_xenstore_data = 0x00000002 +REC_TYPE_emulator_context = 0x00000003 + +rec_type_to_str = { + REC_TYPE_end : "End", + REC_TYPE_libxc_context : "Libxc context", + REC_TYPE_xenstore_data : "Xenstore data", + REC_TYPE_emulator_context : "Emulator context", +} + +# emulator_context +EMULATOR_CONTEXT_FORMAT = "II" + +EMULATOR_ID_unknown = 0x00000000 +EMULATOR_ID_qemu_trad = 0x00000001 +EMULATOR_ID_qemu_upstream = 0x00000002 + +emulator_id_to_str = { + EMULATOR_ID_unknown : "Unknown", + EMULATOR_ID_qemu_trad : "Qemu Traditional", + EMULATOR_ID_qemu_upstream : "Qemu Upstream", +} + + +# +# libxl format +# + +LIBXL_QEMU_SIGNATURE = "DeviceModelRecord0002" +LIBXL_QEMU_RECORD_HDR = "=%dsI" % (len(LIBXL_QEMU_SIGNATURE), ) + +class VerifyLibxl(VerifyBase): + """ Verify a Libxl v2 stream """ + + def __init__(self, info, read): + VerifyBase.__init__(self, info, read) + + + def verify(self): + """ Verity a libxl stream """ + + self.verify_hdr() + + while self.verify_record() != REC_TYPE_end: + pass + + + def verify_hdr(self): + """ Verify a Header """ + ident, version, options = self.unpack_exact(HDR_FORMAT) + + if ident != HDR_IDENT: + raise StreamError("Bad image id: Expected 0x%x, got 0x%x" + % (HDR_IDENT, ident)) + + if version != HDR_VERSION: + raise StreamError("Unknown image version: Expected %d, got %d" + % (HDR_VERSION, version)) + + if options & HDR_OPT_RESZ_MASK: + raise StreamError("Reserved bits set in image options field: 0x%x" + % (options & HDR_OPT_RESZ_MASK)) + + if ( (sys.byteorder == "little") and + ((options & HDR_OPT_BIT_ENDIAN) != HDR_OPT_LE) ): + raise StreamError( + "Stream is not native endianess - unable to validate") + + endian = ["little", "big"][options & HDR_OPT_LE] + + if options & HDR_OPT_LEGACY: + self.info("Libxl Header: %s endian, legacy converted" % (endian, )) + else: + self.info("Libxl Header: %s endian" % (endian, )) + + + def verify_record(self): + """ Verify an individual record """ + rtype, length = self.unpack_exact(RH_FORMAT) + + if rtype not in rec_type_to_str: + raise StreamError("Unrecognised record type %x" % (rtype, )) + + self.info("Libxl Record: %s, length %d" + % (rec_type_to_str[rtype], length)) + + contentsz = (length + 7) & ~7 + content = self.rdexact(contentsz) + + padding = content[length:] + if padding != "\x00" * len(padding): + raise StreamError("Padding containing non0 bytes found") + + if rtype not in record_verifiers: + raise RuntimeError("No verification function for libxl record '%s'" + % rec_type_to_str[rtype]) + else: + record_verifiers[rtype](self, content[:length]) + + return rtype + + + def verify_record_end(self, content): + """ End record """ + + if len(content) != 0: + raise RecordError("End record with non-zero length") + + + def verify_record_libxc_context(self, content): + """ Libxc context record """ + + if len(content) != 0: + raise RecordError("Libxc context record with non-zero length") + + # Verify the libxc stream, as we can't seek forwards through it + VerifyLibxc(self.info, self.read).verify() + + + def verify_record_xenstore_data(self, content): + """ Xenstore Data record """ + + if len(content) == 0: + raise RecordError("Xenstore data record with zero length") + + + def verify_record_emulator_context(self, content): + """ Emulator Context record """ + minsz = calcsize(EMULATOR_CONTEXT_FORMAT) + + if len(content) < minsz: + raise RecordError("Length must be at least %d bytes, got %d" + % (minsz, len(content))) + + emu_id, emu_idx = unpack(EMULATOR_CONTEXT_FORMAT, content[:minsz]) + + if emu_id not in emulator_id_to_str: + raise RecordError("Unrecognised emulator id 0x%x" % (emu_id, )) + + self.info(" Index %d, type %s" % (emu_idx, emulator_id_to_str[emu_id])) + + +record_verifiers = { + REC_TYPE_end: + VerifyLibxl.verify_record_end, + REC_TYPE_libxc_context: + VerifyLibxl.verify_record_libxc_context, + REC_TYPE_xenstore_data: + VerifyLibxl.verify_record_xenstore_data, + REC_TYPE_emulator_context: + VerifyLibxl.verify_record_emulator_context, +} diff --git a/tools/python/xen/migration/tests.py b/tools/python/xen/migration/tests.py index 3e97268..91044cd 100644 --- a/tools/python/xen/migration/tests.py +++ b/tools/python/xen/migration/tests.py @@ -30,10 +30,23 @@ class TestLibxc(unittest.TestCase): self.assertEqual(calcsize(fmt), sz) +class TestLibxl(unittest.TestCase): + + def test_format_sizes(self): + + for fmt, sz in ( (libxl.HDR_FORMAT, 16), + (libxl.RH_FORMAT, 8), + + (libxl.EMULATOR_CONTEXT_FORMAT, 8), + ): + self.assertEqual(calcsize(fmt), sz) + + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestLibxc)) + suite.addTest(unittest.makeSuite(TestLibxl)) return suite -- 1.7.10.4