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

Unified Diff: run.py

Issue 5611225295618048: Issue 1267 - Provide a way to run some command on multiple hosts (Closed)
Patch Set: Print all logging output to stderr Created Aug. 29, 2014, 10:08 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « kick.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: run.py
===================================================================
new file mode 100755
--- /dev/null
+++ b/run.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import sys
+import os
+import re
+import subprocess
+import getopt
+import yaml
+
+def usage():
+ print >>sys.stderr, '''
+Usage: %s [-u <user>] [-h <host>|<group>] [-i] ... <command>
+
+Runs a command on the given hosts or groups of hosts.
+
+Options:
+ -u <user> User name to use with the SSH command
+ -h <host|group> Host or group to run the command on (can be specified multiple times)
+ -i If specified, command will be executed on all hosts despite errors
+''' % sys.argv[0]
+
+def parseOptions(args):
+ try:
+ options, args = getopt.getopt(args, 'u:h:i')
+ except getopt.GetoptError, e:
+ print >>sys.stderr, e
+ usage()
+ sys.exit(1)
+
+ user = None
+ hosts = []
+ ignore_errors = False
+ for option, value in options:
+ if option == '-u':
+ user = value
+ elif option == '-h':
+ hosts.append(value)
+ elif option == '-i':
+ ignore_errors = True
+
+ return user, hosts, ignore_errors, args
+
+def readMonitoringConfig():
+ # Use Puppet's parser to convert monitoringserver.pp into YAML
+ manifest = os.path.join(os.path.dirname(__file__), 'manifests', 'monitoringserver.pp')
+ parseScript = '''
+ require 'puppet'
+ require 'puppet/parser'
+ parser = Puppet::Parser::Parser.new(Puppet[:environment])
+ Puppet.settings[:ignoreimport] = true
+ parser.file = ARGV[0]
+ print ZAML.dump(parser.parse)
+ '''
+ data, dummy = subprocess.Popen(['ruby', '', manifest],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE).communicate(parseScript)
+
+ # See http://stackoverflow.com/q/8357650/785541 on parsing Puppet's YAML
+ yaml.add_multi_constructor(u"!ruby/object:", lambda loader, suffix, node: loader.construct_yaml_map(node))
+ yaml.add_constructor(u"!ruby/sym", lambda loader, node: loader.construct_yaml_str(node))
+ return yaml.load(data)
+
+def getValidHosts():
+ def processNode(node, hosts=None, groups=None):
+ if hosts == None:
+ hosts = set()
+ if groups == None:
+ groups = {}
+
+ if 'context' in node and 'code' in node['context']:
+ node = node['context']['code']
+
+ if node.get('type', None) == 'nagios_hostgroup':
+ data = node['instances']['children'][0]
+ title = data['title']['value']
+ members = filter(lambda c: c['param'] == 'members', data['parameters']['children'])[0]['value']['value']
+ members = re.split(r'\s*,\s*', members)
+ groups[title] = members
+ elif node.get('type', None) == 'nagios_host':
+ data = node['instances']['children'][0]
+ title = data['title']['value']
+ hosts.add(title)
+
+ for child in node['children']:
+ processNode(child, hosts, groups)
+ return hosts, groups
+
+ monitoringConfig = readMonitoringConfig()
+ if not monitoringConfig:
+ print >>sys.stderr, "Failed to parse monitoring configuration"
+ return [[], []]
+ # Extract hosts and groups from monitoring config
+ return processNode(monitoringConfig)
+
+def resolveHostList(hosts):
+ validHosts, validGroups = getValidHosts()
+ if not validHosts:
+ print >>sys.stderr, "Warning: No valid hosts found, not validating"
+ return hosts
+
+ result = set()
+ for param in hosts:
+ if param in validGroups:
+ for host in validGroups[param]:
+ if host == '*':
+ result = result | validHosts
+ else:
+ result.add(host)
+ elif param in validHosts:
+ result.add(param)
+ elif '%s.adblockplus.org' % param in validHosts:
+ result.add('%s.adblockplus.org' % param)
+ else:
+ print >>sys.stderr, 'Warning: failed to recognize host or group %s' %param
+ return result
+
+def runCommand(user, host, command, ignore_errors=False):
+ if not isinstance(command, list):
+ command = [command]
+ command = ["ssh"] + (["-l", user] if user else []) + [host] + command
+ if ignore_errors:
+ subprocess.call(command)
+ else:
+ subprocess.check_call(command)
+
+if __name__ == "__main__":
+ user, hosts, ignore_errors, args = parseOptions(sys.argv[1:])
+ selectedHosts = resolveHostList(hosts)
+ if len(selectedHosts) == 0:
+ print >>sys.stderr, 'No valid hosts or groups specified, nothing to do'
+ sys.exit(0)
+ for host in selectedHosts:
+ print >>sys.stderr, 'Running on %s...' % host
+ runCommand(user, host, args, ignore_errors=ignore_errors)
« no previous file with comments | « kick.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld