def test_reload_allocations_stale_pid(self): (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data, exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data with mock.patch('__builtin__.open') as mock_open: mock_open.return_value.__enter__ = lambda s: s mock_open.return_value.__exit__ = mock.Mock() mock_open.return_value.readline.return_value = None with mock.patch('os.path.isdir') as isdir: isdir.return_value = True with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ipmap: ipmap.return_value = {} dm.reload_allocations() self.assertTrue(ipmap.called) self.safe.assert_has_calls([ mock.call(exp_host_name, exp_host_data), mock.call(exp_addn_name, exp_addn_data), mock.call(exp_opt_name, exp_opt_data), ]) mock_open.assert_called_once_with('/proc/5/cmdline', 'r')
def test_output_opts_file_pxe_3port_2net(self): expected = """ tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 tag:tag0,249,20.0.0.1/24,20.0.0.1 tag:tag0,option:router,192.168.0.1 tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,option:tftp-server,192.168.0.3 tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,option:server-ip-address,192.168.0.2 tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,option:bootfile-name,pxelinux.0 tag:ffffffff-ffff-ffff-ffff-ffffffffffff,option:tftp-server,192.168.1.3 tag:ffffffff-ffff-ffff-ffff-ffffffffffff,option:server-ip-address,192.168.1.2 tag:ffffffff-ffff-ffff-ffff-ffffffffffff,option:bootfile-name,pxelinux2.0 tag:44444444-4444-4444-4444-444444444444,option:tftp-server,192.168.1.3 tag:44444444-4444-4444-4444-444444444444,option:server-ip-address,192.168.1.2 tag:44444444-4444-4444-4444-444444444444,option:bootfile-name,pxelinux3.0""" expected = expected.lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = dhcp.Dnsmasq(self.conf, FakeDualV4Pxe3Ports(), version=float(2.59)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected)
def test_reload_allocations(self): (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data, exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data exp_args = ['kill', '-HUP', 5] with mock.patch('os.path.isdir') as isdir: isdir.return_value = True with mock.patch.object(dhcp.Dnsmasq, 'active') as active: active.__get__ = mock.Mock(return_value=True) with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map: ip_map.return_value = {} dm.reload_allocations() self.assertTrue(ip_map.called) self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_addn_name, exp_addn_data), mock.call(exp_opt_name, exp_opt_data)]) self.execute.assert_called_once_with(exp_args, 'sudo')
def _test_spawn(self, extra_options, network=FakeDualNetwork(), max_leases=16777216): def mock_get_conf_file_name(kind, ensure_conf_dir=False): return '/dhcp/%s/%s' % (network.id, kind) def fake_argv(index): if index == 0: return '/usr/local/bin/neutron-dhcp-agent' else: raise IndexError expected = [ 'ip', 'netns', 'exec', 'qdhcp-ns', 'env', 'NEUTRON_NETWORK_ID=%s' % network.id, 'dnsmasq', '--no-hosts', '--no-resolv', '--strict-order', '--bind-interfaces', '--interface=tap0', '--except-interface=lo', '--pid-file=/dhcp/%s/pid' % network.id, '--dhcp-hostsfile=/dhcp/%s/host' % network.id, '--dhcp-optsfile=/dhcp/%s/opts' % network.id, '--leasefile-ro'] expected.extend( '--dhcp-range=set:tag%d,%s,static,86400s' % (i, s.cidr.split('/')[0]) for i, s in enumerate(network.subnets) ) expected.append('--dhcp-lease-max=%d' % max_leases) expected.extend(extra_options) self.execute.return_value = ('', '') attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['_output_opts_file', 'get_conf_file_name', 'interface_name']] ) with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks: mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name mocks['_output_opts_file'].return_value = ( '/dhcp/%s/opts' % network.id ) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') with mock.patch.object(dhcp.sys, 'argv') as argv: argv.__getitem__.side_effect = fake_argv dm = dhcp.Dnsmasq(self.conf, network, version=float(2.59)) dm.spawn_process() self.assertTrue(mocks['_output_opts_file'].called) self.execute.assert_called_once_with(expected, root_helper='sudo', check_exit_code=True)
def test_reload_allocations(self): (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data, exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data exp_args = ['kill', '-HUP', 5] fake_net = FakeDualNetwork() dm = dhcp.Dnsmasq(self.conf, fake_net, version=float(2.59)) with contextlib.nested( mock.patch('os.path.isdir', return_value=True), mock.patch.object(dhcp.Dnsmasq, 'active'), mock.patch.object(dhcp.Dnsmasq, 'pid'), mock.patch.object(dhcp.Dnsmasq, 'interface_name'), mock.patch.object(dhcp.Dnsmasq, '_make_subnet_interface_ip_map'), mock.patch.object(dm, 'device_manager') ) as (isdir, active, pid, interface_name, ip_map, device_manager): active.__get__ = mock.Mock(return_value=True) pid.__get__ = mock.Mock(return_value=5) interface_name.__get__ = mock.Mock(return_value='tap12345678-12') ip_map.return_value = {} dm.reload_allocations() self.assertTrue(ip_map.called) self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_addn_name, exp_addn_data), mock.call(exp_opt_name, exp_opt_data)]) self.execute.assert_called_once_with(exp_args, 'sudo') device_manager.update.assert_called_with(fake_net, 'tap12345678-12')
def test_release_lease(self): dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) dm.release_lease(mac_address=FakePort2.mac_address, removed_ips=[FakePort2.fixed_ips[0].ip_address]) exp_args = ['ip', 'netns', 'exec', 'qdhcp-ns', 'dhcp_release', dm.interface_name, FakePort2.fixed_ips[0].ip_address, FakePort2.mac_address] self.execute.assert_called_once_with(exp_args, root_helper='sudo', check_exit_code=True)
def test_output_opts_file_multiple_agents_with_dns_provided(self): expected = """ tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:router,192.168.0.1""".lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = dhcp.Dnsmasq(self.conf, FakeV4MultipleAgentsWithDnsProvided(), version=float(2.59)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected)
def test_make_subnet_interface_ip_map(self): with mock.patch('neutron.agent.linux.ip_lib.IPDevice') as ip_dev: ip_dev.return_value.addr.list.return_value = [{ 'cidr': '192.168.0.1/24' }] dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork()) self.assertEqual(dm._make_subnet_interface_ip_map(), {FakeV4Subnet.id: '192.168.0.1'})
def _test_spawn(self, extra_options): def mock_get_conf_file_name(kind, ensure_conf_dir=False): return '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/%s' % kind def fake_argv(index): if index == 0: return '/usr/local/bin/neutron-dhcp-agent' else: raise IndexError expected = [ 'ip', 'netns', 'exec', 'qdhcp-ns', 'env', 'NEUTRON_RELAY_SOCKET_PATH=/dhcp/lease_relay', 'NEUTRON_NETWORK_ID=cccccccc-cccc-cccc-cccc-cccccccccccc', 'dnsmasq', '--no-hosts', '--no-resolv', '--strict-order', '--bind-interfaces', '--interface=tap0', '--except-interface=lo', '--pid-file=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/pid', '--dhcp-hostsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host', '--dhcp-optsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts', ('--dhcp-script=/usr/local/bin/neutron-dhcp-agent-' 'dnsmasq-lease-update'), '--leasefile-ro', '--dhcp-range=set:tag0,192.168.0.0,static,120s', '--dhcp-range=set:tag1,fdca:3ba5:a17a:4ba3::,static,120s' ] expected.extend(extra_options) self.execute.return_value = ('', '') delegate = mock.Mock() delegate.get_interface_name.return_value = 'tap0' attrs_to_mock = dict([ (a, mock.DEFAULT) for a in ['_output_opts_file', 'get_conf_file_name', 'interface_name'] ]) with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks: mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name mocks['_output_opts_file'].return_value = ( '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts') mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') with mock.patch.object(dhcp.sys, 'argv') as argv: argv.__getitem__.side_effect = fake_argv dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), device_delegate=delegate, namespace='qdhcp-ns', version=float(2.59)) dm.spawn_process() self.assertTrue(mocks['_output_opts_file'].called) self.execute.assert_called_once_with(expected, root_helper='sudo', check_exit_code=True)
def test_output_opts_file_single_dhcp_ver2_48(self): expected = """ tag0,option:dns-server,8.8.8.8 tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 tag0,249,20.0.0.1/24,20.0.0.1 tag0,option:router,192.168.0.1""".lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkSingleDHCP(), version=float(2.48)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected)
def test_reload_allocations_stale_pid(self): exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal,' '192.168.0.2\n' '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2.' 'openstacklocal,fdca:3ba5:a17a:4ba3::2\n' '00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal,' '192.168.0.3\n' '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.' 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n' '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,' '192.168.0.1\n').lstrip() exp_host_data.replace('\n', '') exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' exp_opt_data = "tag:tag0,option:router,192.168.0.1" fake_v6 = 'gdca:3ba5:a17a:4ba3::1' fake_v6_cidr = 'gdca:3ba5:a17a:4ba3::/64' exp_opt_data = """ tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 tag:tag0,249,20.0.0.1/24,20.0.0.1 tag:tag0,option:router,192.168.0.1 tag:tag1,option:dns-server,%s tag:tag1,option:classless-static-route,%s,%s tag:tag1,249,%s,%s""".lstrip() % (fake_v6, fake_v6_cidr, fake_v6, fake_v6_cidr, fake_v6) with mock.patch('__builtin__.open') as mock_open: mock_open.return_value.__enter__ = lambda s: s mock_open.return_value.__exit__ = mock.Mock() mock_open.return_value.readline.return_value = None with mock.patch('os.path.isdir') as isdir: isdir.return_value = True with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ipmap: ipmap.return_value = {} dm.reload_allocations() self.assertTrue(ipmap.called) self.safe.assert_has_calls([ mock.call(exp_host_name, exp_host_data), mock.call(exp_opt_name, exp_opt_data) ]) mock_open.assert_called_once_with('/proc/5/cmdline', 'r')
def test_reload_allocations(self): exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal,' '192.168.0.2\n' '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2.' 'openstacklocal,fdca:3ba5:a17a:4ba3::2\n' '00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal,' '192.168.0.3\n' '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.' 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n' '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,' '192.168.0.1\n').lstrip() exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' exp_opt_data = "tag:tag0,option:router,192.168.0.1" fake_v6 = 'gdca:3ba5:a17a:4ba3::1' fake_v6_cidr = 'gdca:3ba5:a17a:4ba3::/64' exp_opt_data = """ tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1 tag:tag0,249,20.0.0.1/24,20.0.0.1 tag:tag0,option:router,192.168.0.1 tag:tag1,option:dns-server,%s tag:tag1,option:classless-static-route,%s,%s tag:tag1,249,%s,%s""".lstrip() % (fake_v6, fake_v6_cidr, fake_v6, fake_v6_cidr, fake_v6) exp_args = ['kill', '-HUP', 5] with mock.patch('os.path.isdir') as isdir: isdir.return_value = True with mock.patch.object(dhcp.Dnsmasq, 'active') as active: active.__get__ = mock.Mock(return_value=True) with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map: ip_map.return_value = {} dm.reload_allocations() self.assertTrue(ip_map.called) self.safe.assert_has_calls([ mock.call(exp_host_name, exp_host_data), mock.call(exp_opt_name, exp_opt_data) ]) self.execute.assert_called_once_with(exp_args, 'sudo')
def test_read_hosts_file_leases(self): filename = '/path/to/file' with mock.patch('os.path.exists') as mock_exists: mock_exists.return_value = True with mock.patch('__builtin__.open') as mock_open: mock_open.return_value.__enter__ = lambda s: s mock_open.return_value.__exit__ = mock.Mock() lines = ["00:00:80:aa:bb:cc,inst-name,192.168.0.1"] mock_open.return_value.readlines.return_value = lines dnsmasq = dhcp.Dnsmasq(self.conf, FakeDualNetwork()) leases = dnsmasq._read_hosts_file_leases(filename) self.assertEqual(set([("192.168.0.1", "00:00:80:aa:bb:cc")]), leases) mock_exists.assert_called_once_with(filename) mock_open.assert_called_once_with(filename)
def test_output_opts_file_no_neutron_router_on_subnet(self): expected = """ tag:tag0,option:classless-static-route,169.254.169.254/32,192.168.1.2 tag:tag0,249,169.254.169.254/32,192.168.1.2 tag:tag0,option:router,192.168.1.1""".lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = dhcp.Dnsmasq(self.conf, FakeV4NetworkNoRouter(), version=float(2.59)) with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm: ipm.return_value = {FakeV4SubnetNoRouter.id: '192.168.1.2'} dm._output_opts_file() self.assertTrue(ipm.called) self.safe.assert_called_once_with('/foo/opts', expected)
def test_release_unused_leases_one_lease(self): dnsmasq = dhcp.Dnsmasq(self.conf, FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.0.3' mac2 = '00:00:80:cc:bb:aa' old_leases = set([(ip1, mac1), (ip2, mac2)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort1()] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_has_calls([mock.call(mac2, ip2)], any_order=True)
def test_output_opts_file_gateway_route(self): fake_v6 = 'gdca:3ba5:a17a:4ba3::1' fake_v6_cidr = 'gdca:3ba5:a17a:4ba3::/64' expected = """ tag:tag0,option:dns-server,8.8.8.8 tag:tag0,option:router,10.0.0.1 tag:tag1,option:dns-server,%s tag:tag1,option:classless-static-route,%s,%s tag:tag1,249,%s,%s""".lstrip() % (fake_v6, fake_v6_cidr, fake_v6, fake_v6_cidr, fake_v6) with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkGatewayRoute(), version=float(2.59)) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected)