Merge lp:~smoser/curtin/trunk.lp1604962 into lp:~curtin-dev/curtin/trunk

Proposed by Scott Moser
Status: Rejected
Rejected by: Scott Moser
Proposed branch: lp:~smoser/curtin/trunk.lp1604962
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 183 lines (+179/-0)
1 file modified
curtin/power_state.py (+179/-0)
To merge this branch: bzr merge lp:~smoser/curtin/trunk.lp1604962
Reviewer Review Type Date Requested Status
curtin developers Pending
Review via email: mp+306544@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

marked as a work in progress, but this is to fix bug 1604962.

Unmerged revisions

427. By Scott Moser

adding code i was playing with.

The intent of this would be for curtin to do something like the main
here. Instead of invoking the shell reboot function, it would
search for a parent that it liked (such as one named cloud-init).
If there was no good looking parent, then it would use itself.

So basically:
 - select a process P to wait until it is gone.
 - fork
 - exit from main process
 - second process polls for process P to have left the building
 - second process (conditionally) invokes the reboot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'curtin/power_state.py'
--- curtin/power_state.py 1970-01-01 00:00:00 +0000
+++ curtin/power_state.py 2016-09-22 20:52:31 +0000
@@ -0,0 +1,179 @@
1# Copyright (C) 2016 Canonical Ltd.
2#
3# Author: Scott Moser <scott.moser@canonical.com>
4#
5# Curtin is free software: you can redistribute it and/or modify it under
6# the terms of the GNU Affero General Public License as published by the
7# Free Software Foundation, either version 3 of the License, or (at your
8# option) any later version.
9#
10# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
13# more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
17from curtin import util
18from curtin.log import LOG
19
20import errno
21import functools
22import os
23import re
24import sys
25import time
26
27EXIT_FAIL = 33
28
29
30def get_pid_info(pid=None):
31 # get_pid_info(return a dictionary with information about pid)
32 if pid is None or pid == "self":
33 pid = os.getpid()
34 path = "/proc/%s" % pid
35 content = util.load_file(path + "/status")
36 info = {}
37 for line in content.splitlines():
38 key, _, val = line.partition(":")
39 info[key.lower()] = val.strip()
40 info['cmdline'] = util.load_file(path + "/cmdline")[:-1].split('\0')
41 info['pid'] = int(pid)
42 return info
43
44def get_pedigree_info(pid=None):
45 if pid is None:
46 pid = os.getpid()
47 tree = []
48 while True:
49 cur = get_pid_info(pid)
50 ppid = int(cur['ppid'])
51 tree.append({'pid': cur['pid'], 'ppid': ppid,
52 'cmdline': cur['cmdline']})
53 if ppid == 0:
54 break
55 pid = ppid
56 return tree
57
58
59def myreboot(*args, **kwargs):
60 print("args=%s" % ' '.join(args))
61 print("kwargs=%s" % str(kwargs))
62
63
64def run_after_pid_gone(pid, cmdline, timeout, func, args, kwargs,
65 condition=True):
66 # wait until pid, with /proc/pid/cmdline contents of pidcmdline
67 # is no longer alive. After it is gone, or timeout has passed
68 # execute func(args)
69 naptime = 0.25
70 msg = None
71 end_time = time.time() + timeout
72
73 def fatal(msg):
74 LOG.fatal(msg)
75 sys.exit(EXIT_FAIL)
76
77 known_errnos = (errno.ENOENT, errno.ESRCH)
78
79 while True:
80 if time.time() > end_time:
81 msg = "timeout reached before %d ended" % pid
82 break
83
84 try:
85 cur_cmdline = get_pid_info(pid)['cmdline']
86 if cur_cmdline != cmdline:
87 msg = "cmdline changed for %s [now: %s]" % (pid, cur_cmdline)
88 break
89
90 except IOError as ioerr:
91 if ioerr.errno in known_errnos:
92 msg = "pid info for %d failed [%d]" % (pid, ioerr.errno)
93 else:
94 fatal("IOError during wait: %s" % ioerr)
95 break
96
97 except Exception as e:
98 fatal("Unexpected Exception: %s" % e)
99
100 time.sleep(naptime)
101
102 if not msg:
103 fatal("Unexpected error in run_after_pid_gone")
104
105 LOG.debug(msg)
106
107 try:
108 if isinstance(condition, bool):
109 LOG.debug("Static Condition: %s" % condition)
110 result = condition
111 else:
112 result = condition()
113 LOG.debug("Calling condition returned %s" % condition)
114
115 if not result:
116 return
117
118 except Exception as e:
119 fatal("Unexpected Exception when checking condition: %s" % e)
120
121 func(*args, **kwargs)
122
123
124def fork_cb(child_cb, *args, **kwargs):
125 fid = os.fork()
126 if fid == 0:
127 try:
128 child_cb(*args, **kwargs)
129 sys.exit(0)
130 except Exception:
131 LOG.fatal("Failed forking and calling callback %s", child_cb)
132 sys.exit(1)
133 else:
134 LOG.debug("Forked child pid '%s' who will run callback %s",
135 fid, child_cb)
136
137
138def find_in_pedigree(match, pedigree=None):
139 if pedigree is None:
140 pedigree = get_pedigree_info()
141
142 match = re.compile(match)
143 print("searching for match to %s in pedigree" % match)
144 for i, v in enumerate(reversed(pedigree)):
145 print(i * " " + "{pid}: {cmdline}".format(**v))
146 if re.search(match, v['cmdline'][0]):
147 return v
148 return None
149
150
151def main():
152 match = sys.argv[1]
153 if len(sys.argv) > 2:
154 pid = sys.argv[2]
155 else:
156 pid = "self"
157
158 print("searching for parent of '%s' matching %s" % (pid, match))
159 pedigree = get_pedigree_info(pid)
160 result = find_in_pedigree(match, pedigree)
161 if result is None:
162 result = pedigree[0]
163 print("No match. using self: %s" % result)
164 else:
165 print("Found match: %s" % result)
166
167 def not_blocked():
168 return not os.path.exists("/run/block-curtin-poweroff")
169
170 fork_cb(run_after_pid_gone, result['pid'], result['cmdline'],
171 30, myreboot, ('arg1', 'arg2'), {'kw1': 'kv1'},
172 condition=not_blocked)
173
174
175
176if __name__ == '__main__':
177 main()
178
179# vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches