xref: /aosp_15_r20/external/openthread/tools/harness-thci/OpenThread_BR.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python
2*cfb92d14SAndroid Build Coastguard Worker#
3*cfb92d14SAndroid Build Coastguard Worker# Copyright (c) 2020, The OpenThread Authors.
4*cfb92d14SAndroid Build Coastguard Worker# All rights reserved.
5*cfb92d14SAndroid Build Coastguard Worker#
6*cfb92d14SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
7*cfb92d14SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met:
8*cfb92d14SAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright
9*cfb92d14SAndroid Build Coastguard Worker#    notice, this list of conditions and the following disclaimer.
10*cfb92d14SAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright
11*cfb92d14SAndroid Build Coastguard Worker#    notice, this list of conditions and the following disclaimer in the
12*cfb92d14SAndroid Build Coastguard Worker#    documentation and/or other materials provided with the distribution.
13*cfb92d14SAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the
14*cfb92d14SAndroid Build Coastguard Worker#    names of its contributors may be used to endorse or promote products
15*cfb92d14SAndroid Build Coastguard Worker#    derived from this software without specific prior written permission.
16*cfb92d14SAndroid Build Coastguard Worker#
17*cfb92d14SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*cfb92d14SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*cfb92d14SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*cfb92d14SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21*cfb92d14SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*cfb92d14SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*cfb92d14SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*cfb92d14SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*cfb92d14SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*cfb92d14SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*cfb92d14SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE.
28*cfb92d14SAndroid Build Coastguard Worker"""
29*cfb92d14SAndroid Build Coastguard Worker>> Thread Host Controller Interface
30*cfb92d14SAndroid Build Coastguard Worker>> Device : OpenThread_BR THCI
31*cfb92d14SAndroid Build Coastguard Worker>> Class : OpenThread_BR
32*cfb92d14SAndroid Build Coastguard Worker"""
33*cfb92d14SAndroid Build Coastguard Workerimport logging
34*cfb92d14SAndroid Build Coastguard Workerimport re
35*cfb92d14SAndroid Build Coastguard Workerimport sys
36*cfb92d14SAndroid Build Coastguard Workerimport time
37*cfb92d14SAndroid Build Coastguard Workerimport ipaddress
38*cfb92d14SAndroid Build Coastguard Worker
39*cfb92d14SAndroid Build Coastguard Workerimport serial
40*cfb92d14SAndroid Build Coastguard Workerfrom IThci import IThci
41*cfb92d14SAndroid Build Coastguard Workerfrom THCI.OpenThread import OpenThreadTHCI, watched, API
42*cfb92d14SAndroid Build Coastguard Worker
43*cfb92d14SAndroid Build Coastguard WorkerRPI_FULL_PROMPT = 'pi@raspberrypi:~$ '
44*cfb92d14SAndroid Build Coastguard WorkerRPI_USERNAME_PROMPT = 'raspberrypi login: '
45*cfb92d14SAndroid Build Coastguard WorkerRPI_PASSWORD_PROMPT = 'Password: '
46*cfb92d14SAndroid Build Coastguard Worker"""regex: used to split lines"""
47*cfb92d14SAndroid Build Coastguard WorkerLINESEPX = re.compile(r'\r\n|\n')
48*cfb92d14SAndroid Build Coastguard Worker
49*cfb92d14SAndroid Build Coastguard WorkerLOGX = re.compile(r'.*Under-voltage detected!')
50*cfb92d14SAndroid Build Coastguard Worker"""regex: used to filter logging"""
51*cfb92d14SAndroid Build Coastguard Worker
52*cfb92d14SAndroid Build Coastguard Workerassert LOGX.match('[57522.618196] Under-voltage detected! (0x00050005)')
53*cfb92d14SAndroid Build Coastguard Worker
54*cfb92d14SAndroid Build Coastguard WorkerOTBR_AGENT_SYSLOG_PATTERN = re.compile(r'raspberrypi otbr-agent\[\d+\]: (.*)')
55*cfb92d14SAndroid Build Coastguard Workerassert OTBR_AGENT_SYSLOG_PATTERN.search(
56*cfb92d14SAndroid Build Coastguard Worker    'Jun 23 05:21:22 raspberrypi otbr-agent[323]: =========[[THCI] direction=send | type=JOIN_FIN.req | len=039]==========]'
57*cfb92d14SAndroid Build Coastguard Worker).group(1) == '=========[[THCI] direction=send | type=JOIN_FIN.req | len=039]==========]'
58*cfb92d14SAndroid Build Coastguard Worker
59*cfb92d14SAndroid Build Coastguard Workerlogging.getLogger('paramiko').setLevel(logging.WARNING)
60*cfb92d14SAndroid Build Coastguard Worker
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard Workerclass SSHHandle(object):
63*cfb92d14SAndroid Build Coastguard Worker    # Unit: second
64*cfb92d14SAndroid Build Coastguard Worker    KEEPALIVE_INTERVAL = 30
65*cfb92d14SAndroid Build Coastguard Worker
66*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, ip, port, username, password):
67*cfb92d14SAndroid Build Coastguard Worker        self.ip = ip
68*cfb92d14SAndroid Build Coastguard Worker        self.port = int(port)
69*cfb92d14SAndroid Build Coastguard Worker        self.username = username
70*cfb92d14SAndroid Build Coastguard Worker        self.password = password
71*cfb92d14SAndroid Build Coastguard Worker        self.__handle = None
72*cfb92d14SAndroid Build Coastguard Worker
73*cfb92d14SAndroid Build Coastguard Worker        self.__connect()
74*cfb92d14SAndroid Build Coastguard Worker
75*cfb92d14SAndroid Build Coastguard Worker    def __connect(self):
76*cfb92d14SAndroid Build Coastguard Worker        import paramiko
77*cfb92d14SAndroid Build Coastguard Worker
78*cfb92d14SAndroid Build Coastguard Worker        self.close()
79*cfb92d14SAndroid Build Coastguard Worker
80*cfb92d14SAndroid Build Coastguard Worker        self.__handle = paramiko.SSHClient()
81*cfb92d14SAndroid Build Coastguard Worker        self.__handle.set_missing_host_key_policy(paramiko.AutoAddPolicy())
82*cfb92d14SAndroid Build Coastguard Worker        try:
83*cfb92d14SAndroid Build Coastguard Worker            self.__handle.connect(self.ip, port=self.port, username=self.username, password=self.password)
84*cfb92d14SAndroid Build Coastguard Worker        except paramiko.ssh_exception.AuthenticationException:
85*cfb92d14SAndroid Build Coastguard Worker            if not self.password:
86*cfb92d14SAndroid Build Coastguard Worker                self.__handle.get_transport().auth_none(self.username)
87*cfb92d14SAndroid Build Coastguard Worker            else:
88*cfb92d14SAndroid Build Coastguard Worker                raise
89*cfb92d14SAndroid Build Coastguard Worker
90*cfb92d14SAndroid Build Coastguard Worker        # Avoid SSH disconnection after idle for a long time
91*cfb92d14SAndroid Build Coastguard Worker        self.__handle.get_transport().set_keepalive(self.KEEPALIVE_INTERVAL)
92*cfb92d14SAndroid Build Coastguard Worker
93*cfb92d14SAndroid Build Coastguard Worker    def close(self):
94*cfb92d14SAndroid Build Coastguard Worker        if self.__handle is not None:
95*cfb92d14SAndroid Build Coastguard Worker            self.__handle.close()
96*cfb92d14SAndroid Build Coastguard Worker            self.__handle = None
97*cfb92d14SAndroid Build Coastguard Worker
98*cfb92d14SAndroid Build Coastguard Worker    def bash(self, cmd, timeout):
99*cfb92d14SAndroid Build Coastguard Worker        from paramiko import SSHException
100*cfb92d14SAndroid Build Coastguard Worker        retry = 3
101*cfb92d14SAndroid Build Coastguard Worker        for i in range(retry):
102*cfb92d14SAndroid Build Coastguard Worker            try:
103*cfb92d14SAndroid Build Coastguard Worker                stdin, stdout, stderr = self.__handle.exec_command(cmd, timeout=timeout)
104*cfb92d14SAndroid Build Coastguard Worker
105*cfb92d14SAndroid Build Coastguard Worker                sys.stderr.write(stderr.read())
106*cfb92d14SAndroid Build Coastguard Worker                output = [r.encode('utf8').rstrip('\r\n') for r in stdout.readlines()]
107*cfb92d14SAndroid Build Coastguard Worker                return output
108*cfb92d14SAndroid Build Coastguard Worker
109*cfb92d14SAndroid Build Coastguard Worker            except Exception:
110*cfb92d14SAndroid Build Coastguard Worker                if i < retry - 1:
111*cfb92d14SAndroid Build Coastguard Worker                    print('SSH connection is lost, try reconnect after 1 second.')
112*cfb92d14SAndroid Build Coastguard Worker                    time.sleep(1)
113*cfb92d14SAndroid Build Coastguard Worker                    self.__connect()
114*cfb92d14SAndroid Build Coastguard Worker                else:
115*cfb92d14SAndroid Build Coastguard Worker                    raise
116*cfb92d14SAndroid Build Coastguard Worker
117*cfb92d14SAndroid Build Coastguard Worker    def log(self, fmt, *args):
118*cfb92d14SAndroid Build Coastguard Worker        try:
119*cfb92d14SAndroid Build Coastguard Worker            msg = fmt % args
120*cfb92d14SAndroid Build Coastguard Worker            print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg))
121*cfb92d14SAndroid Build Coastguard Worker        except Exception:
122*cfb92d14SAndroid Build Coastguard Worker            pass
123*cfb92d14SAndroid Build Coastguard Worker
124*cfb92d14SAndroid Build Coastguard Worker
125*cfb92d14SAndroid Build Coastguard Workerclass SerialHandle:
126*cfb92d14SAndroid Build Coastguard Worker
127*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, port, baudrate):
128*cfb92d14SAndroid Build Coastguard Worker        self.port = port
129*cfb92d14SAndroid Build Coastguard Worker        self.__handle = serial.Serial(port, baudrate, timeout=0)
130*cfb92d14SAndroid Build Coastguard Worker
131*cfb92d14SAndroid Build Coastguard Worker        self.__lines = ['']
132*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) >= 1, self.__lines
133*cfb92d14SAndroid Build Coastguard Worker
134*cfb92d14SAndroid Build Coastguard Worker        self.log("inputing username ...")
135*cfb92d14SAndroid Build Coastguard Worker        self.__bashWriteLine('pi')
136*cfb92d14SAndroid Build Coastguard Worker        deadline = time.time() + 20
137*cfb92d14SAndroid Build Coastguard Worker        loginOk = False
138*cfb92d14SAndroid Build Coastguard Worker        while time.time() < deadline:
139*cfb92d14SAndroid Build Coastguard Worker            time.sleep(1)
140*cfb92d14SAndroid Build Coastguard Worker
141*cfb92d14SAndroid Build Coastguard Worker            lastLine = None
142*cfb92d14SAndroid Build Coastguard Worker            while True:
143*cfb92d14SAndroid Build Coastguard Worker                line = self.__bashReadLine(timeout=1)
144*cfb92d14SAndroid Build Coastguard Worker
145*cfb92d14SAndroid Build Coastguard Worker                if not line:
146*cfb92d14SAndroid Build Coastguard Worker                    break
147*cfb92d14SAndroid Build Coastguard Worker
148*cfb92d14SAndroid Build Coastguard Worker                lastLine = line
149*cfb92d14SAndroid Build Coastguard Worker
150*cfb92d14SAndroid Build Coastguard Worker            if lastLine == RPI_FULL_PROMPT:
151*cfb92d14SAndroid Build Coastguard Worker                self.log("prompt found, login success!")
152*cfb92d14SAndroid Build Coastguard Worker                loginOk = True
153*cfb92d14SAndroid Build Coastguard Worker                break
154*cfb92d14SAndroid Build Coastguard Worker
155*cfb92d14SAndroid Build Coastguard Worker            if lastLine == RPI_PASSWORD_PROMPT:
156*cfb92d14SAndroid Build Coastguard Worker                self.log("inputing password ...")
157*cfb92d14SAndroid Build Coastguard Worker                self.__bashWriteLine('raspberry')
158*cfb92d14SAndroid Build Coastguard Worker            elif lastLine == RPI_USERNAME_PROMPT:
159*cfb92d14SAndroid Build Coastguard Worker                self.log("inputing username ...")
160*cfb92d14SAndroid Build Coastguard Worker                self.__bashWriteLine('pi')
161*cfb92d14SAndroid Build Coastguard Worker            elif not lastLine:
162*cfb92d14SAndroid Build Coastguard Worker                self.log("inputing username ...")
163*cfb92d14SAndroid Build Coastguard Worker                self.__bashWriteLine('pi')
164*cfb92d14SAndroid Build Coastguard Worker
165*cfb92d14SAndroid Build Coastguard Worker        if not loginOk:
166*cfb92d14SAndroid Build Coastguard Worker            raise Exception('login fail')
167*cfb92d14SAndroid Build Coastguard Worker
168*cfb92d14SAndroid Build Coastguard Worker        self.bash('stty cols 256')
169*cfb92d14SAndroid Build Coastguard Worker
170*cfb92d14SAndroid Build Coastguard Worker    def log(self, fmt, *args):
171*cfb92d14SAndroid Build Coastguard Worker        try:
172*cfb92d14SAndroid Build Coastguard Worker            msg = fmt % args
173*cfb92d14SAndroid Build Coastguard Worker            print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg))
174*cfb92d14SAndroid Build Coastguard Worker        except Exception:
175*cfb92d14SAndroid Build Coastguard Worker            pass
176*cfb92d14SAndroid Build Coastguard Worker
177*cfb92d14SAndroid Build Coastguard Worker    def close(self):
178*cfb92d14SAndroid Build Coastguard Worker        self.__handle.close()
179*cfb92d14SAndroid Build Coastguard Worker
180*cfb92d14SAndroid Build Coastguard Worker    def bash(self, cmd, timeout=10):
181*cfb92d14SAndroid Build Coastguard Worker        """
182*cfb92d14SAndroid Build Coastguard Worker        Execute the command in bash.
183*cfb92d14SAndroid Build Coastguard Worker        """
184*cfb92d14SAndroid Build Coastguard Worker        self.__bashClearLines()
185*cfb92d14SAndroid Build Coastguard Worker        self.__bashWriteLine(cmd)
186*cfb92d14SAndroid Build Coastguard Worker        self.__bashExpect(cmd, timeout=timeout, endswith=True)
187*cfb92d14SAndroid Build Coastguard Worker
188*cfb92d14SAndroid Build Coastguard Worker        response = []
189*cfb92d14SAndroid Build Coastguard Worker
190*cfb92d14SAndroid Build Coastguard Worker        deadline = time.time() + timeout
191*cfb92d14SAndroid Build Coastguard Worker        while time.time() < deadline:
192*cfb92d14SAndroid Build Coastguard Worker            line = self.__bashReadLine()
193*cfb92d14SAndroid Build Coastguard Worker            if line is None:
194*cfb92d14SAndroid Build Coastguard Worker                time.sleep(0.01)
195*cfb92d14SAndroid Build Coastguard Worker                continue
196*cfb92d14SAndroid Build Coastguard Worker
197*cfb92d14SAndroid Build Coastguard Worker            if line == RPI_FULL_PROMPT:
198*cfb92d14SAndroid Build Coastguard Worker                # return response lines without prompt
199*cfb92d14SAndroid Build Coastguard Worker                return response
200*cfb92d14SAndroid Build Coastguard Worker
201*cfb92d14SAndroid Build Coastguard Worker            response.append(line)
202*cfb92d14SAndroid Build Coastguard Worker
203*cfb92d14SAndroid Build Coastguard Worker        self.__bashWrite('\x03')
204*cfb92d14SAndroid Build Coastguard Worker        raise Exception('%s: failed to find end of response' % self.port)
205*cfb92d14SAndroid Build Coastguard Worker
206*cfb92d14SAndroid Build Coastguard Worker    def __bashExpect(self, expected, timeout=20, endswith=False):
207*cfb92d14SAndroid Build Coastguard Worker        self.log('Expecting [%r]' % (expected))
208*cfb92d14SAndroid Build Coastguard Worker
209*cfb92d14SAndroid Build Coastguard Worker        deadline = time.time() + timeout
210*cfb92d14SAndroid Build Coastguard Worker        while time.time() < deadline:
211*cfb92d14SAndroid Build Coastguard Worker            line = self.__bashReadLine()
212*cfb92d14SAndroid Build Coastguard Worker            if line is None:
213*cfb92d14SAndroid Build Coastguard Worker                time.sleep(0.01)
214*cfb92d14SAndroid Build Coastguard Worker                continue
215*cfb92d14SAndroid Build Coastguard Worker
216*cfb92d14SAndroid Build Coastguard Worker            print('[%s] Got line [%r]' % (self.port, line))
217*cfb92d14SAndroid Build Coastguard Worker
218*cfb92d14SAndroid Build Coastguard Worker            if endswith:
219*cfb92d14SAndroid Build Coastguard Worker                matched = line.endswith(expected)
220*cfb92d14SAndroid Build Coastguard Worker            else:
221*cfb92d14SAndroid Build Coastguard Worker                matched = line == expected
222*cfb92d14SAndroid Build Coastguard Worker
223*cfb92d14SAndroid Build Coastguard Worker            if matched:
224*cfb92d14SAndroid Build Coastguard Worker                print('[%s] Expected [%r]' % (self.port, expected))
225*cfb92d14SAndroid Build Coastguard Worker                return
226*cfb92d14SAndroid Build Coastguard Worker
227*cfb92d14SAndroid Build Coastguard Worker        # failed to find the expected string
228*cfb92d14SAndroid Build Coastguard Worker        # send Ctrl+C to terminal
229*cfb92d14SAndroid Build Coastguard Worker        self.__bashWrite('\x03')
230*cfb92d14SAndroid Build Coastguard Worker        raise Exception('failed to find expected string[%s]' % expected)
231*cfb92d14SAndroid Build Coastguard Worker
232*cfb92d14SAndroid Build Coastguard Worker    def __bashRead(self, timeout=1):
233*cfb92d14SAndroid Build Coastguard Worker        deadline = time.time() + timeout
234*cfb92d14SAndroid Build Coastguard Worker        data = ''
235*cfb92d14SAndroid Build Coastguard Worker        while True:
236*cfb92d14SAndroid Build Coastguard Worker            piece = self.__handle.read()
237*cfb92d14SAndroid Build Coastguard Worker            data = data + piece.decode('utf8')
238*cfb92d14SAndroid Build Coastguard Worker            if piece:
239*cfb92d14SAndroid Build Coastguard Worker                continue
240*cfb92d14SAndroid Build Coastguard Worker
241*cfb92d14SAndroid Build Coastguard Worker            if data or time.time() >= deadline:
242*cfb92d14SAndroid Build Coastguard Worker                break
243*cfb92d14SAndroid Build Coastguard Worker
244*cfb92d14SAndroid Build Coastguard Worker        if data:
245*cfb92d14SAndroid Build Coastguard Worker            self.log('>>> %r', data)
246*cfb92d14SAndroid Build Coastguard Worker
247*cfb92d14SAndroid Build Coastguard Worker        return data
248*cfb92d14SAndroid Build Coastguard Worker
249*cfb92d14SAndroid Build Coastguard Worker    def __bashReadLine(self, timeout=1):
250*cfb92d14SAndroid Build Coastguard Worker        line = self.__bashGetNextLine()
251*cfb92d14SAndroid Build Coastguard Worker        if line is not None:
252*cfb92d14SAndroid Build Coastguard Worker            return line
253*cfb92d14SAndroid Build Coastguard Worker
254*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) == 1, self.__lines
255*cfb92d14SAndroid Build Coastguard Worker        tail = self.__lines.pop()
256*cfb92d14SAndroid Build Coastguard Worker
257*cfb92d14SAndroid Build Coastguard Worker        try:
258*cfb92d14SAndroid Build Coastguard Worker            tail += self.__bashRead(timeout=timeout)
259*cfb92d14SAndroid Build Coastguard Worker            tail = tail.replace(RPI_FULL_PROMPT, RPI_FULL_PROMPT + '\r\n')
260*cfb92d14SAndroid Build Coastguard Worker            tail = tail.replace(RPI_USERNAME_PROMPT, RPI_USERNAME_PROMPT + '\r\n')
261*cfb92d14SAndroid Build Coastguard Worker            tail = tail.replace(RPI_PASSWORD_PROMPT, RPI_PASSWORD_PROMPT + '\r\n')
262*cfb92d14SAndroid Build Coastguard Worker        finally:
263*cfb92d14SAndroid Build Coastguard Worker            self.__lines += [l.rstrip('\r') for l in LINESEPX.split(tail)]
264*cfb92d14SAndroid Build Coastguard Worker            assert len(self.__lines) >= 1, self.__lines
265*cfb92d14SAndroid Build Coastguard Worker
266*cfb92d14SAndroid Build Coastguard Worker        return self.__bashGetNextLine()
267*cfb92d14SAndroid Build Coastguard Worker
268*cfb92d14SAndroid Build Coastguard Worker    def __bashGetNextLine(self):
269*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) >= 1, self.__lines
270*cfb92d14SAndroid Build Coastguard Worker        while len(self.__lines) > 1:
271*cfb92d14SAndroid Build Coastguard Worker            line = self.__lines.pop(0)
272*cfb92d14SAndroid Build Coastguard Worker            assert len(self.__lines) >= 1, self.__lines
273*cfb92d14SAndroid Build Coastguard Worker            if LOGX.match(line):
274*cfb92d14SAndroid Build Coastguard Worker                logging.info('LOG: %s', line)
275*cfb92d14SAndroid Build Coastguard Worker                continue
276*cfb92d14SAndroid Build Coastguard Worker            else:
277*cfb92d14SAndroid Build Coastguard Worker                return line
278*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) >= 1, self.__lines
279*cfb92d14SAndroid Build Coastguard Worker        return None
280*cfb92d14SAndroid Build Coastguard Worker
281*cfb92d14SAndroid Build Coastguard Worker    def __bashWrite(self, data):
282*cfb92d14SAndroid Build Coastguard Worker        self.__handle.write(data)
283*cfb92d14SAndroid Build Coastguard Worker        self.log("<<< %r", data)
284*cfb92d14SAndroid Build Coastguard Worker
285*cfb92d14SAndroid Build Coastguard Worker    def __bashClearLines(self):
286*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) >= 1, self.__lines
287*cfb92d14SAndroid Build Coastguard Worker        while self.__bashReadLine(timeout=0) is not None:
288*cfb92d14SAndroid Build Coastguard Worker            pass
289*cfb92d14SAndroid Build Coastguard Worker        assert len(self.__lines) >= 1, self.__lines
290*cfb92d14SAndroid Build Coastguard Worker
291*cfb92d14SAndroid Build Coastguard Worker    def __bashWriteLine(self, line):
292*cfb92d14SAndroid Build Coastguard Worker        self.__bashWrite(line + '\n')
293*cfb92d14SAndroid Build Coastguard Worker
294*cfb92d14SAndroid Build Coastguard Worker
295*cfb92d14SAndroid Build Coastguard Workerclass OpenThread_BR(OpenThreadTHCI, IThci):
296*cfb92d14SAndroid Build Coastguard Worker    DEFAULT_COMMAND_TIMEOUT = 20
297*cfb92d14SAndroid Build Coastguard Worker
298*cfb92d14SAndroid Build Coastguard Worker    IsBorderRouter = True
299*cfb92d14SAndroid Build Coastguard Worker    __is_root = False
300*cfb92d14SAndroid Build Coastguard Worker
301*cfb92d14SAndroid Build Coastguard Worker    def _getHandle(self):
302*cfb92d14SAndroid Build Coastguard Worker        if self.connectType == 'ip':
303*cfb92d14SAndroid Build Coastguard Worker            return SSHHandle(self.telnetIp, self.telnetPort, self.telnetUsername, self.telnetPassword)
304*cfb92d14SAndroid Build Coastguard Worker        else:
305*cfb92d14SAndroid Build Coastguard Worker            return SerialHandle(self.port, 115200)
306*cfb92d14SAndroid Build Coastguard Worker
307*cfb92d14SAndroid Build Coastguard Worker    def _connect(self):
308*cfb92d14SAndroid Build Coastguard Worker        self.log("logging in to Raspberry Pi ...")
309*cfb92d14SAndroid Build Coastguard Worker        self.__cli_output_lines = []
310*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_skip_lines = None
311*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_last_read_ts = 0
312*cfb92d14SAndroid Build Coastguard Worker
313*cfb92d14SAndroid Build Coastguard Worker        self.__handle = self._getHandle()
314*cfb92d14SAndroid Build Coastguard Worker        if self.connectType == 'ip':
315*cfb92d14SAndroid Build Coastguard Worker            self.__is_root = self.telnetUsername == 'root'
316*cfb92d14SAndroid Build Coastguard Worker
317*cfb92d14SAndroid Build Coastguard Worker    def _disconnect(self):
318*cfb92d14SAndroid Build Coastguard Worker        if self.__handle:
319*cfb92d14SAndroid Build Coastguard Worker            self.__handle.close()
320*cfb92d14SAndroid Build Coastguard Worker            self.__handle = None
321*cfb92d14SAndroid Build Coastguard Worker
322*cfb92d14SAndroid Build Coastguard Worker    def _deviceBeforeReset(self):
323*cfb92d14SAndroid Build Coastguard Worker        if self.isPowerDown:
324*cfb92d14SAndroid Build Coastguard Worker            self.log('Powering up the device')
325*cfb92d14SAndroid Build Coastguard Worker            self.powerUp()
326*cfb92d14SAndroid Build Coastguard Worker        if self.IsHost:
327*cfb92d14SAndroid Build Coastguard Worker            self.__stopRadvdService()
328*cfb92d14SAndroid Build Coastguard Worker            self.bash('ip -6 addr del 910b::1 dev %s || true' % self.backboneNetif)
329*cfb92d14SAndroid Build Coastguard Worker            self.bash('ip -6 addr del fd00:7d03:7d03:7d03::1 dev %s || true' % self.backboneNetif)
330*cfb92d14SAndroid Build Coastguard Worker
331*cfb92d14SAndroid Build Coastguard Worker        self.stopListeningToAddrAll()
332*cfb92d14SAndroid Build Coastguard Worker
333*cfb92d14SAndroid Build Coastguard Worker    def _deviceAfterReset(self):
334*cfb92d14SAndroid Build Coastguard Worker        self.__dumpSyslog()
335*cfb92d14SAndroid Build Coastguard Worker        self.__truncateSyslog()
336*cfb92d14SAndroid Build Coastguard Worker        self.__enableAcceptRa()
337*cfb92d14SAndroid Build Coastguard Worker        if not self.IsHost:
338*cfb92d14SAndroid Build Coastguard Worker            self._restartAgentService()
339*cfb92d14SAndroid Build Coastguard Worker            time.sleep(2)
340*cfb92d14SAndroid Build Coastguard Worker
341*cfb92d14SAndroid Build Coastguard Worker    def __enableAcceptRa(self):
342*cfb92d14SAndroid Build Coastguard Worker        self.bash('sysctl net.ipv6.conf.%s.accept_ra=2' % self.backboneNetif)
343*cfb92d14SAndroid Build Coastguard Worker
344*cfb92d14SAndroid Build Coastguard Worker    def _beforeRegisterMulticast(self, sAddr='ff04::1234:777a:1', timeout=300):
345*cfb92d14SAndroid Build Coastguard Worker        """subscribe to the given ipv6 address (sAddr) in interface and send MLR.req OTA
346*cfb92d14SAndroid Build Coastguard Worker
347*cfb92d14SAndroid Build Coastguard Worker        Args:
348*cfb92d14SAndroid Build Coastguard Worker            sAddr   : str : Multicast address to be subscribed and notified OTA.
349*cfb92d14SAndroid Build Coastguard Worker        """
350*cfb92d14SAndroid Build Coastguard Worker
351*cfb92d14SAndroid Build Coastguard Worker        if self.externalCommissioner is not None:
352*cfb92d14SAndroid Build Coastguard Worker            self.externalCommissioner.MLR([sAddr], timeout)
353*cfb92d14SAndroid Build Coastguard Worker            return True
354*cfb92d14SAndroid Build Coastguard Worker
355*cfb92d14SAndroid Build Coastguard Worker        cmd = 'nohup ~/repo/openthread/tests/scripts/thread-cert/mcast6.py wpan0 %s' % sAddr
356*cfb92d14SAndroid Build Coastguard Worker        cmd = cmd + ' > /dev/null 2>&1 &'
357*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
358*cfb92d14SAndroid Build Coastguard Worker
359*cfb92d14SAndroid Build Coastguard Worker    @API
360*cfb92d14SAndroid Build Coastguard Worker    def setupHost(self, setDp=False, setDua=False):
361*cfb92d14SAndroid Build Coastguard Worker        self.IsHost = True
362*cfb92d14SAndroid Build Coastguard Worker
363*cfb92d14SAndroid Build Coastguard Worker        self.bash('ip -6 addr add 910b::1 dev %s' % self.backboneNetif)
364*cfb92d14SAndroid Build Coastguard Worker
365*cfb92d14SAndroid Build Coastguard Worker        if setDua:
366*cfb92d14SAndroid Build Coastguard Worker            self.bash('ip -6 addr add fd00:7d03:7d03:7d03::1 dev %s' % self.backboneNetif)
367*cfb92d14SAndroid Build Coastguard Worker
368*cfb92d14SAndroid Build Coastguard Worker        self.__startRadvdService(setDp)
369*cfb92d14SAndroid Build Coastguard Worker
370*cfb92d14SAndroid Build Coastguard Worker    def _deviceEscapeEscapable(self, string):
371*cfb92d14SAndroid Build Coastguard Worker        """Escape CLI escapable characters in the given string.
372*cfb92d14SAndroid Build Coastguard Worker
373*cfb92d14SAndroid Build Coastguard Worker        Args:
374*cfb92d14SAndroid Build Coastguard Worker            string (str): UTF-8 input string.
375*cfb92d14SAndroid Build Coastguard Worker
376*cfb92d14SAndroid Build Coastguard Worker        Returns:
377*cfb92d14SAndroid Build Coastguard Worker            [str]: The modified string with escaped characters.
378*cfb92d14SAndroid Build Coastguard Worker        """
379*cfb92d14SAndroid Build Coastguard Worker        return '"' + string + '"'
380*cfb92d14SAndroid Build Coastguard Worker
381*cfb92d14SAndroid Build Coastguard Worker    @watched
382*cfb92d14SAndroid Build Coastguard Worker    def bash(self, cmd, timeout=DEFAULT_COMMAND_TIMEOUT, sudo=True):
383*cfb92d14SAndroid Build Coastguard Worker        return self.bash_unwatched(cmd, timeout=timeout, sudo=sudo)
384*cfb92d14SAndroid Build Coastguard Worker
385*cfb92d14SAndroid Build Coastguard Worker    def bash_unwatched(self, cmd, timeout=DEFAULT_COMMAND_TIMEOUT, sudo=True):
386*cfb92d14SAndroid Build Coastguard Worker        if sudo and not self.__is_root:
387*cfb92d14SAndroid Build Coastguard Worker            cmd = 'sudo ' + cmd
388*cfb92d14SAndroid Build Coastguard Worker
389*cfb92d14SAndroid Build Coastguard Worker        return self.__handle.bash(cmd, timeout=timeout)
390*cfb92d14SAndroid Build Coastguard Worker
391*cfb92d14SAndroid Build Coastguard Worker    # Override send_udp
392*cfb92d14SAndroid Build Coastguard Worker    @API
393*cfb92d14SAndroid Build Coastguard Worker    def send_udp(self, interface, dst, port, payload):
394*cfb92d14SAndroid Build Coastguard Worker        if interface == 0:  # Thread Interface
395*cfb92d14SAndroid Build Coastguard Worker            super(OpenThread_BR, self).send_udp(interface, dst, port, payload)
396*cfb92d14SAndroid Build Coastguard Worker            return
397*cfb92d14SAndroid Build Coastguard Worker
398*cfb92d14SAndroid Build Coastguard Worker        if interface == 1:
399*cfb92d14SAndroid Build Coastguard Worker            ifname = self.backboneNetif
400*cfb92d14SAndroid Build Coastguard Worker        else:
401*cfb92d14SAndroid Build Coastguard Worker            raise AssertionError('Invalid interface set to send UDP: {} '
402*cfb92d14SAndroid Build Coastguard Worker                                 'Available interface options: 0 - Thread; 1 - Ethernet'.format(interface))
403*cfb92d14SAndroid Build Coastguard Worker        cmd = '/home/pi/reference-device/send_udp.py %s %s %s %s' % (ifname, dst, port, payload)
404*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
405*cfb92d14SAndroid Build Coastguard Worker
406*cfb92d14SAndroid Build Coastguard Worker    @API
407*cfb92d14SAndroid Build Coastguard Worker    def mldv2_query(self):
408*cfb92d14SAndroid Build Coastguard Worker        ifname = self.backboneNetif
409*cfb92d14SAndroid Build Coastguard Worker        dst = 'ff02::1'
410*cfb92d14SAndroid Build Coastguard Worker
411*cfb92d14SAndroid Build Coastguard Worker        cmd = '/home/pi/reference-device/send_mld_query.py %s %s' % (ifname, dst)
412*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
413*cfb92d14SAndroid Build Coastguard Worker
414*cfb92d14SAndroid Build Coastguard Worker    @API
415*cfb92d14SAndroid Build Coastguard Worker    def ip_neighbors_flush(self):
416*cfb92d14SAndroid Build Coastguard Worker        # clear neigh cache on linux
417*cfb92d14SAndroid Build Coastguard Worker        cmd1 = 'sudo ip -6 neigh flush nud all nud failed nud noarp dev %s' % self.backboneNetif
418*cfb92d14SAndroid Build Coastguard Worker        cmd2 = ('sudo ip -6 neigh list nud all dev %s '
419*cfb92d14SAndroid Build Coastguard Worker                '| cut -d " " -f1 '
420*cfb92d14SAndroid Build Coastguard Worker                '| sudo xargs -I{} ip -6 neigh delete {} dev %s') % (self.backboneNetif, self.backboneNetif)
421*cfb92d14SAndroid Build Coastguard Worker        cmd = '%s ; %s' % (cmd1, cmd2)
422*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd, sudo=False)
423*cfb92d14SAndroid Build Coastguard Worker
424*cfb92d14SAndroid Build Coastguard Worker    @API
425*cfb92d14SAndroid Build Coastguard Worker    def ip_neighbors_add(self, addr, lladdr, nud='noarp'):
426*cfb92d14SAndroid Build Coastguard Worker        cmd1 = 'sudo ip -6 neigh delete %s dev %s' % (addr, self.backboneNetif)
427*cfb92d14SAndroid Build Coastguard Worker        cmd2 = 'sudo ip -6 neigh add %s dev %s lladdr %s nud %s' % (addr, self.backboneNetif, lladdr, nud)
428*cfb92d14SAndroid Build Coastguard Worker        cmd = '%s ; %s' % (cmd1, cmd2)
429*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd, sudo=False)
430*cfb92d14SAndroid Build Coastguard Worker
431*cfb92d14SAndroid Build Coastguard Worker    @API
432*cfb92d14SAndroid Build Coastguard Worker    def get_eth_ll(self):
433*cfb92d14SAndroid Build Coastguard Worker        cmd = "ip -6 addr list dev %s | grep 'inet6 fe80' | awk '{print $2}'" % self.backboneNetif
434*cfb92d14SAndroid Build Coastguard Worker        ret = self.bash(cmd)[0].split('/')[0]
435*cfb92d14SAndroid Build Coastguard Worker        return ret
436*cfb92d14SAndroid Build Coastguard Worker
437*cfb92d14SAndroid Build Coastguard Worker    @API
438*cfb92d14SAndroid Build Coastguard Worker    def ping(self, strDestination, ilength=0, hop_limit=5, timeout=5):
439*cfb92d14SAndroid Build Coastguard Worker        """ send ICMPv6 echo request with a given length to a unicast destination
440*cfb92d14SAndroid Build Coastguard Worker            address
441*cfb92d14SAndroid Build Coastguard Worker
442*cfb92d14SAndroid Build Coastguard Worker        Args:
443*cfb92d14SAndroid Build Coastguard Worker            strDestination: the unicast destination address of ICMPv6 echo request
444*cfb92d14SAndroid Build Coastguard Worker            ilength: the size of ICMPv6 echo request payload
445*cfb92d14SAndroid Build Coastguard Worker            hop_limit: the hop limit
446*cfb92d14SAndroid Build Coastguard Worker            timeout: time before ping() stops
447*cfb92d14SAndroid Build Coastguard Worker        """
448*cfb92d14SAndroid Build Coastguard Worker        if hop_limit is None:
449*cfb92d14SAndroid Build Coastguard Worker            hop_limit = 5
450*cfb92d14SAndroid Build Coastguard Worker
451*cfb92d14SAndroid Build Coastguard Worker        if self.IsHost or self.IsBorderRouter:
452*cfb92d14SAndroid Build Coastguard Worker            ifName = self.backboneNetif
453*cfb92d14SAndroid Build Coastguard Worker        else:
454*cfb92d14SAndroid Build Coastguard Worker            ifName = 'wpan0'
455*cfb92d14SAndroid Build Coastguard Worker
456*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ping -6 -I %s %s -c 1 -s %d -W %d -t %d' % (
457*cfb92d14SAndroid Build Coastguard Worker            ifName,
458*cfb92d14SAndroid Build Coastguard Worker            strDestination,
459*cfb92d14SAndroid Build Coastguard Worker            int(ilength),
460*cfb92d14SAndroid Build Coastguard Worker            int(timeout),
461*cfb92d14SAndroid Build Coastguard Worker            int(hop_limit),
462*cfb92d14SAndroid Build Coastguard Worker        )
463*cfb92d14SAndroid Build Coastguard Worker
464*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd, sudo=False)
465*cfb92d14SAndroid Build Coastguard Worker        time.sleep(timeout)
466*cfb92d14SAndroid Build Coastguard Worker
467*cfb92d14SAndroid Build Coastguard Worker    def multicast_Ping(self, destination, length=20):
468*cfb92d14SAndroid Build Coastguard Worker        """send ICMPv6 echo request with a given length to a multicast destination
469*cfb92d14SAndroid Build Coastguard Worker           address
470*cfb92d14SAndroid Build Coastguard Worker
471*cfb92d14SAndroid Build Coastguard Worker        Args:
472*cfb92d14SAndroid Build Coastguard Worker            destination: the multicast destination address of ICMPv6 echo request
473*cfb92d14SAndroid Build Coastguard Worker            length: the size of ICMPv6 echo request payload
474*cfb92d14SAndroid Build Coastguard Worker        """
475*cfb92d14SAndroid Build Coastguard Worker        hop_limit = 5
476*cfb92d14SAndroid Build Coastguard Worker
477*cfb92d14SAndroid Build Coastguard Worker        if self.IsHost or self.IsBorderRouter:
478*cfb92d14SAndroid Build Coastguard Worker            ifName = self.backboneNetif
479*cfb92d14SAndroid Build Coastguard Worker        else:
480*cfb92d14SAndroid Build Coastguard Worker            ifName = 'wpan0'
481*cfb92d14SAndroid Build Coastguard Worker
482*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ping -6 -I %s %s -c 1 -s %d -t %d' % (ifName, destination, str(length), hop_limit)
483*cfb92d14SAndroid Build Coastguard Worker
484*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd, sudo=False)
485*cfb92d14SAndroid Build Coastguard Worker
486*cfb92d14SAndroid Build Coastguard Worker    @API
487*cfb92d14SAndroid Build Coastguard Worker    def getGUA(self, filterByPrefix=None, eth=False):
488*cfb92d14SAndroid Build Coastguard Worker        """get expected global unicast IPv6 address of Thread device
489*cfb92d14SAndroid Build Coastguard Worker
490*cfb92d14SAndroid Build Coastguard Worker        note: existing filterByPrefix are string of in lowercase. e.g.
491*cfb92d14SAndroid Build Coastguard Worker        '2001' or '2001:0db8:0001:0000".
492*cfb92d14SAndroid Build Coastguard Worker
493*cfb92d14SAndroid Build Coastguard Worker        Args:
494*cfb92d14SAndroid Build Coastguard Worker            filterByPrefix: a given expected global IPv6 prefix to be matched
495*cfb92d14SAndroid Build Coastguard Worker
496*cfb92d14SAndroid Build Coastguard Worker        Returns:
497*cfb92d14SAndroid Build Coastguard Worker            a global IPv6 address
498*cfb92d14SAndroid Build Coastguard Worker        """
499*cfb92d14SAndroid Build Coastguard Worker        # get global addrs set if multiple
500*cfb92d14SAndroid Build Coastguard Worker        if eth:
501*cfb92d14SAndroid Build Coastguard Worker            return self.__getEthGUA(filterByPrefix=filterByPrefix)
502*cfb92d14SAndroid Build Coastguard Worker        else:
503*cfb92d14SAndroid Build Coastguard Worker            return super(OpenThread_BR, self).getGUA(filterByPrefix=filterByPrefix)
504*cfb92d14SAndroid Build Coastguard Worker
505*cfb92d14SAndroid Build Coastguard Worker    def __getEthGUA(self, filterByPrefix=None):
506*cfb92d14SAndroid Build Coastguard Worker        globalAddrs = []
507*cfb92d14SAndroid Build Coastguard Worker
508*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ip -6 addr list dev %s | grep inet6' % self.backboneNetif
509*cfb92d14SAndroid Build Coastguard Worker        output = self.bash(cmd, sudo=False)
510*cfb92d14SAndroid Build Coastguard Worker        for line in output:
511*cfb92d14SAndroid Build Coastguard Worker            # example: inet6 2401:fa00:41:23:274a:1329:3ab9:d953/64 scope global dynamic noprefixroute
512*cfb92d14SAndroid Build Coastguard Worker            line = line.strip().split()
513*cfb92d14SAndroid Build Coastguard Worker
514*cfb92d14SAndroid Build Coastguard Worker            if len(line) < 4 or line[2] != 'scope':
515*cfb92d14SAndroid Build Coastguard Worker                continue
516*cfb92d14SAndroid Build Coastguard Worker
517*cfb92d14SAndroid Build Coastguard Worker            if line[3] != 'global':
518*cfb92d14SAndroid Build Coastguard Worker                continue
519*cfb92d14SAndroid Build Coastguard Worker
520*cfb92d14SAndroid Build Coastguard Worker            addr = line[1].split('/')[0]
521*cfb92d14SAndroid Build Coastguard Worker            addr = str(ipaddress.IPv6Address(addr.decode()).exploded)
522*cfb92d14SAndroid Build Coastguard Worker            globalAddrs.append(addr)
523*cfb92d14SAndroid Build Coastguard Worker
524*cfb92d14SAndroid Build Coastguard Worker        if not filterByPrefix:
525*cfb92d14SAndroid Build Coastguard Worker            return globalAddrs[0]
526*cfb92d14SAndroid Build Coastguard Worker        else:
527*cfb92d14SAndroid Build Coastguard Worker            if filterByPrefix[-2:] != '::':
528*cfb92d14SAndroid Build Coastguard Worker                filterByPrefix = '%s::' % filterByPrefix
529*cfb92d14SAndroid Build Coastguard Worker            prefix = ipaddress.IPv6Network((filterByPrefix + '/64').decode())
530*cfb92d14SAndroid Build Coastguard Worker            for fullIp in globalAddrs:
531*cfb92d14SAndroid Build Coastguard Worker                address = ipaddress.IPv6Address(fullIp.decode())
532*cfb92d14SAndroid Build Coastguard Worker                if address in prefix:
533*cfb92d14SAndroid Build Coastguard Worker                    return fullIp
534*cfb92d14SAndroid Build Coastguard Worker
535*cfb92d14SAndroid Build Coastguard Worker    def _cliReadLine(self):
536*cfb92d14SAndroid Build Coastguard Worker        # read commissioning log if it's commissioning
537*cfb92d14SAndroid Build Coastguard Worker        if not self.__cli_output_lines:
538*cfb92d14SAndroid Build Coastguard Worker            self.__readSyslogToCli()
539*cfb92d14SAndroid Build Coastguard Worker
540*cfb92d14SAndroid Build Coastguard Worker        if self.__cli_output_lines:
541*cfb92d14SAndroid Build Coastguard Worker            return self.__cli_output_lines.pop(0)
542*cfb92d14SAndroid Build Coastguard Worker
543*cfb92d14SAndroid Build Coastguard Worker        return None
544*cfb92d14SAndroid Build Coastguard Worker
545*cfb92d14SAndroid Build Coastguard Worker    @watched
546*cfb92d14SAndroid Build Coastguard Worker    def _deviceGetEtherMac(self):
547*cfb92d14SAndroid Build Coastguard Worker        # Harness wants it in string. Because wireshark filter for eth
548*cfb92d14SAndroid Build Coastguard Worker        # cannot be applies in hex
549*cfb92d14SAndroid Build Coastguard Worker        return self.bash('ip addr list dev %s | grep ether' % self.backboneNetif, sudo=False)[0].strip().split()[1]
550*cfb92d14SAndroid Build Coastguard Worker
551*cfb92d14SAndroid Build Coastguard Worker    @watched
552*cfb92d14SAndroid Build Coastguard Worker    def _onCommissionStart(self):
553*cfb92d14SAndroid Build Coastguard Worker        assert self.__syslog_skip_lines is None
554*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_skip_lines = int(self.bash('wc -l /var/log/syslog', sudo=False)[0].split()[0])
555*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_last_read_ts = 0
556*cfb92d14SAndroid Build Coastguard Worker
557*cfb92d14SAndroid Build Coastguard Worker    @watched
558*cfb92d14SAndroid Build Coastguard Worker    def _onCommissionStop(self):
559*cfb92d14SAndroid Build Coastguard Worker        assert self.__syslog_skip_lines is not None
560*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_skip_lines = None
561*cfb92d14SAndroid Build Coastguard Worker
562*cfb92d14SAndroid Build Coastguard Worker    @watched
563*cfb92d14SAndroid Build Coastguard Worker    def __startRadvdService(self, setDp=False):
564*cfb92d14SAndroid Build Coastguard Worker        assert self.IsHost, "radvd service runs on Host only"
565*cfb92d14SAndroid Build Coastguard Worker
566*cfb92d14SAndroid Build Coastguard Worker        conf = "EOF"
567*cfb92d14SAndroid Build Coastguard Worker        conf += "\ninterface %s" % self.backboneNetif
568*cfb92d14SAndroid Build Coastguard Worker        conf += "\n{"
569*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    AdvSendAdvert on;"
570*cfb92d14SAndroid Build Coastguard Worker        conf += "\n"
571*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    MinRtrAdvInterval 3;"
572*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    MaxRtrAdvInterval 30;"
573*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    AdvDefaultPreference low;"
574*cfb92d14SAndroid Build Coastguard Worker        conf += "\n"
575*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    prefix 910b::/64"
576*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    {"
577*cfb92d14SAndroid Build Coastguard Worker        conf += "\n        AdvOnLink on;"
578*cfb92d14SAndroid Build Coastguard Worker        conf += "\n        AdvAutonomous on;"
579*cfb92d14SAndroid Build Coastguard Worker        conf += "\n        AdvRouterAddr on;"
580*cfb92d14SAndroid Build Coastguard Worker        conf += "\n    };"
581*cfb92d14SAndroid Build Coastguard Worker        if setDp:
582*cfb92d14SAndroid Build Coastguard Worker            conf += "\n"
583*cfb92d14SAndroid Build Coastguard Worker            conf += "\n    prefix fd00:7d03:7d03:7d03::/64"
584*cfb92d14SAndroid Build Coastguard Worker            conf += "\n    {"
585*cfb92d14SAndroid Build Coastguard Worker            conf += "\n        AdvOnLink on;"
586*cfb92d14SAndroid Build Coastguard Worker            conf += "\n        AdvAutonomous off;"
587*cfb92d14SAndroid Build Coastguard Worker            conf += "\n        AdvRouterAddr off;"
588*cfb92d14SAndroid Build Coastguard Worker            conf += "\n    };"
589*cfb92d14SAndroid Build Coastguard Worker        conf += "\n};"
590*cfb92d14SAndroid Build Coastguard Worker        conf += "\nEOF"
591*cfb92d14SAndroid Build Coastguard Worker        cmd = 'sh -c "cat >/etc/radvd.conf <<%s"' % conf
592*cfb92d14SAndroid Build Coastguard Worker
593*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
594*cfb92d14SAndroid Build Coastguard Worker        self.bash(self.extraParams.get('cmd-restart-radvd', 'service radvd restart'))
595*cfb92d14SAndroid Build Coastguard Worker        self.bash('service radvd status')
596*cfb92d14SAndroid Build Coastguard Worker
597*cfb92d14SAndroid Build Coastguard Worker    @watched
598*cfb92d14SAndroid Build Coastguard Worker    def __stopRadvdService(self):
599*cfb92d14SAndroid Build Coastguard Worker        assert self.IsHost, "radvd service runs on Host only"
600*cfb92d14SAndroid Build Coastguard Worker        self.bash('service radvd stop')
601*cfb92d14SAndroid Build Coastguard Worker
602*cfb92d14SAndroid Build Coastguard Worker    def __readSyslogToCli(self):
603*cfb92d14SAndroid Build Coastguard Worker        if self.__syslog_skip_lines is None:
604*cfb92d14SAndroid Build Coastguard Worker            return 0
605*cfb92d14SAndroid Build Coastguard Worker
606*cfb92d14SAndroid Build Coastguard Worker        # read syslog once per second
607*cfb92d14SAndroid Build Coastguard Worker        if time.time() < self.__syslog_last_read_ts + 1:
608*cfb92d14SAndroid Build Coastguard Worker            return 0
609*cfb92d14SAndroid Build Coastguard Worker
610*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_last_read_ts = time.time()
611*cfb92d14SAndroid Build Coastguard Worker
612*cfb92d14SAndroid Build Coastguard Worker        lines = self.bash_unwatched('tail +%d /var/log/syslog' % self.__syslog_skip_lines, sudo=False)
613*cfb92d14SAndroid Build Coastguard Worker        for line in lines:
614*cfb92d14SAndroid Build Coastguard Worker            m = OTBR_AGENT_SYSLOG_PATTERN.search(line)
615*cfb92d14SAndroid Build Coastguard Worker            if not m:
616*cfb92d14SAndroid Build Coastguard Worker                continue
617*cfb92d14SAndroid Build Coastguard Worker
618*cfb92d14SAndroid Build Coastguard Worker            self.__cli_output_lines.append(m.group(1))
619*cfb92d14SAndroid Build Coastguard Worker
620*cfb92d14SAndroid Build Coastguard Worker        self.__syslog_skip_lines += len(lines)
621*cfb92d14SAndroid Build Coastguard Worker        return len(lines)
622*cfb92d14SAndroid Build Coastguard Worker
623*cfb92d14SAndroid Build Coastguard Worker    def _cliWriteLine(self, line):
624*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ot-ctl -- %s' % line
625*cfb92d14SAndroid Build Coastguard Worker        output = self.bash(cmd)
626*cfb92d14SAndroid Build Coastguard Worker        # fake the line echo back
627*cfb92d14SAndroid Build Coastguard Worker        self.__cli_output_lines.append(line)
628*cfb92d14SAndroid Build Coastguard Worker        for line in output:
629*cfb92d14SAndroid Build Coastguard Worker            self.__cli_output_lines.append(line)
630*cfb92d14SAndroid Build Coastguard Worker
631*cfb92d14SAndroid Build Coastguard Worker    def _restartAgentService(self):
632*cfb92d14SAndroid Build Coastguard Worker        restart_cmd = self.extraParams.get('cmd-restart-otbr-agent', 'systemctl restart otbr-agent')
633*cfb92d14SAndroid Build Coastguard Worker        self.bash(restart_cmd)
634*cfb92d14SAndroid Build Coastguard Worker
635*cfb92d14SAndroid Build Coastguard Worker    def __truncateSyslog(self):
636*cfb92d14SAndroid Build Coastguard Worker        self.bash('truncate -s 0 /var/log/syslog')
637*cfb92d14SAndroid Build Coastguard Worker
638*cfb92d14SAndroid Build Coastguard Worker    def __dumpSyslog(self):
639*cfb92d14SAndroid Build Coastguard Worker        cmd = self.extraParams.get('cmd-dump-otbr-log', 'grep "otbr-agent" /var/log/syslog')
640*cfb92d14SAndroid Build Coastguard Worker        output = self.bash_unwatched(cmd)
641*cfb92d14SAndroid Build Coastguard Worker        for line in output:
642*cfb92d14SAndroid Build Coastguard Worker            self.log('%s', line)
643*cfb92d14SAndroid Build Coastguard Worker
644*cfb92d14SAndroid Build Coastguard Worker    @API
645*cfb92d14SAndroid Build Coastguard Worker    def get_eth_addrs(self):
646*cfb92d14SAndroid Build Coastguard Worker        cmd = "ip -6 addr list dev %s | grep 'inet6 ' | awk '{print $2}'" % self.backboneNetif
647*cfb92d14SAndroid Build Coastguard Worker        addrs = self.bash(cmd)
648*cfb92d14SAndroid Build Coastguard Worker        return [addr.split('/')[0] for addr in addrs]
649*cfb92d14SAndroid Build Coastguard Worker
650*cfb92d14SAndroid Build Coastguard Worker    @API
651*cfb92d14SAndroid Build Coastguard Worker    def mdns_query(self, service='_meshcop._udp.local', addrs_allowlist=(), addrs_denylist=()):
652*cfb92d14SAndroid Build Coastguard Worker        try:
653*cfb92d14SAndroid Build Coastguard Worker            for deny_addr in addrs_denylist:
654*cfb92d14SAndroid Build Coastguard Worker                self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j DROP' % deny_addr)
655*cfb92d14SAndroid Build Coastguard Worker
656*cfb92d14SAndroid Build Coastguard Worker            if addrs_allowlist:
657*cfb92d14SAndroid Build Coastguard Worker                for allow_addr in addrs_allowlist:
658*cfb92d14SAndroid Build Coastguard Worker                    self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j ACCEPT' % allow_addr)
659*cfb92d14SAndroid Build Coastguard Worker
660*cfb92d14SAndroid Build Coastguard Worker                self.bash('ip6tables -A INPUT -p udp --dport 5353 -j DROP')
661*cfb92d14SAndroid Build Coastguard Worker
662*cfb92d14SAndroid Build Coastguard Worker            return self._mdns_query_impl(service, find_active=(addrs_allowlist or addrs_denylist))
663*cfb92d14SAndroid Build Coastguard Worker
664*cfb92d14SAndroid Build Coastguard Worker        finally:
665*cfb92d14SAndroid Build Coastguard Worker            self.bash('ip6tables -F INPUT')
666*cfb92d14SAndroid Build Coastguard Worker            time.sleep(1)
667*cfb92d14SAndroid Build Coastguard Worker
668*cfb92d14SAndroid Build Coastguard Worker    def _mdns_query_impl(self, service, find_active):
669*cfb92d14SAndroid Build Coastguard Worker        # For BBR-TC-03 or DH test cases (empty arguments) just send a query
670*cfb92d14SAndroid Build Coastguard Worker        output = self.bash('python3 ~/repo/openthread/tests/scripts/thread-cert/find_border_agents.py')
671*cfb92d14SAndroid Build Coastguard Worker
672*cfb92d14SAndroid Build Coastguard Worker        if not find_active:
673*cfb92d14SAndroid Build Coastguard Worker            return
674*cfb92d14SAndroid Build Coastguard Worker
675*cfb92d14SAndroid Build Coastguard Worker        # For MATN-TC-17 and MATN-TC-18 use Zeroconf to get the BBR address and border agent port
676*cfb92d14SAndroid Build Coastguard Worker        for line in output:
677*cfb92d14SAndroid Build Coastguard Worker            print(line)
678*cfb92d14SAndroid Build Coastguard Worker            alias, addr, port, thread_status = eval(line)
679*cfb92d14SAndroid Build Coastguard Worker            if thread_status == 2 and addr:
680*cfb92d14SAndroid Build Coastguard Worker                if ipaddress.IPv6Address(addr.decode()).is_link_local:
681*cfb92d14SAndroid Build Coastguard Worker                    addr = '%s%%%s' % (addr, self.backboneNetif)
682*cfb92d14SAndroid Build Coastguard Worker                return addr, port
683*cfb92d14SAndroid Build Coastguard Worker
684*cfb92d14SAndroid Build Coastguard Worker        raise Exception('No active Border Agents found')
685*cfb92d14SAndroid Build Coastguard Worker
686*cfb92d14SAndroid Build Coastguard Worker    # Override powerDown
687*cfb92d14SAndroid Build Coastguard Worker    @API
688*cfb92d14SAndroid Build Coastguard Worker    def powerDown(self):
689*cfb92d14SAndroid Build Coastguard Worker        self.log('Powering down BBR')
690*cfb92d14SAndroid Build Coastguard Worker        super(OpenThread_BR, self).powerDown()
691*cfb92d14SAndroid Build Coastguard Worker        stop_cmd = self.extraParams.get('cmd-stop-otbr-agent', 'systemctl stop otbr-agent')
692*cfb92d14SAndroid Build Coastguard Worker        self.bash(stop_cmd)
693*cfb92d14SAndroid Build Coastguard Worker
694*cfb92d14SAndroid Build Coastguard Worker    # Override powerUp
695*cfb92d14SAndroid Build Coastguard Worker    @API
696*cfb92d14SAndroid Build Coastguard Worker    def powerUp(self):
697*cfb92d14SAndroid Build Coastguard Worker        self.log('Powering up BBR')
698*cfb92d14SAndroid Build Coastguard Worker        start_cmd = self.extraParams.get('cmd-start-otbr-agent', 'systemctl start otbr-agent')
699*cfb92d14SAndroid Build Coastguard Worker        self.bash(start_cmd)
700*cfb92d14SAndroid Build Coastguard Worker        super(OpenThread_BR, self).powerUp()
701*cfb92d14SAndroid Build Coastguard Worker
702*cfb92d14SAndroid Build Coastguard Worker    # Override forceSetSlaac
703*cfb92d14SAndroid Build Coastguard Worker    @API
704*cfb92d14SAndroid Build Coastguard Worker    def forceSetSlaac(self, slaacAddress):
705*cfb92d14SAndroid Build Coastguard Worker        self.bash('ip -6 addr add %s/64 dev wpan0' % slaacAddress)
706*cfb92d14SAndroid Build Coastguard Worker
707*cfb92d14SAndroid Build Coastguard Worker    # Override stopListeningToAddr
708*cfb92d14SAndroid Build Coastguard Worker    @API
709*cfb92d14SAndroid Build Coastguard Worker    def stopListeningToAddr(self, sAddr):
710*cfb92d14SAndroid Build Coastguard Worker        """
711*cfb92d14SAndroid Build Coastguard Worker        Unsubscribe to a given IPv6 address which was subscribed earlier with `registerMulticast`.
712*cfb92d14SAndroid Build Coastguard Worker
713*cfb92d14SAndroid Build Coastguard Worker        Args:
714*cfb92d14SAndroid Build Coastguard Worker            sAddr   : str : Multicast address to be unsubscribed. Use an empty string to unsubscribe
715*cfb92d14SAndroid Build Coastguard Worker                            all the active multicast addresses.
716*cfb92d14SAndroid Build Coastguard Worker        """
717*cfb92d14SAndroid Build Coastguard Worker        cmd = 'pkill -f mcast6.*%s' % sAddr
718*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
719*cfb92d14SAndroid Build Coastguard Worker
720*cfb92d14SAndroid Build Coastguard Worker    def stopListeningToAddrAll(self):
721*cfb92d14SAndroid Build Coastguard Worker        return self.stopListeningToAddr('')
722*cfb92d14SAndroid Build Coastguard Worker
723*cfb92d14SAndroid Build Coastguard Worker    @API
724*cfb92d14SAndroid Build Coastguard Worker    def deregisterMulticast(self, sAddr):
725*cfb92d14SAndroid Build Coastguard Worker        """
726*cfb92d14SAndroid Build Coastguard Worker        Unsubscribe to a given IPv6 address.
727*cfb92d14SAndroid Build Coastguard Worker        Only used by External Commissioner.
728*cfb92d14SAndroid Build Coastguard Worker
729*cfb92d14SAndroid Build Coastguard Worker        Args:
730*cfb92d14SAndroid Build Coastguard Worker            sAddr   : str : Multicast address to be unsubscribed.
731*cfb92d14SAndroid Build Coastguard Worker        """
732*cfb92d14SAndroid Build Coastguard Worker        self.externalCommissioner.MLR([sAddr], 0)
733*cfb92d14SAndroid Build Coastguard Worker        return True
734*cfb92d14SAndroid Build Coastguard Worker
735*cfb92d14SAndroid Build Coastguard Worker    @watched
736*cfb92d14SAndroid Build Coastguard Worker    def _waitBorderRoutingStabilize(self):
737*cfb92d14SAndroid Build Coastguard Worker        """
738*cfb92d14SAndroid Build Coastguard Worker        Wait for Network Data to stabilize if BORDER_ROUTING is enabled.
739*cfb92d14SAndroid Build Coastguard Worker        """
740*cfb92d14SAndroid Build Coastguard Worker        if not self.isBorderRoutingEnabled():
741*cfb92d14SAndroid Build Coastguard Worker            return
742*cfb92d14SAndroid Build Coastguard Worker
743*cfb92d14SAndroid Build Coastguard Worker        MAX_TIMEOUT = 30
744*cfb92d14SAndroid Build Coastguard Worker        MIN_TIMEOUT = 15
745*cfb92d14SAndroid Build Coastguard Worker        CHECK_INTERVAL = 3
746*cfb92d14SAndroid Build Coastguard Worker
747*cfb92d14SAndroid Build Coastguard Worker        time.sleep(MIN_TIMEOUT)
748*cfb92d14SAndroid Build Coastguard Worker
749*cfb92d14SAndroid Build Coastguard Worker        lastNetData = self.getNetworkData()
750*cfb92d14SAndroid Build Coastguard Worker        for i in range((MAX_TIMEOUT - MIN_TIMEOUT) // CHECK_INTERVAL):
751*cfb92d14SAndroid Build Coastguard Worker            time.sleep(CHECK_INTERVAL)
752*cfb92d14SAndroid Build Coastguard Worker            curNetData = self.getNetworkData()
753*cfb92d14SAndroid Build Coastguard Worker
754*cfb92d14SAndroid Build Coastguard Worker            # Wait until the Network Data is not changing, and there is OMR Prefix and External Routes available
755*cfb92d14SAndroid Build Coastguard Worker            if curNetData == lastNetData and len(curNetData['Prefixes']) > 0 and len(curNetData['Routes']) > 0:
756*cfb92d14SAndroid Build Coastguard Worker                break
757*cfb92d14SAndroid Build Coastguard Worker
758*cfb92d14SAndroid Build Coastguard Worker            lastNetData = curNetData
759*cfb92d14SAndroid Build Coastguard Worker
760*cfb92d14SAndroid Build Coastguard Worker        return lastNetData
761