def test_wait_for_media_switch_diff_interface(self, m_read_netlink_socket, m_socket): """wait_for_media_disconnect_connect ignores unexpected interfaces. The first two messages are for other interfaces and last two are for expected interface. So the function exit only after receiving last 2 messages and therefore the call count for m_read_netlink_socket has to be 4 """ other_ifname = "eth1" expected_ifname = "eth0" data_op_down_eth1 = self._media_switch_data(other_ifname, RTM_NEWLINK, OPER_DOWN) data_op_up_eth1 = self._media_switch_data(other_ifname, RTM_NEWLINK, OPER_UP) data_op_down_eth0 = self._media_switch_data(expected_ifname, RTM_NEWLINK, OPER_DOWN) data_op_up_eth0 = self._media_switch_data(expected_ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [ data_op_down_eth1, data_op_up_eth1, data_op_down_eth0, data_op_up_eth0, ] wait_for_media_disconnect_connect(m_socket, expected_ifname) self.assertIn( "Ignored netlink event on interface %s" % other_ifname, self.logs.getvalue(), ) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_wait_for_media_switch_diff_interface(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect ignores unexpected interfaces. The first two messages are for other interfaces and last two are for expected interface. So the function exit only after receiving last 2 messages and therefore the call count for m_read_netlink_socket has to be 4 ''' other_ifname = "eth1" expected_ifname = "eth0" data_op_down_eth1 = self._media_switch_data( other_ifname, RTM_NEWLINK, OPER_DOWN) data_op_up_eth1 = self._media_switch_data( other_ifname, RTM_NEWLINK, OPER_UP) data_op_down_eth0 = self._media_switch_data( expected_ifname, RTM_NEWLINK, OPER_DOWN) data_op_up_eth0 = self._media_switch_data( expected_ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_op_down_eth1, data_op_up_eth1, data_op_down_eth0, data_op_up_eth0] wait_for_media_disconnect_connect(m_socket, expected_ifname) self.assertIn('Ignored netlink event on interface %s' % other_ifname, self.logs.getvalue()) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_wait_invalid_socket(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect handle none netlink socket.''' socket = None ifname = "eth0" with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(socket, ifname) self.assertTrue('netlink socket is none' in str(context.exception))
def test_invalid_msgtype_setlink(self, m_read_netlink_socket, m_socket): """wait_for_media_disconnect_connect ignores SETLINK events. The first two messages are for oper down and up for RTM_GETLINK type which it will ignore. 3rd and 4th messages are RTM_NEWLINK with down and up messages. This function should exit after 4th messages since it sees down->up scenario. So the call count for m_read_netlink_socket has to be 4 ignoring first 2 messages of RTM_GETLINK and last 2 messages of RTM_NEWLINK """ ifname = "eth0" data_setlink_down = self._media_switch_data(ifname, RTM_SETLINK, OPER_DOWN) data_setlink_up = self._media_switch_data(ifname, RTM_SETLINK, OPER_UP) data_newlink_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_newlink_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [ data_setlink_down, data_setlink_up, data_newlink_down, data_newlink_up, data_newlink_down, data_newlink_up, ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_wait_invalid_socket(self, m_read_netlink_socket, m_socket): """wait_for_media_disconnect_connect handle none netlink socket.""" socket = None ifname = "eth0" with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(socket, ifname) self.assertTrue("netlink socket is none" in str(context.exception))
def test_netlink_invalid_switch_scenario(self, m_read_netlink_socket, m_socket): """returns only if it receives UP event after a DOWN event""" ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_dormant = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DORMANT) data_op_notpresent = self._media_switch_data(ifname, RTM_NEWLINK, OPER_NOTPRESENT) data_op_lowerdown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_LOWERLAYERDOWN) data_op_testing = self._media_switch_data(ifname, RTM_NEWLINK, OPER_TESTING) data_op_unknown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UNKNOWN) m_read_netlink_socket.side_effect = [ data_op_up, data_op_up, data_op_dormant, data_op_up, data_op_notpresent, data_op_up, data_op_lowerdown, data_op_up, data_op_testing, data_op_up, data_op_unknown, data_op_up, data_op_down, data_op_up, ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 14)
def test_netlink_invalid_switch_scenario(self, m_read_netlink_socket, m_socket): '''returns only if it receives UP event after a DOWN event''' ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_dormant = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DORMANT) data_op_notpresent = self._media_switch_data(ifname, RTM_NEWLINK, OPER_NOTPRESENT) data_op_lowerdown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_LOWERLAYERDOWN) data_op_testing = self._media_switch_data(ifname, RTM_NEWLINK, OPER_TESTING) data_op_unknown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UNKNOWN) m_read_netlink_socket.side_effect = [data_op_up, data_op_up, data_op_dormant, data_op_up, data_op_notpresent, data_op_up, data_op_lowerdown, data_op_up, data_op_testing, data_op_up, data_op_unknown, data_op_up, data_op_down, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 14)
def test_invalid_msgtype_setlink(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect ignores SETLINK events. The first two messages are for oper down and up for RTM_GETLINK type which it will ignore. 3rd and 4th messages are RTM_NEWLINK with down and up messages. This function should exit after 4th messages since it sees down->up scenario. So the call count for m_read_netlink_socket has to be 4 ignoring first 2 messages of RTM_GETLINK and last 2 messages of RTM_NEWLINK ''' ifname = "eth0" data_setlink_down = self._media_switch_data( ifname, RTM_SETLINK, OPER_DOWN) data_setlink_up = self._media_switch_data( ifname, RTM_SETLINK, OPER_UP) data_newlink_down = self._media_switch_data( ifname, RTM_NEWLINK, OPER_DOWN) data_newlink_up = self._media_switch_data( ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_setlink_down, data_setlink_up, data_newlink_down, data_newlink_up, data_newlink_down, data_newlink_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_read_multiple_netlink_msgs(self, m_read_netlink_socket, m_socket): """Read multiple messages in single receive call""" ifname = "eth0" bytes = ifname.encode("utf-8") data = bytearray(96) struct.pack_into("=LHHLL", data, 0, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into( "HH4sHHc", data, RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN), ) struct.pack_into("=LHHLL", data, 48, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into( "HH4sHHc", data, 48 + RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_UP), ) m_read_netlink_socket.return_value = data wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 1)
def test_read_partial_netlink_msgs(self, m_read_netlink_socket, m_socket): """Read partial messages in receive call""" ifname = "eth0" bytes = ifname.encode("utf-8") data1 = bytearray(112) data2 = bytearray(32) struct.pack_into("=LHHLL", data1, 0, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into( "HH4sHHc", data1, RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN), ) struct.pack_into("=LHHLL", data1, 48, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data1, 80, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN)) struct.pack_into("=LHHLL", data1, 96, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data2, 16, 8, 3, bytes, 5, 16, int_to_bytes(OPER_UP)) m_read_netlink_socket.side_effect = [data1, data2] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 2)
def _poll_imds(self): """Poll IMDS for the new provisioning data until we get a valid response. Then return the returned JSON object.""" url = IMDS_URL + "reprovisiondata?api-version=2017-04-02" headers = {"Metadata": "true"} nl_sock = None report_ready = bool(not os.path.isfile(REPORTED_READY_MARKER_FILE)) def exc_cb(msg, exception): if isinstance(exception, UrlError) and exception.code == 404: return True # If we get an exception while trying to call IMDS, we # call DHCP and setup the ephemeral network to acquire the new IP. return False LOG.debug("Wait for vnetswitch to happen") while True: try: # Save our EphemeralDHCPv4 context so we avoid repeated dhcp self._ephemeral_dhcp_ctx = EphemeralDHCPv4() lease = self._ephemeral_dhcp_ctx.obtain_lease() if report_ready: try: nl_sock = netlink.create_bound_netlink_socket() except netlink.NetlinkCreateSocketError as e: LOG.warning(e) self._ephemeral_dhcp_ctx.clean_network() return path = REPORTED_READY_MARKER_FILE LOG.info("Creating a marker file to report ready: %s", path) util.write_file( path, "{pid}: {time}\n".format(pid=os.getpid(), time=time())) self._report_ready(lease=lease) report_ready = False try: netlink.wait_for_media_disconnect_connect( nl_sock, lease['interface']) except AssertionError as error: LOG.error(error) return self._ephemeral_dhcp_ctx.clean_network() else: return readurl(url, timeout=1, headers=headers, exception_cb=exc_cb, infinite=True, log_req_resp=False).contents except UrlError: # Teardown our EphemeralDHCPv4 context on failure as we retry self._ephemeral_dhcp_ctx.clean_network() pass finally: if nl_sock: nl_sock.close()
def test_media_down_up_scenario(self, m_read_netlink_socket, m_socket): """Test for media down up sequence for required interface name""" ifname = "eth0" # construct data for Oper State down data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) # construct data for Oper State up data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_op_down, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 2)
def test_wait_invalid_rta_attr(self, m_read_netlink_socket, m_socket): ''' wait_for_media_disconnect_connect handles invalid rta data''' ifname = "eth0" data_invalid1 = self._media_switch_data(None, RTM_NEWLINK, OPER_DOWN) data_invalid2 = self._media_switch_data(ifname, RTM_NEWLINK, None) data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_invalid1, data_invalid2, data_op_down, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_wait_invalid_ifname(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect handle none interface name''' ifname = None with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(m_socket, ifname) self.assertTrue('interface name is none' in str(context.exception)) ifname = "" with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(m_socket, ifname) self.assertTrue('interface name cannot be empty' in str(context.exception))
def test_media_down_up_scenario(self, m_read_netlink_socket, m_socket): '''Test for media down up sequence for required interface name''' ifname = "eth0" # construct data for Oper State down data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) # construct data for Oper State up data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_op_down, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 2)
def test_wait_invalid_ifname(self, m_read_netlink_socket, m_socket): """wait_for_media_disconnect_connect handle none interface name""" ifname = None with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(m_socket, ifname) self.assertTrue("interface name is none" in str(context.exception)) ifname = "" with self.assertRaises(AssertionError) as context: wait_for_media_disconnect_connect(m_socket, ifname) self.assertTrue( "interface name cannot be empty" in str(context.exception))
def test_wait_invalid_rta_attr(self, m_read_netlink_socket, m_socket): ''' wait_for_media_disconnect_connect handles invalid rta data''' ifname = "eth0" data_invalid1 = self._media_switch_data(None, RTM_NEWLINK, OPER_DOWN) data_invalid2 = self._media_switch_data(ifname, RTM_NEWLINK, None) data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [ data_invalid1, data_invalid2, data_op_down, data_op_up ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_netlink_valid_inbetween_transitions(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect handles in between transitions''' ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_dormant = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DORMANT) data_op_unknown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UNKNOWN) m_read_netlink_socket.side_effect = [data_op_down, data_op_dormant, data_op_unknown, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_read_multiple_netlink_msgs(self, m_read_netlink_socket, m_socket): '''Read multiple messages in single receive call''' ifname = "eth0" bytes = ifname.encode("utf-8") data = bytearray(96) struct.pack_into("=LHHLL", data, 0, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data, RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN)) struct.pack_into("=LHHLL", data, 48, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data, 48 + RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_UP)) m_read_netlink_socket.return_value = data wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 1)
def test_netlink_invalid_operstate(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect should handle invalid operstates. The function should not fail and return even if it receives invalid operstates. It always should wait for down up sequence. ''' ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_invalid = self._media_switch_data(ifname, RTM_NEWLINK, 7) m_read_netlink_socket.side_effect = [data_op_invalid, data_op_up, data_op_down, data_op_invalid, data_op_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 5)
def test_netlink_valid_inbetween_transitions(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect handles in between transitions''' ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_dormant = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DORMANT) data_op_unknown = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UNKNOWN) m_read_netlink_socket.side_effect = [ data_op_down, data_op_dormant, data_op_unknown, data_op_up ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_netlink_invalid_operstate(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect should handle invalid operstates. The function should not fail and return even if it receives invalid operstates. It always should wait for down up sequence. ''' ifname = "eth0" data_op_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_op_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) data_op_invalid = self._media_switch_data(ifname, RTM_NEWLINK, 7) m_read_netlink_socket.side_effect = [ data_op_invalid, data_op_up, data_op_down, data_op_invalid, data_op_up ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 5)
def test_read_partial_netlink_msgs(self, m_read_netlink_socket, m_socket): '''Read partial messages in receive call''' ifname = "eth0" bytes = ifname.encode("utf-8") data1 = bytearray(112) data2 = bytearray(32) struct.pack_into("=LHHLL", data1, 0, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data1, RTATTR_START_OFFSET, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN)) struct.pack_into("=LHHLL", data1, 48, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data1, 80, 8, 3, bytes, 5, 16, int_to_bytes(OPER_DOWN)) struct.pack_into("=LHHLL", data1, 96, 48, RTM_NEWLINK, 0, 0, 0) struct.pack_into("HH4sHHc", data2, 16, 8, 3, bytes, 5, 16, int_to_bytes(OPER_UP)) m_read_netlink_socket.side_effect = [data1, data2] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 2)
def test_invalid_msgtype_getlink(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect ignores GETLINK events. The first two messages are for oper down and up for RTM_GETLINK type which netlink module will ignore. The last 2 messages are RTM_NEWLINK with oper state down and up messages. Therefore the call count for m_read_netlink_socket has to be 4 ignoring first 2 messages of RTM_GETLINK ''' ifname = "eth0" data_getlink_down = self._media_switch_data(ifname, RTM_GETLINK, OPER_DOWN) data_getlink_up = self._media_switch_data(ifname, RTM_GETLINK, OPER_UP) data_newlink_down = self._media_switch_data(ifname, RTM_NEWLINK, OPER_DOWN) data_newlink_up = self._media_switch_data(ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [ data_getlink_down, data_getlink_up, data_newlink_down, data_newlink_up ] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)
def test_invalid_msgtype_getlink(self, m_read_netlink_socket, m_socket): '''wait_for_media_disconnect_connect ignores GETLINK events. The first two messages are for oper down and up for RTM_GETLINK type which netlink module will ignore. The last 2 messages are RTM_NEWLINK with oper state down and up messages. Therefore the call count for m_read_netlink_socket has to be 4 ignoring first 2 messages of RTM_GETLINK ''' ifname = "eth0" data_getlink_down = self._media_switch_data( ifname, RTM_GETLINK, OPER_DOWN) data_getlink_up = self._media_switch_data( ifname, RTM_GETLINK, OPER_UP) data_newlink_down = self._media_switch_data( ifname, RTM_NEWLINK, OPER_DOWN) data_newlink_up = self._media_switch_data( ifname, RTM_NEWLINK, OPER_UP) m_read_netlink_socket.side_effect = [data_getlink_down, data_getlink_up, data_newlink_down, data_newlink_up] wait_for_media_disconnect_connect(m_socket, ifname) self.assertEqual(m_read_netlink_socket.call_count, 4)