xref: /aosp_15_r20/external/openthread/tests/scripts/thread-cert/node.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cfb92d14SAndroid Build Coastguard Worker#
3*cfb92d14SAndroid Build Coastguard Worker#  Copyright (c) 2016, 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
30*cfb92d14SAndroid Build Coastguard Workerimport json
31*cfb92d14SAndroid Build Coastguard Workerimport binascii
32*cfb92d14SAndroid Build Coastguard Workerimport ipaddress
33*cfb92d14SAndroid Build Coastguard Workerimport logging
34*cfb92d14SAndroid Build Coastguard Workerimport os
35*cfb92d14SAndroid Build Coastguard Workerimport re
36*cfb92d14SAndroid Build Coastguard Workerimport shlex
37*cfb92d14SAndroid Build Coastguard Workerimport socket
38*cfb92d14SAndroid Build Coastguard Workerimport subprocess
39*cfb92d14SAndroid Build Coastguard Workerimport sys
40*cfb92d14SAndroid Build Coastguard Workerimport time
41*cfb92d14SAndroid Build Coastguard Workerimport traceback
42*cfb92d14SAndroid Build Coastguard Workerimport typing
43*cfb92d14SAndroid Build Coastguard Workerimport unittest
44*cfb92d14SAndroid Build Coastguard Workerfrom ipaddress import IPv6Address, IPv6Network
45*cfb92d14SAndroid Build Coastguard Workerfrom typing import Union, Dict, Optional, List, Any
46*cfb92d14SAndroid Build Coastguard Worker
47*cfb92d14SAndroid Build Coastguard Workerimport pexpect
48*cfb92d14SAndroid Build Coastguard Workerimport pexpect.popen_spawn
49*cfb92d14SAndroid Build Coastguard Worker
50*cfb92d14SAndroid Build Coastguard Workerimport config
51*cfb92d14SAndroid Build Coastguard Workerimport simulator
52*cfb92d14SAndroid Build Coastguard Workerimport thread_cert
53*cfb92d14SAndroid Build Coastguard Worker
54*cfb92d14SAndroid Build Coastguard WorkerPORT_OFFSET = int(os.getenv('PORT_OFFSET', "0"))
55*cfb92d14SAndroid Build Coastguard Worker
56*cfb92d14SAndroid Build Coastguard WorkerINFRA_DNS64 = int(os.getenv('NAT64', 0))
57*cfb92d14SAndroid Build Coastguard Worker
58*cfb92d14SAndroid Build Coastguard Worker
59*cfb92d14SAndroid Build Coastguard Workerclass OtbrDocker:
60*cfb92d14SAndroid Build Coastguard Worker    RESET_DELAY = 3
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard Worker    _socat_proc = None
63*cfb92d14SAndroid Build Coastguard Worker    _ot_rcp_proc = None
64*cfb92d14SAndroid Build Coastguard Worker    _docker_proc = None
65*cfb92d14SAndroid Build Coastguard Worker    _border_routing_counters = None
66*cfb92d14SAndroid Build Coastguard Worker
67*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, nodeid: int, backbone_network: str, **kwargs):
68*cfb92d14SAndroid Build Coastguard Worker        self.verbose = int(float(os.getenv('VERBOSE', 0)))
69*cfb92d14SAndroid Build Coastguard Worker
70*cfb92d14SAndroid Build Coastguard Worker        assert backbone_network is not None
71*cfb92d14SAndroid Build Coastguard Worker        self.backbone_network = backbone_network
72*cfb92d14SAndroid Build Coastguard Worker        try:
73*cfb92d14SAndroid Build Coastguard Worker            self._docker_name = config.OTBR_DOCKER_NAME_PREFIX + str(nodeid)
74*cfb92d14SAndroid Build Coastguard Worker            self._prepare_ot_rcp_sim(nodeid)
75*cfb92d14SAndroid Build Coastguard Worker            self._launch_docker()
76*cfb92d14SAndroid Build Coastguard Worker        except Exception:
77*cfb92d14SAndroid Build Coastguard Worker            traceback.print_exc()
78*cfb92d14SAndroid Build Coastguard Worker            self.destroy()
79*cfb92d14SAndroid Build Coastguard Worker            raise
80*cfb92d14SAndroid Build Coastguard Worker
81*cfb92d14SAndroid Build Coastguard Worker    def _prepare_ot_rcp_sim(self, nodeid: int):
82*cfb92d14SAndroid Build Coastguard Worker        self._socat_proc = subprocess.Popen(['socat', '-d', '-d', 'pty,raw,echo=0', 'pty,raw,echo=0'],
83*cfb92d14SAndroid Build Coastguard Worker                                            stderr=subprocess.PIPE,
84*cfb92d14SAndroid Build Coastguard Worker                                            stdin=subprocess.DEVNULL,
85*cfb92d14SAndroid Build Coastguard Worker                                            stdout=subprocess.DEVNULL)
86*cfb92d14SAndroid Build Coastguard Worker
87*cfb92d14SAndroid Build Coastguard Worker        line = self._socat_proc.stderr.readline().decode('ascii').strip()
88*cfb92d14SAndroid Build Coastguard Worker        self._rcp_device_pty = rcp_device_pty = line[line.index('PTY is /dev') + 7:]
89*cfb92d14SAndroid Build Coastguard Worker        line = self._socat_proc.stderr.readline().decode('ascii').strip()
90*cfb92d14SAndroid Build Coastguard Worker        self._rcp_device = rcp_device = line[line.index('PTY is /dev') + 7:]
91*cfb92d14SAndroid Build Coastguard Worker        logging.info(f"socat running: device PTY: {rcp_device_pty}, device: {rcp_device}")
92*cfb92d14SAndroid Build Coastguard Worker
93*cfb92d14SAndroid Build Coastguard Worker        ot_rcp_path = self._get_ot_rcp_path()
94*cfb92d14SAndroid Build Coastguard Worker        self._ot_rcp_proc = subprocess.Popen(f"{ot_rcp_path} {nodeid} > {rcp_device_pty} < {rcp_device_pty}",
95*cfb92d14SAndroid Build Coastguard Worker                                             shell=True,
96*cfb92d14SAndroid Build Coastguard Worker                                             stdin=subprocess.DEVNULL,
97*cfb92d14SAndroid Build Coastguard Worker                                             stdout=subprocess.DEVNULL,
98*cfb92d14SAndroid Build Coastguard Worker                                             stderr=subprocess.DEVNULL)
99*cfb92d14SAndroid Build Coastguard Worker
100*cfb92d14SAndroid Build Coastguard Worker        try:
101*cfb92d14SAndroid Build Coastguard Worker            self._ot_rcp_proc.wait(1)
102*cfb92d14SAndroid Build Coastguard Worker        except subprocess.TimeoutExpired:
103*cfb92d14SAndroid Build Coastguard Worker            # We expect ot-rcp not to quit in 1 second.
104*cfb92d14SAndroid Build Coastguard Worker            pass
105*cfb92d14SAndroid Build Coastguard Worker        else:
106*cfb92d14SAndroid Build Coastguard Worker            raise Exception(f"ot-rcp {nodeid} exited unexpectedly!")
107*cfb92d14SAndroid Build Coastguard Worker
108*cfb92d14SAndroid Build Coastguard Worker    def _get_ot_rcp_path(self) -> str:
109*cfb92d14SAndroid Build Coastguard Worker        srcdir = os.environ['top_builddir']
110*cfb92d14SAndroid Build Coastguard Worker        path = '%s/examples/apps/ncp/ot-rcp' % srcdir
111*cfb92d14SAndroid Build Coastguard Worker        logging.info("ot-rcp path: %s", path)
112*cfb92d14SAndroid Build Coastguard Worker        return path
113*cfb92d14SAndroid Build Coastguard Worker
114*cfb92d14SAndroid Build Coastguard Worker    def _launch_docker(self):
115*cfb92d14SAndroid Build Coastguard Worker        logging.info(f'Docker image: {config.OTBR_DOCKER_IMAGE}')
116*cfb92d14SAndroid Build Coastguard Worker        subprocess.check_call(f"docker rm -f {self._docker_name} || true", shell=True)
117*cfb92d14SAndroid Build Coastguard Worker        CI_ENV = os.getenv('CI_ENV', '').split()
118*cfb92d14SAndroid Build Coastguard Worker        dns = ['--dns=127.0.0.1'] if INFRA_DNS64 == 1 else ['--dns=8.8.8.8']
119*cfb92d14SAndroid Build Coastguard Worker        nat64_prefix = ['--nat64-prefix', '2001:db8:1:ffff::/96'] if INFRA_DNS64 == 1 else []
120*cfb92d14SAndroid Build Coastguard Worker        os.makedirs('/tmp/coverage/', exist_ok=True)
121*cfb92d14SAndroid Build Coastguard Worker
122*cfb92d14SAndroid Build Coastguard Worker        cmd = ['docker', 'run'] + CI_ENV + [
123*cfb92d14SAndroid Build Coastguard Worker            '--rm',
124*cfb92d14SAndroid Build Coastguard Worker            '--name',
125*cfb92d14SAndroid Build Coastguard Worker            self._docker_name,
126*cfb92d14SAndroid Build Coastguard Worker            '--network',
127*cfb92d14SAndroid Build Coastguard Worker            self.backbone_network,
128*cfb92d14SAndroid Build Coastguard Worker        ] + dns + [
129*cfb92d14SAndroid Build Coastguard Worker            '-i',
130*cfb92d14SAndroid Build Coastguard Worker            '--sysctl',
131*cfb92d14SAndroid Build Coastguard Worker            'net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1',
132*cfb92d14SAndroid Build Coastguard Worker            '--privileged',
133*cfb92d14SAndroid Build Coastguard Worker            '--cap-add=NET_ADMIN',
134*cfb92d14SAndroid Build Coastguard Worker            '--volume',
135*cfb92d14SAndroid Build Coastguard Worker            f'{self._rcp_device}:/dev/ttyUSB0',
136*cfb92d14SAndroid Build Coastguard Worker            '-v',
137*cfb92d14SAndroid Build Coastguard Worker            '/tmp/coverage/:/tmp/coverage/',
138*cfb92d14SAndroid Build Coastguard Worker            config.OTBR_DOCKER_IMAGE,
139*cfb92d14SAndroid Build Coastguard Worker            '-B',
140*cfb92d14SAndroid Build Coastguard Worker            config.BACKBONE_IFNAME,
141*cfb92d14SAndroid Build Coastguard Worker            '--trel-url',
142*cfb92d14SAndroid Build Coastguard Worker            f'trel://{config.BACKBONE_IFNAME}',
143*cfb92d14SAndroid Build Coastguard Worker        ] + nat64_prefix
144*cfb92d14SAndroid Build Coastguard Worker        logging.info(' '.join(cmd))
145*cfb92d14SAndroid Build Coastguard Worker        self._docker_proc = subprocess.Popen(cmd,
146*cfb92d14SAndroid Build Coastguard Worker                                             stdin=subprocess.DEVNULL,
147*cfb92d14SAndroid Build Coastguard Worker                                             stdout=sys.stdout if self.verbose else subprocess.DEVNULL,
148*cfb92d14SAndroid Build Coastguard Worker                                             stderr=sys.stderr if self.verbose else subprocess.DEVNULL)
149*cfb92d14SAndroid Build Coastguard Worker
150*cfb92d14SAndroid Build Coastguard Worker        launch_docker_deadline = time.time() + 300
151*cfb92d14SAndroid Build Coastguard Worker        launch_ok = False
152*cfb92d14SAndroid Build Coastguard Worker
153*cfb92d14SAndroid Build Coastguard Worker        while time.time() < launch_docker_deadline:
154*cfb92d14SAndroid Build Coastguard Worker            try:
155*cfb92d14SAndroid Build Coastguard Worker                subprocess.check_call(f'docker exec -i {self._docker_name} ot-ctl state', shell=True)
156*cfb92d14SAndroid Build Coastguard Worker                launch_ok = True
157*cfb92d14SAndroid Build Coastguard Worker                logging.info("OTBR Docker %s on %s Is Ready!", self._docker_name, self.backbone_network)
158*cfb92d14SAndroid Build Coastguard Worker                break
159*cfb92d14SAndroid Build Coastguard Worker            except subprocess.CalledProcessError:
160*cfb92d14SAndroid Build Coastguard Worker                time.sleep(5)
161*cfb92d14SAndroid Build Coastguard Worker                continue
162*cfb92d14SAndroid Build Coastguard Worker
163*cfb92d14SAndroid Build Coastguard Worker        assert launch_ok
164*cfb92d14SAndroid Build Coastguard Worker
165*cfb92d14SAndroid Build Coastguard Worker        self.start_ot_ctl()
166*cfb92d14SAndroid Build Coastguard Worker
167*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
168*cfb92d14SAndroid Build Coastguard Worker        return f'OtbrDocker<{self.nodeid}>'
169*cfb92d14SAndroid Build Coastguard Worker
170*cfb92d14SAndroid Build Coastguard Worker    def start_otbr_service(self):
171*cfb92d14SAndroid Build Coastguard Worker        self.bash('service otbr-agent start')
172*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(3)
173*cfb92d14SAndroid Build Coastguard Worker        self.start_ot_ctl()
174*cfb92d14SAndroid Build Coastguard Worker
175*cfb92d14SAndroid Build Coastguard Worker    def stop_otbr_service(self):
176*cfb92d14SAndroid Build Coastguard Worker        self.stop_ot_ctl()
177*cfb92d14SAndroid Build Coastguard Worker        self.bash('service otbr-agent stop')
178*cfb92d14SAndroid Build Coastguard Worker
179*cfb92d14SAndroid Build Coastguard Worker    def stop_mdns_service(self):
180*cfb92d14SAndroid Build Coastguard Worker        self.bash('service avahi-daemon stop; service mdns stop; !(cat /proc/net/udp | grep -i :14E9)')
181*cfb92d14SAndroid Build Coastguard Worker
182*cfb92d14SAndroid Build Coastguard Worker    def start_mdns_service(self):
183*cfb92d14SAndroid Build Coastguard Worker        self.bash('service avahi-daemon start; service mdns start; cat /proc/net/udp | grep -i :14E9')
184*cfb92d14SAndroid Build Coastguard Worker
185*cfb92d14SAndroid Build Coastguard Worker    def start_ot_ctl(self):
186*cfb92d14SAndroid Build Coastguard Worker        cmd = f'docker exec -i {self._docker_name} ot-ctl'
187*cfb92d14SAndroid Build Coastguard Worker        self.pexpect = pexpect.popen_spawn.PopenSpawn(cmd, timeout=30)
188*cfb92d14SAndroid Build Coastguard Worker        if self.verbose:
189*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.logfile_read = sys.stdout.buffer
190*cfb92d14SAndroid Build Coastguard Worker
191*cfb92d14SAndroid Build Coastguard Worker        # Add delay to ensure that the process is ready to receive commands.
192*cfb92d14SAndroid Build Coastguard Worker        timeout = 0.4
193*cfb92d14SAndroid Build Coastguard Worker        while timeout > 0:
194*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.send('\r\n')
195*cfb92d14SAndroid Build Coastguard Worker            try:
196*cfb92d14SAndroid Build Coastguard Worker                self.pexpect.expect('> ', timeout=0.1)
197*cfb92d14SAndroid Build Coastguard Worker                break
198*cfb92d14SAndroid Build Coastguard Worker            except pexpect.TIMEOUT:
199*cfb92d14SAndroid Build Coastguard Worker                timeout -= 0.1
200*cfb92d14SAndroid Build Coastguard Worker
201*cfb92d14SAndroid Build Coastguard Worker    def stop_ot_ctl(self):
202*cfb92d14SAndroid Build Coastguard Worker        self.pexpect.sendeof()
203*cfb92d14SAndroid Build Coastguard Worker        self.pexpect.wait()
204*cfb92d14SAndroid Build Coastguard Worker        self.pexpect.proc.kill()
205*cfb92d14SAndroid Build Coastguard Worker
206*cfb92d14SAndroid Build Coastguard Worker    def reserve_udp_port(self, port):
207*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'socat -u UDP6-LISTEN:{port},bindtodevice=wpan0 - &')
208*cfb92d14SAndroid Build Coastguard Worker
209*cfb92d14SAndroid Build Coastguard Worker    def destroy(self):
210*cfb92d14SAndroid Build Coastguard Worker        logging.info("Destroying %s", self)
211*cfb92d14SAndroid Build Coastguard Worker        self._shutdown_docker()
212*cfb92d14SAndroid Build Coastguard Worker        self._shutdown_ot_rcp()
213*cfb92d14SAndroid Build Coastguard Worker        self._shutdown_socat()
214*cfb92d14SAndroid Build Coastguard Worker
215*cfb92d14SAndroid Build Coastguard Worker    def _shutdown_docker(self):
216*cfb92d14SAndroid Build Coastguard Worker        if self._docker_proc is None:
217*cfb92d14SAndroid Build Coastguard Worker            return
218*cfb92d14SAndroid Build Coastguard Worker
219*cfb92d14SAndroid Build Coastguard Worker        try:
220*cfb92d14SAndroid Build Coastguard Worker            COVERAGE = int(os.getenv('COVERAGE', '0'))
221*cfb92d14SAndroid Build Coastguard Worker            OTBR_COVERAGE = int(os.getenv('OTBR_COVERAGE', '0'))
222*cfb92d14SAndroid Build Coastguard Worker            test_name = os.getenv('TEST_NAME')
223*cfb92d14SAndroid Build Coastguard Worker            unique_node_id = f'{test_name}-{PORT_OFFSET}-{self.nodeid}'
224*cfb92d14SAndroid Build Coastguard Worker
225*cfb92d14SAndroid Build Coastguard Worker            if COVERAGE or OTBR_COVERAGE:
226*cfb92d14SAndroid Build Coastguard Worker                self.bash('service otbr-agent stop')
227*cfb92d14SAndroid Build Coastguard Worker
228*cfb92d14SAndroid Build Coastguard Worker                cov_file_path = f'/tmp/coverage/coverage-{unique_node_id}.info'
229*cfb92d14SAndroid Build Coastguard Worker                # Upload OTBR code coverage if OTBR_COVERAGE=1, otherwise OpenThread code coverage.
230*cfb92d14SAndroid Build Coastguard Worker                if OTBR_COVERAGE:
231*cfb92d14SAndroid Build Coastguard Worker                    codecov_cmd = f'lcov --directory . --capture --output-file {cov_file_path}'
232*cfb92d14SAndroid Build Coastguard Worker                else:
233*cfb92d14SAndroid Build Coastguard Worker                    codecov_cmd = ('lcov --directory build/otbr/third_party/openthread/repo --capture '
234*cfb92d14SAndroid Build Coastguard Worker                                   f'--output-file {cov_file_path}')
235*cfb92d14SAndroid Build Coastguard Worker
236*cfb92d14SAndroid Build Coastguard Worker                self.bash(codecov_cmd)
237*cfb92d14SAndroid Build Coastguard Worker
238*cfb92d14SAndroid Build Coastguard Worker            copyCore = subprocess.run(f'docker cp {self._docker_name}:/core ./coredump_{unique_node_id}', shell=True)
239*cfb92d14SAndroid Build Coastguard Worker            if copyCore.returncode == 0:
240*cfb92d14SAndroid Build Coastguard Worker                subprocess.check_call(
241*cfb92d14SAndroid Build Coastguard Worker                    f'docker cp {self._docker_name}:/usr/sbin/otbr-agent ./otbr-agent_{unique_node_id}', shell=True)
242*cfb92d14SAndroid Build Coastguard Worker
243*cfb92d14SAndroid Build Coastguard Worker        finally:
244*cfb92d14SAndroid Build Coastguard Worker            subprocess.check_call(f"docker rm -f {self._docker_name}", shell=True)
245*cfb92d14SAndroid Build Coastguard Worker            self._docker_proc.wait()
246*cfb92d14SAndroid Build Coastguard Worker            del self._docker_proc
247*cfb92d14SAndroid Build Coastguard Worker
248*cfb92d14SAndroid Build Coastguard Worker    def _shutdown_ot_rcp(self):
249*cfb92d14SAndroid Build Coastguard Worker        if self._ot_rcp_proc is not None:
250*cfb92d14SAndroid Build Coastguard Worker            self._ot_rcp_proc.kill()
251*cfb92d14SAndroid Build Coastguard Worker            self._ot_rcp_proc.wait()
252*cfb92d14SAndroid Build Coastguard Worker            del self._ot_rcp_proc
253*cfb92d14SAndroid Build Coastguard Worker
254*cfb92d14SAndroid Build Coastguard Worker    def _shutdown_socat(self):
255*cfb92d14SAndroid Build Coastguard Worker        if self._socat_proc is not None:
256*cfb92d14SAndroid Build Coastguard Worker            self._socat_proc.stderr.close()
257*cfb92d14SAndroid Build Coastguard Worker            self._socat_proc.kill()
258*cfb92d14SAndroid Build Coastguard Worker            self._socat_proc.wait()
259*cfb92d14SAndroid Build Coastguard Worker            del self._socat_proc
260*cfb92d14SAndroid Build Coastguard Worker
261*cfb92d14SAndroid Build Coastguard Worker    def bash(self, cmd: str, encoding='ascii') -> List[str]:
262*cfb92d14SAndroid Build Coastguard Worker        logging.info("%s $ %s", self, cmd)
263*cfb92d14SAndroid Build Coastguard Worker        proc = subprocess.Popen(['docker', 'exec', '-i', self._docker_name, 'bash', '-c', cmd],
264*cfb92d14SAndroid Build Coastguard Worker                                stdin=subprocess.DEVNULL,
265*cfb92d14SAndroid Build Coastguard Worker                                stdout=subprocess.PIPE,
266*cfb92d14SAndroid Build Coastguard Worker                                stderr=sys.stderr,
267*cfb92d14SAndroid Build Coastguard Worker                                encoding=encoding)
268*cfb92d14SAndroid Build Coastguard Worker
269*cfb92d14SAndroid Build Coastguard Worker        with proc:
270*cfb92d14SAndroid Build Coastguard Worker
271*cfb92d14SAndroid Build Coastguard Worker            lines = []
272*cfb92d14SAndroid Build Coastguard Worker
273*cfb92d14SAndroid Build Coastguard Worker            while True:
274*cfb92d14SAndroid Build Coastguard Worker                line = proc.stdout.readline()
275*cfb92d14SAndroid Build Coastguard Worker
276*cfb92d14SAndroid Build Coastguard Worker                if not line:
277*cfb92d14SAndroid Build Coastguard Worker                    break
278*cfb92d14SAndroid Build Coastguard Worker
279*cfb92d14SAndroid Build Coastguard Worker                lines.append(line)
280*cfb92d14SAndroid Build Coastguard Worker                logging.info("%s $ %r", self, line.rstrip('\r\n'))
281*cfb92d14SAndroid Build Coastguard Worker
282*cfb92d14SAndroid Build Coastguard Worker            proc.wait()
283*cfb92d14SAndroid Build Coastguard Worker
284*cfb92d14SAndroid Build Coastguard Worker            if proc.returncode != 0:
285*cfb92d14SAndroid Build Coastguard Worker                raise subprocess.CalledProcessError(proc.returncode, cmd, ''.join(lines))
286*cfb92d14SAndroid Build Coastguard Worker            else:
287*cfb92d14SAndroid Build Coastguard Worker                return lines
288*cfb92d14SAndroid Build Coastguard Worker
289*cfb92d14SAndroid Build Coastguard Worker    def dns_dig(self, server: str, name: str, qtype: str):
290*cfb92d14SAndroid Build Coastguard Worker        """
291*cfb92d14SAndroid Build Coastguard Worker        Run dig command to query a DNS server.
292*cfb92d14SAndroid Build Coastguard Worker
293*cfb92d14SAndroid Build Coastguard Worker        Args:
294*cfb92d14SAndroid Build Coastguard Worker            server: the server address.
295*cfb92d14SAndroid Build Coastguard Worker            name: the name to query.
296*cfb92d14SAndroid Build Coastguard Worker            qtype: the query type (e.g. AAAA, PTR, TXT, SRV).
297*cfb92d14SAndroid Build Coastguard Worker
298*cfb92d14SAndroid Build Coastguard Worker        Returns:
299*cfb92d14SAndroid Build Coastguard Worker            The dig result similar as below:
300*cfb92d14SAndroid Build Coastguard Worker            {
301*cfb92d14SAndroid Build Coastguard Worker                "opcode": "QUERY",
302*cfb92d14SAndroid Build Coastguard Worker                "status": "NOERROR",
303*cfb92d14SAndroid Build Coastguard Worker                "id": "64144",
304*cfb92d14SAndroid Build Coastguard Worker                "QUESTION": [
305*cfb92d14SAndroid Build Coastguard Worker                    ('google.com.', 'IN', 'AAAA')
306*cfb92d14SAndroid Build Coastguard Worker                ],
307*cfb92d14SAndroid Build Coastguard Worker                "ANSWER": [
308*cfb92d14SAndroid Build Coastguard Worker                    ('google.com.', 107,	'IN', 'AAAA', '2404:6800:4008:c00::71'),
309*cfb92d14SAndroid Build Coastguard Worker                    ('google.com.', 107,	'IN', 'AAAA', '2404:6800:4008:c00::8a'),
310*cfb92d14SAndroid Build Coastguard Worker                    ('google.com.', 107,	'IN', 'AAAA', '2404:6800:4008:c00::66'),
311*cfb92d14SAndroid Build Coastguard Worker                    ('google.com.', 107,	'IN', 'AAAA', '2404:6800:4008:c00::8b'),
312*cfb92d14SAndroid Build Coastguard Worker                ],
313*cfb92d14SAndroid Build Coastguard Worker                "ADDITIONAL": [
314*cfb92d14SAndroid Build Coastguard Worker                ],
315*cfb92d14SAndroid Build Coastguard Worker            }
316*cfb92d14SAndroid Build Coastguard Worker        """
317*cfb92d14SAndroid Build Coastguard Worker        output = self.bash(f'dig -6 @{server} \'{name}\' {qtype}', encoding='raw_unicode_escape')
318*cfb92d14SAndroid Build Coastguard Worker
319*cfb92d14SAndroid Build Coastguard Worker        section = None
320*cfb92d14SAndroid Build Coastguard Worker        dig_result = {
321*cfb92d14SAndroid Build Coastguard Worker            'QUESTION': [],
322*cfb92d14SAndroid Build Coastguard Worker            'ANSWER': [],
323*cfb92d14SAndroid Build Coastguard Worker            'ADDITIONAL': [],
324*cfb92d14SAndroid Build Coastguard Worker        }
325*cfb92d14SAndroid Build Coastguard Worker
326*cfb92d14SAndroid Build Coastguard Worker        for line in output:
327*cfb92d14SAndroid Build Coastguard Worker            line = line.strip()
328*cfb92d14SAndroid Build Coastguard Worker
329*cfb92d14SAndroid Build Coastguard Worker            if line.startswith(';; ->>HEADER<<- '):
330*cfb92d14SAndroid Build Coastguard Worker                headers = line[len(';; ->>HEADER<<- '):].split(', ')
331*cfb92d14SAndroid Build Coastguard Worker                for header in headers:
332*cfb92d14SAndroid Build Coastguard Worker                    key, val = header.split(': ')
333*cfb92d14SAndroid Build Coastguard Worker                    dig_result[key] = val
334*cfb92d14SAndroid Build Coastguard Worker
335*cfb92d14SAndroid Build Coastguard Worker                continue
336*cfb92d14SAndroid Build Coastguard Worker
337*cfb92d14SAndroid Build Coastguard Worker            if line == ';; QUESTION SECTION:':
338*cfb92d14SAndroid Build Coastguard Worker                section = 'QUESTION'
339*cfb92d14SAndroid Build Coastguard Worker                continue
340*cfb92d14SAndroid Build Coastguard Worker            elif line == ';; ANSWER SECTION:':
341*cfb92d14SAndroid Build Coastguard Worker                section = 'ANSWER'
342*cfb92d14SAndroid Build Coastguard Worker                continue
343*cfb92d14SAndroid Build Coastguard Worker            elif line == ';; ADDITIONAL SECTION:':
344*cfb92d14SAndroid Build Coastguard Worker                section = 'ADDITIONAL'
345*cfb92d14SAndroid Build Coastguard Worker                continue
346*cfb92d14SAndroid Build Coastguard Worker            elif section and not line:
347*cfb92d14SAndroid Build Coastguard Worker                section = None
348*cfb92d14SAndroid Build Coastguard Worker                continue
349*cfb92d14SAndroid Build Coastguard Worker
350*cfb92d14SAndroid Build Coastguard Worker            if section:
351*cfb92d14SAndroid Build Coastguard Worker                assert line
352*cfb92d14SAndroid Build Coastguard Worker
353*cfb92d14SAndroid Build Coastguard Worker                if section == 'QUESTION':
354*cfb92d14SAndroid Build Coastguard Worker                    assert line.startswith(';')
355*cfb92d14SAndroid Build Coastguard Worker                    line = line[1:]
356*cfb92d14SAndroid Build Coastguard Worker                record = list(line.split())
357*cfb92d14SAndroid Build Coastguard Worker
358*cfb92d14SAndroid Build Coastguard Worker                if section == 'QUESTION':
359*cfb92d14SAndroid Build Coastguard Worker                    if record[2] in ('SRV', 'TXT'):
360*cfb92d14SAndroid Build Coastguard Worker                        record[0] = self.__unescape_dns_instance_name(record[0])
361*cfb92d14SAndroid Build Coastguard Worker                else:
362*cfb92d14SAndroid Build Coastguard Worker                    record[1] = int(record[1])
363*cfb92d14SAndroid Build Coastguard Worker                    if record[3] == 'SRV':
364*cfb92d14SAndroid Build Coastguard Worker                        record[0] = self.__unescape_dns_instance_name(record[0])
365*cfb92d14SAndroid Build Coastguard Worker                        record[4], record[5], record[6] = map(int, [record[4], record[5], record[6]])
366*cfb92d14SAndroid Build Coastguard Worker                    elif record[3] == 'TXT':
367*cfb92d14SAndroid Build Coastguard Worker                        record[0] = self.__unescape_dns_instance_name(record[0])
368*cfb92d14SAndroid Build Coastguard Worker                        record[4:] = [self.__parse_dns_dig_txt(line)]
369*cfb92d14SAndroid Build Coastguard Worker                    elif record[3] == 'PTR':
370*cfb92d14SAndroid Build Coastguard Worker                        record[4] = self.__unescape_dns_instance_name(record[4])
371*cfb92d14SAndroid Build Coastguard Worker
372*cfb92d14SAndroid Build Coastguard Worker                dig_result[section].append(tuple(record))
373*cfb92d14SAndroid Build Coastguard Worker
374*cfb92d14SAndroid Build Coastguard Worker        return dig_result
375*cfb92d14SAndroid Build Coastguard Worker
376*cfb92d14SAndroid Build Coastguard Worker    def call_dbus_method(self, *args):
377*cfb92d14SAndroid Build Coastguard Worker        args = shlex.join([args[0], args[1], json.dumps(args[2:])])
378*cfb92d14SAndroid Build Coastguard Worker        return json.loads(
379*cfb92d14SAndroid Build Coastguard Worker            self.bash(f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/call_dbus_method.py {args}')
380*cfb92d14SAndroid Build Coastguard Worker            [0])
381*cfb92d14SAndroid Build Coastguard Worker
382*cfb92d14SAndroid Build Coastguard Worker    def get_dbus_property(self, property_name):
383*cfb92d14SAndroid Build Coastguard Worker        return self.call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter',
384*cfb92d14SAndroid Build Coastguard Worker                                     property_name)
385*cfb92d14SAndroid Build Coastguard Worker
386*cfb92d14SAndroid Build Coastguard Worker    def set_dbus_property(self, property_name, property_value):
387*cfb92d14SAndroid Build Coastguard Worker        return self.call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter',
388*cfb92d14SAndroid Build Coastguard Worker                                     property_name, property_value)
389*cfb92d14SAndroid Build Coastguard Worker
390*cfb92d14SAndroid Build Coastguard Worker    def get_border_routing_counters(self):
391*cfb92d14SAndroid Build Coastguard Worker        counters = self.get_dbus_property('BorderRoutingCounters')
392*cfb92d14SAndroid Build Coastguard Worker        counters = {
393*cfb92d14SAndroid Build Coastguard Worker            'inbound_unicast': counters[0],
394*cfb92d14SAndroid Build Coastguard Worker            'inbound_multicast': counters[1],
395*cfb92d14SAndroid Build Coastguard Worker            'outbound_unicast': counters[2],
396*cfb92d14SAndroid Build Coastguard Worker            'outbound_multicast': counters[3],
397*cfb92d14SAndroid Build Coastguard Worker            'ra_rx': counters[4],
398*cfb92d14SAndroid Build Coastguard Worker            'ra_tx_success': counters[5],
399*cfb92d14SAndroid Build Coastguard Worker            'ra_tx_failure': counters[6],
400*cfb92d14SAndroid Build Coastguard Worker            'rs_rx': counters[7],
401*cfb92d14SAndroid Build Coastguard Worker            'rs_tx_success': counters[8],
402*cfb92d14SAndroid Build Coastguard Worker            'rs_tx_failure': counters[9],
403*cfb92d14SAndroid Build Coastguard Worker        }
404*cfb92d14SAndroid Build Coastguard Worker        logging.info(f'border routing counters: {counters}')
405*cfb92d14SAndroid Build Coastguard Worker        return counters
406*cfb92d14SAndroid Build Coastguard Worker
407*cfb92d14SAndroid Build Coastguard Worker    def _process_traffic_counters(self, counter):
408*cfb92d14SAndroid Build Coastguard Worker        return {
409*cfb92d14SAndroid Build Coastguard Worker            '4to6': {
410*cfb92d14SAndroid Build Coastguard Worker                'packets': counter[0],
411*cfb92d14SAndroid Build Coastguard Worker                'bytes': counter[1],
412*cfb92d14SAndroid Build Coastguard Worker            },
413*cfb92d14SAndroid Build Coastguard Worker            '6to4': {
414*cfb92d14SAndroid Build Coastguard Worker                'packets': counter[2],
415*cfb92d14SAndroid Build Coastguard Worker                'bytes': counter[3],
416*cfb92d14SAndroid Build Coastguard Worker            }
417*cfb92d14SAndroid Build Coastguard Worker        }
418*cfb92d14SAndroid Build Coastguard Worker
419*cfb92d14SAndroid Build Coastguard Worker    def _process_packet_counters(self, counter):
420*cfb92d14SAndroid Build Coastguard Worker        return {'4to6': {'packets': counter[0]}, '6to4': {'packets': counter[1]}}
421*cfb92d14SAndroid Build Coastguard Worker
422*cfb92d14SAndroid Build Coastguard Worker    def nat64_set_enabled(self, enable):
423*cfb92d14SAndroid Build Coastguard Worker        return self.call_dbus_method('io.openthread.BorderRouter', 'SetNat64Enabled', enable)
424*cfb92d14SAndroid Build Coastguard Worker
425*cfb92d14SAndroid Build Coastguard Worker    def activate_ephemeral_key_mode(self, lifetime):
426*cfb92d14SAndroid Build Coastguard Worker        return self.call_dbus_method('io.openthread.BorderRouter', 'ActivateEphemeralKeyMode', lifetime)
427*cfb92d14SAndroid Build Coastguard Worker
428*cfb92d14SAndroid Build Coastguard Worker    def deactivate_ephemeral_key_mode(self):
429*cfb92d14SAndroid Build Coastguard Worker        return self.call_dbus_method('io.openthread.BorderRouter', 'DeactivateEphemeralKeyMode')
430*cfb92d14SAndroid Build Coastguard Worker
431*cfb92d14SAndroid Build Coastguard Worker    @property
432*cfb92d14SAndroid Build Coastguard Worker    def nat64_cidr(self):
433*cfb92d14SAndroid Build Coastguard Worker        self.send_command('nat64 cidr')
434*cfb92d14SAndroid Build Coastguard Worker        cidr = self._expect_command_output()[0].strip()
435*cfb92d14SAndroid Build Coastguard Worker        return ipaddress.IPv4Network(cidr, strict=False)
436*cfb92d14SAndroid Build Coastguard Worker
437*cfb92d14SAndroid Build Coastguard Worker    @nat64_cidr.setter
438*cfb92d14SAndroid Build Coastguard Worker    def nat64_cidr(self, cidr: ipaddress.IPv4Network):
439*cfb92d14SAndroid Build Coastguard Worker        if not isinstance(cidr, ipaddress.IPv4Network):
440*cfb92d14SAndroid Build Coastguard Worker            raise ValueError("cidr is expected to be an instance of ipaddress.IPv4Network")
441*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'nat64 cidr {cidr}')
442*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
443*cfb92d14SAndroid Build Coastguard Worker
444*cfb92d14SAndroid Build Coastguard Worker    @property
445*cfb92d14SAndroid Build Coastguard Worker    def nat64_state(self):
446*cfb92d14SAndroid Build Coastguard Worker        state = self.get_dbus_property('Nat64State')
447*cfb92d14SAndroid Build Coastguard Worker        return {'PrefixManager': state[0], 'Translator': state[1]}
448*cfb92d14SAndroid Build Coastguard Worker
449*cfb92d14SAndroid Build Coastguard Worker    @property
450*cfb92d14SAndroid Build Coastguard Worker    def nat64_mappings(self):
451*cfb92d14SAndroid Build Coastguard Worker        return [{
452*cfb92d14SAndroid Build Coastguard Worker            'id': row[0],
453*cfb92d14SAndroid Build Coastguard Worker            'ip4': row[1],
454*cfb92d14SAndroid Build Coastguard Worker            'ip6': row[2],
455*cfb92d14SAndroid Build Coastguard Worker            'expiry': row[3],
456*cfb92d14SAndroid Build Coastguard Worker            'counters': {
457*cfb92d14SAndroid Build Coastguard Worker                'total': self._process_traffic_counters(row[4][0]),
458*cfb92d14SAndroid Build Coastguard Worker                'ICMP': self._process_traffic_counters(row[4][1]),
459*cfb92d14SAndroid Build Coastguard Worker                'UDP': self._process_traffic_counters(row[4][2]),
460*cfb92d14SAndroid Build Coastguard Worker                'TCP': self._process_traffic_counters(row[4][3]),
461*cfb92d14SAndroid Build Coastguard Worker            }
462*cfb92d14SAndroid Build Coastguard Worker        } for row in self.get_dbus_property('Nat64Mappings')]
463*cfb92d14SAndroid Build Coastguard Worker
464*cfb92d14SAndroid Build Coastguard Worker    @property
465*cfb92d14SAndroid Build Coastguard Worker    def nat64_counters(self):
466*cfb92d14SAndroid Build Coastguard Worker        res_error = self.get_dbus_property('Nat64ErrorCounters')
467*cfb92d14SAndroid Build Coastguard Worker        res_proto = self.get_dbus_property('Nat64ProtocolCounters')
468*cfb92d14SAndroid Build Coastguard Worker        return {
469*cfb92d14SAndroid Build Coastguard Worker            'protocol': {
470*cfb92d14SAndroid Build Coastguard Worker                'Total': self._process_traffic_counters(res_proto[0]),
471*cfb92d14SAndroid Build Coastguard Worker                'ICMP': self._process_traffic_counters(res_proto[1]),
472*cfb92d14SAndroid Build Coastguard Worker                'UDP': self._process_traffic_counters(res_proto[2]),
473*cfb92d14SAndroid Build Coastguard Worker                'TCP': self._process_traffic_counters(res_proto[3]),
474*cfb92d14SAndroid Build Coastguard Worker            },
475*cfb92d14SAndroid Build Coastguard Worker            'errors': {
476*cfb92d14SAndroid Build Coastguard Worker                'Unknown': self._process_packet_counters(res_error[0]),
477*cfb92d14SAndroid Build Coastguard Worker                'Illegal Pkt': self._process_packet_counters(res_error[1]),
478*cfb92d14SAndroid Build Coastguard Worker                'Unsup Proto': self._process_packet_counters(res_error[2]),
479*cfb92d14SAndroid Build Coastguard Worker                'No Mapping': self._process_packet_counters(res_error[3]),
480*cfb92d14SAndroid Build Coastguard Worker            }
481*cfb92d14SAndroid Build Coastguard Worker        }
482*cfb92d14SAndroid Build Coastguard Worker
483*cfb92d14SAndroid Build Coastguard Worker    @property
484*cfb92d14SAndroid Build Coastguard Worker    def nat64_traffic_counters(self):
485*cfb92d14SAndroid Build Coastguard Worker        res = self.get_dbus_property('Nat64TrafficCounters')
486*cfb92d14SAndroid Build Coastguard Worker        return {
487*cfb92d14SAndroid Build Coastguard Worker            'Total': self._process_traffic_counters(res[0]),
488*cfb92d14SAndroid Build Coastguard Worker            'ICMP': self._process_traffic_counters(res[1]),
489*cfb92d14SAndroid Build Coastguard Worker            'UDP': self._process_traffic_counters(res[2]),
490*cfb92d14SAndroid Build Coastguard Worker            'TCP': self._process_traffic_counters(res[3]),
491*cfb92d14SAndroid Build Coastguard Worker        }
492*cfb92d14SAndroid Build Coastguard Worker
493*cfb92d14SAndroid Build Coastguard Worker    @property
494*cfb92d14SAndroid Build Coastguard Worker    def dns_upstream_query_state(self):
495*cfb92d14SAndroid Build Coastguard Worker        return bool(self.get_dbus_property('DnsUpstreamQueryState'))
496*cfb92d14SAndroid Build Coastguard Worker
497*cfb92d14SAndroid Build Coastguard Worker    @dns_upstream_query_state.setter
498*cfb92d14SAndroid Build Coastguard Worker    def dns_upstream_query_state(self, value):
499*cfb92d14SAndroid Build Coastguard Worker        if type(value) is not bool:
500*cfb92d14SAndroid Build Coastguard Worker            raise ValueError("dns_upstream_query_state must be a bool")
501*cfb92d14SAndroid Build Coastguard Worker        return self.set_dbus_property('DnsUpstreamQueryState', value)
502*cfb92d14SAndroid Build Coastguard Worker
503*cfb92d14SAndroid Build Coastguard Worker    @property
504*cfb92d14SAndroid Build Coastguard Worker    def ephemeral_key_enabled(self):
505*cfb92d14SAndroid Build Coastguard Worker        return bool(self.get_dbus_property('EphemeralKeyEnabled'))
506*cfb92d14SAndroid Build Coastguard Worker
507*cfb92d14SAndroid Build Coastguard Worker    @ephemeral_key_enabled.setter
508*cfb92d14SAndroid Build Coastguard Worker    def ephemeral_key_enabled(self, value):
509*cfb92d14SAndroid Build Coastguard Worker        if type(value) is not bool:
510*cfb92d14SAndroid Build Coastguard Worker            raise ValueError("ephemeral_key_enabled must be a bool")
511*cfb92d14SAndroid Build Coastguard Worker        return self.set_dbus_property('EphemeralKeyEnabled', value)
512*cfb92d14SAndroid Build Coastguard Worker
513*cfb92d14SAndroid Build Coastguard Worker    def read_border_routing_counters_delta(self):
514*cfb92d14SAndroid Build Coastguard Worker        old_counters = self._border_routing_counters
515*cfb92d14SAndroid Build Coastguard Worker        new_counters = self.get_border_routing_counters()
516*cfb92d14SAndroid Build Coastguard Worker        self._border_routing_counters = new_counters
517*cfb92d14SAndroid Build Coastguard Worker        delta_counters = {}
518*cfb92d14SAndroid Build Coastguard Worker        if old_counters is None:
519*cfb92d14SAndroid Build Coastguard Worker            delta_counters = new_counters
520*cfb92d14SAndroid Build Coastguard Worker        else:
521*cfb92d14SAndroid Build Coastguard Worker            for i in ('inbound', 'outbound'):
522*cfb92d14SAndroid Build Coastguard Worker                for j in ('unicast', 'multicast'):
523*cfb92d14SAndroid Build Coastguard Worker                    key = f'{i}_{j}'
524*cfb92d14SAndroid Build Coastguard Worker                    assert (key in old_counters)
525*cfb92d14SAndroid Build Coastguard Worker                    assert (key in new_counters)
526*cfb92d14SAndroid Build Coastguard Worker                    value = [new_counters[key][0] - old_counters[key][0], new_counters[key][1] - old_counters[key][1]]
527*cfb92d14SAndroid Build Coastguard Worker                    delta_counters[key] = value
528*cfb92d14SAndroid Build Coastguard Worker        delta_counters = {
529*cfb92d14SAndroid Build Coastguard Worker            key: value for key, value in delta_counters.items() if not isinstance(value, int) and value[0] and value[1]
530*cfb92d14SAndroid Build Coastguard Worker        }
531*cfb92d14SAndroid Build Coastguard Worker
532*cfb92d14SAndroid Build Coastguard Worker        return delta_counters
533*cfb92d14SAndroid Build Coastguard Worker
534*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
535*cfb92d14SAndroid Build Coastguard Worker    def __unescape_dns_instance_name(name: str) -> str:
536*cfb92d14SAndroid Build Coastguard Worker        new_name = []
537*cfb92d14SAndroid Build Coastguard Worker        i = 0
538*cfb92d14SAndroid Build Coastguard Worker        while i < len(name):
539*cfb92d14SAndroid Build Coastguard Worker            c = name[i]
540*cfb92d14SAndroid Build Coastguard Worker
541*cfb92d14SAndroid Build Coastguard Worker            if c == '\\':
542*cfb92d14SAndroid Build Coastguard Worker                assert i + 1 < len(name), name
543*cfb92d14SAndroid Build Coastguard Worker                if name[i + 1].isdigit():
544*cfb92d14SAndroid Build Coastguard Worker                    assert i + 3 < len(name) and name[i + 2].isdigit() and name[i + 3].isdigit(), name
545*cfb92d14SAndroid Build Coastguard Worker                    new_name.append(chr(int(name[i + 1:i + 4])))
546*cfb92d14SAndroid Build Coastguard Worker                    i += 3
547*cfb92d14SAndroid Build Coastguard Worker                else:
548*cfb92d14SAndroid Build Coastguard Worker                    new_name.append(name[i + 1])
549*cfb92d14SAndroid Build Coastguard Worker                    i += 1
550*cfb92d14SAndroid Build Coastguard Worker            else:
551*cfb92d14SAndroid Build Coastguard Worker                new_name.append(c)
552*cfb92d14SAndroid Build Coastguard Worker
553*cfb92d14SAndroid Build Coastguard Worker            i += 1
554*cfb92d14SAndroid Build Coastguard Worker
555*cfb92d14SAndroid Build Coastguard Worker        return ''.join(new_name)
556*cfb92d14SAndroid Build Coastguard Worker
557*cfb92d14SAndroid Build Coastguard Worker    def __parse_dns_dig_txt(self, line: str):
558*cfb92d14SAndroid Build Coastguard Worker        # Example TXT entry:
559*cfb92d14SAndroid Build Coastguard Worker        # "xp=\\000\\013\\184\\000\\000\\000\\000\\000"
560*cfb92d14SAndroid Build Coastguard Worker        txt = {}
561*cfb92d14SAndroid Build Coastguard Worker        for entry in re.findall(r'"((?:[^\\]|\\.)*?)"', line):
562*cfb92d14SAndroid Build Coastguard Worker            if entry == "":
563*cfb92d14SAndroid Build Coastguard Worker                continue
564*cfb92d14SAndroid Build Coastguard Worker
565*cfb92d14SAndroid Build Coastguard Worker            k, v = entry.split('=', 1)
566*cfb92d14SAndroid Build Coastguard Worker            txt[k] = v
567*cfb92d14SAndroid Build Coastguard Worker
568*cfb92d14SAndroid Build Coastguard Worker        return txt
569*cfb92d14SAndroid Build Coastguard Worker
570*cfb92d14SAndroid Build Coastguard Worker    def _setup_sysctl(self):
571*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra=2')
572*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra_rt_info_max_plen=64')
573*cfb92d14SAndroid Build Coastguard Worker
574*cfb92d14SAndroid Build Coastguard Worker
575*cfb92d14SAndroid Build Coastguard Workerclass OtCli:
576*cfb92d14SAndroid Build Coastguard Worker    RESET_DELAY = 0.1
577*cfb92d14SAndroid Build Coastguard Worker
578*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, nodeid, is_mtd=False, version=None, is_bbr=False, **kwargs):
579*cfb92d14SAndroid Build Coastguard Worker        self.verbose = int(float(os.getenv('VERBOSE', 0)))
580*cfb92d14SAndroid Build Coastguard Worker        self.node_type = os.getenv('NODE_TYPE', 'sim')
581*cfb92d14SAndroid Build Coastguard Worker        self.env_version = os.getenv('THREAD_VERSION', '1.1')
582*cfb92d14SAndroid Build Coastguard Worker        self.is_bbr = is_bbr
583*cfb92d14SAndroid Build Coastguard Worker        self._initialized = False
584*cfb92d14SAndroid Build Coastguard Worker        if os.getenv('COVERAGE', 0) and os.getenv('CC', 'gcc') == 'gcc':
585*cfb92d14SAndroid Build Coastguard Worker            self._cmd_prefix = '/usr/bin/env GCOV_PREFIX=%s/ot-run/%s/ot-gcda.%d ' % (os.getenv(
586*cfb92d14SAndroid Build Coastguard Worker                'top_srcdir', '.'), sys.argv[0], nodeid)
587*cfb92d14SAndroid Build Coastguard Worker        else:
588*cfb92d14SAndroid Build Coastguard Worker            self._cmd_prefix = ''
589*cfb92d14SAndroid Build Coastguard Worker
590*cfb92d14SAndroid Build Coastguard Worker        if version is not None:
591*cfb92d14SAndroid Build Coastguard Worker            self.version = version
592*cfb92d14SAndroid Build Coastguard Worker        else:
593*cfb92d14SAndroid Build Coastguard Worker            self.version = self.env_version
594*cfb92d14SAndroid Build Coastguard Worker
595*cfb92d14SAndroid Build Coastguard Worker        mode = os.environ.get('USE_MTD') == '1' and is_mtd and 'mtd' or 'ftd'
596*cfb92d14SAndroid Build Coastguard Worker
597*cfb92d14SAndroid Build Coastguard Worker        if self.node_type == 'soc':
598*cfb92d14SAndroid Build Coastguard Worker            self.__init_soc(nodeid)
599*cfb92d14SAndroid Build Coastguard Worker        elif self.node_type == 'ncp-sim':
600*cfb92d14SAndroid Build Coastguard Worker            # TODO use mode after ncp-mtd is available.
601*cfb92d14SAndroid Build Coastguard Worker            self.__init_ncp_sim(nodeid, 'ftd')
602*cfb92d14SAndroid Build Coastguard Worker        else:
603*cfb92d14SAndroid Build Coastguard Worker            self.__init_sim(nodeid, mode)
604*cfb92d14SAndroid Build Coastguard Worker
605*cfb92d14SAndroid Build Coastguard Worker        if self.verbose:
606*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.logfile_read = sys.stdout.buffer
607*cfb92d14SAndroid Build Coastguard Worker
608*cfb92d14SAndroid Build Coastguard Worker        self._initialized = True
609*cfb92d14SAndroid Build Coastguard Worker
610*cfb92d14SAndroid Build Coastguard Worker    def __init_sim(self, nodeid, mode):
611*cfb92d14SAndroid Build Coastguard Worker        """ Initialize a simulation node. """
612*cfb92d14SAndroid Build Coastguard Worker
613*cfb92d14SAndroid Build Coastguard Worker        # Default command if no match below, will be overridden if below conditions are met.
614*cfb92d14SAndroid Build Coastguard Worker        cmd = './ot-cli-%s' % (mode)
615*cfb92d14SAndroid Build Coastguard Worker
616*cfb92d14SAndroid Build Coastguard Worker        # For Thread 1.2 MTD node, use ot-cli-mtd build regardless of OT_CLI_PATH
617*cfb92d14SAndroid Build Coastguard Worker        if self.version != '1.1' and mode == 'mtd' and 'top_builddir' in os.environ:
618*cfb92d14SAndroid Build Coastguard Worker            srcdir = os.environ['top_builddir']
619*cfb92d14SAndroid Build Coastguard Worker            cmd = '%s/examples/apps/cli/ot-cli-%s %d' % (srcdir, mode, nodeid)
620*cfb92d14SAndroid Build Coastguard Worker
621*cfb92d14SAndroid Build Coastguard Worker        # If Thread version of node matches the testing environment version.
622*cfb92d14SAndroid Build Coastguard Worker        elif self.version == self.env_version:
623*cfb92d14SAndroid Build Coastguard Worker            # Load Thread 1.2 BBR device when testing Thread 1.2 scenarios
624*cfb92d14SAndroid Build Coastguard Worker            # which requires device with Backbone functionality.
625*cfb92d14SAndroid Build Coastguard Worker            if self.version != '1.1' and self.is_bbr:
626*cfb92d14SAndroid Build Coastguard Worker                if 'OT_CLI_PATH_BBR' in os.environ:
627*cfb92d14SAndroid Build Coastguard Worker                    cmd = os.environ['OT_CLI_PATH_BBR']
628*cfb92d14SAndroid Build Coastguard Worker                elif 'top_builddir_1_3_bbr' in os.environ:
629*cfb92d14SAndroid Build Coastguard Worker                    srcdir = os.environ['top_builddir_1_3_bbr']
630*cfb92d14SAndroid Build Coastguard Worker                    cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode)
631*cfb92d14SAndroid Build Coastguard Worker
632*cfb92d14SAndroid Build Coastguard Worker            # Load Thread device of the testing environment version (may be 1.1 or 1.2)
633*cfb92d14SAndroid Build Coastguard Worker            else:
634*cfb92d14SAndroid Build Coastguard Worker                if 'OT_CLI_PATH' in os.environ:
635*cfb92d14SAndroid Build Coastguard Worker                    cmd = os.environ['OT_CLI_PATH']
636*cfb92d14SAndroid Build Coastguard Worker                elif 'top_builddir' in os.environ:
637*cfb92d14SAndroid Build Coastguard Worker                    srcdir = os.environ['top_builddir']
638*cfb92d14SAndroid Build Coastguard Worker                    cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode)
639*cfb92d14SAndroid Build Coastguard Worker
640*cfb92d14SAndroid Build Coastguard Worker            if 'RADIO_DEVICE' in os.environ:
641*cfb92d14SAndroid Build Coastguard Worker                cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE'],
642*cfb92d14SAndroid Build Coastguard Worker                                                                                           nodeid)
643*cfb92d14SAndroid Build Coastguard Worker                self.is_posix = True
644*cfb92d14SAndroid Build Coastguard Worker            else:
645*cfb92d14SAndroid Build Coastguard Worker                cmd += ' %d' % nodeid
646*cfb92d14SAndroid Build Coastguard Worker
647*cfb92d14SAndroid Build Coastguard Worker        # Load Thread 1.1 node when testing Thread 1.2 scenarios for interoperability
648*cfb92d14SAndroid Build Coastguard Worker        elif self.version == '1.1':
649*cfb92d14SAndroid Build Coastguard Worker            # Posix app
650*cfb92d14SAndroid Build Coastguard Worker            if 'OT_CLI_PATH_1_1' in os.environ:
651*cfb92d14SAndroid Build Coastguard Worker                cmd = os.environ['OT_CLI_PATH_1_1']
652*cfb92d14SAndroid Build Coastguard Worker            elif 'top_builddir_1_1' in os.environ:
653*cfb92d14SAndroid Build Coastguard Worker                srcdir = os.environ['top_builddir_1_1']
654*cfb92d14SAndroid Build Coastguard Worker                cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode)
655*cfb92d14SAndroid Build Coastguard Worker
656*cfb92d14SAndroid Build Coastguard Worker            if 'RADIO_DEVICE_1_1' in os.environ:
657*cfb92d14SAndroid Build Coastguard Worker                cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?forkpty-arg=%d' % (
658*cfb92d14SAndroid Build Coastguard Worker                    os.environ['RADIO_DEVICE_1_1'], nodeid)
659*cfb92d14SAndroid Build Coastguard Worker                self.is_posix = True
660*cfb92d14SAndroid Build Coastguard Worker            else:
661*cfb92d14SAndroid Build Coastguard Worker                cmd += ' %d' % nodeid
662*cfb92d14SAndroid Build Coastguard Worker
663*cfb92d14SAndroid Build Coastguard Worker        print("%s" % cmd)
664*cfb92d14SAndroid Build Coastguard Worker
665*cfb92d14SAndroid Build Coastguard Worker        self.pexpect = pexpect.popen_spawn.PopenSpawn(self._cmd_prefix + cmd, timeout=10)
666*cfb92d14SAndroid Build Coastguard Worker
667*cfb92d14SAndroid Build Coastguard Worker        # Add delay to ensure that the process is ready to receive commands.
668*cfb92d14SAndroid Build Coastguard Worker        timeout = 0.4
669*cfb92d14SAndroid Build Coastguard Worker        while timeout > 0:
670*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.send('\r\n')
671*cfb92d14SAndroid Build Coastguard Worker            try:
672*cfb92d14SAndroid Build Coastguard Worker                self.pexpect.expect('> ', timeout=0.1)
673*cfb92d14SAndroid Build Coastguard Worker                break
674*cfb92d14SAndroid Build Coastguard Worker            except pexpect.TIMEOUT:
675*cfb92d14SAndroid Build Coastguard Worker                timeout -= 0.1
676*cfb92d14SAndroid Build Coastguard Worker
677*cfb92d14SAndroid Build Coastguard Worker    def __init_ncp_sim(self, nodeid, mode):
678*cfb92d14SAndroid Build Coastguard Worker        """ Initialize an NCP simulation node. """
679*cfb92d14SAndroid Build Coastguard Worker
680*cfb92d14SAndroid Build Coastguard Worker        # Default command if no match below, will be overridden if below conditions are met.
681*cfb92d14SAndroid Build Coastguard Worker        cmd = 'spinel-cli.py -p ./ot-ncp-%s -n' % mode
682*cfb92d14SAndroid Build Coastguard Worker
683*cfb92d14SAndroid Build Coastguard Worker        # If Thread version of node matches the testing environment version.
684*cfb92d14SAndroid Build Coastguard Worker        if self.version == self.env_version:
685*cfb92d14SAndroid Build Coastguard Worker            if 'RADIO_DEVICE' in os.environ:
686*cfb92d14SAndroid Build Coastguard Worker                args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE'],
687*cfb92d14SAndroid Build Coastguard Worker                                                                                        nodeid)
688*cfb92d14SAndroid Build Coastguard Worker                self.is_posix = True
689*cfb92d14SAndroid Build Coastguard Worker            else:
690*cfb92d14SAndroid Build Coastguard Worker                args = ''
691*cfb92d14SAndroid Build Coastguard Worker
692*cfb92d14SAndroid Build Coastguard Worker            # Load Thread 1.2 BBR device when testing Thread 1.2 scenarios
693*cfb92d14SAndroid Build Coastguard Worker            # which requires device with Backbone functionality.
694*cfb92d14SAndroid Build Coastguard Worker            if self.version != '1.1' and self.is_bbr:
695*cfb92d14SAndroid Build Coastguard Worker                if 'OT_NCP_PATH_1_3_BBR' in os.environ:
696*cfb92d14SAndroid Build Coastguard Worker                    cmd = 'spinel-cli.py -p "%s%s" -n' % (
697*cfb92d14SAndroid Build Coastguard Worker                        os.environ['OT_NCP_PATH_1_3_BBR'],
698*cfb92d14SAndroid Build Coastguard Worker                        args,
699*cfb92d14SAndroid Build Coastguard Worker                    )
700*cfb92d14SAndroid Build Coastguard Worker                elif 'top_builddir_1_3_bbr' in os.environ:
701*cfb92d14SAndroid Build Coastguard Worker                    srcdir = os.environ['top_builddir_1_3_bbr']
702*cfb92d14SAndroid Build Coastguard Worker                    cmd = '%s/examples/apps/ncp/ot-ncp-%s' % (srcdir, mode)
703*cfb92d14SAndroid Build Coastguard Worker                    cmd = 'spinel-cli.py -p "%s%s" -n' % (
704*cfb92d14SAndroid Build Coastguard Worker                        cmd,
705*cfb92d14SAndroid Build Coastguard Worker                        args,
706*cfb92d14SAndroid Build Coastguard Worker                    )
707*cfb92d14SAndroid Build Coastguard Worker
708*cfb92d14SAndroid Build Coastguard Worker            # Load Thread device of the testing environment version (may be 1.1 or 1.2).
709*cfb92d14SAndroid Build Coastguard Worker            else:
710*cfb92d14SAndroid Build Coastguard Worker                if 'OT_NCP_PATH' in os.environ:
711*cfb92d14SAndroid Build Coastguard Worker                    cmd = 'spinel-cli.py -p "%s%s" -n' % (
712*cfb92d14SAndroid Build Coastguard Worker                        os.environ['OT_NCP_PATH'],
713*cfb92d14SAndroid Build Coastguard Worker                        args,
714*cfb92d14SAndroid Build Coastguard Worker                    )
715*cfb92d14SAndroid Build Coastguard Worker                elif 'top_builddir' in os.environ:
716*cfb92d14SAndroid Build Coastguard Worker                    srcdir = os.environ['top_builddir']
717*cfb92d14SAndroid Build Coastguard Worker                    cmd = '%s/examples/apps/ncp/ot-ncp-%s' % (srcdir, mode)
718*cfb92d14SAndroid Build Coastguard Worker                    cmd = 'spinel-cli.py -p "%s%s" -n' % (
719*cfb92d14SAndroid Build Coastguard Worker                        cmd,
720*cfb92d14SAndroid Build Coastguard Worker                        args,
721*cfb92d14SAndroid Build Coastguard Worker                    )
722*cfb92d14SAndroid Build Coastguard Worker
723*cfb92d14SAndroid Build Coastguard Worker        # Load Thread 1.1 node when testing Thread 1.2 scenarios for interoperability.
724*cfb92d14SAndroid Build Coastguard Worker        elif self.version == '1.1':
725*cfb92d14SAndroid Build Coastguard Worker            if 'RADIO_DEVICE_1_1' in os.environ:
726*cfb92d14SAndroid Build Coastguard Worker                args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE_1_1'],
727*cfb92d14SAndroid Build Coastguard Worker                                                                                        nodeid)
728*cfb92d14SAndroid Build Coastguard Worker                self.is_posix = True
729*cfb92d14SAndroid Build Coastguard Worker            else:
730*cfb92d14SAndroid Build Coastguard Worker                args = ''
731*cfb92d14SAndroid Build Coastguard Worker
732*cfb92d14SAndroid Build Coastguard Worker            if 'OT_NCP_PATH_1_1' in os.environ:
733*cfb92d14SAndroid Build Coastguard Worker                cmd = 'spinel-cli.py -p "%s%s" -n' % (
734*cfb92d14SAndroid Build Coastguard Worker                    os.environ['OT_NCP_PATH_1_1'],
735*cfb92d14SAndroid Build Coastguard Worker                    args,
736*cfb92d14SAndroid Build Coastguard Worker                )
737*cfb92d14SAndroid Build Coastguard Worker            elif 'top_builddir_1_1' in os.environ:
738*cfb92d14SAndroid Build Coastguard Worker                srcdir = os.environ['top_builddir_1_1']
739*cfb92d14SAndroid Build Coastguard Worker                cmd = '%s/examples/apps/ncp/ot-ncp-%s' % (srcdir, mode)
740*cfb92d14SAndroid Build Coastguard Worker                cmd = 'spinel-cli.py -p "%s%s" -n' % (
741*cfb92d14SAndroid Build Coastguard Worker                    cmd,
742*cfb92d14SAndroid Build Coastguard Worker                    args,
743*cfb92d14SAndroid Build Coastguard Worker                )
744*cfb92d14SAndroid Build Coastguard Worker
745*cfb92d14SAndroid Build Coastguard Worker        cmd += ' %d' % nodeid
746*cfb92d14SAndroid Build Coastguard Worker        print("%s" % cmd)
747*cfb92d14SAndroid Build Coastguard Worker
748*cfb92d14SAndroid Build Coastguard Worker        self.pexpect = pexpect.spawn(self._cmd_prefix + cmd, timeout=10)
749*cfb92d14SAndroid Build Coastguard Worker
750*cfb92d14SAndroid Build Coastguard Worker        # Add delay to ensure that the process is ready to receive commands.
751*cfb92d14SAndroid Build Coastguard Worker        time.sleep(0.2)
752*cfb92d14SAndroid Build Coastguard Worker        self._expect('spinel-cli >')
753*cfb92d14SAndroid Build Coastguard Worker        self.debug(int(os.getenv('DEBUG', '0')))
754*cfb92d14SAndroid Build Coastguard Worker
755*cfb92d14SAndroid Build Coastguard Worker    def __init_soc(self, nodeid):
756*cfb92d14SAndroid Build Coastguard Worker        """ Initialize a System-on-a-chip node connected via UART. """
757*cfb92d14SAndroid Build Coastguard Worker        import fdpexpect
758*cfb92d14SAndroid Build Coastguard Worker
759*cfb92d14SAndroid Build Coastguard Worker        serialPort = '/dev/ttyUSB%d' % ((nodeid - 1) * 2)
760*cfb92d14SAndroid Build Coastguard Worker        self.pexpect = fdpexpect.fdspawn(os.open(serialPort, os.O_RDWR | os.O_NONBLOCK | os.O_NOCTTY))
761*cfb92d14SAndroid Build Coastguard Worker
762*cfb92d14SAndroid Build Coastguard Worker    def destroy(self):
763*cfb92d14SAndroid Build Coastguard Worker        if not self._initialized:
764*cfb92d14SAndroid Build Coastguard Worker            return
765*cfb92d14SAndroid Build Coastguard Worker
766*cfb92d14SAndroid Build Coastguard Worker        if (hasattr(self.pexpect, 'proc') and self.pexpect.proc.poll() is None or
767*cfb92d14SAndroid Build Coastguard Worker                not hasattr(self.pexpect, 'proc') and self.pexpect.isalive()):
768*cfb92d14SAndroid Build Coastguard Worker            print("%d: exit" % self.nodeid)
769*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.send('exit\n')
770*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.expect(pexpect.EOF)
771*cfb92d14SAndroid Build Coastguard Worker            self.pexpect.wait()
772*cfb92d14SAndroid Build Coastguard Worker            self._initialized = False
773*cfb92d14SAndroid Build Coastguard Worker
774*cfb92d14SAndroid Build Coastguard Worker
775*cfb92d14SAndroid Build Coastguard Workerclass NodeImpl:
776*cfb92d14SAndroid Build Coastguard Worker    is_host = False
777*cfb92d14SAndroid Build Coastguard Worker    is_otbr = False
778*cfb92d14SAndroid Build Coastguard Worker
779*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, nodeid, name=None, simulator=None, **kwargs):
780*cfb92d14SAndroid Build Coastguard Worker        self.nodeid = nodeid
781*cfb92d14SAndroid Build Coastguard Worker        self.name = name or ('Node%d' % nodeid)
782*cfb92d14SAndroid Build Coastguard Worker        self.is_posix = False
783*cfb92d14SAndroid Build Coastguard Worker
784*cfb92d14SAndroid Build Coastguard Worker        self.simulator = simulator
785*cfb92d14SAndroid Build Coastguard Worker        if self.simulator:
786*cfb92d14SAndroid Build Coastguard Worker            self.simulator.add_node(self)
787*cfb92d14SAndroid Build Coastguard Worker
788*cfb92d14SAndroid Build Coastguard Worker        super().__init__(nodeid, **kwargs)
789*cfb92d14SAndroid Build Coastguard Worker
790*cfb92d14SAndroid Build Coastguard Worker        self.set_addr64('%016x' % (thread_cert.EXTENDED_ADDRESS_BASE + nodeid))
791*cfb92d14SAndroid Build Coastguard Worker
792*cfb92d14SAndroid Build Coastguard Worker    def _expect(self, pattern, timeout=-1, *args, **kwargs):
793*cfb92d14SAndroid Build Coastguard Worker        """ Process simulator events until expected the pattern. """
794*cfb92d14SAndroid Build Coastguard Worker        if timeout == -1:
795*cfb92d14SAndroid Build Coastguard Worker            timeout = self.pexpect.timeout
796*cfb92d14SAndroid Build Coastguard Worker
797*cfb92d14SAndroid Build Coastguard Worker        assert timeout > 0
798*cfb92d14SAndroid Build Coastguard Worker
799*cfb92d14SAndroid Build Coastguard Worker        while timeout > 0:
800*cfb92d14SAndroid Build Coastguard Worker            try:
801*cfb92d14SAndroid Build Coastguard Worker                return self.pexpect.expect(pattern, 0.1, *args, **kwargs)
802*cfb92d14SAndroid Build Coastguard Worker            except pexpect.TIMEOUT:
803*cfb92d14SAndroid Build Coastguard Worker                timeout -= 0.1
804*cfb92d14SAndroid Build Coastguard Worker                self.simulator.go(0)
805*cfb92d14SAndroid Build Coastguard Worker                if timeout <= 0:
806*cfb92d14SAndroid Build Coastguard Worker                    raise
807*cfb92d14SAndroid Build Coastguard Worker
808*cfb92d14SAndroid Build Coastguard Worker    def _expect_done(self, timeout=-1):
809*cfb92d14SAndroid Build Coastguard Worker        self._expect('Done', timeout)
810*cfb92d14SAndroid Build Coastguard Worker
811*cfb92d14SAndroid Build Coastguard Worker    def _expect_result(self, pattern, *args, **kwargs):
812*cfb92d14SAndroid Build Coastguard Worker        """Expect a single matching result.
813*cfb92d14SAndroid Build Coastguard Worker
814*cfb92d14SAndroid Build Coastguard Worker        The arguments are identical to pexpect.expect().
815*cfb92d14SAndroid Build Coastguard Worker
816*cfb92d14SAndroid Build Coastguard Worker        Returns:
817*cfb92d14SAndroid Build Coastguard Worker            The matched line.
818*cfb92d14SAndroid Build Coastguard Worker        """
819*cfb92d14SAndroid Build Coastguard Worker        results = self._expect_results(pattern, *args, **kwargs)
820*cfb92d14SAndroid Build Coastguard Worker        assert len(results) == 1, results
821*cfb92d14SAndroid Build Coastguard Worker        return results[0]
822*cfb92d14SAndroid Build Coastguard Worker
823*cfb92d14SAndroid Build Coastguard Worker    def _expect_results(self, pattern, *args, **kwargs):
824*cfb92d14SAndroid Build Coastguard Worker        """Expect multiple matching results.
825*cfb92d14SAndroid Build Coastguard Worker
826*cfb92d14SAndroid Build Coastguard Worker        The arguments are identical to pexpect.expect().
827*cfb92d14SAndroid Build Coastguard Worker
828*cfb92d14SAndroid Build Coastguard Worker        Returns:
829*cfb92d14SAndroid Build Coastguard Worker            The matched lines.
830*cfb92d14SAndroid Build Coastguard Worker        """
831*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
832*cfb92d14SAndroid Build Coastguard Worker        results = [line for line in output if self._match_pattern(line, pattern)]
833*cfb92d14SAndroid Build Coastguard Worker        return results
834*cfb92d14SAndroid Build Coastguard Worker
835*cfb92d14SAndroid Build Coastguard Worker    def _expect_key_value_pairs(self, pattern, separator=': '):
836*cfb92d14SAndroid Build Coastguard Worker        """Expect 'key: value' in multiple lines.
837*cfb92d14SAndroid Build Coastguard Worker
838*cfb92d14SAndroid Build Coastguard Worker        Returns:
839*cfb92d14SAndroid Build Coastguard Worker            Dictionary of the key:value pairs.
840*cfb92d14SAndroid Build Coastguard Worker        """
841*cfb92d14SAndroid Build Coastguard Worker        result = {}
842*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_results(pattern):
843*cfb92d14SAndroid Build Coastguard Worker            key, val = line.split(separator)
844*cfb92d14SAndroid Build Coastguard Worker            result.update({key: val})
845*cfb92d14SAndroid Build Coastguard Worker        return result
846*cfb92d14SAndroid Build Coastguard Worker
847*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
848*cfb92d14SAndroid Build Coastguard Worker    def _match_pattern(line, pattern):
849*cfb92d14SAndroid Build Coastguard Worker        if isinstance(pattern, str):
850*cfb92d14SAndroid Build Coastguard Worker            pattern = re.compile(pattern)
851*cfb92d14SAndroid Build Coastguard Worker
852*cfb92d14SAndroid Build Coastguard Worker        if isinstance(pattern, typing.Pattern):
853*cfb92d14SAndroid Build Coastguard Worker            return pattern.match(line)
854*cfb92d14SAndroid Build Coastguard Worker        else:
855*cfb92d14SAndroid Build Coastguard Worker            return any(NodeImpl._match_pattern(line, p) for p in pattern)
856*cfb92d14SAndroid Build Coastguard Worker
857*cfb92d14SAndroid Build Coastguard Worker    def _expect_command_output(self, ignore_logs=True):
858*cfb92d14SAndroid Build Coastguard Worker        lines = []
859*cfb92d14SAndroid Build Coastguard Worker
860*cfb92d14SAndroid Build Coastguard Worker        while True:
861*cfb92d14SAndroid Build Coastguard Worker            line = self.__readline(ignore_logs=ignore_logs)
862*cfb92d14SAndroid Build Coastguard Worker
863*cfb92d14SAndroid Build Coastguard Worker            if line == 'Done':
864*cfb92d14SAndroid Build Coastguard Worker                break
865*cfb92d14SAndroid Build Coastguard Worker            elif line.startswith('Error '):
866*cfb92d14SAndroid Build Coastguard Worker                raise Exception(line)
867*cfb92d14SAndroid Build Coastguard Worker            else:
868*cfb92d14SAndroid Build Coastguard Worker                lines.append(line)
869*cfb92d14SAndroid Build Coastguard Worker
870*cfb92d14SAndroid Build Coastguard Worker        print(f'_expect_command_output() returns {lines!r}')
871*cfb92d14SAndroid Build Coastguard Worker        return lines
872*cfb92d14SAndroid Build Coastguard Worker
873*cfb92d14SAndroid Build Coastguard Worker    def __is_logging_line(self, line: str) -> bool:
874*cfb92d14SAndroid Build Coastguard Worker        return len(line) >= 3 and line[:3] in {'[D]', '[I]', '[N]', '[W]', '[C]', '[-]'}
875*cfb92d14SAndroid Build Coastguard Worker
876*cfb92d14SAndroid Build Coastguard Worker    def read_cert_messages_in_commissioning_log(self, timeout=-1):
877*cfb92d14SAndroid Build Coastguard Worker        """Get the log of the traffic after DTLS handshake.
878*cfb92d14SAndroid Build Coastguard Worker        """
879*cfb92d14SAndroid Build Coastguard Worker        format_str = br"=+?\[\[THCI\].*?type=%s.*?\].*?=+?[\s\S]+?-{40,}"
880*cfb92d14SAndroid Build Coastguard Worker        join_fin_req = format_str % br"JOIN_FIN\.req"
881*cfb92d14SAndroid Build Coastguard Worker        join_fin_rsp = format_str % br"JOIN_FIN\.rsp"
882*cfb92d14SAndroid Build Coastguard Worker        dummy_format_str = br"\[THCI\].*?type=%s.*?"
883*cfb92d14SAndroid Build Coastguard Worker        join_ent_ntf = dummy_format_str % br"JOIN_ENT\.ntf"
884*cfb92d14SAndroid Build Coastguard Worker        join_ent_rsp = dummy_format_str % br"JOIN_ENT\.rsp"
885*cfb92d14SAndroid Build Coastguard Worker        pattern = (b"(" + join_fin_req + b")|(" + join_fin_rsp + b")|(" + join_ent_ntf + b")|(" + join_ent_rsp + b")")
886*cfb92d14SAndroid Build Coastguard Worker
887*cfb92d14SAndroid Build Coastguard Worker        messages = []
888*cfb92d14SAndroid Build Coastguard Worker        # There are at most 4 cert messages both for joiner and commissioner
889*cfb92d14SAndroid Build Coastguard Worker        for _ in range(0, 4):
890*cfb92d14SAndroid Build Coastguard Worker            try:
891*cfb92d14SAndroid Build Coastguard Worker                self._expect(pattern, timeout=timeout)
892*cfb92d14SAndroid Build Coastguard Worker                log = self.pexpect.match.group(0)
893*cfb92d14SAndroid Build Coastguard Worker                messages.append(self._extract_cert_message(log))
894*cfb92d14SAndroid Build Coastguard Worker            except BaseException:
895*cfb92d14SAndroid Build Coastguard Worker                break
896*cfb92d14SAndroid Build Coastguard Worker        return messages
897*cfb92d14SAndroid Build Coastguard Worker
898*cfb92d14SAndroid Build Coastguard Worker    def _extract_cert_message(self, log):
899*cfb92d14SAndroid Build Coastguard Worker        res = re.search(br"direction=\w+", log)
900*cfb92d14SAndroid Build Coastguard Worker        assert res
901*cfb92d14SAndroid Build Coastguard Worker        direction = res.group(0).split(b'=')[1].strip()
902*cfb92d14SAndroid Build Coastguard Worker
903*cfb92d14SAndroid Build Coastguard Worker        res = re.search(br"type=\S+", log)
904*cfb92d14SAndroid Build Coastguard Worker        assert res
905*cfb92d14SAndroid Build Coastguard Worker        type = res.group(0).split(b'=')[1].strip()
906*cfb92d14SAndroid Build Coastguard Worker
907*cfb92d14SAndroid Build Coastguard Worker        payload = bytearray([])
908*cfb92d14SAndroid Build Coastguard Worker        payload_len = 0
909*cfb92d14SAndroid Build Coastguard Worker        if type in [b"JOIN_FIN.req", b"JOIN_FIN.rsp"]:
910*cfb92d14SAndroid Build Coastguard Worker            res = re.search(br"len=\d+", log)
911*cfb92d14SAndroid Build Coastguard Worker            assert res
912*cfb92d14SAndroid Build Coastguard Worker            payload_len = int(res.group(0).split(b'=')[1].strip())
913*cfb92d14SAndroid Build Coastguard Worker
914*cfb92d14SAndroid Build Coastguard Worker            hex_pattern = br"\|(\s([0-9a-fA-F]{2}|\.\.))+?\s+?\|"
915*cfb92d14SAndroid Build Coastguard Worker            while True:
916*cfb92d14SAndroid Build Coastguard Worker                res = re.search(hex_pattern, log)
917*cfb92d14SAndroid Build Coastguard Worker                if not res:
918*cfb92d14SAndroid Build Coastguard Worker                    break
919*cfb92d14SAndroid Build Coastguard Worker                data = [int(hex, 16) for hex in res.group(0)[1:-1].split(b' ') if hex and hex != b'..']
920*cfb92d14SAndroid Build Coastguard Worker                payload += bytearray(data)
921*cfb92d14SAndroid Build Coastguard Worker                log = log[res.end() - 1:]
922*cfb92d14SAndroid Build Coastguard Worker        assert len(payload) == payload_len
923*cfb92d14SAndroid Build Coastguard Worker        return (direction, type, payload)
924*cfb92d14SAndroid Build Coastguard Worker
925*cfb92d14SAndroid Build Coastguard Worker    def send_command(self, cmd, go=True, expect_command_echo=True):
926*cfb92d14SAndroid Build Coastguard Worker        print("%d: %s" % (self.nodeid, cmd))
927*cfb92d14SAndroid Build Coastguard Worker        self.pexpect.send(cmd + '\n')
928*cfb92d14SAndroid Build Coastguard Worker        if go:
929*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(0, nodeid=self.nodeid)
930*cfb92d14SAndroid Build Coastguard Worker        sys.stdout.flush()
931*cfb92d14SAndroid Build Coastguard Worker
932*cfb92d14SAndroid Build Coastguard Worker        if expect_command_echo:
933*cfb92d14SAndroid Build Coastguard Worker            self._expect_command_echo(cmd)
934*cfb92d14SAndroid Build Coastguard Worker
935*cfb92d14SAndroid Build Coastguard Worker    def _expect_command_echo(self, cmd):
936*cfb92d14SAndroid Build Coastguard Worker        cmd = cmd.strip()
937*cfb92d14SAndroid Build Coastguard Worker        while True:
938*cfb92d14SAndroid Build Coastguard Worker            line = self.__readline()
939*cfb92d14SAndroid Build Coastguard Worker            if line == cmd:
940*cfb92d14SAndroid Build Coastguard Worker                break
941*cfb92d14SAndroid Build Coastguard Worker
942*cfb92d14SAndroid Build Coastguard Worker            logging.warning("expecting echo %r, but read %r", cmd, line)
943*cfb92d14SAndroid Build Coastguard Worker
944*cfb92d14SAndroid Build Coastguard Worker    def __readline(self, ignore_logs=True):
945*cfb92d14SAndroid Build Coastguard Worker        PROMPT = 'spinel-cli > ' if self.node_type == 'ncp-sim' else '> '
946*cfb92d14SAndroid Build Coastguard Worker        while True:
947*cfb92d14SAndroid Build Coastguard Worker            self._expect(r"[^\n]+\n")
948*cfb92d14SAndroid Build Coastguard Worker            line = self.pexpect.match.group(0).decode('utf8').strip()
949*cfb92d14SAndroid Build Coastguard Worker            while line.startswith(PROMPT):
950*cfb92d14SAndroid Build Coastguard Worker                line = line[len(PROMPT):]
951*cfb92d14SAndroid Build Coastguard Worker
952*cfb92d14SAndroid Build Coastguard Worker            if line == '':
953*cfb92d14SAndroid Build Coastguard Worker                continue
954*cfb92d14SAndroid Build Coastguard Worker
955*cfb92d14SAndroid Build Coastguard Worker            if ignore_logs and self.__is_logging_line(line):
956*cfb92d14SAndroid Build Coastguard Worker                continue
957*cfb92d14SAndroid Build Coastguard Worker
958*cfb92d14SAndroid Build Coastguard Worker            return line
959*cfb92d14SAndroid Build Coastguard Worker
960*cfb92d14SAndroid Build Coastguard Worker    def get_commands(self):
961*cfb92d14SAndroid Build Coastguard Worker        self.send_command('?')
962*cfb92d14SAndroid Build Coastguard Worker        self._expect('Commands:')
963*cfb92d14SAndroid Build Coastguard Worker        return self._expect_results(r'\S+')
964*cfb92d14SAndroid Build Coastguard Worker
965*cfb92d14SAndroid Build Coastguard Worker    def set_mode(self, mode):
966*cfb92d14SAndroid Build Coastguard Worker        cmd = 'mode %s' % mode
967*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
968*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
969*cfb92d14SAndroid Build Coastguard Worker
970*cfb92d14SAndroid Build Coastguard Worker    def debug(self, level):
971*cfb92d14SAndroid Build Coastguard Worker        # `debug` command will not trigger interaction with simulator
972*cfb92d14SAndroid Build Coastguard Worker        self.send_command('debug %d' % level, go=False)
973*cfb92d14SAndroid Build Coastguard Worker
974*cfb92d14SAndroid Build Coastguard Worker    def start(self):
975*cfb92d14SAndroid Build Coastguard Worker        self.interface_up()
976*cfb92d14SAndroid Build Coastguard Worker        self.thread_start()
977*cfb92d14SAndroid Build Coastguard Worker
978*cfb92d14SAndroid Build Coastguard Worker    def stop(self):
979*cfb92d14SAndroid Build Coastguard Worker        self.thread_stop()
980*cfb92d14SAndroid Build Coastguard Worker        self.interface_down()
981*cfb92d14SAndroid Build Coastguard Worker
982*cfb92d14SAndroid Build Coastguard Worker    def set_log_level(self, level: int):
983*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'log level {level}')
984*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
985*cfb92d14SAndroid Build Coastguard Worker
986*cfb92d14SAndroid Build Coastguard Worker    def interface_up(self):
987*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ifconfig up')
988*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
989*cfb92d14SAndroid Build Coastguard Worker
990*cfb92d14SAndroid Build Coastguard Worker    def interface_down(self):
991*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ifconfig down')
992*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
993*cfb92d14SAndroid Build Coastguard Worker
994*cfb92d14SAndroid Build Coastguard Worker    def thread_start(self):
995*cfb92d14SAndroid Build Coastguard Worker        self.send_command('thread start')
996*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
997*cfb92d14SAndroid Build Coastguard Worker
998*cfb92d14SAndroid Build Coastguard Worker    def thread_stop(self):
999*cfb92d14SAndroid Build Coastguard Worker        self.send_command('thread stop')
1000*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1001*cfb92d14SAndroid Build Coastguard Worker
1002*cfb92d14SAndroid Build Coastguard Worker    def detach(self, is_async=False):
1003*cfb92d14SAndroid Build Coastguard Worker        cmd = 'detach'
1004*cfb92d14SAndroid Build Coastguard Worker        if is_async:
1005*cfb92d14SAndroid Build Coastguard Worker            cmd += ' async'
1006*cfb92d14SAndroid Build Coastguard Worker
1007*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1008*cfb92d14SAndroid Build Coastguard Worker
1009*cfb92d14SAndroid Build Coastguard Worker        if is_async:
1010*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
1011*cfb92d14SAndroid Build Coastguard Worker            return
1012*cfb92d14SAndroid Build Coastguard Worker
1013*cfb92d14SAndroid Build Coastguard Worker        end = self.simulator.now() + 4
1014*cfb92d14SAndroid Build Coastguard Worker        while True:
1015*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(1)
1016*cfb92d14SAndroid Build Coastguard Worker            try:
1017*cfb92d14SAndroid Build Coastguard Worker                self._expect_done(timeout=0.1)
1018*cfb92d14SAndroid Build Coastguard Worker                return
1019*cfb92d14SAndroid Build Coastguard Worker            except (pexpect.TIMEOUT, socket.timeout):
1020*cfb92d14SAndroid Build Coastguard Worker                if self.simulator.now() > end:
1021*cfb92d14SAndroid Build Coastguard Worker                    raise
1022*cfb92d14SAndroid Build Coastguard Worker
1023*cfb92d14SAndroid Build Coastguard Worker    def expect_finished_detaching(self):
1024*cfb92d14SAndroid Build Coastguard Worker        self._expect('Finished detaching')
1025*cfb92d14SAndroid Build Coastguard Worker
1026*cfb92d14SAndroid Build Coastguard Worker    def commissioner_start(self):
1027*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner start'
1028*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1029*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1030*cfb92d14SAndroid Build Coastguard Worker
1031*cfb92d14SAndroid Build Coastguard Worker    def commissioner_stop(self):
1032*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner stop'
1033*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1034*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1035*cfb92d14SAndroid Build Coastguard Worker
1036*cfb92d14SAndroid Build Coastguard Worker    def commissioner_state(self):
1037*cfb92d14SAndroid Build Coastguard Worker        states = [r'disabled', r'petitioning', r'active']
1038*cfb92d14SAndroid Build Coastguard Worker        self.send_command('commissioner state')
1039*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states)
1040*cfb92d14SAndroid Build Coastguard Worker
1041*cfb92d14SAndroid Build Coastguard Worker    def commissioner_add_joiner(self, addr, psk):
1042*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner joiner add %s %s' % (addr, psk)
1043*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1044*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1045*cfb92d14SAndroid Build Coastguard Worker
1046*cfb92d14SAndroid Build Coastguard Worker    def commissioner_set_provisioning_url(self, provisioning_url=''):
1047*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner provisioningurl %s' % provisioning_url
1048*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1049*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1050*cfb92d14SAndroid Build Coastguard Worker
1051*cfb92d14SAndroid Build Coastguard Worker    def joiner_start(self, pskd='', provisioning_url=''):
1052*cfb92d14SAndroid Build Coastguard Worker        cmd = 'joiner start %s %s' % (pskd, provisioning_url)
1053*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1054*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1055*cfb92d14SAndroid Build Coastguard Worker
1056*cfb92d14SAndroid Build Coastguard Worker    def clear_allowlist(self):
1057*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter addr clear'
1058*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1059*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1060*cfb92d14SAndroid Build Coastguard Worker
1061*cfb92d14SAndroid Build Coastguard Worker    def enable_allowlist(self):
1062*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter addr allowlist'
1063*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1064*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1065*cfb92d14SAndroid Build Coastguard Worker
1066*cfb92d14SAndroid Build Coastguard Worker    def disable_allowlist(self):
1067*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter addr disable'
1068*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1069*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1070*cfb92d14SAndroid Build Coastguard Worker
1071*cfb92d14SAndroid Build Coastguard Worker    def add_allowlist(self, addr, rssi=None):
1072*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter addr add %s' % addr
1073*cfb92d14SAndroid Build Coastguard Worker
1074*cfb92d14SAndroid Build Coastguard Worker        if rssi is not None:
1075*cfb92d14SAndroid Build Coastguard Worker            cmd += ' %s' % rssi
1076*cfb92d14SAndroid Build Coastguard Worker
1077*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1078*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1079*cfb92d14SAndroid Build Coastguard Worker
1080*cfb92d14SAndroid Build Coastguard Worker    def radiofilter_is_enabled(self) -> bool:
1081*cfb92d14SAndroid Build Coastguard Worker        states = [r'Disabled', r'Enabled']
1082*cfb92d14SAndroid Build Coastguard Worker        self.send_command('radiofilter')
1083*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states) == 'Enabled'
1084*cfb92d14SAndroid Build Coastguard Worker
1085*cfb92d14SAndroid Build Coastguard Worker    def radiofilter_enable(self):
1086*cfb92d14SAndroid Build Coastguard Worker        cmd = 'radiofilter enable'
1087*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1088*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1089*cfb92d14SAndroid Build Coastguard Worker
1090*cfb92d14SAndroid Build Coastguard Worker    def radiofilter_disable(self):
1091*cfb92d14SAndroid Build Coastguard Worker        cmd = 'radiofilter disable'
1092*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1093*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1094*cfb92d14SAndroid Build Coastguard Worker
1095*cfb92d14SAndroid Build Coastguard Worker    def get_bbr_registration_jitter(self):
1096*cfb92d14SAndroid Build Coastguard Worker        self.send_command('bbr jitter')
1097*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result(r'\d+'))
1098*cfb92d14SAndroid Build Coastguard Worker
1099*cfb92d14SAndroid Build Coastguard Worker    def set_bbr_registration_jitter(self, jitter):
1100*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr jitter %d' % jitter
1101*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1102*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1103*cfb92d14SAndroid Build Coastguard Worker
1104*cfb92d14SAndroid Build Coastguard Worker    def get_rcp_version(self) -> str:
1105*cfb92d14SAndroid Build Coastguard Worker        self.send_command('rcp version')
1106*cfb92d14SAndroid Build Coastguard Worker        rcp_version = self._expect_command_output()[0].strip()
1107*cfb92d14SAndroid Build Coastguard Worker        return rcp_version
1108*cfb92d14SAndroid Build Coastguard Worker
1109*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_state(self):
1110*cfb92d14SAndroid Build Coastguard Worker        states = ['disabled', 'running', 'stopped']
1111*cfb92d14SAndroid Build Coastguard Worker        self.send_command('srp server state')
1112*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states)
1113*cfb92d14SAndroid Build Coastguard Worker
1114*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_addr_mode(self):
1115*cfb92d14SAndroid Build Coastguard Worker        modes = [r'unicast', r'anycast']
1116*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server addrmode')
1117*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(modes)
1118*cfb92d14SAndroid Build Coastguard Worker
1119*cfb92d14SAndroid Build Coastguard Worker    def srp_server_set_addr_mode(self, mode):
1120*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server addrmode {mode}')
1121*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1122*cfb92d14SAndroid Build Coastguard Worker
1123*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_anycast_seq_num(self):
1124*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server seqnum')
1125*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result(r'\d+'))
1126*cfb92d14SAndroid Build Coastguard Worker
1127*cfb92d14SAndroid Build Coastguard Worker    def srp_server_set_anycast_seq_num(self, seqnum):
1128*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server seqnum {seqnum}')
1129*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1130*cfb92d14SAndroid Build Coastguard Worker
1131*cfb92d14SAndroid Build Coastguard Worker    def srp_server_set_enabled(self, enable):
1132*cfb92d14SAndroid Build Coastguard Worker        cmd = f'srp server {"enable" if enable else "disable"}'
1133*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1134*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1135*cfb92d14SAndroid Build Coastguard Worker
1136*cfb92d14SAndroid Build Coastguard Worker    def srp_server_set_lease_range(self, min_lease, max_lease, min_key_lease, max_key_lease):
1137*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server lease {min_lease} {max_lease} {min_key_lease} {max_key_lease}')
1138*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1139*cfb92d14SAndroid Build Coastguard Worker
1140*cfb92d14SAndroid Build Coastguard Worker    def srp_server_set_ttl_range(self, min_ttl, max_ttl):
1141*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp server ttl {min_ttl} {max_ttl}')
1142*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1143*cfb92d14SAndroid Build Coastguard Worker
1144*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_hosts(self):
1145*cfb92d14SAndroid Build Coastguard Worker        """Returns the host list on the SRP server as a list of property
1146*cfb92d14SAndroid Build Coastguard Worker           dictionary.
1147*cfb92d14SAndroid Build Coastguard Worker
1148*cfb92d14SAndroid Build Coastguard Worker           Example output:
1149*cfb92d14SAndroid Build Coastguard Worker           [{
1150*cfb92d14SAndroid Build Coastguard Worker               'fullname': 'my-host.default.service.arpa.',
1151*cfb92d14SAndroid Build Coastguard Worker               'name': 'my-host',
1152*cfb92d14SAndroid Build Coastguard Worker               'deleted': 'false',
1153*cfb92d14SAndroid Build Coastguard Worker               'addresses': ['2001::1', '2001::2']
1154*cfb92d14SAndroid Build Coastguard Worker           }]
1155*cfb92d14SAndroid Build Coastguard Worker        """
1156*cfb92d14SAndroid Build Coastguard Worker
1157*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp server host'
1158*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1159*cfb92d14SAndroid Build Coastguard Worker        lines = self._expect_command_output()
1160*cfb92d14SAndroid Build Coastguard Worker        host_list = []
1161*cfb92d14SAndroid Build Coastguard Worker        while lines:
1162*cfb92d14SAndroid Build Coastguard Worker            host = {}
1163*cfb92d14SAndroid Build Coastguard Worker
1164*cfb92d14SAndroid Build Coastguard Worker            host['fullname'] = lines.pop(0).strip()
1165*cfb92d14SAndroid Build Coastguard Worker            host['name'] = host['fullname'].split('.')[0]
1166*cfb92d14SAndroid Build Coastguard Worker
1167*cfb92d14SAndroid Build Coastguard Worker            host['deleted'] = lines.pop(0).strip().split(':')[1].strip()
1168*cfb92d14SAndroid Build Coastguard Worker            if host['deleted'] == 'true':
1169*cfb92d14SAndroid Build Coastguard Worker                host_list.append(host)
1170*cfb92d14SAndroid Build Coastguard Worker                continue
1171*cfb92d14SAndroid Build Coastguard Worker
1172*cfb92d14SAndroid Build Coastguard Worker            addresses = lines.pop(0).strip().split('[')[1].strip(' ]').split(',')
1173*cfb92d14SAndroid Build Coastguard Worker            map(str.strip, addresses)
1174*cfb92d14SAndroid Build Coastguard Worker            host['addresses'] = [addr.strip() for addr in addresses if addr]
1175*cfb92d14SAndroid Build Coastguard Worker
1176*cfb92d14SAndroid Build Coastguard Worker            host_list.append(host)
1177*cfb92d14SAndroid Build Coastguard Worker
1178*cfb92d14SAndroid Build Coastguard Worker        return host_list
1179*cfb92d14SAndroid Build Coastguard Worker
1180*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_host(self, host_name):
1181*cfb92d14SAndroid Build Coastguard Worker        """Returns host on the SRP server that matches given host name.
1182*cfb92d14SAndroid Build Coastguard Worker
1183*cfb92d14SAndroid Build Coastguard Worker           Example usage:
1184*cfb92d14SAndroid Build Coastguard Worker           self.srp_server_get_host("my-host")
1185*cfb92d14SAndroid Build Coastguard Worker        """
1186*cfb92d14SAndroid Build Coastguard Worker
1187*cfb92d14SAndroid Build Coastguard Worker        for host in self.srp_server_get_hosts():
1188*cfb92d14SAndroid Build Coastguard Worker            if host_name == host['name']:
1189*cfb92d14SAndroid Build Coastguard Worker                return host
1190*cfb92d14SAndroid Build Coastguard Worker
1191*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_services(self):
1192*cfb92d14SAndroid Build Coastguard Worker        """Returns the service list on the SRP server as a list of property
1193*cfb92d14SAndroid Build Coastguard Worker           dictionary.
1194*cfb92d14SAndroid Build Coastguard Worker
1195*cfb92d14SAndroid Build Coastguard Worker           Example output:
1196*cfb92d14SAndroid Build Coastguard Worker           [{
1197*cfb92d14SAndroid Build Coastguard Worker               'fullname': 'my-service._ipps._tcp.default.service.arpa.',
1198*cfb92d14SAndroid Build Coastguard Worker               'instance': 'my-service',
1199*cfb92d14SAndroid Build Coastguard Worker               'name': '_ipps._tcp',
1200*cfb92d14SAndroid Build Coastguard Worker               'deleted': 'false',
1201*cfb92d14SAndroid Build Coastguard Worker               'port': '12345',
1202*cfb92d14SAndroid Build Coastguard Worker               'priority': '0',
1203*cfb92d14SAndroid Build Coastguard Worker               'weight': '0',
1204*cfb92d14SAndroid Build Coastguard Worker               'ttl': '7200',
1205*cfb92d14SAndroid Build Coastguard Worker               'lease': '7200',
1206*cfb92d14SAndroid Build Coastguard Worker               'key-lease': '7200',
1207*cfb92d14SAndroid Build Coastguard Worker               'TXT': ['abc=010203'],
1208*cfb92d14SAndroid Build Coastguard Worker               'host_fullname': 'my-host.default.service.arpa.',
1209*cfb92d14SAndroid Build Coastguard Worker               'host': 'my-host',
1210*cfb92d14SAndroid Build Coastguard Worker               'addresses': ['2001::1', '2001::2']
1211*cfb92d14SAndroid Build Coastguard Worker           }]
1212*cfb92d14SAndroid Build Coastguard Worker
1213*cfb92d14SAndroid Build Coastguard Worker           Note that the TXT data is output as a HEX string.
1214*cfb92d14SAndroid Build Coastguard Worker        """
1215*cfb92d14SAndroid Build Coastguard Worker
1216*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp server service'
1217*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1218*cfb92d14SAndroid Build Coastguard Worker        lines = self._expect_command_output()
1219*cfb92d14SAndroid Build Coastguard Worker
1220*cfb92d14SAndroid Build Coastguard Worker        service_list = []
1221*cfb92d14SAndroid Build Coastguard Worker        while lines:
1222*cfb92d14SAndroid Build Coastguard Worker            service = {}
1223*cfb92d14SAndroid Build Coastguard Worker
1224*cfb92d14SAndroid Build Coastguard Worker            service['fullname'] = lines.pop(0).strip()
1225*cfb92d14SAndroid Build Coastguard Worker            name_labels = service['fullname'].split('.')
1226*cfb92d14SAndroid Build Coastguard Worker            service['instance'] = name_labels[0]
1227*cfb92d14SAndroid Build Coastguard Worker            service['name'] = '.'.join(name_labels[1:3])
1228*cfb92d14SAndroid Build Coastguard Worker
1229*cfb92d14SAndroid Build Coastguard Worker            service['deleted'] = lines.pop(0).strip().split(':')[1].strip()
1230*cfb92d14SAndroid Build Coastguard Worker            if service['deleted'] == 'true':
1231*cfb92d14SAndroid Build Coastguard Worker                service_list.append(service)
1232*cfb92d14SAndroid Build Coastguard Worker                continue
1233*cfb92d14SAndroid Build Coastguard Worker
1234*cfb92d14SAndroid Build Coastguard Worker            # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', and 'key-lease'
1235*cfb92d14SAndroid Build Coastguard Worker            for i in range(0, 7):
1236*cfb92d14SAndroid Build Coastguard Worker                key_value = lines.pop(0).strip().split(':')
1237*cfb92d14SAndroid Build Coastguard Worker                service[key_value[0].strip()] = key_value[1].strip()
1238*cfb92d14SAndroid Build Coastguard Worker
1239*cfb92d14SAndroid Build Coastguard Worker            txt_entries = lines.pop(0).strip().split('[')[1].strip(' ]').split(',')
1240*cfb92d14SAndroid Build Coastguard Worker            txt_entries = map(str.strip, txt_entries)
1241*cfb92d14SAndroid Build Coastguard Worker            service['TXT'] = [txt for txt in txt_entries if txt]
1242*cfb92d14SAndroid Build Coastguard Worker
1243*cfb92d14SAndroid Build Coastguard Worker            service['host_fullname'] = lines.pop(0).strip().split(':')[1].strip()
1244*cfb92d14SAndroid Build Coastguard Worker            service['host'] = service['host_fullname'].split('.')[0]
1245*cfb92d14SAndroid Build Coastguard Worker
1246*cfb92d14SAndroid Build Coastguard Worker            addresses = lines.pop(0).strip().split('[')[1].strip(' ]').split(',')
1247*cfb92d14SAndroid Build Coastguard Worker            addresses = map(str.strip, addresses)
1248*cfb92d14SAndroid Build Coastguard Worker            service['addresses'] = [addr for addr in addresses if addr]
1249*cfb92d14SAndroid Build Coastguard Worker
1250*cfb92d14SAndroid Build Coastguard Worker            service_list.append(service)
1251*cfb92d14SAndroid Build Coastguard Worker
1252*cfb92d14SAndroid Build Coastguard Worker        return service_list
1253*cfb92d14SAndroid Build Coastguard Worker
1254*cfb92d14SAndroid Build Coastguard Worker    def srp_server_get_service(self, instance_name, service_name):
1255*cfb92d14SAndroid Build Coastguard Worker        """Returns service on the SRP server that matches given instance
1256*cfb92d14SAndroid Build Coastguard Worker           name and service name.
1257*cfb92d14SAndroid Build Coastguard Worker
1258*cfb92d14SAndroid Build Coastguard Worker           Example usage:
1259*cfb92d14SAndroid Build Coastguard Worker           self.srp_server_get_service("my-service", "_ipps._tcp")
1260*cfb92d14SAndroid Build Coastguard Worker        """
1261*cfb92d14SAndroid Build Coastguard Worker
1262*cfb92d14SAndroid Build Coastguard Worker        for service in self.srp_server_get_services():
1263*cfb92d14SAndroid Build Coastguard Worker            if (instance_name == service['instance'] and service_name == service['name']):
1264*cfb92d14SAndroid Build Coastguard Worker                return service
1265*cfb92d14SAndroid Build Coastguard Worker
1266*cfb92d14SAndroid Build Coastguard Worker    def get_srp_server_port(self):
1267*cfb92d14SAndroid Build Coastguard Worker        """Returns the SRP server UDP port by parsing
1268*cfb92d14SAndroid Build Coastguard Worker           the SRP Server Data in Network Data.
1269*cfb92d14SAndroid Build Coastguard Worker        """
1270*cfb92d14SAndroid Build Coastguard Worker
1271*cfb92d14SAndroid Build Coastguard Worker        for service in self.get_services():
1272*cfb92d14SAndroid Build Coastguard Worker            # TODO: for now, we are using 0xfd as the SRP service data.
1273*cfb92d14SAndroid Build Coastguard Worker            #       May use a dedicated bit flag for SRP server.
1274*cfb92d14SAndroid Build Coastguard Worker            if int(service[1], 16) == 0x5d:
1275*cfb92d14SAndroid Build Coastguard Worker                # The SRP server data contains IPv6 address (16 bytes)
1276*cfb92d14SAndroid Build Coastguard Worker                # followed by UDP port number.
1277*cfb92d14SAndroid Build Coastguard Worker                return int(service[2][2 * 16:], 16)
1278*cfb92d14SAndroid Build Coastguard Worker
1279*cfb92d14SAndroid Build Coastguard Worker    def srp_client_start(self, server_address, server_port):
1280*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client start {server_address} {server_port}')
1281*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1282*cfb92d14SAndroid Build Coastguard Worker
1283*cfb92d14SAndroid Build Coastguard Worker    def srp_client_stop(self):
1284*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client stop')
1285*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1286*cfb92d14SAndroid Build Coastguard Worker
1287*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_state(self):
1288*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client state'
1289*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1290*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
1291*cfb92d14SAndroid Build Coastguard Worker
1292*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_auto_start_mode(self):
1293*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client autostart'
1294*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1295*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
1296*cfb92d14SAndroid Build Coastguard Worker
1297*cfb92d14SAndroid Build Coastguard Worker    def srp_client_enable_auto_start_mode(self):
1298*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client autostart enable')
1299*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1300*cfb92d14SAndroid Build Coastguard Worker
1301*cfb92d14SAndroid Build Coastguard Worker    def srp_client_disable_auto_start_mode(self):
1302*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client autostart disable')
1303*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1304*cfb92d14SAndroid Build Coastguard Worker
1305*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_server_address(self):
1306*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client server address'
1307*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1308*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
1309*cfb92d14SAndroid Build Coastguard Worker
1310*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_server_port(self):
1311*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client server port'
1312*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1313*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_command_output()[0])
1314*cfb92d14SAndroid Build Coastguard Worker
1315*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_host_state(self):
1316*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client host state'
1317*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1318*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
1319*cfb92d14SAndroid Build Coastguard Worker
1320*cfb92d14SAndroid Build Coastguard Worker    def srp_client_set_host_name(self, name):
1321*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host name {name}')
1322*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1323*cfb92d14SAndroid Build Coastguard Worker
1324*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_host_name(self):
1325*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host name')
1326*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1327*cfb92d14SAndroid Build Coastguard Worker
1328*cfb92d14SAndroid Build Coastguard Worker    def srp_client_remove_host(self, remove_key=False, send_unreg_to_server=False):
1329*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host remove {int(remove_key)} {int(send_unreg_to_server)}')
1330*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1331*cfb92d14SAndroid Build Coastguard Worker
1332*cfb92d14SAndroid Build Coastguard Worker    def srp_client_clear_host(self):
1333*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host clear')
1334*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1335*cfb92d14SAndroid Build Coastguard Worker
1336*cfb92d14SAndroid Build Coastguard Worker    def srp_client_enable_auto_host_address(self):
1337*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host address auto')
1338*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1339*cfb92d14SAndroid Build Coastguard Worker
1340*cfb92d14SAndroid Build Coastguard Worker    def srp_client_set_host_address(self, *addrs: str):
1341*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host address {" ".join(addrs)}')
1342*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1343*cfb92d14SAndroid Build Coastguard Worker
1344*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_host_address(self):
1345*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client host address')
1346*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1347*cfb92d14SAndroid Build Coastguard Worker
1348*cfb92d14SAndroid Build Coastguard Worker    def srp_client_add_service(self,
1349*cfb92d14SAndroid Build Coastguard Worker                               instance_name,
1350*cfb92d14SAndroid Build Coastguard Worker                               service_name,
1351*cfb92d14SAndroid Build Coastguard Worker                               port,
1352*cfb92d14SAndroid Build Coastguard Worker                               priority=0,
1353*cfb92d14SAndroid Build Coastguard Worker                               weight=0,
1354*cfb92d14SAndroid Build Coastguard Worker                               txt_entries=[],
1355*cfb92d14SAndroid Build Coastguard Worker                               lease=0,
1356*cfb92d14SAndroid Build Coastguard Worker                               key_lease=0):
1357*cfb92d14SAndroid Build Coastguard Worker        txt_record = "".join(self._encode_txt_entry(entry) for entry in txt_entries)
1358*cfb92d14SAndroid Build Coastguard Worker        if txt_record == '':
1359*cfb92d14SAndroid Build Coastguard Worker            txt_record = '-'
1360*cfb92d14SAndroid Build Coastguard Worker        instance_name = self._escape_escapable(instance_name)
1361*cfb92d14SAndroid Build Coastguard Worker        self.send_command(
1362*cfb92d14SAndroid Build Coastguard Worker            f'srp client service add {instance_name} {service_name} {port} {priority} {weight} {txt_record} {lease} {key_lease}'
1363*cfb92d14SAndroid Build Coastguard Worker        )
1364*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1365*cfb92d14SAndroid Build Coastguard Worker
1366*cfb92d14SAndroid Build Coastguard Worker    def srp_client_remove_service(self, instance_name, service_name):
1367*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client service remove {instance_name} {service_name}')
1368*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1369*cfb92d14SAndroid Build Coastguard Worker
1370*cfb92d14SAndroid Build Coastguard Worker    def srp_client_clear_service(self, instance_name, service_name):
1371*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'srp client service clear {instance_name} {service_name}')
1372*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1373*cfb92d14SAndroid Build Coastguard Worker
1374*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_services(self):
1375*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client service'
1376*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1377*cfb92d14SAndroid Build Coastguard Worker        service_lines = self._expect_command_output()
1378*cfb92d14SAndroid Build Coastguard Worker        return [self._parse_srp_client_service(line) for line in service_lines]
1379*cfb92d14SAndroid Build Coastguard Worker
1380*cfb92d14SAndroid Build Coastguard Worker    def srp_client_set_lease_interval(self, leaseinterval: int):
1381*cfb92d14SAndroid Build Coastguard Worker        cmd = f'srp client leaseinterval {leaseinterval}'
1382*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1383*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1384*cfb92d14SAndroid Build Coastguard Worker
1385*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_lease_interval(self) -> int:
1386*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client leaseinterval'
1387*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1388*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result('\d+'))
1389*cfb92d14SAndroid Build Coastguard Worker
1390*cfb92d14SAndroid Build Coastguard Worker    def srp_client_set_key_lease_interval(self, leaseinterval: int):
1391*cfb92d14SAndroid Build Coastguard Worker        cmd = f'srp client keyleaseinterval {leaseinterval}'
1392*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1393*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1394*cfb92d14SAndroid Build Coastguard Worker
1395*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_key_lease_interval(self) -> int:
1396*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client keyleaseinterval'
1397*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1398*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result('\d+'))
1399*cfb92d14SAndroid Build Coastguard Worker
1400*cfb92d14SAndroid Build Coastguard Worker    def srp_client_set_ttl(self, ttl: int):
1401*cfb92d14SAndroid Build Coastguard Worker        cmd = f'srp client ttl {ttl}'
1402*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1403*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1404*cfb92d14SAndroid Build Coastguard Worker
1405*cfb92d14SAndroid Build Coastguard Worker    def srp_client_get_ttl(self) -> int:
1406*cfb92d14SAndroid Build Coastguard Worker        cmd = 'srp client ttl'
1407*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1408*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result('\d+'))
1409*cfb92d14SAndroid Build Coastguard Worker
1410*cfb92d14SAndroid Build Coastguard Worker    #
1411*cfb92d14SAndroid Build Coastguard Worker    # TREL utilities
1412*cfb92d14SAndroid Build Coastguard Worker    #
1413*cfb92d14SAndroid Build Coastguard Worker
1414*cfb92d14SAndroid Build Coastguard Worker    def enable_trel(self):
1415*cfb92d14SAndroid Build Coastguard Worker        cmd = 'trel enable'
1416*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1417*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1418*cfb92d14SAndroid Build Coastguard Worker
1419*cfb92d14SAndroid Build Coastguard Worker    def is_trel_enabled(self) -> Union[None, bool]:
1420*cfb92d14SAndroid Build Coastguard Worker        states = [r'Disabled', r'Enabled']
1421*cfb92d14SAndroid Build Coastguard Worker        self.send_command('trel')
1422*cfb92d14SAndroid Build Coastguard Worker        try:
1423*cfb92d14SAndroid Build Coastguard Worker            return self._expect_result(states) == 'Enabled'
1424*cfb92d14SAndroid Build Coastguard Worker        except Exception as ex:
1425*cfb92d14SAndroid Build Coastguard Worker            if 'InvalidCommand' in str(ex):
1426*cfb92d14SAndroid Build Coastguard Worker                return None
1427*cfb92d14SAndroid Build Coastguard Worker
1428*cfb92d14SAndroid Build Coastguard Worker            raise
1429*cfb92d14SAndroid Build Coastguard Worker
1430*cfb92d14SAndroid Build Coastguard Worker    def get_trel_counters(self):
1431*cfb92d14SAndroid Build Coastguard Worker        cmd = 'trel counters'
1432*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1433*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_command_output()
1434*cfb92d14SAndroid Build Coastguard Worker
1435*cfb92d14SAndroid Build Coastguard Worker        counters = {}
1436*cfb92d14SAndroid Build Coastguard Worker        for line in result:
1437*cfb92d14SAndroid Build Coastguard Worker            m = re.match(r'(\w+)\:[^\d]+(\d+)[^\d]+(\d+)(?:[^\d]+(\d+))?', line)
1438*cfb92d14SAndroid Build Coastguard Worker            if m:
1439*cfb92d14SAndroid Build Coastguard Worker                groups = m.groups()
1440*cfb92d14SAndroid Build Coastguard Worker                sub_counters = {
1441*cfb92d14SAndroid Build Coastguard Worker                    'packets': int(groups[1]),
1442*cfb92d14SAndroid Build Coastguard Worker                    'bytes': int(groups[2]),
1443*cfb92d14SAndroid Build Coastguard Worker                }
1444*cfb92d14SAndroid Build Coastguard Worker                if groups[3]:
1445*cfb92d14SAndroid Build Coastguard Worker                    sub_counters['failures'] = int(groups[3])
1446*cfb92d14SAndroid Build Coastguard Worker                counters[groups[0]] = sub_counters
1447*cfb92d14SAndroid Build Coastguard Worker        return counters
1448*cfb92d14SAndroid Build Coastguard Worker
1449*cfb92d14SAndroid Build Coastguard Worker    def reset_trel_counters(self):
1450*cfb92d14SAndroid Build Coastguard Worker        cmd = 'trel counters reset'
1451*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1452*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1453*cfb92d14SAndroid Build Coastguard Worker
1454*cfb92d14SAndroid Build Coastguard Worker    def get_trel_port(self):
1455*cfb92d14SAndroid Build Coastguard Worker        cmd = 'trel port'
1456*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1457*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_command_output()[0])
1458*cfb92d14SAndroid Build Coastguard Worker
1459*cfb92d14SAndroid Build Coastguard Worker    def set_epskc(self, keystring: str, timeout=120000, port=0):
1460*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ba ephemeralkey set ' + keystring + ' ' + str(timeout) + ' ' + str(port)
1461*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1462*cfb92d14SAndroid Build Coastguard Worker        self._expect(r"(Done|Error .*)")
1463*cfb92d14SAndroid Build Coastguard Worker
1464*cfb92d14SAndroid Build Coastguard Worker    def clear_epskc(self):
1465*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ba ephemeralkey clear'
1466*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1467*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1468*cfb92d14SAndroid Build Coastguard Worker
1469*cfb92d14SAndroid Build Coastguard Worker    def get_border_agent_counters(self):
1470*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ba counters'
1471*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1472*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_command_output()
1473*cfb92d14SAndroid Build Coastguard Worker
1474*cfb92d14SAndroid Build Coastguard Worker        counters = {}
1475*cfb92d14SAndroid Build Coastguard Worker        for line in result:
1476*cfb92d14SAndroid Build Coastguard Worker            m = re.match(r'(\w+)\: (\d+)', line)
1477*cfb92d14SAndroid Build Coastguard Worker            if m:
1478*cfb92d14SAndroid Build Coastguard Worker                counter_name = m.group(1)
1479*cfb92d14SAndroid Build Coastguard Worker                counter_value = m.group(2)
1480*cfb92d14SAndroid Build Coastguard Worker
1481*cfb92d14SAndroid Build Coastguard Worker                counters[counter_name] = int(counter_value)
1482*cfb92d14SAndroid Build Coastguard Worker        return counters
1483*cfb92d14SAndroid Build Coastguard Worker
1484*cfb92d14SAndroid Build Coastguard Worker    def _encode_txt_entry(self, entry):
1485*cfb92d14SAndroid Build Coastguard Worker        """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string.
1486*cfb92d14SAndroid Build Coastguard Worker
1487*cfb92d14SAndroid Build Coastguard Worker           Example usage:
1488*cfb92d14SAndroid Build Coastguard Worker           self._encode_txt_entries(['abc'])     -> '03616263'
1489*cfb92d14SAndroid Build Coastguard Worker           self._encode_txt_entries(['def='])    -> '046465663d'
1490*cfb92d14SAndroid Build Coastguard Worker           self._encode_txt_entries(['xyz=XYZ']) -> '0778797a3d58595a'
1491*cfb92d14SAndroid Build Coastguard Worker        """
1492*cfb92d14SAndroid Build Coastguard Worker        return '{:02x}'.format(len(entry)) + "".join("{:02x}".format(ord(c)) for c in entry)
1493*cfb92d14SAndroid Build Coastguard Worker
1494*cfb92d14SAndroid Build Coastguard Worker    def _parse_srp_client_service(self, line: str):
1495*cfb92d14SAndroid Build Coastguard Worker        """Parse one line of srp service list into a dictionary which
1496*cfb92d14SAndroid Build Coastguard Worker           maps string keys to string values.
1497*cfb92d14SAndroid Build Coastguard Worker
1498*cfb92d14SAndroid Build Coastguard Worker           Example output for input
1499*cfb92d14SAndroid Build Coastguard Worker           'instance:\"%s\", name:\"%s\", state:%s, port:%d, priority:%d, weight:%d"'
1500*cfb92d14SAndroid Build Coastguard Worker           {
1501*cfb92d14SAndroid Build Coastguard Worker               'instance': 'my-service',
1502*cfb92d14SAndroid Build Coastguard Worker               'name': '_ipps._udp',
1503*cfb92d14SAndroid Build Coastguard Worker               'state': 'ToAdd',
1504*cfb92d14SAndroid Build Coastguard Worker               'port': '12345',
1505*cfb92d14SAndroid Build Coastguard Worker               'priority': '0',
1506*cfb92d14SAndroid Build Coastguard Worker               'weight': '0'
1507*cfb92d14SAndroid Build Coastguard Worker           }
1508*cfb92d14SAndroid Build Coastguard Worker
1509*cfb92d14SAndroid Build Coastguard Worker           Note that value of 'port', 'priority' and 'weight' are represented
1510*cfb92d14SAndroid Build Coastguard Worker           as strings but not integers.
1511*cfb92d14SAndroid Build Coastguard Worker        """
1512*cfb92d14SAndroid Build Coastguard Worker        key_values = [word.strip().split(':') for word in line.split(', ')]
1513*cfb92d14SAndroid Build Coastguard Worker        keys = [key_value[0] for key_value in key_values]
1514*cfb92d14SAndroid Build Coastguard Worker        values = [key_value[1].strip('"') for key_value in key_values]
1515*cfb92d14SAndroid Build Coastguard Worker        return dict(zip(keys, values))
1516*cfb92d14SAndroid Build Coastguard Worker
1517*cfb92d14SAndroid Build Coastguard Worker    def locate(self, anycast_addr):
1518*cfb92d14SAndroid Build Coastguard Worker        cmd = 'locate ' + anycast_addr
1519*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1520*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(5)
1521*cfb92d14SAndroid Build Coastguard Worker        return self._parse_locate_result(self._expect_command_output()[0])
1522*cfb92d14SAndroid Build Coastguard Worker
1523*cfb92d14SAndroid Build Coastguard Worker    def _parse_locate_result(self, line: str):
1524*cfb92d14SAndroid Build Coastguard Worker        """Parse anycast locate result as list of ml-eid and rloc16.
1525*cfb92d14SAndroid Build Coastguard Worker
1526*cfb92d14SAndroid Build Coastguard Worker           Example output for input
1527*cfb92d14SAndroid Build Coastguard Worker           'fd00:db8:0:0:acf9:9d0:7f3c:b06e 0xa800'
1528*cfb92d14SAndroid Build Coastguard Worker
1529*cfb92d14SAndroid Build Coastguard Worker           [ 'fd00:db8:0:0:acf9:9d0:7f3c:b06e', '0xa800' ]
1530*cfb92d14SAndroid Build Coastguard Worker        """
1531*cfb92d14SAndroid Build Coastguard Worker        return line.split(' ')
1532*cfb92d14SAndroid Build Coastguard Worker
1533*cfb92d14SAndroid Build Coastguard Worker    def enable_backbone_router(self):
1534*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr enable'
1535*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1536*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1537*cfb92d14SAndroid Build Coastguard Worker
1538*cfb92d14SAndroid Build Coastguard Worker    def disable_backbone_router(self):
1539*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr disable'
1540*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1541*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1542*cfb92d14SAndroid Build Coastguard Worker
1543*cfb92d14SAndroid Build Coastguard Worker    def register_backbone_router(self):
1544*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr register'
1545*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1546*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1547*cfb92d14SAndroid Build Coastguard Worker
1548*cfb92d14SAndroid Build Coastguard Worker    def get_backbone_router_state(self):
1549*cfb92d14SAndroid Build Coastguard Worker        states = [r'Disabled', r'Primary', r'Secondary']
1550*cfb92d14SAndroid Build Coastguard Worker        self.send_command('bbr state')
1551*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states)
1552*cfb92d14SAndroid Build Coastguard Worker
1553*cfb92d14SAndroid Build Coastguard Worker    @property
1554*cfb92d14SAndroid Build Coastguard Worker    def is_primary_backbone_router(self) -> bool:
1555*cfb92d14SAndroid Build Coastguard Worker        return self.get_backbone_router_state() == 'Primary'
1556*cfb92d14SAndroid Build Coastguard Worker
1557*cfb92d14SAndroid Build Coastguard Worker    def get_backbone_router(self):
1558*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr config'
1559*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1560*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'(.*)Done')
1561*cfb92d14SAndroid Build Coastguard Worker        g = self.pexpect.match.groups()
1562*cfb92d14SAndroid Build Coastguard Worker        output = g[0].decode("utf-8")
1563*cfb92d14SAndroid Build Coastguard Worker        lines = output.strip().split('\n')
1564*cfb92d14SAndroid Build Coastguard Worker        lines = [l.strip() for l in lines]
1565*cfb92d14SAndroid Build Coastguard Worker        ret = {}
1566*cfb92d14SAndroid Build Coastguard Worker        for l in lines:
1567*cfb92d14SAndroid Build Coastguard Worker            z = re.search(r'seqno:\s+([0-9]+)', l)
1568*cfb92d14SAndroid Build Coastguard Worker            if z:
1569*cfb92d14SAndroid Build Coastguard Worker                ret['seqno'] = int(z.groups()[0])
1570*cfb92d14SAndroid Build Coastguard Worker
1571*cfb92d14SAndroid Build Coastguard Worker            z = re.search(r'delay:\s+([0-9]+)', l)
1572*cfb92d14SAndroid Build Coastguard Worker            if z:
1573*cfb92d14SAndroid Build Coastguard Worker                ret['delay'] = int(z.groups()[0])
1574*cfb92d14SAndroid Build Coastguard Worker
1575*cfb92d14SAndroid Build Coastguard Worker            z = re.search(r'timeout:\s+([0-9]+)', l)
1576*cfb92d14SAndroid Build Coastguard Worker            if z:
1577*cfb92d14SAndroid Build Coastguard Worker                ret['timeout'] = int(z.groups()[0])
1578*cfb92d14SAndroid Build Coastguard Worker
1579*cfb92d14SAndroid Build Coastguard Worker        return ret
1580*cfb92d14SAndroid Build Coastguard Worker
1581*cfb92d14SAndroid Build Coastguard Worker    def set_backbone_router(self, seqno=None, reg_delay=None, mlr_timeout=None):
1582*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr config'
1583*cfb92d14SAndroid Build Coastguard Worker
1584*cfb92d14SAndroid Build Coastguard Worker        if seqno is not None:
1585*cfb92d14SAndroid Build Coastguard Worker            cmd += ' seqno %d' % seqno
1586*cfb92d14SAndroid Build Coastguard Worker
1587*cfb92d14SAndroid Build Coastguard Worker        if reg_delay is not None:
1588*cfb92d14SAndroid Build Coastguard Worker            cmd += ' delay %d' % reg_delay
1589*cfb92d14SAndroid Build Coastguard Worker
1590*cfb92d14SAndroid Build Coastguard Worker        if mlr_timeout is not None:
1591*cfb92d14SAndroid Build Coastguard Worker            cmd += ' timeout %d' % mlr_timeout
1592*cfb92d14SAndroid Build Coastguard Worker
1593*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1594*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1595*cfb92d14SAndroid Build Coastguard Worker
1596*cfb92d14SAndroid Build Coastguard Worker    def set_domain_prefix(self, prefix, flags='prosD'):
1597*cfb92d14SAndroid Build Coastguard Worker        self.add_prefix(prefix, flags)
1598*cfb92d14SAndroid Build Coastguard Worker        self.register_netdata()
1599*cfb92d14SAndroid Build Coastguard Worker
1600*cfb92d14SAndroid Build Coastguard Worker    def remove_domain_prefix(self, prefix):
1601*cfb92d14SAndroid Build Coastguard Worker        self.remove_prefix(prefix)
1602*cfb92d14SAndroid Build Coastguard Worker        self.register_netdata()
1603*cfb92d14SAndroid Build Coastguard Worker
1604*cfb92d14SAndroid Build Coastguard Worker    def set_next_dua_response(self, status: Union[str, int], iid=None):
1605*cfb92d14SAndroid Build Coastguard Worker        # Convert 5.00 to COAP CODE 160
1606*cfb92d14SAndroid Build Coastguard Worker        if isinstance(status, str):
1607*cfb92d14SAndroid Build Coastguard Worker            assert '.' in status
1608*cfb92d14SAndroid Build Coastguard Worker            status = status.split('.')
1609*cfb92d14SAndroid Build Coastguard Worker            status = (int(status[0]) << 5) + int(status[1])
1610*cfb92d14SAndroid Build Coastguard Worker
1611*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr mgmt dua {}'.format(status)
1612*cfb92d14SAndroid Build Coastguard Worker        if iid is not None:
1613*cfb92d14SAndroid Build Coastguard Worker            cmd += ' ' + str(iid)
1614*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1615*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1616*cfb92d14SAndroid Build Coastguard Worker
1617*cfb92d14SAndroid Build Coastguard Worker    def set_dua_iid(self, iid: str):
1618*cfb92d14SAndroid Build Coastguard Worker        assert len(iid) == 16
1619*cfb92d14SAndroid Build Coastguard Worker        int(iid, 16)
1620*cfb92d14SAndroid Build Coastguard Worker
1621*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dua iid {}'.format(iid)
1622*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1623*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1624*cfb92d14SAndroid Build Coastguard Worker
1625*cfb92d14SAndroid Build Coastguard Worker    def clear_dua_iid(self):
1626*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dua iid clear'
1627*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1628*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1629*cfb92d14SAndroid Build Coastguard Worker
1630*cfb92d14SAndroid Build Coastguard Worker    def multicast_listener_list(self) -> Dict[IPv6Address, int]:
1631*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr mgmt mlr listener'
1632*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1633*cfb92d14SAndroid Build Coastguard Worker
1634*cfb92d14SAndroid Build Coastguard Worker        table = {}
1635*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_results("\S+ \d+"):
1636*cfb92d14SAndroid Build Coastguard Worker            line = line.split()
1637*cfb92d14SAndroid Build Coastguard Worker            assert len(line) == 2, line
1638*cfb92d14SAndroid Build Coastguard Worker            ip = IPv6Address(line[0])
1639*cfb92d14SAndroid Build Coastguard Worker            timeout = int(line[1])
1640*cfb92d14SAndroid Build Coastguard Worker            assert ip not in table
1641*cfb92d14SAndroid Build Coastguard Worker
1642*cfb92d14SAndroid Build Coastguard Worker            table[ip] = timeout
1643*cfb92d14SAndroid Build Coastguard Worker
1644*cfb92d14SAndroid Build Coastguard Worker        return table
1645*cfb92d14SAndroid Build Coastguard Worker
1646*cfb92d14SAndroid Build Coastguard Worker    def multicast_listener_clear(self):
1647*cfb92d14SAndroid Build Coastguard Worker        cmd = f'bbr mgmt mlr listener clear'
1648*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1649*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1650*cfb92d14SAndroid Build Coastguard Worker
1651*cfb92d14SAndroid Build Coastguard Worker    def multicast_listener_add(self, ip: Union[IPv6Address, str], timeout: int = 0):
1652*cfb92d14SAndroid Build Coastguard Worker        if not isinstance(ip, IPv6Address):
1653*cfb92d14SAndroid Build Coastguard Worker            ip = IPv6Address(ip)
1654*cfb92d14SAndroid Build Coastguard Worker
1655*cfb92d14SAndroid Build Coastguard Worker        cmd = f'bbr mgmt mlr listener add {ip.compressed} {timeout}'
1656*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1657*cfb92d14SAndroid Build Coastguard Worker        self._expect(r"(Done|Error .*)")
1658*cfb92d14SAndroid Build Coastguard Worker
1659*cfb92d14SAndroid Build Coastguard Worker    def set_next_mlr_response(self, status: int):
1660*cfb92d14SAndroid Build Coastguard Worker        cmd = 'bbr mgmt mlr response {}'.format(status)
1661*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1662*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1663*cfb92d14SAndroid Build Coastguard Worker
1664*cfb92d14SAndroid Build Coastguard Worker    def register_multicast_listener(self, *ipaddrs: Union[IPv6Address, str], timeout=None):
1665*cfb92d14SAndroid Build Coastguard Worker        assert len(ipaddrs) > 0, ipaddrs
1666*cfb92d14SAndroid Build Coastguard Worker
1667*cfb92d14SAndroid Build Coastguard Worker        ipaddrs = map(str, ipaddrs)
1668*cfb92d14SAndroid Build Coastguard Worker        cmd = f'mlr reg {" ".join(ipaddrs)}'
1669*cfb92d14SAndroid Build Coastguard Worker        if timeout is not None:
1670*cfb92d14SAndroid Build Coastguard Worker            cmd += f' {int(timeout)}'
1671*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1672*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(3)
1673*cfb92d14SAndroid Build Coastguard Worker        lines = self._expect_command_output()
1674*cfb92d14SAndroid Build Coastguard Worker        m = re.match(r'status (\d+), (\d+) failed', lines[0])
1675*cfb92d14SAndroid Build Coastguard Worker        assert m is not None, lines
1676*cfb92d14SAndroid Build Coastguard Worker        status = int(m.group(1))
1677*cfb92d14SAndroid Build Coastguard Worker        failed_num = int(m.group(2))
1678*cfb92d14SAndroid Build Coastguard Worker        assert failed_num == len(lines) - 1
1679*cfb92d14SAndroid Build Coastguard Worker        failed_ips = list(map(IPv6Address, lines[1:]))
1680*cfb92d14SAndroid Build Coastguard Worker        print(f"register_multicast_listener {ipaddrs} => status: {status}, failed ips: {failed_ips}")
1681*cfb92d14SAndroid Build Coastguard Worker        return status, failed_ips
1682*cfb92d14SAndroid Build Coastguard Worker
1683*cfb92d14SAndroid Build Coastguard Worker    def set_link_quality(self, addr, lqi):
1684*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
1685*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1686*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1687*cfb92d14SAndroid Build Coastguard Worker
1688*cfb92d14SAndroid Build Coastguard Worker    def set_outbound_link_quality(self, lqi):
1689*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter rss add-lqi * %s' % (lqi)
1690*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1691*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1692*cfb92d14SAndroid Build Coastguard Worker
1693*cfb92d14SAndroid Build Coastguard Worker    def remove_allowlist(self, addr):
1694*cfb92d14SAndroid Build Coastguard Worker        cmd = 'macfilter addr remove %s' % addr
1695*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1696*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1697*cfb92d14SAndroid Build Coastguard Worker
1698*cfb92d14SAndroid Build Coastguard Worker    def get_addr16(self):
1699*cfb92d14SAndroid Build Coastguard Worker        self.send_command('rloc16')
1700*cfb92d14SAndroid Build Coastguard Worker        rloc16 = self._expect_result(r'[0-9a-fA-F]{4}')
1701*cfb92d14SAndroid Build Coastguard Worker        return int(rloc16, 16)
1702*cfb92d14SAndroid Build Coastguard Worker
1703*cfb92d14SAndroid Build Coastguard Worker    def get_router_id(self):
1704*cfb92d14SAndroid Build Coastguard Worker        rloc16 = self.get_addr16()
1705*cfb92d14SAndroid Build Coastguard Worker        return rloc16 >> 10
1706*cfb92d14SAndroid Build Coastguard Worker
1707*cfb92d14SAndroid Build Coastguard Worker    def get_addr64(self):
1708*cfb92d14SAndroid Build Coastguard Worker        self.send_command('extaddr')
1709*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result('[0-9a-fA-F]{16}')
1710*cfb92d14SAndroid Build Coastguard Worker
1711*cfb92d14SAndroid Build Coastguard Worker    def set_addr64(self, addr64: str):
1712*cfb92d14SAndroid Build Coastguard Worker        # Make sure `addr64` is a hex string of length 16
1713*cfb92d14SAndroid Build Coastguard Worker        assert len(addr64) == 16
1714*cfb92d14SAndroid Build Coastguard Worker        int(addr64, 16)
1715*cfb92d14SAndroid Build Coastguard Worker        self.send_command('extaddr %s' % addr64)
1716*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1717*cfb92d14SAndroid Build Coastguard Worker
1718*cfb92d14SAndroid Build Coastguard Worker    def get_eui64(self):
1719*cfb92d14SAndroid Build Coastguard Worker        self.send_command('eui64')
1720*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result('[0-9a-fA-F]{16}')
1721*cfb92d14SAndroid Build Coastguard Worker
1722*cfb92d14SAndroid Build Coastguard Worker    def set_extpanid(self, extpanid):
1723*cfb92d14SAndroid Build Coastguard Worker        self.send_command('extpanid %s' % extpanid)
1724*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1725*cfb92d14SAndroid Build Coastguard Worker
1726*cfb92d14SAndroid Build Coastguard Worker    def get_extpanid(self):
1727*cfb92d14SAndroid Build Coastguard Worker        self.send_command('extpanid')
1728*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result('[0-9a-fA-F]{16}')
1729*cfb92d14SAndroid Build Coastguard Worker
1730*cfb92d14SAndroid Build Coastguard Worker    def get_mesh_local_prefix(self):
1731*cfb92d14SAndroid Build Coastguard Worker        self.send_command('prefix meshlocal')
1732*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
1733*cfb92d14SAndroid Build Coastguard Worker
1734*cfb92d14SAndroid Build Coastguard Worker    def set_mesh_local_prefix(self, mesh_local_prefix):
1735*cfb92d14SAndroid Build Coastguard Worker        self.send_command('prefix meshlocal %s' % mesh_local_prefix)
1736*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1737*cfb92d14SAndroid Build Coastguard Worker
1738*cfb92d14SAndroid Build Coastguard Worker    def get_joiner_id(self):
1739*cfb92d14SAndroid Build Coastguard Worker        self.send_command('joiner id')
1740*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result('[0-9a-fA-F]{16}')
1741*cfb92d14SAndroid Build Coastguard Worker
1742*cfb92d14SAndroid Build Coastguard Worker    def get_channel(self):
1743*cfb92d14SAndroid Build Coastguard Worker        self.send_command('channel')
1744*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result(r'\d+'))
1745*cfb92d14SAndroid Build Coastguard Worker
1746*cfb92d14SAndroid Build Coastguard Worker    def set_channel(self, channel):
1747*cfb92d14SAndroid Build Coastguard Worker        cmd = 'channel %d' % channel
1748*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1749*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1750*cfb92d14SAndroid Build Coastguard Worker
1751*cfb92d14SAndroid Build Coastguard Worker    def get_networkkey(self):
1752*cfb92d14SAndroid Build Coastguard Worker        self.send_command('networkkey')
1753*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result('[0-9a-fA-F]{32}')
1754*cfb92d14SAndroid Build Coastguard Worker
1755*cfb92d14SAndroid Build Coastguard Worker    def set_networkkey(self, networkkey):
1756*cfb92d14SAndroid Build Coastguard Worker        cmd = 'networkkey %s' % networkkey
1757*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1758*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1759*cfb92d14SAndroid Build Coastguard Worker
1760*cfb92d14SAndroid Build Coastguard Worker    def get_key_sequence_counter(self):
1761*cfb92d14SAndroid Build Coastguard Worker        self.send_command('keysequence counter')
1762*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_result(r'\d+')
1763*cfb92d14SAndroid Build Coastguard Worker        return int(result)
1764*cfb92d14SAndroid Build Coastguard Worker
1765*cfb92d14SAndroid Build Coastguard Worker    def set_key_sequence_counter(self, key_sequence_counter):
1766*cfb92d14SAndroid Build Coastguard Worker        cmd = 'keysequence counter %d' % key_sequence_counter
1767*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1768*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1769*cfb92d14SAndroid Build Coastguard Worker
1770*cfb92d14SAndroid Build Coastguard Worker    def get_key_switch_guardtime(self):
1771*cfb92d14SAndroid Build Coastguard Worker        self.send_command('keysequence guardtime')
1772*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result(r'\d+'))
1773*cfb92d14SAndroid Build Coastguard Worker
1774*cfb92d14SAndroid Build Coastguard Worker    def set_key_switch_guardtime(self, key_switch_guardtime):
1775*cfb92d14SAndroid Build Coastguard Worker        cmd = 'keysequence guardtime %d' % key_switch_guardtime
1776*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1777*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1778*cfb92d14SAndroid Build Coastguard Worker
1779*cfb92d14SAndroid Build Coastguard Worker    def set_network_id_timeout(self, network_id_timeout):
1780*cfb92d14SAndroid Build Coastguard Worker        cmd = 'networkidtimeout %d' % network_id_timeout
1781*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1782*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1783*cfb92d14SAndroid Build Coastguard Worker
1784*cfb92d14SAndroid Build Coastguard Worker    def _escape_escapable(self, string):
1785*cfb92d14SAndroid Build Coastguard Worker        """Escape CLI escapable characters in the given string.
1786*cfb92d14SAndroid Build Coastguard Worker
1787*cfb92d14SAndroid Build Coastguard Worker        Args:
1788*cfb92d14SAndroid Build Coastguard Worker            string (str): UTF-8 input string.
1789*cfb92d14SAndroid Build Coastguard Worker
1790*cfb92d14SAndroid Build Coastguard Worker        Returns:
1791*cfb92d14SAndroid Build Coastguard Worker            [str]: The modified string with escaped characters.
1792*cfb92d14SAndroid Build Coastguard Worker        """
1793*cfb92d14SAndroid Build Coastguard Worker        escapable_chars = '\\ \t\r\n'
1794*cfb92d14SAndroid Build Coastguard Worker        for char in escapable_chars:
1795*cfb92d14SAndroid Build Coastguard Worker            string = string.replace(char, '\\%s' % char)
1796*cfb92d14SAndroid Build Coastguard Worker        return string
1797*cfb92d14SAndroid Build Coastguard Worker
1798*cfb92d14SAndroid Build Coastguard Worker    def get_network_name(self):
1799*cfb92d14SAndroid Build Coastguard Worker        self.send_command('networkname')
1800*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result([r'\S+'])
1801*cfb92d14SAndroid Build Coastguard Worker
1802*cfb92d14SAndroid Build Coastguard Worker    def set_network_name(self, network_name):
1803*cfb92d14SAndroid Build Coastguard Worker        cmd = 'networkname %s' % self._escape_escapable(network_name)
1804*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1805*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1806*cfb92d14SAndroid Build Coastguard Worker
1807*cfb92d14SAndroid Build Coastguard Worker    def get_panid(self):
1808*cfb92d14SAndroid Build Coastguard Worker        self.send_command('panid')
1809*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_result('0x[0-9a-fA-F]{4}')
1810*cfb92d14SAndroid Build Coastguard Worker        return int(result, 16)
1811*cfb92d14SAndroid Build Coastguard Worker
1812*cfb92d14SAndroid Build Coastguard Worker    def set_panid(self, panid=config.PANID):
1813*cfb92d14SAndroid Build Coastguard Worker        cmd = 'panid %d' % panid
1814*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1815*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1816*cfb92d14SAndroid Build Coastguard Worker
1817*cfb92d14SAndroid Build Coastguard Worker    def set_parent_priority(self, priority):
1818*cfb92d14SAndroid Build Coastguard Worker        cmd = 'parentpriority %d' % priority
1819*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1820*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1821*cfb92d14SAndroid Build Coastguard Worker
1822*cfb92d14SAndroid Build Coastguard Worker    def get_partition_id(self):
1823*cfb92d14SAndroid Build Coastguard Worker        self.send_command('partitionid')
1824*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1825*cfb92d14SAndroid Build Coastguard Worker
1826*cfb92d14SAndroid Build Coastguard Worker    def get_preferred_partition_id(self):
1827*cfb92d14SAndroid Build Coastguard Worker        self.send_command('partitionid preferred')
1828*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1829*cfb92d14SAndroid Build Coastguard Worker
1830*cfb92d14SAndroid Build Coastguard Worker    def set_preferred_partition_id(self, partition_id):
1831*cfb92d14SAndroid Build Coastguard Worker        cmd = 'partitionid preferred %d' % partition_id
1832*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1833*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1834*cfb92d14SAndroid Build Coastguard Worker
1835*cfb92d14SAndroid Build Coastguard Worker    def get_pollperiod(self):
1836*cfb92d14SAndroid Build Coastguard Worker        self.send_command('pollperiod')
1837*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1838*cfb92d14SAndroid Build Coastguard Worker
1839*cfb92d14SAndroid Build Coastguard Worker    def set_pollperiod(self, pollperiod):
1840*cfb92d14SAndroid Build Coastguard Worker        self.send_command('pollperiod %d' % pollperiod)
1841*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1842*cfb92d14SAndroid Build Coastguard Worker
1843*cfb92d14SAndroid Build Coastguard Worker    def get_child_supervision_interval(self):
1844*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision interval')
1845*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1846*cfb92d14SAndroid Build Coastguard Worker
1847*cfb92d14SAndroid Build Coastguard Worker    def set_child_supervision_interval(self, interval):
1848*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision interval %d' % interval)
1849*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1850*cfb92d14SAndroid Build Coastguard Worker
1851*cfb92d14SAndroid Build Coastguard Worker    def get_child_supervision_check_timeout(self):
1852*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision checktimeout')
1853*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1854*cfb92d14SAndroid Build Coastguard Worker
1855*cfb92d14SAndroid Build Coastguard Worker    def set_child_supervision_check_timeout(self, timeout):
1856*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision checktimeout %d' % timeout)
1857*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1858*cfb92d14SAndroid Build Coastguard Worker
1859*cfb92d14SAndroid Build Coastguard Worker    def get_child_supervision_check_failure_counter(self):
1860*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision failcounter')
1861*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1862*cfb92d14SAndroid Build Coastguard Worker
1863*cfb92d14SAndroid Build Coastguard Worker    def reset_child_supervision_check_failure_counter(self):
1864*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childsupervision failcounter reset')
1865*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1866*cfb92d14SAndroid Build Coastguard Worker
1867*cfb92d14SAndroid Build Coastguard Worker    def get_csl_info(self):
1868*cfb92d14SAndroid Build Coastguard Worker        self.send_command('csl')
1869*cfb92d14SAndroid Build Coastguard Worker        return self._expect_key_value_pairs(r'\S+')
1870*cfb92d14SAndroid Build Coastguard Worker
1871*cfb92d14SAndroid Build Coastguard Worker    def set_csl_channel(self, csl_channel):
1872*cfb92d14SAndroid Build Coastguard Worker        self.send_command('csl channel %d' % csl_channel)
1873*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1874*cfb92d14SAndroid Build Coastguard Worker
1875*cfb92d14SAndroid Build Coastguard Worker    def set_csl_period(self, csl_period):
1876*cfb92d14SAndroid Build Coastguard Worker        self.send_command('csl period %d' % csl_period)
1877*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1878*cfb92d14SAndroid Build Coastguard Worker
1879*cfb92d14SAndroid Build Coastguard Worker    def set_csl_timeout(self, csl_timeout):
1880*cfb92d14SAndroid Build Coastguard Worker        self.send_command('csl timeout %d' % csl_timeout)
1881*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1882*cfb92d14SAndroid Build Coastguard Worker
1883*cfb92d14SAndroid Build Coastguard Worker    def send_mac_emptydata(self):
1884*cfb92d14SAndroid Build Coastguard Worker        self.send_command('mac send emptydata')
1885*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1886*cfb92d14SAndroid Build Coastguard Worker
1887*cfb92d14SAndroid Build Coastguard Worker    def send_mac_datarequest(self):
1888*cfb92d14SAndroid Build Coastguard Worker        self.send_command('mac send datarequest')
1889*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1890*cfb92d14SAndroid Build Coastguard Worker
1891*cfb92d14SAndroid Build Coastguard Worker    def set_router_upgrade_threshold(self, threshold):
1892*cfb92d14SAndroid Build Coastguard Worker        cmd = 'routerupgradethreshold %d' % threshold
1893*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1894*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1895*cfb92d14SAndroid Build Coastguard Worker
1896*cfb92d14SAndroid Build Coastguard Worker    def set_router_downgrade_threshold(self, threshold):
1897*cfb92d14SAndroid Build Coastguard Worker        cmd = 'routerdowngradethreshold %d' % threshold
1898*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1899*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1900*cfb92d14SAndroid Build Coastguard Worker
1901*cfb92d14SAndroid Build Coastguard Worker    def get_router_downgrade_threshold(self) -> int:
1902*cfb92d14SAndroid Build Coastguard Worker        self.send_command('routerdowngradethreshold')
1903*cfb92d14SAndroid Build Coastguard Worker        return int(self._expect_result(r'\d+'))
1904*cfb92d14SAndroid Build Coastguard Worker
1905*cfb92d14SAndroid Build Coastguard Worker    def set_router_eligible(self, enable: bool):
1906*cfb92d14SAndroid Build Coastguard Worker        cmd = f'routereligible {"enable" if enable else "disable"}'
1907*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1908*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1909*cfb92d14SAndroid Build Coastguard Worker
1910*cfb92d14SAndroid Build Coastguard Worker    def get_router_eligible(self) -> bool:
1911*cfb92d14SAndroid Build Coastguard Worker        states = [r'Disabled', r'Enabled']
1912*cfb92d14SAndroid Build Coastguard Worker        self.send_command('routereligible')
1913*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states) == 'Enabled'
1914*cfb92d14SAndroid Build Coastguard Worker
1915*cfb92d14SAndroid Build Coastguard Worker    def prefer_router_id(self, router_id):
1916*cfb92d14SAndroid Build Coastguard Worker        cmd = 'preferrouterid %d' % router_id
1917*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1918*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1919*cfb92d14SAndroid Build Coastguard Worker
1920*cfb92d14SAndroid Build Coastguard Worker    def release_router_id(self, router_id):
1921*cfb92d14SAndroid Build Coastguard Worker        cmd = 'releaserouterid %d' % router_id
1922*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1923*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1924*cfb92d14SAndroid Build Coastguard Worker
1925*cfb92d14SAndroid Build Coastguard Worker    def get_state(self):
1926*cfb92d14SAndroid Build Coastguard Worker        states = [r'detached', r'child', r'router', r'leader', r'disabled']
1927*cfb92d14SAndroid Build Coastguard Worker        self.send_command('state')
1928*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states)
1929*cfb92d14SAndroid Build Coastguard Worker
1930*cfb92d14SAndroid Build Coastguard Worker    def set_state(self, state):
1931*cfb92d14SAndroid Build Coastguard Worker        cmd = 'state %s' % state
1932*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1933*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1934*cfb92d14SAndroid Build Coastguard Worker
1935*cfb92d14SAndroid Build Coastguard Worker    def get_ephemeral_key_state(self):
1936*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ba ephemeralkey'
1937*cfb92d14SAndroid Build Coastguard Worker        states = [r'inactive', r'active']
1938*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1939*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(states)
1940*cfb92d14SAndroid Build Coastguard Worker
1941*cfb92d14SAndroid Build Coastguard Worker    def get_timeout(self):
1942*cfb92d14SAndroid Build Coastguard Worker        self.send_command('childtimeout')
1943*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1944*cfb92d14SAndroid Build Coastguard Worker
1945*cfb92d14SAndroid Build Coastguard Worker    def set_timeout(self, timeout):
1946*cfb92d14SAndroid Build Coastguard Worker        cmd = 'childtimeout %d' % timeout
1947*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1948*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1949*cfb92d14SAndroid Build Coastguard Worker
1950*cfb92d14SAndroid Build Coastguard Worker    def set_max_children(self, number):
1951*cfb92d14SAndroid Build Coastguard Worker        cmd = 'childmax %d' % number
1952*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1953*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1954*cfb92d14SAndroid Build Coastguard Worker
1955*cfb92d14SAndroid Build Coastguard Worker    def get_weight(self):
1956*cfb92d14SAndroid Build Coastguard Worker        self.send_command('leaderweight')
1957*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
1958*cfb92d14SAndroid Build Coastguard Worker
1959*cfb92d14SAndroid Build Coastguard Worker    def set_weight(self, weight):
1960*cfb92d14SAndroid Build Coastguard Worker        cmd = 'leaderweight %d' % weight
1961*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1962*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1963*cfb92d14SAndroid Build Coastguard Worker
1964*cfb92d14SAndroid Build Coastguard Worker    def add_ipaddr(self, ipaddr):
1965*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ipaddr add %s' % ipaddr
1966*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1967*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1968*cfb92d14SAndroid Build Coastguard Worker
1969*cfb92d14SAndroid Build Coastguard Worker    def del_ipaddr(self, ipaddr):
1970*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ipaddr del %s' % ipaddr
1971*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1972*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1973*cfb92d14SAndroid Build Coastguard Worker
1974*cfb92d14SAndroid Build Coastguard Worker    def add_ipmaddr(self, ipmaddr):
1975*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ipmaddr add %s' % ipmaddr
1976*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1977*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1978*cfb92d14SAndroid Build Coastguard Worker
1979*cfb92d14SAndroid Build Coastguard Worker    def del_ipmaddr(self, ipmaddr):
1980*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ipmaddr del %s' % ipmaddr
1981*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
1982*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
1983*cfb92d14SAndroid Build Coastguard Worker
1984*cfb92d14SAndroid Build Coastguard Worker    def get_addrs(self, verbose=False):
1985*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ipaddr' + (' -v' if verbose else ''))
1986*cfb92d14SAndroid Build Coastguard Worker
1987*cfb92d14SAndroid Build Coastguard Worker        return self._expect_results(r'\S+(:\S*)+')
1988*cfb92d14SAndroid Build Coastguard Worker
1989*cfb92d14SAndroid Build Coastguard Worker    def get_mleid(self):
1990*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ipaddr mleid')
1991*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\S+(:\S*)+')
1992*cfb92d14SAndroid Build Coastguard Worker
1993*cfb92d14SAndroid Build Coastguard Worker    def get_linklocal(self):
1994*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ipaddr linklocal')
1995*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\S+(:\S*)+')
1996*cfb92d14SAndroid Build Coastguard Worker
1997*cfb92d14SAndroid Build Coastguard Worker    def get_rloc(self):
1998*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ipaddr rloc')
1999*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\S+(:\S*)+')
2000*cfb92d14SAndroid Build Coastguard Worker
2001*cfb92d14SAndroid Build Coastguard Worker    def get_addr(self, prefix):
2002*cfb92d14SAndroid Build Coastguard Worker        network = ipaddress.ip_network(u'%s' % str(prefix))
2003*cfb92d14SAndroid Build Coastguard Worker        addrs = self.get_addrs()
2004*cfb92d14SAndroid Build Coastguard Worker
2005*cfb92d14SAndroid Build Coastguard Worker        for addr in addrs:
2006*cfb92d14SAndroid Build Coastguard Worker            if isinstance(addr, bytearray):
2007*cfb92d14SAndroid Build Coastguard Worker                addr = bytes(addr)
2008*cfb92d14SAndroid Build Coastguard Worker            ipv6_address = ipaddress.ip_address(addr)
2009*cfb92d14SAndroid Build Coastguard Worker            if ipv6_address in network:
2010*cfb92d14SAndroid Build Coastguard Worker                return ipv6_address.exploded
2011*cfb92d14SAndroid Build Coastguard Worker
2012*cfb92d14SAndroid Build Coastguard Worker        return None
2013*cfb92d14SAndroid Build Coastguard Worker
2014*cfb92d14SAndroid Build Coastguard Worker    def has_ipaddr(self, address):
2015*cfb92d14SAndroid Build Coastguard Worker        ipaddr = ipaddress.ip_address(address)
2016*cfb92d14SAndroid Build Coastguard Worker        ipaddrs = self.get_addrs()
2017*cfb92d14SAndroid Build Coastguard Worker        for addr in ipaddrs:
2018*cfb92d14SAndroid Build Coastguard Worker            if isinstance(addr, bytearray):
2019*cfb92d14SAndroid Build Coastguard Worker                addr = bytes(addr)
2020*cfb92d14SAndroid Build Coastguard Worker            if ipaddress.ip_address(addr) == ipaddr:
2021*cfb92d14SAndroid Build Coastguard Worker                return True
2022*cfb92d14SAndroid Build Coastguard Worker        return False
2023*cfb92d14SAndroid Build Coastguard Worker
2024*cfb92d14SAndroid Build Coastguard Worker    def get_ipmaddrs(self):
2025*cfb92d14SAndroid Build Coastguard Worker        self.send_command('ipmaddr')
2026*cfb92d14SAndroid Build Coastguard Worker        return self._expect_results(r'\S+(:\S*)+')
2027*cfb92d14SAndroid Build Coastguard Worker
2028*cfb92d14SAndroid Build Coastguard Worker    def has_ipmaddr(self, address):
2029*cfb92d14SAndroid Build Coastguard Worker        ipmaddr = ipaddress.ip_address(address)
2030*cfb92d14SAndroid Build Coastguard Worker        ipmaddrs = self.get_ipmaddrs()
2031*cfb92d14SAndroid Build Coastguard Worker        for addr in ipmaddrs:
2032*cfb92d14SAndroid Build Coastguard Worker            if isinstance(addr, bytearray):
2033*cfb92d14SAndroid Build Coastguard Worker                addr = bytes(addr)
2034*cfb92d14SAndroid Build Coastguard Worker            if ipaddress.ip_address(addr) == ipmaddr:
2035*cfb92d14SAndroid Build Coastguard Worker                return True
2036*cfb92d14SAndroid Build Coastguard Worker        return False
2037*cfb92d14SAndroid Build Coastguard Worker
2038*cfb92d14SAndroid Build Coastguard Worker    def get_addr_leader_aloc(self):
2039*cfb92d14SAndroid Build Coastguard Worker        addrs = self.get_addrs()
2040*cfb92d14SAndroid Build Coastguard Worker        for addr in addrs:
2041*cfb92d14SAndroid Build Coastguard Worker            segs = addr.split(':')
2042*cfb92d14SAndroid Build Coastguard Worker            if (segs[4] == '0' and segs[5] == 'ff' and segs[6] == 'fe00' and segs[7] == 'fc00'):
2043*cfb92d14SAndroid Build Coastguard Worker                return addr
2044*cfb92d14SAndroid Build Coastguard Worker        return None
2045*cfb92d14SAndroid Build Coastguard Worker
2046*cfb92d14SAndroid Build Coastguard Worker    def get_mleid_iid(self):
2047*cfb92d14SAndroid Build Coastguard Worker        ml_eid = IPv6Address(self.get_mleid())
2048*cfb92d14SAndroid Build Coastguard Worker        return ml_eid.packed[8:].hex()
2049*cfb92d14SAndroid Build Coastguard Worker
2050*cfb92d14SAndroid Build Coastguard Worker    def get_eidcaches(self):
2051*cfb92d14SAndroid Build Coastguard Worker        eidcaches = []
2052*cfb92d14SAndroid Build Coastguard Worker        self.send_command('eidcache')
2053*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_results(r'([a-fA-F0-9\:]+) ([a-fA-F0-9]+)'):
2054*cfb92d14SAndroid Build Coastguard Worker            eidcaches.append(line.split())
2055*cfb92d14SAndroid Build Coastguard Worker
2056*cfb92d14SAndroid Build Coastguard Worker        return eidcaches
2057*cfb92d14SAndroid Build Coastguard Worker
2058*cfb92d14SAndroid Build Coastguard Worker    def add_service(self, enterpriseNumber, serviceData, serverData):
2059*cfb92d14SAndroid Build Coastguard Worker        cmd = 'service add %s %s %s' % (
2060*cfb92d14SAndroid Build Coastguard Worker            enterpriseNumber,
2061*cfb92d14SAndroid Build Coastguard Worker            serviceData,
2062*cfb92d14SAndroid Build Coastguard Worker            serverData,
2063*cfb92d14SAndroid Build Coastguard Worker        )
2064*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2065*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2066*cfb92d14SAndroid Build Coastguard Worker
2067*cfb92d14SAndroid Build Coastguard Worker    def remove_service(self, enterpriseNumber, serviceData):
2068*cfb92d14SAndroid Build Coastguard Worker        cmd = 'service remove %s %s' % (enterpriseNumber, serviceData)
2069*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2070*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2071*cfb92d14SAndroid Build Coastguard Worker
2072*cfb92d14SAndroid Build Coastguard Worker    def get_child_table(self) -> Dict[int, Dict[str, Any]]:
2073*cfb92d14SAndroid Build Coastguard Worker        """Get the table of attached children."""
2074*cfb92d14SAndroid Build Coastguard Worker        cmd = 'child table'
2075*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2076*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
2077*cfb92d14SAndroid Build Coastguard Worker
2078*cfb92d14SAndroid Build Coastguard Worker        #
2079*cfb92d14SAndroid Build Coastguard Worker        # Example output:
2080*cfb92d14SAndroid Build Coastguard Worker        # | ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC     |
2081*cfb92d14SAndroid Build Coastguard Worker        # +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+
2082*cfb92d14SAndroid Build Coastguard Worker        # |   1 | 0xc801 |        240 |         24 |     3 |  131 |1|0|0|  3| 0 |     0 |   129 | 4ecede68435358ac |
2083*cfb92d14SAndroid Build Coastguard Worker        # |   2 | 0xc802 |        240 |          2 |     3 |  131 |0|0|0|  3| 1 |     0 |     0 | a672a601d2ce37d8 |
2084*cfb92d14SAndroid Build Coastguard Worker        # Done
2085*cfb92d14SAndroid Build Coastguard Worker        #
2086*cfb92d14SAndroid Build Coastguard Worker
2087*cfb92d14SAndroid Build Coastguard Worker        headers = self.__split_table_row(output[0])
2088*cfb92d14SAndroid Build Coastguard Worker
2089*cfb92d14SAndroid Build Coastguard Worker        table = {}
2090*cfb92d14SAndroid Build Coastguard Worker        for line in output[2:]:
2091*cfb92d14SAndroid Build Coastguard Worker            line = line.strip()
2092*cfb92d14SAndroid Build Coastguard Worker            if not line:
2093*cfb92d14SAndroid Build Coastguard Worker                continue
2094*cfb92d14SAndroid Build Coastguard Worker
2095*cfb92d14SAndroid Build Coastguard Worker            fields = self.__split_table_row(line)
2096*cfb92d14SAndroid Build Coastguard Worker            col = lambda colname: self.__get_table_col(colname, headers, fields)
2097*cfb92d14SAndroid Build Coastguard Worker
2098*cfb92d14SAndroid Build Coastguard Worker            id = int(col("ID"))
2099*cfb92d14SAndroid Build Coastguard Worker            r, d, n = int(col("R")), int(col("D")), int(col("N"))
2100*cfb92d14SAndroid Build Coastguard Worker            mode = f'{"r" if r else ""}{"d" if d else ""}{"n" if n else ""}'
2101*cfb92d14SAndroid Build Coastguard Worker
2102*cfb92d14SAndroid Build Coastguard Worker            table[int(id)] = {
2103*cfb92d14SAndroid Build Coastguard Worker                'id': int(id),
2104*cfb92d14SAndroid Build Coastguard Worker                'rloc16': int(col('RLOC16'), 16),
2105*cfb92d14SAndroid Build Coastguard Worker                'timeout': int(col('Timeout')),
2106*cfb92d14SAndroid Build Coastguard Worker                'age': int(col('Age')),
2107*cfb92d14SAndroid Build Coastguard Worker                'lq_in': int(col('LQ In')),
2108*cfb92d14SAndroid Build Coastguard Worker                'c_vn': int(col('C_VN')),
2109*cfb92d14SAndroid Build Coastguard Worker                'mode': mode,
2110*cfb92d14SAndroid Build Coastguard Worker                'extaddr': col('Extended MAC'),
2111*cfb92d14SAndroid Build Coastguard Worker                'ver': int(col('Ver')),
2112*cfb92d14SAndroid Build Coastguard Worker                'csl': bool(int(col('CSL'))),
2113*cfb92d14SAndroid Build Coastguard Worker                'qmsgcnt': int(col('QMsgCnt')),
2114*cfb92d14SAndroid Build Coastguard Worker                'suprvsn': int(col('Suprvsn'))
2115*cfb92d14SAndroid Build Coastguard Worker            }
2116*cfb92d14SAndroid Build Coastguard Worker
2117*cfb92d14SAndroid Build Coastguard Worker        return table
2118*cfb92d14SAndroid Build Coastguard Worker
2119*cfb92d14SAndroid Build Coastguard Worker    def __split_table_row(self, row: str) -> List[str]:
2120*cfb92d14SAndroid Build Coastguard Worker        if not (row.startswith('|') and row.endswith('|')):
2121*cfb92d14SAndroid Build Coastguard Worker            raise ValueError(row)
2122*cfb92d14SAndroid Build Coastguard Worker
2123*cfb92d14SAndroid Build Coastguard Worker        fields = row.split('|')
2124*cfb92d14SAndroid Build Coastguard Worker        fields = [x.strip() for x in fields[1:-1]]
2125*cfb92d14SAndroid Build Coastguard Worker        return fields
2126*cfb92d14SAndroid Build Coastguard Worker
2127*cfb92d14SAndroid Build Coastguard Worker    def __get_table_col(self, colname: str, headers: List[str], fields: List[str]) -> str:
2128*cfb92d14SAndroid Build Coastguard Worker        return fields[headers.index(colname)]
2129*cfb92d14SAndroid Build Coastguard Worker
2130*cfb92d14SAndroid Build Coastguard Worker    def __getOmrAddress(self):
2131*cfb92d14SAndroid Build Coastguard Worker        prefixes = [prefix.split('::')[0] for prefix in self.get_prefixes()]
2132*cfb92d14SAndroid Build Coastguard Worker        omr_addrs = []
2133*cfb92d14SAndroid Build Coastguard Worker        for addr in self.get_addrs():
2134*cfb92d14SAndroid Build Coastguard Worker            for prefix in prefixes:
2135*cfb92d14SAndroid Build Coastguard Worker                if (addr.startswith(prefix)) and (addr != self.__getDua()):
2136*cfb92d14SAndroid Build Coastguard Worker                    omr_addrs.append(addr)
2137*cfb92d14SAndroid Build Coastguard Worker                    break
2138*cfb92d14SAndroid Build Coastguard Worker
2139*cfb92d14SAndroid Build Coastguard Worker        return omr_addrs
2140*cfb92d14SAndroid Build Coastguard Worker
2141*cfb92d14SAndroid Build Coastguard Worker    def __getLinkLocalAddress(self):
2142*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2143*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.LINK_LOCAL_REGEX_PATTERN, ip6Addr, re.I):
2144*cfb92d14SAndroid Build Coastguard Worker                return ip6Addr
2145*cfb92d14SAndroid Build Coastguard Worker
2146*cfb92d14SAndroid Build Coastguard Worker        return None
2147*cfb92d14SAndroid Build Coastguard Worker
2148*cfb92d14SAndroid Build Coastguard Worker    def __getGlobalAddress(self):
2149*cfb92d14SAndroid Build Coastguard Worker        global_address = []
2150*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2151*cfb92d14SAndroid Build Coastguard Worker            if ((not re.match(config.LINK_LOCAL_REGEX_PATTERN, ip6Addr, re.I)) and
2152*cfb92d14SAndroid Build Coastguard Worker                (not re.match(config.MESH_LOCAL_PREFIX_REGEX_PATTERN, ip6Addr, re.I)) and
2153*cfb92d14SAndroid Build Coastguard Worker                (not re.match(config.ROUTING_LOCATOR_REGEX_PATTERN, ip6Addr, re.I))):
2154*cfb92d14SAndroid Build Coastguard Worker                global_address.append(ip6Addr)
2155*cfb92d14SAndroid Build Coastguard Worker
2156*cfb92d14SAndroid Build Coastguard Worker        return global_address
2157*cfb92d14SAndroid Build Coastguard Worker
2158*cfb92d14SAndroid Build Coastguard Worker    def __getRloc(self):
2159*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2160*cfb92d14SAndroid Build Coastguard Worker            if (re.match(config.MESH_LOCAL_PREFIX_REGEX_PATTERN, ip6Addr, re.I) and
2161*cfb92d14SAndroid Build Coastguard Worker                    re.match(config.ROUTING_LOCATOR_REGEX_PATTERN, ip6Addr, re.I) and
2162*cfb92d14SAndroid Build Coastguard Worker                    not (re.match(config.ALOC_FLAG_REGEX_PATTERN, ip6Addr, re.I))):
2163*cfb92d14SAndroid Build Coastguard Worker                return ip6Addr
2164*cfb92d14SAndroid Build Coastguard Worker        return None
2165*cfb92d14SAndroid Build Coastguard Worker
2166*cfb92d14SAndroid Build Coastguard Worker    def __getAloc(self):
2167*cfb92d14SAndroid Build Coastguard Worker        aloc = []
2168*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2169*cfb92d14SAndroid Build Coastguard Worker            if (re.match(config.MESH_LOCAL_PREFIX_REGEX_PATTERN, ip6Addr, re.I) and
2170*cfb92d14SAndroid Build Coastguard Worker                    re.match(config.ROUTING_LOCATOR_REGEX_PATTERN, ip6Addr, re.I) and
2171*cfb92d14SAndroid Build Coastguard Worker                    re.match(config.ALOC_FLAG_REGEX_PATTERN, ip6Addr, re.I)):
2172*cfb92d14SAndroid Build Coastguard Worker                aloc.append(ip6Addr)
2173*cfb92d14SAndroid Build Coastguard Worker
2174*cfb92d14SAndroid Build Coastguard Worker        return aloc
2175*cfb92d14SAndroid Build Coastguard Worker
2176*cfb92d14SAndroid Build Coastguard Worker    def __getMleid(self):
2177*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2178*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.MESH_LOCAL_PREFIX_REGEX_PATTERN, ip6Addr,
2179*cfb92d14SAndroid Build Coastguard Worker                        re.I) and not (re.match(config.ROUTING_LOCATOR_REGEX_PATTERN, ip6Addr, re.I)):
2180*cfb92d14SAndroid Build Coastguard Worker                return ip6Addr
2181*cfb92d14SAndroid Build Coastguard Worker
2182*cfb92d14SAndroid Build Coastguard Worker        return None
2183*cfb92d14SAndroid Build Coastguard Worker
2184*cfb92d14SAndroid Build Coastguard Worker    def __getDua(self) -> Optional[str]:
2185*cfb92d14SAndroid Build Coastguard Worker        for ip6Addr in self.get_addrs():
2186*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.DOMAIN_PREFIX_REGEX_PATTERN, ip6Addr, re.I):
2187*cfb92d14SAndroid Build Coastguard Worker                return ip6Addr
2188*cfb92d14SAndroid Build Coastguard Worker
2189*cfb92d14SAndroid Build Coastguard Worker        return None
2190*cfb92d14SAndroid Build Coastguard Worker
2191*cfb92d14SAndroid Build Coastguard Worker    def get_ip6_address_by_prefix(self, prefix: Union[str, IPv6Network]) -> List[IPv6Address]:
2192*cfb92d14SAndroid Build Coastguard Worker        """Get addresses matched with given prefix.
2193*cfb92d14SAndroid Build Coastguard Worker
2194*cfb92d14SAndroid Build Coastguard Worker        Args:
2195*cfb92d14SAndroid Build Coastguard Worker            prefix: the prefix to match against.
2196*cfb92d14SAndroid Build Coastguard Worker                    Can be either a string or ipaddress.IPv6Network.
2197*cfb92d14SAndroid Build Coastguard Worker
2198*cfb92d14SAndroid Build Coastguard Worker        Returns:
2199*cfb92d14SAndroid Build Coastguard Worker            The IPv6 address list.
2200*cfb92d14SAndroid Build Coastguard Worker        """
2201*cfb92d14SAndroid Build Coastguard Worker        if isinstance(prefix, str):
2202*cfb92d14SAndroid Build Coastguard Worker            prefix = IPv6Network(prefix)
2203*cfb92d14SAndroid Build Coastguard Worker        addrs = map(IPv6Address, self.get_addrs())
2204*cfb92d14SAndroid Build Coastguard Worker
2205*cfb92d14SAndroid Build Coastguard Worker        return [addr for addr in addrs if addr in prefix]
2206*cfb92d14SAndroid Build Coastguard Worker
2207*cfb92d14SAndroid Build Coastguard Worker    def get_ip6_address(self, address_type):
2208*cfb92d14SAndroid Build Coastguard Worker        """Get specific type of IPv6 address configured on thread device.
2209*cfb92d14SAndroid Build Coastguard Worker
2210*cfb92d14SAndroid Build Coastguard Worker        Args:
2211*cfb92d14SAndroid Build Coastguard Worker            address_type: the config.ADDRESS_TYPE type of IPv6 address.
2212*cfb92d14SAndroid Build Coastguard Worker
2213*cfb92d14SAndroid Build Coastguard Worker        Returns:
2214*cfb92d14SAndroid Build Coastguard Worker            IPv6 address string.
2215*cfb92d14SAndroid Build Coastguard Worker        """
2216*cfb92d14SAndroid Build Coastguard Worker        if address_type == config.ADDRESS_TYPE.LINK_LOCAL:
2217*cfb92d14SAndroid Build Coastguard Worker            return self.__getLinkLocalAddress()
2218*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.GLOBAL:
2219*cfb92d14SAndroid Build Coastguard Worker            return self.__getGlobalAddress()
2220*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.RLOC:
2221*cfb92d14SAndroid Build Coastguard Worker            return self.__getRloc()
2222*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.ALOC:
2223*cfb92d14SAndroid Build Coastguard Worker            return self.__getAloc()
2224*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.ML_EID:
2225*cfb92d14SAndroid Build Coastguard Worker            return self.__getMleid()
2226*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.DUA:
2227*cfb92d14SAndroid Build Coastguard Worker            return self.__getDua()
2228*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
2229*cfb92d14SAndroid Build Coastguard Worker            return self._getBackboneGua()
2230*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.OMR:
2231*cfb92d14SAndroid Build Coastguard Worker            return self.__getOmrAddress()
2232*cfb92d14SAndroid Build Coastguard Worker        else:
2233*cfb92d14SAndroid Build Coastguard Worker            return None
2234*cfb92d14SAndroid Build Coastguard Worker
2235*cfb92d14SAndroid Build Coastguard Worker    def get_context_reuse_delay(self):
2236*cfb92d14SAndroid Build Coastguard Worker        self.send_command('contextreusedelay')
2237*cfb92d14SAndroid Build Coastguard Worker        return self._expect_result(r'\d+')
2238*cfb92d14SAndroid Build Coastguard Worker
2239*cfb92d14SAndroid Build Coastguard Worker    def set_context_reuse_delay(self, delay):
2240*cfb92d14SAndroid Build Coastguard Worker        cmd = 'contextreusedelay %d' % delay
2241*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2242*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2243*cfb92d14SAndroid Build Coastguard Worker
2244*cfb92d14SAndroid Build Coastguard Worker    def add_prefix(self, prefix, flags='paosr', prf='med'):
2245*cfb92d14SAndroid Build Coastguard Worker        cmd = 'prefix add %s %s %s' % (prefix, flags, prf)
2246*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2247*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2248*cfb92d14SAndroid Build Coastguard Worker
2249*cfb92d14SAndroid Build Coastguard Worker    def remove_prefix(self, prefix):
2250*cfb92d14SAndroid Build Coastguard Worker        cmd = 'prefix remove %s' % prefix
2251*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2252*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2253*cfb92d14SAndroid Build Coastguard Worker
2254*cfb92d14SAndroid Build Coastguard Worker    #
2255*cfb92d14SAndroid Build Coastguard Worker    # BR commands
2256*cfb92d14SAndroid Build Coastguard Worker    #
2257*cfb92d14SAndroid Build Coastguard Worker    def enable_br(self):
2258*cfb92d14SAndroid Build Coastguard Worker        self.send_command('br enable')
2259*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2260*cfb92d14SAndroid Build Coastguard Worker
2261*cfb92d14SAndroid Build Coastguard Worker    def disable_br(self):
2262*cfb92d14SAndroid Build Coastguard Worker        self.send_command('br disable')
2263*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2264*cfb92d14SAndroid Build Coastguard Worker
2265*cfb92d14SAndroid Build Coastguard Worker    def get_br_omr_prefix(self):
2266*cfb92d14SAndroid Build Coastguard Worker        cmd = 'br omrprefix local'
2267*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2268*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
2269*cfb92d14SAndroid Build Coastguard Worker
2270*cfb92d14SAndroid Build Coastguard Worker    def get_br_peers(self) -> List[str]:
2271*cfb92d14SAndroid Build Coastguard Worker        # Example output of `br peers` command:
2272*cfb92d14SAndroid Build Coastguard Worker        #   rloc16:0xa800 age:00:00:50
2273*cfb92d14SAndroid Build Coastguard Worker        #   rloc16:0x6800 age:00:00:51
2274*cfb92d14SAndroid Build Coastguard Worker        #   Done
2275*cfb92d14SAndroid Build Coastguard Worker        self.send_command('br peers')
2276*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()
2277*cfb92d14SAndroid Build Coastguard Worker
2278*cfb92d14SAndroid Build Coastguard Worker    def get_br_peers_rloc16s(self) -> List[int]:
2279*cfb92d14SAndroid Build Coastguard Worker        """parse `br peers` output and return the list of RLOC16s"""
2280*cfb92d14SAndroid Build Coastguard Worker        return [
2281*cfb92d14SAndroid Build Coastguard Worker            int(pair.split(':')[1], 16)
2282*cfb92d14SAndroid Build Coastguard Worker            for line in self.get_br_peers()
2283*cfb92d14SAndroid Build Coastguard Worker            for pair in line.split()
2284*cfb92d14SAndroid Build Coastguard Worker            if pair.split(':')[0] == 'rloc16'
2285*cfb92d14SAndroid Build Coastguard Worker        ]
2286*cfb92d14SAndroid Build Coastguard Worker
2287*cfb92d14SAndroid Build Coastguard Worker    def get_br_routers(self) -> List[str]:
2288*cfb92d14SAndroid Build Coastguard Worker        # Example output of `br routers` command:
2289*cfb92d14SAndroid Build Coastguard Worker        #   fe80:0:0:0:42:acff:fe14:3 (M:0 O:0 Stub:1) ms-since-rx:144160 reachable:yes age:00:17:36 (peer BR)
2290*cfb92d14SAndroid Build Coastguard Worker        #   fe80:0:0:0:42:acff:fe14:2 (M:0 O:0 Stub:1) ms-since-rx:45179 reachable:yes age:00:17:36
2291*cfb92d14SAndroid Build Coastguard Worker        #   Done
2292*cfb92d14SAndroid Build Coastguard Worker        self.send_command('br routers')
2293*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()
2294*cfb92d14SAndroid Build Coastguard Worker
2295*cfb92d14SAndroid Build Coastguard Worker    def get_br_routers_ip_addresses(self) -> List[IPv6Address]:
2296*cfb92d14SAndroid Build Coastguard Worker        """parse `br routers` output and return the list of IPv6 addresses"""
2297*cfb92d14SAndroid Build Coastguard Worker        return [IPv6Address(line.split()[0]) for line in self.get_br_routers()]
2298*cfb92d14SAndroid Build Coastguard Worker
2299*cfb92d14SAndroid Build Coastguard Worker    def get_netdata_omr_prefixes(self):
2300*cfb92d14SAndroid Build Coastguard Worker        omr_prefixes = []
2301*cfb92d14SAndroid Build Coastguard Worker        for prefix in self.get_prefixes():
2302*cfb92d14SAndroid Build Coastguard Worker            prefix, flags = prefix.split()[:2]
2303*cfb92d14SAndroid Build Coastguard Worker            if 'a' in flags and 'o' in flags and 's' in flags and 'D' not in flags:
2304*cfb92d14SAndroid Build Coastguard Worker                omr_prefixes.append(prefix)
2305*cfb92d14SAndroid Build Coastguard Worker
2306*cfb92d14SAndroid Build Coastguard Worker        return omr_prefixes
2307*cfb92d14SAndroid Build Coastguard Worker
2308*cfb92d14SAndroid Build Coastguard Worker    def get_br_on_link_prefix(self):
2309*cfb92d14SAndroid Build Coastguard Worker        cmd = 'br onlinkprefix local'
2310*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2311*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
2312*cfb92d14SAndroid Build Coastguard Worker
2313*cfb92d14SAndroid Build Coastguard Worker    def get_netdata_non_nat64_routes(self):
2314*cfb92d14SAndroid Build Coastguard Worker        nat64_routes = []
2315*cfb92d14SAndroid Build Coastguard Worker        routes = self.get_routes()
2316*cfb92d14SAndroid Build Coastguard Worker        for route in routes:
2317*cfb92d14SAndroid Build Coastguard Worker            if 'n' not in route.split(' ')[1]:
2318*cfb92d14SAndroid Build Coastguard Worker                nat64_routes.append(route.split(' ')[0])
2319*cfb92d14SAndroid Build Coastguard Worker        return nat64_routes
2320*cfb92d14SAndroid Build Coastguard Worker
2321*cfb92d14SAndroid Build Coastguard Worker    def get_netdata_nat64_routes(self):
2322*cfb92d14SAndroid Build Coastguard Worker        nat64_routes = []
2323*cfb92d14SAndroid Build Coastguard Worker        routes = self.get_routes()
2324*cfb92d14SAndroid Build Coastguard Worker        for route in routes:
2325*cfb92d14SAndroid Build Coastguard Worker            if 'n' in route.split(' ')[1]:
2326*cfb92d14SAndroid Build Coastguard Worker                nat64_routes.append(route.split(' ')[0])
2327*cfb92d14SAndroid Build Coastguard Worker        return nat64_routes
2328*cfb92d14SAndroid Build Coastguard Worker
2329*cfb92d14SAndroid Build Coastguard Worker    def get_br_nat64_prefix(self):
2330*cfb92d14SAndroid Build Coastguard Worker        cmd = 'br nat64prefix local'
2331*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2332*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0]
2333*cfb92d14SAndroid Build Coastguard Worker
2334*cfb92d14SAndroid Build Coastguard Worker    def get_br_favored_nat64_prefix(self):
2335*cfb92d14SAndroid Build Coastguard Worker        cmd = 'br nat64prefix favored'
2336*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2337*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()[0].split(' ')[0]
2338*cfb92d14SAndroid Build Coastguard Worker
2339*cfb92d14SAndroid Build Coastguard Worker    def enable_nat64(self):
2340*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'nat64 enable')
2341*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2342*cfb92d14SAndroid Build Coastguard Worker
2343*cfb92d14SAndroid Build Coastguard Worker    def disable_nat64(self):
2344*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'nat64 disable')
2345*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2346*cfb92d14SAndroid Build Coastguard Worker
2347*cfb92d14SAndroid Build Coastguard Worker    def get_nat64_state(self):
2348*cfb92d14SAndroid Build Coastguard Worker        self.send_command('nat64 state')
2349*cfb92d14SAndroid Build Coastguard Worker        res = {}
2350*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_command_output():
2351*cfb92d14SAndroid Build Coastguard Worker            state = line.split(':')
2352*cfb92d14SAndroid Build Coastguard Worker            res[state[0].strip()] = state[1].strip()
2353*cfb92d14SAndroid Build Coastguard Worker        return res
2354*cfb92d14SAndroid Build Coastguard Worker
2355*cfb92d14SAndroid Build Coastguard Worker    def get_nat64_mappings(self):
2356*cfb92d14SAndroid Build Coastguard Worker        cmd = 'nat64 mappings'
2357*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2358*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_command_output()
2359*cfb92d14SAndroid Build Coastguard Worker        session = None
2360*cfb92d14SAndroid Build Coastguard Worker        session_counters = None
2361*cfb92d14SAndroid Build Coastguard Worker        sessions = []
2362*cfb92d14SAndroid Build Coastguard Worker
2363*cfb92d14SAndroid Build Coastguard Worker        for line in result:
2364*cfb92d14SAndroid Build Coastguard Worker            m = re.match(
2365*cfb92d14SAndroid Build Coastguard Worker                r'\|\s+([a-f0-9]+)\s+\|\s+(.+)\s+\|\s+(.+)\s+\|\s+(\d+)s\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|',
2366*cfb92d14SAndroid Build Coastguard Worker                line)
2367*cfb92d14SAndroid Build Coastguard Worker            if m:
2368*cfb92d14SAndroid Build Coastguard Worker                groups = m.groups()
2369*cfb92d14SAndroid Build Coastguard Worker                if session:
2370*cfb92d14SAndroid Build Coastguard Worker                    session['counters'] = session_counters
2371*cfb92d14SAndroid Build Coastguard Worker                    sessions.append(session)
2372*cfb92d14SAndroid Build Coastguard Worker                session = {
2373*cfb92d14SAndroid Build Coastguard Worker                    'id': groups[0],
2374*cfb92d14SAndroid Build Coastguard Worker                    'ip6': groups[1],
2375*cfb92d14SAndroid Build Coastguard Worker                    'ip4': groups[2],
2376*cfb92d14SAndroid Build Coastguard Worker                    'expiry': int(groups[3]),
2377*cfb92d14SAndroid Build Coastguard Worker                }
2378*cfb92d14SAndroid Build Coastguard Worker                session_counters = {}
2379*cfb92d14SAndroid Build Coastguard Worker                session_counters['total'] = {
2380*cfb92d14SAndroid Build Coastguard Worker                    '4to6': {
2381*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[4]),
2382*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[5]),
2383*cfb92d14SAndroid Build Coastguard Worker                    },
2384*cfb92d14SAndroid Build Coastguard Worker                    '6to4': {
2385*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[6]),
2386*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[7]),
2387*cfb92d14SAndroid Build Coastguard Worker                    },
2388*cfb92d14SAndroid Build Coastguard Worker                }
2389*cfb92d14SAndroid Build Coastguard Worker                continue
2390*cfb92d14SAndroid Build Coastguard Worker            if not session:
2391*cfb92d14SAndroid Build Coastguard Worker                continue
2392*cfb92d14SAndroid Build Coastguard Worker            m = re.match(r'\|\s+\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line)
2393*cfb92d14SAndroid Build Coastguard Worker            if m:
2394*cfb92d14SAndroid Build Coastguard Worker                groups = m.groups()
2395*cfb92d14SAndroid Build Coastguard Worker                session_counters[groups[0]] = {
2396*cfb92d14SAndroid Build Coastguard Worker                    '4to6': {
2397*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[1]),
2398*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[2]),
2399*cfb92d14SAndroid Build Coastguard Worker                    },
2400*cfb92d14SAndroid Build Coastguard Worker                    '6to4': {
2401*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[3]),
2402*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[4]),
2403*cfb92d14SAndroid Build Coastguard Worker                    },
2404*cfb92d14SAndroid Build Coastguard Worker                }
2405*cfb92d14SAndroid Build Coastguard Worker        if session:
2406*cfb92d14SAndroid Build Coastguard Worker            session['counters'] = session_counters
2407*cfb92d14SAndroid Build Coastguard Worker            sessions.append(session)
2408*cfb92d14SAndroid Build Coastguard Worker        return sessions
2409*cfb92d14SAndroid Build Coastguard Worker
2410*cfb92d14SAndroid Build Coastguard Worker    def get_nat64_counters(self):
2411*cfb92d14SAndroid Build Coastguard Worker        cmd = 'nat64 counters'
2412*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2413*cfb92d14SAndroid Build Coastguard Worker        result = self._expect_command_output()
2414*cfb92d14SAndroid Build Coastguard Worker
2415*cfb92d14SAndroid Build Coastguard Worker        protocol_counters = {}
2416*cfb92d14SAndroid Build Coastguard Worker        error_counters = {}
2417*cfb92d14SAndroid Build Coastguard Worker        for line in result:
2418*cfb92d14SAndroid Build Coastguard Worker            m = re.match(r'\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line)
2419*cfb92d14SAndroid Build Coastguard Worker            if m:
2420*cfb92d14SAndroid Build Coastguard Worker                groups = m.groups()
2421*cfb92d14SAndroid Build Coastguard Worker                protocol_counters[groups[0]] = {
2422*cfb92d14SAndroid Build Coastguard Worker                    '4to6': {
2423*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[1]),
2424*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[2]),
2425*cfb92d14SAndroid Build Coastguard Worker                    },
2426*cfb92d14SAndroid Build Coastguard Worker                    '6to4': {
2427*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[3]),
2428*cfb92d14SAndroid Build Coastguard Worker                        'bytes': int(groups[4]),
2429*cfb92d14SAndroid Build Coastguard Worker                    },
2430*cfb92d14SAndroid Build Coastguard Worker                }
2431*cfb92d14SAndroid Build Coastguard Worker                continue
2432*cfb92d14SAndroid Build Coastguard Worker            m = re.match(r'\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line)
2433*cfb92d14SAndroid Build Coastguard Worker            if m:
2434*cfb92d14SAndroid Build Coastguard Worker                groups = m.groups()
2435*cfb92d14SAndroid Build Coastguard Worker                error_counters[groups[0]] = {
2436*cfb92d14SAndroid Build Coastguard Worker                    '4to6': {
2437*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[1]),
2438*cfb92d14SAndroid Build Coastguard Worker                    },
2439*cfb92d14SAndroid Build Coastguard Worker                    '6to4': {
2440*cfb92d14SAndroid Build Coastguard Worker                        'packets': int(groups[2]),
2441*cfb92d14SAndroid Build Coastguard Worker                    },
2442*cfb92d14SAndroid Build Coastguard Worker                }
2443*cfb92d14SAndroid Build Coastguard Worker                continue
2444*cfb92d14SAndroid Build Coastguard Worker        return {'protocol': protocol_counters, 'errors': error_counters}
2445*cfb92d14SAndroid Build Coastguard Worker
2446*cfb92d14SAndroid Build Coastguard Worker    def get_prefixes(self):
2447*cfb92d14SAndroid Build Coastguard Worker        return self.get_netdata()['Prefixes']
2448*cfb92d14SAndroid Build Coastguard Worker
2449*cfb92d14SAndroid Build Coastguard Worker    def get_routes(self):
2450*cfb92d14SAndroid Build Coastguard Worker        return self.get_netdata()['Routes']
2451*cfb92d14SAndroid Build Coastguard Worker
2452*cfb92d14SAndroid Build Coastguard Worker    def get_services(self):
2453*cfb92d14SAndroid Build Coastguard Worker        netdata = self.netdata_show()
2454*cfb92d14SAndroid Build Coastguard Worker        services = []
2455*cfb92d14SAndroid Build Coastguard Worker        services_section = False
2456*cfb92d14SAndroid Build Coastguard Worker
2457*cfb92d14SAndroid Build Coastguard Worker        for line in netdata:
2458*cfb92d14SAndroid Build Coastguard Worker            if line.startswith('Services:'):
2459*cfb92d14SAndroid Build Coastguard Worker                services_section = True
2460*cfb92d14SAndroid Build Coastguard Worker            elif line.startswith('Contexts'):
2461*cfb92d14SAndroid Build Coastguard Worker                services_section = False
2462*cfb92d14SAndroid Build Coastguard Worker            elif services_section:
2463*cfb92d14SAndroid Build Coastguard Worker                services.append(line.strip().split(' '))
2464*cfb92d14SAndroid Build Coastguard Worker        return services
2465*cfb92d14SAndroid Build Coastguard Worker
2466*cfb92d14SAndroid Build Coastguard Worker    def netdata_show(self):
2467*cfb92d14SAndroid Build Coastguard Worker        self.send_command('netdata show')
2468*cfb92d14SAndroid Build Coastguard Worker        return self._expect_command_output()
2469*cfb92d14SAndroid Build Coastguard Worker
2470*cfb92d14SAndroid Build Coastguard Worker    def get_netdata(self):
2471*cfb92d14SAndroid Build Coastguard Worker        raw_netdata = self.netdata_show()
2472*cfb92d14SAndroid Build Coastguard Worker        netdata = {'Prefixes': [], 'Routes': [], 'Services': [], 'Contexts': [], 'Commissioning': []}
2473*cfb92d14SAndroid Build Coastguard Worker        key_list = ['Prefixes', 'Routes', 'Services', 'Contexts', 'Commissioning']
2474*cfb92d14SAndroid Build Coastguard Worker        key = None
2475*cfb92d14SAndroid Build Coastguard Worker
2476*cfb92d14SAndroid Build Coastguard Worker        for i in range(0, len(raw_netdata)):
2477*cfb92d14SAndroid Build Coastguard Worker            keys = list(filter(raw_netdata[i].startswith, key_list))
2478*cfb92d14SAndroid Build Coastguard Worker            if keys != []:
2479*cfb92d14SAndroid Build Coastguard Worker                key = keys[0]
2480*cfb92d14SAndroid Build Coastguard Worker            elif key is not None:
2481*cfb92d14SAndroid Build Coastguard Worker                netdata[key].append(raw_netdata[i])
2482*cfb92d14SAndroid Build Coastguard Worker
2483*cfb92d14SAndroid Build Coastguard Worker        return netdata
2484*cfb92d14SAndroid Build Coastguard Worker
2485*cfb92d14SAndroid Build Coastguard Worker    def add_route(self, prefix, stable=False, nat64=False, prf='med'):
2486*cfb92d14SAndroid Build Coastguard Worker        cmd = 'route add %s ' % prefix
2487*cfb92d14SAndroid Build Coastguard Worker        if stable:
2488*cfb92d14SAndroid Build Coastguard Worker            cmd += 's'
2489*cfb92d14SAndroid Build Coastguard Worker        if nat64:
2490*cfb92d14SAndroid Build Coastguard Worker            cmd += 'n'
2491*cfb92d14SAndroid Build Coastguard Worker        cmd += ' %s' % prf
2492*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2493*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2494*cfb92d14SAndroid Build Coastguard Worker
2495*cfb92d14SAndroid Build Coastguard Worker    def remove_route(self, prefix):
2496*cfb92d14SAndroid Build Coastguard Worker        cmd = 'route remove %s' % prefix
2497*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2498*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2499*cfb92d14SAndroid Build Coastguard Worker
2500*cfb92d14SAndroid Build Coastguard Worker    def register_netdata(self):
2501*cfb92d14SAndroid Build Coastguard Worker        self.send_command('netdata register')
2502*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2503*cfb92d14SAndroid Build Coastguard Worker
2504*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_dnssrp_anycast(self, seqnum):
2505*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish dnssrp anycast {seqnum}')
2506*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2507*cfb92d14SAndroid Build Coastguard Worker
2508*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_dnssrp_unicast(self, address, port):
2509*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish dnssrp unicast {address} {port}')
2510*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2511*cfb92d14SAndroid Build Coastguard Worker
2512*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_dnssrp_unicast_mleid(self, port):
2513*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish dnssrp unicast {port}')
2514*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2515*cfb92d14SAndroid Build Coastguard Worker
2516*cfb92d14SAndroid Build Coastguard Worker    def netdata_unpublish_dnssrp(self):
2517*cfb92d14SAndroid Build Coastguard Worker        self.send_command('netdata unpublish dnssrp')
2518*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2519*cfb92d14SAndroid Build Coastguard Worker
2520*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_prefix(self, prefix, flags='paosr', prf='med'):
2521*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish prefix {prefix} {flags} {prf}')
2522*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2523*cfb92d14SAndroid Build Coastguard Worker
2524*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_route(self, prefix, flags='s', prf='med'):
2525*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish route {prefix} {flags} {prf}')
2526*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2527*cfb92d14SAndroid Build Coastguard Worker
2528*cfb92d14SAndroid Build Coastguard Worker    def netdata_publish_replace(self, old_prefix, prefix, flags='s', prf='med'):
2529*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata publish replace {old_prefix} {prefix} {flags} {prf}')
2530*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2531*cfb92d14SAndroid Build Coastguard Worker
2532*cfb92d14SAndroid Build Coastguard Worker    def netdata_unpublish_prefix(self, prefix):
2533*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'netdata unpublish {prefix}')
2534*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2535*cfb92d14SAndroid Build Coastguard Worker
2536*cfb92d14SAndroid Build Coastguard Worker    def send_network_diag_get(self, addr, tlv_types):
2537*cfb92d14SAndroid Build Coastguard Worker        self.send_command('networkdiagnostic get %s %s' % (addr, ' '.join([str(t.value) for t in tlv_types])))
2538*cfb92d14SAndroid Build Coastguard Worker
2539*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
2540*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(8)
2541*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
2542*cfb92d14SAndroid Build Coastguard Worker        else:
2543*cfb92d14SAndroid Build Coastguard Worker            timeout = 8
2544*cfb92d14SAndroid Build Coastguard Worker
2545*cfb92d14SAndroid Build Coastguard Worker        self._expect_done(timeout=timeout)
2546*cfb92d14SAndroid Build Coastguard Worker
2547*cfb92d14SAndroid Build Coastguard Worker    def send_network_diag_reset(self, addr, tlv_types):
2548*cfb92d14SAndroid Build Coastguard Worker        self.send_command('networkdiagnostic reset %s %s' % (addr, ' '.join([str(t.value) for t in tlv_types])))
2549*cfb92d14SAndroid Build Coastguard Worker
2550*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
2551*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(8)
2552*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
2553*cfb92d14SAndroid Build Coastguard Worker        else:
2554*cfb92d14SAndroid Build Coastguard Worker            timeout = 8
2555*cfb92d14SAndroid Build Coastguard Worker
2556*cfb92d14SAndroid Build Coastguard Worker        self._expect_done(timeout=timeout)
2557*cfb92d14SAndroid Build Coastguard Worker
2558*cfb92d14SAndroid Build Coastguard Worker    def energy_scan(self, mask, count, period, scan_duration, ipaddr):
2559*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner energy %d %d %d %d %s' % (
2560*cfb92d14SAndroid Build Coastguard Worker            mask,
2561*cfb92d14SAndroid Build Coastguard Worker            count,
2562*cfb92d14SAndroid Build Coastguard Worker            period,
2563*cfb92d14SAndroid Build Coastguard Worker            scan_duration,
2564*cfb92d14SAndroid Build Coastguard Worker            ipaddr,
2565*cfb92d14SAndroid Build Coastguard Worker        )
2566*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2567*cfb92d14SAndroid Build Coastguard Worker
2568*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
2569*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(8)
2570*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
2571*cfb92d14SAndroid Build Coastguard Worker        else:
2572*cfb92d14SAndroid Build Coastguard Worker            timeout = 8
2573*cfb92d14SAndroid Build Coastguard Worker
2574*cfb92d14SAndroid Build Coastguard Worker        self._expect('Energy:', timeout=timeout)
2575*cfb92d14SAndroid Build Coastguard Worker
2576*cfb92d14SAndroid Build Coastguard Worker    def panid_query(self, panid, mask, ipaddr):
2577*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner panid %d %d %s' % (panid, mask, ipaddr)
2578*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2579*cfb92d14SAndroid Build Coastguard Worker
2580*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
2581*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(8)
2582*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
2583*cfb92d14SAndroid Build Coastguard Worker        else:
2584*cfb92d14SAndroid Build Coastguard Worker            timeout = 8
2585*cfb92d14SAndroid Build Coastguard Worker
2586*cfb92d14SAndroid Build Coastguard Worker        self._expect('Conflict:', timeout=timeout)
2587*cfb92d14SAndroid Build Coastguard Worker
2588*cfb92d14SAndroid Build Coastguard Worker    def scan(self, result=1, timeout=10):
2589*cfb92d14SAndroid Build Coastguard Worker        self.send_command('scan')
2590*cfb92d14SAndroid Build Coastguard Worker
2591*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(timeout)
2592*cfb92d14SAndroid Build Coastguard Worker
2593*cfb92d14SAndroid Build Coastguard Worker        if result == 1:
2594*cfb92d14SAndroid Build Coastguard Worker            networks = []
2595*cfb92d14SAndroid Build Coastguard Worker            for line in self._expect_command_output()[2:]:
2596*cfb92d14SAndroid Build Coastguard Worker                _, panid, extaddr, channel, dbm, lqi, _ = map(str.strip, line.split('|'))
2597*cfb92d14SAndroid Build Coastguard Worker                panid = int(panid, 16)
2598*cfb92d14SAndroid Build Coastguard Worker                channel, dbm, lqi = map(int, (channel, dbm, lqi))
2599*cfb92d14SAndroid Build Coastguard Worker
2600*cfb92d14SAndroid Build Coastguard Worker                networks.append({
2601*cfb92d14SAndroid Build Coastguard Worker                    'panid': panid,
2602*cfb92d14SAndroid Build Coastguard Worker                    'extaddr': extaddr,
2603*cfb92d14SAndroid Build Coastguard Worker                    'channel': channel,
2604*cfb92d14SAndroid Build Coastguard Worker                    'dbm': dbm,
2605*cfb92d14SAndroid Build Coastguard Worker                    'lqi': lqi,
2606*cfb92d14SAndroid Build Coastguard Worker                })
2607*cfb92d14SAndroid Build Coastguard Worker            return networks
2608*cfb92d14SAndroid Build Coastguard Worker
2609*cfb92d14SAndroid Build Coastguard Worker    def scan_energy(self, timeout=10):
2610*cfb92d14SAndroid Build Coastguard Worker        self.send_command('scan energy')
2611*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(timeout)
2612*cfb92d14SAndroid Build Coastguard Worker        rssi_list = []
2613*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_command_output()[2:]:
2614*cfb92d14SAndroid Build Coastguard Worker            _, channel, rssi, _ = line.split('|')
2615*cfb92d14SAndroid Build Coastguard Worker            rssi_list.append({
2616*cfb92d14SAndroid Build Coastguard Worker                'channel': int(channel.strip()),
2617*cfb92d14SAndroid Build Coastguard Worker                'rssi': int(rssi.strip()),
2618*cfb92d14SAndroid Build Coastguard Worker            })
2619*cfb92d14SAndroid Build Coastguard Worker        return rssi_list
2620*cfb92d14SAndroid Build Coastguard Worker
2621*cfb92d14SAndroid Build Coastguard Worker    def ping(self, ipaddr, num_responses=1, size=8, timeout=5, count=1, interval=1, hoplimit=64, interface=None):
2622*cfb92d14SAndroid Build Coastguard Worker        args = f'{ipaddr} {size} {count} {interval} {hoplimit} {timeout}'
2623*cfb92d14SAndroid Build Coastguard Worker        if interface is not None:
2624*cfb92d14SAndroid Build Coastguard Worker            args = f'-I {interface} {args}'
2625*cfb92d14SAndroid Build Coastguard Worker        cmd = f'ping {args}'
2626*cfb92d14SAndroid Build Coastguard Worker
2627*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2628*cfb92d14SAndroid Build Coastguard Worker
2629*cfb92d14SAndroid Build Coastguard Worker        wait_allowance = 3
2630*cfb92d14SAndroid Build Coastguard Worker        end = self.simulator.now() + timeout + wait_allowance
2631*cfb92d14SAndroid Build Coastguard Worker
2632*cfb92d14SAndroid Build Coastguard Worker        responders = {}
2633*cfb92d14SAndroid Build Coastguard Worker
2634*cfb92d14SAndroid Build Coastguard Worker        result = True
2635*cfb92d14SAndroid Build Coastguard Worker        # ncp-sim doesn't print Done
2636*cfb92d14SAndroid Build Coastguard Worker        done = (self.node_type == 'ncp-sim')
2637*cfb92d14SAndroid Build Coastguard Worker        while len(responders) < num_responses or not done:
2638*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(1)
2639*cfb92d14SAndroid Build Coastguard Worker            try:
2640*cfb92d14SAndroid Build Coastguard Worker                i = self._expect([r'from (\S+):', r'Done'], timeout=0.1)
2641*cfb92d14SAndroid Build Coastguard Worker            except (pexpect.TIMEOUT, socket.timeout):
2642*cfb92d14SAndroid Build Coastguard Worker                if self.simulator.now() < end:
2643*cfb92d14SAndroid Build Coastguard Worker                    continue
2644*cfb92d14SAndroid Build Coastguard Worker                result = False
2645*cfb92d14SAndroid Build Coastguard Worker                if isinstance(self.simulator, simulator.VirtualTime):
2646*cfb92d14SAndroid Build Coastguard Worker                    self.simulator.sync_devices()
2647*cfb92d14SAndroid Build Coastguard Worker                break
2648*cfb92d14SAndroid Build Coastguard Worker            else:
2649*cfb92d14SAndroid Build Coastguard Worker                if i == 0:
2650*cfb92d14SAndroid Build Coastguard Worker                    responders[self.pexpect.match.groups()[0]] = 1
2651*cfb92d14SAndroid Build Coastguard Worker                elif i == 1:
2652*cfb92d14SAndroid Build Coastguard Worker                    done = True
2653*cfb92d14SAndroid Build Coastguard Worker        return result
2654*cfb92d14SAndroid Build Coastguard Worker
2655*cfb92d14SAndroid Build Coastguard Worker    def reset(self):
2656*cfb92d14SAndroid Build Coastguard Worker        self._reset('reset')
2657*cfb92d14SAndroid Build Coastguard Worker
2658*cfb92d14SAndroid Build Coastguard Worker    def factory_reset(self):
2659*cfb92d14SAndroid Build Coastguard Worker        self._reset('factoryreset')
2660*cfb92d14SAndroid Build Coastguard Worker
2661*cfb92d14SAndroid Build Coastguard Worker    def _reset(self, cmd):
2662*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd, expect_command_echo=False)
2663*cfb92d14SAndroid Build Coastguard Worker        time.sleep(self.RESET_DELAY)
2664*cfb92d14SAndroid Build Coastguard Worker        # Send a "version" command and drain the CLI output after reset
2665*cfb92d14SAndroid Build Coastguard Worker        self.send_command('version', expect_command_echo=False)
2666*cfb92d14SAndroid Build Coastguard Worker        while True:
2667*cfb92d14SAndroid Build Coastguard Worker            try:
2668*cfb92d14SAndroid Build Coastguard Worker                self._expect(r"[^\n]+\n", timeout=0.1)
2669*cfb92d14SAndroid Build Coastguard Worker                continue
2670*cfb92d14SAndroid Build Coastguard Worker            except pexpect.TIMEOUT:
2671*cfb92d14SAndroid Build Coastguard Worker                break
2672*cfb92d14SAndroid Build Coastguard Worker
2673*cfb92d14SAndroid Build Coastguard Worker        if self.is_otbr:
2674*cfb92d14SAndroid Build Coastguard Worker            self.set_log_level(5)
2675*cfb92d14SAndroid Build Coastguard Worker
2676*cfb92d14SAndroid Build Coastguard Worker    def set_router_selection_jitter(self, jitter):
2677*cfb92d14SAndroid Build Coastguard Worker        cmd = 'routerselectionjitter %d' % jitter
2678*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2679*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2680*cfb92d14SAndroid Build Coastguard Worker
2681*cfb92d14SAndroid Build Coastguard Worker    def set_active_dataset(
2682*cfb92d14SAndroid Build Coastguard Worker        self,
2683*cfb92d14SAndroid Build Coastguard Worker        timestamp=None,
2684*cfb92d14SAndroid Build Coastguard Worker        channel=None,
2685*cfb92d14SAndroid Build Coastguard Worker        channel_mask=None,
2686*cfb92d14SAndroid Build Coastguard Worker        extended_panid=None,
2687*cfb92d14SAndroid Build Coastguard Worker        mesh_local_prefix=None,
2688*cfb92d14SAndroid Build Coastguard Worker        network_key=None,
2689*cfb92d14SAndroid Build Coastguard Worker        network_name=None,
2690*cfb92d14SAndroid Build Coastguard Worker        panid=None,
2691*cfb92d14SAndroid Build Coastguard Worker        pskc=None,
2692*cfb92d14SAndroid Build Coastguard Worker        security_policy=[],
2693*cfb92d14SAndroid Build Coastguard Worker        updateExisting=False,
2694*cfb92d14SAndroid Build Coastguard Worker    ):
2695*cfb92d14SAndroid Build Coastguard Worker
2696*cfb92d14SAndroid Build Coastguard Worker        if updateExisting:
2697*cfb92d14SAndroid Build Coastguard Worker            self.send_command('dataset init active', go=False)
2698*cfb92d14SAndroid Build Coastguard Worker        else:
2699*cfb92d14SAndroid Build Coastguard Worker            self.send_command('dataset clear', go=False)
2700*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2701*cfb92d14SAndroid Build Coastguard Worker
2702*cfb92d14SAndroid Build Coastguard Worker        if timestamp is not None:
2703*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset activetimestamp %d' % timestamp
2704*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2705*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2706*cfb92d14SAndroid Build Coastguard Worker
2707*cfb92d14SAndroid Build Coastguard Worker        if channel is not None:
2708*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset channel %d' % channel
2709*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2710*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2711*cfb92d14SAndroid Build Coastguard Worker
2712*cfb92d14SAndroid Build Coastguard Worker        if channel_mask is not None:
2713*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset channelmask %d' % channel_mask
2714*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2715*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2716*cfb92d14SAndroid Build Coastguard Worker
2717*cfb92d14SAndroid Build Coastguard Worker        if extended_panid is not None:
2718*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset extpanid %s' % extended_panid
2719*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2720*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2721*cfb92d14SAndroid Build Coastguard Worker
2722*cfb92d14SAndroid Build Coastguard Worker        if mesh_local_prefix is not None:
2723*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset meshlocalprefix %s' % mesh_local_prefix
2724*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2725*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2726*cfb92d14SAndroid Build Coastguard Worker
2727*cfb92d14SAndroid Build Coastguard Worker        if network_key is not None:
2728*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset networkkey %s' % network_key
2729*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2730*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2731*cfb92d14SAndroid Build Coastguard Worker
2732*cfb92d14SAndroid Build Coastguard Worker        if network_name is not None:
2733*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset networkname %s' % network_name
2734*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2735*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2736*cfb92d14SAndroid Build Coastguard Worker
2737*cfb92d14SAndroid Build Coastguard Worker        if panid is not None:
2738*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset panid %d' % panid
2739*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2740*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2741*cfb92d14SAndroid Build Coastguard Worker
2742*cfb92d14SAndroid Build Coastguard Worker        if pskc is not None:
2743*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset pskc %s' % pskc
2744*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2745*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2746*cfb92d14SAndroid Build Coastguard Worker
2747*cfb92d14SAndroid Build Coastguard Worker        if security_policy is not None:
2748*cfb92d14SAndroid Build Coastguard Worker            if len(security_policy) >= 2:
2749*cfb92d14SAndroid Build Coastguard Worker                cmd = 'dataset securitypolicy %s %s' % (
2750*cfb92d14SAndroid Build Coastguard Worker                    str(security_policy[0]),
2751*cfb92d14SAndroid Build Coastguard Worker                    security_policy[1],
2752*cfb92d14SAndroid Build Coastguard Worker                )
2753*cfb92d14SAndroid Build Coastguard Worker            if len(security_policy) >= 3:
2754*cfb92d14SAndroid Build Coastguard Worker                cmd += ' %s' % (str(security_policy[2]))
2755*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd, go=False)
2756*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2757*cfb92d14SAndroid Build Coastguard Worker
2758*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset commit active', go=False)
2759*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2760*cfb92d14SAndroid Build Coastguard Worker
2761*cfb92d14SAndroid Build Coastguard Worker    def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None, delay=None):
2762*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset clear')
2763*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2764*cfb92d14SAndroid Build Coastguard Worker
2765*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset pendingtimestamp %d' % pendingtimestamp
2766*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2767*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2768*cfb92d14SAndroid Build Coastguard Worker
2769*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset activetimestamp %d' % activetimestamp
2770*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2771*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2772*cfb92d14SAndroid Build Coastguard Worker
2773*cfb92d14SAndroid Build Coastguard Worker        if panid is not None:
2774*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset panid %d' % panid
2775*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2776*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2777*cfb92d14SAndroid Build Coastguard Worker
2778*cfb92d14SAndroid Build Coastguard Worker        if channel is not None:
2779*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset channel %d' % channel
2780*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2781*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2782*cfb92d14SAndroid Build Coastguard Worker
2783*cfb92d14SAndroid Build Coastguard Worker        if delay is not None:
2784*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset delay %d' % delay
2785*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2786*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2787*cfb92d14SAndroid Build Coastguard Worker
2788*cfb92d14SAndroid Build Coastguard Worker        # Set the meshlocal prefix in config.py
2789*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset meshlocalprefix %s' % config.MESH_LOCAL_PREFIX.split('/')[0])
2790*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2791*cfb92d14SAndroid Build Coastguard Worker
2792*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset commit pending')
2793*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2794*cfb92d14SAndroid Build Coastguard Worker
2795*cfb92d14SAndroid Build Coastguard Worker    def start_dataset_updater(self, panid=None, channel=None, security_policy=None, delay=None):
2796*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset clear')
2797*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2798*cfb92d14SAndroid Build Coastguard Worker
2799*cfb92d14SAndroid Build Coastguard Worker        if panid is not None:
2800*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset panid %d' % panid
2801*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2802*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2803*cfb92d14SAndroid Build Coastguard Worker
2804*cfb92d14SAndroid Build Coastguard Worker        if channel is not None:
2805*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset channel %d' % channel
2806*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2807*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2808*cfb92d14SAndroid Build Coastguard Worker
2809*cfb92d14SAndroid Build Coastguard Worker        if security_policy is not None:
2810*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset securitypolicy %d %s ' % (security_policy[0], security_policy[1])
2811*cfb92d14SAndroid Build Coastguard Worker            if (len(security_policy) >= 3):
2812*cfb92d14SAndroid Build Coastguard Worker                cmd += '%d ' % (security_policy[2])
2813*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2814*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2815*cfb92d14SAndroid Build Coastguard Worker
2816*cfb92d14SAndroid Build Coastguard Worker        if delay is not None:
2817*cfb92d14SAndroid Build Coastguard Worker            cmd = 'dataset delay %d ' % delay
2818*cfb92d14SAndroid Build Coastguard Worker            self.send_command(cmd)
2819*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
2820*cfb92d14SAndroid Build Coastguard Worker
2821*cfb92d14SAndroid Build Coastguard Worker        self.send_command('dataset updater start')
2822*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2823*cfb92d14SAndroid Build Coastguard Worker
2824*cfb92d14SAndroid Build Coastguard Worker    def announce_begin(self, mask, count, period, ipaddr):
2825*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner announce %d %d %d %s' % (
2826*cfb92d14SAndroid Build Coastguard Worker            mask,
2827*cfb92d14SAndroid Build Coastguard Worker            count,
2828*cfb92d14SAndroid Build Coastguard Worker            period,
2829*cfb92d14SAndroid Build Coastguard Worker            ipaddr,
2830*cfb92d14SAndroid Build Coastguard Worker        )
2831*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2832*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2833*cfb92d14SAndroid Build Coastguard Worker
2834*cfb92d14SAndroid Build Coastguard Worker    def send_mgmt_active_set(
2835*cfb92d14SAndroid Build Coastguard Worker        self,
2836*cfb92d14SAndroid Build Coastguard Worker        active_timestamp=None,
2837*cfb92d14SAndroid Build Coastguard Worker        channel=None,
2838*cfb92d14SAndroid Build Coastguard Worker        channel_mask=None,
2839*cfb92d14SAndroid Build Coastguard Worker        extended_panid=None,
2840*cfb92d14SAndroid Build Coastguard Worker        panid=None,
2841*cfb92d14SAndroid Build Coastguard Worker        network_key=None,
2842*cfb92d14SAndroid Build Coastguard Worker        mesh_local=None,
2843*cfb92d14SAndroid Build Coastguard Worker        network_name=None,
2844*cfb92d14SAndroid Build Coastguard Worker        security_policy=None,
2845*cfb92d14SAndroid Build Coastguard Worker        binary=None,
2846*cfb92d14SAndroid Build Coastguard Worker    ):
2847*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset mgmtsetcommand active '
2848*cfb92d14SAndroid Build Coastguard Worker
2849*cfb92d14SAndroid Build Coastguard Worker        if active_timestamp is not None:
2850*cfb92d14SAndroid Build Coastguard Worker            cmd += 'activetimestamp %d ' % active_timestamp
2851*cfb92d14SAndroid Build Coastguard Worker
2852*cfb92d14SAndroid Build Coastguard Worker        if channel is not None:
2853*cfb92d14SAndroid Build Coastguard Worker            cmd += 'channel %d ' % channel
2854*cfb92d14SAndroid Build Coastguard Worker
2855*cfb92d14SAndroid Build Coastguard Worker        if channel_mask is not None:
2856*cfb92d14SAndroid Build Coastguard Worker            cmd += 'channelmask %d ' % channel_mask
2857*cfb92d14SAndroid Build Coastguard Worker
2858*cfb92d14SAndroid Build Coastguard Worker        if extended_panid is not None:
2859*cfb92d14SAndroid Build Coastguard Worker            cmd += 'extpanid %s ' % extended_panid
2860*cfb92d14SAndroid Build Coastguard Worker
2861*cfb92d14SAndroid Build Coastguard Worker        if panid is not None:
2862*cfb92d14SAndroid Build Coastguard Worker            cmd += 'panid %d ' % panid
2863*cfb92d14SAndroid Build Coastguard Worker
2864*cfb92d14SAndroid Build Coastguard Worker        if network_key is not None:
2865*cfb92d14SAndroid Build Coastguard Worker            cmd += 'networkkey %s ' % network_key
2866*cfb92d14SAndroid Build Coastguard Worker
2867*cfb92d14SAndroid Build Coastguard Worker        if mesh_local is not None:
2868*cfb92d14SAndroid Build Coastguard Worker            cmd += 'localprefix %s ' % mesh_local
2869*cfb92d14SAndroid Build Coastguard Worker
2870*cfb92d14SAndroid Build Coastguard Worker        if network_name is not None:
2871*cfb92d14SAndroid Build Coastguard Worker            cmd += 'networkname %s ' % self._escape_escapable(network_name)
2872*cfb92d14SAndroid Build Coastguard Worker
2873*cfb92d14SAndroid Build Coastguard Worker        if security_policy is not None:
2874*cfb92d14SAndroid Build Coastguard Worker            cmd += 'securitypolicy %d %s ' % (security_policy[0], security_policy[1])
2875*cfb92d14SAndroid Build Coastguard Worker            if (len(security_policy) >= 3):
2876*cfb92d14SAndroid Build Coastguard Worker                cmd += '%d ' % (security_policy[2])
2877*cfb92d14SAndroid Build Coastguard Worker
2878*cfb92d14SAndroid Build Coastguard Worker        if binary is not None:
2879*cfb92d14SAndroid Build Coastguard Worker            cmd += '-x %s ' % binary
2880*cfb92d14SAndroid Build Coastguard Worker
2881*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2882*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2883*cfb92d14SAndroid Build Coastguard Worker
2884*cfb92d14SAndroid Build Coastguard Worker    def send_mgmt_active_get(self, addr='', tlvs=[]):
2885*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset mgmtgetcommand active'
2886*cfb92d14SAndroid Build Coastguard Worker
2887*cfb92d14SAndroid Build Coastguard Worker        if addr != '':
2888*cfb92d14SAndroid Build Coastguard Worker            cmd += ' address '
2889*cfb92d14SAndroid Build Coastguard Worker            cmd += addr
2890*cfb92d14SAndroid Build Coastguard Worker
2891*cfb92d14SAndroid Build Coastguard Worker        if len(tlvs) != 0:
2892*cfb92d14SAndroid Build Coastguard Worker            tlv_str = ''.join('%02x' % tlv for tlv in tlvs)
2893*cfb92d14SAndroid Build Coastguard Worker            cmd += ' -x '
2894*cfb92d14SAndroid Build Coastguard Worker            cmd += tlv_str
2895*cfb92d14SAndroid Build Coastguard Worker
2896*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2897*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2898*cfb92d14SAndroid Build Coastguard Worker
2899*cfb92d14SAndroid Build Coastguard Worker    def send_mgmt_pending_get(self, addr='', tlvs=[]):
2900*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset mgmtgetcommand pending'
2901*cfb92d14SAndroid Build Coastguard Worker
2902*cfb92d14SAndroid Build Coastguard Worker        if addr != '':
2903*cfb92d14SAndroid Build Coastguard Worker            cmd += ' address '
2904*cfb92d14SAndroid Build Coastguard Worker            cmd += addr
2905*cfb92d14SAndroid Build Coastguard Worker
2906*cfb92d14SAndroid Build Coastguard Worker        if len(tlvs) != 0:
2907*cfb92d14SAndroid Build Coastguard Worker            tlv_str = ''.join('%02x' % tlv for tlv in tlvs)
2908*cfb92d14SAndroid Build Coastguard Worker            cmd += ' -x '
2909*cfb92d14SAndroid Build Coastguard Worker            cmd += tlv_str
2910*cfb92d14SAndroid Build Coastguard Worker
2911*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2912*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2913*cfb92d14SAndroid Build Coastguard Worker
2914*cfb92d14SAndroid Build Coastguard Worker    def send_mgmt_pending_set(
2915*cfb92d14SAndroid Build Coastguard Worker        self,
2916*cfb92d14SAndroid Build Coastguard Worker        pending_timestamp=None,
2917*cfb92d14SAndroid Build Coastguard Worker        active_timestamp=None,
2918*cfb92d14SAndroid Build Coastguard Worker        delay_timer=None,
2919*cfb92d14SAndroid Build Coastguard Worker        channel=None,
2920*cfb92d14SAndroid Build Coastguard Worker        panid=None,
2921*cfb92d14SAndroid Build Coastguard Worker        network_key=None,
2922*cfb92d14SAndroid Build Coastguard Worker        mesh_local=None,
2923*cfb92d14SAndroid Build Coastguard Worker        network_name=None,
2924*cfb92d14SAndroid Build Coastguard Worker    ):
2925*cfb92d14SAndroid Build Coastguard Worker        cmd = 'dataset mgmtsetcommand pending '
2926*cfb92d14SAndroid Build Coastguard Worker        if pending_timestamp is not None:
2927*cfb92d14SAndroid Build Coastguard Worker            cmd += 'pendingtimestamp %d ' % pending_timestamp
2928*cfb92d14SAndroid Build Coastguard Worker
2929*cfb92d14SAndroid Build Coastguard Worker        if active_timestamp is not None:
2930*cfb92d14SAndroid Build Coastguard Worker            cmd += 'activetimestamp %d ' % active_timestamp
2931*cfb92d14SAndroid Build Coastguard Worker
2932*cfb92d14SAndroid Build Coastguard Worker        if delay_timer is not None:
2933*cfb92d14SAndroid Build Coastguard Worker            cmd += 'delaytimer %d ' % delay_timer
2934*cfb92d14SAndroid Build Coastguard Worker
2935*cfb92d14SAndroid Build Coastguard Worker        if channel is not None:
2936*cfb92d14SAndroid Build Coastguard Worker            cmd += 'channel %d ' % channel
2937*cfb92d14SAndroid Build Coastguard Worker
2938*cfb92d14SAndroid Build Coastguard Worker        if panid is not None:
2939*cfb92d14SAndroid Build Coastguard Worker            cmd += 'panid %d ' % panid
2940*cfb92d14SAndroid Build Coastguard Worker
2941*cfb92d14SAndroid Build Coastguard Worker        if network_key is not None:
2942*cfb92d14SAndroid Build Coastguard Worker            cmd += 'networkkey %s ' % network_key
2943*cfb92d14SAndroid Build Coastguard Worker
2944*cfb92d14SAndroid Build Coastguard Worker        if mesh_local is not None:
2945*cfb92d14SAndroid Build Coastguard Worker            cmd += 'localprefix %s ' % mesh_local
2946*cfb92d14SAndroid Build Coastguard Worker
2947*cfb92d14SAndroid Build Coastguard Worker        if network_name is not None:
2948*cfb92d14SAndroid Build Coastguard Worker            cmd += 'networkname %s ' % self._escape_escapable(network_name)
2949*cfb92d14SAndroid Build Coastguard Worker
2950*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2951*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2952*cfb92d14SAndroid Build Coastguard Worker
2953*cfb92d14SAndroid Build Coastguard Worker    def coap_cancel(self):
2954*cfb92d14SAndroid Build Coastguard Worker        """
2955*cfb92d14SAndroid Build Coastguard Worker        Cancel a CoAP subscription.
2956*cfb92d14SAndroid Build Coastguard Worker        """
2957*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap cancel'
2958*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
2959*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
2960*cfb92d14SAndroid Build Coastguard Worker
2961*cfb92d14SAndroid Build Coastguard Worker    def coap_delete(self, ipaddr, uri, con=False, payload=None):
2962*cfb92d14SAndroid Build Coastguard Worker        """
2963*cfb92d14SAndroid Build Coastguard Worker        Send a DELETE request via CoAP.
2964*cfb92d14SAndroid Build Coastguard Worker        """
2965*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq('delete', ipaddr, uri, con, payload)
2966*cfb92d14SAndroid Build Coastguard Worker
2967*cfb92d14SAndroid Build Coastguard Worker    def coap_get(self, ipaddr, uri, con=False, payload=None):
2968*cfb92d14SAndroid Build Coastguard Worker        """
2969*cfb92d14SAndroid Build Coastguard Worker        Send a GET request via CoAP.
2970*cfb92d14SAndroid Build Coastguard Worker        """
2971*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq('get', ipaddr, uri, con, payload)
2972*cfb92d14SAndroid Build Coastguard Worker
2973*cfb92d14SAndroid Build Coastguard Worker    def coap_get_block(self, ipaddr, uri, size=16, count=0):
2974*cfb92d14SAndroid Build Coastguard Worker        """
2975*cfb92d14SAndroid Build Coastguard Worker        Send a GET request via CoAP.
2976*cfb92d14SAndroid Build Coastguard Worker        """
2977*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq_block('get', ipaddr, uri, size, count)
2978*cfb92d14SAndroid Build Coastguard Worker
2979*cfb92d14SAndroid Build Coastguard Worker    def coap_observe(self, ipaddr, uri, con=False, payload=None):
2980*cfb92d14SAndroid Build Coastguard Worker        """
2981*cfb92d14SAndroid Build Coastguard Worker        Send a GET request via CoAP with Observe set.
2982*cfb92d14SAndroid Build Coastguard Worker        """
2983*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq('observe', ipaddr, uri, con, payload)
2984*cfb92d14SAndroid Build Coastguard Worker
2985*cfb92d14SAndroid Build Coastguard Worker    def coap_post(self, ipaddr, uri, con=False, payload=None):
2986*cfb92d14SAndroid Build Coastguard Worker        """
2987*cfb92d14SAndroid Build Coastguard Worker        Send a POST request via CoAP.
2988*cfb92d14SAndroid Build Coastguard Worker        """
2989*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq('post', ipaddr, uri, con, payload)
2990*cfb92d14SAndroid Build Coastguard Worker
2991*cfb92d14SAndroid Build Coastguard Worker    def coap_post_block(self, ipaddr, uri, size=16, count=0):
2992*cfb92d14SAndroid Build Coastguard Worker        """
2993*cfb92d14SAndroid Build Coastguard Worker        Send a POST request via CoAP.
2994*cfb92d14SAndroid Build Coastguard Worker        """
2995*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq_block('post', ipaddr, uri, size, count)
2996*cfb92d14SAndroid Build Coastguard Worker
2997*cfb92d14SAndroid Build Coastguard Worker    def coap_put(self, ipaddr, uri, con=False, payload=None):
2998*cfb92d14SAndroid Build Coastguard Worker        """
2999*cfb92d14SAndroid Build Coastguard Worker        Send a PUT request via CoAP.
3000*cfb92d14SAndroid Build Coastguard Worker        """
3001*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq('put', ipaddr, uri, con, payload)
3002*cfb92d14SAndroid Build Coastguard Worker
3003*cfb92d14SAndroid Build Coastguard Worker    def coap_put_block(self, ipaddr, uri, size=16, count=0):
3004*cfb92d14SAndroid Build Coastguard Worker        """
3005*cfb92d14SAndroid Build Coastguard Worker        Send a PUT request via CoAP.
3006*cfb92d14SAndroid Build Coastguard Worker        """
3007*cfb92d14SAndroid Build Coastguard Worker        return self._coap_rq_block('put', ipaddr, uri, size, count)
3008*cfb92d14SAndroid Build Coastguard Worker
3009*cfb92d14SAndroid Build Coastguard Worker    def _coap_rq(self, method, ipaddr, uri, con=False, payload=None):
3010*cfb92d14SAndroid Build Coastguard Worker        """
3011*cfb92d14SAndroid Build Coastguard Worker        Issue a GET/POST/PUT/DELETE/GET OBSERVE request.
3012*cfb92d14SAndroid Build Coastguard Worker        """
3013*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap %s %s %s' % (method, ipaddr, uri)
3014*cfb92d14SAndroid Build Coastguard Worker        if con:
3015*cfb92d14SAndroid Build Coastguard Worker            cmd += ' con'
3016*cfb92d14SAndroid Build Coastguard Worker        else:
3017*cfb92d14SAndroid Build Coastguard Worker            cmd += ' non'
3018*cfb92d14SAndroid Build Coastguard Worker
3019*cfb92d14SAndroid Build Coastguard Worker        if payload is not None:
3020*cfb92d14SAndroid Build Coastguard Worker            cmd += ' %s' % payload
3021*cfb92d14SAndroid Build Coastguard Worker
3022*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3023*cfb92d14SAndroid Build Coastguard Worker        return self.coap_wait_response()
3024*cfb92d14SAndroid Build Coastguard Worker
3025*cfb92d14SAndroid Build Coastguard Worker    def _coap_rq_block(self, method, ipaddr, uri, size=16, count=0):
3026*cfb92d14SAndroid Build Coastguard Worker        """
3027*cfb92d14SAndroid Build Coastguard Worker        Issue a GET/POST/PUT/DELETE/GET OBSERVE BLOCK request.
3028*cfb92d14SAndroid Build Coastguard Worker        """
3029*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap %s %s %s' % (method, ipaddr, uri)
3030*cfb92d14SAndroid Build Coastguard Worker
3031*cfb92d14SAndroid Build Coastguard Worker        cmd += ' block-%d' % size
3032*cfb92d14SAndroid Build Coastguard Worker
3033*cfb92d14SAndroid Build Coastguard Worker        if count != 0:
3034*cfb92d14SAndroid Build Coastguard Worker            cmd += ' %d' % count
3035*cfb92d14SAndroid Build Coastguard Worker
3036*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3037*cfb92d14SAndroid Build Coastguard Worker        return self.coap_wait_response()
3038*cfb92d14SAndroid Build Coastguard Worker
3039*cfb92d14SAndroid Build Coastguard Worker    def coap_wait_response(self):
3040*cfb92d14SAndroid Build Coastguard Worker        """
3041*cfb92d14SAndroid Build Coastguard Worker        Wait for a CoAP response, and return it.
3042*cfb92d14SAndroid Build Coastguard Worker        """
3043*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3044*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3045*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3046*cfb92d14SAndroid Build Coastguard Worker        else:
3047*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3048*cfb92d14SAndroid Build Coastguard Worker
3049*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'coap response from ([\da-f:]+)(?: OBS=(\d+))?'
3050*cfb92d14SAndroid Build Coastguard Worker                     r'(?: with payload: ([\da-f]+))?\b',
3051*cfb92d14SAndroid Build Coastguard Worker                     timeout=timeout)
3052*cfb92d14SAndroid Build Coastguard Worker        (source, observe, payload) = self.pexpect.match.groups()
3053*cfb92d14SAndroid Build Coastguard Worker        source = source.decode('UTF-8')
3054*cfb92d14SAndroid Build Coastguard Worker
3055*cfb92d14SAndroid Build Coastguard Worker        if observe is not None:
3056*cfb92d14SAndroid Build Coastguard Worker            observe = int(observe, base=10)
3057*cfb92d14SAndroid Build Coastguard Worker
3058*cfb92d14SAndroid Build Coastguard Worker        if payload is not None:
3059*cfb92d14SAndroid Build Coastguard Worker            try:
3060*cfb92d14SAndroid Build Coastguard Worker                payload = binascii.a2b_hex(payload).decode('UTF-8')
3061*cfb92d14SAndroid Build Coastguard Worker            except UnicodeDecodeError:
3062*cfb92d14SAndroid Build Coastguard Worker                pass
3063*cfb92d14SAndroid Build Coastguard Worker
3064*cfb92d14SAndroid Build Coastguard Worker        # Return the values received
3065*cfb92d14SAndroid Build Coastguard Worker        return dict(source=source, observe=observe, payload=payload)
3066*cfb92d14SAndroid Build Coastguard Worker
3067*cfb92d14SAndroid Build Coastguard Worker    def coap_wait_request(self):
3068*cfb92d14SAndroid Build Coastguard Worker        """
3069*cfb92d14SAndroid Build Coastguard Worker        Wait for a CoAP request to be made.
3070*cfb92d14SAndroid Build Coastguard Worker        """
3071*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3072*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3073*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3074*cfb92d14SAndroid Build Coastguard Worker        else:
3075*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3076*cfb92d14SAndroid Build Coastguard Worker
3077*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'coap request from ([\da-f:]+)(?: OBS=(\d+))?'
3078*cfb92d14SAndroid Build Coastguard Worker                     r'(?: with payload: ([\da-f]+))?\b',
3079*cfb92d14SAndroid Build Coastguard Worker                     timeout=timeout)
3080*cfb92d14SAndroid Build Coastguard Worker        (source, observe, payload) = self.pexpect.match.groups()
3081*cfb92d14SAndroid Build Coastguard Worker        source = source.decode('UTF-8')
3082*cfb92d14SAndroid Build Coastguard Worker
3083*cfb92d14SAndroid Build Coastguard Worker        if observe is not None:
3084*cfb92d14SAndroid Build Coastguard Worker            observe = int(observe, base=10)
3085*cfb92d14SAndroid Build Coastguard Worker
3086*cfb92d14SAndroid Build Coastguard Worker        if payload is not None:
3087*cfb92d14SAndroid Build Coastguard Worker            payload = binascii.a2b_hex(payload).decode('UTF-8')
3088*cfb92d14SAndroid Build Coastguard Worker
3089*cfb92d14SAndroid Build Coastguard Worker        # Return the values received
3090*cfb92d14SAndroid Build Coastguard Worker        return dict(source=source, observe=observe, payload=payload)
3091*cfb92d14SAndroid Build Coastguard Worker
3092*cfb92d14SAndroid Build Coastguard Worker    def coap_wait_subscribe(self):
3093*cfb92d14SAndroid Build Coastguard Worker        """
3094*cfb92d14SAndroid Build Coastguard Worker        Wait for a CoAP client to be subscribed.
3095*cfb92d14SAndroid Build Coastguard Worker        """
3096*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3097*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3098*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3099*cfb92d14SAndroid Build Coastguard Worker        else:
3100*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3101*cfb92d14SAndroid Build Coastguard Worker
3102*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'Subscribing client\b', timeout=timeout)
3103*cfb92d14SAndroid Build Coastguard Worker
3104*cfb92d14SAndroid Build Coastguard Worker    def coap_wait_ack(self):
3105*cfb92d14SAndroid Build Coastguard Worker        """
3106*cfb92d14SAndroid Build Coastguard Worker        Wait for a CoAP notification ACK.
3107*cfb92d14SAndroid Build Coastguard Worker        """
3108*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3109*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3110*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3111*cfb92d14SAndroid Build Coastguard Worker        else:
3112*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3113*cfb92d14SAndroid Build Coastguard Worker
3114*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'Received ACK in reply to notification from ([\da-f:]+)\b', timeout=timeout)
3115*cfb92d14SAndroid Build Coastguard Worker        (source,) = self.pexpect.match.groups()
3116*cfb92d14SAndroid Build Coastguard Worker        source = source.decode('UTF-8')
3117*cfb92d14SAndroid Build Coastguard Worker
3118*cfb92d14SAndroid Build Coastguard Worker        return source
3119*cfb92d14SAndroid Build Coastguard Worker
3120*cfb92d14SAndroid Build Coastguard Worker    def coap_set_resource_path(self, path):
3121*cfb92d14SAndroid Build Coastguard Worker        """
3122*cfb92d14SAndroid Build Coastguard Worker        Set the path for the CoAP resource.
3123*cfb92d14SAndroid Build Coastguard Worker        """
3124*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap resource %s' % path
3125*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3126*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3127*cfb92d14SAndroid Build Coastguard Worker
3128*cfb92d14SAndroid Build Coastguard Worker    def coap_set_resource_path_block(self, path, count=0):
3129*cfb92d14SAndroid Build Coastguard Worker        """
3130*cfb92d14SAndroid Build Coastguard Worker        Set the path for the CoAP resource and how many blocks can be received from this resource.
3131*cfb92d14SAndroid Build Coastguard Worker        """
3132*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap resource %s %d' % (path, count)
3133*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3134*cfb92d14SAndroid Build Coastguard Worker        self._expect('Done')
3135*cfb92d14SAndroid Build Coastguard Worker
3136*cfb92d14SAndroid Build Coastguard Worker    def coap_set_content(self, content):
3137*cfb92d14SAndroid Build Coastguard Worker        """
3138*cfb92d14SAndroid Build Coastguard Worker        Set the content of the CoAP resource.
3139*cfb92d14SAndroid Build Coastguard Worker        """
3140*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap set %s' % content
3141*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3142*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3143*cfb92d14SAndroid Build Coastguard Worker
3144*cfb92d14SAndroid Build Coastguard Worker    def coap_start(self):
3145*cfb92d14SAndroid Build Coastguard Worker        """
3146*cfb92d14SAndroid Build Coastguard Worker        Start the CoAP service.
3147*cfb92d14SAndroid Build Coastguard Worker        """
3148*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap start'
3149*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3150*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3151*cfb92d14SAndroid Build Coastguard Worker
3152*cfb92d14SAndroid Build Coastguard Worker    def coap_stop(self):
3153*cfb92d14SAndroid Build Coastguard Worker        """
3154*cfb92d14SAndroid Build Coastguard Worker        Stop the CoAP service.
3155*cfb92d14SAndroid Build Coastguard Worker        """
3156*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coap stop'
3157*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3158*cfb92d14SAndroid Build Coastguard Worker
3159*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3160*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3161*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3162*cfb92d14SAndroid Build Coastguard Worker        else:
3163*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3164*cfb92d14SAndroid Build Coastguard Worker
3165*cfb92d14SAndroid Build Coastguard Worker        self._expect_done(timeout=timeout)
3166*cfb92d14SAndroid Build Coastguard Worker
3167*cfb92d14SAndroid Build Coastguard Worker    def coaps_start_psk(self, psk, pskIdentity):
3168*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps psk %s %s' % (psk, pskIdentity)
3169*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3170*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3171*cfb92d14SAndroid Build Coastguard Worker
3172*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps start'
3173*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3174*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3175*cfb92d14SAndroid Build Coastguard Worker
3176*cfb92d14SAndroid Build Coastguard Worker    def coaps_start_x509(self):
3177*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps x509'
3178*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3179*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3180*cfb92d14SAndroid Build Coastguard Worker
3181*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps start'
3182*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3183*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3184*cfb92d14SAndroid Build Coastguard Worker
3185*cfb92d14SAndroid Build Coastguard Worker    def coaps_set_resource_path(self, path):
3186*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps resource %s' % path
3187*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3188*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3189*cfb92d14SAndroid Build Coastguard Worker
3190*cfb92d14SAndroid Build Coastguard Worker    def coaps_stop(self):
3191*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps stop'
3192*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3193*cfb92d14SAndroid Build Coastguard Worker
3194*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3195*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3196*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3197*cfb92d14SAndroid Build Coastguard Worker        else:
3198*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3199*cfb92d14SAndroid Build Coastguard Worker
3200*cfb92d14SAndroid Build Coastguard Worker        self._expect_done(timeout=timeout)
3201*cfb92d14SAndroid Build Coastguard Worker
3202*cfb92d14SAndroid Build Coastguard Worker    def coaps_connect(self, ipaddr):
3203*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps connect %s' % ipaddr
3204*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3205*cfb92d14SAndroid Build Coastguard Worker
3206*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3207*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3208*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3209*cfb92d14SAndroid Build Coastguard Worker        else:
3210*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3211*cfb92d14SAndroid Build Coastguard Worker
3212*cfb92d14SAndroid Build Coastguard Worker        self._expect('coaps connected', timeout=timeout)
3213*cfb92d14SAndroid Build Coastguard Worker
3214*cfb92d14SAndroid Build Coastguard Worker    def coaps_disconnect(self):
3215*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps disconnect'
3216*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3217*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3218*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(5)
3219*cfb92d14SAndroid Build Coastguard Worker
3220*cfb92d14SAndroid Build Coastguard Worker    def coaps_get(self):
3221*cfb92d14SAndroid Build Coastguard Worker        cmd = 'coaps get test'
3222*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3223*cfb92d14SAndroid Build Coastguard Worker
3224*cfb92d14SAndroid Build Coastguard Worker        if isinstance(self.simulator, simulator.VirtualTime):
3225*cfb92d14SAndroid Build Coastguard Worker            self.simulator.go(5)
3226*cfb92d14SAndroid Build Coastguard Worker            timeout = 1
3227*cfb92d14SAndroid Build Coastguard Worker        else:
3228*cfb92d14SAndroid Build Coastguard Worker            timeout = 5
3229*cfb92d14SAndroid Build Coastguard Worker
3230*cfb92d14SAndroid Build Coastguard Worker        self._expect('coaps response', timeout=timeout)
3231*cfb92d14SAndroid Build Coastguard Worker
3232*cfb92d14SAndroid Build Coastguard Worker    def commissioner_mgmtget(self, tlvs_binary=None):
3233*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner mgmtget'
3234*cfb92d14SAndroid Build Coastguard Worker        if tlvs_binary is not None:
3235*cfb92d14SAndroid Build Coastguard Worker            cmd += ' -x %s' % tlvs_binary
3236*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3237*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3238*cfb92d14SAndroid Build Coastguard Worker
3239*cfb92d14SAndroid Build Coastguard Worker    def commissioner_mgmtset(self, tlvs_binary):
3240*cfb92d14SAndroid Build Coastguard Worker        cmd = 'commissioner mgmtset -x %s' % tlvs_binary
3241*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3242*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3243*cfb92d14SAndroid Build Coastguard Worker
3244*cfb92d14SAndroid Build Coastguard Worker    def bytes_to_hex_str(self, src):
3245*cfb92d14SAndroid Build Coastguard Worker        return ''.join(format(x, '02x') for x in src)
3246*cfb92d14SAndroid Build Coastguard Worker
3247*cfb92d14SAndroid Build Coastguard Worker    def commissioner_mgmtset_with_tlvs(self, tlvs):
3248*cfb92d14SAndroid Build Coastguard Worker        payload = bytearray()
3249*cfb92d14SAndroid Build Coastguard Worker        for tlv in tlvs:
3250*cfb92d14SAndroid Build Coastguard Worker            payload += tlv.to_hex()
3251*cfb92d14SAndroid Build Coastguard Worker        self.commissioner_mgmtset(self.bytes_to_hex_str(payload))
3252*cfb92d14SAndroid Build Coastguard Worker
3253*cfb92d14SAndroid Build Coastguard Worker    def udp_start(self, local_ipaddr, local_port, bind_unspecified=False):
3254*cfb92d14SAndroid Build Coastguard Worker        cmd = 'udp open'
3255*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3256*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3257*cfb92d14SAndroid Build Coastguard Worker
3258*cfb92d14SAndroid Build Coastguard Worker        cmd = 'udp bind %s %s %s' % ("-u" if bind_unspecified else "", local_ipaddr, local_port)
3259*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3260*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3261*cfb92d14SAndroid Build Coastguard Worker
3262*cfb92d14SAndroid Build Coastguard Worker    def udp_stop(self):
3263*cfb92d14SAndroid Build Coastguard Worker        cmd = 'udp close'
3264*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3265*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3266*cfb92d14SAndroid Build Coastguard Worker
3267*cfb92d14SAndroid Build Coastguard Worker    def udp_send(self, bytes, ipaddr, port, success=True):
3268*cfb92d14SAndroid Build Coastguard Worker        cmd = 'udp send %s %d -s %d ' % (ipaddr, port, bytes)
3269*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3270*cfb92d14SAndroid Build Coastguard Worker        if success:
3271*cfb92d14SAndroid Build Coastguard Worker            self._expect_done()
3272*cfb92d14SAndroid Build Coastguard Worker        else:
3273*cfb92d14SAndroid Build Coastguard Worker            self._expect('Error')
3274*cfb92d14SAndroid Build Coastguard Worker
3275*cfb92d14SAndroid Build Coastguard Worker    def udp_check_rx(self, bytes_should_rx):
3276*cfb92d14SAndroid Build Coastguard Worker        self._expect('%d bytes' % bytes_should_rx)
3277*cfb92d14SAndroid Build Coastguard Worker
3278*cfb92d14SAndroid Build Coastguard Worker    def set_routereligible(self, enable: bool):
3279*cfb92d14SAndroid Build Coastguard Worker        cmd = f'routereligible {"enable" if enable else "disable"}'
3280*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3281*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3282*cfb92d14SAndroid Build Coastguard Worker
3283*cfb92d14SAndroid Build Coastguard Worker    def router_list(self):
3284*cfb92d14SAndroid Build Coastguard Worker        cmd = 'router list'
3285*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3286*cfb92d14SAndroid Build Coastguard Worker        self._expect([r'(\d+)((\s\d+)*)'])
3287*cfb92d14SAndroid Build Coastguard Worker
3288*cfb92d14SAndroid Build Coastguard Worker        g = self.pexpect.match.groups()
3289*cfb92d14SAndroid Build Coastguard Worker        router_list = g[0].decode('utf8') + ' ' + g[1].decode('utf8')
3290*cfb92d14SAndroid Build Coastguard Worker        router_list = [int(x) for x in router_list.split()]
3291*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3292*cfb92d14SAndroid Build Coastguard Worker        return router_list
3293*cfb92d14SAndroid Build Coastguard Worker
3294*cfb92d14SAndroid Build Coastguard Worker    def router_table(self):
3295*cfb92d14SAndroid Build Coastguard Worker        cmd = 'router table'
3296*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3297*cfb92d14SAndroid Build Coastguard Worker
3298*cfb92d14SAndroid Build Coastguard Worker        self._expect(r'(.*)Done')
3299*cfb92d14SAndroid Build Coastguard Worker        g = self.pexpect.match.groups()
3300*cfb92d14SAndroid Build Coastguard Worker        output = g[0].decode('utf8')
3301*cfb92d14SAndroid Build Coastguard Worker        lines = output.strip().split('\n')
3302*cfb92d14SAndroid Build Coastguard Worker        lines = [l.strip() for l in lines]
3303*cfb92d14SAndroid Build Coastguard Worker        router_table = {}
3304*cfb92d14SAndroid Build Coastguard Worker        for i, line in enumerate(lines):
3305*cfb92d14SAndroid Build Coastguard Worker            if not line.startswith('|') or not line.endswith('|'):
3306*cfb92d14SAndroid Build Coastguard Worker                if i not in (0, 2):
3307*cfb92d14SAndroid Build Coastguard Worker                    # should not happen
3308*cfb92d14SAndroid Build Coastguard Worker                    print("unexpected line %d: %s" % (i, line))
3309*cfb92d14SAndroid Build Coastguard Worker
3310*cfb92d14SAndroid Build Coastguard Worker                continue
3311*cfb92d14SAndroid Build Coastguard Worker
3312*cfb92d14SAndroid Build Coastguard Worker            line = line[1:][:-1]
3313*cfb92d14SAndroid Build Coastguard Worker            line = [x.strip() for x in line.split('|')]
3314*cfb92d14SAndroid Build Coastguard Worker            if len(line) < 9:
3315*cfb92d14SAndroid Build Coastguard Worker                print("unexpected line %d: %s" % (i, line))
3316*cfb92d14SAndroid Build Coastguard Worker                continue
3317*cfb92d14SAndroid Build Coastguard Worker
3318*cfb92d14SAndroid Build Coastguard Worker            try:
3319*cfb92d14SAndroid Build Coastguard Worker                int(line[0])
3320*cfb92d14SAndroid Build Coastguard Worker            except ValueError:
3321*cfb92d14SAndroid Build Coastguard Worker                if i != 1:
3322*cfb92d14SAndroid Build Coastguard Worker                    print("unexpected line %d: %s" % (i, line))
3323*cfb92d14SAndroid Build Coastguard Worker                continue
3324*cfb92d14SAndroid Build Coastguard Worker
3325*cfb92d14SAndroid Build Coastguard Worker            id = int(line[0])
3326*cfb92d14SAndroid Build Coastguard Worker            rloc16 = int(line[1], 16)
3327*cfb92d14SAndroid Build Coastguard Worker            nexthop = int(line[2])
3328*cfb92d14SAndroid Build Coastguard Worker            pathcost = int(line[3])
3329*cfb92d14SAndroid Build Coastguard Worker            lqin = int(line[4])
3330*cfb92d14SAndroid Build Coastguard Worker            lqout = int(line[5])
3331*cfb92d14SAndroid Build Coastguard Worker            age = int(line[6])
3332*cfb92d14SAndroid Build Coastguard Worker            emac = str(line[7])
3333*cfb92d14SAndroid Build Coastguard Worker            link = int(line[8])
3334*cfb92d14SAndroid Build Coastguard Worker
3335*cfb92d14SAndroid Build Coastguard Worker            router_table[id] = {
3336*cfb92d14SAndroid Build Coastguard Worker                'rloc16': rloc16,
3337*cfb92d14SAndroid Build Coastguard Worker                'nexthop': nexthop,
3338*cfb92d14SAndroid Build Coastguard Worker                'pathcost': pathcost,
3339*cfb92d14SAndroid Build Coastguard Worker                'lqin': lqin,
3340*cfb92d14SAndroid Build Coastguard Worker                'lqout': lqout,
3341*cfb92d14SAndroid Build Coastguard Worker                'age': age,
3342*cfb92d14SAndroid Build Coastguard Worker                'emac': emac,
3343*cfb92d14SAndroid Build Coastguard Worker                'link': link,
3344*cfb92d14SAndroid Build Coastguard Worker            }
3345*cfb92d14SAndroid Build Coastguard Worker
3346*cfb92d14SAndroid Build Coastguard Worker        return router_table
3347*cfb92d14SAndroid Build Coastguard Worker
3348*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_request_single_probe(self, dst_addr: str, linkmetrics_flags: str, mode: str = ''):
3349*cfb92d14SAndroid Build Coastguard Worker        cmd = 'linkmetrics request %s %s single %s' % (mode, dst_addr, linkmetrics_flags)
3350*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3351*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(5)
3352*cfb92d14SAndroid Build Coastguard Worker        return self._parse_linkmetrics_query_result(self._expect_command_output())
3353*cfb92d14SAndroid Build Coastguard Worker
3354*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_request_forward_tracking_series(self, dst_addr: str, series_id: int, mode: str = ''):
3355*cfb92d14SAndroid Build Coastguard Worker        cmd = 'linkmetrics request %s %s forward %d' % (mode, dst_addr, series_id)
3356*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3357*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(5)
3358*cfb92d14SAndroid Build Coastguard Worker        return self._parse_linkmetrics_query_result(self._expect_command_output())
3359*cfb92d14SAndroid Build Coastguard Worker
3360*cfb92d14SAndroid Build Coastguard Worker    def _parse_linkmetrics_query_result(self, lines):
3361*cfb92d14SAndroid Build Coastguard Worker        """Parse link metrics query result"""
3362*cfb92d14SAndroid Build Coastguard Worker
3363*cfb92d14SAndroid Build Coastguard Worker        # Example of command output:
3364*cfb92d14SAndroid Build Coastguard Worker        # ['Received Link Metrics Report from: fe80:0:0:0:146e:a00:0:1',
3365*cfb92d14SAndroid Build Coastguard Worker        #  '- PDU Counter: 1 (Count/Summation)',
3366*cfb92d14SAndroid Build Coastguard Worker        #  '- LQI: 0 (Exponential Moving Average)',
3367*cfb92d14SAndroid Build Coastguard Worker        #  '- Margin: 80 (dB) (Exponential Moving Average)',
3368*cfb92d14SAndroid Build Coastguard Worker        #  '- RSSI: -20 (dBm) (Exponential Moving Average)']
3369*cfb92d14SAndroid Build Coastguard Worker        #
3370*cfb92d14SAndroid Build Coastguard Worker        # Or 'Link Metrics Report, status: {status}'
3371*cfb92d14SAndroid Build Coastguard Worker
3372*cfb92d14SAndroid Build Coastguard Worker        result = {}
3373*cfb92d14SAndroid Build Coastguard Worker        for line in lines:
3374*cfb92d14SAndroid Build Coastguard Worker            if line.startswith('- '):
3375*cfb92d14SAndroid Build Coastguard Worker                k, v = line[2:].split(': ')
3376*cfb92d14SAndroid Build Coastguard Worker                result[k] = v.split(' ')[0]
3377*cfb92d14SAndroid Build Coastguard Worker            elif line.startswith('Link Metrics Report, status: '):
3378*cfb92d14SAndroid Build Coastguard Worker                result['Status'] = line[29:]
3379*cfb92d14SAndroid Build Coastguard Worker        return result
3380*cfb92d14SAndroid Build Coastguard Worker
3381*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_config_req_enhanced_ack_based_probing(self,
3382*cfb92d14SAndroid Build Coastguard Worker                                                           dst_addr: str,
3383*cfb92d14SAndroid Build Coastguard Worker                                                           enable: bool,
3384*cfb92d14SAndroid Build Coastguard Worker                                                           metrics_flags: str,
3385*cfb92d14SAndroid Build Coastguard Worker                                                           ext_flags='',
3386*cfb92d14SAndroid Build Coastguard Worker                                                           mode: str = ''):
3387*cfb92d14SAndroid Build Coastguard Worker        cmd = "linkmetrics config %s %s enhanced-ack" % (mode, dst_addr)
3388*cfb92d14SAndroid Build Coastguard Worker        if enable:
3389*cfb92d14SAndroid Build Coastguard Worker            cmd = cmd + (" register %s %s" % (metrics_flags, ext_flags))
3390*cfb92d14SAndroid Build Coastguard Worker        else:
3391*cfb92d14SAndroid Build Coastguard Worker            cmd = cmd + " clear"
3392*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3393*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3394*cfb92d14SAndroid Build Coastguard Worker
3395*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_config_req_forward_tracking_series(self,
3396*cfb92d14SAndroid Build Coastguard Worker                                                        dst_addr: str,
3397*cfb92d14SAndroid Build Coastguard Worker                                                        series_id: int,
3398*cfb92d14SAndroid Build Coastguard Worker                                                        series_flags: str,
3399*cfb92d14SAndroid Build Coastguard Worker                                                        metrics_flags: str,
3400*cfb92d14SAndroid Build Coastguard Worker                                                        mode: str = ''):
3401*cfb92d14SAndroid Build Coastguard Worker        cmd = "linkmetrics config %s %s forward %d %s %s" % (mode, dst_addr, series_id, series_flags, metrics_flags)
3402*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3403*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3404*cfb92d14SAndroid Build Coastguard Worker
3405*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_send_link_probe(self, dst_addr: str, series_id: int, length: int):
3406*cfb92d14SAndroid Build Coastguard Worker        cmd = "linkmetrics probe %s %d %d" % (dst_addr, series_id, length)
3407*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3408*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3409*cfb92d14SAndroid Build Coastguard Worker
3410*cfb92d14SAndroid Build Coastguard Worker    def link_metrics_mgr_set_enabled(self, enable: bool):
3411*cfb92d14SAndroid Build Coastguard Worker        op_str = "enable" if enable else "disable"
3412*cfb92d14SAndroid Build Coastguard Worker        cmd = f'linkmetricsmgr {op_str}'
3413*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3414*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3415*cfb92d14SAndroid Build Coastguard Worker
3416*cfb92d14SAndroid Build Coastguard Worker    def send_address_notification(self, dst: str, target: str, mliid: str):
3417*cfb92d14SAndroid Build Coastguard Worker        cmd = f'fake /a/an {dst} {target} {mliid}'
3418*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3419*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3420*cfb92d14SAndroid Build Coastguard Worker
3421*cfb92d14SAndroid Build Coastguard Worker    def send_proactive_backbone_notification(self, target: str, mliid: str, ltt: int):
3422*cfb92d14SAndroid Build Coastguard Worker        cmd = f'fake /b/ba {target} {mliid} {ltt}'
3423*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3424*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3425*cfb92d14SAndroid Build Coastguard Worker
3426*cfb92d14SAndroid Build Coastguard Worker    def dns_get_config(self):
3427*cfb92d14SAndroid Build Coastguard Worker        """
3428*cfb92d14SAndroid Build Coastguard Worker        Returns the DNS config as a list of property dictionary (string key and string value).
3429*cfb92d14SAndroid Build Coastguard Worker
3430*cfb92d14SAndroid Build Coastguard Worker        Example output:
3431*cfb92d14SAndroid Build Coastguard Worker        {
3432*cfb92d14SAndroid Build Coastguard Worker            'Server': '[fd00:0:0:0:0:0:0:1]:1234'
3433*cfb92d14SAndroid Build Coastguard Worker            'ResponseTimeout': '5000 ms'
3434*cfb92d14SAndroid Build Coastguard Worker            'MaxTxAttempts': '2'
3435*cfb92d14SAndroid Build Coastguard Worker            'RecursionDesired': 'no'
3436*cfb92d14SAndroid Build Coastguard Worker        }
3437*cfb92d14SAndroid Build Coastguard Worker        """
3438*cfb92d14SAndroid Build Coastguard Worker        cmd = f'dns config'
3439*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3440*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
3441*cfb92d14SAndroid Build Coastguard Worker        config = {}
3442*cfb92d14SAndroid Build Coastguard Worker        for line in output:
3443*cfb92d14SAndroid Build Coastguard Worker            k, v = line.split(': ')
3444*cfb92d14SAndroid Build Coastguard Worker            config[k] = v
3445*cfb92d14SAndroid Build Coastguard Worker        return config
3446*cfb92d14SAndroid Build Coastguard Worker
3447*cfb92d14SAndroid Build Coastguard Worker    def dns_set_config(self, config):
3448*cfb92d14SAndroid Build Coastguard Worker        cmd = f'dns config {config}'
3449*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3450*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3451*cfb92d14SAndroid Build Coastguard Worker
3452*cfb92d14SAndroid Build Coastguard Worker    def dns_resolve(self, hostname, server=None, port=53):
3453*cfb92d14SAndroid Build Coastguard Worker        cmd = f'dns resolve {hostname}'
3454*cfb92d14SAndroid Build Coastguard Worker        if server is not None:
3455*cfb92d14SAndroid Build Coastguard Worker            cmd += f' {server} {port}'
3456*cfb92d14SAndroid Build Coastguard Worker
3457*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3458*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(10)
3459*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
3460*cfb92d14SAndroid Build Coastguard Worker        dns_resp = output[0]
3461*cfb92d14SAndroid Build Coastguard Worker        # example output: "DNS response for host1.default.service.arpa. - fd00:db8:0:0:fd3d:d471:1e8c:b60 TTL:7190 "
3462*cfb92d14SAndroid Build Coastguard Worker        #                 " fd00:db8:0:0:0:ff:fe00:9000 TTL:7190"
3463*cfb92d14SAndroid Build Coastguard Worker        addrs = dns_resp.strip().split(' - ')[1].split(' ')
3464*cfb92d14SAndroid Build Coastguard Worker        ip = [item.strip() for item in addrs[::2]]
3465*cfb92d14SAndroid Build Coastguard Worker        ttl = [int(item.split('TTL:')[1]) for item in addrs[1::2]]
3466*cfb92d14SAndroid Build Coastguard Worker
3467*cfb92d14SAndroid Build Coastguard Worker        return list(zip(ip, ttl))
3468*cfb92d14SAndroid Build Coastguard Worker
3469*cfb92d14SAndroid Build Coastguard Worker    def _parse_dns_service_info(self, output):
3470*cfb92d14SAndroid Build Coastguard Worker        # Example of `output`
3471*cfb92d14SAndroid Build Coastguard Worker        #   Port:22222, Priority:2, Weight:2, TTL:7155
3472*cfb92d14SAndroid Build Coastguard Worker        #   Host:host2.default.service.arpa.
3473*cfb92d14SAndroid Build Coastguard Worker        #   HostAddress:0:0:0:0:0:0:0:0 TTL:0
3474*cfb92d14SAndroid Build Coastguard Worker        #   TXT:[a=00, b=02bb] TTL:7155
3475*cfb92d14SAndroid Build Coastguard Worker
3476*cfb92d14SAndroid Build Coastguard Worker        m = re.match(
3477*cfb92d14SAndroid Build Coastguard Worker            r'.*Port:(\d+), Priority:(\d+), Weight:(\d+), TTL:(\d+)\s+Host:(.*?)\s+HostAddress:(\S+) TTL:(\d+)\s+TXT:\[(.*?)\] TTL:(\d+)',
3478*cfb92d14SAndroid Build Coastguard Worker            '\r'.join(output))
3479*cfb92d14SAndroid Build Coastguard Worker        if not m:
3480*cfb92d14SAndroid Build Coastguard Worker            return {}
3481*cfb92d14SAndroid Build Coastguard Worker        port, priority, weight, srv_ttl, hostname, address, aaaa_ttl, txt_data, txt_ttl = m.groups()
3482*cfb92d14SAndroid Build Coastguard Worker        return {
3483*cfb92d14SAndroid Build Coastguard Worker            'port': int(port),
3484*cfb92d14SAndroid Build Coastguard Worker            'priority': int(priority),
3485*cfb92d14SAndroid Build Coastguard Worker            'weight': int(weight),
3486*cfb92d14SAndroid Build Coastguard Worker            'host': hostname,
3487*cfb92d14SAndroid Build Coastguard Worker            'address': address,
3488*cfb92d14SAndroid Build Coastguard Worker            'txt_data': txt_data,
3489*cfb92d14SAndroid Build Coastguard Worker            'srv_ttl': int(srv_ttl),
3490*cfb92d14SAndroid Build Coastguard Worker            'txt_ttl': int(txt_ttl),
3491*cfb92d14SAndroid Build Coastguard Worker            'aaaa_ttl': int(aaaa_ttl),
3492*cfb92d14SAndroid Build Coastguard Worker        }
3493*cfb92d14SAndroid Build Coastguard Worker
3494*cfb92d14SAndroid Build Coastguard Worker    def dns_resolve_service(self, instance, service, server=None, port=53):
3495*cfb92d14SAndroid Build Coastguard Worker        """
3496*cfb92d14SAndroid Build Coastguard Worker        Resolves the service instance and returns the instance information as a dict.
3497*cfb92d14SAndroid Build Coastguard Worker
3498*cfb92d14SAndroid Build Coastguard Worker        Example return value:
3499*cfb92d14SAndroid Build Coastguard Worker            {
3500*cfb92d14SAndroid Build Coastguard Worker                'port': 12345,
3501*cfb92d14SAndroid Build Coastguard Worker                'priority': 0,
3502*cfb92d14SAndroid Build Coastguard Worker                'weight': 0,
3503*cfb92d14SAndroid Build Coastguard Worker                'host': 'ins1._ipps._tcp.default.service.arpa.',
3504*cfb92d14SAndroid Build Coastguard Worker                'address': '2001::1',
3505*cfb92d14SAndroid Build Coastguard Worker                'txt_data': 'a=00, b=02bb',
3506*cfb92d14SAndroid Build Coastguard Worker                'srv_ttl': 7100,
3507*cfb92d14SAndroid Build Coastguard Worker                'txt_ttl': 7100,
3508*cfb92d14SAndroid Build Coastguard Worker                'aaaa_ttl': 7100,
3509*cfb92d14SAndroid Build Coastguard Worker            }
3510*cfb92d14SAndroid Build Coastguard Worker        """
3511*cfb92d14SAndroid Build Coastguard Worker        instance = self._escape_escapable(instance)
3512*cfb92d14SAndroid Build Coastguard Worker        cmd = f'dns service {instance} {service}'
3513*cfb92d14SAndroid Build Coastguard Worker        if server is not None:
3514*cfb92d14SAndroid Build Coastguard Worker            cmd += f' {server} {port}'
3515*cfb92d14SAndroid Build Coastguard Worker
3516*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3517*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(10)
3518*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
3519*cfb92d14SAndroid Build Coastguard Worker        info = self._parse_dns_service_info(output)
3520*cfb92d14SAndroid Build Coastguard Worker        if not info:
3521*cfb92d14SAndroid Build Coastguard Worker            raise Exception('dns resolve service failed: %s.%s' % (instance, service))
3522*cfb92d14SAndroid Build Coastguard Worker        return info
3523*cfb92d14SAndroid Build Coastguard Worker
3524*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
3525*cfb92d14SAndroid Build Coastguard Worker    def __parse_hex_string(hexstr: str) -> bytes:
3526*cfb92d14SAndroid Build Coastguard Worker        assert (len(hexstr) % 2 == 0)
3527*cfb92d14SAndroid Build Coastguard Worker        return bytes(int(hexstr[i:i + 2], 16) for i in range(0, len(hexstr), 2))
3528*cfb92d14SAndroid Build Coastguard Worker
3529*cfb92d14SAndroid Build Coastguard Worker    def dns_browse(self, service_name, server=None, port=53):
3530*cfb92d14SAndroid Build Coastguard Worker        """
3531*cfb92d14SAndroid Build Coastguard Worker        Browse the service and returns the instances.
3532*cfb92d14SAndroid Build Coastguard Worker
3533*cfb92d14SAndroid Build Coastguard Worker        Example return value:
3534*cfb92d14SAndroid Build Coastguard Worker            {
3535*cfb92d14SAndroid Build Coastguard Worker                'ins1': {
3536*cfb92d14SAndroid Build Coastguard Worker                    'port': 12345,
3537*cfb92d14SAndroid Build Coastguard Worker                    'priority': 1,
3538*cfb92d14SAndroid Build Coastguard Worker                    'weight': 1,
3539*cfb92d14SAndroid Build Coastguard Worker                    'host': 'ins1._ipps._tcp.default.service.arpa.',
3540*cfb92d14SAndroid Build Coastguard Worker                    'address': '2001::1',
3541*cfb92d14SAndroid Build Coastguard Worker                    'txt_data': 'a=00, b=11cf',
3542*cfb92d14SAndroid Build Coastguard Worker                    'srv_ttl': 7100,
3543*cfb92d14SAndroid Build Coastguard Worker                    'txt_ttl': 7100,
3544*cfb92d14SAndroid Build Coastguard Worker                    'aaaa_ttl': 7100,
3545*cfb92d14SAndroid Build Coastguard Worker                },
3546*cfb92d14SAndroid Build Coastguard Worker                'ins2': {
3547*cfb92d14SAndroid Build Coastguard Worker                    'port': 12345,
3548*cfb92d14SAndroid Build Coastguard Worker                    'priority': 2,
3549*cfb92d14SAndroid Build Coastguard Worker                    'weight': 2,
3550*cfb92d14SAndroid Build Coastguard Worker                    'host': 'ins2._ipps._tcp.default.service.arpa.',
3551*cfb92d14SAndroid Build Coastguard Worker                    'address': '2001::2',
3552*cfb92d14SAndroid Build Coastguard Worker                    'txt_data': 'a=01, b=23dd',
3553*cfb92d14SAndroid Build Coastguard Worker                    'srv_ttl': 7100,
3554*cfb92d14SAndroid Build Coastguard Worker                    'txt_ttl': 7100,
3555*cfb92d14SAndroid Build Coastguard Worker                    'aaaa_ttl': 7100,
3556*cfb92d14SAndroid Build Coastguard Worker                }
3557*cfb92d14SAndroid Build Coastguard Worker            }
3558*cfb92d14SAndroid Build Coastguard Worker        """
3559*cfb92d14SAndroid Build Coastguard Worker        cmd = f'dns browse {service_name}'
3560*cfb92d14SAndroid Build Coastguard Worker        if server is not None:
3561*cfb92d14SAndroid Build Coastguard Worker            cmd += f' {server} {port}'
3562*cfb92d14SAndroid Build Coastguard Worker
3563*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3564*cfb92d14SAndroid Build Coastguard Worker        self.simulator.go(10)
3565*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
3566*cfb92d14SAndroid Build Coastguard Worker
3567*cfb92d14SAndroid Build Coastguard Worker        # Example output:
3568*cfb92d14SAndroid Build Coastguard Worker        # DNS browse response for _ipps._tcp.default.service.arpa.
3569*cfb92d14SAndroid Build Coastguard Worker        # ins2
3570*cfb92d14SAndroid Build Coastguard Worker        #     Port:22222, Priority:2, Weight:2, TTL:7175
3571*cfb92d14SAndroid Build Coastguard Worker        #     Host:host2.default.service.arpa.
3572*cfb92d14SAndroid Build Coastguard Worker        #     HostAddress:fd00:db8:0:0:3205:28dd:5b87:6a63 TTL:7175
3573*cfb92d14SAndroid Build Coastguard Worker        #     TXT:[a=00, b=11cf] TTL:7175
3574*cfb92d14SAndroid Build Coastguard Worker        # ins1
3575*cfb92d14SAndroid Build Coastguard Worker        #     Port:11111, Priority:1, Weight:1, TTL:7170
3576*cfb92d14SAndroid Build Coastguard Worker        #     Host:host1.default.service.arpa.
3577*cfb92d14SAndroid Build Coastguard Worker        #     HostAddress:fd00:db8:0:0:39f4:d9:eb4f:778 TTL:7170
3578*cfb92d14SAndroid Build Coastguard Worker        #     TXT:[a=01, b=23dd] TTL:7170
3579*cfb92d14SAndroid Build Coastguard Worker        # Done
3580*cfb92d14SAndroid Build Coastguard Worker
3581*cfb92d14SAndroid Build Coastguard Worker        result = {}
3582*cfb92d14SAndroid Build Coastguard Worker        index = 1  # skip first line
3583*cfb92d14SAndroid Build Coastguard Worker        while index < len(output):
3584*cfb92d14SAndroid Build Coastguard Worker            ins = output[index].strip()
3585*cfb92d14SAndroid Build Coastguard Worker            result[ins] = self._parse_dns_service_info(output[index + 1:index + 6])
3586*cfb92d14SAndroid Build Coastguard Worker            index = index + (5 if result[ins] else 1)
3587*cfb92d14SAndroid Build Coastguard Worker        return result
3588*cfb92d14SAndroid Build Coastguard Worker
3589*cfb92d14SAndroid Build Coastguard Worker    def set_mliid(self, mliid: str):
3590*cfb92d14SAndroid Build Coastguard Worker        cmd = f'mliid {mliid}'
3591*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3592*cfb92d14SAndroid Build Coastguard Worker        self._expect_command_output()
3593*cfb92d14SAndroid Build Coastguard Worker
3594*cfb92d14SAndroid Build Coastguard Worker    def history_netinfo(self, num_entries=0):
3595*cfb92d14SAndroid Build Coastguard Worker        """
3596*cfb92d14SAndroid Build Coastguard Worker        Get the `netinfo` history list, parse each entry and return
3597*cfb92d14SAndroid Build Coastguard Worker        a list of dictionary (string key and string value) entries.
3598*cfb92d14SAndroid Build Coastguard Worker
3599*cfb92d14SAndroid Build Coastguard Worker        Example of return value:
3600*cfb92d14SAndroid Build Coastguard Worker        [
3601*cfb92d14SAndroid Build Coastguard Worker            {
3602*cfb92d14SAndroid Build Coastguard Worker                'age': '00:00:00.000 ago',
3603*cfb92d14SAndroid Build Coastguard Worker                'role': 'disabled',
3604*cfb92d14SAndroid Build Coastguard Worker                'mode': 'rdn',
3605*cfb92d14SAndroid Build Coastguard Worker                'rloc16': '0x7400',
3606*cfb92d14SAndroid Build Coastguard Worker                'partition-id': '1318093703'
3607*cfb92d14SAndroid Build Coastguard Worker            },
3608*cfb92d14SAndroid Build Coastguard Worker            {
3609*cfb92d14SAndroid Build Coastguard Worker                'age': '00:00:02.588 ago',
3610*cfb92d14SAndroid Build Coastguard Worker                'role': 'leader',
3611*cfb92d14SAndroid Build Coastguard Worker                'mode': 'rdn',
3612*cfb92d14SAndroid Build Coastguard Worker                'rloc16': '0x7400',
3613*cfb92d14SAndroid Build Coastguard Worker                'partition-id': '1318093703'
3614*cfb92d14SAndroid Build Coastguard Worker            }
3615*cfb92d14SAndroid Build Coastguard Worker        ]
3616*cfb92d14SAndroid Build Coastguard Worker        """
3617*cfb92d14SAndroid Build Coastguard Worker        cmd = f'history netinfo list {num_entries}'
3618*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3619*cfb92d14SAndroid Build Coastguard Worker        output = self._expect_command_output()
3620*cfb92d14SAndroid Build Coastguard Worker        netinfos = []
3621*cfb92d14SAndroid Build Coastguard Worker        for entry in output:
3622*cfb92d14SAndroid Build Coastguard Worker            netinfo = {}
3623*cfb92d14SAndroid Build Coastguard Worker            age, info = entry.split(' -> ')
3624*cfb92d14SAndroid Build Coastguard Worker            netinfo['age'] = age
3625*cfb92d14SAndroid Build Coastguard Worker            for item in info.split(' '):
3626*cfb92d14SAndroid Build Coastguard Worker                k, v = item.split(':')
3627*cfb92d14SAndroid Build Coastguard Worker                netinfo[k] = v
3628*cfb92d14SAndroid Build Coastguard Worker            netinfos.append(netinfo)
3629*cfb92d14SAndroid Build Coastguard Worker        return netinfos
3630*cfb92d14SAndroid Build Coastguard Worker
3631*cfb92d14SAndroid Build Coastguard Worker    def history_rx(self, num_entries=0):
3632*cfb92d14SAndroid Build Coastguard Worker        """
3633*cfb92d14SAndroid Build Coastguard Worker        Get the IPv6 RX history list, parse each entry and return
3634*cfb92d14SAndroid Build Coastguard Worker        a list of dictionary (string key and string value) entries.
3635*cfb92d14SAndroid Build Coastguard Worker
3636*cfb92d14SAndroid Build Coastguard Worker        Example of return value:
3637*cfb92d14SAndroid Build Coastguard Worker        [
3638*cfb92d14SAndroid Build Coastguard Worker            {
3639*cfb92d14SAndroid Build Coastguard Worker                'age': '00:00:01.999',
3640*cfb92d14SAndroid Build Coastguard Worker                'type': 'ICMP6(EchoReqst)',
3641*cfb92d14SAndroid Build Coastguard Worker                'len': '16',
3642*cfb92d14SAndroid Build Coastguard Worker                'sec': 'yes',
3643*cfb92d14SAndroid Build Coastguard Worker                'prio': 'norm',
3644*cfb92d14SAndroid Build Coastguard Worker                'rss': '-20',
3645*cfb92d14SAndroid Build Coastguard Worker                'from': '0xac00',
3646*cfb92d14SAndroid Build Coastguard Worker                'radio': '15.4',
3647*cfb92d14SAndroid Build Coastguard Worker                'src': '[fd00:db8:0:0:2cfa:fd61:58a9:f0aa]:0',
3648*cfb92d14SAndroid Build Coastguard Worker                'dst': '[fd00:db8:0:0:ed7e:2d04:e543:eba5]:0',
3649*cfb92d14SAndroid Build Coastguard Worker            }
3650*cfb92d14SAndroid Build Coastguard Worker        ]
3651*cfb92d14SAndroid Build Coastguard Worker        """
3652*cfb92d14SAndroid Build Coastguard Worker        cmd = f'history rx list {num_entries}'
3653*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3654*cfb92d14SAndroid Build Coastguard Worker        return self._parse_history_rx_tx_ouput(self._expect_command_output())
3655*cfb92d14SAndroid Build Coastguard Worker
3656*cfb92d14SAndroid Build Coastguard Worker    def history_tx(self, num_entries=0):
3657*cfb92d14SAndroid Build Coastguard Worker        """
3658*cfb92d14SAndroid Build Coastguard Worker        Get the IPv6 TX history list, parse each entry and return
3659*cfb92d14SAndroid Build Coastguard Worker        a list of dictionary (string key and string value) entries.
3660*cfb92d14SAndroid Build Coastguard Worker
3661*cfb92d14SAndroid Build Coastguard Worker        Example of return value:
3662*cfb92d14SAndroid Build Coastguard Worker        [
3663*cfb92d14SAndroid Build Coastguard Worker            {
3664*cfb92d14SAndroid Build Coastguard Worker                'age': '00:00:01.999',
3665*cfb92d14SAndroid Build Coastguard Worker                'type': 'ICMP6(EchoReply)',
3666*cfb92d14SAndroid Build Coastguard Worker                'len': '16',
3667*cfb92d14SAndroid Build Coastguard Worker                'sec': 'yes',
3668*cfb92d14SAndroid Build Coastguard Worker                'prio': 'norm',
3669*cfb92d14SAndroid Build Coastguard Worker                'to': '0xac00',
3670*cfb92d14SAndroid Build Coastguard Worker                'tx-success': 'yes',
3671*cfb92d14SAndroid Build Coastguard Worker                'radio': '15.4',
3672*cfb92d14SAndroid Build Coastguard Worker                'src': '[fd00:db8:0:0:ed7e:2d04:e543:eba5]:0',
3673*cfb92d14SAndroid Build Coastguard Worker                'dst': '[fd00:db8:0:0:2cfa:fd61:58a9:f0aa]:0',
3674*cfb92d14SAndroid Build Coastguard Worker
3675*cfb92d14SAndroid Build Coastguard Worker            }
3676*cfb92d14SAndroid Build Coastguard Worker        ]
3677*cfb92d14SAndroid Build Coastguard Worker        """
3678*cfb92d14SAndroid Build Coastguard Worker        cmd = f'history tx list {num_entries}'
3679*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3680*cfb92d14SAndroid Build Coastguard Worker        return self._parse_history_rx_tx_ouput(self._expect_command_output())
3681*cfb92d14SAndroid Build Coastguard Worker
3682*cfb92d14SAndroid Build Coastguard Worker    def _parse_history_rx_tx_ouput(self, lines):
3683*cfb92d14SAndroid Build Coastguard Worker        rxtx_list = []
3684*cfb92d14SAndroid Build Coastguard Worker        for line in lines:
3685*cfb92d14SAndroid Build Coastguard Worker            if line.strip().startswith('type:'):
3686*cfb92d14SAndroid Build Coastguard Worker                for item in line.strip().split(' '):
3687*cfb92d14SAndroid Build Coastguard Worker                    k, v = item.split(':')
3688*cfb92d14SAndroid Build Coastguard Worker                    entry[k] = v
3689*cfb92d14SAndroid Build Coastguard Worker            elif line.strip().startswith('src:'):
3690*cfb92d14SAndroid Build Coastguard Worker                entry['src'] = line[4:]
3691*cfb92d14SAndroid Build Coastguard Worker            elif line.strip().startswith('dst:'):
3692*cfb92d14SAndroid Build Coastguard Worker                entry['dst'] = line[4:]
3693*cfb92d14SAndroid Build Coastguard Worker                rxtx_list.append(entry)
3694*cfb92d14SAndroid Build Coastguard Worker            else:
3695*cfb92d14SAndroid Build Coastguard Worker                entry = {}
3696*cfb92d14SAndroid Build Coastguard Worker                entry['age'] = line
3697*cfb92d14SAndroid Build Coastguard Worker
3698*cfb92d14SAndroid Build Coastguard Worker        return rxtx_list
3699*cfb92d14SAndroid Build Coastguard Worker
3700*cfb92d14SAndroid Build Coastguard Worker    def set_router_id_range(self, min_router_id: int, max_router_id: int):
3701*cfb92d14SAndroid Build Coastguard Worker        cmd = f'routeridrange {min_router_id} {max_router_id}'
3702*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3703*cfb92d14SAndroid Build Coastguard Worker        self._expect_command_output()
3704*cfb92d14SAndroid Build Coastguard Worker
3705*cfb92d14SAndroid Build Coastguard Worker    def get_router_id_range(self):
3706*cfb92d14SAndroid Build Coastguard Worker        cmd = 'routeridrange'
3707*cfb92d14SAndroid Build Coastguard Worker        self.send_command(cmd)
3708*cfb92d14SAndroid Build Coastguard Worker        line = self._expect_command_output()[0]
3709*cfb92d14SAndroid Build Coastguard Worker        return [int(item) for item in line.split()]
3710*cfb92d14SAndroid Build Coastguard Worker
3711*cfb92d14SAndroid Build Coastguard Worker    def get_channel_monitor_info(self) -> Dict:
3712*cfb92d14SAndroid Build Coastguard Worker        """
3713*cfb92d14SAndroid Build Coastguard Worker        Returns:
3714*cfb92d14SAndroid Build Coastguard Worker            Dict of channel monitor info, e.g.
3715*cfb92d14SAndroid Build Coastguard Worker                {'enabled': '1',
3716*cfb92d14SAndroid Build Coastguard Worker                 'interval': '41000',
3717*cfb92d14SAndroid Build Coastguard Worker                 'threshold': '-75',
3718*cfb92d14SAndroid Build Coastguard Worker                 'window': '960',
3719*cfb92d14SAndroid Build Coastguard Worker                 'count': '985',
3720*cfb92d14SAndroid Build Coastguard Worker                 'occupancies': {
3721*cfb92d14SAndroid Build Coastguard Worker                    '11': '0.00%',
3722*cfb92d14SAndroid Build Coastguard Worker                    '12': '3.50%',
3723*cfb92d14SAndroid Build Coastguard Worker                    '13': '9.89%',
3724*cfb92d14SAndroid Build Coastguard Worker                    '14': '15.36%',
3725*cfb92d14SAndroid Build Coastguard Worker                    '15': '20.02%',
3726*cfb92d14SAndroid Build Coastguard Worker                    '16': '21.95%',
3727*cfb92d14SAndroid Build Coastguard Worker                    '17': '32.71%',
3728*cfb92d14SAndroid Build Coastguard Worker                    '18': '35.76%',
3729*cfb92d14SAndroid Build Coastguard Worker                    '19': '37.97%',
3730*cfb92d14SAndroid Build Coastguard Worker                    '20': '43.68%',
3731*cfb92d14SAndroid Build Coastguard Worker                    '21': '48.95%',
3732*cfb92d14SAndroid Build Coastguard Worker                    '22': '54.05%',
3733*cfb92d14SAndroid Build Coastguard Worker                    '23': '58.65%',
3734*cfb92d14SAndroid Build Coastguard Worker                    '24': '68.26%',
3735*cfb92d14SAndroid Build Coastguard Worker                    '25': '66.73%',
3736*cfb92d14SAndroid Build Coastguard Worker                    '26': '73.12%'
3737*cfb92d14SAndroid Build Coastguard Worker                    }
3738*cfb92d14SAndroid Build Coastguard Worker                }
3739*cfb92d14SAndroid Build Coastguard Worker        """
3740*cfb92d14SAndroid Build Coastguard Worker        config = {}
3741*cfb92d14SAndroid Build Coastguard Worker        self.send_command('channel monitor')
3742*cfb92d14SAndroid Build Coastguard Worker
3743*cfb92d14SAndroid Build Coastguard Worker        for line in self._expect_results(r'\S+'):
3744*cfb92d14SAndroid Build Coastguard Worker            if re.match(r'.*:\s.*', line):
3745*cfb92d14SAndroid Build Coastguard Worker                key, val = line.split(':')
3746*cfb92d14SAndroid Build Coastguard Worker                config.update({key: val.strip()})
3747*cfb92d14SAndroid Build Coastguard Worker            elif re.match(r'.*:', line):  # occupancy
3748*cfb92d14SAndroid Build Coastguard Worker                occ_key, val = line.split(':')
3749*cfb92d14SAndroid Build Coastguard Worker                val = {}
3750*cfb92d14SAndroid Build Coastguard Worker                config.update({occ_key: val})
3751*cfb92d14SAndroid Build Coastguard Worker            elif 'busy' in line:
3752*cfb92d14SAndroid Build Coastguard Worker                # channel occupancies
3753*cfb92d14SAndroid Build Coastguard Worker                key = line.split()[1]
3754*cfb92d14SAndroid Build Coastguard Worker                val = line.split()[3]
3755*cfb92d14SAndroid Build Coastguard Worker                config[occ_key].update({key: val})
3756*cfb92d14SAndroid Build Coastguard Worker        return config
3757*cfb92d14SAndroid Build Coastguard Worker
3758*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_auto_enable(self, enable: bool):
3759*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager auto {int(enable)}')
3760*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3761*cfb92d14SAndroid Build Coastguard Worker
3762*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_autocsl_enable(self, enable: bool):
3763*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager autocsl {int(enable)}')
3764*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3765*cfb92d14SAndroid Build Coastguard Worker
3766*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_supported(self, channel_mask: int):
3767*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager supported {int(channel_mask)}')
3768*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3769*cfb92d14SAndroid Build Coastguard Worker
3770*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_favored(self, channel_mask: int):
3771*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager favored {int(channel_mask)}')
3772*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3773*cfb92d14SAndroid Build Coastguard Worker
3774*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_interval(self, interval: int):
3775*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager interval {interval}')
3776*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3777*cfb92d14SAndroid Build Coastguard Worker
3778*cfb92d14SAndroid Build Coastguard Worker    def set_channel_manager_cca_threshold(self, hex_value: str):
3779*cfb92d14SAndroid Build Coastguard Worker        self.send_command(f'channel manager threshold {hex_value}')
3780*cfb92d14SAndroid Build Coastguard Worker        self._expect_done()
3781*cfb92d14SAndroid Build Coastguard Worker
3782*cfb92d14SAndroid Build Coastguard Worker    def get_channel_manager_config(self):
3783*cfb92d14SAndroid Build Coastguard Worker        self.send_command('channel manager')
3784*cfb92d14SAndroid Build Coastguard Worker        return self._expect_key_value_pairs(r'\S+')
3785*cfb92d14SAndroid Build Coastguard Worker
3786*cfb92d14SAndroid Build Coastguard Worker
3787*cfb92d14SAndroid Build Coastguard Workerclass Node(NodeImpl, OtCli):
3788*cfb92d14SAndroid Build Coastguard Worker    pass
3789*cfb92d14SAndroid Build Coastguard Worker
3790*cfb92d14SAndroid Build Coastguard Worker
3791*cfb92d14SAndroid Build Coastguard Workerclass LinuxHost():
3792*cfb92d14SAndroid Build Coastguard Worker    PING_RESPONSE_PATTERN = re.compile(r'\d+ bytes from .*:.*')
3793*cfb92d14SAndroid Build Coastguard Worker    ETH_DEV = config.BACKBONE_IFNAME
3794*cfb92d14SAndroid Build Coastguard Worker
3795*cfb92d14SAndroid Build Coastguard Worker    def enable_ether(self):
3796*cfb92d14SAndroid Build Coastguard Worker        """Enable the ethernet interface.
3797*cfb92d14SAndroid Build Coastguard Worker        """
3798*cfb92d14SAndroid Build Coastguard Worker
3799*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'ip link set {self.ETH_DEV} up')
3800*cfb92d14SAndroid Build Coastguard Worker
3801*cfb92d14SAndroid Build Coastguard Worker    def disable_ether(self):
3802*cfb92d14SAndroid Build Coastguard Worker        """Disable the ethernet interface.
3803*cfb92d14SAndroid Build Coastguard Worker        """
3804*cfb92d14SAndroid Build Coastguard Worker
3805*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'ip link set {self.ETH_DEV} down')
3806*cfb92d14SAndroid Build Coastguard Worker
3807*cfb92d14SAndroid Build Coastguard Worker    def get_ether_addrs(self, ipv4=False, ipv6=True):
3808*cfb92d14SAndroid Build Coastguard Worker        output = self.bash(f'ip addr list dev {self.ETH_DEV}')
3809*cfb92d14SAndroid Build Coastguard Worker
3810*cfb92d14SAndroid Build Coastguard Worker        addrs = []
3811*cfb92d14SAndroid Build Coastguard Worker        for line in output:
3812*cfb92d14SAndroid Build Coastguard Worker            # line examples:
3813*cfb92d14SAndroid Build Coastguard Worker            # "inet6 fe80::42:c0ff:fea8:903/64 scope link"
3814*cfb92d14SAndroid Build Coastguard Worker            # "inet 192.168.9.1/24 brd 192.168.9.255 scope global eth0"
3815*cfb92d14SAndroid Build Coastguard Worker            line = line.strip().split()
3816*cfb92d14SAndroid Build Coastguard Worker
3817*cfb92d14SAndroid Build Coastguard Worker            if not line or not line[0].startswith('inet'):
3818*cfb92d14SAndroid Build Coastguard Worker                continue
3819*cfb92d14SAndroid Build Coastguard Worker            if line[0] == 'inet' and not ipv4:
3820*cfb92d14SAndroid Build Coastguard Worker                continue
3821*cfb92d14SAndroid Build Coastguard Worker            if line[0] == 'inet6' and not ipv6:
3822*cfb92d14SAndroid Build Coastguard Worker                continue
3823*cfb92d14SAndroid Build Coastguard Worker
3824*cfb92d14SAndroid Build Coastguard Worker            addr = line[1]
3825*cfb92d14SAndroid Build Coastguard Worker            if '/' in addr:
3826*cfb92d14SAndroid Build Coastguard Worker                addr = addr.split('/')[0]
3827*cfb92d14SAndroid Build Coastguard Worker            addrs.append(addr)
3828*cfb92d14SAndroid Build Coastguard Worker
3829*cfb92d14SAndroid Build Coastguard Worker        logging.debug('%s: get_ether_addrs: %r', self, addrs)
3830*cfb92d14SAndroid Build Coastguard Worker        return addrs
3831*cfb92d14SAndroid Build Coastguard Worker
3832*cfb92d14SAndroid Build Coastguard Worker    def get_ether_mac(self):
3833*cfb92d14SAndroid Build Coastguard Worker        output = self.bash(f'ip addr list dev {self.ETH_DEV}')
3834*cfb92d14SAndroid Build Coastguard Worker        for line in output:
3835*cfb92d14SAndroid Build Coastguard Worker            # link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3836*cfb92d14SAndroid Build Coastguard Worker            line = line.strip().split()
3837*cfb92d14SAndroid Build Coastguard Worker            if line and line[0] == 'link/ether':
3838*cfb92d14SAndroid Build Coastguard Worker                return line[1]
3839*cfb92d14SAndroid Build Coastguard Worker
3840*cfb92d14SAndroid Build Coastguard Worker        assert False, output
3841*cfb92d14SAndroid Build Coastguard Worker
3842*cfb92d14SAndroid Build Coastguard Worker    def add_ipmaddr_ether(self, ip: str):
3843*cfb92d14SAndroid Build Coastguard Worker        cmd = f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/mcast6.py {self.ETH_DEV} {ip} &'
3844*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
3845*cfb92d14SAndroid Build Coastguard Worker
3846*cfb92d14SAndroid Build Coastguard Worker    def ping_ether(self, ipaddr, num_responses=1, size=None, timeout=5, ttl=None, interface='eth0') -> int:
3847*cfb92d14SAndroid Build Coastguard Worker
3848*cfb92d14SAndroid Build Coastguard Worker        cmd = f'ping -6 {ipaddr} -I {interface} -c {num_responses} -W {timeout}'
3849*cfb92d14SAndroid Build Coastguard Worker        if size is not None:
3850*cfb92d14SAndroid Build Coastguard Worker            cmd += f' -s {size}'
3851*cfb92d14SAndroid Build Coastguard Worker
3852*cfb92d14SAndroid Build Coastguard Worker        if ttl is not None:
3853*cfb92d14SAndroid Build Coastguard Worker            cmd += f' -t {ttl}'
3854*cfb92d14SAndroid Build Coastguard Worker
3855*cfb92d14SAndroid Build Coastguard Worker        resp_count = 0
3856*cfb92d14SAndroid Build Coastguard Worker
3857*cfb92d14SAndroid Build Coastguard Worker        try:
3858*cfb92d14SAndroid Build Coastguard Worker            for line in self.bash(cmd):
3859*cfb92d14SAndroid Build Coastguard Worker                if self.PING_RESPONSE_PATTERN.match(line):
3860*cfb92d14SAndroid Build Coastguard Worker                    resp_count += 1
3861*cfb92d14SAndroid Build Coastguard Worker        except subprocess.CalledProcessError:
3862*cfb92d14SAndroid Build Coastguard Worker            pass
3863*cfb92d14SAndroid Build Coastguard Worker
3864*cfb92d14SAndroid Build Coastguard Worker        return resp_count
3865*cfb92d14SAndroid Build Coastguard Worker
3866*cfb92d14SAndroid Build Coastguard Worker    def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
3867*cfb92d14SAndroid Build Coastguard Worker        """Get specific type of IPv6 address configured on thread device.
3868*cfb92d14SAndroid Build Coastguard Worker
3869*cfb92d14SAndroid Build Coastguard Worker        Args:
3870*cfb92d14SAndroid Build Coastguard Worker            address_type: the config.ADDRESS_TYPE type of IPv6 address.
3871*cfb92d14SAndroid Build Coastguard Worker
3872*cfb92d14SAndroid Build Coastguard Worker        Returns:
3873*cfb92d14SAndroid Build Coastguard Worker            IPv6 address string.
3874*cfb92d14SAndroid Build Coastguard Worker        """
3875*cfb92d14SAndroid Build Coastguard Worker        if address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
3876*cfb92d14SAndroid Build Coastguard Worker            return self._getBackboneGua()
3877*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL:
3878*cfb92d14SAndroid Build Coastguard Worker            return self._getInfraLinkLocalAddress()
3879*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.ONLINK_ULA:
3880*cfb92d14SAndroid Build Coastguard Worker            return self._getInfraUla()
3881*cfb92d14SAndroid Build Coastguard Worker        elif address_type == config.ADDRESS_TYPE.ONLINK_GUA:
3882*cfb92d14SAndroid Build Coastguard Worker            return self._getInfraGua()
3883*cfb92d14SAndroid Build Coastguard Worker        else:
3884*cfb92d14SAndroid Build Coastguard Worker            raise ValueError(f'unsupported address type: {address_type}')
3885*cfb92d14SAndroid Build Coastguard Worker
3886*cfb92d14SAndroid Build Coastguard Worker    def _getBackboneGua(self) -> Optional[str]:
3887*cfb92d14SAndroid Build Coastguard Worker        for addr in self.get_ether_addrs():
3888*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.BACKBONE_PREFIX_REGEX_PATTERN, addr, re.I):
3889*cfb92d14SAndroid Build Coastguard Worker                return addr
3890*cfb92d14SAndroid Build Coastguard Worker
3891*cfb92d14SAndroid Build Coastguard Worker        return None
3892*cfb92d14SAndroid Build Coastguard Worker
3893*cfb92d14SAndroid Build Coastguard Worker    def _getInfraUla(self) -> Optional[str]:
3894*cfb92d14SAndroid Build Coastguard Worker        """ Returns the ULA addresses autoconfigured on the infra link.
3895*cfb92d14SAndroid Build Coastguard Worker        """
3896*cfb92d14SAndroid Build Coastguard Worker        addrs = []
3897*cfb92d14SAndroid Build Coastguard Worker        for addr in self.get_ether_addrs():
3898*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.ONLINK_PREFIX_REGEX_PATTERN, addr, re.I):
3899*cfb92d14SAndroid Build Coastguard Worker                addrs.append(addr)
3900*cfb92d14SAndroid Build Coastguard Worker
3901*cfb92d14SAndroid Build Coastguard Worker        return addrs
3902*cfb92d14SAndroid Build Coastguard Worker
3903*cfb92d14SAndroid Build Coastguard Worker    def _getInfraGua(self) -> Optional[str]:
3904*cfb92d14SAndroid Build Coastguard Worker        """ Returns the GUA addresses autoconfigured on the infra link.
3905*cfb92d14SAndroid Build Coastguard Worker        """
3906*cfb92d14SAndroid Build Coastguard Worker
3907*cfb92d14SAndroid Build Coastguard Worker        gua_prefix = config.ONLINK_GUA_PREFIX.split('::/')[0]
3908*cfb92d14SAndroid Build Coastguard Worker        return [addr for addr in self.get_ether_addrs() if addr.startswith(gua_prefix)]
3909*cfb92d14SAndroid Build Coastguard Worker
3910*cfb92d14SAndroid Build Coastguard Worker    def _getInfraLinkLocalAddress(self) -> Optional[str]:
3911*cfb92d14SAndroid Build Coastguard Worker        """ Returns the link-local address autoconfigured on the infra link, which is started with "fe80".
3912*cfb92d14SAndroid Build Coastguard Worker        """
3913*cfb92d14SAndroid Build Coastguard Worker        for addr in self.get_ether_addrs():
3914*cfb92d14SAndroid Build Coastguard Worker            if re.match(config.LINK_LOCAL_REGEX_PATTERN, addr, re.I):
3915*cfb92d14SAndroid Build Coastguard Worker                return addr
3916*cfb92d14SAndroid Build Coastguard Worker
3917*cfb92d14SAndroid Build Coastguard Worker        return None
3918*cfb92d14SAndroid Build Coastguard Worker
3919*cfb92d14SAndroid Build Coastguard Worker    def ping(self, *args, **kwargs):
3920*cfb92d14SAndroid Build Coastguard Worker        backbone = kwargs.pop('backbone', False)
3921*cfb92d14SAndroid Build Coastguard Worker        if backbone:
3922*cfb92d14SAndroid Build Coastguard Worker            return self.ping_ether(*args, **kwargs)
3923*cfb92d14SAndroid Build Coastguard Worker        else:
3924*cfb92d14SAndroid Build Coastguard Worker            return super().ping(*args, **kwargs)
3925*cfb92d14SAndroid Build Coastguard Worker
3926*cfb92d14SAndroid Build Coastguard Worker    def udp_send_host(self, ipaddr, port, data, hop_limit=None):
3927*cfb92d14SAndroid Build Coastguard Worker        if hop_limit is None:
3928*cfb92d14SAndroid Build Coastguard Worker            if ipaddress.ip_address(ipaddr).is_multicast:
3929*cfb92d14SAndroid Build Coastguard Worker                hop_limit = 10
3930*cfb92d14SAndroid Build Coastguard Worker            else:
3931*cfb92d14SAndroid Build Coastguard Worker                hop_limit = 64
3932*cfb92d14SAndroid Build Coastguard Worker        cmd = f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/udp_send_host.py {ipaddr} {port} "{data}" {hop_limit}'
3933*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
3934*cfb92d14SAndroid Build Coastguard Worker
3935*cfb92d14SAndroid Build Coastguard Worker    def add_ipmaddr(self, *args, **kwargs):
3936*cfb92d14SAndroid Build Coastguard Worker        backbone = kwargs.pop('backbone', False)
3937*cfb92d14SAndroid Build Coastguard Worker        if backbone:
3938*cfb92d14SAndroid Build Coastguard Worker            return self.add_ipmaddr_ether(*args, **kwargs)
3939*cfb92d14SAndroid Build Coastguard Worker        else:
3940*cfb92d14SAndroid Build Coastguard Worker            return super().add_ipmaddr(*args, **kwargs)
3941*cfb92d14SAndroid Build Coastguard Worker
3942*cfb92d14SAndroid Build Coastguard Worker    def ip_neighbors_flush(self):
3943*cfb92d14SAndroid Build Coastguard Worker        # clear neigh cache on linux
3944*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'ip -6 neigh list dev {self.ETH_DEV}')
3945*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'ip -6 neigh flush nud all nud failed nud noarp dev {self.ETH_DEV}')
3946*cfb92d14SAndroid Build Coastguard Worker        self.bash('ip -6 neigh list nud all dev %s | cut -d " " -f1 | sudo xargs -I{} ip -6 neigh delete {} dev %s' %
3947*cfb92d14SAndroid Build Coastguard Worker                  (self.ETH_DEV, self.ETH_DEV))
3948*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'ip -6 neigh list dev {self.ETH_DEV}')
3949*cfb92d14SAndroid Build Coastguard Worker
3950*cfb92d14SAndroid Build Coastguard Worker    def publish_mdns_service(self, instance_name, service_type, port, host_name, txt):
3951*cfb92d14SAndroid Build Coastguard Worker        """Publish an mDNS service on the Ethernet.
3952*cfb92d14SAndroid Build Coastguard Worker
3953*cfb92d14SAndroid Build Coastguard Worker        :param instance_name: the service instance name.
3954*cfb92d14SAndroid Build Coastguard Worker        :param service_type: the service type in format of '<service_type>.<protocol>'.
3955*cfb92d14SAndroid Build Coastguard Worker        :param port: the port the service is at.
3956*cfb92d14SAndroid Build Coastguard Worker        :param host_name: the host name this service points to. The domain
3957*cfb92d14SAndroid Build Coastguard Worker                          should not be included.
3958*cfb92d14SAndroid Build Coastguard Worker        :param txt: a dictionary containing the key-value pairs of the TXT record.
3959*cfb92d14SAndroid Build Coastguard Worker        """
3960*cfb92d14SAndroid Build Coastguard Worker        txt_string = ' '.join([f'{key}={value}' for key, value in txt.items()])
3961*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'avahi-publish -s {instance_name}  {service_type} {port} -H {host_name}.local {txt_string} &')
3962*cfb92d14SAndroid Build Coastguard Worker
3963*cfb92d14SAndroid Build Coastguard Worker    def publish_mdns_host(self, hostname, addresses):
3964*cfb92d14SAndroid Build Coastguard Worker        """Publish an mDNS host on the Ethernet
3965*cfb92d14SAndroid Build Coastguard Worker
3966*cfb92d14SAndroid Build Coastguard Worker        :param host_name: the host name this service points to. The domain
3967*cfb92d14SAndroid Build Coastguard Worker                          should not be included.
3968*cfb92d14SAndroid Build Coastguard Worker        :param addresses: a list of strings representing the addresses to
3969*cfb92d14SAndroid Build Coastguard Worker                          be registered with the host.
3970*cfb92d14SAndroid Build Coastguard Worker        """
3971*cfb92d14SAndroid Build Coastguard Worker        for address in addresses:
3972*cfb92d14SAndroid Build Coastguard Worker            self.bash(f'avahi-publish -a {hostname}.local {address} &')
3973*cfb92d14SAndroid Build Coastguard Worker
3974*cfb92d14SAndroid Build Coastguard Worker    def browse_mdns_services(self, name, timeout=2):
3975*cfb92d14SAndroid Build Coastguard Worker        """ Browse mDNS services on the ethernet.
3976*cfb92d14SAndroid Build Coastguard Worker
3977*cfb92d14SAndroid Build Coastguard Worker        :param name: the service type name in format of '<service-name>.<protocol>'.
3978*cfb92d14SAndroid Build Coastguard Worker        :param timeout: timeout value in seconds before returning.
3979*cfb92d14SAndroid Build Coastguard Worker        :return: A list of service instance names.
3980*cfb92d14SAndroid Build Coastguard Worker        """
3981*cfb92d14SAndroid Build Coastguard Worker
3982*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'dns-sd -Z {name} local. > /tmp/{name} 2>&1 &')
3983*cfb92d14SAndroid Build Coastguard Worker        time.sleep(timeout)
3984*cfb92d14SAndroid Build Coastguard Worker        self.bash('pkill dns-sd')
3985*cfb92d14SAndroid Build Coastguard Worker
3986*cfb92d14SAndroid Build Coastguard Worker        instances = []
3987*cfb92d14SAndroid Build Coastguard Worker        for line in self.bash(f'cat /tmp/{name}', encoding='raw_unicode_escape'):
3988*cfb92d14SAndroid Build Coastguard Worker            elements = line.split()
3989*cfb92d14SAndroid Build Coastguard Worker            if len(elements) >= 3 and elements[0] == name and elements[1] == 'PTR':
3990*cfb92d14SAndroid Build Coastguard Worker                instances.append(elements[2][:-len('.' + name)])
3991*cfb92d14SAndroid Build Coastguard Worker        return instances
3992*cfb92d14SAndroid Build Coastguard Worker
3993*cfb92d14SAndroid Build Coastguard Worker    def discover_mdns_service(self, instance, name, host_name, timeout=2):
3994*cfb92d14SAndroid Build Coastguard Worker        """ Discover/resolve the mDNS service on ethernet.
3995*cfb92d14SAndroid Build Coastguard Worker
3996*cfb92d14SAndroid Build Coastguard Worker        :param instance: the service instance name.
3997*cfb92d14SAndroid Build Coastguard Worker        :param name: the service name in format of '<service-name>.<protocol>'.
3998*cfb92d14SAndroid Build Coastguard Worker        :param host_name: the host name this service points to. The domain
3999*cfb92d14SAndroid Build Coastguard Worker                          should not be included.
4000*cfb92d14SAndroid Build Coastguard Worker        :param timeout: timeout value in seconds before returning.
4001*cfb92d14SAndroid Build Coastguard Worker        :return: a dict of service properties or None.
4002*cfb92d14SAndroid Build Coastguard Worker
4003*cfb92d14SAndroid Build Coastguard Worker        The return value is a dict with the same key/values of srp_server_get_service
4004*cfb92d14SAndroid Build Coastguard Worker        except that we don't have a `deleted` field here.
4005*cfb92d14SAndroid Build Coastguard Worker        """
4006*cfb92d14SAndroid Build Coastguard Worker        host_name_file = self.bash('mktemp')[0].strip()
4007*cfb92d14SAndroid Build Coastguard Worker        service_data_file = self.bash('mktemp')[0].strip()
4008*cfb92d14SAndroid Build Coastguard Worker
4009*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'dns-sd -Z {name} local. > {service_data_file} 2>&1 &')
4010*cfb92d14SAndroid Build Coastguard Worker        time.sleep(timeout)
4011*cfb92d14SAndroid Build Coastguard Worker
4012*cfb92d14SAndroid Build Coastguard Worker        full_service_name = f'{instance}.{name}'
4013*cfb92d14SAndroid Build Coastguard Worker        # When hostname is unspecified, extract hostname from browse result
4014*cfb92d14SAndroid Build Coastguard Worker        if host_name is None:
4015*cfb92d14SAndroid Build Coastguard Worker            for line in self.bash(f'cat {service_data_file}', encoding='raw_unicode_escape'):
4016*cfb92d14SAndroid Build Coastguard Worker                elements = line.split()
4017*cfb92d14SAndroid Build Coastguard Worker                if len(elements) >= 6 and elements[0] == full_service_name and elements[1] == 'SRV':
4018*cfb92d14SAndroid Build Coastguard Worker                    host_name = elements[5].split('.')[0]
4019*cfb92d14SAndroid Build Coastguard Worker                    break
4020*cfb92d14SAndroid Build Coastguard Worker
4021*cfb92d14SAndroid Build Coastguard Worker        assert (host_name is not None)
4022*cfb92d14SAndroid Build Coastguard Worker        self.bash(f'dns-sd -G v6 {host_name}.local. > {host_name_file} 2>&1 &')
4023*cfb92d14SAndroid Build Coastguard Worker        time.sleep(timeout)
4024*cfb92d14SAndroid Build Coastguard Worker
4025*cfb92d14SAndroid Build Coastguard Worker        self.bash('pkill dns-sd')
4026*cfb92d14SAndroid Build Coastguard Worker        addresses = []
4027*cfb92d14SAndroid Build Coastguard Worker        service = {}
4028*cfb92d14SAndroid Build Coastguard Worker
4029*cfb92d14SAndroid Build Coastguard Worker        logging.debug(self.bash(f'cat {host_name_file}', encoding='raw_unicode_escape'))
4030*cfb92d14SAndroid Build Coastguard Worker        logging.debug(self.bash(f'cat {service_data_file}', encoding='raw_unicode_escape'))
4031*cfb92d14SAndroid Build Coastguard Worker
4032*cfb92d14SAndroid Build Coastguard Worker        # example output in the host file:
4033*cfb92d14SAndroid Build Coastguard Worker        # Timestamp     A/R Flags if Hostname                               Address                                     TTL
4034*cfb92d14SAndroid Build Coastguard Worker        # 9:38:09.274  Add     23 48 my-host.local.                         2001:0000:0000:0000:0000:0000:0000:0002%<0>  120
4035*cfb92d14SAndroid Build Coastguard Worker        #
4036*cfb92d14SAndroid Build Coastguard Worker        for line in self.bash(f'cat {host_name_file}', encoding='raw_unicode_escape'):
4037*cfb92d14SAndroid Build Coastguard Worker            elements = line.split()
4038*cfb92d14SAndroid Build Coastguard Worker            fullname = f'{host_name}.local.'
4039*cfb92d14SAndroid Build Coastguard Worker            if fullname not in elements:
4040*cfb92d14SAndroid Build Coastguard Worker                continue
4041*cfb92d14SAndroid Build Coastguard Worker            if 'Add' not in elements:
4042*cfb92d14SAndroid Build Coastguard Worker                continue
4043*cfb92d14SAndroid Build Coastguard Worker            addresses.append(elements[elements.index(fullname) + 1].split('%')[0])
4044*cfb92d14SAndroid Build Coastguard Worker
4045*cfb92d14SAndroid Build Coastguard Worker        logging.debug(f'addresses of {host_name}: {addresses}')
4046*cfb92d14SAndroid Build Coastguard Worker
4047*cfb92d14SAndroid Build Coastguard Worker        # example output of in the service file:
4048*cfb92d14SAndroid Build Coastguard Worker        # _ipps._tcp                                      PTR     my-service._ipps._tcp
4049*cfb92d14SAndroid Build Coastguard Worker        # my-service._ipps._tcp                           SRV     0 0 12345 my-host.local. ; Replace with unicast FQDN of target host
4050*cfb92d14SAndroid Build Coastguard Worker        # my-service._ipps._tcp                           TXT     ""
4051*cfb92d14SAndroid Build Coastguard Worker        #
4052*cfb92d14SAndroid Build Coastguard Worker        is_txt = False
4053*cfb92d14SAndroid Build Coastguard Worker        txt = ''
4054*cfb92d14SAndroid Build Coastguard Worker        for line in self.bash(f'cat {service_data_file}', encoding='raw_unicode_escape'):
4055*cfb92d14SAndroid Build Coastguard Worker            elements = line.split()
4056*cfb92d14SAndroid Build Coastguard Worker            if len(elements) >= 2 and elements[0] == full_service_name and elements[1] == 'TXT':
4057*cfb92d14SAndroid Build Coastguard Worker                is_txt = True
4058*cfb92d14SAndroid Build Coastguard Worker            if is_txt:
4059*cfb92d14SAndroid Build Coastguard Worker                txt += line.strip()
4060*cfb92d14SAndroid Build Coastguard Worker                if line.strip().endswith('"'):
4061*cfb92d14SAndroid Build Coastguard Worker                    is_txt = False
4062*cfb92d14SAndroid Build Coastguard Worker                    txt_dict = self.__parse_dns_sd_txt(txt)
4063*cfb92d14SAndroid Build Coastguard Worker                    logging.info(f'txt = {txt_dict}')
4064*cfb92d14SAndroid Build Coastguard Worker                    service['txt'] = txt_dict
4065*cfb92d14SAndroid Build Coastguard Worker
4066*cfb92d14SAndroid Build Coastguard Worker            if not elements or elements[0] != full_service_name:
4067*cfb92d14SAndroid Build Coastguard Worker                continue
4068*cfb92d14SAndroid Build Coastguard Worker            if elements[1] == 'SRV':
4069*cfb92d14SAndroid Build Coastguard Worker                service['fullname'] = elements[0]
4070*cfb92d14SAndroid Build Coastguard Worker                service['instance'] = instance
4071*cfb92d14SAndroid Build Coastguard Worker                service['name'] = name
4072*cfb92d14SAndroid Build Coastguard Worker                service['priority'] = int(elements[2])
4073*cfb92d14SAndroid Build Coastguard Worker                service['weight'] = int(elements[3])
4074*cfb92d14SAndroid Build Coastguard Worker                service['port'] = int(elements[4])
4075*cfb92d14SAndroid Build Coastguard Worker                service['host_fullname'] = elements[5]
4076*cfb92d14SAndroid Build Coastguard Worker                assert (service['host_fullname'] == f'{host_name}.local.')
4077*cfb92d14SAndroid Build Coastguard Worker                service['host'] = host_name
4078*cfb92d14SAndroid Build Coastguard Worker                service['addresses'] = addresses
4079*cfb92d14SAndroid Build Coastguard Worker        return service or None
4080*cfb92d14SAndroid Build Coastguard Worker
4081*cfb92d14SAndroid Build Coastguard Worker    def start_radvd_service(self, prefix, slaac):
4082*cfb92d14SAndroid Build Coastguard Worker        self.bash("""cat >/etc/radvd.conf <<EOF
4083*cfb92d14SAndroid Build Coastguard Workerinterface eth0
4084*cfb92d14SAndroid Build Coastguard Worker{
4085*cfb92d14SAndroid Build Coastguard Worker    AdvSendAdvert on;
4086*cfb92d14SAndroid Build Coastguard Worker
4087*cfb92d14SAndroid Build Coastguard Worker    AdvReachableTime 200;
4088*cfb92d14SAndroid Build Coastguard Worker    AdvRetransTimer 200;
4089*cfb92d14SAndroid Build Coastguard Worker    AdvDefaultLifetime 1800;
4090*cfb92d14SAndroid Build Coastguard Worker    MinRtrAdvInterval 1200;
4091*cfb92d14SAndroid Build Coastguard Worker    MaxRtrAdvInterval 1800;
4092*cfb92d14SAndroid Build Coastguard Worker    AdvDefaultPreference low;
4093*cfb92d14SAndroid Build Coastguard Worker
4094*cfb92d14SAndroid Build Coastguard Worker    prefix %s
4095*cfb92d14SAndroid Build Coastguard Worker    {
4096*cfb92d14SAndroid Build Coastguard Worker        AdvOnLink on;
4097*cfb92d14SAndroid Build Coastguard Worker        AdvAutonomous %s;
4098*cfb92d14SAndroid Build Coastguard Worker        AdvRouterAddr off;
4099*cfb92d14SAndroid Build Coastguard Worker        AdvPreferredLifetime 1800;
4100*cfb92d14SAndroid Build Coastguard Worker        AdvValidLifetime 1800;
4101*cfb92d14SAndroid Build Coastguard Worker    };
4102*cfb92d14SAndroid Build Coastguard Worker};
4103*cfb92d14SAndroid Build Coastguard WorkerEOF
4104*cfb92d14SAndroid Build Coastguard Worker""" % (prefix, 'on' if slaac else 'off'))
4105*cfb92d14SAndroid Build Coastguard Worker        self.bash('service radvd start')
4106*cfb92d14SAndroid Build Coastguard Worker        self.bash('service radvd status')  # Make sure radvd service is running
4107*cfb92d14SAndroid Build Coastguard Worker
4108*cfb92d14SAndroid Build Coastguard Worker    def stop_radvd_service(self):
4109*cfb92d14SAndroid Build Coastguard Worker        self.bash('service radvd stop')
4110*cfb92d14SAndroid Build Coastguard Worker
4111*cfb92d14SAndroid Build Coastguard Worker    def kill_radvd_service(self):
4112*cfb92d14SAndroid Build Coastguard Worker        self.bash('pkill radvd')
4113*cfb92d14SAndroid Build Coastguard Worker
4114*cfb92d14SAndroid Build Coastguard Worker    def __parse_dns_sd_txt(self, line: str):
4115*cfb92d14SAndroid Build Coastguard Worker        # Example TXT entry:
4116*cfb92d14SAndroid Build Coastguard Worker        # "xp=\\000\\013\\184\\000\\000\\000\\000\\000"
4117*cfb92d14SAndroid Build Coastguard Worker        txt = {}
4118*cfb92d14SAndroid Build Coastguard Worker        for entry in re.findall(r'"((?:[^\\]|\\.)*?)"', line):
4119*cfb92d14SAndroid Build Coastguard Worker            if '=' not in entry:
4120*cfb92d14SAndroid Build Coastguard Worker                continue
4121*cfb92d14SAndroid Build Coastguard Worker
4122*cfb92d14SAndroid Build Coastguard Worker            k, v = entry.split('=', 1)
4123*cfb92d14SAndroid Build Coastguard Worker            txt[k] = v
4124*cfb92d14SAndroid Build Coastguard Worker
4125*cfb92d14SAndroid Build Coastguard Worker        return txt
4126*cfb92d14SAndroid Build Coastguard Worker
4127*cfb92d14SAndroid Build Coastguard Worker
4128*cfb92d14SAndroid Build Coastguard Workerclass OtbrNode(LinuxHost, NodeImpl, OtbrDocker):
4129*cfb92d14SAndroid Build Coastguard Worker    TUN_DEV = config.THREAD_IFNAME
4130*cfb92d14SAndroid Build Coastguard Worker    is_otbr = True
4131*cfb92d14SAndroid Build Coastguard Worker    is_bbr = True  # OTBR is also BBR
4132*cfb92d14SAndroid Build Coastguard Worker    node_type = 'otbr-docker'
4133*cfb92d14SAndroid Build Coastguard Worker
4134*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
4135*cfb92d14SAndroid Build Coastguard Worker        return f'Otbr<{self.nodeid}>'
4136*cfb92d14SAndroid Build Coastguard Worker
4137*cfb92d14SAndroid Build Coastguard Worker    def start(self):
4138*cfb92d14SAndroid Build Coastguard Worker        self._setup_sysctl()
4139*cfb92d14SAndroid Build Coastguard Worker        self.set_log_level(5)
4140*cfb92d14SAndroid Build Coastguard Worker        super().start()
4141*cfb92d14SAndroid Build Coastguard Worker
4142*cfb92d14SAndroid Build Coastguard Worker    def add_ipaddr(self, addr):
4143*cfb92d14SAndroid Build Coastguard Worker        cmd = f'ip -6 addr add {addr}/64 dev {self.TUN_DEV}'
4144*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
4145*cfb92d14SAndroid Build Coastguard Worker
4146*cfb92d14SAndroid Build Coastguard Worker    def add_ipmaddr_tun(self, ip: str):
4147*cfb92d14SAndroid Build Coastguard Worker        cmd = f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/mcast6.py {self.TUN_DEV} {ip} &'
4148*cfb92d14SAndroid Build Coastguard Worker        self.bash(cmd)
4149*cfb92d14SAndroid Build Coastguard Worker
4150*cfb92d14SAndroid Build Coastguard Worker    def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
4151*cfb92d14SAndroid Build Coastguard Worker        try:
4152*cfb92d14SAndroid Build Coastguard Worker            return super(OtbrNode, self).get_ip6_address(address_type)
4153*cfb92d14SAndroid Build Coastguard Worker        except Exception as e:
4154*cfb92d14SAndroid Build Coastguard Worker            return super(LinuxHost, self).get_ip6_address(address_type)
4155*cfb92d14SAndroid Build Coastguard Worker
4156*cfb92d14SAndroid Build Coastguard Worker
4157*cfb92d14SAndroid Build Coastguard Workerclass HostNode(LinuxHost, OtbrDocker):
4158*cfb92d14SAndroid Build Coastguard Worker    is_host = True
4159*cfb92d14SAndroid Build Coastguard Worker
4160*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, nodeid, name=None, **kwargs):
4161*cfb92d14SAndroid Build Coastguard Worker        self.nodeid = nodeid
4162*cfb92d14SAndroid Build Coastguard Worker        self.name = name or ('Host%d' % nodeid)
4163*cfb92d14SAndroid Build Coastguard Worker        super().__init__(nodeid, **kwargs)
4164*cfb92d14SAndroid Build Coastguard Worker        self.bash('service otbr-agent stop')
4165*cfb92d14SAndroid Build Coastguard Worker
4166*cfb92d14SAndroid Build Coastguard Worker    def start(self, start_radvd=True, prefix=config.DOMAIN_PREFIX, slaac=False):
4167*cfb92d14SAndroid Build Coastguard Worker        self._setup_sysctl()
4168*cfb92d14SAndroid Build Coastguard Worker        if start_radvd:
4169*cfb92d14SAndroid Build Coastguard Worker            self.start_radvd_service(prefix, slaac)
4170*cfb92d14SAndroid Build Coastguard Worker        else:
4171*cfb92d14SAndroid Build Coastguard Worker            self.stop_radvd_service()
4172*cfb92d14SAndroid Build Coastguard Worker
4173*cfb92d14SAndroid Build Coastguard Worker    def stop(self):
4174*cfb92d14SAndroid Build Coastguard Worker        self.stop_radvd_service()
4175*cfb92d14SAndroid Build Coastguard Worker
4176*cfb92d14SAndroid Build Coastguard Worker    def get_addrs(self) -> List[str]:
4177*cfb92d14SAndroid Build Coastguard Worker        return self.get_ether_addrs()
4178*cfb92d14SAndroid Build Coastguard Worker
4179*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
4180*cfb92d14SAndroid Build Coastguard Worker        return f'Host<{self.nodeid}>'
4181*cfb92d14SAndroid Build Coastguard Worker
4182*cfb92d14SAndroid Build Coastguard Worker    def get_matched_ula_addresses(self, prefix):
4183*cfb92d14SAndroid Build Coastguard Worker        """Get the IPv6 addresses that matches given prefix.
4184*cfb92d14SAndroid Build Coastguard Worker        """
4185*cfb92d14SAndroid Build Coastguard Worker
4186*cfb92d14SAndroid Build Coastguard Worker        addrs = []
4187*cfb92d14SAndroid Build Coastguard Worker        for addr in self.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA):
4188*cfb92d14SAndroid Build Coastguard Worker            if IPv6Address(addr) in IPv6Network(prefix):
4189*cfb92d14SAndroid Build Coastguard Worker                addrs.append(addr)
4190*cfb92d14SAndroid Build Coastguard Worker
4191*cfb92d14SAndroid Build Coastguard Worker        return addrs
4192*cfb92d14SAndroid Build Coastguard Worker
4193*cfb92d14SAndroid Build Coastguard Worker
4194*cfb92d14SAndroid Build Coastguard Workerif __name__ == '__main__':
4195*cfb92d14SAndroid Build Coastguard Worker    unittest.main()
4196