| OLD | NEW | 
|---|
| (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 >>sys.stderr, "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 >>sys.stderr, 'Running on %s...' % host | 
|  | 135     runCommand(user, host, args, ignore_errors=ignore_errors) | 
| OLD | NEW | 
|---|