mirror of
https://github.com/espressif/openthread.git
synced 2026-09-29 20:37:40 +00:00
This commit adds support for RF enclosure simulation. It can pass Leader 9.2.9, Router 9.2.9 and Router 9.2.10 in Thread Test Harness v56.0 now.
148 lines
5.3 KiB
Python
148 lines
5.3 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Copyright (c) 2022, The OpenThread Authors.
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# 3. Neither the name of the copyright holder nor the
|
|
# names of its contributors may be used to endorse or promote products
|
|
# derived from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
"""
|
|
>> Thread Host Controller Interface
|
|
>> Device : OpenThread_BR_Sim THCI
|
|
>> Class : OpenThread_BR_Sim
|
|
"""
|
|
|
|
import ipaddress
|
|
import logging
|
|
import paramiko
|
|
import pipes
|
|
import sys
|
|
import time
|
|
|
|
from THCI.IThci import IThci
|
|
from THCI.OpenThread import watched
|
|
from THCI.OpenThread_BR import OpenThread_BR
|
|
from simulation.config import load_config
|
|
|
|
logging.getLogger('paramiko').setLevel(logging.WARNING)
|
|
|
|
config = load_config()
|
|
|
|
|
|
class SSHHandle(object):
|
|
# Unit: second
|
|
KEEPALIVE_INTERVAL = 30
|
|
|
|
def __init__(self, ip, port, username, password, docker_name):
|
|
self.ip = ip
|
|
self.port = int(port)
|
|
self.username = username
|
|
self.password = password
|
|
self.docker_name = docker_name
|
|
self.__handle = None
|
|
|
|
self.__connect()
|
|
|
|
def __connect(self):
|
|
self.close()
|
|
|
|
self.__handle = paramiko.SSHClient()
|
|
self.__handle.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
try:
|
|
self.__handle.connect(self.ip, port=self.port, username=self.username, password=self.password)
|
|
except paramiko.AuthenticationException:
|
|
if not self.password:
|
|
self.__handle.get_transport().auth_none(self.username)
|
|
else:
|
|
raise Exception('Password error')
|
|
|
|
# Avoid SSH disconnection after idle for a long time
|
|
self.__handle.get_transport().set_keepalive(self.KEEPALIVE_INTERVAL)
|
|
|
|
def close(self):
|
|
if self.__handle is not None:
|
|
self.__handle.close()
|
|
self.__handle = None
|
|
|
|
def bash(self, cmd, timeout):
|
|
# It is necessary to quote the command when there is stdin/stdout redirection
|
|
cmd = pipes.quote(cmd)
|
|
|
|
retry = 3
|
|
for i in range(retry):
|
|
try:
|
|
stdin, stdout, stderr = self.__handle.exec_command('docker exec %s bash -c %s' %
|
|
(self.docker_name, cmd),
|
|
timeout=timeout)
|
|
stdout._set_mode('rb')
|
|
|
|
sys.stderr.write(stderr.read())
|
|
output = [r.rstrip() for r in stdout.readlines()]
|
|
return output
|
|
|
|
except paramiko.SSHException:
|
|
if i < retry - 1:
|
|
print('SSH connection is lost, try reconnect after 1 second.')
|
|
time.sleep(1)
|
|
self.__connect()
|
|
else:
|
|
raise ConnectionError('SSH connection is lost')
|
|
|
|
|
|
class OpenThread_BR_Sim(OpenThread_BR):
|
|
|
|
def _getHandle(self):
|
|
self.log('SSH connecting ...')
|
|
return SSHHandle(self.ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, self.docker_name)
|
|
|
|
@watched
|
|
def _parseConnectionParams(self, params):
|
|
discovery_add = params.get('SerialPort')
|
|
if '@' not in discovery_add:
|
|
raise ValueError('%r in the field `add` is invalid' % discovery_add)
|
|
|
|
self.docker_name, self.ssh_ip = discovery_add.split('@')
|
|
self.tag, self.node_id = self.docker_name.split('_')
|
|
self.node_id = int(self.node_id)
|
|
# Let it crash if it is an invalid IP address
|
|
ipaddress.ip_address(self.ssh_ip)
|
|
|
|
self.connectType = 'ip'
|
|
self.telnetIp = self.port = discovery_add
|
|
|
|
global config
|
|
ssh = config['ssh']
|
|
self.telnetPort = ssh['port']
|
|
self.telnetUsername = ssh['username']
|
|
self.telnetPassword = ssh['password']
|
|
|
|
self.extraParams = {
|
|
'cmd-start-otbr-agent': 'service otbr-agent start',
|
|
'cmd-stop-otbr-agent': 'service otbr-agent stop',
|
|
'cmd-restart-otbr-agent': 'service otbr-agent restart',
|
|
'cmd-restart-radvd': 'service radvd stop; service radvd start',
|
|
}
|
|
|
|
|
|
assert issubclass(OpenThread_BR_Sim, IThci)
|