12 base="/srv/static.debian.org"
13 serialname = '.serial'
16 with open('/etc/static-clients.conf') as f:
19 if line == "": continue
20 if line.startswith('#'): continue
24 t = time.strftime("[%Y-%m-%d %H:%M:%S]", time.gmtime())
27 def stage1(pipes, status):
31 line = p.stdout.readline()
37 log("%s: failed with returncode %d"%(c,p.returncode))
41 log("%s >> %s"%(c, line))
42 if not line.startswith('[MSM]'): continue
43 kw = string.split(line, ' ', 2)[1]
45 if kw == 'ALREADY-CURRENT':
46 pipes[c].stdout.close()
47 pipes[c].stdin.close()
50 log("%s: already current"%(c,))
53 log("%s: said ALREADY-CURRENT but returncode %d"%(c,p.returncode))
56 elif kw == 'STAGE1-DONE':
57 log("%s: waiting"%(c,))
60 elif kw in ['STAGE1-START']:
63 log("%s: ignoring unknown line"%(c,))
65 def count_statuses(status):
69 if v not in cnt: cnt[v] = 1
73 def stage2(pipes, status, command):
75 if status[c] != 'waiting': continue
76 log("%s << %s"%(c, command))
77 pipes[c].stdin.write("%s\n"%(command,))
80 if status[c] != 'waiting': continue
83 (o, dummy) = p.communicate('')
84 for l in string.split(o, "\n"):
85 log("%s >> %s"%(c, l))
86 log("%s: returned %d"%(c, p.returncode))
88 def callout(component, serial):
89 log("Calling clients...")
93 args = ['ssh', '-o', 'BatchMode=yes', c, 'mirror', component, "%d"%(serial,)]
94 p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
96 status[c] = 'in-progress'
101 cnt = count_statuses(status)
103 if 'failed' in cnt > 0:
104 log("Some clients failed, aborting...")
105 stage2(pipes, status, 'abort')
107 elif 'waiting' in cnt > 0:
109 stage2(pipes, status, 'go')
112 log("All clients up to date.")
117 def run_mirror(component):
119 basemaster = os.path.join(base, 'master')
120 componentdir = os.path.join(basemaster, component)
121 cur = componentdir + '-current-push'
122 live = componentdir + '-current-live'
123 tmpdir_new = tempfile.mkdtemp(prefix=component+'-live.new-', dir=basemaster); cleanup_dirs.append(tmpdir_new);
124 tmpdir_old = tempfile.mkdtemp(prefix=component+'-live.old-', dir=basemaster); cleanup_dirs.append(tmpdir_old);
125 os.chmod(tmpdir_new, 0755)
128 for p in (componentdir, live, tmpdir_new):
129 if not os.path.exists(p): os.mkdir(p, 0755)
130 fd = os.open(p, os.O_RDONLY)
131 log("Acquiring lock for %s(%d)."%(p,fd))
132 fcntl.flock(fd, fcntl.LOCK_EX)
134 log("All locks acquired.")
136 serialfile = os.path.join(componentdir, serialname)
138 with open(serialfile) as f: serial = int(f.read())
140 serial = int(time.time())
141 with open(serialfile, "w") as f: f.write("%d\n"%(serial,))
142 log("Serial is %s."%(serial,))
144 log("Populating %s."%(tmpdir_new,))
145 subprocess.check_call(['cp', '-al', os.path.join(componentdir, '.'), tmpdir_new])
147 if os.path.exists(cur):
148 log("Removing existing %s."%(cur,))
151 log("Renaming %s to %s."%(tmpdir_new, cur))
152 os.rename(tmpdir_new, cur)
154 proceed = callout(component, serial)
157 log("Moving %s aside."%(live,))
158 os.rename(live, os.path.join(tmpdir_old, 'old'))
159 log("Renaming %s to %s."%(cur, live))
162 shutil.rmtree(tmpdir_old)
175 if len(sys.argv) != 2:
176 print >> sys.stderr, "Usage: %s <component>"%(sys.argv[0],)
178 component = sys.argv[1]
182 ok = run_mirror(component)
184 for p in cleanup_dirs:
185 if os.path.exists(p): shutil.rmtree(p)
191 # vim:set shiftwidth=2: