root / snf-image-host / copy-monitor.py @ master
History | View | Annotate | Download (5.3 kB)
1 | ebba4508 | Nikos Skalkotos | #!/usr/bin/env python
|
---|---|---|---|
2 | ebba4508 | Nikos Skalkotos | |
3 | ebba4508 | Nikos Skalkotos | # Copyright (C) 2011, 2012 GRNET S.A.
|
4 | ebba4508 | Nikos Skalkotos | #
|
5 | ebba4508 | Nikos Skalkotos | # This program is free software; you can redistribute it and/or modify
|
6 | ebba4508 | Nikos Skalkotos | # it under the terms of the GNU General Public License as published by
|
7 | ebba4508 | Nikos Skalkotos | # the Free Software Foundation; either version 2 of the License, or
|
8 | ebba4508 | Nikos Skalkotos | # (at your option) any later version.
|
9 | ebba4508 | Nikos Skalkotos | #
|
10 | ebba4508 | Nikos Skalkotos | # This program is distributed in the hope that it will be useful, but
|
11 | ebba4508 | Nikos Skalkotos | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 | ebba4508 | Nikos Skalkotos | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 | ebba4508 | Nikos Skalkotos | # General Public License for more details.
|
14 | ebba4508 | Nikos Skalkotos | #
|
15 | ebba4508 | Nikos Skalkotos | # You should have received a copy of the GNU General Public License
|
16 | ebba4508 | Nikos Skalkotos | # along with this program; if not, write to the Free Software
|
17 | ebba4508 | Nikos Skalkotos | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18 | ebba4508 | Nikos Skalkotos | # 02110-1301, USA.
|
19 | ebba4508 | Nikos Skalkotos | |
20 | ebba4508 | Nikos Skalkotos | """Utility to monitor the progress of image deployment
|
21 | ebba4508 | Nikos Skalkotos |
|
22 | ebba4508 | Nikos Skalkotos | A small utility to monitor the progress of image deployment
|
23 | ebba4508 | Nikos Skalkotos | by watching the contents of /proc/<pid>/io and producing
|
24 | ebba4508 | Nikos Skalkotos | notifications.
|
25 | ebba4508 | Nikos Skalkotos | """
|
26 | ebba4508 | Nikos Skalkotos | |
27 | ebba4508 | Nikos Skalkotos | import os |
28 | ebba4508 | Nikos Skalkotos | import sys |
29 | ebba4508 | Nikos Skalkotos | import time |
30 | ebba4508 | Nikos Skalkotos | import json |
31 | ebba4508 | Nikos Skalkotos | import prctl |
32 | ebba4508 | Nikos Skalkotos | import signal |
33 | ebba4508 | Nikos Skalkotos | import socket |
34 | ebba4508 | Nikos Skalkotos | |
35 | f58bbc57 | Nikos Skalkotos | MSG_TYPE = "image-copy-progress"
|
36 | f58bbc57 | Nikos Skalkotos | |
37 | ebba4508 | Nikos Skalkotos | |
38 | ebba4508 | Nikos Skalkotos | def parse_arguments(args): |
39 | ebba4508 | Nikos Skalkotos | from optparse import OptionParser |
40 | ebba4508 | Nikos Skalkotos | |
41 | ebba4508 | Nikos Skalkotos | kw = {} |
42 | ebba4508 | Nikos Skalkotos | kw['usage'] = "%prog [options] command [args...]" |
43 | ebba4508 | Nikos Skalkotos | kw['description'] = \
|
44 | ebba4508 | Nikos Skalkotos | "%prog runs 'command' with the specified arguments, monitoring the " \
|
45 | ebba4508 | Nikos Skalkotos | "number of bytes read by it. 'command' is assumed to be " \
|
46 | ebba4508 | Nikos Skalkotos | "A program used to install the OS for a Ganeti instance. %prog " \
|
47 | df1c62e2 | Nikos Skalkotos | "periodically issues notifications of type 'copy-progress'."
|
48 | ebba4508 | Nikos Skalkotos | |
49 | ebba4508 | Nikos Skalkotos | parser = OptionParser(**kw) |
50 | ebba4508 | Nikos Skalkotos | parser.disable_interspersed_args() |
51 | ebba4508 | Nikos Skalkotos | parser.add_option("-r", "--read-bytes", |
52 | ebba4508 | Nikos Skalkotos | action="store", type="int", dest="read_bytes", |
53 | ebba4508 | Nikos Skalkotos | metavar="BYTES_TO_READ",
|
54 | f58bbc57 | Nikos Skalkotos | help="The expected number of bytes to be read, "
|
55 | ebba4508 | Nikos Skalkotos | "used to compute input progress",
|
56 | ebba4508 | Nikos Skalkotos | default=None)
|
57 | f58bbc57 | Nikos Skalkotos | parser.add_option( |
58 | f58bbc57 | Nikos Skalkotos | "-o", "--output_fd", dest="output", default=None, metavar="FILE", |
59 | f58bbc57 | Nikos Skalkotos | type="int", help="Write output notifications to this file descriptor") |
60 | ebba4508 | Nikos Skalkotos | |
61 | ebba4508 | Nikos Skalkotos | (opts, args) = parser.parse_args(args) |
62 | ebba4508 | Nikos Skalkotos | |
63 | ebba4508 | Nikos Skalkotos | if opts.read_bytes is None: |
64 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Fatal: Option '-r' is mandatory.\n")
|
65 | ebba4508 | Nikos Skalkotos | parser.print_help() |
66 | ebba4508 | Nikos Skalkotos | sys.exit(1)
|
67 | ebba4508 | Nikos Skalkotos | |
68 | ebba4508 | Nikos Skalkotos | if opts.output is None: |
69 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Fatal: Option '-o' is mandatory.\n")
|
70 | ebba4508 | Nikos Skalkotos | parser.print_help() |
71 | ebba4508 | Nikos Skalkotos | sys.exit(1)
|
72 | ebba4508 | Nikos Skalkotos | |
73 | ebba4508 | Nikos Skalkotos | if len(args) == 0: |
74 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Fatal: You need to specify the command to run.\n")
|
75 | ebba4508 | Nikos Skalkotos | parser.print_help() |
76 | ebba4508 | Nikos Skalkotos | sys.exit(1)
|
77 | ebba4508 | Nikos Skalkotos | |
78 | ebba4508 | Nikos Skalkotos | return (opts, args)
|
79 | ebba4508 | Nikos Skalkotos | |
80 | ebba4508 | Nikos Skalkotos | |
81 | ebba4508 | Nikos Skalkotos | def report_wait_status(pid, status): |
82 | ebba4508 | Nikos Skalkotos | if os.WIFEXITED(status):
|
83 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Child PID = %d exited, status = %d\n" %
|
84 | ebba4508 | Nikos Skalkotos | (pid, os.WEXITSTATUS(status))) |
85 | ebba4508 | Nikos Skalkotos | elif os.WIFSIGNALED(status):
|
86 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Child PID = %d died by signal, signal = %d\n" %
|
87 | ebba4508 | Nikos Skalkotos | (pid, os.WTERMSIG(status))) |
88 | ebba4508 | Nikos Skalkotos | elif os.WIFSTOPPED(status):
|
89 | ebba4508 | Nikos Skalkotos | sys.stderr.write("Child PID = %d stopped by signal, signal = %d\n" %
|
90 | ebba4508 | Nikos Skalkotos | (pid, os.WSTOPSIG(status))) |
91 | ebba4508 | Nikos Skalkotos | else:
|
92 | f58bbc57 | Nikos Skalkotos | sys.stderr.write("Internal error: Unhandled case, "
|
93 | ebba4508 | Nikos Skalkotos | "PID = %d, status = %d\n" % (pid, status))
|
94 | ebba4508 | Nikos Skalkotos | sys.exit(1)
|
95 | ebba4508 | Nikos Skalkotos | sys.stderr.flush() |
96 | ebba4508 | Nikos Skalkotos | |
97 | ebba4508 | Nikos Skalkotos | |
98 | ebba4508 | Nikos Skalkotos | def send_message(to, message): |
99 | df1c62e2 | Nikos Skalkotos | message['timestamp'] = time.time()
|
100 | 87d1bf2e | Nikos Skalkotos | os.write(to, "%s\n" % json.dumps(message))
|
101 | ebba4508 | Nikos Skalkotos | |
102 | ebba4508 | Nikos Skalkotos | |
103 | ebba4508 | Nikos Skalkotos | def main(): |
104 | ebba4508 | Nikos Skalkotos | (opts, args) = parse_arguments(sys.argv[1:])
|
105 | 87d1bf2e | Nikos Skalkotos | out = opts.output |
106 | 87d1bf2e | Nikos Skalkotos | pid = os.fork() |
107 | 87d1bf2e | Nikos Skalkotos | if pid == 0: |
108 | 87d1bf2e | Nikos Skalkotos | # In child process:
|
109 | 87d1bf2e | Nikos Skalkotos | |
110 | 87d1bf2e | Nikos Skalkotos | # Make sure we die with the parent and are not left behind
|
111 | 87d1bf2e | Nikos Skalkotos | # WARNING: This uses the prctl(2) call and is Linux-specific.
|
112 | 87d1bf2e | Nikos Skalkotos | prctl.set_pdeathsig(signal.SIGHUP) |
113 | 87d1bf2e | Nikos Skalkotos | |
114 | 87d1bf2e | Nikos Skalkotos | # exec command specified in arguments,
|
115 | 87d1bf2e | Nikos Skalkotos | # searching the $PATH, keeping all environment
|
116 | 87d1bf2e | Nikos Skalkotos | os.execvpe(args[0], args, os.environ)
|
117 | 87d1bf2e | Nikos Skalkotos | sys.stderr.write("execvpe failed, exiting with non-zero status")
|
118 | 87d1bf2e | Nikos Skalkotos | os.exit(1)
|
119 | 87d1bf2e | Nikos Skalkotos | |
120 | 87d1bf2e | Nikos Skalkotos | # In parent process:
|
121 | 87d1bf2e | Nikos Skalkotos | iofname = "/proc/%d/io" % pid
|
122 | 87d1bf2e | Nikos Skalkotos | iof = open(iofname, "r", 0) # 0: unbuffered open |
123 | 87d1bf2e | Nikos Skalkotos | sys.stderr.write("%s: created child PID = %d, monitoring file %s\n" %
|
124 | 87d1bf2e | Nikos Skalkotos | (sys.argv[0], pid, iofname))
|
125 | 87d1bf2e | Nikos Skalkotos | |
126 | 87d1bf2e | Nikos Skalkotos | message = {} |
127 | a50a2bff | Nikos Skalkotos | message['type'] = MSG_TYPE
|
128 | 87d1bf2e | Nikos Skalkotos | message['total'] = opts.read_bytes
|
129 | 87d1bf2e | Nikos Skalkotos | |
130 | 87d1bf2e | Nikos Skalkotos | while True: |
131 | 87d1bf2e | Nikos Skalkotos | # check if the child process is still alive
|
132 | 87d1bf2e | Nikos Skalkotos | (wpid, status) = os.waitpid(pid, os.WNOHANG) |
133 | 87d1bf2e | Nikos Skalkotos | if wpid == pid:
|
134 | 87d1bf2e | Nikos Skalkotos | report_wait_status(pid, status) |
135 | 87d1bf2e | Nikos Skalkotos | if (os.WIFEXITED(status) or os.WIFSIGNALED(status)): |
136 | f58bbc57 | Nikos Skalkotos | if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0): |
137 | 87d1bf2e | Nikos Skalkotos | return 1 |
138 | 87d1bf2e | Nikos Skalkotos | else:
|
139 | 87d1bf2e | Nikos Skalkotos | message['position'] = message['total'] |
140 | 87d1bf2e | Nikos Skalkotos | message['progress'] = float(100) |
141 | ebba4508 | Nikos Skalkotos | send_message(out, message) |
142 | 87d1bf2e | Nikos Skalkotos | return 0 |
143 | 87d1bf2e | Nikos Skalkotos | |
144 | 87d1bf2e | Nikos Skalkotos | iof.seek(0)
|
145 | 87d1bf2e | Nikos Skalkotos | for l in iof.readlines(): |
146 | 87d1bf2e | Nikos Skalkotos | if l.startswith("rchar:"): |
147 | 87d1bf2e | Nikos Skalkotos | message['position'] = int(l.split(': ')[1]) |
148 | 42ed2a70 | Nikos Skalkotos | message['progress'] = float(0) if opts.read_bytes == 0 \ |
149 | 87d1bf2e | Nikos Skalkotos | else float("%2.2f" % ( |
150 | 87d1bf2e | Nikos Skalkotos | message['position'] * 100.0 / message['total'])) |
151 | 87d1bf2e | Nikos Skalkotos | send_message(out, message) |
152 | 87d1bf2e | Nikos Skalkotos | break
|
153 | 87d1bf2e | Nikos Skalkotos | |
154 | 87d1bf2e | Nikos Skalkotos | # Sleep for a while
|
155 | 87d1bf2e | Nikos Skalkotos | time.sleep(3)
|
156 | ebba4508 | Nikos Skalkotos | |
157 | ebba4508 | Nikos Skalkotos | if __name__ == "__main__": |
158 | ebba4508 | Nikos Skalkotos | sys.exit(main()) |
159 | ebba4508 | Nikos Skalkotos | |
160 | ebba4508 | Nikos Skalkotos | # vim: set sta sts=4 shiftwidth=4 sw=4 et ai : |