Beispiel #1
0
 def __init__(self, ip_type, iptables_mgr):
     super(MasqueradeManager, self).__init__(qualifier=str(ip_type))
     assert ip_type in (IPV4, IPV6)
     assert iptables_mgr.table == "nat"
     self.ip_type = ip_type
     self.pools_by_id = {}
     self._iptables_mgr = iptables_mgr
     ip_family = "inet" if ip_type == IPV4 else "inet6"
     self._all_pools_ipset = Ipset(ALL_POOLS_SET_NAME, ALL_POOLS_SET_NAME + "-tmp", ip_family, "hash:net")
     self._masq_pools_ipset = Ipset(MASQ_POOLS_SET_NAME, MASQ_POOLS_SET_NAME + "-tmp", ip_family, "hash:net")
     self._dirty = False
Beispiel #2
0
 def __init__(self, ip_type, iptables_mgr):
     super(MasqueradeManager, self).__init__(qualifier=str(ip_type))
     assert ip_type in (IPV4, IPV6)
     assert iptables_mgr.table == "nat"
     self.ip_type = ip_type
     self.pools_by_id = {}
     self._iptables_mgr = iptables_mgr
     ip_family = "inet" if ip_type == IPV4 else "inet6"
     self._all_pools_ipset = Ipset(ALL_POOLS_SET_NAME,
                                   ALL_POOLS_SET_NAME + "-tmp", ip_family,
                                   "hash:net")
     self._masq_pools_ipset = Ipset(MASQ_POOLS_SET_NAME,
                                    MASQ_POOLS_SET_NAME + "-tmp", ip_family,
                                    "hash:net")
     self._dirty = False
class TestIpset(BaseTestCase):
    def setUp(self):
        super(TestIpset, self).setUp()
        self.ipset = Ipset("foo", "foo-tmp", "inet")

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_mainline(self, m_check_call):
        self.ipset.replace_members(set(["10.0.0.1"]))
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
                      'create foo-tmp hash:ip family inet --exist\n'
                      'flush foo-tmp\n'
                      'add foo-tmp 10.0.0.1\n'
                      'swap foo foo-tmp\n'
                      'destroy foo-tmp\n'
                      'COMMIT\n'
        )

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_ensure_exists(self, m_check_call):
        self.ipset.ensure_exists()
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
                      'COMMIT\n'
        )

    @patch("calico.felix.futils.call_silent", autospec=True)
    def test_delete(self, m_call_silent):
        self.ipset.delete()
        self.assertEqual(
            m_call_silent.mock_calls,
            [
                call(["ipset", "destroy", "foo"]),
                call(["ipset", "destroy", "foo-tmp"]),
            ]
        )
Beispiel #4
0
 def setUp(self):
     super(TestIpset, self).setUp()
     self.ipset = Ipset("foo", "foo-tmp", "inet")
Beispiel #5
0
class TestIpset(BaseTestCase):
    def setUp(self):
        super(TestIpset, self).setUp()
        self.ipset = Ipset("foo", "foo-tmp", "inet")

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members(self, m_check_call):
        self.ipset.replace_members(set(["10.0.0.1"]))
        exp_calls = [
            call(["ipset", "destroy", "foo-tmp"]),
            call(["ipset", "list", "foo"]),
            call(["ipset", "restore"],
                 input_str='create foo-tmp hash:ip family inet '
                 'maxelem 1048576 --exist\n'
                 'flush foo-tmp\n'
                 'add foo-tmp 10.0.0.1\n'
                 'swap foo foo-tmp\n'
                 'destroy foo-tmp\n'
                 'COMMIT\n')
        ]
        self.assertEqual(m_check_call.mock_calls, exp_calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members_delete_fails(self, m_check_call):
        m_check_call.side_effect = iter(
            [FailedSystemCall("Blah", [], 1, None, "err"), None, None, None])
        self.ipset.replace_members(set(["10.0.0.1"]))
        exp_calls = [
            call(["ipset", "destroy", "foo-tmp"]),
            call(['ipset', 'list', 'foo-tmp']),
            call(['ipset', 'list', 'foo']),
            call(["ipset", "restore"],
                 input_str='create foo-tmp hash:ip family inet '
                 'maxelem 1048576 --exist\n'
                 'flush foo-tmp\n'
                 'add foo-tmp 10.0.0.1\n'
                 'swap foo foo-tmp\n'
                 'destroy foo-tmp\n'
                 'COMMIT\n')
        ]
        self.assertEqual(m_check_call.mock_calls, exp_calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_apply_changes(self, m_check_call):
        added = set(["10.0.0.2"])
        removed = set(["10.0.0.1"])
        self.ipset.apply_changes(added, removed)
        self.assertEqual(m_check_call.mock_calls, [
            call(["ipset", "restore"],
                 input_str='del foo 10.0.0.1\n'
                 'add foo 10.0.0.2\n'
                 'COMMIT\n')
        ])

    @patch("calico.felix.futils.check_call",
           autospec=True,
           side_effect=FailedSystemCall("Blah", [], None, None, "err"))
    def test_apply_changes_err(self, m_check_call):
        # First call to update_members will fail, leading to a retry.
        added = set(["10.0.0.2"])
        removed = set(["10.0.0.1"])
        self.assertRaises(FailedSystemCall, self.ipset.apply_changes, added,
                          removed)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_ensure_exists(self, m_check_call):
        self.ipset.ensure_exists()
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet maxelem 1048576 --exist\n'
            'COMMIT\n')

    @patch("calico.felix.futils.call_silent", autospec=True)
    def test_delete(self, m_call_silent):
        self.ipset.delete()
        self.assertEqual(m_call_silent.mock_calls, [
            call(["ipset", "destroy", "foo"]),
            call(["ipset", "destroy", "foo-tmp"]),
        ])

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_list_ipset_names(self, m_check_call):
        m_check_call.return_value = CommandOutput(IPSET_LIST_OUTPUT, "")
        self.assertEqual(list_ipset_names(),
                         ['felix-v4-calico_net', 'felix-v6-calico_net'])
Beispiel #6
0
class MasqueradeManager(Actor):
    def __init__(self, ip_type, iptables_mgr):
        super(MasqueradeManager, self).__init__(qualifier=str(ip_type))
        assert ip_type in (IPV4, IPV6)
        assert iptables_mgr.table == "nat"
        self.ip_type = ip_type
        self.pools_by_id = {}
        self._iptables_mgr = iptables_mgr
        ip_family = "inet" if ip_type == IPV4 else "inet6"
        self._all_pools_ipset = Ipset(ALL_POOLS_SET_NAME,
                                      ALL_POOLS_SET_NAME + "-tmp",
                                      ip_family,
                                      "hash:net")
        self._masq_pools_ipset = Ipset(MASQ_POOLS_SET_NAME,
                                       MASQ_POOLS_SET_NAME + "-tmp",
                                       ip_family,
                                       "hash:net")
        self._dirty = False

    @actor_message()
    def apply_snapshot(self, pools_by_id):
        _log.info("Applying IPAM pool snapshot with %s pools",
                  len(pools_by_id))
        self.pools_by_id.clear()
        self.pools_by_id.update(pools_by_id)
        self._dirty = True

    @actor_message()
    def on_ipam_pool_updated(self, pool_id, pool):
        if self.pools_by_id.get(pool_id) != pool:
            if pool is None:
                _log.info("IPAM pool deleted: %s", pool_id)
                del self.pools_by_id[pool_id]
            else:
                _log.info("IPAM pool %s updated: %s", pool_id, pool)
                self.pools_by_id[pool_id] = pool
            self._dirty = True

    def _finish_msg_batch(self, batch, results):
        _log.debug("Finishing batch of IPAM pool changes")
        if self._dirty:
            _log.info("Marked as dirty, looking for masq-enabled pools")
            masq_enabled_cidrs = set()
            all_cidrs = set()
            for pool in self.pools_by_id.itervalues():
                all_cidrs.add(pool["cidr"])
                if pool.get("masquerade", False):
                    masq_enabled_cidrs.add(pool["cidr"])
            if masq_enabled_cidrs:
                _log.info("There are masquerade-enabled pools present. "
                          "Updating.")
                self._all_pools_ipset.replace_members(all_cidrs)
                self._masq_pools_ipset.replace_members(masq_enabled_cidrs)
                # Enable masquerading for traffic coming from pools that
                # have it enabled only when the traffic is heading to an IP
                # that isn't in any Calico-owned pool.  (We assume that NAT
                # is not required for Calico-owned IPs.)
                self._iptables_mgr.ensure_rule_inserted(MASQ_RULE_FRAGMENT,
                                                        async=True)
            else:
                _log.info("No masquerade-enabled pools present. "
                          "Removing rules and ipsets.")
                # Ensure that the rule doesn't exist before we try to remove
                # our ipsets. Have to make a blocking call so that we don't
                # try to remove the ipsets before we've cleaned up the rule
                # that references them.
                self._iptables_mgr.ensure_rule_removed(MASQ_RULE_FRAGMENT,
                                                       async=False)
                # Safe to call even if the ipsets don't exist:
                self._all_pools_ipset.delete()
                self._masq_pools_ipset.delete()
            self._dirty = False
            _log.info("Finished refreshing ipsets")
 def setUp(self):
     super(TestIpset, self).setUp()
     self.ipset = Ipset("foo", "foo-tmp", "inet")
class TestIpset(BaseTestCase):
    def setUp(self):
        super(TestIpset, self).setUp()
        self.ipset = Ipset("foo", "foo-tmp", "inet")

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members(self, m_check_call):
        self.ipset.replace_members(set(["10.0.0.1"]))
        exp_calls = [
            call(["ipset", "destroy", "foo-tmp"]),
            call(["ipset", "list", "foo"]),
            call(
                ["ipset", "restore"],
                input_str='create foo-tmp hash:ip family inet '
                          'maxelem 1048576 --exist\n'
                          'flush foo-tmp\n'
                          'add foo-tmp 10.0.0.1\n'
                          'swap foo foo-tmp\n'
                          'destroy foo-tmp\n'
                          'COMMIT\n'
            )
        ]
        self.assertEqual(m_check_call.mock_calls, exp_calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members_delete_fails(self, m_check_call):
        m_check_call.side_effect = iter([
            FailedSystemCall("Blah", [], 1, None, "err"),
            None, None, None])
        self.ipset.replace_members(set(["10.0.0.1"]))
        exp_calls = [
            call(["ipset", "destroy", "foo-tmp"]),
            call(['ipset', 'list', 'foo-tmp']),
            call(['ipset', 'list', 'foo']),
            call(
                ["ipset", "restore"],
                input_str='create foo-tmp hash:ip family inet '
                          'maxelem 1048576 --exist\n'
                          'flush foo-tmp\n'
                          'add foo-tmp 10.0.0.1\n'
                          'swap foo foo-tmp\n'
                          'destroy foo-tmp\n'
                          'COMMIT\n'
            )
        ]
        self.assertEqual(m_check_call.mock_calls, exp_calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_apply_changes(self, m_check_call):
        added = set(["10.0.0.2"])
        removed = set(["10.0.0.1"])
        self.ipset.apply_changes(added, removed)
        self.assertEqual(
            m_check_call.mock_calls,
            [call(["ipset", "restore"],
                   input_str='del foo 10.0.0.1\n'
                             'add foo 10.0.0.2\n'
                             'COMMIT\n')]
        )

    @patch("calico.felix.futils.check_call", autospec=True,
           side_effect=FailedSystemCall("Blah", [], None, None, "err"))
    def test_apply_changes_err(self, m_check_call):
        # First call to update_members will fail, leading to a retry.
        added = set(["10.0.0.2"])
        removed = set(["10.0.0.1"])
        self.assertRaises(FailedSystemCall,
                          self.ipset.apply_changes,
                          added, removed)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_ensure_exists(self, m_check_call):
        self.ipset.ensure_exists()
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet maxelem 1048576 --exist\n'
                      'COMMIT\n'
        )

    @patch("calico.felix.futils.call_silent", autospec=True)
    def test_delete(self, m_call_silent):
        self.ipset.delete()
        self.assertEqual(
            m_call_silent.mock_calls,
            [
                call(["ipset", "destroy", "foo"]),
                call(["ipset", "destroy", "foo-tmp"]),
            ]
        )

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_list_ipset_names(self, m_check_call):
        m_check_call.return_value = CommandOutput(IPSET_LIST_OUTPUT, "")
        self.assertEqual(list_ipset_names(),
                         ['felix-v4-calico_net', 'felix-v6-calico_net'])
Beispiel #9
0
class MasqueradeManager(Actor):
    def __init__(self, ip_type, iptables_mgr):
        super(MasqueradeManager, self).__init__(qualifier=str(ip_type))
        assert ip_type in (IPV4, IPV6)
        assert iptables_mgr.table == "nat"
        self.ip_type = ip_type
        self.pools_by_id = {}
        self._iptables_mgr = iptables_mgr
        ip_family = "inet" if ip_type == IPV4 else "inet6"
        self._all_pools_ipset = Ipset(ALL_POOLS_SET_NAME,
                                      ALL_POOLS_SET_NAME + "-tmp",
                                      ip_family,
                                      "hash:net")
        self._masq_pools_ipset = Ipset(MASQ_POOLS_SET_NAME,
                                       MASQ_POOLS_SET_NAME + "-tmp",
                                       ip_family,
                                       "hash:net")
        self._dirty = False

    @actor_message()
    def apply_snapshot(self, pools_by_id):
        _log.info("Applying IPAM pool snapshot with %s pools",
                  len(pools_by_id))
        self.pools_by_id.clear()
        self.pools_by_id.update(pools_by_id)
        self._dirty = True

    @actor_message()
    def on_ipam_pool_updated(self, pool_id, pool):
        if self.pools_by_id.get(pool_id) != pool:
            if pool is None:
                _log.info("IPAM pool deleted: %s", pool_id)
                del self.pools_by_id[pool_id]
            else:
                _log.info("IPAM pool %s updated: %s", pool_id, pool)
                self.pools_by_id[pool_id] = pool
            self._dirty = True

    def _finish_msg_batch(self, batch, results):
        _log.debug("Finishing batch of IPAM pool changes")
        if self._dirty:
            _log.info("Marked as dirty, looking for masq-enabled pools")
            masq_enabled_cidrs = set()
            all_cidrs = set()
            for pool in self.pools_by_id.itervalues():
                all_cidrs.add(pool["cidr"])
                if pool.get("masquerade", False):
                    masq_enabled_cidrs.add(pool["cidr"])
            if masq_enabled_cidrs:
                _log.info("There are masquerade-enabled pools present. "
                          "Updating.")
                self._all_pools_ipset.replace_members(all_cidrs)
                self._masq_pools_ipset.replace_members(masq_enabled_cidrs)
                # Enable masquerading for traffic coming from pools that
                # have it enabled only when the traffic is heading to an IP
                # that isn't in any Calico-owned pool.  (We assume that NAT
                # is not required for Calico-owned IPs.)
                self._iptables_mgr.ensure_rule_inserted(MASQ_RULE_FRAGMENT,
                                                        async=True)
            else:
                _log.info("No masquerade-enabled pools present. "
                          "Removing rules and ipsets.")
                # Ensure that the rule doesn't exist before we try to remove
                # our ipsets. Have to make a blocking call so that we don't
                # try to remove the ipsets before we've cleaned up the rule
                # that references them.
                self._iptables_mgr.ensure_rule_removed(MASQ_RULE_FRAGMENT,
                                                       async=False)
                # Safe to call even if the ipsets don't exist:
                self._all_pools_ipset.delete()
                self._masq_pools_ipset.delete()
            self._dirty = False
            _log.info("Finished refreshing ipsets")
Beispiel #10
0
class TestIpset(BaseTestCase):
    def setUp(self):
        super(TestIpset, self).setUp()
        self.ipset = Ipset("foo", "foo-tmp", "inet")

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members(self, m_check_call):
        self.ipset.replace_members(set(["10.0.0.1"]))
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
                      'create foo-tmp hash:ip family inet --exist\n'
                      'flush foo-tmp\n'
                      'add foo-tmp 10.0.0.1\n'
                      'swap foo foo-tmp\n'
                      'destroy foo-tmp\n'
                      'COMMIT\n'
        )

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_update_members(self, m_check_call):
        old = set(["10.0.0.2"])
        new = set(["10.0.0.1", "10.0.0.2"])
        self.ipset.update_members(old, new)

        old = set(["10.0.0.1", "10.0.0.2"])
        new = set(["10.0.0.1", "1.2.3.4"])

        self.ipset.update_members(old, new)

        calls = [call(["ipset", "restore"],
                      input_str='add foo 10.0.0.1\nCOMMIT\n'),
                 call(["ipset", "restore"],
                      input_str='del foo 10.0.0.2\n'
                                 'add foo 1.2.3.4\n'
                                 'COMMIT\n')]

        self.assertEqual(m_check_call.call_count, 2)
        m_check_call.assert_has_calls(calls)

    @patch("calico.felix.futils.check_call", autospec=True,
           side_effect=iter([
               FailedSystemCall("Blah", [], None, None, "err"),
               None]))
    def test_update_members_err(self, m_check_call):
        # First call to update_members will fail, leading to a retry.
        old = set(["10.0.0.2"])
        new = set(["10.0.0.1"])
        self.ipset.update_members(old, new)

        calls = [call(["ipset", "restore"],
                      input_str='del foo 10.0.0.2\n'
                                'add foo 10.0.0.1\n'
                                'COMMIT\n'),
                 call(["ipset", "restore"],
                      input_str='create foo hash:ip family inet --exist\n'
                                'create foo-tmp hash:ip family inet --exist\n'
                                'flush foo-tmp\n'
                                'add foo-tmp 10.0.0.1\n'
                                'swap foo foo-tmp\n'
                                'destroy foo-tmp\n'
                                'COMMIT\n')]

        self.assertEqual(m_check_call.call_count, 2)
        m_check_call.assert_has_calls(calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_ensure_exists(self, m_check_call):
        self.ipset.ensure_exists()
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
                      'COMMIT\n'
        )

    @patch("calico.felix.futils.call_silent", autospec=True)
    def test_delete(self, m_call_silent):
        self.ipset.delete()
        self.assertEqual(
            m_call_silent.mock_calls,
            [
                call(["ipset", "destroy", "foo"]),
                call(["ipset", "destroy", "foo-tmp"]),
            ]
        )
Beispiel #11
0
class TestIpset(BaseTestCase):
    def setUp(self):
        super(TestIpset, self).setUp()
        self.ipset = Ipset("foo", "foo-tmp", "inet")

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_replace_members(self, m_check_call):
        self.ipset.replace_members(set(["10.0.0.1"]))
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
            'create foo-tmp hash:ip family inet --exist\n'
            'flush foo-tmp\n'
            'add foo-tmp 10.0.0.1\n'
            'swap foo foo-tmp\n'
            'destroy foo-tmp\n'
            'COMMIT\n')

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_update_members(self, m_check_call):
        old = set(["10.0.0.2"])
        new = set(["10.0.0.1", "10.0.0.2"])
        self.ipset.update_members(old, new)

        old = set(["10.0.0.1", "10.0.0.2"])
        new = set(["10.0.0.1", "1.2.3.4"])

        self.ipset.update_members(old, new)

        calls = [
            call(["ipset", "restore"], input_str='add foo 10.0.0.1\nCOMMIT\n'),
            call(["ipset", "restore"],
                 input_str='del foo 10.0.0.2\n'
                 'add foo 1.2.3.4\n'
                 'COMMIT\n')
        ]

        self.assertEqual(m_check_call.call_count, 2)
        m_check_call.assert_has_calls(calls)

    @patch("calico.felix.futils.check_call",
           autospec=True,
           side_effect=iter(
               [FailedSystemCall("Blah", [], None, None, "err"), None]))
    def test_update_members_err(self, m_check_call):
        # First call to update_members will fail, leading to a retry.
        old = set(["10.0.0.2"])
        new = set(["10.0.0.1"])
        self.ipset.update_members(old, new)

        calls = [
            call(["ipset", "restore"],
                 input_str='del foo 10.0.0.2\n'
                 'add foo 10.0.0.1\n'
                 'COMMIT\n'),
            call(["ipset", "restore"],
                 input_str='create foo hash:ip family inet --exist\n'
                 'create foo-tmp hash:ip family inet --exist\n'
                 'flush foo-tmp\n'
                 'add foo-tmp 10.0.0.1\n'
                 'swap foo foo-tmp\n'
                 'destroy foo-tmp\n'
                 'COMMIT\n')
        ]

        self.assertEqual(m_check_call.call_count, 2)
        m_check_call.assert_has_calls(calls)

    @patch("calico.felix.futils.check_call", autospec=True)
    def test_ensure_exists(self, m_check_call):
        self.ipset.ensure_exists()
        m_check_call.assert_called_once_with(
            ["ipset", "restore"],
            input_str='create foo hash:ip family inet --exist\n'
            'COMMIT\n')

    @patch("calico.felix.futils.call_silent", autospec=True)
    def test_delete(self, m_call_silent):
        self.ipset.delete()
        self.assertEqual(m_call_silent.mock_calls, [
            call(["ipset", "destroy", "foo"]),
            call(["ipset", "destroy", "foo-tmp"]),
        ])