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)
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_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())
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())
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)
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)
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()
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()
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()
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()
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)
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)
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)
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)