comparison silvermirror/unify.py @ 1:9b139702a8f9

use a real backend architecture with an inteface and vastly simplify unify.py
author k0s <k0scist@gmail.com>
date Sat, 26 Sep 2009 23:36:42 -0400
parents abb358e2434c
children 743c920bc041
comparison
equal deleted inserted replaced
0:abb358e2434c 1:9b139702a8f9
7 import subprocess 7 import subprocess
8 import sys 8 import sys
9 9
10 from martini.config import ConfigMunger 10 from martini.config import ConfigMunger
11 from optparse import OptionParser 11 from optparse import OptionParser
12 from pkg_resources import iter_entry_points
12 from pprint import pprint 13 from pprint import pprint
13 from utils import home 14 from utils import home
14 from utils import ip_addresses 15 from utils import ip_addresses
15 16
16 def make_config(filename): 17 def make_config(filename):
50 51
51 ### 52 ###
52 config = { 'main': main, 'resources': config } 53 config = { 'main': main, 'resources': config }
53 return config 54 return config
54 55
55 def unify(args=sys.argv[1:]): 56 def unify(conf, _resources, test=False):
56 57
57 # passwords 58 # passwords
58 pw = {} 59 pw = {}
60
61 # XXX needed for now
62 assert conf['main']['basedir'] == home()
63
64 ### determine hosts to sync with
65 hosts = conf['hosts']
66 addresses = ip_addresses().values()
67 hosts = hosts.difference(addresses) # don't sync with self
68 _hosts = []
69 for host in hosts:
70 s = socket.socket()
71 s.settimeout(conf['main']['timeout'])
72 if test:
73 print 'Resolving %s' % host
74 try:
75 s.connect((host, 22))
76 s.close()
77 except (socket.gaierror, socket.timeout, socket.error):
78 continue
79 _hosts.append(host)
80 hosts = _hosts
81 if test:
82 print 'Hosts:'
83 for host in hosts:
84 print host
85 assert hosts
86
87 if conf['main']['password']:
88 for host in hosts:
89 pw[host] = getpass.getpass('Enter password for %s: ' % host)
90 # TODO: ensure that the hosts are resolvable
91 # XXX: hosts should actually be manageable on a per-resource basis
92
93 ### determine resources to sync
94 cwd = os.path.realpath(os.getcwd())
95 resources = conf['resources']
96 if 'all' not in _resources:
97 if _resources:
98 resources = dict([(key, value) for key, value in resources.items()
99 if key in _resources])
100 else:
101 for key, value in resources.items():
102 directory = os.path.realpath(value['directory']) + os.sep
103 if (cwd + os.sep).startswith(directory):
104 resources = { key: value }
105 break
106 if test:
107 print 'Resources:'
108 pprint(resources)
109
110 ### choose reflector backend
111 reflectors = dict([(i.name, i.load()) for i in iter_entry_points('silvermirror.reflectors')])
112 reflector = reflectors['unison']() # only one right now
113
114 ### sync with hosts
115 os.chdir(conf['main']['basedir'])
116 for resource in resources:
117 for host in hosts:
118 reflector.sync(host, resource, resources[resource]['ignore'], pw, test)
119 os.chdir(cwd)
120
121 def main(args=sys.argv[1:]):
59 122
60 ### command line options 123 ### command line options
61 parser = OptionParser() 124 parser = OptionParser()
62 parser.add_option('-c', '--config') 125 parser.add_option('-c', '--config')
63 parser.add_option('-H', '--host', dest='hosts', 126 parser.add_option('-H', '--host', dest='hosts',
65 parser.add_option('--no-password', dest='password', 128 parser.add_option('--no-password', dest='password',
66 action='store_false', default=True) 129 action='store_false', default=True)
67 parser.add_option('--test', dest='test', 130 parser.add_option('--test', dest='test',
68 action='store_true', default=False) 131 action='store_true', default=False)
69 (options, args) = parser.parse_args() 132 (options, args) = parser.parse_args()
70 133
134
71 ### configuration 135 ### configuration
72 user_conf = os.path.join(home(), '.silvermirror') 136 user_conf = os.path.join(home(), '.silvermirror')
73 if options.config: 137 if options.config:
74 assert os.path.exists(options.config) 138 assert os.path.exists(options.config)
75 conf = read_config(options.config) 139 conf = read_config(options.config)
82 conf = make_config(user_conf) 146 conf = make_config(user_conf)
83 147
84 # XXX needed for now 148 # XXX needed for now
85 assert conf['main']['basedir'] == home() 149 assert conf['main']['basedir'] == home()
86 150
87 ### determine hosts to sync with 151 # fix up configuration from command line options
88 hosts = set(options.hosts or conf['main']['hosts']) 152 conf['hosts'] = set(options.hosts or conf['main']['hosts'])
89 addresses = ip_addresses().values() 153 conf['main']['password'] = options.password and conf['main']['password']
90 hosts = hosts.difference(addresses) # don't sync with self
91 _hosts = []
92 for host in hosts:
93 s = socket.socket()
94 s.settimeout(conf['main']['timeout'])
95 if options.test:
96 print 'Resolving %s' % host
97 try:
98 s.connect((host, 22))
99 s.close()
100 except (socket.gaierror, socket.timeout, socket.error):
101 continue
102 _hosts.append(host)
103 hosts = _hosts
104 if options.test:
105 print 'Hosts:'
106 for host in hosts:
107 print host
108 assert hosts
109 154
110 if options.password and conf['main']['password']: 155 unify(conf, args, options.test)
111 for host in hosts:
112 pw[host] = getpass.getpass('Enter password for %s: ' % host)
113 # TODO: ensure that the hosts are resolvable
114 # XXX: hosts should actually be manageable on a per-resource basis
115
116 ### determine resources to sync
117 cwd = os.path.realpath(os.getcwd())
118 resources = conf['resources']
119 _resources = args
120 if 'all' not in _resources:
121 if _resources:
122 resources = dict([(key, value) for key, value in resources.items()
123 if key in _resources])
124 else:
125 for key, value in resources.items():
126 directory = os.path.realpath(value['directory']) + os.sep
127 if (cwd + os.sep).startswith(directory):
128 resources = { key: value }
129 break
130 if options.test:
131 print 'Resources:'
132 pprint(resources)
133
134 ### sync with hosts
135 os.chdir(conf['main']['basedir'])
136 for resource in resources:
137 for host in hosts:
138 command = ['unison', '-auto', '-batch', resource, 'ssh://%s/%s' % (host, resource)]
139
140 # XXX - to refactor?
141 for i in resources[resource]['ignore']:
142 command.extend(('-ignore', "'Name %s'" % i))
143
144 command = ' '.join(command)
145 print command # XXX debug
146 if not options.test:
147 child = pexpect.spawn(command, timeout=36000, maxread=1)
148 child.expect('password: ')
149 child.sendline(pw[host])
150 print child.read()
151 # subprocess.call(command)
152 os.chdir(cwd)
153 156
154 if __name__ == '__main__': 157 if __name__ == '__main__':
155 unify() 158 unify()