def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") etcd_watcher = EtcdWatcher(config) etcd_watcher.start() # Ask the EtcdWatcher to fill in the global config object before we # proceed. We don't yet support config updates. etcd_watcher.load_config(async=False) _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") v4_filter_updater = IptablesUpdater("filter", ip_version=4) v4_nat_updater = IptablesUpdater("nat", ip_version=4) v4_ipset_mgr = IpsetManager(IPV4) v4_rules_manager = RulesManager(4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager) v6_filter_updater = IptablesUpdater("filter", ip_version=6) v6_ipset_mgr = IpsetManager(IPV6) v6_rules_manager = RulesManager(6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager) update_splitter = UpdateSplitter( config, [v4_ipset_mgr, v6_ipset_mgr], [v4_rules_manager, v6_rules_manager], [v4_ep_manager, v6_ep_manager], [v4_filter_updater, v6_filter_updater]) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") update_splitter.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() iface_watcher.start() monitored_items = [ update_splitter.greenlet, v4_nat_updater.greenlet, v4_filter_updater.greenlet, v4_nat_updater.greenlet, v4_ipset_mgr.greenlet, v4_rules_manager.greenlet, v4_dispatch_chains.greenlet, v4_ep_manager.greenlet, v6_filter_updater.greenlet, v6_ipset_mgr.greenlet, v6_rules_manager.greenlet, v6_dispatch_chains.greenlet, v6_ep_manager.greenlet, iface_watcher.greenlet, etcd_watcher.greenlet ] # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v6_filter_updater, v4_nat_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) f = etcd_watcher.watch_etcd(update_splitter, async=True) monitored_items.append(f) # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") hosts_ipset_v4 = IpsetActor(HOSTS_IPSET_V4) etcd_api = EtcdAPI(config, hosts_ipset_v4) etcd_api.start() # Ask the EtcdAPI to fill in the global config object before we # proceed. We don't yet support config updates. config_loaded = etcd_api.load_config(async=False) config_loaded.wait() _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") v4_filter_updater = IptablesUpdater("filter", ip_version=4) v4_nat_updater = IptablesUpdater("nat", ip_version=4) v4_ipset_mgr = IpsetManager(IPV4) v4_masq_manager = MasqueradeManager(IPV4, v4_nat_updater) v4_rules_manager = RulesManager(4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager) v6_filter_updater = IptablesUpdater("filter", ip_version=6) v6_ipset_mgr = IpsetManager(IPV6) v6_rules_manager = RulesManager(6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager) update_splitter = UpdateSplitter(config, [v4_ipset_mgr, v6_ipset_mgr], [v4_rules_manager, v6_rules_manager], [v4_ep_manager, v6_ep_manager], [v4_filter_updater, v6_filter_updater], v4_masq_manager) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") hosts_ipset_v4.start() update_splitter.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_masq_manager.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() iface_watcher.start() top_level_actors = [ hosts_ipset_v4, update_splitter, v4_nat_updater, v4_filter_updater, v4_nat_updater, v4_ipset_mgr, v4_masq_manager, v4_rules_manager, v4_dispatch_chains, v4_ep_manager, v6_filter_updater, v6_ipset_mgr, v6_rules_manager, v6_dispatch_chains, v6_ep_manager, iface_watcher, etcd_api, ] monitored_items = [actor.greenlet for actor in top_level_actors] # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v6_filter_updater, v4_nat_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) etcd_api.start_watch(update_splitter, async=True) # Register a SIG_USR handler to trigger a diags dump. def dump_top_level_actors(log): for a in top_level_actors: # The output will include queue length and the like. log.info("%s", a) futils.register_diags("Top-level actors", dump_top_level_actors) try: gevent.signal(signal.SIGUSR1, functools.partial(futils.dump_diags)) except AttributeError: # It doesn't matter too much if we fail to do this. _log.warning("Unable to install diag dump handler") pass # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def test_install_global_ipip_disabled(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_set_ips = m_devices.set_interface_ips env_dict = { "FELIX_ETCDADDR": "localhost:4001", "FELIX_HOSTNAME": "myhost", "FELIX_INTERFACEPREFIX": "tap", "FELIX_METADATAADDR": "123.0.0.1", "FELIX_METADATAPORT": "1234", "FELIX_IPINIPENABLED": "false", "FELIX_IPINIPMTU": "1480", "FELIX_DEFAULTENDPOINTTOHOSTACTION": "RETURN" } config = load_config("felix_missing.cfg", env_dict=env_dict) m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v6_raw_upd = Mock(spec=IptablesUpdater) m_v6_nat_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(config, m_v4_upd, m_v6_upd, m_v4_nat_upd, m_v6_nat_upd, m_v6_raw_upd) self.assertEqual( m_v4_nat_upd.ensure_rule_inserted.mock_calls, [call("PREROUTING --jump felix-PREROUTING", async=False), call("POSTROUTING --jump felix-POSTROUTING", async=False)] ) self.assertEqual( m_v4_nat_upd.ensure_rule_removed.mock_calls, [call("POSTROUTING --out-interface tunl0 " "-m addrtype ! --src-type LOCAL --limit-iface-out " "-m addrtype --src-type LOCAL " "-j MASQUERADE", async=False)] ) m_v6_raw_upd.ensure_rule_inserted.assert_called_once_with( 'PREROUTING --in-interface tap+ --match rpfilter --invert --jump ' 'felix-PREROUTING', async=False, ) m_v6_raw_upd.rewrite_chains.assert_called_once_with( {'felix-PREROUTING': [ '--append felix-PREROUTING --jump DROP -m comment ' '--comment "IPv6 rpfilter failed"' ]}, { 'felix-PREROUTING': {} }, async=False ) self.assertFalse(m_ipset.ensure_exists.called) self.assertFalse(m_check_call.called) self.assertFalse(m_set_ips.called) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT ! --in-interface tap+ --jump RETURN', '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-INPUT': set(['felix-FROM-ENDPOINT']), 'felix-FORWARD': set([ 'felix-FROM-ENDPOINT', 'felix-TO-ENDPOINT' ]) }, async=False ) self.assertEqual( m_v4_upd.ensure_rule_inserted.mock_calls, [ call("INPUT --jump felix-INPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False), ] )
def test_install_global_rules(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_set_ips = m_devices.set_interface_ips env_dict = { "FELIX_ETCDADDR": "localhost:4001", "FELIX_HOSTNAME": "myhost", "FELIX_INTERFACEPREFIX": "tap", "FELIX_METADATAADDR": "123.0.0.1", "FELIX_METADATAPORT": "1234", "FELIX_IPINIPENABLED": "True", "FELIX_IPINIPMTU": "1480", "FELIX_DEFAULTENDPOINTTOHOSTACTION": "RETURN" } config = load_config("felix_missing.cfg", env_dict=env_dict) config.IP_IN_IP_ADDR = IPAddress("10.0.0.1") m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v6_raw_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) m_v6_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(config, m_v4_upd, m_v4_nat_upd, ip_version=4) frules.install_global_rules(config, m_v6_upd, m_v6_nat_upd, ip_version=6, raw_updater=m_v6_raw_upd) self.assertEqual( m_v4_nat_upd.ensure_rule_inserted.mock_calls, [ call("POSTROUTING --out-interface tunl0 " "-m addrtype ! --src-type LOCAL --limit-iface-out " "-m addrtype --src-type LOCAL " "-j MASQUERADE", async=False), call("PREROUTING --jump felix-PREROUTING", async=False), call("POSTROUTING --jump felix-POSTROUTING", async=False) ] ) m_v4_upd.ensure_rule_inserted.assert_has_calls([ call("INPUT --jump felix-INPUT", async=False), call("OUTPUT --jump felix-OUTPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False) ] ) expected_chains = { 'felix-FIP-DNAT': [], 'felix-FIP-SNAT': [], 'felix-PREROUTING': [ '--append felix-PREROUTING --jump felix-FIP-DNAT', '--append felix-PREROUTING --protocol tcp --dport 80 --destination ' '169.254.169.254/32 --jump DNAT --to-destination 123.0.0.1:1234' ], 'felix-POSTROUTING': [ '--append felix-POSTROUTING --jump felix-FIP-SNAT' ] } m_v4_nat_upd.rewrite_chains.assert_called_once_with( expected_chains, {'felix-PREROUTING': set(['felix-FIP-DNAT']), 'felix-POSTROUTING': set(['felix-FIP-SNAT'])}, async=False ) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --goto felix-FROM-HOST-IF ! --in-interface tap+', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-OUTPUT': [ '--append felix-OUTPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-OUTPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-OUTPUT --goto felix-TO-HOST-IF ! --out-interface tap+', ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ], 'felix-FAILSAFE-IN': [ '--append felix-FAILSAFE-IN --protocol tcp --dport 22 --jump ACCEPT' ], 'felix-FAILSAFE-OUT': [ '--append felix-FAILSAFE-OUT --protocol tcp --dport 2379 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 2380 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 4001 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 7001 --jump ACCEPT' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, EXPECTED_TOP_LEVEL_DEPS, async=False ) self.assertEqual( m_v6_nat_upd.ensure_rule_inserted.mock_calls, [ call("PREROUTING --jump felix-PREROUTING", async=False), call("POSTROUTING --jump felix-POSTROUTING", async=False), ] ) m_v6_upd.ensure_rule_inserted.assert_has_calls([ call("INPUT --jump felix-INPUT", async=False), call("OUTPUT --jump felix-OUTPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False) ] ) expected_chains = { 'felix-FIP-DNAT': [], 'felix-FIP-SNAT': [], 'felix-PREROUTING': [ '--append felix-PREROUTING --jump felix-FIP-DNAT' ], 'felix-POSTROUTING': [ '--append felix-POSTROUTING --jump felix-FIP-SNAT' ] } m_v6_nat_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-PREROUTING': set(['felix-FIP-DNAT']), 'felix-POSTROUTING': set(['felix-FIP-SNAT']) }, async=False ) m_v6_raw_upd.rewrite_chains.assert_called_once_with( {'felix-PREROUTING': [ '--append felix-PREROUTING --jump DROP -m comment ' '--comment "IPv6 rpfilter failed"' ]}, { 'felix-PREROUTING': {} }, async=False ) m_ipset.ensure_exists.assert_called_once_with() self.assertEqual( m_check_call.mock_calls, [ call(["ip", "tunnel", "add", "tunl0", "mode", "ipip"]), call(["ip", "link", "set", "tunl0", "mtu", "1480"]), call(["ip", "link", "set", "tunl0", "up"]), ] ) self.assertEqual( m_set_ips.mock_calls, [call(IPV4, "tunl0", set([IPAddress("10.0.0.1")]))] )
def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") hosts_ipset_v4 = IpsetActor(HOSTS_IPSET_V4) etcd_api = EtcdAPI(config, hosts_ipset_v4) etcd_api.start() # Ask the EtcdAPI to fill in the global config object before we # proceed. We don't yet support config updates. config_loaded = etcd_api.load_config(async=False) config_loaded.wait() # Ensure the Kernel's global options are correctly configured for # Calico. devices.configure_global_kernel_config() _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") v4_filter_updater = IptablesUpdater("filter", ip_version=4, config=config) v4_nat_updater = IptablesUpdater("nat", ip_version=4, config=config) v4_ipset_mgr = IpsetManager(IPV4) v4_masq_manager = MasqueradeManager(IPV4, v4_nat_updater) v4_rules_manager = RulesManager(4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager, etcd_api.status_reporter) v6_raw_updater = IptablesUpdater("raw", ip_version=6, config=config) v6_filter_updater = IptablesUpdater("filter", ip_version=6, config=config) v6_ipset_mgr = IpsetManager(IPV6) v6_rules_manager = RulesManager(6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager, etcd_api.status_reporter) update_splitter = UpdateSplitter(config, [v4_ipset_mgr, v6_ipset_mgr], [v4_rules_manager, v6_rules_manager], [v4_ep_manager, v6_ep_manager], [v4_filter_updater, v6_filter_updater, v6_raw_updater, v4_nat_updater], v4_masq_manager) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") hosts_ipset_v4.start() update_splitter.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_masq_manager.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v6_raw_updater.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() iface_watcher.start() top_level_actors = [ hosts_ipset_v4, update_splitter, v4_nat_updater, v4_filter_updater, v4_nat_updater, v4_ipset_mgr, v4_masq_manager, v4_rules_manager, v4_dispatch_chains, v4_ep_manager, v6_raw_updater, v6_filter_updater, v6_ipset_mgr, v6_rules_manager, v6_dispatch_chains, v6_ep_manager, iface_watcher, etcd_api, ] monitored_items = [actor.greenlet for actor in top_level_actors] # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v6_filter_updater, v4_nat_updater, v6_raw_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) etcd_api.start_watch(update_splitter, async=True) # Register a SIG_USR handler to trigger a diags dump. def dump_top_level_actors(log): for a in top_level_actors: # The output will include queue length and the like. log.info("%s", a) futils.register_diags("Top-level actors", dump_top_level_actors) futils.register_process_statistics() try: gevent.signal(signal.SIGUSR1, functools.partial(futils.dump_diags)) except AttributeError: # It doesn't matter too much if we fail to do this. _log.warning("Unable to install diag dump handler") pass # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") etcd_watcher = EtcdWatcher(config) etcd_watcher.start() # Ask the EtcdWatcher to fill in the global config object before we # proceed. We don't yet support config updates. etcd_watcher.load_config(async=False) _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") v4_filter_updater = IptablesUpdater("filter", ip_version=4) v4_nat_updater = IptablesUpdater("nat", ip_version=4) v4_ipset_mgr = IpsetManager(IPV4) v4_rules_manager = RulesManager(4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager) v6_filter_updater = IptablesUpdater("filter", ip_version=6) v6_ipset_mgr = IpsetManager(IPV6) v6_rules_manager = RulesManager(6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager) update_splitter = UpdateSplitter(config, [v4_ipset_mgr, v6_ipset_mgr], [v4_rules_manager, v6_rules_manager], [v4_ep_manager, v6_ep_manager], [v4_filter_updater, v6_filter_updater]) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") update_splitter.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() iface_watcher.start() monitored_items = [ update_splitter.greenlet, v4_nat_updater.greenlet, v4_filter_updater.greenlet, v4_nat_updater.greenlet, v4_ipset_mgr.greenlet, v4_rules_manager.greenlet, v4_dispatch_chains.greenlet, v4_ep_manager.greenlet, v6_filter_updater.greenlet, v6_ipset_mgr.greenlet, v6_rules_manager.greenlet, v6_dispatch_chains.greenlet, v6_ep_manager.greenlet, iface_watcher.greenlet, etcd_watcher.greenlet ] # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v6_filter_updater, v4_nat_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) f = etcd_watcher.watch_etcd(update_splitter, async=True) monitored_items.append(f) # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") hosts_ipset_v4 = IpsetActor(HOSTS_IPSET_V4) etcd_api = EtcdAPI(config, hosts_ipset_v4) etcd_api.start() # Ask the EtcdAPI to fill in the global config object before we # proceed. We don't yet support config updates. config_loaded = etcd_api.load_config(async=False) config_loaded.wait() # Ensure the Kernel's global options are correctly configured for # Calico. devices.configure_global_kernel_config() _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") monitored_items = [] if config.PROM_METRICS_ENABLED: httpd = HTTPServer(("0.0.0.0", config.PROM_METRICS_PORT), MetricsHandler) stats_server = gevent.Greenlet(httpd.serve_forever) stats_server.start() monitored_items.append(stats_server) v4_filter_updater = IptablesUpdater("filter", ip_version=4, config=config) v4_nat_updater = IptablesUpdater("nat", ip_version=4, config=config) v4_ipset_mgr = IpsetManager(IPV4, config) v4_masq_manager = MasqueradeManager(IPV4, v4_nat_updater) v4_rules_manager = RulesManager(config, 4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_fip_manager = FloatingIPManager(config, 4, v4_nat_updater) v4_ep_manager = EndpointManager( config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager, v4_fip_manager, etcd_api.status_reporter, ) cleanup_updaters = [v4_filter_updater, v4_nat_updater] cleanup_ip_mgrs = [v4_ipset_mgr] update_splitter_args = [v4_ipset_mgr, v4_rules_manager, v4_ep_manager, v4_masq_manager, v4_nat_updater] v6_enabled = os.path.exists("/proc/sys/net/ipv6") if v6_enabled: v6_raw_updater = IptablesUpdater("raw", ip_version=6, config=config) v6_filter_updater = IptablesUpdater("filter", ip_version=6, config=config) v6_nat_updater = IptablesUpdater("nat", ip_version=6, config=config) v6_ipset_mgr = IpsetManager(IPV6, config) v6_rules_manager = RulesManager(config, 6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_fip_manager = FloatingIPManager(config, 6, v6_nat_updater) v6_ep_manager = EndpointManager( config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager, v6_fip_manager, etcd_api.status_reporter, ) cleanup_updaters.append(v6_filter_updater) cleanup_ip_mgrs.append(v6_ipset_mgr) update_splitter_args += [v6_ipset_mgr, v6_rules_manager, v6_ep_manager, v6_raw_updater, v6_nat_updater] cleanup_mgr = CleanupManager(config, cleanup_updaters, cleanup_ip_mgrs) update_splitter_args.append(cleanup_mgr) update_splitter = UpdateSplitter(update_splitter_args) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") hosts_ipset_v4.start() cleanup_mgr.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_masq_manager.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v4_fip_manager.start() if v6_enabled: v6_raw_updater.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_nat_updater.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() v6_fip_manager.start() iface_watcher.start() top_level_actors = [ hosts_ipset_v4, cleanup_mgr, v4_filter_updater, v4_nat_updater, v4_ipset_mgr, v4_masq_manager, v4_rules_manager, v4_dispatch_chains, v4_ep_manager, v4_fip_manager, iface_watcher, etcd_api, ] if v6_enabled: top_level_actors += [ v6_raw_updater, v6_filter_updater, v6_nat_updater, v6_ipset_mgr, v6_rules_manager, v6_dispatch_chains, v6_ep_manager, v6_fip_manager, ] monitored_items += [actor.greenlet for actor in top_level_actors] # Try to ensure that the nf_conntrack_netlink kernel module is present. # This works around an issue[1] where the first call to the "conntrack" # command fails while waiting for the module to load. # [1] https://github.com/projectcalico/calico/issues/986 load_nf_conntrack() # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v4_nat_updater, ip_version=4) if v6_enabled: install_global_rules(config, v6_filter_updater, v6_nat_updater, ip_version=6, raw_updater=v6_raw_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) etcd_api.start_watch(update_splitter, async=True) # Register a SIG_USR handler to trigger a diags dump. def dump_top_level_actors(log): for a in top_level_actors: # The output will include queue length and the like. log.info("%s", a) futils.register_diags("Top-level actors", dump_top_level_actors) futils.register_process_statistics() try: gevent.signal(signal.SIGUSR1, functools.partial(futils.dump_diags)) except AttributeError: # It doesn't matter too much if we fail to do this. _log.warning("Unable to install diag dump handler") pass # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def _main_greenlet(config): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") hosts_ipset_v4 = IpsetActor(HOSTS_IPSET_V4) etcd_api = EtcdAPI(config, hosts_ipset_v4) etcd_api.start() # Ask the EtcdAPI to fill in the global config object before we # proceed. We don't yet support config updates. config_loaded = etcd_api.load_config(async=False) config_loaded.wait() # Ensure the Kernel's global options are correctly configured for # Calico. devices.configure_global_kernel_config() _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") monitored_items = [] if config.PROM_METRICS_ENABLED: httpd = HTTPServer(("0.0.0.0", config.PROM_METRICS_PORT), MetricsHandler) stats_server = gevent.Greenlet(httpd.serve_forever) stats_server.start() monitored_items.append(stats_server) v4_filter_updater = IptablesUpdater("filter", ip_version=4, config=config) v4_nat_updater = IptablesUpdater("nat", ip_version=4, config=config) v4_ipset_mgr = IpsetManager(IPV4, config) v4_masq_manager = MasqueradeManager(IPV4, v4_nat_updater) v4_rules_manager = RulesManager(config, 4, v4_filter_updater, v4_ipset_mgr) v4_dispatch_chains = DispatchChains(config, 4, v4_filter_updater) v4_fip_manager = FloatingIPManager(config, 4, v4_nat_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_dispatch_chains, v4_rules_manager, v4_fip_manager, etcd_api.status_reporter) cleanup_updaters = [v4_filter_updater, v4_nat_updater] cleanup_ip_mgrs = [v4_ipset_mgr] update_splitter_args = [v4_ipset_mgr, v4_rules_manager, v4_ep_manager, v4_masq_manager, v4_nat_updater] v6_enabled = os.path.exists("/proc/sys/net/ipv6") if v6_enabled: v6_raw_updater = IptablesUpdater("raw", ip_version=6, config=config) v6_filter_updater = IptablesUpdater("filter", ip_version=6, config=config) v6_nat_updater = IptablesUpdater("nat", ip_version=6, config=config) v6_ipset_mgr = IpsetManager(IPV6, config) v6_rules_manager = RulesManager(config, 6, v6_filter_updater, v6_ipset_mgr) v6_dispatch_chains = DispatchChains(config, 6, v6_filter_updater) v6_fip_manager = FloatingIPManager(config, 6, v6_nat_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_dispatch_chains, v6_rules_manager, v6_fip_manager, etcd_api.status_reporter) cleanup_updaters.append(v6_filter_updater) cleanup_ip_mgrs.append(v6_ipset_mgr) update_splitter_args += [v6_ipset_mgr, v6_rules_manager, v6_ep_manager, v6_raw_updater, v6_nat_updater] cleanup_mgr = CleanupManager(config, cleanup_updaters, cleanup_ip_mgrs) update_splitter_args.append(cleanup_mgr) update_splitter = UpdateSplitter(update_splitter_args) iface_watcher = InterfaceWatcher(update_splitter) _log.info("Starting actors.") hosts_ipset_v4.start() cleanup_mgr.start() v4_filter_updater.start() v4_nat_updater.start() v4_ipset_mgr.start() v4_masq_manager.start() v4_rules_manager.start() v4_dispatch_chains.start() v4_ep_manager.start() v4_fip_manager.start() if v6_enabled: v6_raw_updater.start() v6_filter_updater.start() v6_ipset_mgr.start() v6_nat_updater.start() v6_rules_manager.start() v6_dispatch_chains.start() v6_ep_manager.start() v6_fip_manager.start() iface_watcher.start() top_level_actors = [ hosts_ipset_v4, cleanup_mgr, v4_filter_updater, v4_nat_updater, v4_ipset_mgr, v4_masq_manager, v4_rules_manager, v4_dispatch_chains, v4_ep_manager, v4_fip_manager, iface_watcher, etcd_api, ] if v6_enabled: top_level_actors += [ v6_raw_updater, v6_filter_updater, v6_nat_updater, v6_ipset_mgr, v6_rules_manager, v6_dispatch_chains, v6_ep_manager, v6_fip_manager, ] monitored_items += [actor.greenlet for actor in top_level_actors] # Try to ensure that the nf_conntrack_netlink kernel module is present. # This works around an issue[1] where the first call to the "conntrack" # command fails while waiting for the module to load. # [1] https://github.com/projectcalico/calico/issues/986 load_nf_conntrack() # Install the global rules before we start polling for updates. _log.info("Installing global rules.") install_global_rules(config, v4_filter_updater, v4_nat_updater, ip_version=4) if v6_enabled: install_global_rules(config, v6_filter_updater, v6_nat_updater, ip_version=6, raw_updater=v6_raw_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) etcd_api.start_watch(update_splitter, async=True) # Register a SIG_USR handler to trigger a diags dump. def dump_top_level_actors(log): for a in top_level_actors: # The output will include queue length and the like. log.info("%s", a) futils.register_diags("Top-level actors", dump_top_level_actors) futils.register_process_statistics() try: gevent.signal(signal.SIGUSR1, functools.partial(futils.dump_diags)) except AttributeError: # It doesn't matter too much if we fail to do this. _log.warning("Unable to install diag dump handler") pass # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise
def test_install_global_rules(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_set_ips = m_devices.set_interface_ips env_dict = { "FELIX_ETCDADDR": "localhost:4001", "FELIX_HOSTNAME": "myhost", "FELIX_INTERFACEPREFIX": "tap", "FELIX_METADATAADDR": "123.0.0.1", "FELIX_METADATAPORT": "1234", "FELIX_IPINIPENABLED": "True", "FELIX_IPINIPMTU": "1480", "FELIX_DEFAULTENDPOINTTOHOSTACTION": "RETURN" } config = load_config("felix_missing.cfg", env_dict=env_dict) config.IP_IN_IP_ADDR = IPAddress("10.0.0.1") m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v6_raw_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) m_v6_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(config, m_v4_upd, m_v4_nat_upd, ip_version=4) frules.install_global_rules(config, m_v6_upd, m_v6_nat_upd, ip_version=6, raw_updater=m_v6_raw_upd) self.assertEqual(m_v4_nat_upd.ensure_rule_inserted.mock_calls, [ call( "POSTROUTING --out-interface tunl0 " "-m addrtype ! --src-type LOCAL --limit-iface-out " "-m addrtype --src-type LOCAL " "-j MASQUERADE", async=False), call("PREROUTING --jump felix-PREROUTING", async=False), call("POSTROUTING --jump felix-POSTROUTING", async=False) ]) m_v4_upd.ensure_rule_inserted.assert_has_calls([ call("INPUT --jump felix-INPUT", async=False), call("OUTPUT --jump felix-OUTPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False) ]) expected_chains = { 'felix-FIP-DNAT': [], 'felix-FIP-SNAT': [], 'felix-PREROUTING': [ '--append felix-PREROUTING --jump felix-FIP-DNAT', '--append felix-PREROUTING --protocol tcp --dport 80 --destination ' '169.254.169.254/32 --jump DNAT --to-destination 123.0.0.1:1234' ], 'felix-POSTROUTING': ['--append felix-POSTROUTING --jump felix-FIP-SNAT'] } m_v4_nat_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-PREROUTING': set(['felix-FIP-DNAT']), 'felix-POSTROUTING': set(['felix-FIP-SNAT']) }, async=False) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --jump MARK --set-mark 0/0x4000000', '--append felix-INPUT --in-interface tap+ --jump MARK --set-mark 0x4000000/0x4000000', '--append felix-INPUT --goto felix-FROM-HOST-IF --match mark --mark 0/0x4000000', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-OUTPUT': [ '--append felix-OUTPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-OUTPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-OUTPUT --jump MARK --set-mark 0/0x4000000', '--append felix-OUTPUT --out-interface tap+ --jump MARK --set-mark 0x4000000/0x4000000', '--append felix-OUTPUT --goto felix-TO-HOST-IF --match mark --mark 0/0x4000000', ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ], 'felix-FAILSAFE-IN': [ '--append felix-FAILSAFE-IN --protocol tcp --dport 22 --jump ACCEPT' ], 'felix-FAILSAFE-OUT': [ '--append felix-FAILSAFE-OUT --protocol tcp --dport 2379 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 2380 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 4001 --jump ACCEPT', '--append felix-FAILSAFE-OUT --protocol tcp --dport 7001 --jump ACCEPT' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, EXPECTED_TOP_LEVEL_DEPS, async=False) self.assertEqual(m_v6_nat_upd.ensure_rule_inserted.mock_calls, [ call("PREROUTING --jump felix-PREROUTING", async=False), call("POSTROUTING --jump felix-POSTROUTING", async=False), ]) m_v6_upd.ensure_rule_inserted.assert_has_calls([ call("INPUT --jump felix-INPUT", async=False), call("OUTPUT --jump felix-OUTPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False) ]) expected_chains = { 'felix-FIP-DNAT': [], 'felix-FIP-SNAT': [], 'felix-PREROUTING': ['--append felix-PREROUTING --jump felix-FIP-DNAT'], 'felix-POSTROUTING': ['--append felix-POSTROUTING --jump felix-FIP-SNAT'] } m_v6_nat_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-PREROUTING': set(['felix-FIP-DNAT']), 'felix-POSTROUTING': set(['felix-FIP-SNAT']) }, async=False) m_v6_raw_upd.rewrite_chains.assert_called_once_with( { 'felix-PREROUTING': [ '--append felix-PREROUTING --jump DROP -m comment ' '--comment "IPv6 rpfilter failed"' ] }, {'felix-PREROUTING': {}}, async=False) m_ipset.ensure_exists.assert_called_once_with() self.assertEqual(m_check_call.mock_calls, [ call(["ip", "tunnel", "add", "tunl0", "mode", "ipip"]), call(["ip", "link", "set", "tunl0", "mtu", "1480"]), call(["ip", "link", "set", "tunl0", "up"]), ]) self.assertEqual(m_set_ips.mock_calls, [call(IPV4, "tunl0", set([IPAddress("10.0.0.1")]))])
def test_install_global_rules(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_config = Mock(spec=Config) m_config.IP_IN_IP_ENABLED = True m_config.IP_IN_IP_MTU = 1480 m_config.METADATA_IP = "123.0.0.1" m_config.METADATA_PORT = 1234 m_config.DEFAULT_INPUT_CHAIN_ACTION = "RETURN" m_config.IFACE_PREFIX = "tap" m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v6_raw_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(m_config, m_v4_upd, m_v6_upd, m_v4_nat_upd, m_v6_raw_upd) m_v6_raw_upd.ensure_rule_inserted.assert_called_once_with( 'PREROUTING --in-interface tap+ --match rpfilter --invert -j DROP', async=False, ) m_ipset.ensure_exists.assert_called_once_with() self.assertEqual( m_check_call.mock_calls, [ call(["ip", "tunnel", "add", "tunl0", "mode", "ipip"]), call(["ip", "link", "set", "tunl0", "mtu", "1480"]), call(["ip", "link", "set", "tunl0", "up"]), ] ) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT ! --in-interface tap+ --jump RETURN', '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-INPUT': set(['felix-FROM-ENDPOINT']), 'felix-FORWARD': set([ 'felix-FROM-ENDPOINT', 'felix-TO-ENDPOINT' ]) }, async=False ) self.assertEqual( m_v4_upd.ensure_rule_inserted.mock_calls, [ call("INPUT --jump felix-INPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False), ] )
def test_install_global_rules(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_config = Mock(spec=Config) m_config.IP_IN_IP_ENABLED = True m_config.METADATA_IP = "123.0.0.1" m_config.METADATA_PORT = 1234 m_config.DEFAULT_INPUT_CHAIN_ACTION = "RETURN" m_config.IFACE_PREFIX = "tap" m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(m_config, m_v4_upd, m_v6_upd, m_v4_nat_upd) m_ipset.ensure_exists.assert_called_once_with() self.assertEqual( m_check_call.mock_calls, [ call(["ip", "tunnel", "add", "tunl0", "mode", "ipip"]), call(["ip", "link", "set", "tunl0", "up"]), ] ) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT ! --in-interface tap+ --jump RETURN', '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-INPUT': set(['felix-FROM-ENDPOINT']), 'felix-FORWARD': set([ 'felix-FROM-ENDPOINT', 'felix-TO-ENDPOINT' ]) }, async=False ) self.assertEqual( m_v4_upd.ensure_rule_inserted.mock_calls, [ call("INPUT --jump felix-INPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False), ] )
def test_install_global_ipip_disabled(self, m_ipset, m_devices, m_check_call): m_devices.interface_exists.return_value = False m_devices.interface_up.return_value = False m_set_ips = m_devices.set_interface_ips env_dict = { "FELIX_ETCDADDR": "localhost:4001", "FELIX_HOSTNAME": "myhost", "FELIX_INTERFACEPREFIX": "tap", "FELIX_METADATAADDR": "123.0.0.1", "FELIX_METADATAPORT": "1234", "FELIX_IPINIPENABLED": "false", "FELIX_IPINIPMTU": "1480", "FELIX_DEFAULTENDPOINTTOHOSTACTION": "RETURN" } config = load_config("felix_missing.cfg", env_dict=env_dict) m_v4_upd = Mock(spec=IptablesUpdater) m_v6_upd = Mock(spec=IptablesUpdater) m_v6_raw_upd = Mock(spec=IptablesUpdater) m_v4_nat_upd = Mock(spec=IptablesUpdater) frules.install_global_rules(config, m_v4_upd, m_v6_upd, m_v4_nat_upd, m_v6_raw_upd) self.assertEqual( m_v4_nat_upd.ensure_rule_inserted.mock_calls, [call("PREROUTING --jump felix-PREROUTING", async=False)] ) self.assertEqual( m_v4_nat_upd.ensure_rule_removed.mock_calls, [call("POSTROUTING --out-interface tunl0 " "-m addrtype ! --src-type LOCAL --limit-iface-out " "-m addrtype --src-type LOCAL " "-j MASQUERADE", async=False)] ) m_v6_raw_upd.ensure_rule_inserted.assert_called_once_with( 'PREROUTING --in-interface tap+ --match rpfilter --invert --jump ' 'felix-PREROUTING', async=False, ) m_v6_raw_upd.rewrite_chains.assert_called_once_with( {'felix-PREROUTING': [ '--append felix-PREROUTING --jump DROP -m comment ' '--comment "IPv6 rpfilter failed"' ]}, { 'felix-PREROUTING': {} }, async=False ) self.assertFalse(m_ipset.ensure_exists.called) self.assertFalse(m_check_call.called) self.assertFalse(m_set_ips.called) expected_chains = { 'felix-INPUT': [ '--append felix-INPUT ! --in-interface tap+ --jump RETURN', '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP', '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT', '--append felix-INPUT --protocol tcp --destination 123.0.0.1 --dport 1234 --jump ACCEPT', '--append felix-INPUT --protocol udp --sport 68 --dport 67 --jump ACCEPT', '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT', '--append felix-INPUT --jump felix-FROM-ENDPOINT' ], 'felix-FORWARD': [ '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate INVALID --jump DROP', '--append felix-FORWARD --in-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --out-interface tap+ --match conntrack --ctstate RELATED,ESTABLISHED --jump RETURN', '--append felix-FORWARD --jump felix-FROM-ENDPOINT --in-interface tap+', '--append felix-FORWARD --jump felix-TO-ENDPOINT --out-interface tap+', '--append felix-FORWARD --jump ACCEPT --in-interface tap+', '--append felix-FORWARD --jump ACCEPT --out-interface tap+' ] } m_v4_upd.rewrite_chains.assert_called_once_with( expected_chains, { 'felix-INPUT': set(['felix-FROM-ENDPOINT']), 'felix-FORWARD': set([ 'felix-FROM-ENDPOINT', 'felix-TO-ENDPOINT' ]) }, async=False ) self.assertEqual( m_v4_upd.ensure_rule_inserted.mock_calls, [ call("INPUT --jump felix-INPUT", async=False), call("FORWARD --jump felix-FORWARD", async=False), ] )
def _main_greenlet(): """ The root of our tree of greenlets. Responsible for restarting its children if desired. """ try: _log.info("Connecting to etcd to get our configuration.") hosts_ipset_v4 = IpsetActor(HOSTS_IPSET_V4) monitored_items = [] # The parent process sends us communication pipes as FD 3 and 4. Open # those as files. Wrap the resulting files in a FileObject to make # them cooperate with gevent. pipe_from_parent = FileObject(os.fdopen(3, 'rb', -1), 'rb') pipe_to_parent = FileObject(os.fdopen(4, 'wb', -1), 'wb') config = Config() datastore = DatastoreAPI(config, pipe_from_parent, pipe_to_parent, hosts_ipset_v4) datastore.start() monitored_items.append(datastore.greenlet) # Ask the DatastoreAPI to fill in the global config object before we # proceed. We don't yet support config updates. config_loaded = datastore.load_config(async=False) config_loaded.wait() # Ensure the Kernel's global options are correctly configured for # Calico. devices.configure_global_kernel_config(config) # Check the commands we require are present. futils.check_command_deps() _log.info("Main greenlet: Configuration loaded, starting remaining " "actors...") if config.PROM_METRICS_ENABLED: httpd = HTTPServer(("0.0.0.0", config.PROM_METRICS_DRIVER_PORT), MetricsHandler) stats_server = gevent.Greenlet(httpd.serve_forever) stats_server.start() monitored_items.append(stats_server) v4_filter_updater = IptablesUpdater("filter", ip_version=4, config=config) v4_nat_updater = IptablesUpdater("nat", ip_version=4, config=config) v4_ipset_mgr = IpsetManager(IPV4, config) v4_masq_manager = MasqueradeManager(IPV4, v4_nat_updater) v4_rules_manager = RulesManager(config, 4, v4_filter_updater, v4_ipset_mgr) v4_ep_dispatch_chains = WorkloadDispatchChains(config, 4, v4_filter_updater) v4_if_dispatch_chains = HostEndpointDispatchChains( config, 4, v4_filter_updater) v4_fip_manager = FloatingIPManager(config, 4, v4_nat_updater) v4_ep_manager = EndpointManager(config, IPV4, v4_filter_updater, v4_ep_dispatch_chains, v4_if_dispatch_chains, v4_rules_manager, v4_fip_manager, datastore.write_api) cleanup_updaters = [v4_filter_updater, v4_nat_updater] cleanup_ip_mgrs = [v4_ipset_mgr] managers = [ v4_ipset_mgr, v4_rules_manager, v4_ep_manager, v4_masq_manager, v4_nat_updater ] actors_to_start = [ hosts_ipset_v4, v4_filter_updater, v4_nat_updater, v4_ipset_mgr, v4_masq_manager, v4_rules_manager, v4_ep_dispatch_chains, v4_if_dispatch_chains, v4_ep_manager, v4_fip_manager, ] # Determine if ipv6 is enabled using the config option. if config.IPV6_SUPPORT == "true": v6_enabled = True ipv6_reason = None elif config.IPV6_SUPPORT == "auto": v6_enabled, ipv6_reason = futils.detect_ipv6_supported() else: v6_enabled = False ipv6_reason = "Ipv6Support is 'false'" if v6_enabled: v6_raw_updater = IptablesUpdater("raw", ip_version=6, config=config) v6_filter_updater = IptablesUpdater("filter", ip_version=6, config=config) v6_nat_updater = IptablesUpdater("nat", ip_version=6, config=config) v6_ipset_mgr = IpsetManager(IPV6, config) v6_rules_manager = RulesManager(config, 6, v6_filter_updater, v6_ipset_mgr) v6_ep_dispatch_chains = WorkloadDispatchChains( config, 6, v6_filter_updater) v6_if_dispatch_chains = HostEndpointDispatchChains( config, 6, v6_filter_updater) v6_fip_manager = FloatingIPManager(config, 6, v6_nat_updater) v6_ep_manager = EndpointManager(config, IPV6, v6_filter_updater, v6_ep_dispatch_chains, v6_if_dispatch_chains, v6_rules_manager, v6_fip_manager, datastore.write_api) cleanup_updaters.append(v6_filter_updater) cleanup_ip_mgrs.append(v6_ipset_mgr) managers += [ v6_ipset_mgr, v6_rules_manager, v6_ep_manager, v6_raw_updater, v6_nat_updater ] actors_to_start += [ v6_raw_updater, v6_filter_updater, v6_nat_updater, v6_ipset_mgr, v6_rules_manager, v6_ep_dispatch_chains, v6_if_dispatch_chains, v6_ep_manager, v6_fip_manager, ] else: # Keep the linter happy. _log.warn("IPv6 support disabled: %s.", ipv6_reason) v6_filter_updater = None v6_nat_updater = None v6_raw_updater = None v6_if_dispatch_chains = None cleanup_mgr = CleanupManager(config, cleanup_updaters, cleanup_ip_mgrs) managers.append(cleanup_mgr) update_splitter = UpdateSplitter(managers) iface_watcher = InterfaceWatcher(update_splitter) actors_to_start += [ cleanup_mgr, iface_watcher, ] _log.info("Starting actors.") for actor in actors_to_start: actor.start() monitored_items += [actor.greenlet for actor in actors_to_start] # Try to ensure that the nf_conntrack_netlink kernel module is present. # This works around an issue[1] where the first call to the "conntrack" # command fails while waiting for the module to load. # [1] https://github.com/projectcalico/felix/issues/986 load_nf_conntrack() # Install the global rules before we start polling for updates. _log.info("Installing global rules.") # Dispatch chain needs to make its configuration before we insert the # top-level chains. v4_if_dispatch_chains.configure_iptables(async=False) install_global_rules(config, v4_filter_updater, v4_nat_updater, ip_version=4) if v6_enabled: # Dispatch chain needs to make its configuration before we insert # the top-level chains. v6_if_dispatch_chains.configure_iptables(async=False) install_global_rules(config, v6_filter_updater, v6_nat_updater, ip_version=6, raw_updater=v6_raw_updater) # Start polling for updates. These kicks make the actors poll # indefinitely. _log.info("Starting polling for interface and etcd updates.") f = iface_watcher.watch_interfaces(async=True) monitored_items.append(f) datastore.start_watch(update_splitter, async=True) # Register a SIG_USR handler to trigger a diags dump. def dump_top_level_actors(log): for a in actors_to_start: # The output will include queue length and the like. log.info("%s", a) futils.register_diags("Top-level actors", dump_top_level_actors) futils.register_process_statistics() try: gevent.signal(signal.SIGUSR1, functools.partial(futils.dump_diags)) except AttributeError: # It doesn't matter too much if we fail to do this. _log.warning("Unable to install diag dump handler") pass gevent.signal(signal.SIGTERM, functools.partial(shut_down, datastore)) gevent.signal(signal.SIGINT, functools.partial(shut_down, datastore)) # Wait for something to fail. _log.info("All top-level actors started, waiting on failures...") stopped_greenlets_iter = gevent.iwait(monitored_items) stopped_greenlet = next(stopped_greenlets_iter) try: stopped_greenlet.get() except Exception: _log.exception("Greenlet failed: %s", stopped_greenlet) raise else: _log.error("Greenlet %s unexpectedly returned.", stopped_greenlet) raise AssertionError("Greenlet unexpectedly returned") except: _log.exception("Exception killing main greenlet") raise