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