OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # coding: utf-8 | 2 # coding: utf-8 |
3 | 3 |
4 import sys | 4 import sys |
5 import os | |
6 import re | |
7 import subprocess | |
8 import getopt | 5 import getopt |
9 import yaml | 6 from run import resolveHostList, runCommand |
10 | 7 |
11 def usage(): | 8 def usage(): |
12 print >>sys.stderr, ''' | 9 print >>sys.stderr, ''' |
13 Usage: %s -u <user> [-t|-q] [<host>|<group>] ... | 10 Usage: %s [-u <user>] [-t|-q] [<host>|<group>] ... |
14 | 11 |
15 Runs provisioning on the given hosts or groups of hosts. | 12 Runs provisioning on the given hosts or groups of hosts. |
16 | 13 |
17 Options: | 14 Options: |
18 -u <user> User name to use with the SSH command (needs access to puppet | 15 -u <user> User name to use with the SSH command (needs access to puppet |
19 master and all hosts) | 16 master and all hosts) |
20 -t Dry-run mode, will produce the usual output but not change | 17 -t Dry-run mode, will produce the usual output but not change |
21 host configuration | 18 host configuration |
22 -q Quiet mode, suppress Puppet output to console | 19 -q Quiet mode, suppress Puppet output to console |
23 ''' % sys.argv[0] | 20 ''' % sys.argv[0] |
(...skipping 14 matching lines...) Expand all Loading... |
38 user = None | 35 user = None |
39 mode = ' --test' | 36 mode = ' --test' |
40 for option, value in options: | 37 for option, value in options: |
41 if option == '-u': | 38 if option == '-u': |
42 user = value | 39 user = value |
43 elif option == '-q': | 40 elif option == '-q': |
44 mode = '' | 41 mode = '' |
45 elif option == '-t': | 42 elif option == '-t': |
46 mode = ' --test --noop' | 43 mode = ' --test --noop' |
47 | 44 |
48 if user == None: | |
49 print >>sys.stderr, 'No user name specified' | |
50 usage() | |
51 sys.exit(1) | |
52 | |
53 return user, mode, args | 45 return user, mode, args |
54 | 46 |
55 def readMonitoringConfig(): | |
56 # Use Puppet's parser to convert monitoringserver.pp into YAML | |
57 manifest = os.path.join(os.path.dirname(__file__), 'manifests', 'monitoringser
ver.pp') | |
58 parseScript = ''' | |
59 require 'puppet' | |
60 require 'puppet/parser' | |
61 parser = Puppet::Parser::Parser.new(Puppet[:environment]) | |
62 Puppet.settings[:ignoreimport] = true | |
63 parser.file = ARGV[0] | |
64 print ZAML.dump(parser.parse) | |
65 ''' | |
66 data, dummy = subprocess.Popen(['ruby', '', manifest], | |
67 stdin=subprocess.PIPE, | |
68 stdout=subprocess.PIPE).communicate(parseScript) | |
69 | |
70 # See http://stackoverflow.com/q/8357650/785541 on parsing Puppet's YAML | |
71 yaml.add_multi_constructor(u"!ruby/object:", lambda loader, suffix, node: load
er.construct_yaml_map(node)) | |
72 yaml.add_constructor(u"!ruby/sym", lambda loader, node: loader.construct_yaml_
str(node)) | |
73 return yaml.load(data) | |
74 | |
75 def getValidHosts(): | |
76 def processNode(node, hosts=None, groups=None): | |
77 if hosts == None: | |
78 hosts = set() | |
79 if groups == None: | |
80 groups = {} | |
81 | |
82 if 'context' in node and 'code' in node['context']: | |
83 node = node['context']['code'] | |
84 | |
85 if node.get('type', None) == 'nagios_hostgroup': | |
86 data = node['instances']['children'][0] | |
87 title = data['title']['value'] | |
88 members = filter(lambda c: c['param'] == 'members', data['parameters']['ch
ildren'])[0]['value']['value'] | |
89 members = re.split(r'\s*,\s*', members) | |
90 groups[title] = members | |
91 elif node.get('type', None) == 'nagios_host': | |
92 data = node['instances']['children'][0] | |
93 title = data['title']['value'] | |
94 hosts.add(title) | |
95 | |
96 for child in node['children']: | |
97 processNode(child, hosts, groups) | |
98 return hosts, groups | |
99 | |
100 monitoringConfig = readMonitoringConfig() | |
101 if not monitoringConfig: | |
102 print >>sys.stderr, "Failed to parse monitoring configuration" | |
103 return [[], []] | |
104 # Extract hosts and groups from monitoring config | |
105 return processNode(monitoringConfig) | |
106 | |
107 def resolveHostList(hosts, validHosts, validGroups): | |
108 if not validHosts: | |
109 print "Warning: No valid hosts found, not validating" | |
110 return hosts | |
111 | |
112 result = set() | |
113 for param in hosts: | |
114 if param in validGroups: | |
115 for host in validGroups[param]: | |
116 if host == '*': | |
117 result = result | validHosts | |
118 else: | |
119 result.add(host) | |
120 elif param in validHosts: | |
121 result.add(param) | |
122 elif '%s.adblockplus.org' % param in validHosts: | |
123 result.add('%s.adblockplus.org' % param) | |
124 else: | |
125 print >>sys.stderr, 'Warning: failed to recognize host or group %s' %param | |
126 return result | |
127 | |
128 def updateMaster(user): | 47 def updateMaster(user): |
129 print 'Updating data on the puppet master...' | 48 print 'Updating data on the puppet master...' |
130 remoteCommand = ' && '.join([ | 49 remoteCommand = ' && '.join([ |
131 'sudo hg pull -qu -R /etc/puppet/infrastructure', | 50 'sudo hg pull -qu -R /etc/puppet/infrastructure', |
132 'sudo hg pull -qu -R /etc/puppet/infrastructure/modules/private', | 51 'sudo hg pull -qu -R /etc/puppet/infrastructure/modules/private', |
133 ]) | 52 ]) |
134 os.system('ssh -l %s puppetmaster.adblockplus.org "%s"' % (user, remoteCommand
)) | 53 runCommand(user, "puppetmaster.adblockplus.org", remoteCommand) |
135 | 54 |
136 def updateClient(user, host, mode): | 55 def updateClient(user, host, mode): |
137 print 'Provisioning %s...' % host | 56 print 'Provisioning %s...' % host |
138 remoteCommand = 'sudo puppet agent%s' %mode | 57 remoteCommand = 'sudo puppet agent%s' % mode |
139 os.system('ssh -l %s %s "%s"' % (user, host, remoteCommand)) | 58 |
| 59 # Have to ignore errors here, Puppet will return non-zero for successful runs |
| 60 runCommand(user, host, remoteCommand, ignore_errors=True) |
140 | 61 |
141 if __name__ == "__main__": | 62 if __name__ == "__main__": |
142 user, mode, args = parseOptions(sys.argv[1:]) | 63 user, mode, args = parseOptions(sys.argv[1:]) |
143 hosts, groups = getValidHosts() | 64 needKicking = resolveHostList(args) |
144 needKicking = resolveHostList(args, hosts, groups) | |
145 if len(needKicking) == 0: | 65 if len(needKicking) == 0: |
146 print >>sys.stderr, 'No valid hosts or groups specified, nothing to do' | 66 print >>sys.stderr, 'No valid hosts or groups specified, nothing to do' |
147 sys.exit(0) | 67 sys.exit(0) |
148 updateMaster(user) | 68 updateMaster(user) |
149 for host in needKicking: | 69 for host in needKicking: |
150 updateClient(user, host, mode) | 70 updateClient(user, host, mode) |
OLD | NEW |