def check_ip6_addresses(): # Verify that SLAAC addresses are removed on r2 and fed1 wpan_util.verify_no_address([self.r2, self.fed1], PREFIX) # And that user-added address matching the prefix is not removed on r1 r1_addrs = wpan_table_parser.parse_list( self.r1.get(wpan.WPAN_IP6_ALL_ADDRESSES)) verify(IP_ADDRESS in r1_addrs)
def test07_verify_sed_forced_switch_to_parent_r3(self): # Force sed to switch its parent from r1 to r3 # # r3 ---- r1 ---- r2 # | \ # | \ # ed3 sed self.r1.un_allowlist_node(self.sed) self.r3.allowlist_node(self.sed) self.sed.allowlist_node(self.r3) # Wait for sed to detach from r1 and attach to r3. def check_sed_is_removed_from_r1_child_table(): child_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_address_result( child_table) verify(len(child_table) == 0) verify_within(check_sed_is_removed_from_r1_child_table, REATTACH_WAIT_TIME) # check that sed is now a child of r3 (r3 should have two child, sed and ed3) child_table = self.r3.wpanctl("get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_address_result( child_table) verify(len(child_table) == 2)
def test06_verify_address_cache_table_on_r1_removes_sed2(self): # New network topology # # r1 ---- r2 ---- r3 # | /\ # | / \ # fed1 sed2 fed3 # From r1 send again to sed2 (which is now a child of r3). # # Note that r1 still has r2 as the destination for sed2's address in its address # cache table. But since r2 is aware that sed2 is no longer its child, when it # receives the IPv6 message with sed2's address, r2 itself would do an address # query for the address and forward the IPv6 message. self.transmit_receive_udp_message([ (self.r1, self.sed2, self.r1_address, self.sed2_address) ]) time.sleep(ADDRESS_CACHE_TABLE_WAIT_TIME) # The address cache table on r1 should have sed2's address removed. addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == 1) for entry in addr_cache_table: if entry.address == self.fed3_address: # Entry for fed3 should still point towards fed3 verify(entry.rloc16 == self.fed3_rloc) else: raise VerifyError("Unknown entry in the address cache table")
def test10_verify_sed2_forced_switch_again_to_parent_r3(self): # Force sed2 to switch its parent from r2 to r3 self.sed2.setprop(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, str(CHILD_SUPERVISION_CHECK_TIMEOUT)) self.r3.setprop(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL)) self.r2.un_allowlist_node(self.sed2) self.r3.allowlist_node(self.sed2) self.sed2.allowlist_node(self.r3) # Wait for sed2 to detach from r2 and attach to r3. # # Upon re-attach, previous parent r2 is notified and should remove sed2 from its child table. def check_sed2_is_removed_from_r2_child_table(): child_table = self.r2.wpanctl( "get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_address_result( child_table) verify(len(child_table) == 0) verify_within(check_sed2_is_removed_from_r2_child_table, REATTACH_WAIT_TIME) # Verify that both sed2, fed3 are children of r3 child_table = self.r3.wpanctl("get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_address_result( child_table) verify(len(child_table) == 2)
def verify_child_table(self, parent, children): """ This function verifies that child table on `parent` node contains all the entries in `children` list and the child table entry's mode value matches the children Thread mode. """ child_table = wpan_table_parser.parse_child_table_result( parent.wpanctl("get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2)) print(child_table) verify(len(child_table) == len(children)) for child in children: ext_addr = child.get(wpan.WPAN_EXT_ADDRESS)[1:-1] for entry in child_table: if entry.ext_address == ext_addr: break else: raise VerifyError( "Failed to find a child entry for extended address" " {} in table".format(ext_addr)) verify(int(entry.rloc16, 16) \ == int(child.get(wpan.WPAN_THREAD_RLOC16), 16)) mode = int(child.get(wpan.WPAN_THREAD_DEVICE_MODE), 0) verify(entry.is_rx_on_when_idle() \ == (mode & wpan.THREAD_MODE_FLAG_RX_ON_WHEN_IDLE != 0)) verify(entry.is_ftd() \ == (mode & wpan.THREAD_MODE_FLAG_FULL_THREAD_DEV != 0)) verify(entry.is_full_net_data() \ == (mode & wpan.THREAD_MODE_FLAG_FULL_NETWORK_DATA != 0))
def check_device_role(): node_type = device.wpanctl("get", "get " + wpan.WPAN_NODE_TYPE, 2).split("=")[1].strip()[1:-1] self.logger.info( "Node type for {} is {} currently, expect to be {}".format( device.device_path, node_type, expect_role)) verify(node_type == expect_role)
def check_r1_router_table(): router_table = wpan_table_parser.parse_router_table_result( self.r1.get(wpan.WPAN_THREAD_ROUTER_TABLE)) verify(len(router_table) == 3) for entry in router_table: if entry.rloc16 == self.r3_rloc: verify(entry.next_hop != INVALID_ROUTER_ID)
def check_child_is_reattached(self): child_stat_ncp_changes = len( wpan_table_parser.parse_list(self.child1.get("stat:ncp"))) print(child_stat_ncp_changes) verify(child_stat_ncp_changes > self.child_num_state_changes) verify(is_associated(self.child1))
def check_r1_router_table(): router_table = self.routers[0].get(wpan.WPAN_THREAD_ROUTER_TABLE) router_table = wpan_table_parser.parse_router_table_result( router_table) self.assertEqual(len(router_table), NUM_ROUTERS) for entry in router_table: verify(entry.rloc16 == r1_rloc or entry.is_link_established() or entry.next_hop != INVALID_ROUTER_ID)
def test02_verify_discerner_value_after_device_reset(self): # Verify Discerner value after device reset on Joiner. verify( int(self.joiner1.get(wpan.WPAN_THREAD_JOINER_DISCERNER_BIT_LENGTH), 0) == 0) verify( int(self.joiner1.get(wpan.WPAN_THREAD_JOINER_DISCERNER_VALUE), 0) == 0)
def test03_verify_child_sends_data_poll(self): # Ensure data poll is sent by the child within Child Timeout. poll_count_before = int( self.child.get(wpan.WPAN_NCP_COUNTER_TX_PKT_DATA_POLL), 0) time.sleep(CHILD_TIMEOUT * 1.1) poll_count_after = int( self.child.get(wpan.WPAN_NCP_COUNTER_TX_PKT_DATA_POLL), 0) verify(poll_count_after > poll_count_before)
def check_cache_entry_switch_to_query_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_QUERY) verify(cache_table[index].can_evict() == True)
def check_cache_entry_in_retry_state_to_get_to_zero_timeout(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify(cache_table[index].timeout == 0)
def check_address_prefix_removed(): # Verify that address is removed from r2 verify(self.r2.find_ip6_address_with_prefix(IP6_PREFIX_1) == "") # Verify that the related prefix is also removed on all nodes for node in self.all_nodes: prefixes = wpan_table_parser.parse_on_mesh_prefix_result( node.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES)) for p in prefixes: verify(p.prefix != IP6_PREFIX_1)
def test04_Verify_Add_Remove_Prefix(self): # Test `add-prefix` and `remove-prefix` self.r1.add_prefix(prefix4, 48, priority="1", stable=False, on_mesh=True, slaac=False, dhcp=True, configure=False, default_route=True, preferred=False) verify_prefix([self.r1], prefix4, 48, priority="high", stable=False, on_mesh=True, slaac=False, dhcp=True, configure=False, default_route=True, preferred=False) # Remove prefix and verify that it is removed from list self.r1.remove_prefix(prefix4, 48) time.sleep(0.5) verify( self.r1.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES).find(prefix4) < 0) self.r1.add_prefix(prefix4, 48, priority="-1", stable=True, on_mesh=False, slaac=True, dhcp=False, configure=True, default_route=False, preferred=True) verify_prefix([self.r1], prefix4, 48, priority="low", stable=True, on_mesh=False, slaac=True, dhcp=False, configure=True, default_route=False, preferred=True)
def test03_verify_channel_change(self): channel = self.network_data.channel verify(int(self.r1.get(wpan.WPAN_CHANNEL), 0) == channel) verify_channel(self.all_nodes, channel) # Request a channel change to channel 13 from router r1 self.r1.set(wpan.WPAN_CHANNEL_MANAGER_NEW_CHANNEL, '13') verify( int(self.r1.get(wpan.WPAN_CHANNEL_MANAGER_NEW_CHANNEL), 0) == 13) verify_channel(self.all_nodes, 13)
def test02_verify_default_poll_interval(self): # Verify the default poll interval is smaller than child_timeout TestDataPollInterval.child_timeout = int( self.sed.get(wpan.WPAN_THREAD_CHILD_TIMEOUT), 0) * 1000 TestDataPollInterval.default_poll_interval = int( self.sed.get(wpan.WPAN_POLL_INTERVAL), 0) self.logger.info( "verify condition: 0 < default_poll_interval <= child_timeout" " i.e. 0 < {} <= {}".format(self.default_poll_interval, self.child_timeout)) verify(0 < self.default_poll_interval <= self.child_timeout)
def test03_Add_Multicast_Address(self): for node in self.device_list: node.add(wpan.WPAN_IP6_MULTICAST_ADDRESSES, MCAST_ADDR) addrs = wpan_table_parser.parse_list( node.get(wpan.WPAN_IP6_MULTICAST_ADDRESSES)) verify(MCAST_ADDR in addrs) node.remove(wpan.WPAN_IP6_MULTICAST_ADDRESSES, MCAST_ADDR) addrs = wpan_table_parser.parse_list( node.get(wpan.WPAN_IP6_MULTICAST_ADDRESSES)) verify(not MCAST_ADDR in addrs)
def test04_Verify_New_Parent(self): # Verify that the `child` is now attached to `parent1` child_table = self.parent1.wpanctl( "get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_result(child_table) verify(len(child_table) == 1) # Finally verify that the `child` is removed from previous parent's child # table (which indicates that the `child` did indeed inform its previous # parent). verify_within(self.check_child_is_removed_from_parent2_table, 1)
def check_cache_entry_switch_to_retry_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): self.logger.info( f"{cache_table[index].state}, {wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY}" ) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify( cache_table[index].retry_delay >= 2 * INITIAL_RETRY_DELAY)
def test05_verify_data_poll_with_poll_interval_zero(self): # Verify behavior when poll interval is set to zero. self.sed.set(wpan.WPAN_POLL_INTERVAL, "0") # Poll interval should use default interval again (based on child timeout). self.logger.info( "verify condition: setting poll interval 0ms should use default poll interval" " again (based on child timeout) i.e. {} == {}".format( int(self.sed.get(wpan.WPAN_POLL_INTERVAL), 0), self.default_poll_interval)) verify( int(self.sed.get(wpan.WPAN_POLL_INTERVAL), 0) == self.default_poll_interval)
def test02_Verify_Child_Mode(self): # Disable child supervision on all devices self.parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, "0") self.child1.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, "0") self.child2.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, "0") # Verify Thread Device Mode on both children verify(int(self.child1.get(wpan.WPAN_THREAD_DEVICE_MODE), 0) \ == DEVICE_MODE_END_DEVICE) verify(int(self.child2.get(wpan.WPAN_THREAD_DEVICE_MODE), 0) \ == DEVICE_MODE_SLEEPY_END_DEVICE) verify_within(self.check_child_table, WAIT_INTERVAL)
def test04_Verify_Child_Mode_Change(self): # Change mode on both children (make child1 sleepy, and child2 non-sleepy) self.child1.set(wpan.WPAN_THREAD_DEVICE_MODE, str(DEVICE_MODE_SLEEPY_END_DEVICE)) verify(int(self.child1.get(wpan.WPAN_THREAD_DEVICE_MODE), 0) \ == DEVICE_MODE_SLEEPY_END_DEVICE) self.child2.set(wpan.WPAN_THREAD_DEVICE_MODE, str(DEVICE_MODE_END_DEVICE)) verify(int(self.child2.get(wpan.WPAN_THREAD_DEVICE_MODE), 0) \ == DEVICE_MODE_END_DEVICE) # Verify that the child table on parent is also updated verify_within(self.check_child_table, WAIT_INTERVAL)
def test02_start_commissioning(self): joiner_hw_addr = self.joiner.get(wpan.WPAN_HW_ADDRESS)[1:-1] # Remove the `[]` # Start the commissioner and add joiner hw address along with PSKd self.r1.commissioner_start() self.wait_for_completion(self.device_list) self.r1.commissioner_add_joiner(joiner_hw_addr, PSKd) # Start Joiner self.joiner.joiner_join(PSKd) # Verify that Joiner succeeds in getting commissioned verify(self.joiner.get(wpan.WPAN_STATE) == wpan.STATE_COMMISSIONED)
def test02_start_commissioning(self): self.logger.info("Get joiner {}'s hardware address, it is needed to let commissioner {} know which joiner" " should be able to join.".format(self.joiner.name, self.commissioner.name)) joiner_hw_addr = self.joiner.get(wpan.WPAN_HW_ADDRESS)[1:-1] # Remove the `[]` # Start the commissioner and add joiner hw address along with PSKd self.commissioner.commissioner_start() self.wait_for_completion(self.device_list) self.commissioner.commissioner_add_joiner(joiner_hw_addr, PSKd) # Start Joiner self.joiner.joiner_join(PSKd) # Verify that Joiner succeeds in getting commissioned verify(self.joiner.get(wpan.WPAN_STATE) == wpan.STATE_COMMISSIONED)
def test03_verify_reenabled_sc2_attaches_via_mle_announcement(self): # Now re-enable sc2 and verify that it does attach to router and is on channel 26 # sc2 would go through the ML Announce recovery. channel = self.network_data.channel self.sc2.set('Daemon:AutoAssociateAfterReset', 'true') self.sc2.reset_thread_radio() self.wait_for_completion(self.device_list) verify(int(self.sc2.get(wpan.WPAN_CHANNEL), 0) == channel) # Wait for 20s for sc2 to be attached/associated def check_c2_is_associated(): verify(is_associated(self.sc2)) verify_within(check_c2_is_associated, WAIT_TIME) # Check that sc2 is now on channel 26. verify(int(self.sc2.get(wpan.WPAN_CHANNEL), 0) == 26)
def test04_verify_role_prefix_childTable(self): # Verify that the prefix1 and its corresponding address are present on all nodes verify_prefix(self.all_nodes, prefix1, stable=True, on_mesh=True, slaac=True) verify_address(self.all_nodes, prefix1) # Verify that the prefix1 and its corresponding address are present on all nodes verify_prefix(self.all_nodes, prefix2, stable=True, on_mesh=True, slaac=True, default_route=True, priority="high") verify_address(self.all_nodes, prefix2) # verify r1, r2 role, one of them is leader r1_role = self.r1.wpanctl("get", "get " + wpan.WPAN_NODE_TYPE, 2).split("=")[1].strip()[1:-1] r2_role = self.r2.wpanctl("get", "get " + wpan.WPAN_NODE_TYPE, 2).split("=")[1].strip()[1:-1] roles_result = (r1_role == "leader" and r2_role == "router") or (r2_role == "leader" and r1_role == "router") self.assertTrue( roles_result, "r1, r2 role is not right, r1 is {}, r2 is {}".format( r1_role, r2_role)) # verify their children stayed with their parents child_table = self.r1.wpanctl("get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_result(child_table) verify(len(child_table) == NUM_CHILDREN) child_table = self.r2.wpanctl("get", "get " + wpan.WPAN_THREAD_CHILD_TABLE, 2) child_table = wpan_table_parser.parse_child_table_result(child_table) verify(len(child_table) == NUM_CHILDREN)
def test06_verify_data_poll_with_changed_child_timeout(self): # Change "child timeout" and verify that default interval also changes self.sed.set(wpan.WPAN_THREAD_CHILD_TIMEOUT, str(self.child_timeout / 1000 * 2)) new_default_interval = int(self.sed.get(wpan.WPAN_POLL_INTERVAL), 0) self.logger.info( "verify condition: default_poll_interval < new_default_poll_interval " "(created by double child timeout) <= child_timeout * 2" " i.e. {} < {} <= {}".format(self.default_poll_interval, new_default_interval, self.child_timeout * 2)) verify(self.default_poll_interval < new_default_interval <= self.child_timeout * 2) # reset the child timeout to default value self.sed.set(wpan.WPAN_THREAD_CHILD_TIMEOUT, str(self.child_timeout / 1000)) verify( int(self.sed.get(wpan.WPAN_POLL_INTERVAL), 0) == self.default_poll_interval)
def test05_add_joiners_on_commissioner(self): self.commissioner.commissioner_start() self.wait_for_completion(self.device_list) # Add `joiner1` and `joiner2` using joiner discerner. self.commissioner.commissioner_add_joiner_with_discerner( DISCERNER1, D_LEN1, PSK1, JOINER_TIMOUT) self.commissioner.commissioner_add_joiner_with_discerner( DISCERNER2, D_LEN2, PSK2, JOINER_TIMOUT) # Add `joiner3` using EUI64 Hardware Address of joiner3. joiner3_hw_addr = self.joiner3.get( wpan.WPAN_HW_ADDRESS)[1:-1] # Remove the `[]` self.commissioner.commissioner_add_joiner(joiner3_hw_addr, PSK3, JOINER_TIMOUT) verify( len( wpan_table_parser.parse_list( self.commissioner.get( wpan.WPAN_THREAD_COMMISSIONER_JOINERS))) == 3)
def test04_verify_data_poll_with_long_to_short_poll_interval(self): # Verify behavior when poll interval is switched from long to short. # # - Poll interval set to 5 seconds # - Wait for 200 ms # - Change poll interval to 200 ms # - This should immediately trigger a poll tx since 100 ms is # already passed since last poll transmission. self.sed.set(wpan.WPAN_POLL_INTERVAL, "5000") time.sleep(WAIT_TIME2) poll_count_before = int( self.sed.get(wpan.WPAN_NCP_COUNTER_TX_PKT_DATA_POLL), 0) self.sed.set(wpan.WPAN_POLL_INTERVAL, str(WAIT_TIME2 * 1000)) time.sleep(0.01) poll_count_after = int( self.sed.get(wpan.WPAN_NCP_COUNTER_TX_PKT_DATA_POLL), 0) self.logger.info( "verify condition: poll count with poll interval 200ms > poll count with poll interval 5s" " i.e. {} > {}".format(poll_count_after, poll_count_before)) verify(poll_count_after > poll_count_before)