11 base='/home/staticsync/static-master'
12 subdirs = { 'master': 'master', # where updates from off-site end up going, the source of everything we do here
13 'cur': 'current-push', # where clients rsync from during a mirror push
14 'live': 'current-live'} # what is currently on the mirrors, and what they rsync from when they come back from being down
15 serialname = '.serial'
18 with open('/etc/static-clients.conf') as f:
21 if line == "": continue
22 if line.startswith('#'): continue
26 t = time.strftime("[%Y-%m-%d %H:%M:%S]", time.gmtime())
29 def stage1(pipes, status):
33 line = p.stdout.readline()
39 log("%s: failed with returncode %d"%(c,p.returncode))
43 log("%s >> %s"%(c, line))
44 if not line.startswith('[MSM]'): continue
45 kw = string.split(line, ' ', 2)[1]
47 if kw == 'ALREADY-CURRENT':
48 pipes[c].stdout.close()
49 pipes[c].stdin.close()
52 log("%s: already current"%(c,))
55 log("%s: said ALREADY-CURRENT but returncode %d"%(c,p.returncode))
58 elif kw == 'STAGE1-DONE':
59 log("%s: waiting"%(c,))
62 elif kw in ['STAGE1-START']:
65 log("%s: ignoring unknown line"%(c,))
67 def count_statuses(status):
71 if v not in cnt: cnt[v] = 1
75 def stage2(pipes, status, command):
77 if status[c] != 'waiting': continue
78 log("%s << %s"%(c, command))
79 pipes[c].stdin.write("%s\n"%(command,))
82 if status[c] != 'waiting': continue
85 (o, dummy) = p.communicate('')
86 for l in string.split(o, "\n"):
87 log("%s >> %s"%(c, l))
88 log("%s: returned %d"%(c, p.returncode))
91 log("Calling clients...")
95 args = ['ssh', '-o', 'BatchMode=yes', c, 'mirror', "%d"%(serial,)]
96 p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
98 status[c] = 'in-progress'
101 stage1(pipes, status)
103 cnt = count_statuses(status)
105 if 'failed' in cnt > 0:
106 log("Some clients failed, aborting...")
107 stage2(pipes, status, 'abort')
109 elif 'waiting' in cnt > 0:
111 stage2(pipes, status, 'go')
114 log("All clients up to date.")
121 master = os.path.join(base, subdirs['master'])
122 cur = os.path.join(base, subdirs['cur'])
123 live = os.path.join(base, subdirs['live'])
124 tmpdir_new = tempfile.mkdtemp(prefix='live.new-', dir=base); cleanup_dirs.append(tmpdir_new);
125 tmpdir_old = tempfile.mkdtemp(prefix='live.new-', dir=base); cleanup_dirs.append(tmpdir_old);
126 os.chmod(tmpdir_new, 0755)
129 for p in (master, live, tmpdir_new):
130 if not os.path.exists(p): os.mkdir(p, 0755)
131 fd = os.open(p, os.O_RDONLY)
132 log("Acquiring lock for %s(%d)."%(p,fd))
133 fcntl.flock(fd, fcntl.LOCK_EX)
135 log("All locks acquired.")
137 serialfile = os.path.join(master, serialname)
139 with open(serialfile) as f: serial = int(f.read())
141 serial = int(time.time())
142 with open(serialfile, "w") as f: f.write("%d\n"%(serial,))
143 log("Serial is %s."%(serial,))
145 log("Populating %s."%(tmpdir_new,))
146 subprocess.check_call(['cp', '-al', os.path.join(master, '.'), tmpdir_new])
148 if os.path.exists(cur):
149 log("Removing existing %s."%(cur,))
152 log("Renaming %s to %s."%(tmpdir_new, cur))
153 os.rename(tmpdir_new, cur)
155 proceed = callout(serial)
158 log("Moving %s aside."%(live,))
159 os.rename(live, os.path.join(tmpdir_old, 'old'))
160 log("Renaming %s to %s."%(cur, live))
163 shutil.rmtree(tmpdir_old)
172 for p in cleanup_dirs:
173 if os.path.exists(p): shutil.rmtree(p)
176 # vim:set shiftwidth=2: