1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4""" Verify a v2 format migration stream """ 5 6import sys 7import struct 8import os, os.path 9import syslog 10import traceback 11 12from xen.migration.verify import StreamError, RecordError 13from xen.migration.libxc import VerifyLibxc 14from xen.migration.libxl import VerifyLibxl 15 16fin = None # Input file/fd 17log_to_syslog = False # Boolean - Log to syslog instead of stdout/err? 18verbose = False # Boolean - Summarise stream contents 19quiet = False # Boolean - Suppress error printing 20 21def info(msg): 22 """Info message, routed to appropriate destination""" 23 if not quiet and verbose: 24 if log_to_syslog: 25 for line in msg.split("\n"): 26 syslog.syslog(syslog.LOG_INFO, line) 27 else: 28 print msg 29 30def err(msg): 31 """Error message, routed to appropriate destination""" 32 if not quiet: 33 if log_to_syslog: 34 for line in msg.split("\n"): 35 syslog.syslog(syslog.LOG_ERR, line) 36 print >> sys.stderr, msg 37 38def stream_read(_ = None): 39 """Read from input""" 40 return fin.read(_) 41 42def rdexact(nr_bytes): 43 """Read exactly nr_bytes from fin""" 44 _ = stream_read(nr_bytes) 45 if len(_) != nr_bytes: 46 raise IOError("Stream truncated") 47 return _ 48 49def unpack_exact(fmt): 50 """Unpack a format from fin""" 51 sz = struct.calcsize(fmt) 52 return struct.unpack(fmt, rdexact(sz)) 53 54 55def skip_xl_header(): 56 """Skip over an xl header in the stream""" 57 58 hdr = rdexact(32) 59 if hdr != "Xen saved domain, xl format\n \0 \r": 60 raise StreamError("No xl header") 61 62 _, mflags, _, optlen = unpack_exact("=IIII") 63 _ = rdexact(optlen) 64 65 info("Processed xl header") 66 67 if mflags & 2: # XL_MANDATORY_FLAG_STREAMv2 68 return "libxl" 69 else: 70 return "libxc" 71 72def read_stream(fmt): 73 """ Read an entire stream """ 74 75 try: 76 if fmt == "xl": 77 fmt = skip_xl_header() 78 79 if fmt == "libxc": 80 VerifyLibxc(info, stream_read).verify() 81 else: 82 VerifyLibxl(info, stream_read).verify() 83 84 except (IOError, StreamError, RecordError): 85 err("Stream Error:") 86 err(traceback.format_exc()) 87 return 1 88 89 except StandardError: 90 err("Script Error:") 91 err(traceback.format_exc()) 92 err("Please fix me") 93 return 2 94 95 return 0 96 97def open_file_or_fd(val, mode, buffering): 98 """ 99 If 'val' looks like a decimal integer, open it as an fd. If not, try to 100 open it as a regular file. 101 """ 102 103 fd = -1 104 try: 105 # Does it look like an integer? 106 try: 107 fd = int(val, 10) 108 except ValueError: 109 pass 110 111 # Try to open it... 112 if fd != -1: 113 return os.fdopen(fd, mode, buffering) 114 else: 115 return open(val, mode, buffering) 116 117 except StandardError, e: 118 if fd != -1: 119 err("Unable to open fd %d: %s: %s" % 120 (fd, e.__class__.__name__, e)) 121 else: 122 err("Unable to open file '%s': %s: %s" % 123 (val, e.__class__.__name__, e)) 124 125 raise SystemExit(2) 126 127def main(): 128 """ main """ 129 from optparse import OptionParser 130 global fin, quiet, verbose 131 132 # Change stdout to be line-buffered. 133 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) 134 135 parser = OptionParser(usage = "%prog [options]", 136 description = 137 "Verify a stream according to the v2 spec") 138 139 # Optional options 140 parser.add_option("-i", "--in", dest = "fin", metavar = "<FD or FILE>", 141 default = "0", 142 help = "Stream to verify (defaults to stdin)") 143 parser.add_option("-v", "--verbose", action = "store_true", default = False, 144 help = "Summarise stream contents") 145 parser.add_option("-q", "--quiet", action = "store_true", default = False, 146 help = "Suppress all logging/errors") 147 parser.add_option("-f", "--format", dest = "format", 148 metavar = "<libxc|libxl|xl>", default = "libxc", 149 choices = ["libxc", "libxl", "xl"], 150 help = "Format of the incoming stream (defaults to libxc)") 151 parser.add_option("--syslog", action = "store_true", default = False, 152 help = "Log to syslog instead of stdout") 153 154 opts, _ = parser.parse_args() 155 156 if opts.syslog: 157 global log_to_syslog 158 159 syslog.openlog("verify-stream-v2", syslog.LOG_PID) 160 log_to_syslog = True 161 162 verbose = opts.verbose 163 quiet = opts.quiet 164 fin = open_file_or_fd(opts.fin, "rb", 0) 165 166 return read_stream(opts.format) 167 168if __name__ == "__main__": 169 try: 170 sys.exit(main()) 171 except SystemExit, e: 172 sys.exit(e.code) 173 except KeyboardInterrupt: 174 sys.exit(2) 175