Neutron integration with OVN
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.

1641 lines
78KB

  1. # Copyright 2016 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. import mock
  15. from neutron.services.segments import db as segments_db
  16. from neutron.tests.unit.api import test_extensions
  17. from neutron.tests.unit.extensions import test_extraroute
  18. from neutron.tests.unit.extensions import test_securitygroup
  19. from neutron_lib.api.definitions import dns as dns_apidef
  20. from neutron_lib.api.definitions import l3
  21. from neutron_lib.api.definitions import port_security as ps
  22. from neutron_lib import constants
  23. from neutron_lib import context
  24. from neutron_lib.plugins import directory
  25. from oslo_utils import uuidutils
  26. from ovsdbapp.backend.ovs_idl import idlutils
  27. from networking_ovn.common import acl as acl_utils
  28. from networking_ovn.common import config as ovn_config
  29. from networking_ovn.common import constants as ovn_const
  30. from networking_ovn.common import utils
  31. from networking_ovn import ovn_db_sync
  32. from networking_ovn.tests.functional import base
  33. class TestOvnNbSync(base.TestOVNFunctionalBase):
  34. _extension_drivers = ['port_security', 'dns']
  35. def setUp(self):
  36. ovn_config.cfg.CONF.set_override('dns_domain', 'ovn.test')
  37. super(TestOvnNbSync, self).setUp()
  38. ext_mgr = test_extraroute.ExtraRouteTestExtensionManager()
  39. self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
  40. sg_mgr = test_securitygroup.SecurityGroupTestExtensionManager()
  41. self._sg_api = test_extensions.setup_extensions_middleware(sg_mgr)
  42. self.create_lswitches = []
  43. self.create_lswitch_ports = []
  44. self.create_lrouters = []
  45. self.create_lrouter_ports = []
  46. self.create_lrouter_routes = []
  47. self.create_lrouter_nats = []
  48. self.update_lrouter_ports = []
  49. self.create_acls = []
  50. self.delete_lswitches = []
  51. self.delete_lswitch_ports = []
  52. self.delete_lrouters = []
  53. self.delete_lrouter_ports = []
  54. self.delete_lrouter_routes = []
  55. self.delete_lrouter_nats = []
  56. self.delete_acls = []
  57. self.create_address_sets = []
  58. self.delete_address_sets = []
  59. self.update_address_sets = []
  60. self.create_port_groups = []
  61. self.delete_port_groups = []
  62. self.expected_dhcp_options_rows = []
  63. self.reset_lport_dhcpv4_options = []
  64. self.reset_lport_dhcpv6_options = []
  65. self.stale_lport_dhcpv4_options = []
  66. self.stale_lport_dhcpv6_options = []
  67. self.orphaned_lport_dhcp_options = []
  68. self.lport_dhcpv4_disabled = {}
  69. self.lport_dhcpv6_disabled = {}
  70. self.missed_dhcp_options = []
  71. self.dirty_dhcp_options = []
  72. self.lport_dhcp_ignored = []
  73. self.match_old_mac_dhcp_subnets = []
  74. self.expected_dns_records = []
  75. self.expected_ports_with_unknown_addr = []
  76. ovn_config.cfg.CONF.set_override('ovn_metadata_enabled', True,
  77. group='ovn')
  78. def _api_for_resource(self, resource):
  79. if resource in ['security-groups']:
  80. return self._sg_api
  81. else:
  82. return super(TestOvnNbSync, self)._api_for_resource(resource)
  83. def _create_resources(self, restart_ovsdb_processes=False):
  84. net_kwargs = {dns_apidef.DNSDOMAIN: 'ovn.test.'}
  85. net_kwargs['arg_list'] = (dns_apidef.DNSDOMAIN,)
  86. res = self._create_network(self.fmt, 'n1', True, **net_kwargs)
  87. n1 = self.deserialize(self.fmt, res)
  88. self.expected_dns_records = [
  89. {'external_ids': {'ls_name': utils.ovn_name(n1['network']['id'])},
  90. 'records': {}}
  91. ]
  92. res = self._create_subnet(self.fmt, n1['network']['id'],
  93. '10.0.0.0/24')
  94. n1_s1 = self.deserialize(self.fmt, res)
  95. res = self._create_subnet(self.fmt, n1['network']['id'],
  96. '2001:dba::/64', ip_version=6,
  97. enable_dhcp=True)
  98. n1_s2 = self.deserialize(self.fmt, res)
  99. res = self._create_subnet(self.fmt, n1['network']['id'],
  100. '2001:dbb::/64', ip_version=6,
  101. ipv6_address_mode='slaac',
  102. ipv6_ra_mode='slaac')
  103. n1_s3 = self.deserialize(self.fmt, res)
  104. self.expected_dhcp_options_rows.append({
  105. 'cidr': '10.0.0.0/24',
  106. 'external_ids': {'subnet_id': n1_s1['subnet']['id'],
  107. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  108. 'options': {'classless_static_route':
  109. '{169.254.169.254/32,10.0.0.2, 0.0.0.0/0,10.0.0.1}',
  110. 'server_id': '10.0.0.1',
  111. 'server_mac': '01:02:03:04:05:06',
  112. 'dns_server': '{10.10.10.10}',
  113. 'lease_time': str(12 * 60 * 60),
  114. 'mtu': str(n1['network']['mtu']),
  115. 'domain_name': '"ovn.test"',
  116. 'router': n1_s1['subnet']['gateway_ip']}})
  117. self.expected_dhcp_options_rows.append({
  118. 'cidr': '2001:dba::/64',
  119. 'external_ids': {'subnet_id': n1_s2['subnet']['id'],
  120. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  121. 'options': {'server_id': '01:02:03:04:05:06'}})
  122. n1_s1_dhcp_options_uuid = (
  123. self.mech_driver._nb_ovn.get_subnet_dhcp_options(
  124. n1_s1['subnet']['id'])['subnet']['uuid'])
  125. n1_s2_dhcpv6_options_uuid = (
  126. self.mech_driver._nb_ovn.get_subnet_dhcp_options(
  127. n1_s2['subnet']['id'])['subnet']['uuid'])
  128. update_port_ids_v4 = []
  129. update_port_ids_v6 = []
  130. n1_port_dict = {}
  131. for p in ['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7']:
  132. if p in ['p1', 'p5']:
  133. port_kwargs = {
  134. 'arg_list': (dns_apidef.DNSNAME, ps.PORTSECURITY),
  135. dns_apidef.DNSNAME: 'n1-' + p,
  136. ps.PORTSECURITY: 'False',
  137. 'device_id': 'n1-' + p}
  138. else:
  139. port_kwargs = {}
  140. res = self._create_port(self.fmt, n1['network']['id'],
  141. name='n1-' + p,
  142. device_owner='compute:None',
  143. **port_kwargs)
  144. port = self.deserialize(self.fmt, res)
  145. n1_port_dict[p] = port['port']['id']
  146. lport_name = port['port']['id']
  147. lswitch_name = 'neutron-' + n1['network']['id']
  148. if p in ['p1', 'p5']:
  149. port_ips = " ".join([f['ip_address']
  150. for f in port['port']['fixed_ips']])
  151. hname = 'n1-' + p
  152. self.expected_dns_records[0]['records'][hname] = port_ips
  153. hname = 'n1-' + p + '.ovn.test'
  154. self.expected_dns_records[0]['records'][hname] = port_ips
  155. self.expected_ports_with_unknown_addr.append(lport_name)
  156. if p == 'p1':
  157. fake_subnet = {'cidr': '11.11.11.11/24'}
  158. dhcp_acls = acl_utils.add_acl_dhcp(port['port'], fake_subnet)
  159. for dhcp_acl in dhcp_acls:
  160. self.create_acls.append(dhcp_acl)
  161. elif p == 'p2':
  162. self.delete_lswitch_ports.append((lport_name, lswitch_name))
  163. update_port_ids_v4.append(port['port']['id'])
  164. update_port_ids_v6.append(port['port']['id'])
  165. self.expected_dhcp_options_rows.append({
  166. 'cidr': '10.0.0.0/24',
  167. 'external_ids': {'subnet_id': n1_s1['subnet']['id'],
  168. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0',
  169. 'port_id': port['port']['id']},
  170. 'options': {
  171. 'classless_static_route':
  172. '{169.254.169.254/32,10.0.0.2, 0.0.0.0/0,10.0.0.1}',
  173. 'server_id': '10.0.0.1',
  174. 'server_mac': '01:02:03:04:05:06',
  175. 'lease_time': str(12 * 60 * 60),
  176. 'mtu': str(n1['network']['mtu']),
  177. 'router': n1_s1['subnet']['gateway_ip'],
  178. 'tftp_server': '20.0.0.20',
  179. 'domain_name': '"ovn.test"',
  180. 'dns_server': '8.8.8.8'}})
  181. self.expected_dhcp_options_rows.append({
  182. 'cidr': '2001:dba::/64',
  183. 'external_ids': {'subnet_id': n1_s2['subnet']['id'],
  184. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0',
  185. 'port_id': port['port']['id']},
  186. 'options': {'server_id': '01:02:03:04:05:06',
  187. 'domain_search': 'foo-domain'}})
  188. self.dirty_dhcp_options.append({
  189. 'subnet_id': n1_s1['subnet']['id'],
  190. 'port_id': lport_name})
  191. self.dirty_dhcp_options.append({
  192. 'subnet_id': n1_s2['subnet']['id'],
  193. 'port_id': lport_name})
  194. elif p == 'p3':
  195. self.delete_acls.append((lport_name, lswitch_name))
  196. self.reset_lport_dhcpv4_options.append(lport_name)
  197. self.lport_dhcpv6_disabled.update({
  198. lport_name: n1_s2_dhcpv6_options_uuid})
  199. data = {'port': {
  200. 'extra_dhcp_opts': [{'ip_version': 6,
  201. 'opt_name': 'dhcp_disabled',
  202. 'opt_value': 'True'}]}}
  203. port_req = self.new_update_request('ports', data, lport_name)
  204. port_req.get_response(self.api)
  205. elif p == 'p4':
  206. self.lport_dhcpv4_disabled.update({
  207. lport_name: n1_s1_dhcp_options_uuid})
  208. data = {'port': {
  209. 'extra_dhcp_opts': [{'ip_version': 4,
  210. 'opt_name': 'dhcp_disabled',
  211. 'opt_value': 'True'}]}}
  212. port_req = self.new_update_request('ports', data, lport_name)
  213. port_req.get_response(self.api)
  214. self.reset_lport_dhcpv6_options.append(lport_name)
  215. elif p == 'p5':
  216. self.stale_lport_dhcpv4_options.append({
  217. 'subnet_id': n1_s1['subnet']['id'],
  218. 'port_id': port['port']['id'],
  219. 'cidr': '10.0.0.0/24',
  220. 'options': {'server_id': '10.0.0.254',
  221. 'server_mac': '01:02:03:04:05:06',
  222. 'lease_time': str(3 * 60 * 60),
  223. 'mtu': str(n1['network']['mtu'] / 2),
  224. 'router': '10.0.0.254',
  225. 'tftp_server': '20.0.0.234',
  226. 'dns_server': '8.8.8.8'},
  227. 'external_ids': {'subnet_id': n1_s1['subnet']['id'],
  228. 'port_id': port['port']['id']},
  229. })
  230. elif p == 'p6':
  231. self.delete_lswitch_ports.append((lport_name, lswitch_name))
  232. elif p == 'p7':
  233. update_port_ids_v4.append(port['port']['id'])
  234. update_port_ids_v6.append(port['port']['id'])
  235. self.expected_dhcp_options_rows.append({
  236. 'cidr': '10.0.0.0/24',
  237. 'external_ids': {'subnet_id': n1_s1['subnet']['id'],
  238. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0',
  239. 'port_id': port['port']['id']},
  240. 'options': {
  241. 'classless_static_route':
  242. '{169.254.169.254/32,10.0.0.2, 0.0.0.0/0,10.0.0.1}',
  243. 'server_id': '10.0.0.1',
  244. 'server_mac': '01:02:03:04:05:06',
  245. 'lease_time': str(12 * 60 * 60),
  246. 'mtu': str(n1['network']['mtu']),
  247. 'router': n1_s1['subnet']['gateway_ip'],
  248. 'tftp_server': '20.0.0.20',
  249. 'domain_name': '"ovn.test"',
  250. 'dns_server': '8.8.8.8'}})
  251. self.expected_dhcp_options_rows.append({
  252. 'cidr': '2001:dba::/64',
  253. 'external_ids': {'subnet_id': n1_s2['subnet']['id'],
  254. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0',
  255. 'port_id': port['port']['id']},
  256. 'options': {'server_id': '01:02:03:04:05:06',
  257. 'domain_search': 'foo-domain'}})
  258. self.reset_lport_dhcpv4_options.append(lport_name)
  259. self.reset_lport_dhcpv6_options.append(lport_name)
  260. self.dirty_dhcp_options.append({'subnet_id': n1_s1['subnet']['id']})
  261. self.dirty_dhcp_options.append({'subnet_id': n1_s2['subnet']['id']})
  262. res = self._create_network(self.fmt, 'n2', True, **net_kwargs)
  263. n2 = self.deserialize(self.fmt, res)
  264. res = self._create_subnet(self.fmt, n2['network']['id'],
  265. '20.0.0.0/24')
  266. n2_s1 = self.deserialize(self.fmt, res)
  267. res = self._create_subnet(self.fmt, n2['network']['id'],
  268. '2001:dbd::/64', ip_version=6)
  269. n2_s2 = self.deserialize(self.fmt, res)
  270. self.expected_dhcp_options_rows.append({
  271. 'cidr': '20.0.0.0/24',
  272. 'external_ids': {'subnet_id': n2_s1['subnet']['id'],
  273. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  274. 'options': {'classless_static_route':
  275. '{169.254.169.254/32,20.0.0.2, 0.0.0.0/0,20.0.0.1}',
  276. 'server_id': '20.0.0.1',
  277. 'server_mac': '01:02:03:04:05:06',
  278. 'dns_server': '{10.10.10.10}',
  279. 'lease_time': str(12 * 60 * 60),
  280. 'mtu': str(n2['network']['mtu']),
  281. 'domain_name': '"ovn.test"',
  282. 'router': n2_s1['subnet']['gateway_ip']}})
  283. self.expected_dhcp_options_rows.append({
  284. 'cidr': '2001:dbd::/64',
  285. 'external_ids': {'subnet_id': n2_s2['subnet']['id'],
  286. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  287. 'options': {'server_id': '01:02:03:04:05:06'}})
  288. for p in ['p1', 'p2']:
  289. port = self._make_port(self.fmt, n2['network']['id'],
  290. name='n2-' + p,
  291. device_owner='compute:None')
  292. if p == 'p1':
  293. update_port_ids_v4.append(port['port']['id'])
  294. self.expected_dhcp_options_rows.append({
  295. 'cidr': '20.0.0.0/24',
  296. 'external_ids': {'subnet_id': n2_s1['subnet']['id'],
  297. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0',
  298. 'port_id': port['port']['id']},
  299. 'options': {
  300. 'classless_static_route':
  301. '{169.254.169.254/32,20.0.0.2, 0.0.0.0/0,20.0.0.1}',
  302. 'server_id': '20.0.0.1',
  303. 'server_mac': '01:02:03:04:05:06',
  304. 'lease_time': str(12 * 60 * 60),
  305. 'mtu': str(n1['network']['mtu']),
  306. 'router': n2_s1['subnet']['gateway_ip'],
  307. 'tftp_server': '20.0.0.20',
  308. 'domain_name': '"ovn.test"',
  309. 'dns_server': '8.8.8.8'}})
  310. self.missed_dhcp_options.extend([
  311. opts['uuid']
  312. for opts in self.mech_driver._nb_ovn.get_subnets_dhcp_options(
  313. [n2_s1['subnet']['id'], n2_s2['subnet']['id']])])
  314. for port_id in update_port_ids_v4:
  315. data = {'port': {'extra_dhcp_opts': [{'ip_version': 4,
  316. 'opt_name': 'tftp-server',
  317. 'opt_value': '20.0.0.20'},
  318. {'ip_version': 4,
  319. 'opt_name': 'dns-server',
  320. 'opt_value': '8.8.8.8'}]}}
  321. port_req = self.new_update_request('ports', data, port_id)
  322. port_req.get_response(self.api)
  323. for port_id in update_port_ids_v6:
  324. data = {'port': {'extra_dhcp_opts': [{'ip_version': 6,
  325. 'opt_name': 'domain-search',
  326. 'opt_value': 'foo-domain'}]}}
  327. port_req = self.new_update_request('ports', data, port_id)
  328. port_req.get_response(self.api)
  329. # External network and subnet
  330. e1 = self._make_network(self.fmt, 'e1', True,
  331. arg_list=('router:external',
  332. 'provider:network_type',
  333. 'provider:physical_network'),
  334. **{'router:external': True,
  335. 'provider:network_type': 'flat',
  336. 'provider:physical_network': 'public'})
  337. self.assertEqual(True, e1['network']['router:external'])
  338. self.assertEqual('flat', e1['network']['provider:network_type'])
  339. self.assertEqual('public', e1['network']['provider:physical_network'])
  340. res = self._create_subnet(self.fmt, e1['network']['id'],
  341. '100.0.0.0/24', gateway_ip='100.0.0.254',
  342. allocation_pools=[{'start': '100.0.0.2',
  343. 'end': '100.0.0.253'}],
  344. enable_dhcp=False)
  345. e1_s1 = self.deserialize(self.fmt, res)
  346. res = self._create_subnet(self.fmt, e1['network']['id'],
  347. '2001:db8::/64',
  348. gateway_ip='fd05:59e4:ef16::1',
  349. ip_version=constants.IP_VERSION_6,
  350. enable_dhcp=False)
  351. e1_s2 = self.deserialize(self.fmt, res)
  352. self.create_lswitches.append('neutron-' + uuidutils.generate_uuid())
  353. self.create_lswitch_ports.append(('neutron-' +
  354. uuidutils.generate_uuid(),
  355. 'neutron-' + n1['network']['id']))
  356. self.create_lswitch_ports.append(('neutron-' +
  357. uuidutils.generate_uuid(),
  358. 'neutron-' + n1['network']['id']))
  359. self.delete_lswitches.append('neutron-' + n2['network']['id'])
  360. self.delete_lswitch_ports.append(
  361. (utils.ovn_provnet_port_name(e1['network']['id']),
  362. utils.ovn_name(e1['network']['id'])))
  363. r1 = self.l3_plugin.create_router(
  364. self.context,
  365. {'router': {
  366. 'name': 'r1', 'admin_state_up': True,
  367. 'tenant_id': self._tenant_id,
  368. 'external_gateway_info': {
  369. 'enable_snat': True,
  370. 'network_id': e1['network']['id'],
  371. 'external_fixed_ips': [
  372. {'ip_address': '100.0.0.2',
  373. 'subnet_id': e1_s1['subnet']['id']},
  374. {'ip_address': '2001:db8::23a',
  375. 'subnet_id': e1_s2['subnet']['id']}]}}})
  376. self.l3_plugin.add_router_interface(
  377. self.context, r1['id'], {'subnet_id': n1_s1['subnet']['id']})
  378. r1_p2 = self.l3_plugin.add_router_interface(
  379. self.context, r1['id'], {'subnet_id': n1_s2['subnet']['id']})
  380. self.l3_plugin.add_router_interface(
  381. self.context, r1['id'], {'subnet_id': n1_s3['subnet']['id']})
  382. r1_p3 = self.l3_plugin.add_router_interface(
  383. self.context, r1['id'], {'subnet_id': n2_s1['subnet']['id']})
  384. self.update_lrouter_ports.append(('lrp-' + r1_p2['port_id'],
  385. 'neutron-' + r1['id'],
  386. n1_s2['subnet']['gateway_ip']))
  387. self.delete_lrouter_ports.append(('lrp-' + r1_p3['port_id'],
  388. 'neutron-' + r1['id']))
  389. self.delete_lrouter_ports.append(('lrp-' + r1['gw_port_id'],
  390. 'neutron-' + r1['id']))
  391. self.l3_plugin.update_router(
  392. self.context, r1['id'],
  393. {'router': {'routes': [{'destination': '10.10.0.0/24',
  394. 'nexthop': '20.0.0.10'},
  395. {'destination': '10.11.0.0/24',
  396. 'nexthop': '20.0.0.11'}]}})
  397. r1_f1 = self.l3_plugin.create_floatingip(
  398. self.context, {'floatingip': {
  399. 'tenant_id': self._tenant_id,
  400. 'floating_network_id': e1['network']['id'],
  401. 'floating_ip_address': '100.0.0.20',
  402. 'subnet_id': None,
  403. 'port_id': n1_port_dict['p1']}})
  404. r1_f2 = self.l3_plugin.create_floatingip(
  405. self.context, {'floatingip': {
  406. 'tenant_id': self._tenant_id,
  407. 'floating_network_id': e1['network']['id'],
  408. 'subnet_id': None,
  409. 'floating_ip_address': '100.0.0.21'}})
  410. self.l3_plugin.update_floatingip(
  411. self.context, r1_f2['id'], {'floatingip': {
  412. 'port_id': n1_port_dict['p2']}})
  413. # update External subnet gateway ip to test function _subnet_update
  414. # of L3 OVN plugin.
  415. data = {'subnet': {'gateway_ip': '100.0.0.1'}}
  416. subnet_req = self.new_update_request(
  417. 'subnets', data, e1_s1['subnet']['id'])
  418. subnet_req.get_response(self.api)
  419. # Static routes
  420. self.create_lrouter_routes.append(('neutron-' + r1['id'],
  421. '10.12.0.0/24',
  422. '20.0.0.12'))
  423. self.create_lrouter_routes.append(('neutron-' + r1['id'],
  424. '10.13.0.0/24',
  425. '20.0.0.13'))
  426. self.delete_lrouter_routes.append(('neutron-' + r1['id'],
  427. '10.10.0.0/24',
  428. '20.0.0.10'))
  429. # Gateway default route
  430. self.delete_lrouter_routes.append(('neutron-' + r1['id'],
  431. '0.0.0.0/0',
  432. '100.0.0.1'))
  433. # Gateway sNATs
  434. self.create_lrouter_nats.append(('neutron-' + r1['id'],
  435. {'external_ip': '100.0.0.100',
  436. 'logical_ip': '200.0.0.0/24',
  437. 'type': 'snat'}))
  438. self.delete_lrouter_nats.append(('neutron-' + r1['id'],
  439. {'external_ip': '100.0.0.2',
  440. 'logical_ip': '10.0.0.0/24',
  441. 'type': 'snat'}))
  442. # Floating IPs
  443. self.create_lrouter_nats.append(('neutron-' + r1['id'],
  444. {'external_ip': '100.0.0.200',
  445. 'logical_ip': '200.0.0.200',
  446. 'type': 'dnat_and_snat'}))
  447. self.create_lrouter_nats.append(('neutron-' + r1['id'],
  448. {'external_ip': '100.0.0.201',
  449. 'logical_ip': '200.0.0.201',
  450. 'type': 'dnat_and_snat',
  451. 'external_mac': '01:02:03:04:05:06',
  452. 'logical_port': 'vm1'
  453. }))
  454. self.delete_lrouter_nats.append(('neutron-' + r1['id'],
  455. {'external_ip':
  456. r1_f1['floating_ip_address'],
  457. 'logical_ip':
  458. r1_f1['fixed_ip_address'],
  459. 'type': 'dnat_and_snat'}))
  460. res = self._create_network(self.fmt, 'n4', True, **net_kwargs)
  461. n4 = self.deserialize(self.fmt, res)
  462. res = self._create_subnet(self.fmt, n4['network']['id'],
  463. '40.0.0.0/24', enable_dhcp=False)
  464. self.expected_dns_records.append(
  465. {'external_ids': {'ls_name': utils.ovn_name(n4['network']['id'])},
  466. 'records': {}}
  467. )
  468. n4_s1 = self.deserialize(self.fmt, res)
  469. n4_port_dict = {}
  470. for p in ['p1', 'p2', 'p3']:
  471. if p in ['p1', 'p2']:
  472. port_kwargs = {'arg_list': (dns_apidef.DNSNAME,),
  473. dns_apidef.DNSNAME: 'n4-' + p,
  474. 'device_id': 'n4-' + p}
  475. else:
  476. port_kwargs = {}
  477. res = self._create_port(self.fmt, n4['network']['id'],
  478. name='n4-' + p,
  479. device_owner='compute:None',
  480. **port_kwargs)
  481. port = self.deserialize(self.fmt, res)
  482. if p in ['p1', 'p2']:
  483. port_ips = " ".join([f['ip_address']
  484. for f in port['port']['fixed_ips']])
  485. hname = 'n4-' + p
  486. self.expected_dns_records[1]['records'][hname] = port_ips
  487. hname = 'n4-' + p + '.ovn.test'
  488. self.expected_dns_records[1]['records'][hname] = port_ips
  489. n4_port_dict[p] = port['port']['id']
  490. self.lport_dhcp_ignored.append(port['port']['id'])
  491. r2 = self.l3_plugin.create_router(
  492. self.context,
  493. {'router': {'name': 'r2', 'admin_state_up': True,
  494. 'tenant_id': self._tenant_id}})
  495. n1_prtr = self._make_port(self.fmt, n1['network']['id'],
  496. name='n1-p-rtr')
  497. self.l3_plugin.add_router_interface(
  498. self.context, r2['id'], {'port_id': n1_prtr['port']['id']})
  499. self.l3_plugin.add_router_interface(
  500. self.context, r2['id'], {'subnet_id': n4_s1['subnet']['id']})
  501. self.l3_plugin.update_router(
  502. self.context, r2['id'],
  503. # FIXME(lucasagomes): Add "routes" back, it has been
  504. # removed to avoid a race condition that was happening from
  505. # time to time. The error was: "Invalid format for routes:
  506. # [{'destination': '10.20.0.0/24', 'nexthop': '10.0.0.20'}],
  507. # the nexthop is used by route". It seems to be a race within
  508. # the tests itself, running the functional tests without
  509. # any concurrency doesn't fail when the "routes" are set.
  510. #
  511. # {'router': {'routes': [{'destination': '10.20.0.0/24',
  512. # 'nexthop': '10.0.0.20'}],
  513. # ...
  514. {'router': {'external_gateway_info': {
  515. 'enable_snat': False,
  516. 'network_id': e1['network']['id'],
  517. 'external_fixed_ips': [
  518. {'ip_address': '100.0.0.3',
  519. 'subnet_id': e1_s1['subnet']['id']}]}}})
  520. self.l3_plugin.create_floatingip(
  521. self.context, {'floatingip': {
  522. 'tenant_id': self._tenant_id,
  523. 'floating_network_id': e1['network']['id'],
  524. 'floating_ip_address': '100.0.0.30',
  525. 'subnet_id': None,
  526. 'port_id': n4_port_dict['p1']}})
  527. self.l3_plugin.create_floatingip(
  528. self.context, {'floatingip': {
  529. 'tenant_id': self._tenant_id,
  530. 'floating_network_id': e1['network']['id'],
  531. 'floating_ip_address': '100.0.0.31',
  532. 'subnet_id': None,
  533. 'port_id': n4_port_dict['p2']}})
  534. # To test l3_plugin.disassociate_floatingips, associating floating IP
  535. # to port p3 and then deleting p3.
  536. self.l3_plugin.create_floatingip(
  537. self.context, {'floatingip': {
  538. 'tenant_id': self._tenant_id,
  539. 'floating_network_id': e1['network']['id'],
  540. 'floating_ip_address': '100.0.0.32',
  541. 'subnet_id': None,
  542. 'port_id': n4_port_dict['p3']}})
  543. self._delete('ports', n4_port_dict['p3'])
  544. self.create_lrouters.append('neutron-' + uuidutils.generate_uuid())
  545. self.create_lrouter_ports.append(('lrp-' + uuidutils.generate_uuid(),
  546. 'neutron-' + r1['id']))
  547. self.create_lrouter_ports.append(('lrp-' + uuidutils.generate_uuid(),
  548. 'neutron-' + r1['id']))
  549. self.delete_lrouters.append('neutron-' + r2['id'])
  550. address_set_name = n1_prtr['port']['security_groups'][0]
  551. self.create_address_sets.extend([('fake_sg', 'ip4'),
  552. ('fake_sg', 'ip6')])
  553. self.delete_address_sets.append((address_set_name, 'ip6'))
  554. address_adds = ['10.0.0.101', '10.0.0.102']
  555. address_dels = []
  556. for address in n1_prtr['port']['fixed_ips']:
  557. address_dels.append(address['ip_address'])
  558. self.update_address_sets.append((address_set_name, 'ip4',
  559. address_adds, address_dels))
  560. self.create_port_groups.extend([{'name': 'pg1', 'acls': []},
  561. {'name': 'pg2', 'acls': []}])
  562. self.delete_port_groups.append(
  563. utils.ovn_port_group_name(n1_prtr['port']['security_groups'][0]))
  564. # Create a network and subnet with orphaned OVN resources.
  565. n3 = self._make_network(self.fmt, 'n3', True)
  566. res = self._create_subnet(self.fmt, n3['network']['id'],
  567. '30.0.0.0/24')
  568. n3_s1 = self.deserialize(self.fmt, res)
  569. res = self._create_subnet(self.fmt, n3['network']['id'],
  570. '2001:dbc::/64', ip_version=6)
  571. n3_s2 = self.deserialize(self.fmt, res)
  572. if not restart_ovsdb_processes:
  573. # Test using original mac when syncing.
  574. dhcp_mac_v4 = (self.mech_driver._nb_ovn.get_subnet_dhcp_options(
  575. n3_s1['subnet']['id'])['subnet'].get('options', {})
  576. .get('server_mac'))
  577. dhcp_mac_v6 = (self.mech_driver._nb_ovn.get_subnet_dhcp_options(
  578. n3_s2['subnet']['id'])['subnet'].get('options', {})
  579. .get('server_id'))
  580. self.assertTrue(dhcp_mac_v4 is not None)
  581. self.assertTrue(dhcp_mac_v6 is not None)
  582. self.match_old_mac_dhcp_subnets.append(n3_s1['subnet']['id'])
  583. self.match_old_mac_dhcp_subnets.append(n3_s2['subnet']['id'])
  584. else:
  585. dhcp_mac_v4 = '01:02:03:04:05:06'
  586. dhcp_mac_v6 = '01:02:03:04:05:06'
  587. self.expected_dhcp_options_rows.append({
  588. 'cidr': '30.0.0.0/24',
  589. 'external_ids': {'subnet_id': n3_s1['subnet']['id'],
  590. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  591. 'options': {'classless_static_route':
  592. '{169.254.169.254/32,30.0.0.2, 0.0.0.0/0,30.0.0.1}',
  593. 'server_id': '30.0.0.1',
  594. 'dns_server': '{10.10.10.10}',
  595. 'server_mac': dhcp_mac_v4,
  596. 'lease_time': str(12 * 60 * 60),
  597. 'mtu': str(n3['network']['mtu']),
  598. 'router': n3_s1['subnet']['gateway_ip']}})
  599. self.expected_dhcp_options_rows.append({
  600. 'cidr': '2001:dbc::/64',
  601. 'external_ids': {'subnet_id': n3_s2['subnet']['id'],
  602. ovn_const.OVN_REV_NUM_EXT_ID_KEY: '0'},
  603. 'options': {'server_id': dhcp_mac_v6}})
  604. fake_port_id1 = uuidutils.generate_uuid()
  605. fake_port_id2 = uuidutils.generate_uuid()
  606. self.create_lswitch_ports.append(('neutron-' + fake_port_id1,
  607. 'neutron-' + n3['network']['id']))
  608. self.create_lswitch_ports.append(('neutron-' + fake_port_id2,
  609. 'neutron-' + n3['network']['id']))
  610. stale_dhcpv4_options1 = {
  611. 'subnet_id': n3_s1['subnet']['id'],
  612. 'port_id': fake_port_id1,
  613. 'cidr': '30.0.0.0/24',
  614. 'options': {'server_id': '30.0.0.254',
  615. 'server_mac': dhcp_mac_v4,
  616. 'lease_time': str(3 * 60 * 60),
  617. 'mtu': str(n3['network']['mtu'] / 2),
  618. 'router': '30.0.0.254',
  619. 'tftp_server': '30.0.0.234',
  620. 'dns_server': '8.8.8.8'},
  621. 'external_ids': {'subnet_id': n3_s1['subnet']['id'],
  622. 'port_id': fake_port_id1},
  623. }
  624. self.stale_lport_dhcpv4_options.append(stale_dhcpv4_options1)
  625. stale_dhcpv4_options2 = stale_dhcpv4_options1.copy()
  626. stale_dhcpv4_options2.update({
  627. 'port_id': fake_port_id2,
  628. 'external_ids': {'subnet_id': n3_s1['subnet']['id'],
  629. 'port_id': fake_port_id2}})
  630. self.stale_lport_dhcpv4_options.append(stale_dhcpv4_options2)
  631. self.orphaned_lport_dhcp_options.append(fake_port_id2)
  632. stale_dhcpv6_options1 = {
  633. 'subnet_id': n3_s2['subnet']['id'],
  634. 'port_id': fake_port_id1,
  635. 'cidr': '2001:dbc::/64',
  636. 'options': {'server_id': dhcp_mac_v6,
  637. 'domain-search': 'foo-domain'},
  638. 'external_ids': {'subnet_id': n3_s2['subnet']['id'],
  639. 'port_id': fake_port_id1},
  640. }
  641. self.stale_lport_dhcpv6_options.append(stale_dhcpv6_options1)
  642. stale_dhcpv6_options2 = stale_dhcpv6_options1.copy()
  643. stale_dhcpv6_options2.update({
  644. 'port_id': fake_port_id2,
  645. 'external_ids': {'subnet_id': n3_s2['subnet']['id'],
  646. 'port_id': fake_port_id2}})
  647. self.stale_lport_dhcpv6_options.append(stale_dhcpv6_options2)
  648. fake_port = {'id': fake_port_id1, 'network_id': n3['network']['id']}
  649. dhcp_acls = acl_utils.add_acl_dhcp(fake_port, n3_s1['subnet'])
  650. for dhcp_acl in dhcp_acls:
  651. self.create_acls.append(dhcp_acl)
  652. columns = list(self.nb_api.tables['ACL'].columns)
  653. if not (('name' in columns) and ('severity' in columns)):
  654. for acl in self.create_acls:
  655. acl.pop('name')
  656. acl.pop('severity')
  657. def _modify_resources_in_nb_db(self):
  658. self._delete_metadata_ports()
  659. with self.nb_api.transaction(check_error=True) as txn:
  660. for lswitch_name in self.create_lswitches:
  661. external_ids = {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
  662. lswitch_name}
  663. txn.add(self.nb_api.ls_add(lswitch_name, True,
  664. external_ids=external_ids))
  665. for lswitch_name in self.delete_lswitches:
  666. txn.add(self.nb_api.ls_del(lswitch_name, True))
  667. for lport_name, lswitch_name in self.create_lswitch_ports:
  668. external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  669. lport_name}
  670. txn.add(self.nb_api.create_lswitch_port(
  671. lport_name, lswitch_name, True, external_ids=external_ids))
  672. for lport_name, lswitch_name in self.delete_lswitch_ports:
  673. txn.add(self.nb_api.delete_lswitch_port(lport_name,
  674. lswitch_name, True))
  675. for lrouter_name in self.create_lrouters:
  676. external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  677. lrouter_name}
  678. txn.add(self.nb_api.create_lrouter(lrouter_name, True,
  679. external_ids=external_ids))
  680. for lrouter_name in self.delete_lrouters:
  681. txn.add(self.nb_api.delete_lrouter(lrouter_name, True))
  682. for lrport, lrouter_name in self.create_lrouter_ports:
  683. txn.add(self.nb_api.add_lrouter_port(lrport, lrouter_name))
  684. for lrport, lrouter_name, networks in self.update_lrouter_ports:
  685. txn.add(self.nb_api.update_lrouter_port(
  686. lrport, True, **{'networks': [networks],
  687. 'ipv6_ra_configs': {'foo': 'bar'}}))
  688. for lrport, lrouter_name in self.delete_lrouter_ports:
  689. txn.add(self.nb_api.delete_lrouter_port(lrport,
  690. lrouter_name, True))
  691. for lrouter_name, ip_prefix, nexthop in self.create_lrouter_routes:
  692. txn.add(self.nb_api.add_static_route(lrouter_name,
  693. ip_prefix=ip_prefix,
  694. nexthop=nexthop))
  695. for lrouter_name, ip_prefix, nexthop in self.delete_lrouter_routes:
  696. txn.add(self.nb_api.delete_static_route(lrouter_name,
  697. ip_prefix, nexthop,
  698. True))
  699. for lrouter_name, nat_dict in(
  700. self.create_lrouter_nats):
  701. txn.add(self.nb_api.add_nat_rule_in_lrouter(
  702. lrouter_name, **nat_dict))
  703. for lrouter_name, nat_dict in(
  704. self.delete_lrouter_nats):
  705. txn.add(self.nb_api.delete_nat_rule_in_lrouter(
  706. lrouter_name, if_exists=True, **nat_dict))
  707. for acl in self.create_acls:
  708. txn.add(self.nb_api.add_acl(**acl))
  709. for lport_name, lswitch_name in self.delete_acls:
  710. txn.add(self.nb_api.delete_acl(lswitch_name,
  711. lport_name, True))
  712. for name, ip_version in self.create_address_sets:
  713. ovn_name = utils.ovn_addrset_name(name, ip_version)
  714. external_ids = {ovn_const.OVN_SG_EXT_ID_KEY: name}
  715. txn.add(self.nb_api.create_address_set(
  716. ovn_name, True, external_ids=external_ids))
  717. for name, ip_version in self.delete_address_sets:
  718. ovn_name = utils.ovn_addrset_name(name, ip_version)
  719. txn.add(self.nb_api.delete_address_set(ovn_name, True))
  720. for name, ip_version, ip_adds, ip_dels in self.update_address_sets:
  721. ovn_name = utils.ovn_addrset_name(name, ip_version)
  722. txn.add(self.nb_api.update_address_set(ovn_name,
  723. ip_adds, ip_dels, True))
  724. if self.nb_api.is_port_groups_supported():
  725. for pg in self.create_port_groups:
  726. txn.add(self.nb_api.pg_add(**pg))
  727. for pg in self.delete_port_groups:
  728. txn.add(self.nb_api.pg_del(pg))
  729. for lport_name in self.reset_lport_dhcpv4_options:
  730. txn.add(self.nb_api.set_lswitch_port(lport_name, True,
  731. dhcpv4_options=[]))
  732. for lport_name in self.reset_lport_dhcpv6_options:
  733. txn.add(self.nb_api.set_lswitch_port(lport_name, True,
  734. dhcpv6_options=[]))
  735. for dhcp_opts in self.stale_lport_dhcpv4_options:
  736. dhcpv4_opts = txn.add(self.nb_api.add_dhcp_options(
  737. dhcp_opts['subnet_id'],
  738. port_id=dhcp_opts['port_id'],
  739. cidr=dhcp_opts['cidr'],
  740. options=dhcp_opts['options'],
  741. external_ids=dhcp_opts['external_ids'],
  742. may_exist=False))
  743. if dhcp_opts['port_id'] in self.orphaned_lport_dhcp_options:
  744. continue
  745. txn.add(self.nb_api.set_lswitch_port(
  746. lport_name, True, dhcpv4_options=dhcpv4_opts))
  747. for dhcp_opts in self.stale_lport_dhcpv6_options:
  748. dhcpv6_opts = txn.add(self.nb_api.add_dhcp_options(
  749. dhcp_opts['subnet_id'],
  750. port_id=dhcp_opts['port_id'],
  751. cidr=dhcp_opts['cidr'],
  752. options=dhcp_opts['options'],
  753. external_ids=dhcp_opts['external_ids'],
  754. may_exist=False))
  755. if dhcp_opts['port_id'] in self.orphaned_lport_dhcp_options:
  756. continue
  757. txn.add(self.nb_api.set_lswitch_port(
  758. lport_name, True, dhcpv6_options=dhcpv6_opts))
  759. for row_uuid in self.missed_dhcp_options:
  760. txn.add(self.nb_api.delete_dhcp_options(row_uuid))
  761. for dhcp_opts in self.dirty_dhcp_options:
  762. external_ids = {'subnet_id': dhcp_opts['subnet_id']}
  763. if dhcp_opts.get('port_id'):
  764. external_ids['port_id'] = dhcp_opts['port_id']
  765. txn.add(self.nb_api.add_dhcp_options(
  766. dhcp_opts['subnet_id'],
  767. port_id=dhcp_opts.get('port_id'),
  768. external_ids=external_ids,
  769. options={'foo': 'bar'}))
  770. for port_id in self.lport_dhcpv4_disabled:
  771. txn.add(self.nb_api.set_lswitch_port(
  772. port_id, True,
  773. dhcpv4_options=[self.lport_dhcpv4_disabled[port_id]]))
  774. for port_id in self.lport_dhcpv6_disabled:
  775. txn.add(self.nb_api.set_lswitch_port(
  776. port_id, True,
  777. dhcpv6_options=[self.lport_dhcpv6_disabled[port_id]]))
  778. # Delete the first DNS record and clear the second row records
  779. i = 0
  780. for dns_row in self.nb_api.tables['DNS'].rows.values():
  781. if i == 0:
  782. txn.add(self.nb_api.dns_del(dns_row.uuid))
  783. else:
  784. txn.add(self.nb_api.dns_set_records(dns_row.uuid, **{}))
  785. i += 1
  786. def _validate_networks(self, should_match=True):
  787. db_networks = self._list('networks')
  788. db_net_ids = [net['id'] for net in db_networks['networks']]
  789. db_provnet_ports = [utils.ovn_provnet_port_name(net['id'])
  790. for net in db_networks['networks']
  791. if net.get('provider:physical_network')]
  792. # Get the list of lswitch ids stored in the OVN plugin IDL
  793. _plugin_nb_ovn = self.mech_driver._nb_ovn
  794. plugin_lswitch_ids = [
  795. row.name.replace('neutron-', '') for row in (
  796. _plugin_nb_ovn._tables['Logical_Switch'].rows.values())]
  797. # Get the list of lswitch ids stored in the monitor IDL connection
  798. monitor_lswitch_ids = [
  799. row.name.replace('neutron-', '') for row in (
  800. self.nb_api.tables['Logical_Switch'].rows.values())]
  801. # Get the list of provnet ports stored in the OVN plugin IDL
  802. plugin_provnet_ports = [row.name for row in (
  803. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  804. if row.name.startswith(ovn_const.OVN_PROVNET_PORT_NAME_PREFIX)]
  805. # Get the list of provnet ports stored in the monitor IDL connection
  806. monitor_provnet_ports = [row.name for row in (
  807. self.nb_api.tables['Logical_Switch_Port'].rows.values())
  808. if row.name.startswith(ovn_const.OVN_PROVNET_PORT_NAME_PREFIX)]
  809. if should_match:
  810. self.assertItemsEqual(db_net_ids, plugin_lswitch_ids)
  811. self.assertItemsEqual(db_net_ids, monitor_lswitch_ids)
  812. self.assertItemsEqual(db_provnet_ports, plugin_provnet_ports)
  813. self.assertItemsEqual(db_provnet_ports, monitor_provnet_ports)
  814. else:
  815. self.assertRaises(
  816. AssertionError, self.assertItemsEqual, db_net_ids,
  817. plugin_lswitch_ids)
  818. self.assertRaises(
  819. AssertionError, self.assertItemsEqual, db_net_ids,
  820. monitor_lswitch_ids)
  821. self.assertRaises(
  822. AssertionError, self.assertItemsEqual, db_provnet_ports,
  823. plugin_provnet_ports)
  824. self.assertRaises(
  825. AssertionError, self.assertItemsEqual, db_provnet_ports,
  826. monitor_provnet_ports)
  827. def _validate_metadata_ports(self, should_match=True):
  828. """Validate metadata ports.
  829. This method will check that all networks have one and only one metadata
  830. port and that every metadata port in Neutron also exists in OVN.
  831. """
  832. db_ports = self._list('ports')
  833. db_metadata_ports_ids = []
  834. db_metadata_ports_nets = []
  835. for port in db_ports['ports']:
  836. if (port['device_owner'] == constants.DEVICE_OWNER_DHCP and
  837. port['device_id'].startswith('ovnmeta')):
  838. db_metadata_ports_ids.append(port['id'])
  839. db_metadata_ports_nets.append(port['network_id'])
  840. db_networks = self._list('networks')
  841. db_net_ids = [net['id'] for net in db_networks['networks']]
  842. # Retrieve all localports in OVN
  843. _plugin_nb_ovn = self.mech_driver._nb_ovn
  844. plugin_metadata_ports = [row.name for row in (
  845. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  846. if row.type == 'localport']
  847. if should_match:
  848. # Check that metadata ports exist in both Neutron and OVN dbs.
  849. self.assertItemsEqual(db_metadata_ports_ids, plugin_metadata_ports)
  850. # Check that all networks have one and only one metadata port.
  851. self.assertItemsEqual(db_metadata_ports_nets, db_net_ids)
  852. else:
  853. metadata_sync = (sorted(db_metadata_ports_ids) ==
  854. sorted(plugin_metadata_ports))
  855. metadata_unique = (sorted(db_net_ids) ==
  856. sorted(db_metadata_ports_nets))
  857. self.assertFalse(metadata_sync and metadata_unique)
  858. def _validate_ports(self, should_match=True):
  859. db_ports = self._list('ports')
  860. db_port_ids = [port['id'] for port in db_ports['ports'] if
  861. not utils.is_lsp_ignored(port)]
  862. db_port_ids_dhcp_valid = set(
  863. port['id'] for port in db_ports['ports']
  864. if not utils.is_network_device_port(port) and
  865. port['id'] not in self.lport_dhcp_ignored)
  866. _plugin_nb_ovn = self.mech_driver._nb_ovn
  867. plugin_lport_ids = [
  868. row.name for row in (
  869. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  870. if ovn_const.OVN_PORT_NAME_EXT_ID_KEY in row.external_ids]
  871. plugin_lport_ids_dhcpv4_enabled = [
  872. row.name for row in (
  873. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  874. if row.dhcpv4_options]
  875. plugin_lport_ids_dhcpv6_enabled = [
  876. row.name for row in (
  877. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  878. if row.dhcpv6_options]
  879. monitor_lport_ids = [
  880. row.name for row in (
  881. self.nb_api.tables['Logical_Switch_Port'].
  882. rows.values())
  883. if ovn_const.OVN_PORT_NAME_EXT_ID_KEY in row.external_ids]
  884. monitor_lport_ids_dhcpv4_enabled = [
  885. row.name for row in (
  886. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  887. if row.dhcpv4_options]
  888. monitor_lport_ids_dhcpv6_enabled = [
  889. row.name for row in (
  890. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  891. if row.dhcpv6_options]
  892. if should_match:
  893. self.assertItemsEqual(db_port_ids, plugin_lport_ids)
  894. self.assertItemsEqual(db_port_ids, monitor_lport_ids)
  895. expected_dhcpv4_options_ports_ids = (
  896. db_port_ids_dhcp_valid.difference(
  897. set(self.lport_dhcpv4_disabled.keys())))
  898. self.assertItemsEqual(expected_dhcpv4_options_ports_ids,
  899. plugin_lport_ids_dhcpv4_enabled)
  900. self.assertItemsEqual(expected_dhcpv4_options_ports_ids,
  901. monitor_lport_ids_dhcpv4_enabled)
  902. expected_dhcpv6_options_ports_ids = (
  903. db_port_ids_dhcp_valid.difference(
  904. set(self.lport_dhcpv6_disabled.keys())))
  905. self.assertItemsEqual(expected_dhcpv6_options_ports_ids,
  906. plugin_lport_ids_dhcpv6_enabled)
  907. self.assertItemsEqual(expected_dhcpv6_options_ports_ids,
  908. monitor_lport_ids_dhcpv6_enabled)
  909. # Check if unknow address is set for the expected lports.
  910. for row in (
  911. self.nb_api.tables['Logical_Switch_Port'].
  912. rows.values()):
  913. if row.name in self.expected_ports_with_unknown_addr:
  914. self.assertIn('unknown', row.addresses)
  915. else:
  916. self.assertRaises(
  917. AssertionError, self.assertItemsEqual, db_port_ids,
  918. plugin_lport_ids)
  919. self.assertRaises(
  920. AssertionError, self.assertItemsEqual, db_port_ids,
  921. monitor_lport_ids)
  922. self.assertRaises(
  923. AssertionError, self.assertItemsEqual, db_port_ids,
  924. plugin_lport_ids_dhcpv4_enabled)
  925. self.assertRaises(
  926. AssertionError, self.assertItemsEqual, db_port_ids,
  927. monitor_lport_ids_dhcpv4_enabled)
  928. @staticmethod
  929. def _build_acl_for_pgs(priority, direction, log, name, action,
  930. severity, match, port_group, **kwargs):
  931. return {
  932. 'priority': priority,
  933. 'direction': direction,
  934. 'log': log,
  935. 'name': name,
  936. 'action': action,
  937. 'severity': severity,
  938. 'match': match,
  939. 'external_ids': kwargs,
  940. }
  941. def _validate_dhcp_opts(self, should_match=True):
  942. observed_plugin_dhcp_options_rows = []
  943. _plugin_nb_ovn = self.mech_driver._nb_ovn
  944. for row in _plugin_nb_ovn._tables['DHCP_Options'].rows.values():
  945. opts = dict(row.options)
  946. ids = dict(row.external_ids)
  947. if ids.get('subnet_id') not in self.match_old_mac_dhcp_subnets:
  948. if 'server_mac' in opts:
  949. opts['server_mac'] = '01:02:03:04:05:06'
  950. else:
  951. opts['server_id'] = '01:02:03:04:05:06'
  952. observed_plugin_dhcp_options_rows.append({
  953. 'cidr': row.cidr, 'external_ids': row.external_ids,
  954. 'options': opts})
  955. observed_monitor_dhcp_options_rows = []
  956. for row in self.nb_api.tables['DHCP_Options'].rows.values():
  957. opts = dict(row.options)
  958. ids = dict(row.external_ids)
  959. if ids.get('subnet_id') not in self.match_old_mac_dhcp_subnets:
  960. if 'server_mac' in opts:
  961. opts['server_mac'] = '01:02:03:04:05:06'
  962. else:
  963. opts['server_id'] = '01:02:03:04:05:06'
  964. observed_monitor_dhcp_options_rows.append({
  965. 'cidr': row.cidr, 'external_ids': row.external_ids,
  966. 'options': opts})
  967. if should_match:
  968. self.assertItemsEqual(self.expected_dhcp_options_rows,
  969. observed_plugin_dhcp_options_rows)
  970. self.assertItemsEqual(self.expected_dhcp_options_rows,
  971. observed_monitor_dhcp_options_rows)
  972. else:
  973. self.assertRaises(
  974. AssertionError, self.assertItemsEqual,
  975. self.expected_dhcp_options_rows,
  976. observed_plugin_dhcp_options_rows)
  977. self.assertRaises(
  978. AssertionError, self.assertItemsEqual,
  979. self.expected_dhcp_options_rows,
  980. observed_monitor_dhcp_options_rows)
  981. def _build_acl_to_compare(self, acl, extra_fields=None):
  982. acl_to_compare = {}
  983. for acl_key in getattr(acl, "_data", {}):
  984. try:
  985. acl_to_compare[acl_key] = getattr(acl, acl_key)
  986. except AttributeError:
  987. pass
  988. return acl_utils.filter_acl_dict(acl_to_compare, extra_fields)
  989. def _validate_acls(self, should_match=True):
  990. # Get the neutron DB ACLs.
  991. db_acls = []
  992. sg_cache = {}
  993. subnet_cache = {}
  994. _plugin_nb_ovn = self.mech_driver._nb_ovn
  995. if not _plugin_nb_ovn.is_port_groups_supported():
  996. for db_port in self._list('ports')['ports']:
  997. acls = acl_utils.add_acls(self.plugin,
  998. context.get_admin_context(),
  999. db_port,
  1000. sg_cache,
  1001. subnet_cache,
  1002. self.mech_driver._nb_ovn)
  1003. for acl in acls:
  1004. db_acls.append(acl_utils.filter_acl_dict(acl))
  1005. else:
  1006. # ACLs due to SGs and default drop port group
  1007. for sg in self._list('security-groups')['security_groups']:
  1008. for sgr in sg['security_group_rules']:
  1009. acl = acl_utils._add_sg_rule_acl_for_port_group(
  1010. utils.ovn_port_group_name(sg['id']), sgr,
  1011. self.mech_driver._nb_ovn)
  1012. db_acls.append(TestOvnNbSync._build_acl_for_pgs(**acl))
  1013. for acl in acl_utils.add_acls_for_drop_port_group(
  1014. ovn_const.OVN_DROP_PORT_GROUP_NAME):
  1015. db_acls.append(TestOvnNbSync._build_acl_for_pgs(**acl))
  1016. # Get the list of ACLs stored in the OVN plugin IDL.
  1017. plugin_acls = []
  1018. for row in _plugin_nb_ovn._tables['Logical_Switch'].rows.values():
  1019. for acl in getattr(row, 'acls', []):
  1020. plugin_acls.append(self._build_acl_to_compare(acl))
  1021. if self.nb_api.is_port_groups_supported():
  1022. for row in _plugin_nb_ovn._tables['Port_Group'].rows.values():
  1023. for acl in getattr(row, 'acls', []):
  1024. plugin_acls.append(
  1025. self._build_acl_to_compare(
  1026. acl, extra_fields=['external_ids']))
  1027. # Get the list of ACLs stored in the OVN monitor IDL.
  1028. monitor_acls = []
  1029. for row in self.nb_api.tables['Logical_Switch'].rows.values():
  1030. for acl in getattr(row, 'acls', []):
  1031. monitor_acls.append(self._build_acl_to_compare(acl))
  1032. if _plugin_nb_ovn.is_port_groups_supported():
  1033. for row in self.nb_api.tables['Port_Group'].rows.values():
  1034. for acl in getattr(row, 'acls', []):
  1035. monitor_acls.append(self._build_acl_to_compare(acl))
  1036. if should_match:
  1037. self.assertItemsEqual(db_acls, plugin_acls)
  1038. self.assertItemsEqual(db_acls, monitor_acls)
  1039. else:
  1040. self.assertRaises(
  1041. AssertionError, self.assertItemsEqual,
  1042. db_acls, plugin_acls)
  1043. self.assertRaises(
  1044. AssertionError, self.assertItemsEqual,
  1045. db_acls, monitor_acls)
  1046. def _validate_routers_and_router_ports(self, should_match=True):
  1047. db_routers = self._list('routers')
  1048. db_router_ids = []
  1049. db_routes = {}
  1050. db_nats = {}
  1051. for db_router in db_routers['routers']:
  1052. db_router_ids.append(db_router['id'])
  1053. db_routes[db_router['id']] = [db_route['destination'] +
  1054. db_route['nexthop']
  1055. for db_route in db_router['routes']]
  1056. db_nats[db_router['id']] = []
  1057. if db_router.get(l3.EXTERNAL_GW_INFO):
  1058. gateways = self.l3_plugin._ovn_client._get_gw_info(
  1059. self.context, db_router)
  1060. for gw_info in gateways:
  1061. # Add gateway default route and snats
  1062. if gw_info.gateway_ip:
  1063. db_routes[db_router['id']].append(gw_info.ip_prefix +
  1064. gw_info.gateway_ip)
  1065. if (gw_info.ip_version == constants.IP_VERSION_4 and
  1066. gw_info.router_ip and
  1067. utils.is_snat_enabled(db_router)):
  1068. networks = self.l3_plugin._ovn_client.\
  1069. _get_v4_network_of_all_router_ports(
  1070. self.context, db_router['id'])
  1071. db_nats[db_router['id']].extend(
  1072. [gw_info.router_ip + network + 'snat'
  1073. for network in networks])
  1074. fips = self._list('floatingips')
  1075. fip_macs = {}
  1076. if ovn_config.is_ovn_distributed_floating_ip():
  1077. params = 'device_owner=%s' % constants.DEVICE_OWNER_FLOATINGIP
  1078. fports = self._list('ports', query_params=params)['ports']
  1079. fip_macs = {p['device_id']: p['mac_address'] for p in fports
  1080. if p['device_id']}
  1081. for fip in fips['floatingips']:
  1082. if fip['router_id']:
  1083. mac_address = ''
  1084. fip_port = ''
  1085. if fip['id'] in fip_macs:
  1086. fip_port = fip['port_id']
  1087. db_nats[fip['router_id']].append(
  1088. fip['floating_ip_address'] + fip['fixed_ip_address'] +
  1089. 'dnat_and_snat' + mac_address + fip_port)
  1090. _plugin_nb_ovn = self.mech_driver._nb_ovn
  1091. plugin_lrouter_ids = [
  1092. row.name.replace('neutron-', '') for row in (
  1093. _plugin_nb_ovn._tables['Logical_Router'].rows.values())]
  1094. monitor_lrouter_ids = [
  1095. row.name.replace('neutron-', '') for row in (
  1096. self.nb_api.tables['Logical_Router'].rows.values())]
  1097. if should_match:
  1098. self.assertItemsEqual(db_router_ids, plugin_lrouter_ids)
  1099. self.assertItemsEqual(db_router_ids, monitor_lrouter_ids)
  1100. else:
  1101. self.assertRaises(
  1102. AssertionError, self.assertItemsEqual, db_router_ids,
  1103. plugin_lrouter_ids)
  1104. self.assertRaises(
  1105. AssertionError, self.assertItemsEqual, db_router_ids,
  1106. monitor_lrouter_ids)
  1107. def _get_networks_for_router_port(port_fixed_ips):
  1108. _ovn_client = self.l3_plugin._ovn_client
  1109. networks, _ = (
  1110. _ovn_client._get_nets_and_ipv6_ra_confs_for_router_port(
  1111. port_fixed_ips))
  1112. return networks
  1113. def _get_ipv6_ra_configs_for_router_port(port_fixed_ips):
  1114. _ovn_client = self.l3_plugin._ovn_client
  1115. networks, ipv6_ra_configs = (
  1116. _ovn_client._get_nets_and_ipv6_ra_confs_for_router_port(
  1117. port_fixed_ips))
  1118. return ipv6_ra_configs
  1119. for router_id in db_router_ids:
  1120. r_ports = self._list('ports',
  1121. query_params='device_id=%s' % (router_id))
  1122. r_port_ids = [p['id'] for p in r_ports['ports']]
  1123. r_port_networks = {
  1124. p['id']:
  1125. _get_networks_for_router_port(p['fixed_ips'])
  1126. for p in r_ports['ports']}
  1127. r_port_ipv6_ra_configs = {
  1128. p['id']: _get_ipv6_ra_configs_for_router_port(p['fixed_ips'])
  1129. for p in r_ports['ports']}
  1130. r_routes = db_routes[router_id]
  1131. r_nats = db_nats[router_id]
  1132. try:
  1133. lrouter = idlutils.row_by_value(
  1134. self.mech_driver._nb_ovn.idl, 'Logical_Router', 'name',
  1135. 'neutron-' + str(router_id), None)
  1136. lports = getattr(lrouter, 'ports', [])
  1137. plugin_lrouter_port_ids = [lport.name.replace('lrp-', '')
  1138. for lport in lports]
  1139. plugin_lport_networks = {
  1140. lport.name.replace('lrp-', ''): lport.networks
  1141. for lport in lports}
  1142. plugin_lport_ra_configs = {
  1143. lport.name.replace('lrp-', ''): lport.ipv6_ra_configs
  1144. for lport in lports}
  1145. sroutes = getattr(lrouter, 'static_routes', [])
  1146. plugin_routes = [sroute.ip_prefix + sroute.nexthop
  1147. for sroute in sroutes]
  1148. nats = getattr(lrouter, 'nat', [])
  1149. plugin_nats = [
  1150. nat.external_ip + nat.logical_ip + nat.type +
  1151. (nat.external_mac[0] if nat.external_mac else '') +
  1152. (nat.logical_port[0] if nat.logical_port else '')
  1153. for nat in nats]
  1154. except idlutils.RowNotFound:
  1155. plugin_lrouter_port_ids = []
  1156. plugin_routes = []
  1157. plugin_nats = []
  1158. try:
  1159. lrouter = idlutils.row_by_value(
  1160. self.nb_api.idl, 'Logical_Router', 'name',
  1161. 'neutron-' + router_id, None)
  1162. lports = getattr(lrouter, 'ports', [])
  1163. monitor_lrouter_port_ids = [lport.name.replace('lrp-', '')
  1164. for lport in lports]
  1165. monitor_lport_networks = {
  1166. lport.name.replace('lrp-', ''): lport.networks
  1167. for lport in lports}
  1168. monitor_lport_ra_configs = {
  1169. lport.name.replace('lrp-', ''): lport.ipv6_ra_configs
  1170. for lport in lports}
  1171. sroutes = getattr(lrouter, 'static_routes', [])
  1172. monitor_routes = [sroute.ip_prefix + sroute.nexthop
  1173. for sroute in sroutes]
  1174. nats = getattr(lrouter, 'nat', [])
  1175. monitor_nats = [
  1176. nat.external_ip + nat.logical_ip + nat.type +
  1177. (nat.external_mac[0] if nat.external_mac else '') +
  1178. (nat.logical_port[0] if nat.logical_port else '')
  1179. for nat in nats]
  1180. except idlutils.RowNotFound:
  1181. monitor_lrouter_port_ids = []
  1182. monitor_routes = []
  1183. monitor_nats = []
  1184. if should_match:
  1185. self.assertItemsEqual(r_port_ids, plugin_lrouter_port_ids)
  1186. self.assertItemsEqual(r_port_ids, monitor_lrouter_port_ids)
  1187. for p in plugin_lport_networks:
  1188. self.assertItemsEqual(r_port_networks[p],
  1189. plugin_lport_networks[p])
  1190. self.assertItemsEqual(r_port_ipv6_ra_configs[p],
  1191. plugin_lport_ra_configs[p])
  1192. for p in monitor_lport_networks:
  1193. self.assertItemsEqual(r_port_networks[p],
  1194. monitor_lport_networks[p])
  1195. self.assertItemsEqual(r_port_ipv6_ra_configs[p],
  1196. monitor_lport_ra_configs[p])
  1197. self.assertItemsEqual(r_routes, plugin_routes)
  1198. self.assertItemsEqual(r_routes, monitor_routes)
  1199. self.assertItemsEqual(r_nats, plugin_nats)
  1200. self.assertItemsEqual(r_nats, monitor_nats)
  1201. else:
  1202. self.assertRaises(
  1203. AssertionError, self.assertItemsEqual, r_port_ids,
  1204. plugin_lrouter_port_ids)
  1205. self.assertRaises(
  1206. AssertionError, self.assertItemsEqual, r_port_ids,
  1207. monitor_lrouter_port_ids)
  1208. for _p in self.update_lrouter_ports:
  1209. p = _p[0].replace('lrp-', '')
  1210. if p in plugin_lport_networks:
  1211. self.assertRaises(
  1212. AssertionError, self.assertItemsEqual,
  1213. r_port_networks[p], plugin_lport_networks[p])
  1214. self.assertRaises(
  1215. AssertionError, self.assertItemsEqual,
  1216. r_port_ipv6_ra_configs[p],
  1217. plugin_lport_ra_configs[p])
  1218. if p in monitor_lport_networks:
  1219. self.assertRaises(
  1220. AssertionError, self.assertItemsEqual,
  1221. r_port_networks[p], monitor_lport_networks[p])
  1222. self.assertRaises(
  1223. AssertionError, self.assertItemsEqual,
  1224. r_port_ipv6_ra_configs[p],
  1225. monitor_lport_ra_configs[p])
  1226. self.assertRaises(
  1227. AssertionError, self.assertItemsEqual, r_routes,
  1228. plugin_routes)
  1229. self.assertRaises(
  1230. AssertionError, self.assertItemsEqual, r_routes,
  1231. monitor_routes)
  1232. self.assertRaises(
  1233. AssertionError, self.assertItemsEqual, r_nats,
  1234. plugin_nats)
  1235. self.assertRaises(
  1236. AssertionError, self.assertItemsEqual, r_nats,
  1237. monitor_nats)
  1238. def _validate_address_sets(self, should_match=True):
  1239. _plugin_nb_ovn = self.mech_driver._nb_ovn
  1240. if _plugin_nb_ovn.is_port_groups_supported():
  1241. # If Port Groups are supported, no Address Sets are expected.
  1242. # This validation is still useful as we expect existing ones to
  1243. # be deleted after the sync.
  1244. db_sgs = []
  1245. else:
  1246. db_ports = self._list('ports')['ports']
  1247. sgs = self._list('security-groups')['security_groups']
  1248. db_sgs = {}
  1249. for sg in sgs:
  1250. for ip_version in ['ip4', 'ip6']:
  1251. name = utils.ovn_addrset_name(sg['id'], ip_version)
  1252. db_sgs[name] = []
  1253. for port in db_ports:
  1254. sg_ids = utils.get_lsp_security_groups(port)
  1255. addresses = acl_utils.acl_port_ips(port)
  1256. for sg_id in sg_ids:
  1257. for ip_version in addresses:
  1258. name = utils.ovn_addrset_name(sg_id, ip_version)
  1259. db_sgs[name].extend(addresses[ip_version])
  1260. nb_address_sets = _plugin_nb_ovn.get_address_sets()
  1261. nb_sgs = {}
  1262. for nb_sgid, nb_values in nb_address_sets.items():
  1263. nb_sgs[nb_sgid] = nb_values['addresses']
  1264. mn_sgs = {}
  1265. for row in self.nb_api.tables['Address_Set'].rows.values():
  1266. mn_sgs[getattr(row, 'name')] = getattr(row, 'addresses')
  1267. if should_match:
  1268. self.assertItemsEqual(nb_sgs, db_sgs)
  1269. self.assertItemsEqual(mn_sgs, db_sgs)
  1270. else:
  1271. # This condition is to cover the case when we use Port Groups
  1272. # and we completely deleted the NB DB. At this point, the expected
  1273. # number of Address Sets is 0 and the observed number in NB is
  1274. # also 0 so we can't have the asserts below as both will be empty.
  1275. if _plugin_nb_ovn.is_port_groups_supported() and nb_sgs:
  1276. self.assertRaises(AssertionError, self.assertItemsEqual,
  1277. nb_sgs, db_sgs)
  1278. self.assertRaises(AssertionError, self.assertItemsEqual,
  1279. mn_sgs, db_sgs)
  1280. def _validate_port_groups(self, should_match=True):
  1281. _plugin_nb_ovn = self.mech_driver._nb_ovn
  1282. if not _plugin_nb_ovn.is_port_groups_supported():
  1283. return
  1284. db_pgs = []
  1285. for sg in self._list('security-groups')['security_groups']:
  1286. db_pgs.append(utils.ovn_port_group_name(sg['id']))
  1287. db_pgs.append(ovn_const.OVN_DROP_PORT_GROUP_NAME)
  1288. nb_pgs = _plugin_nb_ovn.get_port_groups()
  1289. mn_pgs = []
  1290. for row in self.nb_api.tables['Port_Group'].rows.values():
  1291. mn_pgs.append(getattr(row, 'name', ''))
  1292. if should_match:
  1293. self.assertItemsEqual(nb_pgs, db_pgs)
  1294. self.assertItemsEqual(mn_pgs, db_pgs)
  1295. else:
  1296. self.assertRaises(AssertionError, self.assertItemsEqual,
  1297. nb_pgs, db_pgs)
  1298. self.assertRaises(AssertionError, self.assertItemsEqual,
  1299. mn_pgs, db_pgs)
  1300. def _delete_metadata_ports(self):
  1301. """Delete some metadata ports.
  1302. This method will delete one half of the metadata ports from Neutron and
  1303. the remaining ones only from OVN. This way we can exercise the metadata
  1304. sync completely: ie., that metadata ports are recreated in Neutron when
  1305. missing and that the corresponding OVN localports are also created.
  1306. """
  1307. db_ports = self._list('ports')
  1308. db_metadata_ports = [port for port in db_ports['ports'] if
  1309. port['device_owner'] ==
  1310. constants.DEVICE_OWNER_DHCP and
  1311. port['device_id'].startswith('ovnmeta')]
  1312. lswitches = {}
  1313. ports_to_delete = len(db_metadata_ports) / 2
  1314. for port in db_metadata_ports:
  1315. lswitches[port['id']] = 'neutron-' + port['network_id']
  1316. if ports_to_delete:
  1317. self._delete('ports', port['id'])
  1318. ports_to_delete -= 1
  1319. _plugin_nb_ovn = self.mech_driver._nb_ovn
  1320. plugin_metadata_ports = [row.name for row in (
  1321. _plugin_nb_ovn._tables['Logical_Switch_Port'].rows.values())
  1322. if row.type == 'localport']
  1323. with self.nb_api.transaction(check_error=True) as txn:
  1324. for port in plugin_metadata_ports:
  1325. txn.add(self.nb_api.delete_lswitch_port(port, lswitches[port],
  1326. True))
  1327. def _validate_dns_records(self, should_match=True):
  1328. observed_dns_records = []
  1329. for dns_row in self.nb_api.tables['DNS'].rows.values():
  1330. observed_dns_records.append(
  1331. {'external_ids': dns_row.external_ids,
  1332. 'records': dns_row.records})
  1333. if should_match:
  1334. self.assertItemsEqual(self.expected_dns_records,
  1335. observed_dns_records)
  1336. else:
  1337. self.assertRaises(AssertionError, self.assertItemsEqual,
  1338. self.expected_dns_records, observed_dns_records)
  1339. def _validate_resources(self, should_match=True):
  1340. self._validate_networks(should_match=should_match)
  1341. self._validate_metadata_ports(should_match=should_match)
  1342. self._validate_ports(should_match=should_match)
  1343. self._validate_dhcp_opts(should_match=should_match)
  1344. self._validate_acls(should_match=should_match)
  1345. self._validate_routers_and_router_ports(should_match=should_match)
  1346. self._validate_address_sets(should_match=should_match)
  1347. self._validate_port_groups(should_match=should_match)
  1348. self._validate_dns_records(should_match=should_match)
  1349. def _sync_resources(self, mode):
  1350. nb_synchronizer = ovn_db_sync.OvnNbSynchronizer(
  1351. self.plugin, self.mech_driver._nb_ovn, self.mech_driver._sb_ovn,
  1352. mode, self.mech_driver)
  1353. self.addCleanup(nb_synchronizer.stop)
  1354. nb_synchronizer.do_sync()
  1355. def _test_ovn_nb_sync_helper(self, mode, modify_resources=True,
  1356. restart_ovsdb_processes=False,
  1357. should_match_after_sync=True):
  1358. self._create_resources(restart_ovsdb_processes)
  1359. self._validate_resources(should_match=True)
  1360. if modify_resources:
  1361. self._modify_resources_in_nb_db()
  1362. if restart_ovsdb_processes:
  1363. # Restart the ovsdb-server and plugin idl.
  1364. # This causes a new ovsdb-server to be started with empty
  1365. # OVN NB DB
  1366. self.restart()
  1367. if modify_resources or restart_ovsdb_processes:
  1368. self._validate_resources(should_match=False)
  1369. self._sync_resources(mode)
  1370. self._validate_resources(should_match=should_match_after_sync)
  1371. def test_ovn_nb_sync_repair(self):
  1372. self._test_ovn_nb_sync_helper('repair')
  1373. def test_ovn_nb_sync_repair_delete_ovn_nb_db(self):
  1374. # In this test case, the ovsdb-server for OVN NB DB is restarted
  1375. # with empty OVN NB DB.
  1376. self._test_ovn_nb_sync_helper('repair', modify_resources=False,
  1377. restart_ovsdb_processes=True)
  1378. def test_ovn_nb_sync_log(self):
  1379. self._test_ovn_nb_sync_helper('log', should_match_after_sync=False)
  1380. def test_ovn_nb_sync_off(self):
  1381. self._test_ovn_nb_sync_helper('off', should_match_after_sync=False)
  1382. class TestOvnSbSync(base.TestOVNFunctionalBase):
  1383. def setUp(self):
  1384. super(TestOvnSbSync, self).setUp(ovn_worker=False)
  1385. self.segments_plugin = directory.get_plugin('segments')
  1386. self.sb_synchronizer = ovn_db_sync.OvnSbSynchronizer(
  1387. self.plugin, self.mech_driver._sb_ovn, self.mech_driver)
  1388. self.addCleanup(self.sb_synchronizer.stop)
  1389. self.ctx = context.get_admin_context()
  1390. def get_additional_service_plugins(self):
  1391. p = super(TestOvnSbSync, self).get_additional_service_plugins()
  1392. p.update({'segments': 'neutron.services.segments.plugin.Plugin'})
  1393. return p
  1394. def _sync_resources(self):
  1395. self.sb_synchronizer.sync_hostname_and_physical_networks(self.ctx)
  1396. def create_segment(self, network_id, physical_network, segmentation_id):
  1397. segment_data = {'network_id': network_id,
  1398. 'physical_network': physical_network,
  1399. 'segmentation_id': segmentation_id,
  1400. 'network_type': 'vlan',
  1401. 'name': constants.ATTR_NOT_SPECIFIED,
  1402. 'description': constants.ATTR_NOT_SPECIFIED}
  1403. return self.segments_plugin.create_segment(
  1404. self.ctx, segment={'segment': segment_data})
  1405. def test_ovn_sb_sync_add_new_host(self):
  1406. with self.network() as network:
  1407. network_id = network['network']['id']
  1408. self.create_segment(network_id, 'physnet1', 50)
  1409. self.add_fake_chassis('host1', ['physnet1'])
  1410. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1411. self.assertFalse(segment_hosts)
  1412. self._sync_resources()
  1413. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1414. self.assertEqual({'host1'}, segment_hosts)
  1415. def test_ovn_sb_sync_update_existing_host(self):
  1416. with self.network() as network:
  1417. network_id = network['network']['id']
  1418. segment = self.create_segment(network_id, 'physnet1', 50)
  1419. segments_db.update_segment_host_mapping(
  1420. self.ctx, 'host1', {segment['id']})
  1421. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1422. self.assertEqual({'host1'}, segment_hosts)
  1423. self.add_fake_chassis('host1', ['physnet2'])
  1424. self._sync_resources()
  1425. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1426. self.assertFalse(segment_hosts)
  1427. def test_ovn_sb_sync_delete_stale_host(self):
  1428. with self.network() as network:
  1429. network_id = network['network']['id']
  1430. segment = self.create_segment(network_id, 'physnet1', 50)
  1431. segments_db.update_segment_host_mapping(
  1432. self.ctx, 'host1', {segment['id']})
  1433. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1434. self.assertEqual({'host1'}, segment_hosts)
  1435. # Since there is no chassis in the sb DB, host1 is the stale host
  1436. # recorded in neutron DB. It should be deleted after sync.
  1437. self._sync_resources()
  1438. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1439. self.assertFalse(segment_hosts)
  1440. def test_ovn_sb_sync(self):
  1441. with self.network() as network:
  1442. network_id = network['network']['id']
  1443. seg1 = self.create_segment(network_id, 'physnet1', 50)
  1444. self.create_segment(network_id, 'physnet2', 51)
  1445. segments_db.update_segment_host_mapping(
  1446. self.ctx, 'host1', {seg1['id']})
  1447. segments_db.update_segment_host_mapping(
  1448. self.ctx, 'host2', {seg1['id']})
  1449. segments_db.update_segment_host_mapping(
  1450. self.ctx, 'host3', {seg1['id']})
  1451. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1452. self.assertEqual({'host1', 'host2', 'host3'}, segment_hosts)
  1453. self.add_fake_chassis('host2', ['physnet2'])
  1454. self.add_fake_chassis('host3', ['physnet3'])
  1455. self.add_fake_chassis('host4', ['physnet1'])
  1456. self._sync_resources()
  1457. segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx)
  1458. # host1 should be cleared since it is not in the chassis DB. host3
  1459. # should be cleared since there is no segment for mapping.
  1460. self.assertEqual({'host2', 'host4'}, segment_hosts)
  1461. class TestOvnNbSyncOverTcp(TestOvnNbSync):
  1462. def setUp(self):
  1463. super(TestOvnNbSyncOverTcp, self).setUp()
  1464. ovn_config.cfg.CONF.set_override(
  1465. 'enable_distributed_floating_ip', True, group='ovn')
  1466. def get_ovsdb_server_protocol(self):
  1467. return 'tcp'
  1468. class TestOvnSbSyncOverTcp(TestOvnSbSync):
  1469. def get_ovsdb_server_protocol(self):
  1470. return 'tcp'
  1471. class TestOvnNbSyncOverSsl(TestOvnNbSync):
  1472. def get_ovsdb_server_protocol(self):
  1473. return 'ssl'
  1474. class TestOvnSbSyncOverSsl(TestOvnSbSync):
  1475. def get_ovsdb_server_protocol(self):
  1476. return 'ssl'
  1477. @mock.patch('networking_ovn.ovsdb.impl_idl_ovn.OvsdbNbOvnIdl.'
  1478. 'is_port_groups_supported', lambda *args: False)
  1479. class TestOvnNbSyncNoPgs(TestOvnNbSync):
  1480. pass