|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 3/6] tools/libxc: Scripts for inspection/valdiation of legacy and new streams
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>
---
tools/libxc/saverestore/scripts/generate.py | 59 ++++
.../libxc/saverestore/scripts/inspect-legacy32.py | 167 ++++++++++
tools/libxc/saverestore/scripts/streamspec.py | 89 ++++++
tools/libxc/saverestore/scripts/verify.py | 330 ++++++++++++++++++++
4 files changed, 645 insertions(+)
create mode 100755 tools/libxc/saverestore/scripts/generate.py
create mode 100755 tools/libxc/saverestore/scripts/inspect-legacy32.py
create mode 100644 tools/libxc/saverestore/scripts/streamspec.py
create mode 100755 tools/libxc/saverestore/scripts/verify.py
diff --git a/tools/libxc/saverestore/scripts/generate.py
b/tools/libxc/saverestore/scripts/generate.py
new file mode 100755
index 0000000..3b01e65
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/generate.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from streamspec import *
+import struct, sys
+
+ihdr = struct.pack(IHDR_FORMAT,
+ 0xffffffffffffffff, # Marker
+ IHDR_IDENT, # "XENF" in ASCII
+ 1, # Version
+ IHDR_OPT_LE, # Options
+ 0, 0 # Reserved
+ )
+
+def emit_record(type, data):
+ length = len(data)
+
+ r = struct.pack(RH_FORMAT, type, length)
+ r += data
+
+ padding_len = (8 - (length & 7)) & 7
+ r += '\x00' * padding_len
+
+ sys.stdout.write(r)
+
+
+def emit_pv():
+
+ dhdr = struct.pack(DHDR_FORMAT,
+ DHDR_TYPE_x86_pv, # Type
+ 12, # Page size
+ 0, # Reserved
+ 4, # Xen major
+ 5 # Xen minor
+ )
+
+ sys.stdout.write(ihdr)
+ sys.stdout.write(dhdr)
+
+ x86_pv_info = struct.pack(X86_PV_INFO_FORMAT,
+ 8, # Guest width
+ 4, # Guest levels
+ 0 # Options
+ )
+
+ emit_record(REC_TYPE_x86_pv_info, x86_pv_info)
+ emit_record(REC_TYPE_end, "")
+ return 0
+
+
+def main(argv = sys.argv):
+
+ if len(argv) == 0 or argv[0] == "pv":
+ return emit_pv()
+ else:
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/libxc/saverestore/scripts/inspect-legacy32.py
b/tools/libxc/saverestore/scripts/inspect-legacy32.py
new file mode 100755
index 0000000..e20a8f2
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/inspect-legacy32.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import struct
+
+fin = None
+guest_width = 0
+guest_levels = 0
+
+class StreamError(StandardError):
+ pass
+
+def rdexact(n):
+ _ = fin.read(n)
+ if len(_) != n:
+ raise IOError("Stream truncated")
+ #print"> read 0x%x bytes" % (n, )
+ return _
+
+def unpack_exact(fmt):
+ l = struct.calcsize(fmt)
+ return struct.unpack(fmt, rdexact(l))
+
+def read_extended_info():
+ global guest_width, guest_level
+
+ sig, rem_length = struct.unpack("II", rdexact(8))
+
+ if sig != 0xffffffff:
+ raise StreamError("Bad extended info signature 0x%08x" % (sig,))
+ else:
+ print "Endended Info: length 0x%x" % (rem_length,)
+
+ so_far = 0
+ while so_far < rem_length:
+
+ blkid, datasz = struct.unpack("4sI", rdexact(8))
+ so_far += 8
+
+ print " Record type: %s, size 0x%x" % (blkid, datasz)
+
+ # Eww, but this is how it is done :(
+ if blkid == "vcpu":
+ if datasz == 0x1430:
+ guest_width = 8
+ guest_levels = 4
+ print " 64bit domain, 4 levels"
+ else:
+ raise StreamError("Unable to determine guest width/level")
+
+ rdexact(datasz)
+ so_far += datasz
+
+ if so_far != rem_length:
+ raise StreamError("Overshot total Extended Info size. Consumed 0x%x
bytes" % (so_far,))
+
+def read_chunks():
+
+ while True:
+
+ chunk_type, = struct.unpack("i", rdexact(4))
+ print "Chunk: type 0x%x" % (chunk_type,)
+
+ if chunk_type == 0:
+ print " End"
+ return
+
+ elif chunk_type > 0:
+ print " Page Batch"
+ pfn_array = rdexact(chunk_type * 4)
+ page_data = rdexact(chunk_type * 4096)
+
+ elif chunk_type == -2:
+ max_id, = unpack_exact("i")
+ bitmap = rdexact(((max_id/64) + 1) * 8)
+ print " Vcpu info: max_id %d" % (max_id, )
+
+ elif chunk_type == -7:
+ mode, nsec, khz, incarn = unpack_exact("IQII")
+ print " TSC_INFO: mode %s, %d ns, %d khz, %d incarn" % ( mode,
nsec, khz, incarn)
+
+ elif chunk_type == -9:
+ print " Last Checkpoint"
+
+ elif chunk_type == -12:
+ sz, = unpack_exact("I")
+ data = rdexact(sz)
+ print " Compressed Data: sz 0x%x" % (sz, )
+
+ elif chunk_type == -18:
+ sz, = unpack_exact("I")
+ data = rdexact(sz)
+ print " Toolstack Data: sz 0x%x" % (sz, )
+
+ else:
+ raise StreamError("Unrecognised chunk")
+
+def main(argv = sys.argv):
+ global fin
+
+ if len(argv) == 2:
+ fin = open(argv[1], "rb")
+ else:
+ fin = sys.stdin
+
+ try:
+ # Skip Xl header
+ if "Xen saved domain, xl format\n \0 \r" != rdexact(32):
+ raise StreamError("No xl header")
+
+ _, _, _, optlen = struct.unpack("=IIII", rdexact(16))
+ rdexact(optlen)
+ print "xl header skipped"
+
+ # P2M size
+ p2m_size, = struct.unpack("I", rdexact(4))
+ print "P2M Size: 0x%x" % (p2m_size,)
+
+ # Extended info
+ read_extended_info()
+
+ # P2M list
+
+ fpp = 4096/guest_width
+ p2m_len = (p2m_size + fpp - 1) / fpp
+
+ print "Reading p2m frames. fpp: %d, p2m_len: %d" % (fpp, p2m_len)
+
+ p2m_frames = rdexact(p2m_len * 4)
+ if p2m_len < 20:
+ print list(struct.unpack("I" * p2m_len, p2m_frames))
+
+ read_chunks()
+
+ unmapped_pfn_count, = unpack_exact("I")
+ unmapped_pfn_list = rdexact(unmapped_pfn_count * 4)
+ print "Unmapped PFN count: 0x%x" % (unmapped_pfn_count, )
+
+ # VCPU Context fudge
+ _ = rdexact(0x1430)
+ _ = rdexact(128)
+ xfeature_mask, xsize = unpack_exact("QQ")
+ _ = rdexact(xsize)
+ print "Got VCPU information"
+
+ shared_info = rdexact(4096)
+ print "Got shinfo"
+
+ if fin.read(1) != "":
+ raise StreamError("Junk found on the end of the stream")
+
+ except (IOError, StreamError, ) as e:
+ print "Error: ", e
+ return 1
+
+ except RuntimeError as e:
+ print "Script error", e
+ print "Please fix me"
+ return 2
+
+ print "Done"
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/tools/libxc/saverestore/scripts/streamspec.py
b/tools/libxc/saverestore/scripts/streamspec.py
new file mode 100644
index 0000000..12b351e
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/streamspec.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Image Header
+IHDR_FORMAT = "!QIIHHI"
+
+IHDR_IDENT = 0x58454E46 # "XENF" in ASCII
+
+IHDR_OPT_ENDIAN_ = 0
+IHDR_OPT_LE = (0 << IHDR_OPT_ENDIAN_)
+IHDR_OPT_BE = (1 << IHDR_OPT_ENDIAN_)
+
+IHDR_OPT_RESZ_MASK = 0xfffe
+
+# Domain Header
+DHDR_FORMAT = "=IHHII"
+
+DHDR_TYPE_x86_pv = 0x00000001
+DHDR_TYPE_x86_hvm = 0x00000002
+DHDR_TYPE_x86_pvh = 0x00000003
+DHDR_TYPE_arm = 0x00000004
+
+dhdr_type_to_str = {
+ DHDR_TYPE_x86_pv : "x86 PV",
+ DHDR_TYPE_x86_hvm : "x86 HVM",
+ DHDR_TYPE_x86_pvh : "x86 PVH",
+ DHDR_TYPE_arm : "ARM",
+}
+
+RH_FORMAT = "=II"
+
+REC_TYPE_end = 0x00000000
+REC_TYPE_page_data = 0x00000001
+REC_TYPE_x86_pv_info = 0x00000002
+REC_TYPE_x86_pv_p2m_frames = 0x00000003
+REC_TYPE_x86_pv_vcpu_basic = 0x00000004
+REC_TYPE_x86_pv_vcpu_extended = 0x00000005
+REC_TYPE_x86_pv_vcpu_xsave = 0x00000006
+REC_TYPE_x86_pv_shared_info = 0x00000007
+REC_TYPE_tsc_info = 0x00000008
+
+rec_type_to_str = {
+ REC_TYPE_end : "End",
+ REC_TYPE_page_data : "Page data",
+ REC_TYPE_x86_pv_info : "x86 PV info",
+ REC_TYPE_x86_pv_p2m_frames : "x86 PV P2M frames",
+ REC_TYPE_x86_pv_vcpu_basic : "x86 PV vcpu basic",
+ REC_TYPE_x86_pv_vcpu_extended : "x86 PV vcpu extended",
+ REC_TYPE_x86_pv_vcpu_xsave : "x86 PV vcpu xsave",
+ REC_TYPE_x86_pv_shared_info : "x86 PV shared info",
+ REC_TYPE_tsc_info : "TSC info",
+}
+
+# page_data
+PAGE_DATA_FORMAT = "=II"
+PAGE_DATA_PFN_MASK = (1L << 52) - 1
+PAGE_DATA_PFN_RESZ_MASK = ((1L << 60) - 1) & ~((1L << 52) - 1)
+
+# flags from xen/public/domctl.h: XEN_DOMCTL_PFINFO_* shifted by 32 bits
+PAGE_DATA_TYPE_SHIFT = 60
+PAGE_DATA_TYPE_LTABTYPE_MASK = (0x7L << PAGE_DATA_TYPE_SHIFT)
+PAGE_DATA_TYPE_LTAB_MASK = (0xfL << PAGE_DATA_TYPE_SHIFT)
+PAGE_DATA_TYPE_LPINTAB = (0x8L << PAGE_DATA_TYPE_SHIFT) # Pinned
pagetable
+
+PAGE_DATA_TYPE_NOTAB = (0x0L << PAGE_DATA_TYPE_SHIFT) # Regular page
+PAGE_DATA_TYPE_L1TAB = (0x1L << PAGE_DATA_TYPE_SHIFT) # L1 pagetable
+PAGE_DATA_TYPE_L2TAB = (0x2L << PAGE_DATA_TYPE_SHIFT) # L2 pagetable
+PAGE_DATA_TYPE_L3TAB = (0x3L << PAGE_DATA_TYPE_SHIFT) # L3 pagetable
+PAGE_DATA_TYPE_L4TAB = (0x4L << PAGE_DATA_TYPE_SHIFT) # L4 pagetable
+PAGE_DATA_TYPE_BROKEN = (0xdL << PAGE_DATA_TYPE_SHIFT) # Broken
+PAGE_DATA_TYPE_XALLOC = (0xeL << PAGE_DATA_TYPE_SHIFT) # Allocate-only
+PAGE_DATA_TYPE_XTAB = (0xfL << PAGE_DATA_TYPE_SHIFT) # Invalid
+
+# x86_pv_info
+X86_PV_INFO_FORMAT = "=BBB"
+
+X86_PV_INFO_OPT_VMASST_ = 0
+X86_PV_INFO_OPT_VMASST = (1 << X86_PV_INFO_OPT_VMASST_)
+
+X86_PV_INFO_OPT_RESZ_MASK = 0xfe
+
+# x86_pv_vcpu_{basic,extended}
+X86_PV_VCPU_FORMAT = "=II"
+
+# x86_pv_vcpu_xsave
+X86_PV_VCPU_XSAVE_FORMAT = "=IIQ"
+
+# tsc_info
+TSC_INFO_FORMAT = "=IIQI"
diff --git a/tools/libxc/saverestore/scripts/verify.py
b/tools/libxc/saverestore/scripts/verify.py
new file mode 100755
index 0000000..0b1ec14
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/verify.py
@@ -0,0 +1,330 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import struct
+
+from streamspec import *
+
+class StreamError(StandardError):
+ pass
+
+class RecordError(StandardError):
+ pass
+
+def skip_xl_header(stream):
+
+ magic = stream.read(8)
+ if magic != "mat\n \0 \r":
+ return False
+
+ header = stream.read(16)
+ if len(header) != 16:
+ return False
+
+ _, _, _, optlen = struct.unpack("=IIII", header)
+
+ optdata = stream.read(optlen)
+ if len(optdata) != optlen:
+ return False
+
+ return True
+
+
+def verify_ihdr(stream):
+ """ Verify an image header """
+
+ datasz = struct.calcsize(IHDR_FORMAT)
+ data = stream.read(datasz)
+
+ # xl header record?
+ if data == "Xen saved domain, xl for":
+ if skip_xl_header(stream):
+ data = stream.read(datasz)
+ else:
+ raise StreamError("Invalid looking xl header on the stream")
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ marker, id, version, options, res1, res2 = struct.unpack(IHDR_FORMAT, data)
+
+ if marker != 0xffffffffffffffff:
+ raise StreamError("Bad image marker: Expected 0xffffffffffffffff, "
+ "got 0x%x" % (marker, ))
+
+ if id != 0x58454e46:
+ raise StreamError("Bad image id: Expected 0x0x58454e46, got 0x%x"
+ % (id, ))
+
+ if version != 1:
+ raise StreamError("Unknown image version: Expected 1, got %d"
+ % (version, ))
+
+ if options & IHDR_OPT_RESZ_MASK:
+ raise StreamError("Reserved bits set in image options field: 0x%x"
+ % (options & IHDR_OPT_RESZ_MASK))
+
+ if res1 != 0 or res2 != 0:
+ raise StreamError("Reserved bits set in image header: 0x%04x:0x%08x"
+ % (res1, res2))
+
+ if ( sys.byteorder == "little" and
+ (options & IHDR_OPT_ENDIAN_) != IHDR_OPT_LE ):
+ raise StreamError("Stream is not native endianess - unable to
validate")
+
+ print "Valid Image Header:",
+ if options & IHDR_OPT_BE:
+ print "big endian"
+ else:
+ print "little endian"
+
+def verify_dhdr(stream):
+ """ Verify a domain header """
+
+ datasz = struct.calcsize(DHDR_FORMAT)
+ data = stream.read(datasz)
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ type, page_shift, res1, major, minor = struct.unpack(DHDR_FORMAT, data)
+
+ if type not in dhdr_type_to_str:
+ raise StreamError("Unrecognised domain type 0x%x" % (type, ))
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in domain header 0x%04x"
+ % (res1, ))
+
+ if page_shift != 12:
+ raise StreamError("Page shift expected to be 12. Got %d"
+ % (page_shift, ))
+
+ print "Valid Domain Header: %s from Xen %d.%d (page sz %d)" \
+ % (dhdr_type_to_str[type], major, minor, 2**page_shift)
+
+
+def verify_record_end(content):
+
+ if len(content) != 0:
+ raise RecordError("End record with non-zero length")
+
+def verify_page_data(content):
+ minsz = struct.calcsize(PAGE_DATA_FORMAT)
+
+ if len(content) <= minsz:
+ raise RecordError("PAGE_DATA record must be at least %d bytes long"
+ % (minsz, ))
+
+ count, res1 = struct.unpack_from(PAGE_DATA_FORMAT, content)
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in PAGE_DATA record 0x%04x"
+ % (res1, ))
+
+ pfnsz = count * 8
+ if (len(content) - minsz) < pfnsz:
+ raise RecordError("PAGE_DATA record must contain a pfn record for "
+ "each count")
+
+ pfns = list(struct.unpack_from("=%dQ" % (count,), content, minsz))
+
+ nr_pages = 0
+ for idx, pfn in enumerate(pfns):
+
+ if pfn & PAGE_DATA_PFN_RESZ_MASK:
+ raise RecordError("Reserved bits set in pfn[%d]: 0x%016x",
+ idx, pfn & PAGE_DATA_PFN_RESZ_MASK)
+
+ if pfn >> PAGE_DATA_TYPE_SHIFT in (5, 6, 7, 8):
+ raise RecordError("Invalid type value in pfn[%d]: 0x%016x",
+ idx, pfn & PAGE_DATA_TYPE_LTAB_MASK)
+
+ # We expect page data for each normal page or pagetable
+ if PAGE_DATA_TYPE_NOTAB <= (pfn & PAGE_DATA_TYPE_LTABTYPE_MASK) <=
PAGE_DATA_TYPE_L4TAB:
+ nr_pages += 1
+
+ pagesz = nr_pages * 4096
+ if len(content) != minsz + pfnsz + pagesz:
+ raise RecordError("Wrong size")
+
+
+def verify_record_x86_pv_vcpu_generic(content, name):
+ # Generic for both REC_TYPE_x86_pv_vcpu_{basic,extended}
+ minsz = struct.calcsize(X86_PV_VCPU_FORMAT)
+
+ if len(content) <= minsz:
+ raise RecordError("X86_PV_VCPU_%s record length must be at least %d"
+ " bytes long" % (name, minsz))
+
+ vcpuid, res1 = struct.unpack_from(X86_PV_VCPU_FORMAT, content)
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in x86_pv_vcpu_%s record 0x%04x"
+ % (name, res1))
+
+ print " vcpu%d %s context, %d bytes" % (vcpuid, name, len(content) -
minsz)
+
+def verify_record_x86_pv_vcpu_xsave(content):
+ minsz = struct.calcsize(X86_PV_VCPU_XSAVE_FORMAT)
+
+ if len(content) <= minsz:
+ raise RecordError("X86_PV_VCPU_XSAVE record length must be at least %d"
+ " bytes long" % (minsz, ))
+
+ vcpuid, res1, xmask = struct.unpack_from(X86_PV_VCPU_XSAVE_FORMAT,
+ content)
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in X86_PV_VCPU_XSAVE record "
+ "0x%04x" % (res1, ))
+
+ print " vcpu%d xsave context, mask 0x%x" % (vcpuid, xmask)
+
+
+def verify_x86_pv_info(content):
+
+ if len(content) != 3:
+ raise RecordError("x86_pf_info: expected length of 3, got %d"
+ % (len(content), ))
+
+ width, levels, options = struct.unpack(X86_PV_INFO_FORMAT, content)
+
+ if width not in (4, 8):
+ raise RecordError("Expected width of 4 or 8, got %d" % (width, ))
+
+ if levels not in (4, 8):
+ raise RecordError("Expected levels of 3 or 4, got %d" % (levels, ))
+
+ if (options & X86_PV_INFO_OPT_RESZ_MASK) != 0:
+ raise StreamError("Reserved bits set in X86_PV_INFO options: 0x%02x"
+ % (options & X86_PV_INFO_OPT_RESZ_MASK, ))
+
+ bitness = {4:32, 8:64}[width]
+
+ print " %sbit guest, %d levels of pagetables" % (bitness, levels)
+
+def verify_x86_pv_p2m_frames(content):
+
+ if len(content) % 8 != 0:
+ raise RecordError("Length expected to be a multiple of 8, not %d"
+ % (len(content), ))
+
+ start, end = struct.unpack_from("=II", content)
+
+ print " Start pfn 0x%x, End 0x%x" % (start, end)
+
+def verify_record_x86_pv_shared_info(content):
+
+ if len(content) != 4096:
+ raise RecordError("Length expected to be 4906 bytes, not %d"
+ % (len(content), ))
+
+def verify_record_tsc_info(content):
+
+ sz = struct.calcsize(TSC_INFO_FORMAT)
+
+ if len(content) != sz:
+ raise RecordError("Length should be %u bytes" % (sz, ))
+
+ mode, khz, nsec, incarn = struct.unpack(TSC_INFO_FORMAT, content)
+ print (" Mode %u, %u kHz, %u ns, incarnation %d"
+ % (mode, khz, nsec, incarn))
+
+record_verifiers = {
+ REC_TYPE_end : verify_record_end,
+ REC_TYPE_page_data : verify_page_data,
+
+ REC_TYPE_x86_pv_info: verify_x86_pv_info,
+ REC_TYPE_x86_pv_p2m_frames: verify_x86_pv_p2m_frames,
+
+ REC_TYPE_x86_pv_vcpu_basic :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "basic"),
+ REC_TYPE_x86_pv_vcpu_extended :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "extended"),
+ REC_TYPE_x86_pv_vcpu_xsave : verify_record_x86_pv_vcpu_xsave,
+
+ REC_TYPE_x86_pv_shared_info: verify_record_x86_pv_shared_info,
+ REC_TYPE_tsc_info: verify_record_tsc_info,
+}
+
+_squahsed_data_records = 0
+def verify_record(stream):
+ """ Verify a record """
+ global _squahsed_data_records
+
+ datasz = struct.calcsize(RH_FORMAT)
+ data = stream.read(datasz)
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ type, length = struct.unpack(RH_FORMAT, data)
+
+ if type not in rec_type_to_str:
+ raise StreamError("Unrecognised record type %x" % (type, ))
+
+ contentsz = (length + 7) & ~7
+ content = stream.read(contentsz)
+
+ if len(content) != contentsz:
+ raise IOError("Truncated stream")
+
+ padding = content[length:]
+ if padding != "\x00" * len(padding):
+ raise StreamError("Padding containging non0 bytes found")
+
+ if type != REC_TYPE_page_data:
+
+ if _squahsed_data_records > 0:
+ print ("Squashed %d valid Page Data records together"
+ % (_squahsed_data_records, ))
+ _squahsed_data_records = 0
+
+ print ("Valid Record Header: %s, length %d"
+ % (rec_type_to_str[type], length))
+
+ else:
+ _squahsed_data_records += 1
+
+ if type not in record_verifiers:
+ raise RuntimeError("No verification function")
+ else:
+ record_verifiers[type](content[:length])
+
+ return type
+
+
+def main(argv = sys.argv):
+
+ if len(argv) == 2:
+ fin = open(argv[1], "rb")
+ else:
+ fin = sys.stdin
+
+ try:
+ verify_ihdr(fin)
+ verify_dhdr(fin)
+
+ while verify_record(fin) != REC_TYPE_end:
+ pass
+
+ if fin.read(1) != "":
+ raise StreamError("Junk found on the end of the stream")
+
+ except (IOError, StreamError, RecordError) as e:
+ print "Error: ", e
+ return 1
+
+ except RuntimeError as e:
+ print "Script error", e
+ print "Please fix me"
+ return 2
+
+ print "Done"
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
--
1.7.10.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |