~ubuntu-branches/ubuntu/vivid/crmsh/vivid-proposed

« back to all changes in this revision

Viewing changes to modules/crm_pssh.py

  • Committer: Package Import Robot
  • Author(s): Martin Loschwitz
  • Date: 2013-04-19 07:08:47 UTC
  • Revision ID: package-import@ubuntu.com-20130419070847-adka0ljtpr04pkrl
Tags: upstream-1.2.5+hg953
ImportĀ upstreamĀ versionĀ 1.2.5+hg953

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Modified pssh
 
2
# Copyright (c) 2011, Dejan Muhamedagic
 
3
# Copyright (c) 2009, Andrew McNabb
 
4
# Copyright (c) 2003-2008, Brent N. Chun
 
5
 
 
6
"""Parallel ssh to the set of nodes in hosts.txt.
 
7
 
 
8
For each node, this essentially does an "ssh host -l user prog [arg0] [arg1]
 
9
...". The -o option can be used to store stdout from each remote node in a
 
10
directory.  Each output file in that directory will be named by the
 
11
corresponding remote node's hostname or IP address.
 
12
"""
 
13
 
 
14
import fcntl
 
15
import os
 
16
import sys
 
17
import glob
 
18
import re
 
19
 
 
20
parent, bindir = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))
 
21
if os.path.exists(os.path.join(parent, 'psshlib')):
 
22
    sys.path.insert(0, parent)
 
23
 
 
24
from psshlib import psshutil
 
25
from psshlib.manager import Manager, FatalError
 
26
from psshlib.task import Task
 
27
from psshlib.cli import common_parser, common_defaults
 
28
 
 
29
from msg import *
 
30
 
 
31
_DEFAULT_TIMEOUT = 60
 
32
_EC_LOGROT = 120
 
33
 
 
34
def option_parser():
 
35
    parser = common_parser()
 
36
    parser.usage = "%prog [OPTIONS] command [...]"
 
37
    parser.epilog = "Example: pssh -h hosts.txt -l irb2 -o /tmp/foo uptime"
 
38
 
 
39
    parser.add_option('-i', '--inline', dest='inline', action='store_true',
 
40
            help='inline aggregated output for each server')
 
41
    parser.add_option('-I', '--send-input', dest='send_input',
 
42
            action='store_true',
 
43
            help='read from standard input and send as input to ssh')
 
44
    parser.add_option('-P', '--print', dest='print_out', action='store_true',
 
45
            help='print output as we get it')
 
46
 
 
47
    return parser
 
48
 
 
49
def parse_args(myargs, t = _DEFAULT_TIMEOUT):
 
50
    parser = option_parser()
 
51
    defaults = common_defaults(timeout=t)
 
52
    parser.set_defaults(**defaults)
 
53
    opts, args = parser.parse_args(myargs)
 
54
    return opts, args
 
55
 
 
56
def get_output(dir, host):
 
57
    l = []
 
58
    fl = glob.glob("%s/*%s*" % (dir,host))
 
59
    for fname in fl:
 
60
        try:
 
61
            if os.stat(fname).st_size == 0:
 
62
                continue
 
63
            f = open(fname)
 
64
        except:
 
65
            continue
 
66
        l = [x for x in f]
 
67
        f.close()
 
68
    return l
 
69
 
 
70
def show_output(dir, hosts, desc):
 
71
    for host in hosts:
 
72
        out_l = get_output(dir, host)
 
73
        if out_l:
 
74
            print "%s %s:" % (host, desc)
 
75
            print ''.join(out_l)
 
76
 
 
77
def do_pssh(l, opts):
 
78
    if opts.outdir and not os.path.exists(opts.outdir):
 
79
        os.makedirs(opts.outdir)
 
80
    if opts.errdir and not os.path.exists(opts.errdir):
 
81
        os.makedirs(opts.errdir)
 
82
    if opts.send_input:
 
83
        stdin = sys.stdin.read()
 
84
    else:
 
85
        stdin = None
 
86
    manager = Manager(opts)
 
87
    user = ""
 
88
    port = ""
 
89
    hosts = []
 
90
    for host, cmdline in l:
 
91
        cmd = ['ssh', host, '-o', 'PasswordAuthentication=no',
 
92
                '-o', 'SendEnv=PSSH_NODENUM',
 
93
                '-o', 'StrictHostKeyChecking=no']
 
94
        if opts.options:
 
95
            for opt in opts.options:
 
96
                cmd += ['-o', opt]
 
97
        if user:
 
98
            cmd += ['-l', user]
 
99
        if port:
 
100
            cmd += ['-p', port]
 
101
        if opts.extra:
 
102
            cmd.extend(opts.extra)
 
103
        if cmdline:
 
104
            cmd.append(cmdline)
 
105
        hosts.append(host)
 
106
        t = Task(host, port, user, cmd, opts, stdin)
 
107
        manager.add_task(t)
 
108
    try:
 
109
        return manager.run() # returns a list of exit codes
 
110
    except FatalError:
 
111
        common_err("pssh to nodes failed")
 
112
        show_output(opts.errdir, hosts, "stderr")
 
113
        return False
 
114
 
 
115
def examine_outcome(l, opts, statuses):
 
116
    '''
 
117
    A custom function to show stderr in case there were issues.
 
118
    Not suited for callers who want better control of output or
 
119
    per-host processing.
 
120
    '''
 
121
    hosts = [x[0] for x in l]
 
122
    if min(statuses) < 0:
 
123
        # At least one process was killed.
 
124
        common_err("ssh process was killed")
 
125
        show_output(opts.errdir, hosts, "stderr")
 
126
        return False
 
127
    # The any builtin was introduced in Python 2.5 (so we can't use it yet):
 
128
    #elif any(x==255 for x in statuses):
 
129
    for status in statuses:
 
130
        if status == 255:
 
131
            common_warn("ssh processes failed")
 
132
            show_output(opts.errdir, hosts, "stderr")
 
133
            return False
 
134
    for status in statuses:
 
135
        if status not in (0, _EC_LOGROT):
 
136
            common_warn("some ssh processes failed")
 
137
            show_output(opts.errdir, hosts, "stderr")
 
138
            return False
 
139
    return True
 
140
 
 
141
def next_loglines(a, outdir, errdir):
 
142
    '''
 
143
    pssh to nodes to collect new logs.
 
144
    '''
 
145
    l = []
 
146
    for node,rptlog,logfile,nextpos in a:
 
147
        common_debug("updating %s from %s (pos %d)" % (logfile, node, nextpos))
 
148
        cmdline = "perl -e 'exit(%d) if (stat(\"%s\"))[7]<%d' && tail -c +%d %s" % (_EC_LOGROT, logfile, nextpos-1, nextpos, logfile)
 
149
        myopts = ["-q", "-o", outdir, "-e", errdir]
 
150
        opts, args = parse_args(myopts)
 
151
        l.append([node, cmdline])
 
152
    statuses = do_pssh(l, opts)
 
153
    if statuses:
 
154
        return examine_outcome(l, opts, statuses)
 
155
    else:
 
156
        return False
 
157
 
 
158
def next_peinputs(node_pe_l, outdir, errdir):
 
159
    '''
 
160
    pssh to nodes to collect new logs.
 
161
    '''
 
162
    l = []
 
163
    for node,pe_l in node_pe_l:
 
164
        r = re.search("(.*)/pengine/", pe_l[0])
 
165
        if not r:
 
166
            common_err("strange, %s doesn't contain string pengine" % pe_l[0])
 
167
            continue
 
168
        dir = "/%s" % r.group(1)
 
169
        red_pe_l = [x.replace("%s/" % r.group(1),"") for x in pe_l]
 
170
        common_debug("getting new PE inputs %s from %s" % (red_pe_l, node))
 
171
        cmdline = "tar -C %s -cf - %s" % (dir, ' '.join(red_pe_l))
 
172
        myopts = ["-q", "-o", outdir, "-e", errdir]
 
173
        opts, args = parse_args(myopts)
 
174
        l.append([node, cmdline])
 
175
    if not l:
 
176
        # is this a failure?
 
177
        return True
 
178
    statuses = do_pssh(l, opts)
 
179
    if statuses:
 
180
        return examine_outcome(l, opts, statuses)
 
181
    else:
 
182
        return False
 
183
 
 
184
def do_pssh_cmd(cmd, node_l, outdir, errdir, timeout = 20000):
 
185
    '''
 
186
    pssh to nodes and run cmd.
 
187
    '''
 
188
    l = []
 
189
    for node in node_l:
 
190
        l.append([node, cmd])
 
191
    if not l:
 
192
        return True
 
193
    myopts = ["-q", "-o", outdir, "-e", errdir]
 
194
    opts, args = parse_args(myopts, t = str(int(timeout/1000)))
 
195
    return do_pssh(l, opts)
 
196
 
 
197
# vim:ts=4:sw=4:et: