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.

240 lines
9.8 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 distutils import spawn
  15. import os
  16. import fixtures
  17. from neutron.agent.linux import utils
  18. import psutil
  19. import tenacity
  20. class DaemonProcessFixture(fixtures.Fixture):
  21. def __init__(self, temp_dir):
  22. super(DaemonProcessFixture, self).__init__()
  23. self.temp_dir = temp_dir
  24. def _get_pid_from_pidfile(self, pidfile):
  25. with open(os.path.join(self.temp_dir, pidfile), 'r') as pidfile_f:
  26. pid = pidfile_f.read().strip()
  27. try:
  28. return int(pid)
  29. except ValueError:
  30. raise RuntimeError(
  31. "Pidfile %(pidfile)s contains %(pid)s that "
  32. "is not a pid" % {'pidfile': pidfile, 'pid': pid}
  33. )
  34. class OvnNorthd(DaemonProcessFixture):
  35. def __init__(self, temp_dir, ovn_nb_db, ovn_sb_db, protocol='unix',
  36. debug=True):
  37. super(OvnNorthd, self).__init__(temp_dir)
  38. self.ovn_nb_db = ovn_nb_db
  39. self.ovn_sb_db = ovn_sb_db
  40. self.protocol = protocol
  41. self.unixctl_path = self.temp_dir + '/ovn_northd.ctl'
  42. self.log_file_path = self.temp_dir + '/ovn_northd.log'
  43. self.debug = debug
  44. if self.protocol == 'ssl':
  45. self.private_key = os.path.join(self.temp_dir, 'ovn-privkey.pem')
  46. self.certificate = os.path.join(self.temp_dir, 'ovn-cert.pem')
  47. self.ca_cert = os.path.join(self.temp_dir, 'controllerca',
  48. 'cacert.pem')
  49. def _setUp(self):
  50. self.addCleanup(self.stop)
  51. self.start()
  52. def start(self):
  53. # start the ovn-northd
  54. ovn_northd_cmd = [
  55. spawn.find_executable('ovn-northd'), '-vconsole:off',
  56. '--detach',
  57. '--ovnnb-db=%s' % self.ovn_nb_db,
  58. '--ovnsb-db=%s' % self.ovn_sb_db,
  59. '--no-chdir',
  60. '--unixctl=%s' % self.unixctl_path,
  61. '--log-file=%s' % (self.log_file_path)]
  62. if self.protocol == 'ssl':
  63. ovn_northd_cmd.append('--private-key=%s' % self.private_key)
  64. ovn_northd_cmd.append('--certificate=%s' % self.certificate)
  65. ovn_northd_cmd.append('--ca-cert=%s' % self.ca_cert)
  66. if self.debug:
  67. ovn_northd_cmd.append('--verbose')
  68. obj, _ = utils.create_process(ovn_northd_cmd)
  69. obj.communicate()
  70. def stop(self):
  71. try:
  72. stop_cmd = ['ovs-appctl', '-t', self.unixctl_path, 'exit']
  73. utils.execute(stop_cmd)
  74. except Exception:
  75. pass
  76. class OvsdbServer(DaemonProcessFixture):
  77. def __init__(self, temp_dir, ovs_dir, ovn_nb_db=True, ovn_sb_db=False,
  78. protocol='unix', debug=True):
  79. super(OvsdbServer, self).__init__(temp_dir)
  80. self.ovs_dir = ovs_dir
  81. self.ovn_nb_db = ovn_nb_db
  82. self.ovn_sb_db = ovn_sb_db
  83. # The value of the protocol must be unix or tcp or ssl
  84. self.protocol = protocol
  85. self.ovsdb_server_processes = []
  86. self.private_key = os.path.join(self.temp_dir, 'ovn-privkey.pem')
  87. self.certificate = os.path.join(self.temp_dir, 'ovn-cert.pem')
  88. self.ca_cert = os.path.join(self.temp_dir, 'controllerca',
  89. 'cacert.pem')
  90. self.debug = debug
  91. def _setUp(self):
  92. if self.ovn_nb_db:
  93. self.ovsdb_server_processes.append(
  94. {'db_path': self.temp_dir + '/ovn_nb.db',
  95. 'schema_path': self.ovs_dir + '/ovn-nb.ovsschema',
  96. 'remote_path': self.temp_dir + '/ovnnb_db.sock',
  97. 'protocol': self.protocol,
  98. 'remote_ip': '127.0.0.1',
  99. 'remote_port': '0',
  100. 'pidfile': 'ovn-nb.pid',
  101. 'unixctl_path': self.temp_dir + '/ovnnb_db.ctl',
  102. 'log_file_path': self.temp_dir + '/ovn_nb.log',
  103. 'db_type': 'nb',
  104. 'connection': 'db:OVN_Northbound,NB_Global,connections',
  105. 'ctl_cmd': 'ovn-nbctl'})
  106. if self.ovn_sb_db:
  107. self.ovsdb_server_processes.append(
  108. {'db_path': self.temp_dir + '/ovn_sb.db',
  109. 'schema_path': self.ovs_dir + '/ovn-sb.ovsschema',
  110. 'remote_path': self.temp_dir + '/ovnsb_db.sock',
  111. 'protocol': self.protocol,
  112. 'remote_ip': '127.0.0.1',
  113. 'remote_port': '0',
  114. 'pidfile': 'ovn-sb.pid',
  115. 'unixctl_path': self.temp_dir + '/ovnsb_db.ctl',
  116. 'log_file_path': self.temp_dir + '/ovn_sb.log',
  117. 'db_type': 'sb',
  118. 'connection': 'db:OVN_Southbound,SB_Global,connections',
  119. 'ctl_cmd': 'ovn-sbctl'})
  120. self.addCleanup(self.stop)
  121. self.start()
  122. def _init_ovsdb_pki(self):
  123. os.chdir(self.temp_dir)
  124. pki_init_cmd = [spawn.find_executable('ovs-pki'), 'init',
  125. '-d', self.temp_dir, '-l',
  126. os.path.join(self.temp_dir, 'pki.log'), '--force']
  127. utils.execute(pki_init_cmd)
  128. pki_req_sign = [spawn.find_executable('ovs-pki'), 'req+sign', 'ovn',
  129. 'controller', '-d', self.temp_dir, '-l',
  130. os.path.join(self.temp_dir, 'pki.log'), '--force']
  131. utils.execute(pki_req_sign)
  132. def delete_dbs(self):
  133. for ovsdb in self.ovsdb_server_processes:
  134. try:
  135. os.remove(ovsdb['db_path'])
  136. except OSError:
  137. pass
  138. def start(self):
  139. pki_done = False
  140. for ovsdb_process in self.ovsdb_server_processes:
  141. # create the db from the schema using ovsdb-tool
  142. ovsdb_tool_cmd = [spawn.find_executable('ovsdb-tool'),
  143. 'create', ovsdb_process['db_path'],
  144. ovsdb_process['schema_path']]
  145. utils.execute(ovsdb_tool_cmd)
  146. # start the ovsdb-server
  147. ovsdb_server_cmd = [
  148. spawn.find_executable('ovsdb-server'), '-vconsole:off',
  149. '--detach',
  150. '--pidfile=%s' % os.path.join(
  151. self.temp_dir, ovsdb_process['pidfile']),
  152. '--log-file=%s' % (ovsdb_process['log_file_path']),
  153. '--remote=punix:%s' % (ovsdb_process['remote_path']),
  154. '--remote=%s' % (ovsdb_process['connection']),
  155. '--unixctl=%s' % (ovsdb_process['unixctl_path']),
  156. '--detach']
  157. if ovsdb_process['protocol'] == 'ssl':
  158. if not pki_done:
  159. pki_done = True
  160. self._init_ovsdb_pki()
  161. ovsdb_server_cmd.append('--private-key=%s' % self.private_key)
  162. ovsdb_server_cmd.append('--certificate=%s' % self.certificate)
  163. ovsdb_server_cmd.append('--ca-cert=%s' % self.ca_cert)
  164. ovsdb_server_cmd.append(ovsdb_process['db_path'])
  165. if self.debug:
  166. ovsdb_server_cmd.append('--verbose')
  167. obj, _ = utils.create_process(ovsdb_server_cmd)
  168. obj.communicate()
  169. conn_cmd = [spawn.find_executable(ovsdb_process['ctl_cmd']),
  170. '--db=unix:%s' % ovsdb_process['remote_path'],
  171. 'set-connection',
  172. 'p%s:%s:%s' % (ovsdb_process['protocol'],
  173. ovsdb_process['remote_port'],
  174. ovsdb_process['remote_ip']),
  175. '--', 'set', 'connection', '.',
  176. 'inactivity_probe=60000']
  177. @tenacity.retry(wait=tenacity.wait_exponential(multiplier=0.1),
  178. stop=tenacity.stop_after_delay(3), reraise=True)
  179. def _set_connection():
  180. utils.execute(conn_cmd)
  181. @tenacity.retry(
  182. wait=tenacity.wait_exponential(multiplier=0.1),
  183. stop=tenacity.stop_after_delay(10),
  184. reraise=True)
  185. def get_ovsdb_remote_port_retry(pid):
  186. process = psutil.Process(pid)
  187. for connect in process.connections():
  188. if connect.status == 'LISTEN':
  189. return connect.laddr[1]
  190. raise Exception(_("Could not find LISTEN port."))
  191. if ovsdb_process['protocol'] != 'unix':
  192. _set_connection()
  193. pid = self._get_pid_from_pidfile(ovsdb_process['pidfile'])
  194. ovsdb_process['remote_port'] = \
  195. get_ovsdb_remote_port_retry(pid)
  196. def stop(self):
  197. for ovsdb_process in self.ovsdb_server_processes:
  198. try:
  199. stop_cmd = ['ovs-appctl', '-t', ovsdb_process['unixctl_path'],
  200. 'exit']
  201. utils.execute(stop_cmd)
  202. except Exception:
  203. pass
  204. def get_ovsdb_connection_path(self, db_type='nb'):
  205. for ovsdb_process in self.ovsdb_server_processes:
  206. if ovsdb_process['db_type'] == db_type:
  207. if ovsdb_process['protocol'] == 'unix':
  208. return 'unix:' + ovsdb_process['remote_path']
  209. else:
  210. return '%s:%s:%s' % (ovsdb_process['protocol'],
  211. ovsdb_process['remote_ip'],
  212. ovsdb_process['remote_port'])