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.

764 lines
37 KiB

  1. #
  2. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  3. # not use this file except in compliance with the License. You may obtain
  4. # a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  10. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  11. # License for the specific language governing permissions and limitations
  12. # under the License.
  13. #
  14. import copy
  15. import uuid
  16. import mock
  17. from networking_ovn.common import config
  18. from networking_ovn.common import constants as ovn_const
  19. from networking_ovn.common import utils
  20. from networking_ovn.ovsdb import impl_idl_ovn
  21. from networking_ovn.tests import base
  22. from networking_ovn.tests.unit import fakes
  23. class TestDBImplIdlOvn(base.TestCase):
  24. def _load_ovsdb_fake_rows(self, table, fake_attrs):
  25. for fake_attr in fake_attrs:
  26. fake_row = fakes.FakeOvsdbRow.create_one_ovsdb_row(
  27. attrs=fake_attr)
  28. # Pre-populate ovs idl "._data"
  29. fake_data = copy.deepcopy(fake_attr)
  30. try:
  31. del fake_data["unit_test_id"]
  32. except KeyError:
  33. pass
  34. setattr(fake_row, "_data", fake_data)
  35. table.rows[fake_row.uuid] = fake_row
  36. def _find_ovsdb_fake_row(self, table, key, value):
  37. for fake_row in table.rows.values():
  38. if getattr(fake_row, key) == value:
  39. return fake_row
  40. return None
  41. def _construct_ovsdb_references(self, fake_associations,
  42. parent_table, child_table,
  43. parent_key, child_key,
  44. reference_column_name):
  45. for p_name, c_names in fake_associations.items():
  46. p_row = self._find_ovsdb_fake_row(parent_table, parent_key, p_name)
  47. c_uuids = []
  48. for c_name in c_names:
  49. c_row = self._find_ovsdb_fake_row(child_table, child_key,
  50. c_name)
  51. if not c_row:
  52. continue
  53. # Fake IDL processing (uuid -> row)
  54. c_uuids.append(c_row)
  55. setattr(p_row, reference_column_name, c_uuids)
  56. class TestNBImplIdlOvn(TestDBImplIdlOvn):
  57. fake_set = {
  58. 'lswitches': [
  59. {'name': utils.ovn_name('ls-id-1'),
  60. 'external_ids': {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
  61. 'ls-name-1'}},
  62. {'name': utils.ovn_name('ls-id-2'),
  63. 'external_ids': {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
  64. 'ls-name-2'}},
  65. {'name': utils.ovn_name('ls-id-3'),
  66. 'external_ids': {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
  67. 'ls-name-3'}},
  68. {'name': 'ls-id-4',
  69. 'external_ids': {'not-neutron:network_name': 'ls-name-4'}},
  70. {'name': utils.ovn_name('ls-id-5'),
  71. 'external_ids': {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
  72. 'ls-name-5'}}],
  73. 'lswitch_ports': [
  74. {'name': 'lsp-id-11', 'addresses': ['10.0.1.1'],
  75. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  76. 'lsp-name-11'}},
  77. {'name': 'lsp-id-12', 'addresses': ['10.0.1.2'],
  78. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  79. 'lsp-name-12'}},
  80. {'name': 'lsp-rp-id-1', 'addresses': ['10.0.1.254'],
  81. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  82. 'lsp-rp-name-1'},
  83. 'options': {'router-port':
  84. utils.ovn_lrouter_port_name('orp-id-a1')}},
  85. {'name': 'provnet-ls-id-1', 'addresses': ['unknown'],
  86. 'external_ids': {},
  87. 'options': {'network_name': 'physnet1'}},
  88. {'name': 'lsp-id-21', 'addresses': ['10.0.2.1'],
  89. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  90. 'lsp-name-21'}},
  91. {'name': 'lsp-id-22', 'addresses': ['10.0.2.2'],
  92. 'external_ids': {}},
  93. {'name': 'lsp-id-23', 'addresses': ['10.0.2.3'],
  94. 'external_ids': {'not-neutron:port_name': 'lsp-name-23'}},
  95. {'name': 'lsp-rp-id-2', 'addresses': ['10.0.2.254'],
  96. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  97. 'lsp-rp-name-2'},
  98. 'options': {'router-port':
  99. utils.ovn_lrouter_port_name('orp-id-a2')}},
  100. {'name': 'provnet-ls-id-2', 'addresses': ['unknown'],
  101. 'external_ids': {},
  102. 'options': {'network_name': 'physnet2'}},
  103. {'name': 'lsp-id-31', 'addresses': ['10.0.3.1'],
  104. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  105. 'lsp-name-31'}},
  106. {'name': 'lsp-id-32', 'addresses': ['10.0.3.2'],
  107. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  108. 'lsp-name-32'}},
  109. {'name': 'lsp-rp-id-3', 'addresses': ['10.0.3.254'],
  110. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  111. 'lsp-rp-name-3'},
  112. 'options': {'router-port':
  113. utils.ovn_lrouter_port_name('orp-id-a3')}},
  114. {'name': 'lsp-vpn-id-3', 'addresses': ['10.0.3.253'],
  115. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  116. 'lsp-vpn-name-3'}},
  117. {'name': 'lsp-id-41', 'addresses': ['20.0.1.1'],
  118. 'external_ids': {'not-neutron:port_name': 'lsp-name-41'}},
  119. {'name': 'lsp-rp-id-4', 'addresses': ['20.0.1.254'],
  120. 'external_ids': {},
  121. 'options': {'router-port': 'xrp-id-b1'}},
  122. {'name': 'lsp-id-51', 'addresses': ['20.0.2.1'],
  123. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  124. 'lsp-name-51'}},
  125. {'name': 'lsp-id-52', 'addresses': ['20.0.2.2'],
  126. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  127. 'lsp-name-52'}},
  128. {'name': 'lsp-rp-id-5', 'addresses': ['20.0.2.254'],
  129. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  130. 'lsp-rp-name-5'},
  131. 'options': {'router-port':
  132. utils.ovn_lrouter_port_name('orp-id-b2')}},
  133. {'name': 'lsp-vpn-id-5', 'addresses': ['20.0.2.253'],
  134. 'external_ids': {ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
  135. 'lsp-vpn-name-5'}}],
  136. 'lrouters': [
  137. {'name': utils.ovn_name('lr-id-a'),
  138. 'external_ids': {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  139. 'lr-name-a'}},
  140. {'name': utils.ovn_name('lr-id-b'),
  141. 'external_ids': {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  142. 'lr-name-b'}},
  143. {'name': utils.ovn_name('lr-id-c'),
  144. 'external_ids': {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  145. 'lr-name-c'}},
  146. {'name': utils.ovn_name('lr-id-d'),
  147. 'external_ids': {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  148. 'lr-name-d'}},
  149. {'name': utils.ovn_name('lr-id-e'),
  150. 'external_ids': {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
  151. 'lr-name-e'}}],
  152. 'lrouter_ports': [
  153. {'name': utils.ovn_lrouter_port_name('orp-id-a1'),
  154. 'external_ids': {}, 'networks': ['10.0.1.0/24'],
  155. 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'}},
  156. {'name': utils.ovn_lrouter_port_name('orp-id-a2'),
  157. 'external_ids': {}, 'networks': ['10.0.2.0/24'],
  158. 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'}},
  159. {'name': utils.ovn_lrouter_port_name('orp-id-a3'),
  160. 'external_ids': {}, 'networks': ['10.0.3.0/24'],
  161. 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY:
  162. ovn_const.OVN_GATEWAY_INVALID_CHASSIS}},
  163. {'name': 'xrp-id-b1',
  164. 'external_ids': {}, 'networks': ['20.0.1.0/24']},
  165. {'name': utils.ovn_lrouter_port_name('orp-id-b2'),
  166. 'external_ids': {}, 'networks': ['20.0.2.0/24'],
  167. 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'}},
  168. {'name': utils.ovn_lrouter_port_name('orp-id-b3'),
  169. 'external_ids': {}, 'networks': ['20.0.3.0/24'],
  170. 'options': {}}],
  171. 'static_routes': [{'ip_prefix': '20.0.0.0/16',
  172. 'nexthop': '10.0.3.253'},
  173. {'ip_prefix': '10.0.0.0/16',
  174. 'nexthop': '20.0.2.253'}],
  175. 'nats': [{'external_ip': '10.0.3.1', 'logical_ip': '20.0.0.0/16',
  176. 'type': 'snat'},
  177. {'external_ip': '20.0.2.1', 'logical_ip': '10.0.0.0/24',
  178. 'type': 'snat'},
  179. {'external_ip': '20.0.2.4', 'logical_ip': '10.0.0.4',
  180. 'type': 'dnat_and_snat', 'external_mac': [],
  181. 'logical_port': []},
  182. {'external_ip': '20.0.2.5', 'logical_ip': '10.0.0.5',
  183. 'type': 'dnat_and_snat',
  184. 'external_mac': ['00:01:02:03:04:05'],
  185. 'logical_port': ['lsp-id-001']}],
  186. 'acls': [
  187. {'unit_test_id': 1,
  188. 'action': 'allow-related', 'direction': 'from-lport',
  189. 'external_ids': {'neutron:lport': 'lsp-id-11'},
  190. 'match': 'inport == "lsp-id-11" && ip4'},
  191. {'unit_test_id': 2,
  192. 'action': 'allow-related', 'direction': 'to-lport',
  193. 'external_ids': {'neutron:lport': 'lsp-id-11'},
  194. 'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'},
  195. {'unit_test_id': 3,
  196. 'action': 'allow-related', 'direction': 'from-lport',
  197. 'external_ids': {'neutron:lport': 'lsp-id-12'},
  198. 'match': 'inport == "lsp-id-12" && ip4'},
  199. {'unit_test_id': 4,
  200. 'action': 'allow-related', 'direction': 'to-lport',
  201. 'external_ids': {'neutron:lport': 'lsp-id-12'},
  202. 'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'},
  203. {'unit_test_id': 5,
  204. 'action': 'allow-related', 'direction': 'from-lport',
  205. 'external_ids': {'neutron:lport': 'lsp-id-21'},
  206. 'match': 'inport == "lsp-id-21" && ip4'},
  207. {'unit_test_id': 6,
  208. 'action': 'allow-related', 'direction': 'to-lport',
  209. 'external_ids': {'neutron:lport': 'lsp-id-21'},
  210. 'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'},
  211. {'unit_test_id': 7,
  212. 'action': 'allow-related', 'direction': 'from-lport',
  213. 'external_ids': {'neutron:lport': 'lsp-id-41'},
  214. 'match': 'inport == "lsp-id-41" && ip4'},
  215. {'unit_test_id': 8,
  216. 'action': 'allow-related', 'direction': 'to-lport',
  217. 'external_ids': {'neutron:lport': 'lsp-id-41'},
  218. 'match': 'outport == "lsp-id-41" && ip4.src == $as_ip4_id_4'},
  219. {'unit_test_id': 9,
  220. 'action': 'allow-related', 'direction': 'from-lport',
  221. 'external_ids': {'neutron:lport': 'lsp-id-52'},
  222. 'match': 'inport == "lsp-id-52" && ip4'},
  223. {'unit_test_id': 10,
  224. 'action': 'allow-related', 'direction': 'to-lport',
  225. 'external_ids': {'neutron:lport': 'lsp-id-52'},
  226. 'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'}],
  227. 'dhcp_options': [
  228. {'cidr': '10.0.1.0/24',
  229. 'external_ids': {'subnet_id': 'subnet-id-10-0-1-0'},
  230. 'options': {'mtu': '1442', 'router': '10.0.1.254'}},
  231. {'cidr': '10.0.2.0/24',
  232. 'external_ids': {'subnet_id': 'subnet-id-10-0-2-0'},
  233. 'options': {'mtu': '1442', 'router': '10.0.2.254'}},
  234. {'cidr': '10.0.1.0/26',
  235. 'external_ids': {'subnet_id': 'subnet-id-10-0-1-0',
  236. 'port_id': 'lsp-vpn-id-3'},
  237. 'options': {'mtu': '1442', 'router': '10.0.1.1'}},
  238. {'cidr': '20.0.1.0/24',
  239. 'external_ids': {'subnet_id': 'subnet-id-20-0-1-0'},
  240. 'options': {'mtu': '1442', 'router': '20.0.1.254'}},
  241. {'cidr': '20.0.2.0/24',
  242. 'external_ids': {'subnet_id': 'subnet-id-20-0-2-0',
  243. 'port_id': 'lsp-vpn-id-5'},
  244. 'options': {'mtu': '1442', 'router': '20.0.2.254'}},
  245. {'cidr': '2001:dba::/64',
  246. 'external_ids': {'subnet_id': 'subnet-id-2001-dba',
  247. 'port_id': 'lsp-vpn-id-5'},
  248. 'options': {'server_id': '12:34:56:78:9a:bc'}},
  249. {'cidr': '30.0.1.0/24',
  250. 'external_ids': {'port_id': 'port-id-30-0-1-0'},
  251. 'options': {'mtu': '1442', 'router': '30.0.2.254'}},
  252. {'cidr': '30.0.2.0/24', 'external_ids': {}, 'options': {}}],
  253. 'address_sets': [
  254. {'name': '$as_ip4_id_1',
  255. 'addresses': ['10.0.1.1', '10.0.1.2'],
  256. 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'id_1'}},
  257. {'name': '$as_ip4_id_2',
  258. 'addresses': ['10.0.2.1'],
  259. 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'id_2'}},
  260. {'name': '$as_ip4_id_3',
  261. 'addresses': ['10.0.3.1', '10.0.3.2'],
  262. 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'id_3'}},
  263. {'name': '$as_ip4_id_4',
  264. 'addresses': ['20.0.1.1', '20.0.1.2'],
  265. 'external_ids': {}},
  266. {'name': '$as_ip4_id_5',
  267. 'addresses': ['20.0.2.1', '20.0.2.2'],
  268. 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'id_5'}},
  269. ]}
  270. fake_associations = {
  271. 'lstolsp': {
  272. utils.ovn_name('ls-id-1'): [
  273. 'lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1', 'provnet-ls-id-1'],
  274. utils.ovn_name('ls-id-2'): [
  275. 'lsp-id-21', 'lsp-id-22', 'lsp-id-23', 'lsp-rp-id-2',
  276. 'provnet-ls-id-2'],
  277. utils.ovn_name('ls-id-3'): [
  278. 'lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'],
  279. 'ls-id-4': [
  280. 'lsp-id-41', 'lsp-rp-id-4'],
  281. utils.ovn_name('ls-id-5'): [
  282. 'lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5']},
  283. 'lrtolrp': {
  284. utils.ovn_name('lr-id-a'): [
  285. utils.ovn_lrouter_port_name('orp-id-a1'),
  286. utils.ovn_lrouter_port_name('orp-id-a2'),
  287. utils.ovn_lrouter_port_name('orp-id-a3')],
  288. utils.ovn_name('lr-id-b'): [
  289. 'xrp-id-b1',
  290. utils.ovn_lrouter_port_name('orp-id-b2')]},
  291. 'lrtosroute': {
  292. utils.ovn_name('lr-id-a'): ['20.0.0.0/16'],
  293. utils.ovn_name('lr-id-b'): ['10.0.0.0/16']
  294. },
  295. 'lrtonat': {
  296. utils.ovn_name('lr-id-a'): ['10.0.3.1'],
  297. utils.ovn_name('lr-id-b'): ['20.0.2.1', '20.0.2.4', '20.0.2.5'],
  298. },
  299. 'lstoacl': {
  300. utils.ovn_name('ls-id-1'): [1, 2, 3, 4],
  301. utils.ovn_name('ls-id-2'): [5, 6],
  302. 'ls-id-4': [7, 8],
  303. utils.ovn_name('ls-id-5'): [9, 10]}
  304. }
  305. def setUp(self):
  306. super(TestNBImplIdlOvn, self).setUp()
  307. self.lswitch_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  308. self.lsp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  309. self.lrouter_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  310. self.lrp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  311. self.sroute_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  312. self.nat_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  313. self.acl_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  314. self.dhcp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  315. self.address_set_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  316. self._tables = {}
  317. self._tables['Logical_Switch'] = self.lswitch_table
  318. self._tables['Logical_Switch_Port'] = self.lsp_table
  319. self._tables['Logical_Router'] = self.lrouter_table
  320. self._tables['Logical_Router_Port'] = self.lrp_table
  321. self._tables['Logical_Router_Static_Route'] = self.sroute_table
  322. self._tables['ACL'] = self.acl_table
  323. self._tables['DHCP_Options'] = self.dhcp_table
  324. self._tables['Address_Set'] = self.address_set_table
  325. with mock.patch.object(impl_idl_ovn, 'get_connection',
  326. return_value=mock.Mock()):
  327. impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection = None
  328. self.nb_ovn_idl = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())
  329. self.nb_ovn_idl.idl.tables = self._tables
  330. def _load_nb_db(self):
  331. # Load Switches and Switch Ports
  332. fake_lswitches = TestNBImplIdlOvn.fake_set['lswitches']
  333. self._load_ovsdb_fake_rows(self.lswitch_table, fake_lswitches)
  334. fake_lsps = TestNBImplIdlOvn.fake_set['lswitch_ports']
  335. self._load_ovsdb_fake_rows(self.lsp_table, fake_lsps)
  336. # Associate switches and ports
  337. self._construct_ovsdb_references(
  338. TestNBImplIdlOvn.fake_associations['lstolsp'],
  339. self.lswitch_table, self.lsp_table,
  340. 'name', 'name', 'ports')
  341. # Load Routers and Router Ports
  342. fake_lrouters = TestNBImplIdlOvn.fake_set['lrouters']
  343. self._load_ovsdb_fake_rows(self.lrouter_table, fake_lrouters)
  344. fake_lrps = TestNBImplIdlOvn.fake_set['lrouter_ports']
  345. self._load_ovsdb_fake_rows(self.lrp_table, fake_lrps)
  346. # Associate routers and router ports
  347. self._construct_ovsdb_references(
  348. TestNBImplIdlOvn.fake_associations['lrtolrp'],
  349. self.lrouter_table, self.lrp_table,
  350. 'name', 'name', 'ports')
  351. # Load static routes
  352. fake_sroutes = TestNBImplIdlOvn.fake_set['static_routes']
  353. self._load_ovsdb_fake_rows(self.sroute_table, fake_sroutes)
  354. # Associate routers and static routes
  355. self._construct_ovsdb_references(
  356. TestNBImplIdlOvn.fake_associations['lrtosroute'],
  357. self.lrouter_table, self.sroute_table,
  358. 'name', 'ip_prefix', 'static_routes')
  359. # Load nats
  360. fake_nats = TestNBImplIdlOvn.fake_set['nats']
  361. self._load_ovsdb_fake_rows(self.nat_table, fake_nats)
  362. # Associate routers and nats
  363. self._construct_ovsdb_references(
  364. TestNBImplIdlOvn.fake_associations['lrtonat'],
  365. self.lrouter_table, self.nat_table,
  366. 'name', 'external_ip', 'nat')
  367. # Load acls
  368. fake_acls = TestNBImplIdlOvn.fake_set['acls']
  369. self._load_ovsdb_fake_rows(self.acl_table, fake_acls)
  370. # Associate switches and acls
  371. self._construct_ovsdb_references(
  372. TestNBImplIdlOvn.fake_associations['lstoacl'],
  373. self.lswitch_table, self.acl_table,
  374. 'name', 'unit_test_id', 'acls')
  375. # Load dhcp options
  376. fake_dhcp_options = TestNBImplIdlOvn.fake_set['dhcp_options']
  377. self._load_ovsdb_fake_rows(self.dhcp_table, fake_dhcp_options)
  378. # Load address sets
  379. fake_address_sets = TestNBImplIdlOvn.fake_set['address_sets']
  380. self._load_ovsdb_fake_rows(self.address_set_table, fake_address_sets)
  381. @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None)
  382. @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
  383. def test_setting_ovsdb_probe_timeout_default_value(self):
  384. inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())
  385. inst.idl._session.reconnect.set_probe_interval.assert_called_with(
  386. 60000)
  387. @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None)
  388. @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
  389. @mock.patch.object(config, 'get_ovn_ovsdb_probe_interval')
  390. def test_setting_ovsdb_probe_timeout(self, mock_get_probe_interval):
  391. mock_get_probe_interval.return_value = 5000
  392. inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())
  393. inst.idl._session.reconnect.set_probe_interval.assert_called_with(5000)
  394. def test_get_all_logical_switches_with_ports(self):
  395. # Test empty
  396. mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
  397. self.assertItemsEqual(mapping, {})
  398. # Test loaded values
  399. self._load_nb_db()
  400. mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
  401. expected = [{'name': utils.ovn_name('ls-id-1'),
  402. 'ports': ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1'],
  403. 'provnet_port': 'provnet-ls-id-1'},
  404. {'name': utils.ovn_name('ls-id-2'),
  405. 'ports': ['lsp-id-21', 'lsp-rp-id-2'],
  406. 'provnet_port': 'provnet-ls-id-2'},
  407. {'name': utils.ovn_name('ls-id-3'),
  408. 'ports': ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3',
  409. 'lsp-vpn-id-3'],
  410. 'provnet_port': None},
  411. {'name': utils.ovn_name('ls-id-5'),
  412. 'ports': ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5',
  413. 'lsp-vpn-id-5'],
  414. 'provnet_port': None}]
  415. self.assertItemsEqual(mapping, expected)
  416. def test_get_all_logical_routers_with_rports(self):
  417. # Test empty
  418. mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
  419. self.assertItemsEqual(mapping, {})
  420. # Test loaded values
  421. self._load_nb_db()
  422. mapping = self.nb_ovn_idl.get_all_logical_routers_with_rports()
  423. expected = [{'name': 'lr-id-a',
  424. 'ports': {'orp-id-a1': ['10.0.1.0/24'],
  425. 'orp-id-a2': ['10.0.2.0/24'],
  426. 'orp-id-a3': ['10.0.3.0/24']},
  427. 'static_routes': [{'destination': '20.0.0.0/16',
  428. 'nexthop': '10.0.3.253'}],
  429. 'snats': [{'external_ip': '10.0.3.1',
  430. 'logical_ip': '20.0.0.0/16',
  431. 'type': 'snat'}],
  432. 'dnat_and_snats': []},
  433. {'name': 'lr-id-b',
  434. 'ports': {'xrp-id-b1': ['20.0.1.0/24'],
  435. 'orp-id-b2': ['20.0.2.0/24']},
  436. 'static_routes': [{'destination': '10.0.0.0/16',
  437. 'nexthop': '20.0.2.253'}],
  438. 'snats': [{'external_ip': '20.0.2.1',
  439. 'logical_ip': '10.0.0.0/24',
  440. 'type': 'snat'}],
  441. 'dnat_and_snats': [{'external_ip': '20.0.2.4',
  442. 'logical_ip': '10.0.0.4',
  443. 'type': 'dnat_and_snat'},
  444. {'external_ip': '20.0.2.5',
  445. 'logical_ip': '10.0.0.5',
  446. 'type': 'dnat_and_snat',
  447. 'external_mac': '00:01:02:03:04:05',
  448. 'logical_port': 'lsp-id-001'}]},
  449. {'name': 'lr-id-c', 'ports': {}, 'static_routes': [],
  450. 'snats': [], 'dnat_and_snats': []},
  451. {'name': 'lr-id-d', 'ports': {}, 'static_routes': [],
  452. 'snats': [], 'dnat_and_snats': []},
  453. {'name': 'lr-id-e', 'ports': {}, 'static_routes': [],
  454. 'snats': [], 'dnat_and_snats': []}]
  455. self.assertItemsEqual(mapping, expected)
  456. def test_get_acls_for_lswitches(self):
  457. self._load_nb_db()
  458. # Test neutron switches
  459. lswitches = ['ls-id-1', 'ls-id-2', 'ls-id-3', 'ls-id-5']
  460. acl_values, acl_objs, lswitch_ovsdb_dict = \
  461. self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
  462. excepted_acl_values = {
  463. 'lsp-id-11': [
  464. {'action': 'allow-related', 'lport': 'lsp-id-11',
  465. 'lswitch': 'neutron-ls-id-1',
  466. 'external_ids': {'neutron:lport': 'lsp-id-11'},
  467. 'direction': 'from-lport',
  468. 'match': 'inport == "lsp-id-11" && ip4'},
  469. {'action': 'allow-related', 'lport': 'lsp-id-11',
  470. 'lswitch': 'neutron-ls-id-1',
  471. 'external_ids': {'neutron:lport': 'lsp-id-11'},
  472. 'direction': 'to-lport',
  473. 'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'}
  474. ],
  475. 'lsp-id-12': [
  476. {'action': 'allow-related', 'lport': 'lsp-id-12',
  477. 'lswitch': 'neutron-ls-id-1',
  478. 'external_ids': {'neutron:lport': 'lsp-id-12'},
  479. 'direction': 'from-lport',
  480. 'match': 'inport == "lsp-id-12" && ip4'},
  481. {'action': 'allow-related', 'lport': 'lsp-id-12',
  482. 'lswitch': 'neutron-ls-id-1',
  483. 'external_ids': {'neutron:lport': 'lsp-id-12'},
  484. 'direction': 'to-lport',
  485. 'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'}
  486. ],
  487. 'lsp-id-21': [
  488. {'action': 'allow-related', 'lport': 'lsp-id-21',
  489. 'lswitch': 'neutron-ls-id-2',
  490. 'external_ids': {'neutron:lport': 'lsp-id-21'},
  491. 'direction': 'from-lport',
  492. 'match': 'inport == "lsp-id-21" && ip4'},
  493. {'action': 'allow-related', 'lport': 'lsp-id-21',
  494. 'lswitch': 'neutron-ls-id-2',
  495. 'external_ids': {'neutron:lport': 'lsp-id-21'},
  496. 'direction': 'to-lport',
  497. 'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'}
  498. ],
  499. 'lsp-id-52': [
  500. {'action': 'allow-related', 'lport': 'lsp-id-52',
  501. 'lswitch': 'neutron-ls-id-5',
  502. 'external_ids': {'neutron:lport': 'lsp-id-52'},
  503. 'direction': 'from-lport',
  504. 'match': 'inport == "lsp-id-52" && ip4'},
  505. {'action': 'allow-related', 'lport': 'lsp-id-52',
  506. 'lswitch': 'neutron-ls-id-5',
  507. 'external_ids': {'neutron:lport': 'lsp-id-52'},
  508. 'direction': 'to-lport',
  509. 'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'}
  510. ]}
  511. self.assertItemsEqual(acl_values, excepted_acl_values)
  512. self.assertEqual(len(acl_objs), 8)
  513. self.assertEqual(len(lswitch_ovsdb_dict), len(lswitches))
  514. # Test non-neutron switches
  515. lswitches = ['ls-id-4']
  516. acl_values, acl_objs, lswitch_ovsdb_dict = \
  517. self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
  518. self.assertItemsEqual(acl_values, {})
  519. self.assertEqual(len(acl_objs), 0)
  520. self.assertEqual(len(lswitch_ovsdb_dict), 0)
  521. def test_get_all_chassis_gateway_bindings(self):
  522. self._load_nb_db()
  523. bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings()
  524. expected = {'host-1': [utils.ovn_lrouter_port_name('orp-id-a1'),
  525. utils.ovn_lrouter_port_name('orp-id-a2')],
  526. 'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')],
  527. ovn_const.OVN_GATEWAY_INVALID_CHASSIS: [
  528. utils.ovn_name('orp-id-a3')]}
  529. self.assertItemsEqual(bindings, expected)
  530. bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([])
  531. self.assertItemsEqual(bindings, expected)
  532. bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1'])
  533. expected = {'host-1': [utils.ovn_lrouter_port_name('orp-id-a1'),
  534. utils.ovn_lrouter_port_name('orp-id-a2')]}
  535. self.assertItemsEqual(bindings, expected)
  536. def test_get_gateway_chassis_binding(self):
  537. self._load_nb_db()
  538. chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
  539. utils.ovn_lrouter_port_name('orp-id-a1'))
  540. self.assertEqual(chassis, ['host-1'])
  541. chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
  542. utils.ovn_lrouter_port_name('orp-id-b2'))
  543. self.assertEqual(chassis, ['host-2'])
  544. chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
  545. utils.ovn_lrouter_port_name('orp-id-a3'))
  546. self.assertEqual(chassis, ['neutron-ovn-invalid-chassis'])
  547. chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
  548. utils.ovn_lrouter_port_name('orp-id-b3'))
  549. self.assertEqual(chassis, [])
  550. chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad')
  551. self.assertEqual(chassis, [])
  552. def test_get_unhosted_gateways(self):
  553. self._load_nb_db()
  554. # Test only host-1 in the valid list
  555. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
  556. {}, {'host-1': 'physnet1'}, [])
  557. expected = {
  558. utils.ovn_lrouter_port_name('orp-id-b2'): {
  559. ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'},
  560. utils.ovn_lrouter_port_name('orp-id-a3'): {
  561. ovn_const.OVN_GATEWAY_CHASSIS_KEY:
  562. ovn_const.OVN_GATEWAY_INVALID_CHASSIS}}
  563. self.assertItemsEqual(unhosted_gateways, expected)
  564. # Test both host-1, host-2 in valid list
  565. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
  566. {}, {'host-1': 'physnet1', 'host-2': 'physnet2'}, [])
  567. expected = {utils.ovn_lrouter_port_name('orp-id-a3'): {
  568. ovn_const.OVN_GATEWAY_CHASSIS_KEY:
  569. ovn_const.OVN_GATEWAY_INVALID_CHASSIS}}
  570. self.assertItemsEqual(unhosted_gateways, expected)
  571. # Schedule unhosted_gateways on host-2
  572. for unhosted_gateway in unhosted_gateways:
  573. router_row = self._find_ovsdb_fake_row(self.lrp_table,
  574. 'name', unhosted_gateway)
  575. setattr(router_row, 'options', {
  576. ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'})
  577. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
  578. {}, {'host-1': 'physnet1', 'host-2': 'physnet2'}, [])
  579. self.assertItemsEqual(unhosted_gateways, {})
  580. def test_get_subnet_dhcp_options(self):
  581. self._load_nb_db()
  582. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  583. 'subnet-id-10-0-2-0')
  584. expected_row = self._find_ovsdb_fake_row(self.dhcp_table,
  585. 'cidr', '10.0.2.0/24')
  586. self.assertEqual({
  587. 'subnet': {'cidr': expected_row.cidr,
  588. 'external_ids': expected_row.external_ids,
  589. 'options': expected_row.options,
  590. 'uuid': expected_row.uuid},
  591. 'ports': []}, subnet_options)
  592. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  593. 'subnet-id-11-0-2-0')['subnet']
  594. self.assertIsNone(subnet_options)
  595. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  596. 'port-id-30-0-1-0')['subnet']
  597. self.assertIsNone(subnet_options)
  598. def test_get_subnet_dhcp_options_with_ports(self):
  599. # Test empty
  600. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  601. 'subnet-id-10-0-1-0', with_ports=True)
  602. self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options)
  603. # Test loaded values
  604. self._load_nb_db()
  605. # Test getting both subnet and port dhcp options
  606. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  607. 'subnet-id-10-0-1-0', with_ports=True)
  608. dhcp_rows = [
  609. self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/24'),
  610. self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/26')]
  611. expected_rows = [{'cidr': dhcp_row.cidr,
  612. 'external_ids': dhcp_row.external_ids,
  613. 'options': dhcp_row.options,
  614. 'uuid': dhcp_row.uuid} for dhcp_row in dhcp_rows]
  615. self.assertItemsEqual(expected_rows, [
  616. subnet_options['subnet']] + subnet_options['ports'])
  617. # Test getting only subnet dhcp options
  618. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  619. 'subnet-id-10-0-2-0', with_ports=True)
  620. dhcp_rows = [
  621. self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.2.0/24')]
  622. expected_rows = [{'cidr': dhcp_row.cidr,
  623. 'external_ids': dhcp_row.external_ids,
  624. 'options': dhcp_row.options,
  625. 'uuid': dhcp_row.uuid} for dhcp_row in dhcp_rows]
  626. self.assertItemsEqual(expected_rows, [
  627. subnet_options['subnet']] + subnet_options['ports'])
  628. # Test getting no dhcp options
  629. subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
  630. 'subnet-id-11-0-2-0', with_ports=True)
  631. self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options)
  632. def test_get_subnets_dhcp_options(self):
  633. self._load_nb_db()
  634. def get_row_dict(row):
  635. return {'cidr': row.cidr, 'external_ids': row.external_ids,
  636. 'options': row.options, 'uuid': row.uuid}
  637. subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
  638. ['subnet-id-10-0-1-0', 'subnet-id-10-0-2-0'])
  639. expected_rows = [
  640. get_row_dict(
  641. self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', cidr))
  642. for cidr in ('10.0.1.0/24', '10.0.2.0/24')]
  643. self.assertItemsEqual(expected_rows, subnets_options)
  644. subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
  645. ['subnet-id-11-0-2-0', 'subnet-id-20-0-1-0'])
  646. expected_row = get_row_dict(
  647. self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '20.0.1.0/24'))
  648. self.assertItemsEqual([expected_row], subnets_options)
  649. subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
  650. ['port-id-30-0-1-0', 'fake-not-exist'])
  651. self.assertEqual([], subnets_options)
  652. def test_get_all_dhcp_options(self):
  653. self._load_nb_db()
  654. dhcp_options = self.nb_ovn_idl.get_all_dhcp_options()
  655. self.assertEqual(len(dhcp_options['subnets']), 3)
  656. self.assertEqual(len(dhcp_options['ports_v4']), 2)
  657. def test_get_address_sets(self):
  658. self._load_nb_db()
  659. address_sets = self.nb_ovn_idl.get_address_sets()
  660. self.assertEqual(len(address_sets), 4)
  661. def test_get_port_group_not_supported(self):
  662. self._load_nb_db()
  663. # Make sure that PG tables doesn't exist in fake db.
  664. self._tables.pop('Port_Group', None)
  665. port_group = self.nb_ovn_idl.get_port_group(str(uuid.uuid4()))
  666. self.assertIsNone(port_group)
  667. def test_get_port_groups_not_supported(self):
  668. self._load_nb_db()
  669. # Make sure that PG tables doesn't exist in fake db.
  670. self._tables.pop('Port_Group', None)
  671. port_groups = self.nb_ovn_idl.get_port_groups()
  672. self.assertEqual({}, port_groups)
  673. class TestSBImplIdlOvn(TestDBImplIdlOvn):
  674. fake_set = {
  675. 'chassis': [
  676. {'name': 'host-1', 'hostname': 'host-1.localdomain.com',
  677. 'external_ids': {'ovn-bridge-mappings':
  678. 'public:br-ex,private:br-0'}},
  679. {'name': 'host-2', 'hostname': 'host-2.localdomain.com',
  680. 'external_ids': {'ovn-bridge-mappings':
  681. 'public:br-ex,public2:br-ex'}},
  682. {'name': 'host-3', 'hostname': 'host-3.localdomain.com',
  683. 'external_ids': {'ovn-bridge-mappings':
  684. 'public:br-ex'}},
  685. ]
  686. }
  687. def setUp(self):
  688. super(TestSBImplIdlOvn, self).setUp()
  689. self.chassis_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
  690. self._tables = {}
  691. self._tables['Chassis'] = self.chassis_table
  692. with mock.patch.object(impl_idl_ovn, 'get_connection',
  693. return_value=mock.Mock()):
  694. impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection = None
  695. self.sb_ovn_idl = impl_idl_ovn.OvsdbSbOvnIdl(mock.Mock())
  696. self.sb_ovn_idl.idl.tables = self._tables
  697. def _load_sb_db(self):
  698. # Load Chassis
  699. fake_chassis = TestSBImplIdlOvn.fake_set['chassis']
  700. self._load_ovsdb_fake_rows(self.chassis_table, fake_chassis)
  701. @mock.patch.object(impl_idl_ovn.OvsdbSbOvnIdl, 'ovsdb_connection', None)
  702. @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
  703. def test_setting_ovsdb_probe_timeout_default_value(self):
  704. inst = impl_idl_ovn.OvsdbSbOvnIdl(mock.Mock())
  705. inst.idl._session.reconnect.set_probe_interval.assert_called_with(
  706. 60000)
  707. @mock.patch.object(impl_idl_ovn.OvsdbSbOvnIdl, 'ovsdb_connection', None)
  708. @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
  709. @mock.patch.object(config, 'get_ovn_ovsdb_probe_interval')
  710. def test_setting_ovsdb_probe_timeout(self, mock_get_probe_interval):
  711. mock_get_probe_interval.return_value = 5000
  712. inst = impl_idl_ovn.OvsdbSbOvnIdl(mock.Mock())
  713. inst.idl._session.reconnect.set_probe_interval.assert_called_with(5000)