Пример #1
0
    def test_initial_snapshot(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.start.assert_called_with()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify a non-empty initial snapshot.
            snapshot_data = agent.etcd._pre_snapshot_hook()
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'spec': {
                        'interfaceName': 'tap1234',
                        'mac': 'fe:16:65:12:33:44',
                        'profiles': ['profile-1'],
                        'ipNetworks': ['10.28.0.2/32'],
                        'ipv4Gateway': '10.28.0.1',
                    }
                })), make_endpoint_name('endpoint-4'))
            agent.etcd._post_snapshot_hook(snapshot_data)

            # Check DHCP driver was not troubled - because the subnet data was
            # missing and so the port could not be processed further.
            call_driver.assert_not_called()
    def test_initial_snapshot(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.loop.assert_called_with()

        # Arrange for subnet read to fail.
        etcd_client.read.side_effect = etcd.EtcdKeyNotFound

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify a non-empty initial snapshot.
            etcd_snapshot_node = mock.Mock()
            etcd_snapshot_node.action = 'exist'
            etcd_snapshot_node.key = ("/calico/v1/host/" +
                                      socket.gethostname() +
                                      "/workload/openstack" +
                                      "/workload_id/endpoint/endpoint-4")
            etcd_snapshot_node.value = json.dumps({
                'state':
                'active',
                'name':
                'tap1234',
                'mac':
                'fe:16:65:12:33:44',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.28.0.2/32'],
                'ipv4_subnet_ids': ['v4subnet-4'],
                'ipv4_gateway':
                '10.28.0.1',
                'ipv6_nets': [],
                'ipv6_subnet_ids': []
            })
            etcd_snapshot_response = mock.Mock()
            etcd_snapshot_response.leaves = [etcd_snapshot_node]
            agent.etcd._on_snapshot_loaded(etcd_snapshot_response)

            # Check expected subnet was read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-4'),
                          consistent=True)
            ])
            etcd_client.read.reset_mock()

            # Check DHCP driver was not troubled - because the subnet data was
            # missing and so the port could not be processed further.
            call_driver.assert_not_called()
    def test_invalid_endpoint_data(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint missing some required fields.
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'state': 'active',
                    'mac': 'fe:16:65:12:33:44',
                    'ipv6_subnet_ids': ['v6subnet-1'],
                    'ipv6_gateway': '2001:db8:1::1'
                })), 'hostname-ignored', 'openstack', 'workload-id-ignored',
                'endpoint-1')

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()

            # Notify an endpoint with non-dict data.
            agent.etcd.on_endpoint_set(EtcdResponse(value="not even a dict!"),
                                       'hostname-ignored', 'openstack',
                                       'workload-id-ignored', 'endpoint-1')

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()

            # One more variant.
            agent.etcd.on_endpoint_set(EtcdResponse(value="\"nor this!\""),
                                       'hostname-ignored', 'openstack',
                                       'workload-id-ignored', 'endpoint-1')

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()
    def test_etcd_watchers_init_with_conf_values(self, *_):
        agent = CalicoDhcpAgent()

        provided_kwargs = get_etcd_connection_settings()

        def check_provided_args(watcher_obj, expected_key_to_poll):
            for attr_name in ('etcd_scheme', 'etcd_key', 'etcd_cert',
                              'etcd_ca'):
                self.assertEqual(getattr(watcher_obj, attr_name),
                                 provided_kwargs[attr_name])

            # provided hosts are stored in form of list [(host, port)...]
            # on etcd watcher
            etcd_host, etcd_port = provided_kwargs['etcd_addrs'].split(':')
            self.assertEqual(watcher_obj.etcd_hosts,
                             [(etcd_host, int(etcd_port))])

            self.assertEqual(watcher_obj.key_to_poll, expected_key_to_poll)

        expected_key = \
            datamodel_v1.dir_for_host(socket.gethostname()) + '/workload'
        check_provided_args(agent.etcd, expected_key)

        expected_key = datamodel_v1.SUBNET_DIR
        check_provided_args(agent.etcd.subnet_watcher, expected_key)
Пример #5
0
    def test_invalid_endpoint_data(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint missing some required fields.
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'mac': 'fe:16:65:12:33:44',
                    'ipNetworks': ['2001:db8:1::1']
                })), make_endpoint_name('endpoint-1'))

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()

            # Notify an endpoint with non-dict data.
            agent.etcd.on_endpoint_set(EtcdResponse(value="not even a dict!"),
                                       make_endpoint_name('endpoint-1'))

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()

            # One more variant.
            agent.etcd.on_endpoint_set(EtcdResponse(value="\"nor this!\""),
                                       make_endpoint_name('endpoint-1'))

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()
    def test_initial_snapshot(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.loop.assert_called_with()

        # Arrange for subnet read to fail.
        etcd_client.read.side_effect = etcd.EtcdKeyNotFound

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify a non-empty initial snapshot.
            etcd_snapshot_node = mock.Mock()
            etcd_snapshot_node.action = 'exist'
            etcd_snapshot_node.key = ("/calico/v1/host/" +
                                      socket.gethostname() +
                                      "/workload/openstack" +
                                      "/workload_id/endpoint/endpoint-4")
            etcd_snapshot_node.value = json.dumps({
                'state': 'active',
                'name': 'tap1234',
                'mac': 'fe:16:65:12:33:44',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.28.0.2/32'],
                'ipv4_subnet_ids': ['v4subnet-4'],
                'ipv4_gateway': '10.28.0.1',
                'ipv6_nets': [],
                'ipv6_subnet_ids': []
            })
            etcd_snapshot_response = mock.Mock()
            etcd_snapshot_response.leaves = [etcd_snapshot_node]
            agent.etcd._on_snapshot_loaded(etcd_snapshot_response)

            # Check expected subnet was read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-4'),
                          consistent=True)
            ])
            etcd_client.read.reset_mock()

            # Check DHCP driver was not troubled - because the subnet data was
            # missing and so the port could not be processed further.
            call_driver.assert_not_called()
Пример #7
0
 def test_etcd_watchers_init_with_conf_values(self):
     agent = CalicoDhcpAgent()
     self.assertEqual(
         agent.etcd.prefix, "/calico/resources/v3/projectcalico.org/" +
         "workloadendpoints/openstack/" + self.hostname.replace('-', '--') +
         "-openstack-")
     self.assertEqual(agent.etcd.v1_subnet_watcher.prefix,
                      datamodel_v1.SUBNET_DIR)
     self.assertEqual(agent.etcd.subnet_watcher.prefix,
                      datamodel_v2.subnet_dir())
Пример #8
0
 def test_host_config(self):
     cfg.CONF.host = "my-special-hostname"
     agent = CalicoDhcpAgent()
     self.assertEqual(
         agent.etcd.prefix, "/calico/resources/v3/projectcalico.org/" +
         "workloadendpoints/openstack/" + "my--special--hostname" +
         "-openstack-")
     self.assertEqual(agent.etcd.v1_subnet_watcher.prefix,
                      datamodel_v1.SUBNET_DIR)
     self.assertEqual(agent.etcd.subnet_watcher.prefix,
                      datamodel_v2.subnet_dir())
Пример #9
0
    def test_invalid_subnet_data(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        agent.etcd.subnet_watcher.on_subnet_set(
            EtcdResponse(value=json.dumps({'gateway_ip': '10.28.0.1'})),
            'v4subnet-1')

        agent.etcd.subnet_watcher.on_subnet_set(
            EtcdResponse(value=json.dumps({'gateway_ip': '2001:db8:1::1'})),
            'v6subnet-1')

        self.assertFalse(agent.etcd.subnet_watcher.subnets_by_id)
Пример #10
0
    def test_kill_agent(self, loop_fn):

        # To test handling of SubnetWatcher's loop exiting, make
        # EtcdWatcher.loop throw an exception.
        loop_fn.side_effect = Exception('from test_kill_agent')

        # Create the DHCP agent and allow it to start the SubnetWatcher loop.
        agent = CalicoDhcpAgent()
        eventlet.sleep(0.2)

        # Check that exception handling caused the endpoint watcher loop to be
        # marked as stopped.
        self.assertTrue(agent.etcd._stopped)
Пример #11
0
    def test_invalid_subnet_data(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Arrange to deliver invalid subnet data.
        def etcd_client_read(key, **kwargs):
            LOG.info('etcd_client_read %s %s', key, kwargs)
            if 'v4subnet-1' in key:
                return EtcdResponse(value=json.dumps({
                    'gateway_ip': '10.28.0.1'
                }))
            if 'v6subnet-1' in key:
                return EtcdResponse(value=json.dumps({
                    'gateway_ip': '2001:db8:1::1'
                }))

            eventlet.sleep(10)
            return None

        etcd_client.read.side_effect = etcd_client_read

        # Notify an endpoint.
        agent.etcd.on_endpoint_set(EtcdResponse(value=json.dumps({
            'state': 'active',
            'name': 'tap1234',
            'mac': 'fe:16:65:12:33:44',
            'profile_ids': ['profile-1'],
            'ipv4_nets': ['10.28.0.2/32'],
            'ipv4_subnet_ids': ['v4subnet-1'],
            'ipv4_gateway': '10.28.0.1',
            'ipv6_nets': ['2001:db8:1::2/128'],
            'ipv6_subnet_ids': ['v6subnet-1'],
            'ipv6_gateway': '2001:db8:1::1'
        })),
            'hostname-ignored',
            'openstack',
            'workload-id-ignored',
            'endpoint-1'
        )

        # Check expected subnets were read from etcd.
        etcd_client.read.assert_has_calls([
            mock.call(datamodel_v1.key_for_subnet('v4subnet-1'),
                      consistent=True),
            mock.call(datamodel_v1.key_for_subnet('v6subnet-1'),
                      consistent=True)
        ],
            any_order=True
        )
        etcd_client.read.reset_mock()
Пример #12
0
 def test_region_config(self):
     cfg.CONF.set_override('openstack_region',
                           'asia-central',
                           group='calico')
     calico_config._reset_globals()
     agent = CalicoDhcpAgent()
     self.assertEqual(
         agent.etcd.prefix, "/calico/resources/v3/projectcalico.org/" +
         "workloadendpoints/openstack-region-asia-central/" +
         self.hostname.replace('-', '--') + "-openstack-")
     self.assertEqual(agent.etcd.v1_subnet_watcher.prefix,
                      datamodel_v1.SUBNET_DIR)
     self.assertEqual(agent.etcd.subnet_watcher.prefix,
                      datamodel_v2.subnet_dir("region-asia-central"))
     calico_config._reset_globals()
Пример #13
0
    def test_invalid_subnet_data(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Arrange to deliver invalid subnet data.
        def etcd_client_read(key, **kwargs):
            LOG.info('etcd_client_read %s %s', key, kwargs)
            if 'v4subnet-1' in key:
                return EtcdResponse(
                    value=json.dumps({'gateway_ip': '10.28.0.1'}))
            if 'v6subnet-1' in key:
                return EtcdResponse(
                    value=json.dumps({'gateway_ip': '2001:db8:1::1'}))

            eventlet.sleep(10)
            return None

        etcd_client.read.side_effect = etcd_client_read

        # Notify an endpoint.
        agent.etcd.on_endpoint_set(
            EtcdResponse(value=json.dumps({
                'state': 'active',
                'name': 'tap1234',
                'mac': 'fe:16:65:12:33:44',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.28.0.2/32'],
                'ipv4_subnet_ids': ['v4subnet-1'],
                'ipv4_gateway': '10.28.0.1',
                'ipv6_nets': ['2001:db8:1::2/128'],
                'ipv6_subnet_ids': ['v6subnet-1'],
                'ipv6_gateway': '2001:db8:1::1'
            })), 'hostname-ignored', 'openstack', 'workload-id-ignored',
            'endpoint-1')

        # Check that either the v4 or the v6 subnet was read from etcd.  Since
        # it's invalid, the processing of the new endpoint stops at that point,
        # and the other subnet is not read at all.
        read_calls = etcd_client.read.mock_calls
        self.assertEqual(1, len(read_calls))
        self.assertTrue(read_calls[0] in [
            mock.call(datamodel_v1.key_for_subnet('v4subnet-1'),
                      consistent=True),
            mock.call(datamodel_v1.key_for_subnet('v6subnet-1'),
                      consistent=True),
        ])
        etcd_client.read.reset_mock()
    def test_no_subnet_data(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint.
            agent.etcd.on_endpoint_set(EtcdResponse(value=json.dumps({'spec': {
                'interfaceName': 'tap1234',
                'mac': 'fe:16:65:12:33:44',
                'profiles': ['profile-1'],
                'ipNetworks': ['10.28.0.2/32', '2001:db8:1::2/128'],
                'ipv4Gateway': '10.28.0.1',
                'ipv6Gateway': '2001:db8:1::1'
            }})),
                make_endpoint_name('endpoint-1')
            )
            call_driver.assert_not_called()
Пример #15
0
    def test_endpoint_no_ipnetworks(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            with mock.patch.object(agent.etcd, 'on_endpoint_delete') as ep_del:
                # Notify an endpoint that is valid but has no ipNetworks.
                agent.etcd.on_endpoint_set(
                    EtcdResponse(value=json.dumps({
                        'spec': {
                            'interfaceName': 'tapfe166512-33',
                            'mac': 'fe:16:65:12:33:44',
                            'ipNetworks': []
                        }
                    })), make_endpoint_name('endpoint-1'))

                # Check handled as a deletion.
                ep_del.assert_called()

            # Check DHCP driver was not asked to do anything.
            call_driver.assert_not_called()
Пример #16
0
    def test_mainline(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.loop.assert_called_with()

        # Notify initial snapshot (empty).
        with mock.patch.object(agent, 'call_driver') as call_driver:
            etcd_snapshot_response = mock.Mock()
            etcd_snapshot_response.leaves = []
            agent.etcd._on_snapshot_loaded(etcd_snapshot_response)
            call_driver.assert_not_called()

        # Prepare subnet reads for the endpoints that we will notify.
        self.first_workload_read = True

        def etcd_client_read(key, **kwargs):
            LOG.info('etcd_client_read %s %s', key, kwargs)
            if 'v4subnet-1' in key:
                self.assertEqual('/calico/dhcp/v1/subnet/v4subnet-1', key)
                return EtcdResponse(value=json.dumps({
                    'cidr': '10.28.0.0/24',
                    'gateway_ip': '10.28.0.1',
                    'host_routes': []
                }))
            if 'v6subnet-1' in key:
                return EtcdResponse(
                    value=json.dumps({
                        'cidr': '2001:db8:1::/80',
                        'gateway_ip': '2001:db8:1::1'
                    }))
            if 'v4subnet-2' in key:
                return EtcdResponse(value=json.dumps({
                    'cidr':
                    '10.29.0.0/24',
                    'gateway_ip':
                    '10.29.0.1',
                    'host_routes': [{
                        'destination': '11.11.0.0/16',
                        'nexthop': '10.65.0.1'
                    }]
                }))
            if key == '/calico/v1/host/nj-ubuntu/workload':
                if self.first_workload_read:
                    # This is the recursive read that the CalicoEtcdWatcher
                    # loop makes after we've triggered it to resync.
                    etcd_snapshot_node = mock.Mock()
                    etcd_snapshot_node.action = 'exist'
                    etcd_snapshot_node.key = (
                        "/calico/v1/host/" + socket.gethostname() +
                        "/workload/openstack" +
                        "/workload_id/endpoint/endpoint-4")
                    etcd_snapshot_node.value = json.dumps({
                        'state':
                        'active',
                        'name':
                        'tap1234',
                        'mac':
                        'fe:16:65:12:33:44',
                        'profile_ids': ['profile-1'],
                        'ipv4_nets': ['10.28.0.2/32'],
                        'ipv4_subnet_ids': ['v4subnet-1'],
                        'ipv4_gateway':
                        '10.28.0.1',
                        'ipv6_nets': [],
                        'ipv6_subnet_ids': []
                    })
                    etcd_snapshot_response = mock.Mock()
                    etcd_snapshot_response.etcd_index = 99
                    etcd_snapshot_response.leaves = [etcd_snapshot_node]
                    self.first_workload_read = False
                    return etcd_snapshot_response

            eventlet.sleep(10)
            return None

        etcd_client.read.side_effect = etcd_client_read

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint.
            agent.etcd.on_endpoint_set(
                EtcdResponse(
                    value=json.dumps({
                        'state': 'active',
                        'name': 'tap1234',
                        'mac': 'fe:16:65:12:33:44',
                        'profile_ids': ['profile-1'],
                        'ipv4_nets': ['10.28.0.2/32'],
                        'ipv4_subnet_ids': ['v4subnet-1'],
                        'ipv4_gateway': '10.28.0.1',
                        'ipv6_nets': ['2001:db8:1::2/128'],
                        'ipv6_subnet_ids': ['v6subnet-1'],
                        'ipv6_gateway': '2001:db8:1::1'
                    })), 'hostname-ignored', 'openstack',
                'workload-id-ignored', 'endpoint-1')

            # Check expected subnets were read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-1'),
                          consistent=True),
                mock.call(datamodel_v1.key_for_subnet('v6subnet-1'),
                          consistent=True)
            ],
                                              any_order=True)
            etcd_client.read.reset_mock()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint (using the same subnets).
            agent.etcd.on_endpoint_set(
                EtcdResponse(
                    value=json.dumps({
                        'state': 'active',
                        'name': 'tap5678',
                        'mac': 'fe:16:65:12:33:55',
                        'profile_ids': ['profile-1'],
                        'ipv4_nets': ['10.28.0.3/32'],
                        'ipv4_subnet_ids': ['v4subnet-1'],
                        'ipv4_gateway': '10.28.0.1',
                        'ipv6_nets': ['2001:db8:1::3/128'],
                        'ipv6_subnet_ids': ['v6subnet-1'],
                        'ipv6_gateway': '2001:db8:1::1',
                        'fqdn': 'calico-vm17.datcon.co.uk'
                    })), 'hostname-ignored', 'openstack',
                'workload-id-ignored', 'endpoint-2')

            # Check no further etcd reads.
            etcd_client.read.assert_not_called()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify deletion of the first endpoint.
            agent.etcd.on_endpoint_delete(None, 'hostname-ignored',
                                          'openstack', 'workload-id-ignored',
                                          'endpoint-1')

            # Check no further etcd reads.
            etcd_client.read.assert_not_called()

            # Check DHCP driver was asked to reload allocations.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint using a new subnet.
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'state': 'active',
                    'name': 'tapABCD',
                    'mac': 'fe:16:65:12:33:66',
                    'profile_ids': ['profile-1'],
                    'ipv4_nets': ['10.29.0.3/32'],
                    'ipv4_subnet_ids': ['v4subnet-2'],
                    'ipv4_gateway': '10.29.0.1',
                    'ipv6_nets': [],
                    'ipv6_subnet_ids': []
                })), 'hostname-ignored', 'openstack', 'workload-id-ignored',
                'endpoint-3')

            # Check expected new subnet was read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-2'),
                          consistent=True)
            ])
            etcd_client.read.reset_mock()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Set the endpoint watcher loop running.
            eventlet.spawn(agent.etcd.loop)

            # Report that the subnet watcher noticed a change.
            agent.etcd.on_subnet_set(None, 'some-subnet-X')
            eventlet.sleep(0.2)

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Report that the subnet watcher loaded a new snapshot.
            agent.etcd.subnet_watcher._on_snapshot_loaded('ignored')
            eventlet.sleep(0.2)

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)
Пример #17
0
    def test_mainline(self, etcd_client_cls):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.loop.assert_called_with()

        # Notify initial snapshot (empty).
        with mock.patch.object(agent, 'call_driver') as call_driver:
            etcd_snapshot_response = mock.Mock()
            etcd_snapshot_response.leaves = []
            agent.etcd._on_snapshot_loaded(etcd_snapshot_response)
            call_driver.assert_not_called()

        # Prepare subnet reads for the endpoints that we will notify.
        self.first_workload_read = True

        def etcd_client_read(key, **kwargs):
            LOG.info('etcd_client_read %s %s', key, kwargs)
            if 'v4subnet-1' in key:
                self.assertEqual('/calico/dhcp/v1/subnet/v4subnet-1',
                                 key)
                return EtcdResponse(value=json.dumps({
                    'cidr': '10.28.0.0/24',
                    'gateway_ip': '10.28.0.1',
                    'host_routes': []
                }))
            if 'v6subnet-1' in key:
                return EtcdResponse(value=json.dumps({
                    'cidr': '2001:db8:1::/80',
                    'gateway_ip': '2001:db8:1::1'
                }))
            if 'v4subnet-2' in key:
                return EtcdResponse(value=json.dumps({
                    'cidr': '10.29.0.0/24',
                    'gateway_ip': '10.29.0.1',
                    'host_routes': [{'destination': '11.11.0.0/16',
                                     'nexthop': '10.65.0.1'}]
                }))
            if key == '/calico/v1/host/nj-ubuntu/workload':
                if self.first_workload_read:
                    # This is the recursive read that the CalicoEtcdWatcher
                    # loop makes after we've triggered it to resync.
                    etcd_snapshot_node = mock.Mock()
                    etcd_snapshot_node.action = 'exist'
                    etcd_snapshot_node.key = (
                        "/calico/v1/host/" +
                        socket.gethostname() +
                        "/workload/openstack" +
                        "/workload_id/endpoint/endpoint-4"
                    )
                    etcd_snapshot_node.value = json.dumps({
                        'state': 'active',
                        'name': 'tap1234',
                        'mac': 'fe:16:65:12:33:44',
                        'profile_ids': ['profile-1'],
                        'ipv4_nets': ['10.28.0.2/32'],
                        'ipv4_subnet_ids': ['v4subnet-1'],
                        'ipv4_gateway': '10.28.0.1',
                        'ipv6_nets': [],
                        'ipv6_subnet_ids': []
                    })
                    etcd_snapshot_response = mock.Mock()
                    etcd_snapshot_response.etcd_index = 99
                    etcd_snapshot_response.leaves = [etcd_snapshot_node]
                    self.first_workload_read = False
                    return etcd_snapshot_response

            eventlet.sleep(10)
            return None

        etcd_client.read.side_effect = etcd_client_read

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint.
            agent.etcd.on_endpoint_set(EtcdResponse(value=json.dumps({
                'state': 'active',
                'name': 'tap1234',
                'mac': 'fe:16:65:12:33:44',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.28.0.2/32'],
                'ipv4_subnet_ids': ['v4subnet-1'],
                'ipv4_gateway': '10.28.0.1',
                'ipv6_nets': ['2001:db8:1::2/128'],
                'ipv6_subnet_ids': ['v6subnet-1'],
                'ipv6_gateway': '2001:db8:1::1'
            })),
                'hostname-ignored',
                'openstack',
                'workload-id-ignored',
                'endpoint-1'
            )

            # Check expected subnets were read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-1'),
                          consistent=True),
                mock.call(datamodel_v1.key_for_subnet('v6subnet-1'),
                          consistent=True)
            ],
                any_order=True)
            etcd_client.read.reset_mock()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint (using the same subnets).
            agent.etcd.on_endpoint_set(EtcdResponse(value=json.dumps({
                'state': 'active',
                'name': 'tap5678',
                'mac': 'fe:16:65:12:33:55',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.28.0.3/32'],
                'ipv4_subnet_ids': ['v4subnet-1'],
                'ipv4_gateway': '10.28.0.1',
                'ipv6_nets': ['2001:db8:1::3/128'],
                'ipv6_subnet_ids': ['v6subnet-1'],
                'ipv6_gateway': '2001:db8:1::1',
                'fqdn': 'calico-vm17.datcon.co.uk'
            })),
                'hostname-ignored',
                'openstack',
                'workload-id-ignored',
                'endpoint-2'
            )

            # Check no further etcd reads.
            etcd_client.read.assert_not_called()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify deletion of the first endpoint.
            agent.etcd.on_endpoint_delete(None,
                                          'hostname-ignored',
                                          'openstack',
                                          'workload-id-ignored',
                                          'endpoint-1')

            # Check no further etcd reads.
            etcd_client.read.assert_not_called()

            # Check DHCP driver was asked to reload allocations.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint using a new subnet.
            agent.etcd.on_endpoint_set(EtcdResponse(value=json.dumps({
                'state': 'active',
                'name': 'tapABCD',
                'mac': 'fe:16:65:12:33:66',
                'profile_ids': ['profile-1'],
                'ipv4_nets': ['10.29.0.3/32'],
                'ipv4_subnet_ids': ['v4subnet-2'],
                'ipv4_gateway': '10.29.0.1',
                'ipv6_nets': [],
                'ipv6_subnet_ids': []
            })),
                'hostname-ignored',
                'openstack',
                'workload-id-ignored',
                'endpoint-3'
            )

            # Check expected new subnet was read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-2'),
                          consistent=True)
            ])
            etcd_client.read.reset_mock()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Set the endpoint watcher loop running.
            eventlet.spawn(agent.etcd.loop)

            # Report that the subnet watcher noticed a change.
            agent.etcd.on_subnet_set(None, 'some-subnet-X')
            eventlet.sleep(0.2)

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Report that the subnet watcher loaded a new snapshot.
            agent.etcd.subnet_watcher._on_snapshot_loaded('ignored')
            eventlet.sleep(0.2)

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)
Пример #18
0
    def test_mainline(self):
        # Create the DHCP agent.
        agent = CalicoDhcpAgent()

        # Check that running it invokes the etcd watcher loop.
        with mock.patch.object(agent, 'etcd') as etcdobj:
            agent.run()
            etcdobj.start.assert_called_with()

        # Notify initial snapshot (empty).
        with mock.patch.object(agent, 'call_driver') as call_driver:
            snapshot_data = agent.etcd._pre_snapshot_hook()
            agent.etcd._post_snapshot_hook(snapshot_data)
            call_driver.assert_not_called()

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify subnets.
            agent.etcd.subnet_watcher.on_subnet_set(
                EtcdResponse(value=json.dumps({
                    'cidr': '10.28.0.0/24',
                    'gateway_ip': '10.28.0.1',
                    'host_routes': []
                })), 'v4subnet-1')
            agent.etcd.subnet_watcher.on_subnet_set(
                EtcdResponse(value=json.dumps({
                    'cidr': '2001:db8:1::/80',
                    'gateway_ip': '2001:db8:1::1'
                })), 'v6subnet-1')
            agent.etcd.subnet_watcher.on_subnet_set(
                EtcdResponse(value=json.dumps({
                    'cidr':
                    '10.29.0.0/24',
                    'gateway_ip':
                    '10.29.0.1',
                    'host_routes': [{
                        'destination': '11.11.0.0/16',
                        'nexthop': '10.65.0.1'
                    }]
                })), 'v4subnet-2')

            # Notify an endpoint.
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'spec': {
                        'interfaceName': 'tap1234',
                        'mac': 'fe:16:65:12:33:44',
                        'profiles': ['profile-1'],
                        'ipNetworks': ['10.28.0.2/32', '2001:db8:1::2/128'],
                        'ipv4Gateway': '10.28.0.1',
                        'ipv6Gateway': '2001:db8:1::1'
                    }
                })), make_endpoint_name('endpoint-1'))

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint (using the same subnets).
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'spec': {
                        'interfaceName': 'tap5678',
                        'mac': 'fe:16:65:12:33:55',
                        'profiles': ['profile-1'],
                        'ipNetworks': ['10.28.0.3/32', '2001:db8:1::3/128'],
                        'ipv4Gateway': '10.28.0.1',
                        'ipv6Gateway': '2001:db8:1::1',
                        'fqdn': 'calico-vm17.datcon.co.uk'
                    }
                })), make_endpoint_name('endpoint-2'))

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify deletion of the first endpoint.
            agent.etcd.on_endpoint_delete(None,
                                          make_endpoint_name('endpoint-1'))

            # Check DHCP driver was asked to reload allocations.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify another endpoint using a new subnet.
            agent.etcd.on_endpoint_set(
                EtcdResponse(value=json.dumps({
                    'spec': {
                        'interfaceName': 'tapABCD',
                        'mac': 'fe:16:65:12:33:66',
                        'profiles': ['profile-1'],
                        'ipNetworks': ['10.29.0.3/32'],
                        'ipv4Gateway': '10.29.0.1',
                    }
                })), make_endpoint_name('endpoint-3'))

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)
Пример #19
0
    def test_dir_delete(self, etcd_client_cls):
        LOG.debug('test_dir_delete')

        # Create the DHCP agent.
        agent = CalicoDhcpAgent()
        etcd_client = etcd_client_cls.return_value

        def etcd_client_read(key, **kwargs):
            LOG.info('etcd_client_read %s %s', key, kwargs)
            if 'v4subnet-1' in key:
                return EtcdResponse(value=json.dumps({
                    'cidr': '10.28.0.0/24',
                    'gateway_ip': '10.28.0.1'
                }))
            if 'v6subnet-1' in key:
                return EtcdResponse(
                    value=json.dumps({
                        'cidr': '2001:db8:1::/80',
                        'gateway_ip': '2001:db8:1::1'
                    }))

            eventlet.sleep(10)
            return None

        LOG.debug('etcd_client=%r', etcd_client)
        etcd_client.read.side_effect = etcd_client_read

        # Notify initial snapshot (empty).
        etcd_snapshot_response = mock.Mock()
        etcd_snapshot_response.leaves = []
        LOG.debug('Call _on_snapshot_loaded')
        agent.etcd._on_snapshot_loaded(etcd_snapshot_response)

        with mock.patch.object(agent, 'call_driver') as call_driver:
            # Notify an endpoint.
            agent.etcd.on_endpoint_set(
                EtcdResponse(
                    value=json.dumps({
                        'state': 'active',
                        'name': 'tap1234',
                        'mac': 'fe:16:65:12:33:44',
                        'profile_ids': ['profile-1'],
                        'ipv4_nets': ['10.28.0.2/32'],
                        'ipv4_subnet_ids': ['v4subnet-1'],
                        'ipv4_gateway': '10.28.0.1',
                        'ipv6_nets': ['2001:db8:1::2/128'],
                        'ipv6_subnet_ids': ['v6subnet-1'],
                        'ipv6_gateway': '2001:db8:1::1'
                    })), 'hostname-ignored', 'openstack',
                'workload-id-ignored', 'endpoint-1')

            # Check expected subnets were read from etcd.
            etcd_client.read.assert_has_calls([
                mock.call(datamodel_v1.key_for_subnet('v4subnet-1'),
                          consistent=True),
                mock.call(datamodel_v1.key_for_subnet('v6subnet-1'),
                          consistent=True)
            ],
                                              any_order=True)
            etcd_client.read.reset_mock()

            # Check DHCP driver was asked to restart.
            call_driver.assert_called_with('restart', mock.ANY)

            # Notify deletion of one of that endpoint's parent directories.
            agent.etcd.on_dir_delete(None,
                                     hostname='hostname-ignored',
                                     orchestrator='openstack',
                                     workload_id='workload-id-ignored')
            self.assertTrue(agent.etcd.resync_after_current_poll)