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

315 lines
11KB

  1. # Copyright (c) 2013 OpenStack Foundation
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. from neutron_lib.api.definitions import portbindings
  16. from neutron_lib import constants
  17. from neutron_lib.plugins.ml2 import api
  18. from oslo_log import log
  19. from oslo_serialization import jsonutils
  20. import sqlalchemy
  21. from neutron.db import segments_db
  22. LOG = log.getLogger(__name__)
  23. class InstanceSnapshot(object):
  24. """Used to avoid holding references to DB objects in PortContext."""
  25. def __init__(self, obj):
  26. self._model_class = obj.__class__
  27. self._identity_key = sqlalchemy.orm.util.identity_key(instance=obj)[1]
  28. self._cols = [col.key
  29. for col in sqlalchemy.inspect(self._model_class).columns]
  30. for col in self._cols:
  31. setattr(self, col, getattr(obj, col))
  32. def persist_state_to_session(self, session):
  33. """Updates the state of the snapshot in the session.
  34. Finds the SQLA object in the session if it exists or creates a new
  35. object and updates the object with the column values stored in this
  36. snapshot.
  37. """
  38. db_obj = session.query(self._model_class).get(self._identity_key)
  39. if db_obj:
  40. for col in self._cols:
  41. setattr(db_obj, col, getattr(self, col))
  42. else:
  43. session.add(self._model_class(**{col: getattr(self, col)
  44. for col in self._cols}))
  45. class MechanismDriverContext(object):
  46. """MechanismDriver context base class."""
  47. def __init__(self, plugin, plugin_context):
  48. self._plugin = plugin
  49. # This temporarily creates a reference loop, but the
  50. # lifetime of PortContext is limited to a single
  51. # method call of the plugin.
  52. self._plugin_context = plugin_context
  53. class NetworkContext(MechanismDriverContext, api.NetworkContext):
  54. def __init__(self, plugin, plugin_context, network,
  55. original_network=None, segments=None):
  56. super(NetworkContext, self).__init__(plugin, plugin_context)
  57. self._network = network
  58. self._original_network = original_network
  59. self._segments = segments_db.get_network_segments(
  60. plugin_context, network['id']) if segments is None else segments
  61. @property
  62. def current(self):
  63. return self._network
  64. @property
  65. def original(self):
  66. return self._original_network
  67. @property
  68. def network_segments(self):
  69. return self._segments
  70. class SubnetContext(MechanismDriverContext, api.SubnetContext):
  71. def __init__(self, plugin, plugin_context, subnet, network,
  72. original_subnet=None):
  73. super(SubnetContext, self).__init__(plugin, plugin_context)
  74. self._subnet = subnet
  75. self._original_subnet = original_subnet
  76. self._network_context = NetworkContext(plugin, plugin_context,
  77. network) if network else None
  78. @property
  79. def current(self):
  80. return self._subnet
  81. @property
  82. def original(self):
  83. return self._original_subnet
  84. @property
  85. def network(self):
  86. if self._network_context is None:
  87. network = self._plugin.get_network(
  88. self._plugin_context, self.current['network_id'])
  89. self._network_context = NetworkContext(
  90. self._plugin, self._plugin_context, network)
  91. return self._network_context
  92. class PortContext(MechanismDriverContext, api.PortContext):
  93. def __init__(self, plugin, plugin_context, port, network, binding,
  94. binding_levels, original_port=None):
  95. super(PortContext, self).__init__(plugin, plugin_context)
  96. self._port = port
  97. self._original_port = original_port
  98. if isinstance(network, NetworkContext):
  99. self._network_context = network
  100. else:
  101. self._network_context = NetworkContext(
  102. plugin, plugin_context, network) if network else None
  103. # NOTE(kevinbenton): InstanceSnapshot can go away once we are working
  104. # with OVO objects instead of native SQLA objects.
  105. self._binding = InstanceSnapshot(binding)
  106. self._binding_levels = [InstanceSnapshot(l)
  107. for l in (binding_levels or [])]
  108. self._segments_to_bind = None
  109. self._new_bound_segment = None
  110. self._next_segments_to_bind = None
  111. if original_port:
  112. self._original_vif_type = binding.vif_type
  113. self._original_vif_details = self._plugin._get_vif_details(binding)
  114. self._original_binding_levels = self._binding_levels
  115. else:
  116. self._original_vif_type = None
  117. self._original_vif_details = None
  118. self._original_binding_levels = None
  119. self._new_port_status = None
  120. # The following methods are for use by the ML2 plugin and are not
  121. # part of the driver API.
  122. def _prepare_to_bind(self, segments_to_bind):
  123. self._segments_to_bind = segments_to_bind
  124. self._new_bound_segment = None
  125. self._next_segments_to_bind = None
  126. def _clear_binding_levels(self):
  127. self._binding_levels = []
  128. def _push_binding_level(self, binding_level):
  129. self._binding_levels.append(InstanceSnapshot(binding_level))
  130. def _pop_binding_level(self):
  131. return self._binding_levels.pop()
  132. # The following implement the abstract methods and properties of
  133. # the driver API.
  134. @property
  135. def current(self):
  136. return self._port
  137. @property
  138. def original(self):
  139. return self._original_port
  140. @property
  141. def status(self):
  142. # REVISIT(rkukura): Eliminate special DVR case as part of
  143. # resolving bug 1367391?
  144. if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
  145. return self._binding.status
  146. return self._port['status']
  147. @property
  148. def original_status(self):
  149. # REVISIT(rkukura): Should return host-specific status for DVR
  150. # ports. Fix as part of resolving bug 1367391.
  151. if self._original_port:
  152. return self._original_port['status']
  153. @property
  154. def network(self):
  155. if not self._network_context:
  156. network = self._plugin.get_network(
  157. self._plugin_context, self.current['network_id'])
  158. self._network_context = NetworkContext(
  159. self._plugin, self._plugin_context, network)
  160. return self._network_context
  161. @property
  162. def binding_levels(self):
  163. if self._binding_levels:
  164. return [{
  165. api.BOUND_DRIVER: level.driver,
  166. api.BOUND_SEGMENT: self._expand_segment(level.segment_id)
  167. } for level in self._binding_levels]
  168. @property
  169. def original_binding_levels(self):
  170. if self._original_binding_levels:
  171. return [{
  172. api.BOUND_DRIVER: level.driver,
  173. api.BOUND_SEGMENT: self._expand_segment(level.segment_id)
  174. } for level in self._original_binding_levels]
  175. @property
  176. def top_bound_segment(self):
  177. if self._binding_levels:
  178. return self._expand_segment(self._binding_levels[0].segment_id)
  179. @property
  180. def original_top_bound_segment(self):
  181. if self._original_binding_levels:
  182. return self._expand_segment(
  183. self._original_binding_levels[0].segment_id)
  184. @property
  185. def bottom_bound_segment(self):
  186. if self._binding_levels:
  187. return self._expand_segment(self._binding_levels[-1].segment_id)
  188. @property
  189. def original_bottom_bound_segment(self):
  190. if self._original_binding_levels:
  191. return self._expand_segment(
  192. self._original_binding_levels[-1].segment_id)
  193. def _expand_segment(self, segment_id):
  194. for s in self.network.network_segments:
  195. if s['id'] == segment_id:
  196. return s
  197. # TODO(kevinbenton): eliminate the query below. The above should
  198. # always return since the port is bound to a network segment. Leaving
  199. # in for now for minimally invasive change for back-port.
  200. segment = segments_db.get_segment_by_id(self._plugin_context,
  201. segment_id)
  202. if not segment:
  203. LOG.warning("Could not expand segment %s", segment_id)
  204. return segment
  205. @property
  206. def host(self):
  207. # REVISIT(rkukura): Eliminate special DVR case as part of
  208. # resolving bug 1367391?
  209. if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
  210. return self._binding.host
  211. return self._port.get(portbindings.HOST_ID)
  212. @property
  213. def original_host(self):
  214. # REVISIT(rkukura): Eliminate special DVR case as part of
  215. # resolving bug 1367391?
  216. if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
  217. return self._original_port and self._binding.host
  218. else:
  219. return (self._original_port and
  220. self._original_port.get(portbindings.HOST_ID))
  221. @property
  222. def vif_type(self):
  223. return self._binding.vif_type
  224. @property
  225. def original_vif_type(self):
  226. return self._original_vif_type
  227. @property
  228. def vif_details(self):
  229. return self._plugin._get_vif_details(self._binding)
  230. @property
  231. def original_vif_details(self):
  232. return self._original_vif_details
  233. @property
  234. def segments_to_bind(self):
  235. return self._segments_to_bind
  236. def host_agents(self, agent_type):
  237. return self._plugin.get_agents(self._plugin_context,
  238. filters={'agent_type': [agent_type],
  239. 'host': [self._binding.host]})
  240. def set_binding(self, segment_id, vif_type, vif_details,
  241. status=None):
  242. # TODO(rkukura) Verify binding allowed, segment in network
  243. self._new_bound_segment = segment_id
  244. self._binding.vif_type = vif_type
  245. self._binding.vif_details = jsonutils.dumps(vif_details)
  246. self._new_port_status = status
  247. def continue_binding(self, segment_id, next_segments_to_bind):
  248. # TODO(rkukura) Verify binding allowed, segment in network
  249. self._new_bound_segment = segment_id
  250. self._next_segments_to_bind = next_segments_to_bind
  251. def allocate_dynamic_segment(self, segment):
  252. network_id = self._network_context.current['id']
  253. return self._plugin.type_manager.allocate_dynamic_segment(
  254. self._plugin_context, network_id, segment)
  255. def release_dynamic_segment(self, segment_id):
  256. return self._plugin.type_manager.release_dynamic_segment(
  257. self._plugin_context, segment_id)