| 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) |