1import threading
2import time
3
4from lib.remote.remote_util import RemoteMachineShellConnection
5
6from pytests.performance.perf_defaults import PerfDefaults
7
8
9class NRUMonitor(threading.Thread):
10
11    CMD_NUM_RUNS = "/opt/couchbase/bin/cbstats localhost:11210 "\
12                   "all | grep ep_num_access_scanner_runs"
13
14    CMD_NUM_ITEMS = "/opt/couchbase/bin/cbstats localhost:11210 "\
15                    "all | grep ep_access_scanner_num_items"
16
17    CMD_RUNTIME = "/opt/couchbase/bin/cbstats localhost:11210 "\
18                  "all | grep ep_access_scanner_last_runtime"
19
20    def __init__(self, freq, reb_delay, eperf):
21        self.freq = freq
22        self.reb_delay = reb_delay
23        self.eperf = eperf
24        self.shell = None
25        super(NRUMonitor, self).__init__()
26
27    def run(self):
28        print "[NRUMonitor] started running"
29
30        # TODO: evaluate all servers, smarter polling freq
31        server = self.eperf.input.servers[0]
32        self.shell = RemoteMachineShellConnection(server)
33
34        nru_num = self.nru_num = self.get_nru_num()
35        if self.nru_num < 0:
36            return
37
38        while nru_num <= self.nru_num:
39            print "[NRUMonitor] nru_num = %d, sleep for %d seconds"\
40                  % (nru_num, self.freq)
41            time.sleep(self.freq)
42            nru_num = self.get_nru_num()
43            if nru_num < 0:
44                break
45
46        gmt_now = time.strftime(PerfDefaults.strftime, time.gmtime())
47        speed, num_items, run_time = self.get_nru_speed()
48
49        print "[NRUMonitor] access scanner finished at: %s, speed: %s, "\
50              "num_items: %s, run_time: %s"\
51              % (gmt_now, speed, num_items, run_time)
52
53        self.eperf.clear_hot_keys()
54
55        print "[NRUMonitor] scheduled rebalance after %d seconds"\
56              % self.reb_delay
57
58        self.shell.disconnect()
59        self.eperf.latched_rebalance(delay=self.reb_delay, sync=True)
60
61        gmt_now = time.strftime(PerfDefaults.strftime, time.gmtime())
62        print "[NRUMonitor] rebalance finished: %s" % gmt_now
63
64        print "[NRUMonitor] stopped running"
65
66    def get_nru_num(self):
67        """Retrieve how many times nru access scanner has been run"""
68        return self._get_shell_int(NRUMonitor.CMD_NUM_RUNS)
69
70    def get_nru_speed(self):
71        """Retrieve runtime and num_items for the last access scanner run
72        Calculate access running speed
73
74        @return (speed, num_items, run_time)
75        """
76        num_items = self._get_shell_int(NRUMonitor.CMD_NUM_ITEMS)
77
78        if num_items <= 0:
79            return -1, -1, -1
80
81        run_time = self._get_shell_int(NRUMonitor.CMD_RUNTIME)
82
83        if run_time <= 0:
84            return -1, num_items, -1
85
86        speed = num_items / run_time
87
88        return speed, num_items, run_time
89
90    def _get_shell_int(self, cmd):
91        """Fire a shell command and return output as integer"""
92        if not cmd:
93            print "<_get_shell_int> invalid cmd"
94            return -1
95
96        output, error = self.shell.execute_command(cmd)
97
98        if error:
99            print "<_get_shell_int> unable to execute cmd '%s' from %s: %s"\
100                  % (cmd, self.shell.ip, error)
101            return -1
102
103        if not output:
104            print "<_get_shell_int> unable to execute cmd '%s' from %s: "\
105                  "empty output" % (cmd, self.shell.ip)
106            return -1
107
108        try:
109            num = int(output[0].split(":")[1])
110        except (AttributeError, IndexError, ValueError), e:
111            print "<_get_shell_int> unable to execute cmd '%s' from %s:"\
112                  "output - %s, error - %s" % (cmd, self.shell.ip, output, e)
113            return -1
114
115        if num < 0:
116            print "<_get_shell_int> invalid number: %d" % num
117            return -1
118
119        return num
120