-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathrun.py
More file actions
executable file
·128 lines (103 loc) · 3.85 KB
/
run.py
File metadata and controls
executable file
·128 lines (103 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python
# coding: utf-8
import argparse
import sys
import os
import posixpath
import re
import subprocess
import yaml
def createArgumentParser(**kwargs):
parser = argparse.ArgumentParser(**kwargs)
parser.add_argument(
'-u', '--user', metavar='user', dest='user', type=str,
help='user name for use with SSH, must exist on all hosts'
)
parser.add_argument(
'-l', '--local', action='store_false', dest='remote', default=None,
help='use the local version of hosts.yaml'
)
parser.add_argument(
'-r', '--remote', metavar='master', dest='remote', type=str,
help='use a remote (puppet-master) version of hosts.yaml'
)
return parser
def parseOptions(args):
description = 'Run a command on the given hosts or groups of hosts'
parser = createArgumentParser(description=description)
parser.add_argument(
'-i', '--ignore-errors', action='store_true', dest='ignore_errors',
help='continue execution on next host in case of an error'
)
hosts = set()
parser.add_argument(
'-t', '--target', metavar='host|group',
help='target host or group, can be specified multiple times',
type=lambda value: hosts.update([value])
)
parser.add_argument(
'args', metavar='command', type=str, nargs='+',
help='the command to run on the specified hosts'
)
options = parser.parse_args(args)
options.hosts = hosts
return options
def getValidHosts(options):
path_canonical = ('modules', 'private', 'hiera', 'hosts.yaml')
if options.remote:
login = ['-l', options.user] if options.user else []
path_name = posixpath.join('/etc/puppet/infrastructure', *path_canonical)
command = ['ssh'] + login + [options.remote, '--', 'sudo', 'cat', path_name]
child = subprocess.Popen(command, stderr=sys.stderr, stdout=subprocess.PIPE)
try:
config = yaml.load(child.stdout)
finally:
child.stdout.close()
child.wait()
elif options.remote is False:
dirname = os.path.dirname(sys.argv[0])
path_name = os.path.join(dirname, *path_canonical)
with open(path_name, 'rb') as handle:
config = yaml.load(handle)
else:
sys.exit('Please either specify a --remote host or use --local')
servers = config.get('adblockplus::hosts', {})
return servers
def resolveHostList(options):
result = set()
try:
valid_hosts = getValidHosts(options)
except Warning as error:
print >>sys.stderr, 'Warning: failed to determine valid hosts:', error
result.update(options.hosts)
else:
for name in options.hosts:
chunk = [
value.get('fqdn', key) for (key, value) in valid_hosts.items()
if name == key
or name == '*'
or name == value.get('fqdn', None)
or name in value.get('groups', ())
]
if len(chunk) == 0:
print >>sys.stderr, 'Warning: failed to recognize host or group', name
else:
result.update(chunk)
return result
def runCommand(user, host, command, ignore_errors=False):
if not isinstance(command, list):
command = [command]
command = ['ssh'] + ['-t'] + (['-l', user] if user else []) + [host] + command
if ignore_errors:
subprocess.call(command)
else:
subprocess.check_call(command)
if __name__ == '__main__':
options = parseOptions(sys.argv[1:])
selectedHosts = resolveHostList(options)
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(options.user, host, options.args, options.ignore_errors)