def test_set_routes_arp_ipv4_only(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() with self.assertRaisesRegexp(ValueError, "reset_arp may only be supplied for " "IPv4"): devices.set_routes(futils.IPV6, ips, interface, mac=mac, reset_arp=True)
def test_add_route(self): tap = "tap" + str(uuid.uuid4())[:11] mac = stub_utils.get_mac() retcode = futils.CommandOutput("", "") type = futils.IPV4 ip = "1.2.3.4" with mock.patch('calico.felix.futils.check_call', return_value=retcode): devices.add_route(type, ip, tap, mac) futils.check_call.assert_any_call(['arp', '-s', ip, mac, '-i', tap]) futils.check_call.assert_called_with(["ip", "route", "replace", ip, "dev", tap]) with self.assertRaisesRegexp(ValueError, "mac must be supplied if ip is provided"): devices.add_route(type, ip, tap, None) type = futils.IPV6 ip = "2001::" with mock.patch('calico.felix.futils.check_call', return_value=retcode): devices.add_route(type, ip, tap, mac) futils.check_call.assert_called_with(["ip", "-6", "route", "replace", ip, "dev", tap]) with self.assertRaisesRegexp(ValueError, "mac must be supplied if ip is provided"): devices.add_route(type, ip, tap, None)
def test_add_route(self): tap = "tap" + str(uuid.uuid4())[:11] mac = stub_utils.get_mac() retcode = futils.CommandOutput("", "") type = futils.IPV4 ip = "1.2.3.4" with mock.patch('calico.felix.futils.check_call', return_value=retcode): devices.add_route(type, ip, tap, mac) futils.check_call.assert_any_call( ['arp', '-s', ip, mac, '-i', tap]) futils.check_call.assert_called_with( ["ip", "route", "replace", ip, "dev", tap]) with mock.patch("calico.felix.futils.check_call") as m_check_call: devices.add_route(type, ip, tap, None) type = futils.IPV6 ip = "2001::" with mock.patch('calico.felix.futils.check_call', return_value=retcode): devices.add_route(type, ip, tap, mac) futils.check_call.assert_called_with( ["ip", "-6", "route", "replace", ip, "dev", tap]) with mock.patch("calico.felix.futils.check_call") as m_check_call: devices.add_route(type, ip, tap, None)
def test_set_routes_mac_required(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() with self.assertRaisesRegexp(ValueError, "mac must be supplied if ips is not " "empty"): devices.set_routes(type, ips, interface)
def test_set_routes_mac_required(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() with self.assertRaisesRegexp(ValueError, "mac must be supplied if ips is not " "empty"): devices.set_routes(type, ips, interface)
def test_set_routes_arp_ipv4_only(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() with self.assertRaisesRegexp(ValueError, "reset_arp may only be supplied for " "IPv4"): devices.set_routes(futils.IPV6, ips, interface, mac=mac, reset_arp=True)
def test_set_routes_nothing_to_do(self, m_remove_conntrack): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) retcode = futils.CommandOutput("", "") interface = "tapabcdef" mac = stub_utils.get_mac() with mock.patch("calico.felix.futils.check_call", return_value=retcode): with mock.patch("calico.felix.devices.list_interface_ips", return_value=ips): devices.set_routes(type, ips, interface, mac) self.assertEqual(futils.check_call.call_count, 0) m_remove_conntrack.assert_called_once_with(set(), 4)
def test_set_routes_nothing_to_do(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) retcode = futils.CommandOutput("", "") interface = "tapabcdef" mac = stub_utils.get_mac() with mock.patch('calico.felix.futils.check_call', return_value=retcode): with mock.patch('calico.felix.devices.list_interface_route_ips', return_value=ips): devices.set_routes(type, ips, interface, mac) self.assertEqual(futils.check_call.call_count, 0)
def test_profile_id_update_triggers_iptables(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["10.0.0.1"] iface = "tapabcdef" mac = stub_utils.get_mac() data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"]} local_ep._pending_endpoint = data.copy() # First update with endpoint not yet set, should trigger full sync. local_ep._apply_endpoint_update() self.assertEqual(local_ep.endpoint, data) self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._iptables_in_sync = True local_ep._device_in_sync = True # No-op update local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertTrue(local_ep._device_in_sync) # Profiles update. Should update iptables. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof2"]} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) local_ep._iptables_in_sync = True self.assertTrue(local_ep._device_in_sync) # IP update. Should update routing. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ["10.0.0.2"], 'profile_ids': ["prof2"]} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True # Delete, should update everything. local_ep._pending_endpoint = None local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync)
def test_on_endpoint_update_delete_fail(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["1.2.3.4/32"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack,\ mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv4') as m_conf,\ mock.patch('calico.felix.devices.interface_exists') as m_iface_exists,\ mock.patch('calico.felix.devices.interface_up') as m_iface_up: m_iface_exists.return_value = True m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(["1.2.3.4"]), iface, data['mac'], reset_arp=True) self.assertFalse(m_rem_conntrack.called) # Send empty data, which deletes the endpoint. Raise an exception # from set_routes to check that it's handled. with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.interface_exists', return_value=True),\ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: m_set_routes.side_effect = FailedSystemCall("", [], 1, "", "") local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None) # Should clean up conntrack entries for all IPs. m_rem_conntrack.assert_called_once_with( set(['1.2.3.4']), 4 )
def test_on_interface_update_v4(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["1.2.3.4"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"] } # We can only get on_interface_update calls after the first # on_endpoint_update, so trigger that. with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv4'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_up]: m_iface_up.return_value = False local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) self.assertTrue(local_ep._device_in_sync) # Now pretend to get an interface update - does all the same work. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) self.assertTrue(local_ep._device_in_sync)
def test_set_routes_mainline(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() calls = [mock.call(['arp', '-s', "1.2.3.4", mac, '-i', interface]), mock.call(["ip", "route", "replace", "1.2.3.4", "dev", interface]), mock.call(['arp', '-s', "2.3.4.5", mac, '-i', interface]), mock.call(["ip", "route", "replace", "2.3.4.5", "dev", interface])] with mock.patch('calico.felix.futils.check_call', return_value=futils.CommandOutput("", "")): with mock.patch('calico.felix.devices.list_interface_route_ips', return_value=set()): devices.set_routes(type, ips, interface, mac) self.assertEqual(futils.check_call.call_count, len(calls)) futils.check_call.assert_has_calls(calls, any_order=True)
def test_set_routes_mainline(self): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() calls = [mock.call(['arp', '-s', "1.2.3.4", mac, '-i', interface]), mock.call(["ip", "route", "replace", "1.2.3.4", "dev", interface]), mock.call(['arp', '-s', "2.3.4.5", mac, '-i', interface]), mock.call(["ip", "route", "replace", "2.3.4.5", "dev", interface])] with mock.patch('calico.felix.futils.check_call', return_value=futils.CommandOutput("", "")): with mock.patch('calico.felix.devices.list_interface_route_ips', return_value=set()): devices.set_routes(type, ips, interface, mac) self.assertEqual(futils.check_call.call_count, len(calls)) futils.check_call.assert_has_calls(calls, any_order=True)
def test_set_routes_mainline(self, m_remove_conntrack): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() calls = [ mock.call(["arp", "-s", "1.2.3.4", mac, "-i", interface]), mock.call(["ip", "route", "replace", "1.2.3.4", "dev", interface]), mock.call(["arp", "-s", "2.3.4.5", mac, "-i", interface]), mock.call(["ip", "route", "replace", "2.3.4.5", "dev", interface]), ] with mock.patch("calico.felix.futils.check_call", return_value=futils.CommandOutput("", "")): with mock.patch("calico.felix.devices.list_interface_ips", return_value=set()): devices.set_routes(type, ips, interface, mac) self.assertEqual(futils.check_call.call_count, len(calls)) futils.check_call.assert_has_calls(calls, any_order=True) m_remove_conntrack.assert_called_once_with(set(), 4)
def test_set_routes_changed_ips_reset_arp(self, m_remove_conntrack): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() retcode = futils.CommandOutput("", "") current_ips = set(["2.3.4.5", "3.4.5.6"]) calls = [mock.call(['arp', '-s', "1.2.3.4", mac, '-i', interface]), mock.call(["ip", "route", "replace", "1.2.3.4", "dev", interface]), mock.call(['arp', '-s', "2.3.4.5", mac, '-i', interface]), mock.call(['arp', '-d', "3.4.5.6", '-i', interface]), mock.call(["ip", "route", "del", "3.4.5.6", "dev", interface])] with mock.patch('calico.felix.futils.check_call', return_value=retcode): with mock.patch('calico.felix.devices.list_interface_ips', return_value=current_ips): devices.set_routes(type, ips, interface, mac, reset_arp=True) self.assertEqual(futils.check_call.call_count, len(calls)) futils.check_call.assert_has_calls(calls, any_order=True) m_remove_conntrack.assert_called_once_with(set(["3.4.5.6"]), 4)
def test_set_routes_changed_ips_reset_arp(self, m_remove_conntrack): type = futils.IPV4 ips = set(["1.2.3.4", "2.3.4.5"]) interface = "tapabcdef" mac = stub_utils.get_mac() retcode = futils.CommandOutput("", "") current_ips = set(["2.3.4.5", "3.4.5.6"]) calls = [mock.call(['arp', '-s', "1.2.3.4", mac, '-i', interface]), mock.call(["ip", "route", "replace", "1.2.3.4", "dev", interface]), mock.call(['arp', '-s', "2.3.4.5", mac, '-i', interface]), mock.call(['arp', '-d', "3.4.5.6", '-i', interface]), mock.call(["ip", "route", "del", "3.4.5.6", "dev", interface])] with mock.patch('calico.felix.futils.check_call', return_value=retcode): with mock.patch('calico.felix.devices.list_interface_route_ips', return_value=current_ips): devices.set_routes(type, ips, interface, mac, reset_arp=True) self.assertEqual(futils.check_call.call_count, len(calls)) futils.check_call.assert_has_calls(calls, any_order=True) m_remove_conntrack.assert_called_once_with(set(["3.4.5.6"]), 4)
def test_tiered_policy_mainline(self, m_devices): self.config.plugins["iptables_generator"] = self.m_ipt_gen ep = self.get_local_endpoint(ENDPOINT_ID, futils.IPV4) mac = stub_utils.get_mac() ep.on_endpoint_update( { 'state': "active", 'endpoint': "endpoint_id", 'mac': mac, 'name': "tap1234", 'ipv4_nets': ["10.0.0.1"], 'profile_ids': ["prof1"] }, async=True) self.step_actor(ep) self.assertEqual( self.m_ipt_gen.endpoint_updates.mock_calls, [ mock.call(4, 'd', '1234', mac, ['prof1'], {}), ] ) self.m_ipt_gen.endpoint_updates.reset_mock() tiers = OrderedDict() t1_1 = TieredPolicyId("t1", "t1_1") t1_2 = TieredPolicyId("t1", "t1_2") tiers["t1"] = [t1_1, t1_2] t2_1 = TieredPolicyId("t2", "t2_1") tiers["t2"] = [t2_1] ep.on_tiered_policy_update(tiers, async=True) self.step_actor(ep) self.assertEqual( self.m_ipt_gen.endpoint_updates.mock_calls, [ mock.call(4, 'd', '1234', mac, ['prof1'], OrderedDict([('t1', [TieredPolicyId('t1','t1_1'), TieredPolicyId('t1','t1_2')]), ('t2', [TieredPolicyId('t2','t2_1')])])) ])
def test_on_interface_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 retcode = futils.CommandOutput("", "") local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["1234::5678"] iface = "tapabcdef" data = { 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'profile_ids': []} # We can only get on_interface_update calls after the first # on_endpoint_update, so trigger that. with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv6'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv6.assert_called_once_with(iface, None) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Now pretend to get an interface update - does all the same work. with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv6'): local_ep.on_interface_update() devices.configure_interface_ipv6.assert_called_once_with(iface, None) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False)
def test_on_endpoint_update_v4(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 retcode = futils.CommandOutput("", "") local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=False) ips = ["1.2.3.4"] iface = "tapabcdef" data = { 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': []} # Report an initial update (endpoint creation) and check configured with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv4'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv4.assert_called_once_with(iface) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send through an update with no changes - should redo without # resetting ARP. with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv4'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv4.assert_called_once_with(iface) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Change the MAC address and try again, leading to reset of ARP. data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv4'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv4.assert_called_once_with(iface) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes'): local_ep.on_endpoint_update(None, async=False) devices.set_routes.assert_called_once_with(ip_type, set(), data["name"], None)
def test_on_endpoint_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 retcode = futils.CommandOutput("", "") local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=False) ips = ["2001::abcd"] gway = "2020:ab::9876" iface = "tapabcdef" data = { 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'ipv6_gateway': gway, 'profile_ids': []} # Report an initial update (endpoint creation) and check configured with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv6'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv6.assert_called_once_with(iface, gway) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send through an update with no changes - should redo without # resetting ARP. with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv6'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv6.assert_called_once_with(iface, gway) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send through an update with no changes - would reset ARP, but this is # IPv6 so it won't. data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes'): with mock.patch('calico.felix.devices.configure_interface_ipv6'): local_ep.on_endpoint_update(data, async=False) self.assertEqual(local_ep._mac, data['mac']) devices.configure_interface_ipv6.assert_called_once_with(iface, gway) devices.set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes'): local_ep.on_endpoint_update(None, async=False) devices.set_routes.assert_called_once_with(ip_type, set(), data["name"], None)
def test_main_flow(self): """ Test starting up and going through some of the basic flow. """ common.default_logging() context = stub_zmq.Context() agent = felix.FelixAgent(config_path, context) context.add_poll_result(0) agent.run() # Now we want to reply to the RESYNC request. resync_req = context.sent_data[TYPE_EP_REQ].pop() log.debug("Resync request : %s" % resync_req) self.assertFalse(context.sent_data_present()) resync_id = resync_req['resync_id'] resync_rsp = { 'type': "RESYNCSTATE", 'endpoint_count': 1, 'rc': "SUCCESS", 'message': "hello" } poll_result = context.add_poll_result(50) poll_result.add(TYPE_EP_REQ, resync_rsp) agent.run() # Felix expects one endpoint created message - give it what it wants endpoint_id = str(uuid.uuid4()) log.debug("Build first endpoint created : %s" % endpoint_id) mac = stub_utils.get_mac() suffix = endpoint_id[:11] tap = "tap" + suffix addr = '1.2.3.4' endpoint_created_req = { 'type': "ENDPOINTCREATED", 'endpoint_id': endpoint_id, 'resync_id': resync_id, 'issued': futils.time_ms(), 'mac': mac, 'state': Endpoint.STATE_ENABLED, 'addrs': [{ 'gateway': "1.2.3.1", 'addr': addr }] } poll_result = context.add_poll_result(100) poll_result.add(TYPE_EP_REP, endpoint_created_req) agent.run() log.debug("Create tap interface %s" % tap) tap_obj = stub_devices.TapInterface(tap) stub_devices.add_tap(tap_obj) poll_result = context.add_poll_result(150) agent.run() #*********************************************************************# #* As soon as that endpoint has been made to exist, we should see an *# #* ACL request coming through, and a response to the endpoint *# #* created. We send a reply to that now. *# #*********************************************************************# endpoint_created_rsp = context.sent_data[TYPE_EP_REP].pop() self.assertEqual(endpoint_created_rsp['rc'], "SUCCESS") acl_req = context.sent_data[TYPE_ACL_REQ].pop() self.assertFalse(context.sent_data_present()) self.assertEqual(acl_req['endpoint_id'], endpoint_id) acl_rsp = {'type': "GETACLSTATE", 'rc': "SUCCESS", 'message': ""} poll_result = context.add_poll_result(200) poll_result.add(TYPE_ACL_REQ, acl_rsp) # Check the rules are what we expect. set_expected_global_rules() add_endpoint_rules(suffix, tap, addr, None, mac) stub_fiptables.check_state(expected_iptables) add_endpoint_ipsets(suffix) stub_ipsets.check_state(expected_ipsets) # OK - now try giving it some ACLs, and see if they get applied correctly. acls = get_blank_acls() acls['v4']['outbound'].append({ 'cidr': "0.0.0.0/0", 'protocol': "icmp" }) acls['v4']['outbound'].append({ 'cidr': "1.2.3.0/24", 'protocol': "tcp" }) acls['v4']['outbound'].append({ 'cidr': "0.0.0.0/0", 'protocol': "tcp", 'port': "80" }) acls['v4']['inbound'].append({ 'cidr': "1.2.2.0/24", 'protocol': "icmp" }) acls['v4']['inbound'].append({ 'cidr': "0.0.0.0/0", 'protocol': "tcp", 'port': "8080" }) acls['v4']['inbound'].append({ 'cidr': "2.4.6.8/32", 'protocol': "udp", 'port': "8080" }) acls['v4']['inbound'].append({'cidr': "1.2.3.3/32"}) acls['v4']['inbound'].append({ 'cidr': "3.6.9.12/32", 'protocol': "tcp", 'port': ['10', '50'] }) acls['v4']['inbound'].append({ 'cidr': "5.4.3.2/32", 'protocol': "icmp", 'icmp_type': "3", 'icmp_code': "2" }) acls['v4']['inbound'].append({ 'cidr': "5.4.3.2/32", 'protocol': "icmp", 'icmp_type': "9" }) acls['v4']['inbound'].append({ 'cidr': "5.4.3.2/32", 'protocol': "icmp", 'icmp_type': "blah" }) # We include a couple of invalid rules that Felix will just ignore (and log). acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "tcp", 'port': ['blah', 'blah'] }) acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "tcp", 'port': ['1', '2', '3'] }) acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "tcp", 'port': 'flibble' }) acls['v4']['inbound'].append({'protocol': "tcp"}) acls['v4']['inbound'].append({'cidr': "4.3.2.1/32", 'port': "123"}) acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "icmp", 'icmp_code': "blah" }) acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "icmp", 'port': "1" }) acls['v4']['inbound'].append({ 'cidr': "4.3.2.1/32", 'protocol': "rsvp", 'port': "1" }) acl_req = {'type': "ACLUPDATE", 'acls': acls} poll_result.add(TYPE_ACL_SUB, acl_req, endpoint_id) agent.run() stub_fiptables.check_state(expected_iptables) expected_ipsets.add("felix-from-icmp-" + suffix, "0.0.0.0/1") expected_ipsets.add("felix-from-icmp-" + suffix, "128.0.0.0/1") expected_ipsets.add("felix-from-port-" + suffix, "1.2.3.0/24,tcp:0") expected_ipsets.add("felix-from-port-" + suffix, "0.0.0.0/1,tcp:80") expected_ipsets.add("felix-from-port-" + suffix, "128.0.0.0/1,tcp:80") expected_ipsets.add("felix-to-icmp-" + suffix, "1.2.2.0/24") expected_ipsets.add("felix-to-port-" + suffix, "0.0.0.0/1,tcp:8080") expected_ipsets.add("felix-to-port-" + suffix, "128.0.0.0/1,tcp:8080") expected_ipsets.add("felix-to-port-" + suffix, "2.4.6.8/32,udp:8080") expected_ipsets.add("felix-to-addr-" + suffix, "1.2.3.3/32") expected_ipsets.add("felix-to-port-" + suffix, "3.6.9.12/32,tcp:10-50") expected_ipsets.add("felix-to-port-" + suffix, "5.4.3.2/32,icmp:3/2") expected_ipsets.add("felix-to-port-" + suffix, "5.4.3.2/32,icmp:9/0") expected_ipsets.add("felix-to-port-" + suffix, "5.4.3.2/32,icmp:blah") stub_ipsets.check_state(expected_ipsets) # Add another endpoint, and check the state. endpoint_id2 = str(uuid.uuid4()) log.debug("Build second endpoint created : %s" % endpoint_id2) mac2 = stub_utils.get_mac() suffix2 = endpoint_id2[:11] tap2 = "tap" + suffix2 addr2 = '1.2.3.5' endpoint_created_req = { 'type': "ENDPOINTCREATED", 'endpoint_id': endpoint_id2, 'issued': futils.time_ms(), 'mac': mac2, 'state': Endpoint.STATE_ENABLED, 'addrs': [{ 'gateway': "1.2.3.1", 'addr': addr2 }] } poll_result = context.add_poll_result(250) poll_result.add(TYPE_EP_REP, endpoint_created_req) tap_obj2 = stub_devices.TapInterface(tap2) stub_devices.add_tap(tap_obj2) agent.run() # Check that we got what we expected - i.e. a success response, a GETACLSTATE, # and the rules in the right state. endpoint_created_rsp = context.sent_data[TYPE_EP_REP].pop() self.assertEqual(endpoint_created_rsp['rc'], "SUCCESS") acl_req = context.sent_data[TYPE_ACL_REQ].pop() self.assertEqual(acl_req['endpoint_id'], endpoint_id2) self.assertFalse(context.sent_data_present()) add_endpoint_rules(suffix2, tap2, addr2, None, mac2) stub_fiptables.check_state(expected_iptables) add_endpoint_ipsets(suffix2) stub_ipsets.check_state(expected_ipsets) # OK, finally wind down with an ENDPOINTDESTROYED message for that second endpoint. endpoint_destroyed_req = { 'type': "ENDPOINTDESTROYED", 'endpoint_id': endpoint_id2, 'issued': futils.time_ms() } poll_result = context.add_poll_result(300) poll_result.add(TYPE_EP_REP, endpoint_destroyed_req) stub_devices.del_tap(tap2) agent.run() # Rebuild and recheck the state. set_expected_global_rules() add_endpoint_rules(suffix, tap, addr, None, mac) stub_fiptables.check_state(expected_iptables)
def test_resync(self): """ Test the resync flows. """ common.default_logging() context = stub_zmq.Context() agent = felix.FelixAgent(config_path, context) #*********************************************************************# #* Set the resync timeout to 5 seconds, and the KEEPALIVE timeout to *# #* much more. *# #*********************************************************************# agent.config.RESYNC_INT_SEC = 5 agent.config.CONN_TIMEOUT_MS = 50000 agent.config.CONN_KEEPALIVE_MS = 50000 # Get started. context.add_poll_result(0) agent.run() # Now we should have got a resync request. resync_req = context.sent_data[TYPE_EP_REQ].pop() log.debug("Resync request : %s" % resync_req) self.assertFalse(context.sent_data_present()) resync_id = resync_req['resync_id'] resync_rsp = { 'type': "RESYNCSTATE", 'endpoint_count': "0", 'rc': "SUCCESS", 'message': "hello" } poll_result = context.add_poll_result(1000) poll_result.add(TYPE_EP_REQ, resync_rsp) agent.run() # nothing yet self.assertFalse(context.sent_data_present()) poll_result = context.add_poll_result(5999) agent.run() # nothing yet - 4999 ms since last request self.assertFalse(context.sent_data_present()) poll_result = context.add_poll_result(6001) agent.run() # We should have got another resync request. resync_req = context.sent_data[TYPE_EP_REQ].pop() log.debug("Resync request : %s" % resync_req) self.assertFalse(context.sent_data_present()) resync_id = resync_req['resync_id'] resync_rsp = { 'type': "RESYNCSTATE", 'endpoint_count': "2", 'rc': "SUCCESS", 'message': "hello" } # No more resyncs until enough data has arrived. poll_result = context.add_poll_result(15000) poll_result.add(TYPE_EP_REQ, resync_rsp) agent.run() self.assertFalse(context.sent_data_present()) # Send an endpoint created message to Felix. endpoint_id = str(uuid.uuid4()) log.debug("Build first endpoint created : %s" % endpoint_id) mac = stub_utils.get_mac() suffix = endpoint_id[:11] tap = "tap" + suffix addr = '1.2.3.4' endpoint_created_req = { 'type': "ENDPOINTCREATED", 'endpoint_id': endpoint_id, 'resync_id': resync_id, 'issued': futils.time_ms(), 'mac': mac, 'state': Endpoint.STATE_ENABLED, 'addrs': [{ 'gateway': "1.2.3.1", 'addr': addr }] } poll_result = context.add_poll_result(15001) poll_result.add(TYPE_EP_REP, endpoint_created_req) agent.run() # We stop using sent_data_present, since there are ACL requests around. endpoint_created_rsp = context.sent_data[TYPE_EP_REP].pop() self.assertEqual(endpoint_created_rsp['rc'], "SUCCESS") self.assertFalse(context.sent_data[TYPE_EP_REQ]) # Send a second endpoint created message to Felix - triggers another resync. endpoint_id = str(uuid.uuid4()) log.debug("Build second endpoint created : %s" % endpoint_id) mac = stub_utils.get_mac() suffix = endpoint_id[:11] tap = "tap" + suffix addr = '1.2.3.5' endpoint_created_req = { 'type': "ENDPOINTCREATED", 'endpoint_id': endpoint_id, 'resync_id': resync_id, 'issued': futils.time_ms(), 'mac': mac, 'state': Endpoint.STATE_ENABLED, 'addrs': [{ 'gateway': "1.2.3.1", 'addr': addr }] } poll_result = context.add_poll_result(15002) poll_result.add(TYPE_EP_REP, endpoint_created_req) agent.run() endpoint_created_rsp = context.sent_data[TYPE_EP_REP].pop() self.assertEqual(endpoint_created_rsp['rc'], "SUCCESS") self.assertFalse(context.sent_data[TYPE_EP_REQ]) # No more resyncs until enough 5000 ms after last rsp. poll_result = context.add_poll_result(20000) poll_result.add(TYPE_EP_REQ, resync_rsp) agent.run() self.assertFalse(context.sent_data[TYPE_EP_REQ]) # We should have got another resync request. poll_result = context.add_poll_result(20003) poll_result.add(TYPE_EP_REP, endpoint_created_req) agent.run() resync_req = context.sent_data[TYPE_EP_REQ].pop() log.debug("Resync request : %s" % resync_req) self.assertFalse(context.sent_data[TYPE_EP_REQ])
def test_on_endpoint_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) ips = ["2001::abcd"] gway = "2020:ab::9876" iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'ipv6_gateway': gway, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv6'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_up]: m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send through an update with no changes but a force update. Should # force a re-write to iptables. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, force_reprogram=True, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertTrue(m_conf.called) self.assertTrue(m_set_routes.called) # Send through an update with no changes - would reset ARP, but this is # IPv6 so it won't. data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: local_ep.on_endpoint_update(None, async=True) local_ep.on_unreferenced(async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None) self.assertRaises(AssertionError, local_ep._finish_msg_batch, [], []) self.m_manager.on_object_cleanup_complete.assert_called_once_with( local_ep._id, local_ep, async=True, )
def test_profile_id_update_triggers_iptables(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["10.0.0.1"] iface = "tapabcdef" mac = stub_utils.get_mac() data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': [], 'state': "active"} local_ep._pending_endpoint = data.copy() # First update with endpoint not yet set, should trigger full sync. with mock.patch("calico.felix.devices.interface_up", return_value=True): local_ep._apply_endpoint_update() self.assertEqual(local_ep.endpoint, data) self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._iptables_in_sync = True local_ep._device_in_sync = True # No-op update local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertTrue(local_ep._device_in_sync) # Set the state. local_ep._pending_endpoint = data.copy() local_ep._pending_endpoint["state"] = "inactive" local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True local_ep._iptables_in_sync = True # Set the state back again... local_ep._pending_endpoint = data.copy() local_ep._pending_endpoint["state"] = "active" local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True local_ep._iptables_in_sync = True # Profiles update. Should update iptables. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof2"], "state": "active"} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) local_ep._iptables_in_sync = True self.assertTrue(local_ep._device_in_sync) # IP update. Should update routing. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ["10.0.0.2"], 'profile_ids': ["prof2"], "state": "active"} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True # Delete, should update everything. local_ep._pending_endpoint = None local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync)
def test_on_interface_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["1234::5678"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'profile_ids': ["prof1"] } # We can only get on_interface_update calls after the first # on_endpoint_update, so trigger that. with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv6'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_up]: m_iface_up.return_value = False local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertFalse(m_conf.called) self.assertFalse(m_set_routes.called) self.assertFalse(local_ep._device_in_sync) # Now pretend to get an interface update - does all the same work. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) m_conf.assert_called_once_with(iface, None) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) self.assertTrue(local_ep._device_in_sync) # Now cover the error cases... with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: with mock.patch('calico.felix.devices.' 'interface_exists') as ifce_exists: with mock.patch('calico.felix.devices.' 'interface_up') as ifce_up: # Cycle through all the possibilities for the state. ifce_exists.side_effect = [True, False, True] ifce_up.side_effect = [True, False] m_conf.side_effect = FailedSystemCall("", [], 1, "", "") local_ep.on_interface_update(False, async=True) self.step_actor(local_ep) local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) self.assertFalse(local_ep._device_in_sync)
def test_on_endpoint_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) ips = ["2001::abcd"] gway = "2020:ab::9876" iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'ipv6_gateway': gway, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv6'), mock.patch('calico.felix.devices.interface_exists'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_exists, m_iface_up]: m_iface_exists.return_value = True m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send through an update with no changes but a force update. Should # force a re-write to iptables. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, force_reprogram=True, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertTrue(m_conf.called) self.assertTrue(m_set_routes.called) # Send through an update with no changes - would reset ARP, but this is # IPv6 so it won't. data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: local_ep.on_endpoint_update(None, async=True) local_ep.on_unreferenced(async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None) local_ep._finish_msg_batch([], []) # Should be ignored self.m_manager.on_object_cleanup_complete.assert_called_once_with( local_ep._id, local_ep, async=True, )
def test_on_endpoint_update_v4(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) ips = ["1.2.3.4"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv4'), mock.patch('calico.felix.devices.interface_exists'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_exists, m_iface_up]: m_iface_exists.return_value = True m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send through an update with no changes - should be a no-op. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertFalse(m_conf.called) self.assertFalse(m_set_routes.called) # Change the MAC address and try again, leading to reset of ARP data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None)
def test_on_interface_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["1234::5678"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': ips, 'profile_ids': ["prof1"] } # We can only get on_interface_update calls after the first # on_endpoint_update, so trigger that. with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv6'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_up]: m_iface_up.return_value = False local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, None) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) # Now pretend to get an interface update - does all the same work. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) m_conf.assert_called_once_with(iface, None) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=False) self.assertTrue(local_ep._device_in_sync) # Now cover the error cases... with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: with mock.patch('calico.felix.devices.' 'interface_exists') as ifce_exists: with mock.patch('calico.felix.devices.' 'interface_up') as ifce_up: # Cycle through all the possibilities for the state. ifce_exists.side_effect = [True, False, True] ifce_up.side_effect = [True, False] m_conf.side_effect = FailedSystemCall("", [], 1, "", "") local_ep.on_interface_update(False, async=True) self.step_actor(local_ep) local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) local_ep.on_interface_update(True, async=True) self.step_actor(local_ep) self.assertFalse(local_ep._device_in_sync)
def test_profile_id_update_triggers_iptables(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) ips = ["10.0.0.1"] iface = "tapabcdef" mac = stub_utils.get_mac() data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': [], 'state': "active"} local_ep._pending_endpoint = data.copy() # First update with endpoint not yet set, should trigger full sync. with mock.patch("calico.felix.devices.interface_up", return_value=True): local_ep._apply_endpoint_update() self.assertEqual(local_ep.endpoint, data) self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._iptables_in_sync = True local_ep._device_in_sync = True # No-op update local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertTrue(local_ep._device_in_sync) # Set the state. local_ep._pending_endpoint = data.copy() local_ep._pending_endpoint["state"] = "inactive" local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True local_ep._iptables_in_sync = True # Set the state back again... local_ep._pending_endpoint = data.copy() local_ep._pending_endpoint["state"] = "active" local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True local_ep._iptables_in_sync = True # Profiles update. Should update iptables. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof2"], "state": "active"} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) local_ep._iptables_in_sync = True self.assertTrue(local_ep._device_in_sync) # IP update. Should update routing. data = {'endpoint': "endpoint_id", 'mac': mac, 'name': iface, 'ipv4_nets': ["10.0.0.2"], 'profile_ids': ["prof2"], "state": "active"} local_ep._pending_endpoint = data.copy() local_ep._apply_endpoint_update() self.assertTrue(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync) local_ep._device_in_sync = True # Delete, should update everything. local_ep._pending_endpoint = None local_ep._apply_endpoint_update() self.assertFalse(local_ep._iptables_in_sync) self.assertFalse(local_ep._device_in_sync)
def test_on_endpoint_update_v4(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) ips = ["1.2.3.4"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with nested( mock.patch('calico.felix.devices.set_routes'), mock.patch('calico.felix.devices.configure_interface_ipv4'), mock.patch('calico.felix.devices.interface_up'), ) as [m_set_routes, m_conf, m_iface_up]: m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send through an update with no changes - should be a no-op. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertFalse(m_conf.called) self.assertFalse(m_set_routes.called) # Change the MAC address and try again, leading to reset of ARP data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(ips), iface, data['mac'], reset_arp=True) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None)
def test_on_endpoint_update_v4(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV4 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) ips = ["1.2.3.4/32"] iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv4_nets': ips, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack,\ mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv4') as m_conf,\ mock.patch('calico.felix.devices.interface_exists') as m_iface_exists,\ mock.patch('calico.felix.devices.interface_up') as m_iface_up: m_iface_exists.return_value = True m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(["1.2.3.4"]), iface, data['mac'], reset_arp=True) self.assertFalse(m_rem_conntrack.called) # Send through an update with no changes - should be a no-op. with mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack,\ mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertFalse(m_conf.called) self.assertFalse(m_set_routes.called) self.assertFalse(m_rem_conntrack.called) # Change the MAC address and try again, leading to reset of ARP data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv4') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface) m_set_routes.assert_called_once_with(ip_type, set(["1.2.3.4"]), iface, data['mac'], reset_arp=True) # Change the IP address, causing an iptables and route refresh. data = data.copy() data["ipv4_nets"] = ["1.2.3.5"] with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv4') as _m_conf,\ mock.patch('calico.felix.endpoint.LocalEndpoint._update_chains') as _m_up_c,\ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(["1.2.3.5"]), iface, data['mac'], reset_arp=True) self.assertFalse(local_ep._update_chains.called) m_rem_conntrack.assert_called_once_with(set(["1.2.3.4"]), 4) # Change the nat mappings, causing an iptables and route refresh. data = data.copy() data['ipv4_nat'] = [ { 'int_ip': '1.2.3.4', 'ext_ip': '5.6.7.8' } ] with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv4') as _m_conf,\ mock.patch('calico.felix.endpoint.LocalEndpoint._update_chains') as _m_up_c,\ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(["1.2.3.5", "5.6.7.8"]), iface, data['mac'], reset_arp=True) local_ep._update_chains.assert_called_once_with() self.assertFalse(m_rem_conntrack.called) # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None) # Should clean up conntrack entries for all IPs. m_rem_conntrack.assert_called_once_with( set(['1.2.3.5', '5.6.7.8']), 4 )
def test_on_endpoint_update_v6(self): combined_id = EndpointId("host_id", "orchestrator_id", "workload_id", "endpoint_id") ip_type = futils.IPV6 local_ep = self.get_local_endpoint(combined_id, ip_type) # Call with no data; should be ignored (no configuration to remove). local_ep.on_endpoint_update(None, async=True) self.step_actor(local_ep) nets = ["2001::abcd/128"] gway = "2020:ab::9876" iface = "tapabcdef" data = { 'state': "active", 'endpoint': "endpoint_id", 'mac': stub_utils.get_mac(), 'name': iface, 'ipv6_nets': nets, 'ipv6_gateway': gway, 'profile_ids': ["prof1"] } # Report an initial update (endpoint creation) and check configured with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv6') as m_conf,\ mock.patch('calico.felix.devices.interface_exists') as m_iface_exists,\ mock.patch('calico.felix.devices.interface_up') as m_iface_up, \ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: m_iface_exists.return_value = True m_iface_up.return_value = True local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(["2001::abcd"]), iface, data['mac'], reset_arp=False) self.assertFalse(m_rem_conntrack.called) # Send through an update with no changes but a force update. Should # force a re-write to iptables. with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, force_reprogram=True, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) self.assertTrue(m_conf.called) self.assertTrue(m_set_routes.called) # Send through an update with no changes - would reset ARP, but this is # IPv6 so it won't. data = data.copy() data['mac'] = stub_utils.get_mac() with mock.patch('calico.felix.devices.set_routes') as m_set_routes: with mock.patch('calico.felix.devices.' 'configure_interface_ipv6') as m_conf: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) self.assertEqual(local_ep._mac, data['mac']) m_conf.assert_called_once_with(iface, gway) m_set_routes.assert_called_once_with(ip_type, set(["2001::abcd"]), iface, data['mac'], reset_arp=False) # Change the nat mappings, causing an iptables and route refresh. data = data.copy() nets.append('2001::abce/128') data['ipv6_nat'] = [ { 'int_ip': '2001::abcd', 'ext_ip': '2001::abce' } ] with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.configure_interface_ipv6') as m_conf,\ mock.patch('calico.felix.endpoint.LocalEndpoint._update_chains') as _m_up_c: local_ep.on_endpoint_update(data, async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with( ip_type, set(["2001::abcd", "2001::abce"]), iface, data['mac'], reset_arp=False ) local_ep._update_chains.assert_called_once_with() # Send empty data, which deletes the endpoint. with mock.patch('calico.felix.devices.set_routes') as m_set_routes,\ mock.patch('calico.felix.devices.remove_conntrack_flows') as m_rem_conntrack: local_ep.on_endpoint_update(None, async=True) local_ep.on_unreferenced(async=True) self.step_actor(local_ep) m_set_routes.assert_called_once_with(ip_type, set(), data["name"], None) local_ep._finish_msg_batch([], []) # Should be ignored self.m_manager.on_object_cleanup_complete.assert_called_once_with( local_ep._id, local_ep, async=True, ) m_rem_conntrack.assert_called_once_with(set(['2001::abcd', '2001::abce']), 6)