Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: kick.py

Issue 5611225295618048: Issue 1267 - Provide a way to run some command on multiple hosts (Closed)
Patch Set: Created Aug. 25, 2014, 2:43 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | run.py » ('j') | run.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 5 import os
6 import re 6 import re
7 import subprocess 7 import subprocess
8 import getopt 8 import getopt
9 import yaml 9 import yaml
10 from run import resolveHostList, runCommand
10 11
11 def usage(): 12 def usage():
12 print >>sys.stderr, ''' 13 print >>sys.stderr, '''
13 Usage: %s -u <user> [-t|-q] [<host>|<group>] ... 14 Usage: %s [-u <user>] [-t|-q] [<host>|<group>] ...
14 15
15 Runs provisioning on the given hosts or groups of hosts. 16 Runs provisioning on the given hosts or groups of hosts.
16 17
17 Options: 18 Options:
18 -u <user> User name to use with the SSH command (needs access to puppet 19 -u <user> User name to use with the SSH command (needs access to puppet
19 master and all hosts) 20 master and all hosts)
20 -t Dry-run mode, will produce the usual output but not change 21 -t Dry-run mode, will produce the usual output but not change
21 host configuration 22 host configuration
22 -q Quiet mode, suppress Puppet output to console 23 -q Quiet mode, suppress Puppet output to console
23 ''' % sys.argv[0] 24 ''' % sys.argv[0]
(...skipping 14 matching lines...) Expand all
38 user = None 39 user = None
39 mode = ' --test' 40 mode = ' --test'
40 for option, value in options: 41 for option, value in options:
41 if option == '-u': 42 if option == '-u':
42 user = value 43 user = value
43 elif option == '-q': 44 elif option == '-q':
44 mode = '' 45 mode = ''
45 elif option == '-t': 46 elif option == '-t':
46 mode = ' --test --noop' 47 mode = ' --test --noop'
47 48
48 if user == None:
49 print >>sys.stderr, 'No user name specified'
50 usage()
51 sys.exit(1)
52
53 return user, mode, args 49 return user, mode, args
54 50
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): 51 def updateMaster(user):
129 print 'Updating data on the puppet master...' 52 print 'Updating data on the puppet master...'
130 remoteCommand = ' && '.join([ 53 remoteCommand = ' && '.join([
131 'sudo hg pull -qu -R /etc/puppet/infrastructure', 54 'sudo hg pull -qu -R /etc/puppet/infrastructure',
132 'sudo hg pull -qu -R /etc/puppet/infrastructure/modules/private', 55 'sudo hg pull -qu -R /etc/puppet/infrastructure/modules/private',
133 ]) 56 ])
134 os.system('ssh -l %s puppetmaster.adblockplus.org "%s"' % (user, remoteCommand )) 57 runCommand(user, "puppetmaster.adblockplus.org", remoteCommand)
135 58
136 def updateClient(user, host, mode): 59 def updateClient(user, host, mode):
137 print 'Provisioning %s...' % host 60 print 'Provisioning %s...' % host
138 remoteCommand = 'sudo puppet agent%s' %mode 61 remoteCommand = 'sudo puppet agent%s' % mode
139 os.system('ssh -l %s %s "%s"' % (user, host, remoteCommand)) 62
63 # Have to ignore errors here, Puppet will return non-zero for successful runs
64 runCommand(user, host, remoteCommand, ignore_errors=True)
140 65
141 if __name__ == "__main__": 66 if __name__ == "__main__":
142 user, mode, args = parseOptions(sys.argv[1:]) 67 user, mode, args = parseOptions(sys.argv[1:])
143 hosts, groups = getValidHosts() 68 needKicking = resolveHostList(args)
144 needKicking = resolveHostList(args, hosts, groups)
145 if len(needKicking) == 0: 69 if len(needKicking) == 0:
146 print >>sys.stderr, 'No valid hosts or groups specified, nothing to do' 70 print >>sys.stderr, 'No valid hosts or groups specified, nothing to do'
147 sys.exit(0) 71 sys.exit(0)
148 updateMaster(user) 72 updateMaster(user)
149 for host in needKicking: 73 for host in needKicking:
150 updateClient(user, host, mode) 74 updateClient(user, host, mode)
OLDNEW
« no previous file with comments | « no previous file | run.py » ('j') | run.py » ('J')

Powered by Google App Engine
This is Rietveld