2 ## Copyright (c) 2010-2020 Intel Corporation
4 ## Licensed under the Apache License, Version 2.0 (the "License");
5 ## you may not use this file except in compliance with the License.
6 ## You may obtain a copy of the License at
8 ## http://www.apache.org/licenses/LICENSE-2.0
10 ## Unless required by applicable law or agreed to in writing, software
11 ## distributed under the License is distributed on an "AS IS" BASIS,
12 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ## See the License for the specific language governing permissions and
14 ## limitations under the License.
17 from __future__ import print_function
18 from __future__ import division
20 from builtins import map
21 from builtins import range
22 from past.utils import old_div
23 from builtins import object
28 from rapid_log import RapidLog
30 class prox_ctrl(object):
31 def __init__(self, ip, key=None, user=None):
40 def test_connect(self):
41 """Simply try to run 'true' over ssh on remote system.
42 On failure, raise RuntimeWarning exception when possibly worth
43 retrying, and raise RuntimeError exception otherwise.
45 return self.run_cmd('test -e /opt/rapid/system_ready_for_rapid', True)
49 RapidLog.debug("Trying to connect to machine \
50 on %s, attempt: %d" % (self._ip, attempts))
55 except RuntimeWarning as ex:
56 RapidLog.debug("RuntimeWarning %d:\n%s"
57 % (ex.returncode, ex.output.strip()))
60 RapidLog.exception("Failed to connect to instance after %d\
61 attempts:\n%s" % (attempts, ex))
62 raise Exception("Failed to connect to instance after %d \
63 attempts:\n%s" % (attempts, ex))
65 RapidLog.debug("Trying to connect to machine \
66 on %s, attempt: %d" % (self._ip, attempts))
67 RapidLog.debug("Connected to machine on %s" % self._ip)
69 def connect_socket(self):
71 RapidLog.debug("Trying to connect to PROX (just launched) on %s, \
72 attempt: %d" % (self._ip, attempts))
75 sock = self.prox_sock()
80 RapidLog.exception("Failed to connect to PROX on %s after %d \
81 attempts" % (self._ip, attempts))
82 raise Exception("Failed to connect to PROX on %s after %d \
83 attempts" % (self._ip, attempts))
85 RapidLog.debug("Trying to connect to PROX (just launched) on %s, \
86 attempt: %d" % (self._ip, attempts))
87 RapidLog.info("Connected to PROX on %s" % self._ip)
91 for sock in self._proxsock:
94 def run_cmd(self, command, _connect=False):
95 """Execute command over ssh on remote system.
96 Wait for remote command completion.
97 Return command output (combined stdout and stderr).
98 _connect argument is reserved for connect() method.
100 cmd = self._build_ssh(command)
102 return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
103 except subprocess.CalledProcessError as ex:
104 #if _connect and ex.returncode == 255:
106 raise RuntimeWarning(ex.output.strip())
107 raise RuntimeError('ssh returned exit status %d:\n%s'
108 % (ex.returncode, ex.output.strip()))
110 def prox_sock(self, port=8474):
111 """Connect to the PROX instance on remote system.
112 Return a prox_sock object on success, None on failure.
114 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116 sock.connect((self._ip, port))
117 prox = prox_sock(sock)
118 self._proxsock.append(prox)
123 def scp_put(self, src, dst):
124 """Copy src file from local system to dst on remote system."""
127 '-oStrictHostKeyChecking=no',
128 '-oUserKnownHostsFile=/dev/null',
130 if self._key is not None:
131 cmd.extend(['-i', self._key])
134 if self._user is not None:
135 remote += self._user + '@'
136 remote += self._ip + ':' + dst
139 # Actually ignore output on success, but capture stderr on failure
140 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
141 except subprocess.CalledProcessError as ex:
142 raise RuntimeError('scp returned exit status %d:\n%s'
143 % (ex.returncode, ex.output.strip()))
145 def scp_get(self, src, dst):
146 """Copy src file from remote system to dst on local system."""
149 '-oStrictHostKeyChecking=no',
150 '-oUserKnownHostsFile=/dev/null',
152 if self._key is not None:
153 cmd.extend(['-i', self._key])
155 if self._user is not None:
156 remote += self._user + '@'
157 remote += self._ip + ':/home/' + self._user + src
161 # Actually ignore output on success, but capture stderr on failure
162 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
163 except subprocess.CalledProcessError as ex:
164 raise RuntimeError('scp returned exit status %d:\n%s'
165 % (ex.returncode, ex.output.strip()))
167 def _build_ssh(self, command):
170 '-oStrictHostKeyChecking=no',
171 '-oUserKnownHostsFile=/dev/null',
173 if self._key is not None:
174 cmd.extend(['-i', self._key])
176 if self._user is not None:
177 remote += self._user + '@'
183 class prox_sock(object):
184 def __init__(self, sock):
189 if self._sock is not None:
193 def start(self, cores):
194 self._send('start %s' % ','.join(map(str, cores)))
196 def stop(self, cores):
197 self._send('stop %s' % ','.join(map(str, cores)))
199 def speed(self, speed, cores, tasks=[0]):
202 self._send('speed %s %s %s' % (core, task, speed))
204 def reset_stats(self):
205 self._send('reset stats')
207 def lat_stats(self, cores, tasks=[0]):
209 max_lat = avg_lat = 0
210 number_tasks_returning_stats = 0
212 self._send('lat all stats %s %s' % (','.join(map(str, cores)),
213 ','.join(map(str, tasks))))
216 stats = self._recv().split(',')
217 if 'is not measuring' in stats[0]:
219 if stats[0].startswith('error'):
220 RapidLog.critical("lat stats error: unexpected reply from PROX\
221 (potential incompatibility between scripts and PROX)")
222 raise Exception("lat stats error")
223 number_tasks_returning_stats += 1
224 min_lat = min(int(stats[0]),min_lat)
225 max_lat = max(int(stats[1]),max_lat)
226 avg_lat += int(stats[2])
227 #min_since begin = int(stats[3])
228 #max_since_begin = int(stats[4])
229 tsc = int(stats[5]) # Taking the last tsc as the timestamp since
230 # PROX will return the same tsc for each
231 # core/task combination
233 #coreid = int(stats[7])
234 #taskid = int(stats[8])
235 mis_ordered = int(stats[9])
236 extent = int(stats[10])
237 duplicate = int(stats[11])
238 stats = self._recv().split(':')
239 if stats[0].startswith('error'):
240 RapidLog.critical("lat stats error: unexpected lat bucket \
241 reply (potential incompatibility between scripts \
243 raise Exception("lat bucket reply error")
244 buckets[0] = int(stats[1])
245 for i in range(1, 128):
246 stats = self._recv().split(':')
247 buckets[i] = int(stats[1])
248 avg_lat = old_div(avg_lat,number_tasks_returning_stats)
249 self._send('stats latency(0).used')
250 used = float(self._recv())
251 self._send('stats latency(0).total')
252 total = float(self._recv())
253 return (min_lat, max_lat, avg_lat, (old_div(used,total)), tsc, hz,
256 def irq_stats(self, core, bucket, task=0):
257 self._send('stats task.core(%s).task(%s).irq(%s)' %
258 (core, task, bucket))
259 stats = self._recv().split(',')
262 def show_irq_buckets(self, core, task=0):
263 rx = tx = drop = tsc = hz = 0
264 self._send('show irq buckets %s %s' % (core,task))
265 buckets = self._recv().split(';')
266 buckets = buckets[:-1]
269 def core_stats(self, cores, tasks=[0]):
270 rx = tx = drop = tsc = hz = rx_non_dp = tx_non_dp = tx_fail = 0
271 self._send('dp core stats %s %s' % (','.join(map(str, cores)),
272 ','.join(map(str, tasks))))
275 stats = self._recv().split(',')
276 if stats[0].startswith('error'):
277 if stats[0].startswith('error: invalid syntax'):
278 RapidLog.critical("dp core stats error: unexpected \
279 invalid syntax (potential incompatibility \
280 between scripts and PROX)")
281 raise Exception("dp core stats error")
285 rx_non_dp += int(stats[2])
286 tx_non_dp += int(stats[3])
287 drop += int(stats[4])
288 tx_fail += int(stats[5])
291 return rx, rx_non_dp, tx, tx_non_dp, drop, tx_fail, tsc, hz
293 def multi_port_stats(self, ports=[0]):
294 rx = tx = port_id = tsc = no_mbufs = errors = 0
295 self._send('multi port stats %s' % (','.join(map(str, ports))))
296 result = self._recv().split(';')
297 if result[0].startswith('error'):
298 RapidLog.critical("multi port stats error: unexpected invalid \
299 syntax (potential incompatibility between scripts and \
301 raise Exception("multi port stats error")
302 for statistics in result:
303 stats = statistics.split(',')
304 port_id = int(stats[0])
307 no_mbufs += int(stats[3])
308 errors += int(stats[4])
310 return rx, tx, no_mbufs, errors, tsc
312 def set_random(self, cores, task, offset, mask, length):
313 self._send('set random %s %s %s %s %s' % (','.join(map(str, cores)),
314 task, offset, mask, length))
316 def set_size(self, cores, task, pkt_size):
317 self._send('pkt_size %s %s %s' % (','.join(map(str, cores)), task,
320 def set_imix(self, cores, task, imix):
321 self._send('imix %s %s %s' % (','.join(map(str, cores)), task,
322 ','.join(map(str,imix))))
324 def set_value(self, cores, task, offset, value, length):
325 self._send('set value %s %s %s %s %s' % (','.join(map(str, cores)),
326 task, offset, value, length))
331 def _send(self, cmd):
332 """Append LF and send command to the PROX instance."""
333 if self._sock is None:
334 raise RuntimeError("PROX socket closed, cannot send '%s'" % cmd)
335 self._sock.sendall(cmd.encode() + b'\n')
338 """Receive response from PROX instance, return it with LF removed."""
339 if self._sock is None:
340 raise RuntimeError("PROX socket closed, cannot receive anymore")
341 pos = self._rcvd.find(b'\n')
343 self._rcvd += self._sock.recv(256)
344 pos = self._rcvd.find(b'\n')
345 rsp = self._rcvd[:pos]
346 self._rcvd = self._rcvd[pos+1:]