1#!/usr/bin/env python3 2# 3# Copyright (c) 2018, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28 29import wpan 30from wpan import verify 31 32# ----------------------------------------------------------------------------------------------------------------------- 33# Test description: Parent restoring children after reset 34# 35# This test covers the following: 36# 37# - A parent node with a given number sleepy and rx-on children. 38# - Parent node is reset 39# - It is verified that all children are recovered and are present in the parent's child table 40# - It is also verified that all children are restored on parent through "Child Update" exchange process and none of 41# them got detached and needed to attach back. 42# 43 44test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 45print('-' * 120) 46print('Starting \'{}\''.format(test_name)) 47 48# ----------------------------------------------------------------------------------------------------------------------- 49# Creating `wpan.Nodes` instances 50 51speedup = 2 52wpan.Node.set_time_speedup_factor(speedup) 53 54NUM_SLEEPY_CHILDREN = 3 55NUM_RX_ON_CHILDREN = 2 56 57NUM_CHILDREN = NUM_SLEEPY_CHILDREN + NUM_RX_ON_CHILDREN 58 59parent = wpan.Node() 60 61sleepy_children = [] 62for num in range(NUM_SLEEPY_CHILDREN): 63 sleepy_children.append(wpan.Node()) 64 65rx_on_children = [] 66for num in range(NUM_RX_ON_CHILDREN): 67 rx_on_children.append(wpan.Node()) 68 69all_children = sleepy_children + rx_on_children 70 71# ----------------------------------------------------------------------------------------------------------------------- 72# Init all nodes 73 74wpan.Node.init_all_nodes() 75 76# ----------------------------------------------------------------------------------------------------------------------- 77# Build network topology 78# 79 80parent.form("recovery") 81 82for child in sleepy_children: 83 child.join_node(parent, wpan.JOIN_TYPE_SLEEPY_END_DEVICE) 84 child.set(wpan.WPAN_POLL_INTERVAL, '4000') 85 86for child in rx_on_children: 87 child.join_node(parent, wpan.JOIN_TYPE_END_DEVICE) 88 89# ----------------------------------------------------------------------------------------------------------------------- 90# Test implementation 91 92# The test verifies that all children are recovered after the parent is reset. 93# 94# The `wpantund` statistics collector is used to determine if the children are 95# recovered through "Child Update Request/Response" exchange triggered by parent 96# after it was reset (child restoration process) or if they got detached and 97# went through full attach process again. The wpantund stat-collector "stat:ncp" 98# property keeps track of all NCP state changes on a device: 99# 100# Node.wpanctl('get -v stat:ncp'): 101# [ 102# "NCP State History" 103# "-------------------------" 104# "00:00:03.220 ago -> associated" 105# "00:00:03.604 ago -> associating" 106# "00:00:06.027 ago -> offline" 107# ] 108# 109# 110# In the first case (child being restored through "Child Update" exchange) the 111# child should never be detached and its "stat:ncp" history should remain the 112# same. In the second case the child would get detached and we should 113# observe a wpantund state change "associated:no-parent" before it gets attached 114# again: 115# 116# Node.wpanctl('get -v stat:ncp'): 117# [ 118# "NCP State History" 119# "-------------------------" 120# "00:00:01.160 ago -> associated" 121# "00:00:01.814 ago -> associated:no-parent" 122# "00:00:04.911 ago -> associated" 123# "00:00:05.298 ago -> associating" 124# "00:00:06.475 ago -> offline" 125# ] 126# 127 128 129def check_child_table(): 130 # Checks the child table includes the expected number of children. 131 child_table = wpan.parse_list(parent.get(wpan.WPAN_THREAD_CHILD_TABLE)) 132 verify(len(child_table) == NUM_CHILDREN) 133 134 135# Verify that all children are present in the child table 136check_child_table() 137 138# Remember number of NCP state changes (using "stat:ncp" property) per child 139child_num_state_changes = [] 140for child in all_children: 141 child_num_state_changes.append(len(wpan.parse_list(child.get("stat:ncp")))) 142 143# Reset the parent 144parent.reset() 145 146 147def check_parent_is_associated(): 148 verify(parent.is_associated()) 149 150 151wpan.verify_within(check_parent_is_associated, 5) 152 153# Verify that all the children are recovered and present in the parent's 154# child table again (within 5 seconds). 155wpan.verify_within(check_child_table, 9) 156 157# Verify that number of state changes on all children stays as before 158# (indicating they did not get detached). 159for i in range(len(all_children)): 160 verify(child_num_state_changes[i] == len(wpan.parse_list(all_children[i].get("stat:ncp")))) 161 162# ----------------------------------------------------------------------------------------------------------------------- 163# Test finished 164 165wpan.Node.finalize_all_nodes() 166 167print('\'{}\' passed.'.format(test_name)) 168