~ubuntu-branches/ubuntu/utopic/pacemaker/utopic-proposed

« back to all changes in this revision

Viewing changes to cts/CTS.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2013-07-16 16:40:24 UTC
  • mfrom: (1.1.11) (2.2.3 experimental)
  • Revision ID: package-import@ubuntu.com-20130716164024-lvwrf4xivk1wdr3c
Tags: 1.1.9+git20130321-1ubuntu1
* Resync from debian expiremental.
* debian/control:
  - Use lower version for Build-Depends on libcorosync-dev
    and libqb-dev.
  - Build-Depends on libcfg-dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import types, string, select, sys, time, re, os, struct, signal
27
27
import time, syslog, random, traceback, base64, pickle, binascii, fcntl
28
28
 
 
29
 
29
30
from socket import gethostbyname_ex
30
31
from UserDict import UserDict
31
32
from subprocess import Popen,PIPE
32
33
from cts.CTSvars import *
33
34
 
 
35
trace_rsh=None
 
36
trace_lw=None
 
37
 
 
38
has_log_stats = {}
 
39
log_stats_bin = CTSvars.CRM_DAEMON_DIR + "/cts_log_stats.sh"
 
40
log_stats = """
 
41
#!/bin/bash
 
42
# Tool for generating system load reports while CTS runs
 
43
 
 
44
f=$1; shift
 
45
action=$1; shift
 
46
base=`basename $0`
 
47
 
 
48
if [ ! -e $f ]; then
 
49
    echo "Time, Load 1, Load 5, Load 15, Test Marker" > $f
 
50
fi
 
51
 
 
52
function killpid() {
 
53
    if [ -e $f.pid ]; then
 
54
       kill -9 `cat $f.pid`
 
55
       rm -f $f.pid
 
56
    fi
 
57
}
 
58
 
 
59
function status() {
 
60
    if [ -e $f.pid ]; then
 
61
       kill -0 `cat $f.pid`
 
62
       return $?
 
63
    else
 
64
       return 1
 
65
    fi
 
66
}
 
67
 
 
68
function start() {
 
69
    # Is it already running?
 
70
    if
 
71
        status
 
72
    then
 
73
        return
 
74
    fi
 
75
 
 
76
    echo Active as $$
 
77
    echo $$ > $f.pid
 
78
 
 
79
    while [ 1 = 1 ]; do
 
80
        uptime | sed s/up.*:/,/ | tr '\\n' ',' >> $f
 
81
        #top -b -c -n1 | grep -e usr/libexec/pacemaker | grep -v -e grep -e python | head -n 1 | sed s@/usr/libexec/pacemaker/@@ | awk '{print " 0, "$9", "$10", "$12}' | tr '\\n' ',' >> $f
 
82
        echo 0 >> $f
 
83
        sleep 5
 
84
    done
 
85
}
 
86
 
 
87
case $action in
 
88
    start)
 
89
        start
 
90
        ;;
 
91
    stop)
 
92
        killpid
 
93
        ;;
 
94
    delete)
 
95
        killpid
 
96
        rm -f $f
 
97
        ;;
 
98
    mark)
 
99
        uptime | sed s/up.*:/,/ | tr '\\n' ',' >> $f
 
100
        echo " $*" >> $f
 
101
        start
 
102
        ;;
 
103
    *)
 
104
        echo "Unknown action: $action."
 
105
        ;;
 
106
esac
 
107
"""
 
108
 
34
109
class CtsLab(UserDict):
35
110
    '''This class defines the Lab Environment for the Cluster Test System.
36
111
    It defines those things which are expected to change from test
93
168
            self.log("Random seed is: " + str(seed))
94
169
 
95
170
        self["RandSeed"] = seed
96
 
        self.RandomGen.seed(str(seed)) 
 
171
        self.RandomGen.seed(str(seed))
97
172
 
98
173
    def HasMinimalKeys(self):
99
174
        'Return TRUE if our object has the minimal set of keys/values in it'
118
193
        keys = []
119
194
        for key in self.keys():
120
195
            keys.append(key)
121
 
            
 
196
 
122
197
        keys.sort()
123
198
        for key in keys:
124
199
            self.debug("Environment["+key+"]:\t"+str(self[key]))
132
207
        for node in self["nodes"]:
133
208
            self.log("    * %s" % (node))
134
209
 
 
210
        self.StatsMark(0)
135
211
        if not Scenario.SetUp():
136
212
            return 1
137
213
 
141
217
            self.log("Exception by %s" % sys.exc_info()[0])
142
218
            for logmethod in self["logger"]:
143
219
                traceback.print_exc(50, logmethod)
144
 
            
 
220
 
145
221
            Scenario.summarize()
146
222
            Scenario.TearDown()
 
223
            self.StatsExtract()
147
224
            return 1
148
225
 
149
 
        #ClusterManager.oprofileSave(Iterations) 
 
226
        #ClusterManager.oprofileSave(Iterations)
150
227
        Scenario.TearDown()
151
 
        
 
228
        self.StatsExtract()
 
229
 
152
230
        Scenario.summarize()
153
231
        if Scenario.Stats["failure"] > 0:
154
232
            return Scenario.Stats["failure"]
206
284
        #    for elem in value:
207
285
        #        if not isinstance(elem, types.IntType):
208
286
        #            raise ValueError("'Randseed' list must all be ints")
209
 
              
 
287
 
210
288
        self.data[key] = value
211
289
 
212
290
    def IsValidNode(self, node):
223
301
        '''Choose a random node from the cluster'''
224
302
        return self.RandomGen.choice(self["nodes"])
225
303
 
 
304
    def StatsExtract(self):
 
305
        if not self["stats"]:
 
306
            return
 
307
 
 
308
        for host in self["nodes"]:
 
309
            log_stats_file = "%s/cts-stats.csv" % CTSvars.CRM_DAEMON_DIR
 
310
            if has_log_stats.has_key(host):
 
311
                self.rsh(host, '''bash %s %s stop''' % (log_stats_bin, log_stats_file))
 
312
                (rc, lines) = self.rsh(host, '''cat %s''' % log_stats_file, stdout=2)
 
313
                self.rsh(host, '''bash %s %s delete''' % (log_stats_bin, log_stats_file))
 
314
 
 
315
                fname = "cts-stats-%d-nodes-%s.csv" % (len(self["nodes"]), host)
 
316
                print "Extracted stats: %s" % fname
 
317
                fd = open(fname, "a")
 
318
                fd.writelines(lines)
 
319
                fd.close()
 
320
 
 
321
    def StatsMark(self, testnum):
 
322
        '''Mark the test number in the stats log'''
 
323
 
 
324
        global has_log_stats
 
325
        if not self["stats"]:
 
326
            return
 
327
 
 
328
        for host in self["nodes"]:
 
329
            log_stats_file = "%s/cts-stats.csv" % CTSvars.CRM_DAEMON_DIR
 
330
            if not has_log_stats.has_key(host):
 
331
 
 
332
                global log_stats
 
333
                global log_stats_bin
 
334
                script=log_stats
 
335
                #script = re.sub("\\\\", "\\\\", script)
 
336
                script = re.sub('\"', '\\\"', script)
 
337
                script = re.sub("'", "\'", script)
 
338
                script = re.sub("`", "\`", script)
 
339
                script = re.sub("\$", "\\\$", script)
 
340
 
 
341
                self.debug("Installing %s on %s" % (log_stats_bin, host))
 
342
                self.rsh(host, '''echo "%s" > %s''' % (script, log_stats_bin), silent=True)
 
343
                self.rsh(host, '''bash %s %s delete''' % (log_stats_bin, log_stats_file))
 
344
                has_log_stats[host] = 1
 
345
 
 
346
            # Now mark it
 
347
            self.rsh(host, '''bash %s %s mark %s''' % (log_stats_bin, log_stats_file, testnum), synchronous=0)
 
348
 
226
349
class Logger:
227
350
    TimeFormat = "%b %d %H:%M:%S\t"
228
351
 
285
408
          self.facility=SysLog.map[self.facility]
286
409
        syslog.closelog()
287
410
        syslog.openlog(self.source, 0, self.facility)
288
 
        
 
411
 
289
412
 
290
413
    def __call__(self, lines):
291
414
        if isinstance(lines, types.StringType):
303
426
        pass
304
427
 
305
428
    def __call__(self, lines):
306
 
        t = time.strftime(Logger.TimeFormat, time.localtime(time.time()))  
 
429
        t = time.strftime(Logger.TimeFormat, time.localtime(time.time()))
307
430
        if isinstance(lines, types.StringType):
308
431
            sys.__stderr__.writelines([t, lines, "\n"])
309
432
        else:
319
442
 
320
443
        if filename == None:
321
444
            filename=labinfo["LogFileName"]
322
 
        
 
445
 
323
446
        self.logfile=filename
324
447
        import os
325
448
        self.hostname = os.uname()[1]+" "
327
450
    def __call__(self, lines):
328
451
 
329
452
        fd = open(self.logfile, "a")
330
 
        t = time.strftime(Logger.TimeFormat, time.localtime(time.time()))  
 
453
        t = time.strftime(Logger.TimeFormat, time.localtime(time.time()))
331
454
 
332
455
        if isinstance(lines, types.StringType):
333
456
            fd.writelines([t, self.hostname, self.source, lines, "\n"])
350
473
        self.Env = Env
351
474
        self.silent = silent
352
475
 
 
476
        if trace_rsh:
 
477
            self.silent = False
 
478
 
353
479
        #   -n: no stdin, -x: no X11,
354
 
        #   -o ServerAliveInterval=5 disconnect after 3*5s if the server stops responding 
 
480
        #   -o ServerAliveInterval=5 disconnect after 3*5s if the server stops responding
355
481
        self.Command = "ssh -l root -n -x -o ServerAliveInterval=5 -o ConnectTimeout=10 -o TCPKeepAlive=yes -o ServerAliveCountMax=3 "
356
482
        #        -B: batch mode, -q: no stats (quiet)
357
483
        self.CpCommand = "scp -B -q"
361
487
    def enable_qarsh(self):
362
488
        # http://nstraz.wordpress.com/2008/12/03/introducing-qarsh/
363
489
        self.log("Using QARSH for connections to cluster nodes")
364
 
        
 
490
 
365
491
        self.Command = "qarsh -t 300 -l root"
366
492
        self.CpCommand = "qacp -q"
367
 
        
 
493
 
368
494
    def _fixcmd(self, cmd):
369
495
        return re.sub("\'", "'\\''", cmd)
370
496
 
407
533
        run it on.
408
534
        '''
409
535
 
 
536
        if trace_rsh:
 
537
            silent = False
 
538
 
410
539
        rc = 0
411
540
        result = None
412
541
        if not synchronous:
417
546
            if proc.pid > 0:
418
547
                return 0
419
548
            return -1
420
 
        
 
549
 
421
550
        proc = Popen(self._cmd([node, command]),
422
551
                     stdout = PIPE, stderr = PIPE, close_fds = True, shell = True)
423
552
 
459
588
        '''Perform a remote copy'''
460
589
        cpstring = self.CpCommand  + " \'" + source + "\'"  + " \'" + target + "\'"
461
590
        rc = os.system(cpstring)
 
591
        if trace_rsh:
 
592
            silent = False
462
593
        if not silent: self.debug("cmd: rc=%d: %s" % (rc, cpstring))
463
 
        
 
594
 
464
595
        return rc
465
596
 
 
597
 
466
598
has_log_watcher = {}
467
 
log_watcher_bin = "/tmp/cts_log_watcher.py"
 
599
log_watcher_bin = CTSvars.CRM_DAEMON_DIR + "/cts_log_watcher.py"
468
600
log_watcher = """
469
601
import sys, os, fcntl
470
602
 
487
619
    if skipthis:
488
620
        skipthis=None
489
621
        continue
490
 
    
 
622
 
491
623
    elif args[i] == '-l' or args[i] == '--limit':
492
624
        skipthis=1
493
625
        limit = int(args[i+1])
499
631
    elif args[i] == '-o' or args[i] == '--offset':
500
632
        skipthis=1
501
633
        offset = args[i+1]
502
 
    
 
634
 
503
635
    elif args[i] == '-p' or args[i] == '--prefix':
504
636
        skipthis=1
505
637
        prefix = args[i+1]
506
 
    
 
638
 
 
639
    elif args[i] == '-t' or args[i] == '--tag':
 
640
        skipthis=1
 
641
 
 
642
if not os.access(filename, os.R_OK):
 
643
    print prefix + 'Last read: %d, limit=%d, count=%d - unreadable' % (0, limit, 0)
 
644
    sys.exit(1)
 
645
 
507
646
logfile=open(filename, 'r')
508
647
logfile.seek(0, os.SEEK_END)
509
648
newsize=logfile.tell()
524
663
fcntl.fcntl(logfile.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
525
664
 
526
665
count = 0
527
 
while True: 
 
666
while True:
528
667
    if logfile.tell() >= newsize:   break
529
668
    elif limit and count >= limit: break
530
669
 
539
678
"""
540
679
 
541
680
class SearchObj:
542
 
    def __init__(self, Env, filename, host=None):
 
681
    def __init__(self, Env, filename, host=None, name=None):
543
682
 
544
683
        self.Env = Env
545
684
        self.host = host
 
685
        self.name = name
546
686
        self.filename = filename
547
687
 
548
688
        self.cache = []
553
693
 
554
694
        global has_log_watcher
555
695
        if not has_log_watcher.has_key(host):
556
 
            
 
696
 
557
697
            global log_watcher
558
698
            global log_watcher_bin
559
699
            self.debug("Installing %s on %s" % (log_watcher_bin, host))
587
727
            global log_watcher_bin
588
728
            (rc, lines) = self.Env.rsh(
589
729
                self.host,
590
 
                "python %s -p CTSwatcher: -f %s -o %s" % (log_watcher_bin, self.filename, self.offset), 
 
730
                "python %s -t %s -p CTSwatcher: -f %s -o %s" % (log_watcher_bin, self.name, self.filename, self.offset),
591
731
                stdout=None, silent=True, blocking=False)
592
 
            
 
732
 
593
733
            for line in lines:
594
734
                match = re.search("^CTSwatcher:Last read: (\d+)", line)
595
735
                if match:
620
760
          Call look() to scan the log looking for the patterns
621
761
    '''
622
762
 
623
 
    def __init__(self, Env, log, regexes, name="Anon", timeout=10, debug_level=None, silent=False):
 
763
    def __init__(self, Env, log, regexes, name="Anon", timeout=10, debug_level=None, silent=False, hosts=None):
624
764
        '''This is the constructor for the LogWatcher class.  It takes a
625
765
        log name to watch, and a list of regular expressions to watch for."
626
766
        '''
640
780
        self.file_list = []
641
781
        self.line_cache = []
642
782
 
 
783
        if hosts:
 
784
            self.hosts = hosts
 
785
        else:
 
786
            self.hosts = self.Env["nodes"]
 
787
 
 
788
        if trace_lw:
 
789
            self.debug_level = 3
 
790
            silent = False
 
791
 
643
792
        if not silent:
644
793
            for regex in self.regexes:
645
794
                self.debug("Looking for regex: "+regex)
659
808
        '''
660
809
 
661
810
        if self.Env["LogWatcher"] == "remote":
662
 
            for node in self.Env["nodes"]:
663
 
                self.file_list.append(SearchObj(self.Env, self.filename, node))
664
 
    
 
811
            for node in self.hosts:
 
812
                self.file_list.append(SearchObj(self.Env, self.filename, node, self.name))
 
813
 
665
814
        else:
666
815
            self.file_list.append(SearchObj(self.Env, self.filename))
667
816
 
682
831
            lines = f.next()
683
832
            if len(lines):
684
833
                self.line_cache.extend(lines)
685
 
        
 
834
 
686
835
    def look(self, timeout=None, silent=False):
687
836
        '''Examine the log looking for the given patterns.
688
837
        It starts looking from the place marked by setwatch().
695
844
        '''
696
845
        if timeout == None: timeout = self.Timeout
697
846
 
 
847
        if trace_lw:
 
848
            silent = False
 
849
 
698
850
        lines=0
 
851
        needlines=True
699
852
        begin=time.time()
700
853
        end=begin+timeout+1
701
854
        if self.debug_level > 2: self.debug("starting single search: timeout=%d, begin=%d, end=%d" % (timeout, begin, end))
702
855
 
703
 
        self.__get_lines()
 
856
        if not self.regexes:
 
857
            self.debug("Nothing to look for")
 
858
            return None
 
859
 
704
860
        while True:
705
861
 
706
862
            if len(self.line_cache):
714
870
                if self.debug_level > 2: self.debug("Processing: "+ line)
715
871
                for regex in self.regexes:
716
872
                    which=which+1
717
 
                    if self.debug_level > 2: self.debug("Comparing line to: "+ regex)
 
873
                    if self.debug_level > 3: self.debug("Comparing line to: "+ regex)
718
874
                    #matchobj = re.search(string.lower(regex), string.lower(line))
719
875
                    matchobj = re.search(regex, line)
720
876
                    if matchobj:
726
882
                            if self.debug_level > 1: self.debug("With: "+ regex)
727
883
                            return line
728
884
 
729
 
            elif timeout > 0 and end > time.time(): 
 
885
            elif timeout > 0 and end > time.time():
 
886
                if self.debug_level > 1: self.debug("lines during timeout")
730
887
                time.sleep(1)
731
888
                self.__get_lines()
732
889
 
733
 
            elif timeout > 0:
 
890
            elif needlines:
734
891
                # Grab any relevant messages that might have arrived since
735
892
                # the last time the buffer was populated
 
893
                if self.debug_level > 1: self.debug("lines without timeout")
736
894
                self.__get_lines()
737
895
 
738
896
                # Don't come back here again
739
 
                timeout = 0
 
897
                needlines = False
740
898
 
741
899
            else:
742
900
                self.debug("Single search terminated: start=%d, end=%d, now=%d, lines=%d" % (begin, end, time.time(), lines))
757
915
        save_regexes = self.regexes
758
916
        returnresult = []
759
917
 
760
 
        if not silent: 
 
918
        if trace_lw:
 
919
            silent = False
 
920
 
 
921
        if not silent:
761
922
            self.debug("starting search: timeout=%d" % timeout)
762
923
            for regex in self.regexes:
763
924
                if self.debug_level > 2: self.debug("Looking for regex: "+regex)
816
977
            time.sleep(30)
817
978
            if (not anytimeouts):
818
979
                self.Env.debug("Waiting for node %s to come up" % node)
819
 
                
 
980
 
820
981
            anytimeouts=1
821
982
            timeout = timeout - 1
822
983
 
889
1050
            "Pat:We_stopped"   : None,
890
1051
            "Pat:They_stopped" : None,
891
1052
 
 
1053
            "Pat:InfraUp"      : "%s",
 
1054
            "Pat:PacemakerUp"  : "%s",
 
1055
 
892
1056
            "BadRegexes"     : None,        # A set of "bad news" regexes
893
1057
                                        # to apply to the log
894
1058
        }
897
1061
        self.ShouldBeStatus={}
898
1062
        self.ns = NodeStatus(self.Env)
899
1063
        self.OurNode=string.lower(os.uname()[1])
 
1064
        self.__instance_errorstoignore = []
900
1065
 
901
1066
    def key_for_node(self, node):
902
1067
        return node
903
1068
 
 
1069
    def instance_errorstoignore_clear(self):
 
1070
        '''Allows the test scenario to reset instance errors to ignore on each iteration.'''
 
1071
        self.__instance_errorstoignore = []
 
1072
 
 
1073
    def instance_errorstoignore(self):
 
1074
        '''Return list of errors which are 'normal' for a specific test instance'''
 
1075
        return self.__instance_errorstoignore
 
1076
 
904
1077
    def errorstoignore(self):
905
1078
        '''Return list of errors which are 'normal' and should be ignored'''
906
1079
        return []
930
1103
            count=count+1
931
1104
        return count
932
1105
 
933
 
    def install_helper(self, filename, nodes=None):
934
 
        file_with_path="%s/%s" % (CTSvars.CTS_home, filename)
 
1106
    def install_helper(self, filename, destdir=None, nodes=None, sourcedir=None):
 
1107
        if sourcedir == None:
 
1108
            sourcedir = CTSvars.CTS_home
 
1109
        file_with_path="%s/%s" % (sourcedir, filename)
935
1110
        if not nodes:
936
1111
            nodes = self.Env["nodes"]
937
1112
 
938
 
        self.debug("Installing %s to %s on %s" % (filename, CTSvars.CTS_home, repr(self.Env["nodes"])))
 
1113
        if not destdir:
 
1114
            destdir=CTSvars.CTS_home
 
1115
 
 
1116
        self.debug("Installing %s to %s on %s" % (filename, destdir, repr(self.Env["nodes"])))
939
1117
        for node in nodes:
940
 
            self.rsh(node, "mkdir -p %s" % CTSvars.CTS_home)
941
 
            self.rsh.cp(file_with_path, "root@%s:%s" % (node, file_with_path))
 
1118
            self.rsh(node, "mkdir -p %s" % destdir)
 
1119
            self.rsh.cp(file_with_path, "root@%s:%s/%s" % (node, destdir, filename))
942
1120
        return file_with_path
943
1121
 
944
1122
    def install_config(self, node):
956
1134
    def prepare_fencing_watcher(self, node):
957
1135
        # If we don't have quorum now but get it as a result of starting this node,
958
1136
        # then a bunch of nodes might get fenced
 
1137
        upnode=None
959
1138
        if self.HasQuorum(None):
960
1139
            return None
961
1140
 
962
 
        if not self.has_key("Pat:They_fenced"):
 
1141
        if not self.has_key("Pat:Fencing_start"):
963
1142
            return None
964
1143
 
965
 
        if not self.has_key("Pat:They_fenced_offset"):
 
1144
        if not self.has_key("Pat:Fencing_ok"):
966
1145
            return None
967
1146
 
968
1147
        stonith = None
969
1148
        stonithPats = []
970
1149
        for peer in self.Env["nodes"]:
971
1150
            if peer != node and self.ShouldBeStatus[peer] != "up":
972
 
                stonithPats.append(self["Pat:They_fenced"] % peer)
 
1151
                stonithPats.append(self["Pat:Fencing_ok"] % peer)
 
1152
                stonithPats.append(self["Pat:Fencing_start"] % peer)
 
1153
            elif self.Env["Stack"] == "corosync (cman)":
 
1154
                # There is a delay between gaining quorum and CMAN starting fencing
 
1155
                # This can mean that even nodes that are fully up get fenced
 
1156
                # There is no use fighting it, just look for everyone so that CTS doesn't get confused
 
1157
                stonithPats.append(self["Pat:Fencing_ok"] % peer)
 
1158
                stonithPats.append(self["Pat:Fencing_start"] % peer)
973
1159
 
 
1160
            if peer != node and not upnode and self.ShouldBeStatus[peer] == "up":
 
1161
                upnode = peer
974
1162
 
975
1163
        # Look for STONITH ops, depending on Env["at-boot"] we might need to change the nodes status
976
 
        stonith = LogWatcher(self.Env, self["LogFileName"], stonithPats, "StartaCM", 0)
 
1164
        if not upnode:
 
1165
            return None
 
1166
 
 
1167
        stonith = LogWatcher(self.Env, self["LogFileName"], stonithPats, "StartupFencing", 0, hosts=[upnode])
977
1168
        stonith.setwatch()
978
1169
        return stonith
979
1170
 
980
1171
    def fencing_cleanup(self, node, stonith):
981
1172
        peer_list = []
 
1173
        peer_state = {}
 
1174
 
 
1175
        self.debug("Looking for nodes that were fenced as a result of %s starting" % node)
982
1176
 
983
1177
        # If we just started a node, we may now have quorum (and permission to fence)
984
 
        # Make sure everyone is online before continuing
985
 
        self.ns.WaitForAllNodesToComeUp(self.Env["nodes"])
986
 
 
987
1178
        if not stonith:
 
1179
            self.debug("Nothing to do")
988
1180
            return peer_list
989
1181
 
990
 
        if not self.HasQuorum(None) and len(self.Env["nodes"]) > 2:
 
1182
        q = self.HasQuorum(None)
 
1183
        if not q and len(self.Env["nodes"]) > 2:
991
1184
            # We didn't gain quorum - we shouldn't have shot anyone
 
1185
            self.debug("Quorum: %d Len: %d" % (q, len(self.Env["nodes"])))
992
1186
            return peer_list
993
1187
 
994
1188
        # Now see if any states need to be updated
997
1191
        while shot:
998
1192
            line = repr(shot)
999
1193
            self.debug("Found: "+ line)
 
1194
            del stonith.regexes[stonith.whichmatch]
1000
1195
 
1001
1196
            # Extract node name
1002
 
            start = line.find(self["Pat:They_fenced_offset"]) + len(self["Pat:They_fenced_offset"])
1003
 
            peer = line[start:].split("' ")[0]
1004
 
 
1005
 
            self.debug("Found peer: "+ peer)
1006
 
 
1007
 
            peer_list.append(peer)
1008
 
            self.ShouldBeStatus[peer]="down"
1009
 
            self.log("   Peer %s was fenced as a result of %s starting" % (peer, node)) 
1010
 
                
 
1197
            for n in self.Env["nodes"]:
 
1198
                if re.search(self["Pat:Fencing_ok"] % n, shot):
 
1199
                    peer = n
 
1200
                    peer_state[peer] = "complete"
 
1201
                    self.__instance_errorstoignore.append(self["Pat:Fencing_ok"] % peer)
 
1202
 
 
1203
                elif re.search(self["Pat:Fencing_start"] % n, shot):
 
1204
                    peer = n
 
1205
                    peer_state[peer] = "in-progress"
 
1206
                    self.__instance_errorstoignore.append(self["Pat:Fencing_start"] % peer)
 
1207
 
 
1208
            if not peer:
 
1209
                self.log("ERROR: Unknown stonith match: %s" % line)
 
1210
 
 
1211
            elif not peer in peer_list:
 
1212
                self.debug("Found peer: "+ peer)
 
1213
                peer_list.append(peer)
 
1214
 
1011
1215
            # Get the next one
1012
1216
            shot = stonith.look(60)
1013
1217
 
 
1218
        for peer in peer_list:
 
1219
 
 
1220
            self.ShouldBeStatus[peer] = "down"
 
1221
            self.debug("   Peer %s was fenced as a result of %s starting: %s" % (peer, node, peer_state[peer]))
 
1222
 
 
1223
            self.ns.WaitForNodeToComeUp(peer, self["DeadTime"])
 
1224
 
1014
1225
            # Poll until it comes up
1015
1226
            if self.Env["at-boot"]:
1016
1227
                if not self.StataCM(peer):
1017
1228
                    time.sleep(self["StartTime"])
1018
1229
 
1019
1230
                if not self.StataCM(peer):
1020
 
                    self.log("ERROR: Peer %s failed to restart after being fenced" % peer) 
 
1231
                    self.log("ERROR: Peer %s failed to restart after being fenced" % peer)
1021
1232
                    return None
1022
1233
 
1023
 
                self.ShouldBeStatus[peer]="up"
 
1234
                self.ShouldBeStatus[peer] = "up"
1024
1235
 
1025
1236
        return peer_list
1026
1237
 
1048
1259
 
1049
1260
        watch = LogWatcher(
1050
1261
            self.Env, self["LogFileName"], patterns, "StartaCM", self["StartTime"]+10)
1051
 
        
1052
 
        watch.setwatch()
1053
1262
 
1054
1263
        self.install_config(node)
1055
1264
 
1076
1285
 
1077
1286
        stonith = self.prepare_fencing_watcher(node)
1078
1287
 
 
1288
        watch.setwatch()
 
1289
 
1079
1290
        if self.rsh(node, startCmd) != 0:
1080
1291
            self.log ("Warn: Start command failed on node %s" %(node))
 
1292
            self.fencing_cleanup(node, stonith)
1081
1293
            return None
1082
1294
 
1083
1295
        self.ShouldBeStatus[node]="up"
1084
1296
        watch_result = watch.lookforall()
1085
1297
 
1086
 
        self.fencing_cleanup(node, stonith)
1087
 
 
1088
1298
        if watch.unmatched:
1089
1299
            for regex in watch.unmatched:
1090
1300
                self.log ("Warn: Startup pattern not found: %s" %(regex))
1091
1301
 
1092
 
 
1093
1302
        if watch_result and self.cluster_stable(self["DeadTime"]):
1094
1303
            #self.debug("Found match: "+ repr(watch_result))
 
1304
            self.fencing_cleanup(node, stonith)
1095
1305
            return 1
1096
1306
 
1097
1307
        elif self.StataCM(node) and self.cluster_stable(self["DeadTime"]):
 
1308
            self.fencing_cleanup(node, stonith)
1098
1309
            return 1
1099
1310
 
1100
1311
        self.log ("Warn: Start failed for node %s" %(node))
1140
1351
        if self.rsh(node, self["StopCmd"]) == 0:
1141
1352
            # Make sure we can continue even if corosync leaks
1142
1353
            # fdata-* is the old name
1143
 
            self.rsh(node, "rm -f /dev/shm/qb-* /dev/shm/fdata-*")
 
1354
            #self.rsh(node, "rm -f /dev/shm/qb-* /dev/shm/fdata-*")
1144
1355
            self.ShouldBeStatus[node]="down"
1145
1356
            self.cluster_stable(self["DeadTime"])
1146
1357
            return 1
1147
1358
        else:
1148
 
            self.log ("Could not stop %s on node %s" %(self["Name"], node))
 
1359
            self.log ("ERROR: Could not stop %s on node %s" %(self["Name"], node))
1149
1360
 
1150
1361
        return None
1151
1362
 
1184
1395
 
1185
1396
        '''Report the status of the cluster manager on a given node'''
1186
1397
 
1187
 
        out=self.rsh(node, self["StatusCmd"], 1)
 
1398
        out=self.rsh(node, self["StatusCmd"] % node, 1)
1188
1399
        ret= (string.find(out, 'stopped') == -1)
1189
1400
 
1190
1401
        try:
1215
1426
            nodelist=self.Env["nodes"]
1216
1427
        for node in nodelist:
1217
1428
            if self.ShouldBeStatus[node] == "down":
 
1429
                self.ns.WaitForAllNodesToComeUp(nodelist, 300)
1218
1430
                if not self.StartaCM(node, verbose=verbose):
1219
1431
                    ret = 0
1220
1432
        return ret
1280
1492
                else:
1281
1493
                    self.debug("Communication cut between %s and %s" % (target, node))
1282
1494
        return 1
1283
 
 
 
1495
 
1284
1496
    def unisolate_node(self, target, nodes=None):
1285
1497
        '''fix the communication between the nodes'''
1286
1498
        if not nodes:
1295
1507
                self.rsh(target, self["FixCommCmd"] % self.key_for_node(node), synchronous=0)
1296
1508
                self.rsh(node, self["FixCommCmd"] % self.key_for_node(target), synchronous=0)
1297
1509
                self.debug("Communication restored between %s and %s" % (target, node))
1298
 
        
 
1510
 
1299
1511
    def reducecomm_node(self,node):
1300
1512
        '''reduce the communication between the nodes'''
1301
1513
        rc = self.rsh(node, self["ReduceCommCmd"]%(self.Env["XmitLoss"],self.Env["RecvLoss"]))
1304
1516
        else:
1305
1517
            self.log("Could not reduce the communication between the nodes from node: %s" % node)
1306
1518
        return None
1307
 
    
 
1519
 
1308
1520
    def restorecomm_node(self,node):
1309
1521
        '''restore the saved communication between the nodes'''
1310
1522
        rc = 0
1321
1533
        # If we are auditing a partition, then one side will
1322
1534
        #   have quorum and the other not.
1323
1535
        # So the caller needs to tell us which we are checking
1324
 
        # If no value for node_list is specified... assume all nodes  
 
1536
        # If no value for node_list is specified... assume all nodes
1325
1537
        raise ValueError("Abstract Class member (HasQuorum)")
1326
 
    
 
1538
 
1327
1539
    def Components(self):
1328
1540
        raise ValueError("Abstract Class member (Components)")
1329
1541
 
1333
1545
                self.oprofileStart(n)
1334
1546
 
1335
1547
        elif node in self.Env["oprofile"]:
1336
 
            self.debug("Enabling oprofile on %s" % node) 
 
1548
            self.debug("Enabling oprofile on %s" % node)
1337
1549
            self.rsh(node, "opcontrol --init")
1338
1550
            self.rsh(node, "opcontrol --setup --no-vmlinux --separate=lib --callgraph=20 --image=all")
1339
1551
            self.rsh(node, "opcontrol --start")
1360
1572
                self.oprofileStop(n)
1361
1573
 
1362
1574
        elif node in self.Env["oprofile"]:
1363
 
            self.debug("Stopping oprofile on %s" % node) 
 
1575
            self.debug("Stopping oprofile on %s" % node)
1364
1576
            self.rsh(node, "opcontrol --reset")
1365
1577
            self.rsh(node, "opcontrol --shutdown 2>&1 > /dev/null")
1366
1578
 
1367
 
    
 
1579
 
1368
1580
class Resource:
1369
1581
    '''
1370
1582
    This is an HA resource (not a resource group).
1428
1640
class Component:
1429
1641
    def kill(self, node):
1430
1642
        None
1431
 
        
 
1643
 
1432
1644
class Process(Component):
1433
 
    def __init__(self, cm, name, process=None, dc_only=0, pats=[], dc_pats=[], badnews_ignore=[], triggersreboot=0):
 
1645
    def __init__(self, cm, name, process=None, dc_only=0, pats=[], dc_pats=[], badnews_ignore=[], common_ignore=[], triggersreboot=0):
1434
1646
        self.name = str(name)
1435
1647
        self.dc_only = dc_only
1436
1648
        self.pats = pats
1437
1649
        self.dc_pats = dc_pats
1438
1650
        self.CM = cm
1439
1651
        self.badnews_ignore = badnews_ignore
 
1652
        self.badnews_ignore.extend(common_ignore)
1440
1653
        self.triggersreboot = triggersreboot
 
1654
 
1441
1655
        if process:
1442
1656
            self.proc = str(process)
1443
1657
        else:
1449
1663
            self.CM.log ("ERROR: Kill %s failed on node %s" %(self.name,node))
1450
1664
            return None
1451
1665
        return 1
1452