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

Subscribers

People subscribed via source and target branches