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.

317 lines
13 KiB

  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. from datetime import datetime
  15. import os
  16. import shutil
  17. import time
  18. import fixtures
  19. import mock
  20. from neutron.conf.plugins.ml2 import config
  21. from neutron.plugins.ml2.drivers import type_geneve # noqa
  22. from neutron.tests.unit.plugins.ml2 import test_plugin
  23. from neutron_lib import fixture
  24. from neutron_lib.plugins import constants
  25. from neutron_lib.plugins import directory
  26. from oslo_concurrency import lockutils
  27. from oslo_config import cfg
  28. from oslo_db import exception as os_db_exc
  29. from oslo_db.sqlalchemy import provision
  30. from oslo_log import log
  31. from oslo_utils import uuidutils
  32. from ovsdbapp.backend.ovs_idl import connection
  33. # Load all the models to register them into SQLAlchemy metadata before using
  34. # the SqlFixture
  35. from networking_ovn.db import models # noqa
  36. from networking_ovn.ovsdb import impl_idl_ovn
  37. from networking_ovn.ovsdb import ovsdb_monitor
  38. from networking_ovn.ovsdb import worker
  39. from networking_ovn.tests import base
  40. from networking_ovn.tests.functional.resources import process
  41. LOG = log.getLogger(__name__)
  42. # This is the directory from which infra fetches log files for functional tests
  43. DEFAULT_LOG_DIR = os.path.join(os.environ.get('OS_LOG_PATH', '/tmp'),
  44. 'dsvm-functional-logs')
  45. SQL_FIXTURE_LOCK = 'sql_fixture_lock'
  46. class ConnectionFixture(fixtures.Fixture):
  47. def __init__(self, idl=None, constr=None, schema=None, timeout=60):
  48. self.idl = idl or ovsdb_monitor.BaseOvnIdl.from_server(
  49. constr, schema)
  50. self.connection = connection.Connection(
  51. idl=self.idl, timeout=timeout)
  52. def _setUp(self):
  53. self.addCleanup(self.stop)
  54. self.connection.start()
  55. def stop(self):
  56. self.connection.stop()
  57. class OVNSqlFixture(fixture.StaticSqlFixture):
  58. @classmethod
  59. @lockutils.synchronized(SQL_FIXTURE_LOCK)
  60. def _init_resources(cls):
  61. cls.schema_resource = provision.SchemaResource(
  62. provision.DatabaseResource("sqlite"),
  63. cls._generate_schema, teardown=False)
  64. dependency_resources = {}
  65. for name, resource in cls.schema_resource.resources:
  66. dependency_resources[name] = resource.getResource()
  67. cls.schema_resource.make(dependency_resources)
  68. cls.engine = dependency_resources['database'].engine
  69. def _delete_from_schema(self, engine):
  70. try:
  71. super(OVNSqlFixture, self)._delete_from_schema(engine)
  72. except os_db_exc.DBNonExistentTable:
  73. pass
  74. class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase):
  75. # Please see networking_ovn/tests/contrib/gate_hook.sh.
  76. # It installs openvswitch in the '/usr/local' path and the ovn-nb schema
  77. # file will be present in this path.
  78. OVS_INSTALL_SHARE_PATH = '/usr/local/share/openvswitch'
  79. _mechanism_drivers = ['logger', 'ovn']
  80. _extension_drivers = ['port_security']
  81. _counter = 0
  82. l3_plugin = 'networking_ovn.l3.l3_ovn.OVNL3RouterPlugin'
  83. def setUp(self, maintenance_worker=False):
  84. config.cfg.CONF.set_override('extension_drivers',
  85. self._extension_drivers,
  86. group='ml2')
  87. config.cfg.CONF.set_override('tenant_network_types',
  88. ['geneve'],
  89. group='ml2')
  90. config.cfg.CONF.set_override('vni_ranges',
  91. ['1:65536'],
  92. group='ml2_type_geneve')
  93. config.cfg.CONF.set_override('dns_servers',
  94. ['10.10.10.10'],
  95. group='ovn')
  96. super(TestOVNFunctionalBase, self).setUp()
  97. self.test_log_dir = os.path.join(DEFAULT_LOG_DIR, self.id())
  98. base.setup_test_logging(
  99. cfg.CONF, self.test_log_dir, "testrun.txt")
  100. mm = directory.get_plugin().mechanism_manager
  101. self.mech_driver = mm.mech_drivers['ovn'].obj
  102. self.l3_plugin = directory.get_plugin(constants.L3)
  103. self.ovsdb_server_mgr = None
  104. self.ovn_northd_mgr = None
  105. self.maintenance_worker = maintenance_worker
  106. self.temp_dir = self.useFixture(fixtures.TempDir()).path
  107. self._start_ovsdb_server_and_idls()
  108. self._start_ovn_northd()
  109. # FIXME(lucasagomes): Workaround for
  110. # https://bugs.launchpad.net/networking-ovn/+bug/1808146. We should
  111. # investigate and properly fix the problem. This method is just a
  112. # workaround to alleviate the gate for now and should not be considered
  113. # a proper fix.
  114. def _setup_database_fixtures(self):
  115. fixture = OVNSqlFixture()
  116. self.useFixture(fixture)
  117. self.engine = fixture.engine
  118. def get_additional_service_plugins(self):
  119. p = super(TestOVNFunctionalBase, self).get_additional_service_plugins()
  120. p.update({'revision_plugin_name': 'revisions'})
  121. return p
  122. @property
  123. def _ovsdb_protocol(self):
  124. return self.get_ovsdb_server_protocol()
  125. def get_ovsdb_server_protocol(self):
  126. return 'unix'
  127. def _start_ovn_northd(self):
  128. if not self.ovsdb_server_mgr:
  129. return
  130. ovn_nb_db = self.ovsdb_server_mgr.get_ovsdb_connection_path('nb')
  131. ovn_sb_db = self.ovsdb_server_mgr.get_ovsdb_connection_path('sb')
  132. self.ovn_northd_mgr = self.useFixture(
  133. process.OvnNorthd(self.temp_dir,
  134. ovn_nb_db, ovn_sb_db,
  135. protocol=self._ovsdb_protocol))
  136. def _start_ovsdb_server_and_idls(self):
  137. # Start 2 ovsdb-servers one each for OVN NB DB and OVN SB DB
  138. # ovsdb-server with OVN SB DB can be used to test the chassis up/down
  139. # events.
  140. mgr = self.ovsdb_server_mgr = self.useFixture(
  141. process.OvsdbServer(self.temp_dir, self.OVS_INSTALL_SHARE_PATH,
  142. ovn_nb_db=True, ovn_sb_db=True,
  143. protocol=self._ovsdb_protocol))
  144. set_cfg = cfg.CONF.set_override
  145. set_cfg('ovn_nb_connection',
  146. self.ovsdb_server_mgr.get_ovsdb_connection_path(), 'ovn')
  147. set_cfg('ovn_sb_connection',
  148. self.ovsdb_server_mgr.get_ovsdb_connection_path(
  149. db_type='sb'), 'ovn')
  150. set_cfg('ovn_nb_private_key', self.ovsdb_server_mgr.private_key, 'ovn')
  151. set_cfg('ovn_nb_certificate', self.ovsdb_server_mgr.certificate, 'ovn')
  152. set_cfg('ovn_nb_ca_cert', self.ovsdb_server_mgr.ca_cert, 'ovn')
  153. set_cfg('ovn_sb_private_key', self.ovsdb_server_mgr.private_key, 'ovn')
  154. set_cfg('ovn_sb_certificate', self.ovsdb_server_mgr.certificate, 'ovn')
  155. set_cfg('ovn_sb_ca_cert', self.ovsdb_server_mgr.ca_cert, 'ovn')
  156. num_attempts = 0
  157. # 5 seconds should be more than enough for the transaction to complete
  158. # for the test cases.
  159. # This also fixes the bug #1607639.
  160. cfg.CONF.set_override(
  161. 'ovsdb_connection_timeout', 5,
  162. 'ovn')
  163. # Created monitor IDL connection to the OVN NB DB.
  164. # This monitor IDL connection can be used to
  165. # - Verify that the ML2 OVN driver has written to the OVN NB DB
  166. # as expected.
  167. # - Create and delete resources in OVN NB DB outside of the
  168. # ML2 OVN driver scope to test scenarios like ovn_nb_sync.
  169. while num_attempts < 3:
  170. try:
  171. con = self.useFixture(ConnectionFixture(
  172. constr=mgr.get_ovsdb_connection_path(),
  173. schema='OVN_Northbound')).connection
  174. self.nb_api = impl_idl_ovn.OvsdbNbOvnIdl(con)
  175. break
  176. except Exception:
  177. LOG.exception("Error connecting to the OVN_Northbound DB")
  178. num_attempts += 1
  179. time.sleep(1)
  180. num_attempts = 0
  181. # Create monitor IDL connection to the OVN SB DB.
  182. # This monitor IDL connection can be used to
  183. # - Create chassis rows
  184. # - Update chassis columns etc.
  185. while num_attempts < 3:
  186. try:
  187. con = self.useFixture(ConnectionFixture(
  188. constr=mgr.get_ovsdb_connection_path('sb'),
  189. schema='OVN_Southbound')).connection
  190. self.sb_api = impl_idl_ovn.OvsdbSbOvnIdl(con)
  191. break
  192. except Exception:
  193. LOG.exception("Error connecting to the OVN_Southbound DB")
  194. num_attempts += 1
  195. time.sleep(1)
  196. class TriggerCls(mock.MagicMock):
  197. def trigger(self):
  198. pass
  199. trigger_cls = TriggerCls()
  200. if self.maintenance_worker:
  201. trigger_cls.trigger.__self__.__class__ = worker.MaintenanceWorker
  202. cfg.CONF.set_override('neutron_sync_mode', 'off', 'ovn')
  203. self.addCleanup(self._collect_processes_logs)
  204. self.addCleanup(self.stop)
  205. # mech_driver.post_fork_initialize creates the IDL connections
  206. self.mech_driver.post_fork_initialize(
  207. mock.ANY, mock.ANY, trigger_cls.trigger)
  208. def _collect_processes_logs(self):
  209. for database in ("nb", "sb"):
  210. for file_suffix in ("log", "db"):
  211. src_filename = "ovn_%(db)s.%(suffix)s" % {
  212. 'db': database,
  213. 'suffix': file_suffix
  214. }
  215. dst_filename = "ovn_%(db)s-%(timestamp)s.%(suffix)s" % {
  216. 'db': database,
  217. 'suffix': file_suffix,
  218. 'timestamp': datetime.now().strftime('%y-%m-%d_%H-%M-%S'),
  219. }
  220. filepath = os.path.join(self.temp_dir, src_filename)
  221. shutil.copyfile(
  222. filepath, os.path.join(self.test_log_dir, dst_filename))
  223. def stop(self):
  224. if self.maintenance_worker:
  225. self.mech_driver.nb_synchronizer.stop()
  226. self.mech_driver.sb_synchronizer.stop()
  227. self.mech_driver._nb_ovn.ovsdb_connection.stop()
  228. self.mech_driver._sb_ovn.ovsdb_connection.stop()
  229. def restart(self):
  230. self.stop()
  231. # The OVN sync test starts its own synchronizers...
  232. self.l3_plugin._nb_ovn_idl.ovsdb_connection.stop()
  233. self.l3_plugin._sb_ovn_idl.ovsdb_connection.stop()
  234. # Stop our monitor connections
  235. self.nb_api.ovsdb_connection.stop()
  236. self.sb_api.ovsdb_connection.stop()
  237. if self.ovsdb_server_mgr:
  238. self.ovsdb_server_mgr.stop()
  239. if self.ovn_northd_mgr:
  240. self.ovn_northd_mgr.stop()
  241. self.mech_driver._nb_ovn = None
  242. self.mech_driver._sb_ovn = None
  243. self.l3_plugin._nb_ovn_idl = None
  244. self.l3_plugin._sb_ovn_idl = None
  245. self.nb_api.ovsdb_connection = None
  246. self.sb_api.ovsdb_connection = None
  247. self.ovsdb_server_mgr.delete_dbs()
  248. self._start_ovsdb_server_and_idls()
  249. self._start_ovn_northd()
  250. def add_fake_chassis(self, host, physical_nets=None, external_ids=None,
  251. name=None):
  252. physical_nets = physical_nets or []
  253. external_ids = external_ids or {}
  254. bridge_mapping = ",".join(["%s:br-provider%s" % (phys_net, i)
  255. for i, phys_net in enumerate(physical_nets)])
  256. if name is None:
  257. name = uuidutils.generate_uuid()
  258. external_ids['ovn-bridge-mappings'] = bridge_mapping
  259. # We'll be using different IP addresses every time for the Encap of
  260. # the fake chassis as the SB schema doesn't allow to have two entries
  261. # with same (ip,type) pairs as of OVS 2.11. This shouldn't have any
  262. # impact as the tunnels won't get created anyways since ovn-controller
  263. # is not running. Ideally we shouldn't be creating more than 255
  264. # fake chassis but from the SB db point of view, 'ip' column can be
  265. # any string so we could add entries with ip='172.24.4.1000'.
  266. self._counter += 1
  267. self.sb_api.chassis_add(
  268. name, ['geneve'], '172.24.4.%d' % self._counter,
  269. external_ids=external_ids, hostname=host).execute(check_error=True)
  270. return name
  271. def del_fake_chassis(self, chassis, if_exists=True):
  272. self.sb_api.chassis_del(
  273. chassis, if_exists=if_exists).execute(check_error=True)