OpenStack Networking (Neutron)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1492 lines
60 KiB

  1. # Copyright 2012 OpenStack Foundation
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import contextlib
  16. import os
  17. import mock
  18. import netaddr
  19. from oslo.config import cfg
  20. import testtools
  21. from neutron.agent.common import config
  22. from neutron.agent.linux import dhcp
  23. from neutron.common import config as base_config
  24. from neutron.common import constants
  25. from neutron.openstack.common import log as logging
  26. from neutron.tests import base
  27. LOG = logging.getLogger(__name__)
  28. class FakeIPAllocation:
  29. def __init__(self, address, subnet_id=None):
  30. self.ip_address = address
  31. self.subnet_id = subnet_id
  32. class DhcpOpt(object):
  33. def __init__(self, **kwargs):
  34. self.__dict__.update(kwargs)
  35. def __str__(self):
  36. return str(self.__dict__)
  37. class FakePort1:
  38. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  39. admin_state_up = True
  40. device_owner = 'foo1'
  41. fixed_ips = [FakeIPAllocation('192.168.0.2',
  42. 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
  43. mac_address = '00:00:80:aa:bb:cc'
  44. def __init__(self):
  45. self.extra_dhcp_opts = []
  46. class FakePort2:
  47. id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  48. admin_state_up = False
  49. device_owner = 'foo2'
  50. fixed_ips = [FakeIPAllocation('192.168.0.3',
  51. 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
  52. mac_address = '00:00:f3:aa:bb:cc'
  53. def __init__(self):
  54. self.extra_dhcp_opts = []
  55. class FakePort3:
  56. id = '44444444-4444-4444-4444-444444444444'
  57. admin_state_up = True
  58. device_owner = 'foo3'
  59. fixed_ips = [FakeIPAllocation('192.168.0.4',
  60. 'dddddddd-dddd-dddd-dddd-dddddddddddd'),
  61. FakeIPAllocation('192.168.1.2',
  62. 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
  63. mac_address = '00:00:0f:aa:bb:cc'
  64. def __init__(self):
  65. self.extra_dhcp_opts = []
  66. class FakePort4:
  67. id = 'gggggggg-gggg-gggg-gggg-gggggggggggg'
  68. admin_state_up = False
  69. device_owner = 'foo3'
  70. fixed_ips = [FakeIPAllocation('192.168.0.4',
  71. 'ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d')]
  72. mac_address = '00:16:3E:C2:77:1D'
  73. def __init__(self):
  74. self.extra_dhcp_opts = []
  75. class FakeV6Port:
  76. id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh'
  77. admin_state_up = True
  78. device_owner = 'foo3'
  79. fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2',
  80. 'ffffffff-ffff-ffff-ffff-ffffffffffff')]
  81. mac_address = '00:00:f3:aa:bb:cc'
  82. def __init__(self):
  83. self.extra_dhcp_opts = []
  84. class FakeV6PortExtraOpt(object):
  85. id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh'
  86. admin_state_up = True
  87. device_owner = 'foo3'
  88. fixed_ips = [FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d',
  89. 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
  90. mac_address = '00:16:3e:c2:77:1d'
  91. def __init__(self):
  92. self.extra_dhcp_opts = [
  93. DhcpOpt(opt_name='dns-server',
  94. opt_value='ffea:3ba5:a17a:4ba3::100',
  95. ip_version=6)]
  96. class FakeDualPort:
  97. id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh'
  98. admin_state_up = True
  99. device_owner = 'foo3'
  100. fixed_ips = [FakeIPAllocation('192.168.0.3',
  101. 'dddddddd-dddd-dddd-dddd-dddddddddddd'),
  102. FakeIPAllocation('fdca:3ba5:a17a:4ba3::3',
  103. 'ffffffff-ffff-ffff-ffff-ffffffffffff')]
  104. mac_address = '00:00:0f:aa:bb:cc'
  105. def __init__(self):
  106. self.extra_dhcp_opts = []
  107. class FakeRouterPort:
  108. id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr'
  109. admin_state_up = True
  110. device_owner = constants.DEVICE_OWNER_ROUTER_INTF
  111. mac_address = '00:00:0f:rr:rr:rr'
  112. def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF,
  113. ip_address='192.168.0.1'):
  114. self.extra_dhcp_opts = []
  115. self.device_owner = dev_owner
  116. self.fixed_ips = [FakeIPAllocation(
  117. ip_address, 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
  118. class FakePortMultipleAgents1:
  119. id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr'
  120. admin_state_up = True
  121. device_owner = constants.DEVICE_OWNER_DHCP
  122. fixed_ips = [FakeIPAllocation('192.168.0.5',
  123. 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
  124. mac_address = '00:00:0f:dd:dd:dd'
  125. def __init__(self):
  126. self.extra_dhcp_opts = []
  127. class FakePortMultipleAgents2:
  128. id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
  129. admin_state_up = True
  130. device_owner = constants.DEVICE_OWNER_DHCP
  131. fixed_ips = [FakeIPAllocation('192.168.0.6',
  132. 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
  133. mac_address = '00:00:0f:ee:ee:ee'
  134. def __init__(self):
  135. self.extra_dhcp_opts = []
  136. class FakeV4HostRoute:
  137. destination = '20.0.0.1/24'
  138. nexthop = '20.0.0.1'
  139. class FakeV4HostRouteGateway:
  140. destination = '0.0.0.0/0'
  141. nexthop = '10.0.0.1'
  142. class FakeV6HostRoute:
  143. destination = '2001:0200:feed:7ac0::/64'
  144. nexthop = '2001:0200:feed:7ac0::1'
  145. class FakeV4Subnet:
  146. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  147. ip_version = 4
  148. cidr = '192.168.0.0/24'
  149. gateway_ip = '192.168.0.1'
  150. enable_dhcp = True
  151. host_routes = [FakeV4HostRoute]
  152. dns_nameservers = ['8.8.8.8']
  153. class FakeV4MetadataSubnet:
  154. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  155. ip_version = 4
  156. cidr = '169.254.169.254/30'
  157. gateway_ip = '169.254.169.253'
  158. enable_dhcp = True
  159. host_routes = []
  160. dns_nameservers = []
  161. class FakeV4SubnetGatewayRoute:
  162. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  163. ip_version = 4
  164. cidr = '192.168.0.0/24'
  165. gateway_ip = '192.168.0.1'
  166. enable_dhcp = True
  167. host_routes = [FakeV4HostRouteGateway]
  168. dns_nameservers = ['8.8.8.8']
  169. class FakeV4SubnetMultipleAgentsWithoutDnsProvided:
  170. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  171. ip_version = 4
  172. cidr = '192.168.0.0/24'
  173. gateway_ip = '192.168.0.1'
  174. enable_dhcp = True
  175. dns_nameservers = []
  176. host_routes = []
  177. class FakeV4MultipleAgentsWithoutDnsProvided:
  178. id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  179. subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()]
  180. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(),
  181. FakePortMultipleAgents1(), FakePortMultipleAgents2()]
  182. namespace = 'qdhcp-ns'
  183. class FakeV4SubnetMultipleAgentsWithDnsProvided:
  184. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  185. ip_version = 4
  186. cidr = '192.168.0.0/24'
  187. gateway_ip = '192.168.0.1'
  188. enable_dhcp = True
  189. dns_nameservers = ['8.8.8.8']
  190. host_routes = []
  191. class FakeV4MultipleAgentsWithDnsProvided:
  192. id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  193. subnets = [FakeV4SubnetMultipleAgentsWithDnsProvided()]
  194. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(),
  195. FakePortMultipleAgents1(), FakePortMultipleAgents2()]
  196. namespace = 'qdhcp-ns'
  197. class FakeV6Subnet:
  198. id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  199. ip_version = 6
  200. cidr = 'fdca:3ba5:a17a:4ba3::/64'
  201. gateway_ip = 'fdca:3ba5:a17a:4ba3::1'
  202. enable_dhcp = True
  203. host_routes = [FakeV6HostRoute]
  204. dns_nameservers = ['2001:0200:feed:7ac0::1']
  205. ipv6_ra_mode = None
  206. ipv6_address_mode = None
  207. class FakeV4SubnetNoDHCP:
  208. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  209. ip_version = 4
  210. cidr = '192.168.1.0/24'
  211. gateway_ip = '192.168.1.1'
  212. enable_dhcp = False
  213. host_routes = []
  214. dns_nameservers = []
  215. class FakeV6SubnetDHCPStateful:
  216. id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  217. ip_version = 6
  218. cidr = 'fdca:3ba5:a17a:4ba3::/64'
  219. gateway_ip = 'fdca:3ba5:a17a:4ba3::1'
  220. enable_dhcp = True
  221. host_routes = [FakeV6HostRoute]
  222. dns_nameservers = ['2001:0200:feed:7ac0::1']
  223. ipv6_ra_mode = None
  224. ipv6_address_mode = constants.DHCPV6_STATEFUL
  225. class FakeV6SubnetSlaac:
  226. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  227. ip_version = 6
  228. cidr = 'ffda:3ba5:a17a:4ba3::/64'
  229. gateway_ip = 'ffda:3ba5:a17a:4ba3::1'
  230. enable_dhcp = True
  231. host_routes = [FakeV6HostRoute]
  232. ipv6_address_mode = constants.IPV6_SLAAC
  233. ipv6_ra_mode = None
  234. class FakeV6SubnetStateless(object):
  235. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  236. ip_version = 6
  237. cidr = 'ffea:3ba5:a17a:4ba3::/64'
  238. gateway_ip = 'ffea:3ba5:a17a:4ba3::1'
  239. enable_dhcp = True
  240. dns_nameservers = []
  241. host_routes = []
  242. ipv6_address_mode = constants.DHCPV6_STATELESS
  243. ipv6_ra_mode = None
  244. class FakeV4SubnetNoGateway:
  245. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  246. ip_version = 4
  247. cidr = '192.168.1.0/24'
  248. gateway_ip = None
  249. enable_dhcp = True
  250. host_routes = []
  251. dns_nameservers = []
  252. class FakeV4SubnetNoRouter:
  253. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  254. ip_version = 4
  255. cidr = '192.168.1.0/24'
  256. gateway_ip = '192.168.1.1'
  257. enable_dhcp = True
  258. host_routes = []
  259. dns_nameservers = []
  260. class FakeV4Network:
  261. id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
  262. subnets = [FakeV4Subnet()]
  263. ports = [FakePort1()]
  264. namespace = 'qdhcp-ns'
  265. class FakeV6Network:
  266. id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'
  267. subnets = [FakeV6Subnet()]
  268. ports = [FakePort2()]
  269. namespace = 'qdhcp-ns'
  270. class FakeDualNetwork:
  271. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  272. subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()]
  273. ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort()]
  274. namespace = 'qdhcp-ns'
  275. class FakeDualNetworkGatewayRoute:
  276. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  277. subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()]
  278. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
  279. namespace = 'qdhcp-ns'
  280. class FakeDualNetworkSingleDHCP:
  281. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  282. subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()]
  283. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
  284. namespace = 'qdhcp-ns'
  285. class FakeV4NoGatewayNetwork:
  286. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  287. subnets = [FakeV4SubnetNoGateway()]
  288. ports = [FakePort1()]
  289. class FakeV4NetworkNoRouter:
  290. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  291. subnets = [FakeV4SubnetNoRouter()]
  292. ports = [FakePort1()]
  293. class FakeV4MetadataNetwork:
  294. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  295. subnets = [FakeV4MetadataSubnet()]
  296. ports = [FakeRouterPort(ip_address='169.254.169.253')]
  297. class FakeV4NetworkDistRouter:
  298. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  299. subnets = [FakeV4Subnet()]
  300. ports = [FakePort1(),
  301. FakeRouterPort(dev_owner=constants.DEVICE_OWNER_DVR_INTERFACE)]
  302. class FakeDualV4Pxe3Ports:
  303. id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
  304. subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()]
  305. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
  306. namespace = 'qdhcp-ns'
  307. def __init__(self, port_detail="portsSame"):
  308. if port_detail == "portsSame":
  309. self.ports[0].extra_dhcp_opts = [
  310. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  311. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  312. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  313. self.ports[1].extra_dhcp_opts = [
  314. DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'),
  315. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'),
  316. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')]
  317. self.ports[2].extra_dhcp_opts = [
  318. DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'),
  319. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'),
  320. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')]
  321. else:
  322. self.ports[0].extra_dhcp_opts = [
  323. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.2'),
  324. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  325. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  326. self.ports[1].extra_dhcp_opts = [
  327. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'),
  328. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'),
  329. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')]
  330. self.ports[2].extra_dhcp_opts = [
  331. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'),
  332. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'),
  333. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')]
  334. class FakeV4NetworkPxe2Ports:
  335. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  336. subnets = [FakeV4Subnet()]
  337. ports = [FakePort1(), FakePort2(), FakeRouterPort()]
  338. namespace = 'qdhcp-ns'
  339. def __init__(self, port_detail="portsSame"):
  340. if port_detail == "portsSame":
  341. self.ports[0].extra_dhcp_opts = [
  342. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  343. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  344. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  345. self.ports[1].extra_dhcp_opts = [
  346. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  347. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  348. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  349. else:
  350. self.ports[0].extra_dhcp_opts = [
  351. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  352. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  353. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  354. self.ports[1].extra_dhcp_opts = [
  355. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'),
  356. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'),
  357. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  358. class FakeV4NetworkPxe3Ports:
  359. id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
  360. subnets = [FakeV4Subnet()]
  361. ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
  362. namespace = 'qdhcp-ns'
  363. def __init__(self, port_detail="portsSame"):
  364. if port_detail == "portsSame":
  365. self.ports[0].extra_dhcp_opts = [
  366. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  367. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  368. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  369. self.ports[1].extra_dhcp_opts = [
  370. DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'),
  371. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'),
  372. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  373. self.ports[2].extra_dhcp_opts = [
  374. DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'),
  375. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'),
  376. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  377. else:
  378. self.ports[0].extra_dhcp_opts = [
  379. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'),
  380. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'),
  381. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')]
  382. self.ports[1].extra_dhcp_opts = [
  383. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'),
  384. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'),
  385. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')]
  386. self.ports[2].extra_dhcp_opts = [
  387. DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'),
  388. DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'),
  389. DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')]
  390. class FakeDualStackNetworkSingleDHCP:
  391. id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
  392. subnets = [FakeV4Subnet(), FakeV6SubnetSlaac()]
  393. ports = [FakePort1(), FakePort4(), FakeRouterPort()]
  394. class FakeV6NetworkStatelessDHCP(object):
  395. id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'
  396. subnets = [FakeV6SubnetStateless()]
  397. ports = [FakeV6PortExtraOpt()]
  398. namespace = 'qdhcp-ns'
  399. class LocalChild(dhcp.DhcpLocalProcess):
  400. PORTS = {4: [4], 6: [6]}
  401. def __init__(self, *args, **kwargs):
  402. super(LocalChild, self).__init__(*args, **kwargs)
  403. self.called = []
  404. def reload_allocations(self):
  405. self.called.append('reload')
  406. def restart(self):
  407. self.called.append('restart')
  408. def spawn_process(self):
  409. self.called.append('spawn')
  410. class TestBase(base.BaseTestCase):
  411. def setUp(self):
  412. super(TestBase, self).setUp()
  413. self.conf = config.setup_conf()
  414. self.conf.register_opts(base_config.core_opts)
  415. self.conf.register_opts(dhcp.OPTS)
  416. config.register_interface_driver_opts_helper(self.conf)
  417. config.register_use_namespaces_opts_helper(self.conf)
  418. instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager")
  419. self.mock_mgr = instance.start()
  420. self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata',
  421. default=True))
  422. self.conf.register_opt(cfg.BoolOpt('enable_metadata_network',
  423. default=False))
  424. self.config_parse(self.conf)
  425. self.conf.set_override('state_path', '')
  426. self.replace_p = mock.patch('neutron.agent.linux.utils.replace_file')
  427. self.execute_p = mock.patch('neutron.agent.linux.utils.execute')
  428. self.safe = self.replace_p.start()
  429. self.execute = self.execute_p.start()
  430. class TestDhcpBase(TestBase):
  431. def test_existing_dhcp_networks_abstract_error(self):
  432. self.assertRaises(NotImplementedError,
  433. dhcp.DhcpBase.existing_dhcp_networks,
  434. None, None)
  435. def test_check_version_abstract_error(self):
  436. self.assertRaises(NotImplementedError,
  437. dhcp.DhcpBase.check_version)
  438. def test_base_abc_error(self):
  439. self.assertRaises(TypeError, dhcp.DhcpBase, None)
  440. def test_restart(self):
  441. class SubClass(dhcp.DhcpBase):
  442. def __init__(self):
  443. dhcp.DhcpBase.__init__(self, cfg.CONF, FakeV4Network(), None)
  444. self.called = []
  445. def enable(self):
  446. self.called.append('enable')
  447. def disable(self, retain_port=False):
  448. self.called.append('disable %s' % retain_port)
  449. def reload_allocations(self):
  450. pass
  451. @property
  452. def active(self):
  453. return True
  454. c = SubClass()
  455. c.restart()
  456. self.assertEqual(c.called, ['disable True', 'enable'])
  457. class TestDhcpLocalProcess(TestBase):
  458. def test_active(self):
  459. with mock.patch('__builtin__.open') as mock_open:
  460. mock_open.return_value.__enter__ = lambda s: s
  461. mock_open.return_value.__exit__ = mock.Mock()
  462. mock_open.return_value.readline.return_value = \
  463. 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
  464. with mock.patch.object(LocalChild, 'pid') as pid:
  465. pid.__get__ = mock.Mock(return_value=4)
  466. lp = LocalChild(self.conf, FakeV4Network())
  467. self.assertTrue(lp.active)
  468. mock_open.assert_called_once_with('/proc/4/cmdline', 'r')
  469. def test_active_none(self):
  470. dummy_cmd_line = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
  471. self.execute.return_value = (dummy_cmd_line, '')
  472. with mock.patch.object(LocalChild, 'pid') as pid:
  473. pid.__get__ = mock.Mock(return_value=None)
  474. lp = LocalChild(self.conf, FakeV4Network())
  475. self.assertFalse(lp.active)
  476. def test_active_cmd_mismatch(self):
  477. with mock.patch('__builtin__.open') as mock_open:
  478. mock_open.return_value.__enter__ = lambda s: s
  479. mock_open.return_value.__exit__ = mock.Mock()
  480. mock_open.return_value.readline.return_value = \
  481. 'bbbbbbbb-bbbb-bbbb-aaaa-aaaaaaaaaaaa'
  482. with mock.patch.object(LocalChild, 'pid') as pid:
  483. pid.__get__ = mock.Mock(return_value=4)
  484. lp = LocalChild(self.conf, FakeV4Network())
  485. self.assertFalse(lp.active)
  486. mock_open.assert_called_once_with('/proc/4/cmdline', 'r')
  487. def test_get_conf_file_name(self):
  488. tpl = '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dev'
  489. with mock.patch('os.path.isdir') as isdir:
  490. isdir.return_value = False
  491. with mock.patch('os.makedirs') as makedirs:
  492. lp = LocalChild(self.conf, FakeV4Network())
  493. self.assertEqual(lp.get_conf_file_name('dev'), tpl)
  494. self.assertFalse(makedirs.called)
  495. def test_get_conf_file_name_ensure_dir(self):
  496. tpl = '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dev'
  497. with mock.patch('os.path.isdir') as isdir:
  498. isdir.return_value = False
  499. with mock.patch('os.makedirs') as makedirs:
  500. lp = LocalChild(self.conf, FakeV4Network())
  501. self.assertEqual(lp.get_conf_file_name('dev', True), tpl)
  502. self.assertTrue(makedirs.called)
  503. def test_enable_already_active(self):
  504. with mock.patch.object(LocalChild, 'active') as patched:
  505. patched.__get__ = mock.Mock(return_value=True)
  506. lp = LocalChild(self.conf, FakeV4Network())
  507. lp.enable()
  508. self.assertEqual(lp.called, ['restart'])
  509. self.assertFalse(self.mock_mgr.return_value.setup.called)
  510. def test_enable(self):
  511. attrs_to_mock = dict(
  512. [(a, mock.DEFAULT) for a in
  513. ['active', 'get_conf_file_name', 'interface_name']]
  514. )
  515. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  516. mocks['active'].__get__ = mock.Mock(return_value=False)
  517. mocks['get_conf_file_name'].return_value = '/dir'
  518. mocks['interface_name'].__set__ = mock.Mock()
  519. lp = LocalChild(self.conf,
  520. FakeDualNetwork())
  521. lp.enable()
  522. self.mock_mgr.assert_has_calls(
  523. [mock.call(self.conf, 'sudo', None),
  524. mock.call().setup(mock.ANY)])
  525. self.assertEqual(lp.called, ['spawn'])
  526. self.assertTrue(mocks['interface_name'].__set__.called)
  527. def test_disable_not_active(self):
  528. attrs_to_mock = dict([(a, mock.DEFAULT) for a in
  529. ['active', 'interface_name', 'pid']])
  530. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  531. mocks['active'].__get__ = mock.Mock(return_value=False)
  532. mocks['pid'].__get__ = mock.Mock(return_value=5)
  533. mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
  534. with mock.patch.object(dhcp.LOG, 'debug') as log:
  535. network = FakeDualNetwork()
  536. lp = LocalChild(self.conf, network)
  537. lp.device_manager = mock.Mock()
  538. lp.disable()
  539. msg = log.call_args[0][0]
  540. self.assertIn('does not exist', msg)
  541. lp.device_manager.destroy.assert_called_once_with(
  542. network, 'tap0')
  543. def test_disable_unknown_network(self):
  544. attrs_to_mock = dict([(a, mock.DEFAULT) for a in
  545. ['active', 'interface_name', 'pid']])
  546. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  547. mocks['active'].__get__ = mock.Mock(return_value=False)
  548. mocks['pid'].__get__ = mock.Mock(return_value=None)
  549. mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
  550. with mock.patch.object(dhcp.LOG, 'debug') as log:
  551. lp = LocalChild(self.conf, FakeDualNetwork())
  552. lp.disable()
  553. msg = log.call_args[0][0]
  554. self.assertIn('No DHCP', msg)
  555. def test_disable_retain_port(self):
  556. attrs_to_mock = dict([(a, mock.DEFAULT) for a in
  557. ['active', 'interface_name', 'pid']])
  558. network = FakeDualNetwork()
  559. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  560. mocks['active'].__get__ = mock.Mock(return_value=True)
  561. mocks['pid'].__get__ = mock.Mock(return_value=5)
  562. mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
  563. lp = LocalChild(self.conf, network)
  564. lp.disable(retain_port=True)
  565. exp_args = ['kill', '-9', 5]
  566. self.execute.assert_called_once_with(exp_args, 'sudo')
  567. def test_disable(self):
  568. attrs_to_mock = dict([(a, mock.DEFAULT) for a in
  569. ['active', 'interface_name', 'pid']])
  570. network = FakeDualNetwork()
  571. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  572. mocks['active'].__get__ = mock.Mock(return_value=True)
  573. mocks['pid'].__get__ = mock.Mock(return_value=5)
  574. mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
  575. lp = LocalChild(self.conf, network)
  576. with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip:
  577. lp.disable()
  578. self.mock_mgr.assert_has_calls([mock.call(self.conf, 'sudo', None),
  579. mock.call().destroy(network, 'tap0')])
  580. exp_args = ['kill', '-9', 5]
  581. self.execute.assert_called_once_with(exp_args, 'sudo')
  582. self.assertEqual(ip.return_value.netns.delete.call_count, 0)
  583. def test_disable_delete_ns(self):
  584. self.conf.set_override('dhcp_delete_namespaces', True)
  585. attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['active', 'pid']])
  586. with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
  587. mocks['active'].__get__ = mock.Mock(return_value=False)
  588. mocks['pid'].__get__ = mock.Mock(return_value=False)
  589. lp = LocalChild(self.conf, FakeDualNetwork())
  590. with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip:
  591. lp.disable()
  592. ip.return_value.netns.delete.assert_called_with('qdhcp-ns')
  593. def test_pid(self):
  594. with mock.patch('__builtin__.open') as mock_open:
  595. mock_open.return_value.__enter__ = lambda s: s
  596. mock_open.return_value.__exit__ = mock.Mock()
  597. mock_open.return_value.read.return_value = '5'
  598. lp = LocalChild(self.conf, FakeDualNetwork())
  599. self.assertEqual(lp.pid, 5)
  600. def test_pid_no_an_int(self):
  601. with mock.patch('__builtin__.open') as mock_open:
  602. mock_open.return_value.__enter__ = lambda s: s
  603. mock_open.return_value.__exit__ = mock.Mock()
  604. mock_open.return_value.read.return_value = 'foo'
  605. lp = LocalChild(self.conf, FakeDualNetwork())
  606. self.assertIsNone(lp.pid)
  607. def test_pid_invalid_file(self):
  608. with mock.patch.object(LocalChild, 'get_conf_file_name') as conf_file:
  609. conf_file.return_value = '.doesnotexist/pid'
  610. lp = LocalChild(self.conf, FakeDualNetwork())
  611. self.assertIsNone(lp.pid)
  612. def test_get_interface_name(self):
  613. with mock.patch('__builtin__.open') as mock_open:
  614. mock_open.return_value.__enter__ = lambda s: s
  615. mock_open.return_value.__exit__ = mock.Mock()
  616. mock_open.return_value.read.return_value = 'tap0'
  617. lp = LocalChild(self.conf, FakeDualNetwork())
  618. self.assertEqual(lp.interface_name, 'tap0')
  619. def test_set_interface_name(self):
  620. with mock.patch('neutron.agent.linux.utils.replace_file') as replace:
  621. lp = LocalChild(self.conf, FakeDualNetwork())
  622. with mock.patch.object(lp, 'get_conf_file_name') as conf_file:
  623. conf_file.return_value = '/interface'
  624. lp.interface_name = 'tap0'
  625. conf_file.assert_called_once_with('interface',
  626. ensure_conf_dir=True)
  627. replace.assert_called_once_with(mock.ANY, 'tap0')
  628. class TestDnsmasq(TestBase):
  629. def _test_spawn(self, extra_options, network=FakeDualNetwork(),
  630. max_leases=16777216, lease_duration=86400,
  631. has_static=True):
  632. def mock_get_conf_file_name(kind, ensure_conf_dir=False):
  633. return '/dhcp/%s/%s' % (network.id, kind)
  634. def fake_argv(index):
  635. if index == 0:
  636. return '/usr/local/bin/neutron-dhcp-agent'
  637. else:
  638. raise IndexError()
  639. expected = [
  640. 'ip',
  641. 'netns',
  642. 'exec',
  643. 'qdhcp-ns',
  644. 'env',
  645. 'NEUTRON_NETWORK_ID=%s' % network.id,
  646. 'dnsmasq',
  647. '--no-hosts',
  648. '--no-resolv',
  649. '--strict-order',
  650. '--bind-interfaces',
  651. '--interface=tap0',
  652. '--except-interface=lo',
  653. '--pid-file=/dhcp/%s/pid' % network.id,
  654. '--dhcp-hostsfile=/dhcp/%s/host' % network.id,
  655. '--addn-hosts=/dhcp/%s/addn_hosts' % network.id,
  656. '--dhcp-optsfile=/dhcp/%s/opts' % network.id,
  657. '--leasefile-ro',
  658. '--dhcp-authoritative']
  659. seconds = ''
  660. if lease_duration == -1:
  661. lease_duration = 'infinite'
  662. else:
  663. seconds = 's'
  664. if has_static:
  665. prefix = '--dhcp-range=set:tag%d,%s,static,%s%s'
  666. prefix6 = '--dhcp-range=set:tag%d,%s,static,%s,%s%s'
  667. else:
  668. prefix = '--dhcp-range=set:tag%d,%s,%s%s'
  669. prefix6 = '--dhcp-range=set:tag%d,%s,%s,%s%s'
  670. possible_leases = 0
  671. for i, s in enumerate(network.subnets):
  672. if (s.ip_version != 6
  673. or s.ipv6_address_mode == constants.DHCPV6_STATEFUL):
  674. if s.ip_version == 4:
  675. expected.extend([prefix % (
  676. i, s.cidr.split('/')[0], lease_duration, seconds)])
  677. else:
  678. expected.extend([prefix6 % (
  679. i, s.cidr.split('/')[0], s.cidr.split('/')[1],
  680. lease_duration, seconds)])
  681. possible_leases += netaddr.IPNetwork(s.cidr).size
  682. expected.append('--dhcp-lease-max=%d' % min(
  683. possible_leases, max_leases))
  684. expected.extend(extra_options)
  685. self.execute.return_value = ('', '')
  686. attrs_to_mock = dict(
  687. [(a, mock.DEFAULT) for a in
  688. ['_output_opts_file', 'get_conf_file_name', 'interface_name']]
  689. )
  690. with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks:
  691. mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name
  692. mocks['_output_opts_file'].return_value = (
  693. '/dhcp/%s/opts' % network.id
  694. )
  695. mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
  696. with mock.patch.object(dhcp.sys, 'argv') as argv:
  697. argv.__getitem__.side_effect = fake_argv
  698. dm = dhcp.Dnsmasq(self.conf, network,
  699. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  700. dm.spawn_process()
  701. self.assertTrue(mocks['_output_opts_file'].called)
  702. self.execute.assert_called_once_with(expected,
  703. root_helper='sudo',
  704. check_exit_code=True,
  705. extra_ok_codes=None)
  706. def test_spawn(self):
  707. self._test_spawn(['--conf-file=', '--domain=openstacklocal'])
  708. def test_spawn_infinite_lease_duration(self):
  709. self.conf.set_override('dhcp_lease_duration', -1)
  710. self._test_spawn(['--conf-file=', '--domain=openstacklocal'],
  711. FakeDualNetwork(), 16777216, -1)
  712. def test_spawn_cfg_config_file(self):
  713. self.conf.set_override('dnsmasq_config_file', '/foo')
  714. self._test_spawn(['--conf-file=/foo', '--domain=openstacklocal'])
  715. def test_spawn_no_dhcp_domain(self):
  716. (exp_host_name, exp_host_data,
  717. exp_addn_name, exp_addn_data) = self._test_no_dhcp_domain_alloc_data
  718. self.conf.set_override('dhcp_domain', '')
  719. self._test_spawn(['--conf-file='])
  720. self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
  721. mock.call(exp_addn_name, exp_addn_data)])
  722. def test_spawn_no_dhcp_range(self):
  723. network = FakeV6Network()
  724. subnet = FakeV6SubnetSlaac()
  725. network.subnets = [subnet]
  726. self._test_spawn(['--conf-file=', '--domain=openstacklocal'],
  727. network, has_static=False)
  728. def test_spawn_cfg_dns_server(self):
  729. self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8'])
  730. self._test_spawn(['--conf-file=',
  731. '--server=8.8.8.8',
  732. '--domain=openstacklocal'])
  733. def test_spawn_cfg_multiple_dns_server(self):
  734. self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8',
  735. '9.9.9.9'])
  736. self._test_spawn(['--conf-file=',
  737. '--server=8.8.8.8',
  738. '--server=9.9.9.9',
  739. '--domain=openstacklocal'])
  740. def test_spawn_max_leases_is_smaller_than_cap(self):
  741. self._test_spawn(
  742. ['--conf-file=', '--domain=openstacklocal'],
  743. network=FakeV4Network(),
  744. max_leases=256)
  745. def test_output_opts_file(self):
  746. fake_v6 = '2001:0200:feed:7ac0::1'
  747. expected = (
  748. 'tag:tag0,option:dns-server,8.8.8.8\n'
  749. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  750. '0.0.0.0/0,192.168.0.1\n'
  751. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  752. 'tag:tag0,option:router,192.168.0.1\n'
  753. 'tag:tag1,option6:dns-server,%s\n'
  754. 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
  755. '[' + fake_v6 + ']')
  756. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  757. conf_fn.return_value = '/foo/opts'
  758. dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(),
  759. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  760. dm._output_opts_file()
  761. self.safe.assert_called_once_with('/foo/opts', expected)
  762. def test_output_opts_file_gateway_route(self):
  763. fake_v6 = '2001:0200:feed:7ac0::1'
  764. expected = """
  765. tag:tag0,option:dns-server,8.8.8.8
  766. tag:tag0,option:router,192.168.0.1
  767. tag:tag1,option6:dns-server,%s
  768. tag:tag1,option6:domain-search,openstacklocal""".lstrip() % (
  769. '[' + fake_v6 + ']')
  770. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  771. conf_fn.return_value = '/foo/opts'
  772. dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkGatewayRoute(),
  773. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  774. dm._output_opts_file()
  775. self.safe.assert_called_once_with('/foo/opts', expected)
  776. def test_output_opts_file_multiple_agents_without_dns_provided(self):
  777. expected = """
  778. tag:tag0,option:router,192.168.0.1
  779. tag:tag0,option:dns-server,192.168.0.5,192.168.0.6""".lstrip()
  780. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  781. conf_fn.return_value = '/foo/opts'
  782. dm = dhcp.Dnsmasq(self.conf,
  783. FakeV4MultipleAgentsWithoutDnsProvided(),
  784. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  785. dm._output_opts_file()
  786. self.safe.assert_called_once_with('/foo/opts', expected)
  787. def test_output_opts_file_multiple_agents_with_dns_provided(self):
  788. expected = """
  789. tag:tag0,option:dns-server,8.8.8.8
  790. tag:tag0,option:router,192.168.0.1""".lstrip()
  791. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  792. conf_fn.return_value = '/foo/opts'
  793. dm = dhcp.Dnsmasq(self.conf,
  794. FakeV4MultipleAgentsWithDnsProvided(),
  795. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  796. dm._output_opts_file()
  797. self.safe.assert_called_once_with('/foo/opts', expected)
  798. def test_output_opts_file_single_dhcp(self):
  799. expected = (
  800. 'tag:tag0,option:dns-server,8.8.8.8\n'
  801. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  802. '0.0.0.0/0,192.168.0.1\n'
  803. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  804. 'tag:tag0,option:router,192.168.0.1').lstrip()
  805. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  806. conf_fn.return_value = '/foo/opts'
  807. dm = dhcp.Dnsmasq(self.conf, FakeDualNetworkSingleDHCP(),
  808. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  809. dm._output_opts_file()
  810. self.safe.assert_called_once_with('/foo/opts', expected)
  811. def test_output_opts_file_no_gateway(self):
  812. expected = """
  813. tag:tag0,option:classless-static-route,169.254.169.254/32,192.168.1.1
  814. tag:tag0,249,169.254.169.254/32,192.168.1.1
  815. tag:tag0,option:router""".lstrip()
  816. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  817. conf_fn.return_value = '/foo/opts'
  818. dm = dhcp.Dnsmasq(self.conf, FakeV4NoGatewayNetwork(),
  819. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  820. with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm:
  821. ipm.return_value = {FakeV4SubnetNoGateway.id: '192.168.1.1'}
  822. dm._output_opts_file()
  823. self.assertTrue(ipm.called)
  824. self.safe.assert_called_once_with('/foo/opts', expected)
  825. def test_output_opts_file_no_neutron_router_on_subnet(self):
  826. expected = (
  827. 'tag:tag0,option:classless-static-route,'
  828. '169.254.169.254/32,192.168.1.2,0.0.0.0/0,192.168.1.1\n'
  829. 'tag:tag0,249,169.254.169.254/32,192.168.1.2,'
  830. '0.0.0.0/0,192.168.1.1\n'
  831. 'tag:tag0,option:router,192.168.1.1').lstrip()
  832. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  833. conf_fn.return_value = '/foo/opts'
  834. dm = dhcp.Dnsmasq(self.conf, FakeV4NetworkNoRouter(),
  835. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  836. with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm:
  837. ipm.return_value = {FakeV4SubnetNoRouter.id: '192.168.1.2'}
  838. dm._output_opts_file()
  839. self.assertTrue(ipm.called)
  840. self.safe.assert_called_once_with('/foo/opts', expected)
  841. def test_output_opts_file_dist_neutron_router_on_subnet(self):
  842. expected = (
  843. 'tag:tag0,option:dns-server,8.8.8.8\n'
  844. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  845. '0.0.0.0/0,192.168.0.1\n'
  846. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  847. 'tag:tag0,option:router,192.168.0.1').lstrip()
  848. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  849. conf_fn.return_value = '/foo/opts'
  850. dm = dhcp.Dnsmasq(self.conf, FakeV4NetworkDistRouter(),
  851. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  852. with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm:
  853. ipm.return_value = {FakeV4Subnet.id: '192.168.0.1'}
  854. dm._output_opts_file()
  855. self.assertTrue(ipm.called)
  856. self.safe.assert_called_once_with('/foo/opts', expected)
  857. def test_output_opts_file_pxe_2port_1net(self):
  858. expected = (
  859. 'tag:tag0,option:dns-server,8.8.8.8\n'
  860. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  861. '0.0.0.0/0,192.168.0.1\n'
  862. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  863. 'tag:tag0,option:router,192.168.0.1\n'
  864. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  865. 'option:tftp-server,192.168.0.3\n'
  866. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  867. 'option:server-ip-address,192.168.0.2\n'
  868. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  869. 'option:bootfile-name,pxelinux.0\n'
  870. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  871. 'option:tftp-server,192.168.0.3\n'
  872. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  873. 'option:server-ip-address,192.168.0.2\n'
  874. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  875. 'option:bootfile-name,pxelinux.0')
  876. expected = expected.lstrip()
  877. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  878. conf_fn.return_value = '/foo/opts'
  879. fp = FakeV4NetworkPxe2Ports()
  880. dm = dhcp.Dnsmasq(self.conf, fp,
  881. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  882. dm._output_opts_file()
  883. self.safe.assert_called_once_with('/foo/opts', expected)
  884. def test_output_opts_file_pxe_2port_1net_diff_details(self):
  885. expected = (
  886. 'tag:tag0,option:dns-server,8.8.8.8\n'
  887. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  888. '0.0.0.0/0,192.168.0.1\n'
  889. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  890. 'tag:tag0,option:router,192.168.0.1\n'
  891. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  892. 'option:tftp-server,192.168.0.3\n'
  893. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  894. 'option:server-ip-address,192.168.0.2\n'
  895. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  896. 'option:bootfile-name,pxelinux.0\n'
  897. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  898. 'option:tftp-server,192.168.0.5\n'
  899. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  900. 'option:server-ip-address,192.168.0.5\n'
  901. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  902. 'option:bootfile-name,pxelinux.0')
  903. expected = expected.lstrip()
  904. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  905. conf_fn.return_value = '/foo/opts'
  906. dm = dhcp.Dnsmasq(self.conf, FakeV4NetworkPxe2Ports("portsDiff"),
  907. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  908. dm._output_opts_file()
  909. self.safe.assert_called_once_with('/foo/opts', expected)
  910. def test_output_opts_file_pxe_3port_2net(self):
  911. expected = (
  912. 'tag:tag0,option:dns-server,8.8.8.8\n'
  913. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  914. '0.0.0.0/0,192.168.0.1\n'
  915. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  916. 'tag:tag0,option:router,192.168.0.1\n'
  917. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  918. 'option:tftp-server,192.168.0.3\n'
  919. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  920. 'option:server-ip-address,192.168.0.2\n'
  921. 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,'
  922. 'option:bootfile-name,pxelinux.0\n'
  923. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  924. 'option:tftp-server,192.168.1.3\n'
  925. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  926. 'option:server-ip-address,192.168.1.2\n'
  927. 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,'
  928. 'option:bootfile-name,pxelinux2.0\n'
  929. 'tag:44444444-4444-4444-4444-444444444444,'
  930. 'option:tftp-server,192.168.1.3\n'
  931. 'tag:44444444-4444-4444-4444-444444444444,'
  932. 'option:server-ip-address,192.168.1.2\n'
  933. 'tag:44444444-4444-4444-4444-444444444444,'
  934. 'option:bootfile-name,pxelinux3.0')
  935. expected = expected.lstrip()
  936. with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
  937. conf_fn.return_value = '/foo/opts'
  938. dm = dhcp.Dnsmasq(self.conf, FakeDualV4Pxe3Ports(),
  939. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  940. dm._output_opts_file()
  941. self.safe.assert_called_once_with('/foo/opts', expected)
  942. @property
  943. def _test_no_dhcp_domain_alloc_data(self):
  944. exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host'
  945. exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2,'
  946. '192.168.0.2\n'
  947. '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2,'
  948. '[fdca:3ba5:a17a:4ba3::2]\n'
  949. '00:00:0f:aa:bb:cc,host-192-168-0-3,'
  950. '192.168.0.3\n'
  951. '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3,'
  952. '[fdca:3ba5:a17a:4ba3::3]\n'
  953. '00:00:0f:rr:rr:rr,host-192-168-0-1,'
  954. '192.168.0.1\n').lstrip()
  955. exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts'
  956. exp_addn_data = (
  957. '192.168.0.2\t'
  958. 'host-192-168-0-2 host-192-168-0-2\n'
  959. 'fdca:3ba5:a17a:4ba3::2\t'
  960. 'host-fdca-3ba5-a17a-4ba3--2 '
  961. 'host-fdca-3ba5-a17a-4ba3--2\n'
  962. '192.168.0.3\thost-192-168-0-3 '
  963. 'host-192-168-0-3\n'
  964. 'fdca:3ba5:a17a:4ba3::3\t'
  965. 'host-fdca-3ba5-a17a-4ba3--3 '
  966. 'host-fdca-3ba5-a17a-4ba3--3\n'
  967. '192.168.0.1\t'
  968. 'host-192-168-0-1 '
  969. 'host-192-168-0-1\n'
  970. ).lstrip()
  971. return (exp_host_name, exp_host_data,
  972. exp_addn_name, exp_addn_data)
  973. @property
  974. def _test_reload_allocation_data(self):
  975. exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host'
  976. exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal,'
  977. '192.168.0.2\n'
  978. '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2.'
  979. 'openstacklocal,[fdca:3ba5:a17a:4ba3::2]\n'
  980. '00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal,'
  981. '192.168.0.3\n'
  982. '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.'
  983. 'openstacklocal,[fdca:3ba5:a17a:4ba3::3]\n'
  984. '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,'
  985. '192.168.0.1\n').lstrip()
  986. exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts'
  987. exp_addn_data = (
  988. '192.168.0.2\t'
  989. 'host-192-168-0-2.openstacklocal host-192-168-0-2\n'
  990. 'fdca:3ba5:a17a:4ba3::2\t'
  991. 'host-fdca-3ba5-a17a-4ba3--2.openstacklocal '
  992. 'host-fdca-3ba5-a17a-4ba3--2\n'
  993. '192.168.0.3\thost-192-168-0-3.openstacklocal '
  994. 'host-192-168-0-3\n'
  995. 'fdca:3ba5:a17a:4ba3::3\t'
  996. 'host-fdca-3ba5-a17a-4ba3--3.openstacklocal '
  997. 'host-fdca-3ba5-a17a-4ba3--3\n'
  998. '192.168.0.1\t'
  999. 'host-192-168-0-1.openstacklocal '
  1000. 'host-192-168-0-1\n'
  1001. ).lstrip()
  1002. exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
  1003. fake_v6 = '2001:0200:feed:7ac0::1'
  1004. exp_opt_data = (
  1005. 'tag:tag0,option:dns-server,8.8.8.8\n'
  1006. 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,'
  1007. '0.0.0.0/0,192.168.0.1\n'
  1008. 'tag:tag0,249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n'
  1009. 'tag:tag0,option:router,192.168.0.1\n'
  1010. 'tag:tag1,option6:dns-server,%s\n'
  1011. 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % (
  1012. '[' + fake_v6 + ']')
  1013. return (exp_host_name, exp_host_data,
  1014. exp_addn_name, exp_addn_data,
  1015. exp_opt_name, exp_opt_data,)
  1016. def test_reload_allocations(self):
  1017. (exp_host_name, exp_host_data,
  1018. exp_addn_name, exp_addn_data,
  1019. exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data
  1020. exp_args = ['kill', '-HUP', 5]
  1021. fake_net = FakeDualNetwork()
  1022. dm = dhcp.Dnsmasq(self.conf, fake_net,
  1023. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  1024. with contextlib.nested(
  1025. mock.patch('os.path.isdir', return_value=True),
  1026. mock.patch.object(dhcp.Dnsmasq, 'active'),
  1027. mock.patch.object(dhcp.Dnsmasq, 'pid'),
  1028. mock.patch.object(dhcp.Dnsmasq, 'interface_name'),
  1029. mock.patch.object(dhcp.Dnsmasq, '_make_subnet_interface_ip_map'),
  1030. mock.patch.object(dm, 'device_manager')
  1031. ) as (isdir, active, pid, interface_name, ip_map, device_manager):
  1032. active.__get__ = mock.Mock(return_value=True)
  1033. pid.__get__ = mock.Mock(return_value=5)
  1034. interface_name.__get__ = mock.Mock(return_value='tap12345678-12')
  1035. ip_map.return_value = {}
  1036. dm.reload_allocations()
  1037. self.assertTrue(ip_map.called)
  1038. self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
  1039. mock.call(exp_addn_name, exp_addn_data),
  1040. mock.call(exp_opt_name, exp_opt_data)])
  1041. self.execute.assert_called_once_with(exp_args, 'sudo')
  1042. device_manager.update.assert_called_with(fake_net, 'tap12345678-12')
  1043. def test_reload_allocations_stale_pid(self):
  1044. (exp_host_name, exp_host_data,
  1045. exp_addn_name, exp_addn_data,
  1046. exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data
  1047. with mock.patch('__builtin__.open') as mock_open:
  1048. mock_open.return_value.__enter__ = lambda s: s
  1049. mock_open.return_value.__exit__ = mock.Mock()
  1050. mock_open.return_value.readline.return_value = None
  1051. with mock.patch('os.path.isdir') as isdir:
  1052. isdir.return_value = True
  1053. with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid:
  1054. pid.__get__ = mock.Mock(return_value=5)
  1055. dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(),
  1056. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  1057. method_name = '_make_subnet_interface_ip_map'
  1058. with mock.patch.object(dhcp.Dnsmasq, method_name) as ipmap:
  1059. ipmap.return_value = {}
  1060. with mock.patch.object(dhcp.Dnsmasq, 'interface_name'):
  1061. dm.reload_allocations()
  1062. self.assertTrue(ipmap.called)
  1063. self.safe.assert_has_calls([
  1064. mock.call(exp_host_name, exp_host_data),
  1065. mock.call(exp_addn_name, exp_addn_data),
  1066. mock.call(exp_opt_name, exp_opt_data),
  1067. ])
  1068. mock_open.assert_called_once_with('/proc/5/cmdline', 'r')
  1069. def test_release_unused_leases(self):
  1070. dnsmasq = dhcp.Dnsmasq(self.conf, FakeDualNetwork())
  1071. ip1 = '192.168.1.2'
  1072. mac1 = '00:00:80:aa:bb:cc'
  1073. ip2 = '192.168.1.3'
  1074. mac2 = '00:00:80:cc:bb:aa'
  1075. old_leases = set([(ip1, mac1), (ip2, mac2)])
  1076. dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
  1077. dnsmasq._output_hosts_file = mock.Mock()
  1078. dnsmasq._release_lease = mock.Mock()
  1079. dnsmasq.network.ports = []
  1080. dnsmasq._release_unused_leases()
  1081. dnsmasq._release_lease.assert_has_calls([mock.call(mac1, ip1),
  1082. mock.call(mac2, ip2)],
  1083. any_order=True)
  1084. def test_release_unused_leases_one_lease(self):
  1085. dnsmasq = dhcp.Dnsmasq(self.conf, FakeDualNetwork())
  1086. ip1 = '192.168.0.2'
  1087. mac1 = '00:00:80:aa:bb:cc'
  1088. ip2 = '192.168.0.3'
  1089. mac2 = '00:00:80:cc:bb:aa'
  1090. old_leases = set([(ip1, mac1), (ip2, mac2)])
  1091. dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
  1092. dnsmasq._output_hosts_file = mock.Mock()
  1093. dnsmasq._release_lease = mock.Mock()
  1094. dnsmasq.network.ports = [FakePort1()]
  1095. dnsmasq._release_unused_leases()
  1096. dnsmasq._release_lease.assert_has_calls([mock.call(mac2, ip2)],
  1097. any_order=True)
  1098. def test_read_hosts_file_leases(self):
  1099. filename = '/path/to/file'
  1100. with mock.patch('os.path.exists') as mock_exists:
  1101. mock_exists.return_value = True
  1102. with mock.patch('__builtin__.open') as mock_open:
  1103. mock_open.return_value.__enter__ = lambda s: s
  1104. mock_open.return_value.__exit__ = mock.Mock()
  1105. lines = ["00:00:80:aa:bb:cc,inst-name,192.168.0.1"]
  1106. mock_open.return_value.readlines.return_value = lines
  1107. dnsmasq = dhcp.Dnsmasq(self.conf, FakeDualNetwork())
  1108. leases = dnsmasq._read_hosts_file_leases(filename)
  1109. self.assertEqual(set([("192.168.0.1", "00:00:80:aa:bb:cc")]), leases)
  1110. mock_exists.assert_called_once_with(filename)
  1111. mock_open.assert_called_once_with(filename)
  1112. def test_make_subnet_interface_ip_map(self):
  1113. with mock.patch('neutron.agent.linux.ip_lib.IPDevice') as ip_dev:
  1114. ip_dev.return_value.addr.list.return_value = [
  1115. {'cidr': '192.168.0.1/24'}
  1116. ]
  1117. dm = dhcp.Dnsmasq(self.conf,
  1118. FakeDualNetwork())
  1119. self.assertEqual(
  1120. dm._make_subnet_interface_ip_map(),
  1121. {FakeV4Subnet.id: '192.168.0.1'}
  1122. )
  1123. def test_remove_config_files(self):
  1124. net = FakeV4Network()
  1125. path = '/opt/data/neutron/dhcp'
  1126. self.conf.dhcp_confs = path
  1127. with mock.patch('shutil.rmtree') as rmtree:
  1128. lp = LocalChild(self.conf, net)
  1129. lp._remove_config_files()
  1130. rmtree.assert_called_once_with(os.path.join(path, net.id),
  1131. ignore_errors=True)
  1132. def test_existing_dhcp_networks(self):
  1133. path = '/opt/data/neutron/dhcp'
  1134. self.conf.dhcp_confs = path
  1135. cases = {
  1136. # network_uuid --> is_dhcp_alive?
  1137. 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa': True,
  1138. 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': False,
  1139. 'not_uuid_like_name': True
  1140. }
  1141. def active_fake(self, instance, cls):
  1142. return cases[instance.network.id]
  1143. with mock.patch('os.listdir') as mock_listdir:
  1144. with mock.patch.object(dhcp.Dnsmasq, 'active') as mock_active:
  1145. mock_active.__get__ = active_fake
  1146. mock_listdir.return_value = cases.keys()
  1147. result = dhcp.Dnsmasq.existing_dhcp_networks(self.conf, 'sudo')
  1148. mock_listdir.assert_called_once_with(path)
  1149. self.assertEqual(['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
  1150. 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'],
  1151. sorted(result))
  1152. def _check_version(self, cmd_out, expected_value):
  1153. with mock.patch('neutron.agent.linux.utils.execute') as cmd:
  1154. cmd.return_value = cmd_out
  1155. result = dhcp.Dnsmasq.check_version()
  1156. self.assertEqual(result, expected_value)
  1157. def test_check_minimum_version(self):
  1158. self._check_version('Dnsmasq version 2.63 Copyright (c)...',
  1159. dhcp.Dnsmasq.MINIMUM_VERSION)
  1160. def test_check_future_version(self):
  1161. self._check_version('Dnsmasq version 2.65 Copyright (c)...',
  1162. float(2.65))
  1163. def test_check_fail_version(self):
  1164. with testtools.ExpectedException(SystemExit):
  1165. self._check_version('Dnsmasq version 2.62 Copyright (c)...', 0)
  1166. def test_check_version_failed_cmd_execution(self):
  1167. with testtools.ExpectedException(SystemExit):
  1168. self._check_version('Error while executing command', 0)
  1169. def test_check_version_ipv6_succeed(self):
  1170. with mock.patch('neutron.agent.linux.dhcp.LOG.warning') as warning:
  1171. self._check_version('Dnsmasq version 2.69 Copyright (c)...',
  1172. float(2.69))
  1173. self.assertFalse(warning.called)
  1174. def test_check_version_ipv6_fail(self):
  1175. with mock.patch('neutron.agent.linux.dhcp.LOG.warning') as warning:
  1176. self._check_version('Dnsmasq version 2.66 Copyright (c)...',
  1177. float(2.66))
  1178. self.assertTrue(warning.called)
  1179. def test__output_hosts_file_log_only_twice(self):
  1180. dm = dhcp.Dnsmasq(self.conf, FakeDualStackNetworkSingleDHCP(),
  1181. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  1182. with mock.patch.object(dhcp.LOG, 'process') as process:
  1183. process.return_value = ('fake_message', {})
  1184. dm._output_hosts_file()
  1185. # The method logs twice, at the start of and the end. There should be
  1186. # no other logs, no matter how many hosts there are to dump in the
  1187. # file.
  1188. self.assertEqual(2, process.call_count)
  1189. def test_only_populates_dhcp_enabled_subnets(self):
  1190. exp_host_name = '/dhcp/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee/host'
  1191. exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal,'
  1192. '192.168.0.2\n'
  1193. '00:16:3E:C2:77:1D,host-192-168-0-4.openstacklocal,'
  1194. '192.168.0.4\n'
  1195. '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,'
  1196. '192.168.0.1\n').lstrip()
  1197. dm = dhcp.Dnsmasq(self.conf, FakeDualStackNetworkSingleDHCP(),
  1198. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  1199. dm._output_hosts_file()
  1200. self.safe.assert_has_calls([mock.call(exp_host_name,
  1201. exp_host_data)])
  1202. def test_host_and_opts_file_on_stateless_dhcpv6_network(self):
  1203. exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host'
  1204. exp_host_data = ('00:16:3e:c2:77:1d,'
  1205. 'set:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh\n').lstrip()
  1206. exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts'
  1207. exp_opt_data = ('tag:tag0,option6:domain-search,openstacklocal\n'
  1208. 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,'
  1209. 'option6:dns-server,ffea:3ba5:a17a:4ba3::100').lstrip()
  1210. dm = dhcp.Dnsmasq(self.conf, FakeV6NetworkStatelessDHCP(),
  1211. version=dhcp.Dnsmasq.MINIMUM_VERSION)
  1212. dm._output_hosts_file()
  1213. dm._output_opts_file()
  1214. self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
  1215. mock.call(exp_opt_name, exp_opt_data)])
  1216. def test_should_enable_metadata_namespaces_disabled_returns_false(self):
  1217. self.conf.set_override('use_namespaces', False)
  1218. self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(self.conf,
  1219. mock.ANY))
  1220. def test_should_enable_metadata_isolated_network_returns_true(self):
  1221. self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
  1222. self.conf, FakeV4NetworkNoRouter()))
  1223. def test_should_enable_metadata_non_isolated_network_returns_false(self):
  1224. self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(
  1225. self.conf, FakeV4NetworkDistRouter()))
  1226. def test_should_enable_metadata_isolated_meta_disabled_returns_false(self):
  1227. self.conf.set_override('enable_isolated_metadata', False)
  1228. self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(self.conf,
  1229. mock.ANY))
  1230. def test_should_enable_metadata_with_metadata_network_returns_true(self):
  1231. self.conf.set_override('enable_metadata_network', True)
  1232. self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
  1233. self.conf, FakeV4MetadataNetwork()))