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

Side by Side Diff: run.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 | « kick.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 import sys
5 import os
6 import re
7 import subprocess
8 import getopt
9 import yaml
10
11 def usage():
12 print >>sys.stderr, '''
13 Usage: %s [-u <user>] [-h <host>|<group>] [-i] ... <command>
14
15 Runs a command on the given hosts or groups of hosts.
16
17 Options:
18 -u <user> User name to use with the SSH command
19 -h <host|group> Host or group to run the command on (can be specified multiple times)
20 -i If specified, command will be executed on all hosts despite er rors
21 ''' % sys.argv[0]
22
23 def parseOptions(args):
24 try:
25 options, args = getopt.getopt(args, 'u:h:i')
26 except getopt.GetoptError, e:
27 print >>sys.stderr, e
28 usage()
29 sys.exit(1)
30
31 user = None
32 hosts = []
33 ignore_errors = False
34 for option, value in options:
35 if option == '-u':
36 user = value
37 elif option == '-h':
38 hosts.append(value)
39 elif option == '-i':
40 ignore_errors = True
41
42 return user, hosts, ignore_errors, args
43
44 def readMonitoringConfig():
45 # Use Puppet's parser to convert monitoringserver.pp into YAML
46 manifest = os.path.join(os.path.dirname(__file__), 'manifests', 'monitoringser ver.pp')
47 parseScript = '''
48 require 'puppet'
49 require 'puppet/parser'
50 parser = Puppet::Parser::Parser.new(Puppet[:environment])
51 Puppet.settings[:ignoreimport] = true
52 parser.file = ARGV[0]
53 print ZAML.dump(parser.parse)
54 '''
55 data, dummy = subprocess.Popen(['ruby', '', manifest],
56 stdin=subprocess.PIPE,
57 stdout=subprocess.PIPE).communicate(parseScript)
58
59 # See http://stackoverflow.com/q/8357650/785541 on parsing Puppet's YAML
60 yaml.add_multi_constructor(u"!ruby/object:", lambda loader, suffix, node: load er.construct_yaml_map(node))
61 yaml.add_constructor(u"!ruby/sym", lambda loader, node: loader.construct_yaml_ str(node))
62 return yaml.load(data)
63
64 def getValidHosts():
65 def processNode(node, hosts=None, groups=None):
66 if hosts == None:
67 hosts = set()
68 if groups == None:
69 groups = {}
70
71 if 'context' in node and 'code' in node['context']:
72 node = node['context']['code']
73
74 if node.get('type', None) == 'nagios_hostgroup':
75 data = node['instances']['children'][0]
76 title = data['title']['value']
77 members = filter(lambda c: c['param'] == 'members', data['parameters']['ch ildren'])[0]['value']['value']
78 members = re.split(r'\s*,\s*', members)
79 groups[title] = members
80 elif node.get('type', None) == 'nagios_host':
81 data = node['instances']['children'][0]
82 title = data['title']['value']
83 hosts.add(title)
84
85 for child in node['children']:
86 processNode(child, hosts, groups)
87 return hosts, groups
88
89 monitoringConfig = readMonitoringConfig()
90 if not monitoringConfig:
91 print >>sys.stderr, "Failed to parse monitoring configuration"
92 return [[], []]
93 # Extract hosts and groups from monitoring config
94 return processNode(monitoringConfig)
95
96 def resolveHostList(hosts):
97 validHosts, validGroups = getValidHosts()
98 if not validHosts:
99 print "Warning: No valid hosts found, not validating"
100 return hosts
101
102 result = set()
103 for param in hosts:
104 if param in validGroups:
105 for host in validGroups[param]:
106 if host == '*':
107 result = result | validHosts
108 else:
109 result.add(host)
110 elif param in validHosts:
111 result.add(param)
112 elif '%s.adblockplus.org' % param in validHosts:
113 result.add('%s.adblockplus.org' % param)
114 else:
115 print >>sys.stderr, 'Warning: failed to recognize host or group %s' %param
116 return result
117
118 def runCommand(user, host, command, ignore_errors=False):
119 if not isinstance(command, list):
120 command = [command]
121 command = ["ssh"] + (["-l", user] if user else []) + [host] + command
122 if ignore_errors:
123 subprocess.call(command)
124 else:
125 subprocess.check_call(command)
126
127 if __name__ == "__main__":
128 user, hosts, ignore_errors, args = parseOptions(sys.argv[1:])
129 selectedHosts = resolveHostList(hosts)
130 if len(selectedHosts) == 0:
131 print >>sys.stderr, 'No valid hosts or groups specified, nothing to do'
132 sys.exit(0)
133 for host in selectedHosts:
134 print 'Running on %s...' % host
mathias 2014/08/25 16:21:25 If all logging would be printed to >>stderr (just
Wladimir Palant 2014/08/29 22:11:15 I'm not usually using scripts on the output. And t
135 runCommand(user, host, args, ignore_errors=ignore_errors)
OLDNEW
« no previous file with comments | « kick.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld