12 base='/home/staticsync/static-master'
13 subdirs = { 'master': 'master', # where updates from off-site end up going, the source of everything we do here
14 'cur': 'current-push', # where clients rsync from during a mirror push
15 'live': 'current-live'} # what is currently on the mirrors, and what they rsync from when they come back from being down
16 serialname = '.serial'
19 with open('/etc/static-clients.conf') as f:
22 if line == "": continue
23 if line.startswith('#'): continue
27 t = time.strftime("[%Y-%m-%d %H:%M:%S]", time.gmtime())
30 def stage1(pipes, status):
34 line = p.stdout.readline()
40 log("%s: failed with returncode %d"%(c,p.returncode))
44 log("%s >> %s"%(c, line))
45 if not line.startswith('[MSM]'): continue
46 kw = string.split(line, ' ', 2)[1]
48 if kw == 'ALREADY-CURRENT':
49 pipes[c].stdout.close()
50 pipes[c].stdin.close()
53 log("%s: already current"%(c,))
56 log("%s: said ALREADY-CURRENT but returncode %d"%(c,p.returncode))
59 elif kw == 'STAGE1-DONE':
60 log("%s: waiting"%(c,))
63 elif kw in ['STAGE1-START']:
66 log("%s: ignoring unknown line"%(c,))
68 def count_statuses(status):
72 if v not in cnt: cnt[v] = 1
76 def stage2(pipes, status, command):
78 if status[c] != 'waiting': continue
79 log("%s << %s"%(c, command))
80 pipes[c].stdin.write("%s\n"%(command,))
83 if status[c] != 'waiting': continue
86 (o, dummy) = p.communicate('')
87 for l in string.split(o, "\n"):
88 log("%s >> %s"%(c, l))
89 log("%s: returned %d"%(c, p.returncode))
92 log("Calling clients...")
96 args = ['ssh', '-o', 'BatchMode=yes', c, 'mirror', "%d"%(serial,)]
97 p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
99 status[c] = 'in-progress'
102 stage1(pipes, status)
104 cnt = count_statuses(status)
106 if 'failed' in cnt > 0:
107 log("Some clients failed, aborting...")
108 stage2(pipes, status, 'abort')
110 elif 'waiting' in cnt > 0:
112 stage2(pipes, status, 'go')
115 log("All clients up to date.")
122 master = os.path.join(base, subdirs['master'])
123 cur = os.path.join(base, subdirs['cur'])
124 live = os.path.join(base, subdirs['live'])
125 tmpdir_new = tempfile.mkdtemp(prefix='live.new-', dir=base); cleanup_dirs.append(tmpdir_new);
126 tmpdir_old = tempfile.mkdtemp(prefix='live.old-', dir=base); cleanup_dirs.append(tmpdir_old);
127 os.chmod(tmpdir_new, 0755)
130 for p in (master, live, tmpdir_new):
131 if not os.path.exists(p): os.mkdir(p, 0755)
132 fd = os.open(p, os.O_RDONLY)
133 log("Acquiring lock for %s(%d)."%(p,fd))
134 fcntl.flock(fd, fcntl.LOCK_EX)
136 log("All locks acquired.")
138 serialfile = os.path.join(master, serialname)
140 with open(serialfile) as f: serial = int(f.read())
142 serial = int(time.time())
143 with open(serialfile, "w") as f: f.write("%d\n"%(serial,))
144 log("Serial is %s."%(serial,))
146 log("Populating %s."%(tmpdir_new,))
147 subprocess.check_call(['cp', '-al', os.path.join(master, '.'), tmpdir_new])
149 if os.path.exists(cur):
150 log("Removing existing %s."%(cur,))
153 log("Renaming %s to %s."%(tmpdir_new, cur))
154 os.rename(tmpdir_new, cur)
156 proceed = callout(serial)
159 log("Moving %s aside."%(live,))
160 os.rename(live, os.path.join(tmpdir_old, 'old'))
161 log("Renaming %s to %s."%(cur, live))
164 shutil.rmtree(tmpdir_old)
181 for p in cleanup_dirs:
182 if os.path.exists(p): shutil.rmtree(p)
188 # vim:set shiftwidth=2: