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.

330 lines
15KB

  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. import mock
  16. from neutron_lib.api.definitions import portbindings
  17. from neutron_lib import constants as const
  18. from neutron_lib import context
  19. from neutron_lib.plugins import directory
  20. from oslo_config import cfg
  21. from oslo_serialization import jsonutils
  22. from neutron.conf.plugins.ml2.drivers import driver_type
  23. from neutron.plugins.ml2 import driver_context
  24. from neutron.plugins.ml2 import models as ml2_models
  25. from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
  26. class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
  27. def setUp(self):
  28. # Enable the test mechanism driver to ensure that
  29. # we can successfully call through to all mechanism
  30. # driver apis.
  31. cfg.CONF.set_override('mechanism_drivers',
  32. ['logger', 'test'],
  33. 'ml2')
  34. # NOTE(dasm): ml2_type_vlan requires to be registered before used.
  35. # This piece was refactored and removed from .config, so it causes
  36. # a problem, when tests are executed with pdb.
  37. # There is no problem when tests are running without debugger.
  38. driver_type.register_ml2_drivers_vlan_opts()
  39. cfg.CONF.set_override('network_vlan_ranges',
  40. ['physnet1:1000:1099'],
  41. group='ml2_type_vlan')
  42. super(PortBindingTestCase, self).setUp('ml2')
  43. self.port_create_status = 'DOWN'
  44. self.plugin = directory.get_plugin()
  45. self.plugin.start_rpc_listeners()
  46. def _check_response(self, port, vif_type, has_port_filter, bound, status):
  47. self.assertEqual(vif_type, port[portbindings.VIF_TYPE])
  48. vif_details = port[portbindings.VIF_DETAILS]
  49. port_status = port['status']
  50. if bound:
  51. # TODO(rkukura): Replace with new VIF security details
  52. self.assertEqual(has_port_filter,
  53. vif_details[portbindings.CAP_PORT_FILTER])
  54. self.assertEqual(status or 'DOWN', port_status)
  55. else:
  56. self.assertEqual('DOWN', port_status)
  57. def _test_port_binding(self, host, vif_type, has_port_filter, bound,
  58. status=None, network_type='local'):
  59. mac_address = 'aa:aa:aa:aa:aa:aa'
  60. host_arg = {portbindings.HOST_ID: host,
  61. 'mac_address': mac_address}
  62. with self.port(name='name', arg_list=(portbindings.HOST_ID,),
  63. **host_arg) as port:
  64. self._check_response(port['port'], vif_type, has_port_filter,
  65. bound, status)
  66. port_id = port['port']['id']
  67. neutron_context = context.get_admin_context()
  68. details = self.plugin.endpoints[0].get_device_details(
  69. neutron_context, agent_id="theAgentId", device=port_id)
  70. if bound:
  71. self.assertEqual(network_type, details['network_type'])
  72. self.assertEqual(mac_address, details['mac_address'])
  73. else:
  74. self.assertNotIn('network_type', details)
  75. self.assertNotIn('mac_address', details)
  76. def test_unbound(self):
  77. self._test_port_binding("",
  78. portbindings.VIF_TYPE_UNBOUND,
  79. False, False)
  80. def test_binding_failed(self):
  81. self._test_port_binding("host-fail",
  82. portbindings.VIF_TYPE_BINDING_FAILED,
  83. False, False)
  84. def test_binding_no_filter(self):
  85. self._test_port_binding("host-ovs-no_filter",
  86. portbindings.VIF_TYPE_OVS,
  87. False, True)
  88. def test_binding_filter(self):
  89. self._test_port_binding("host-bridge-filter",
  90. portbindings.VIF_TYPE_BRIDGE,
  91. True, True)
  92. def test_binding_status_active(self):
  93. self._test_port_binding("host-ovs-filter-active",
  94. portbindings.VIF_TYPE_OVS,
  95. True, True, 'ACTIVE')
  96. def test_update_port_binding_no_binding(self):
  97. ctx = context.get_admin_context()
  98. with self.port(name='name') as port:
  99. # emulating concurrent binding deletion
  100. with ctx.session.begin():
  101. for item in (ctx.session.query(ml2_models.PortBinding).
  102. filter_by(port_id=port['port']['id'])):
  103. ctx.session.delete(item)
  104. self.assertIsNone(
  105. self.plugin.get_bound_port_context(ctx, port['port']['id']))
  106. def test_hierarchical_binding(self):
  107. self._test_port_binding("host-hierarchical",
  108. portbindings.VIF_TYPE_OVS,
  109. False, True, network_type='vlan')
  110. def test_get_bound_port_context_cache_hit(self):
  111. ctx = context.get_admin_context()
  112. with self.port(name='name') as port:
  113. cached_network_id = port['port']['network_id']
  114. some_network = {'id': cached_network_id}
  115. cached_networks = {cached_network_id: some_network}
  116. self.plugin.get_network = mock.Mock(return_value=some_network)
  117. self.plugin.get_bound_port_context(ctx, port['port']['id'],
  118. cached_networks=cached_networks)
  119. self.assertFalse(self.plugin.get_network.called)
  120. def _test_update_port_binding(self, host, new_host=None):
  121. with mock.patch.object(self.plugin,
  122. '_notify_port_updated') as notify_mock:
  123. host_arg = {portbindings.HOST_ID: host}
  124. update_body = {'name': 'test_update'}
  125. if new_host is not None:
  126. update_body[portbindings.HOST_ID] = new_host
  127. with self.port(name='name', arg_list=(portbindings.HOST_ID,),
  128. **host_arg) as port:
  129. neutron_context = context.get_admin_context()
  130. updated_port = self._update('ports', port['port']['id'],
  131. {'port': update_body},
  132. neutron_context=neutron_context)
  133. port_data = updated_port['port']
  134. if new_host is not None:
  135. self.assertEqual(new_host,
  136. port_data[portbindings.HOST_ID])
  137. else:
  138. self.assertEqual(host, port_data[portbindings.HOST_ID])
  139. if new_host is not None and new_host != host:
  140. notify_mock.assert_called_once_with(mock.ANY)
  141. else:
  142. self.assertFalse(notify_mock.called)
  143. def test_update_with_new_host_binding_notifies_agent(self):
  144. self._test_update_port_binding('host-ovs-no_filter',
  145. 'host-bridge-filter')
  146. def test_update_with_same_host_binding_does_not_notify(self):
  147. self._test_update_port_binding('host-ovs-no_filter',
  148. 'host-ovs-no_filter')
  149. def test_update_without_binding_does_not_notify(self):
  150. self._test_update_port_binding('host-ovs-no_filter')
  151. def testt_update_from_empty_to_host_binding_notifies_agent(self):
  152. self._test_update_port_binding('', 'host-ovs-no_filter')
  153. def test_update_from_host_to_empty_binding_notifies_agent(self):
  154. self._test_update_port_binding('host-ovs-no_filter', '')
  155. def test_process_binding_port_host_id_changed(self):
  156. ctx = context.get_admin_context()
  157. plugin = directory.get_plugin()
  158. host_id = {portbindings.HOST_ID: 'host1'}
  159. with self.port(**host_id) as port:
  160. # Since the port is DOWN at first
  161. # It's necessary to make its status ACTIVE for this test
  162. plugin.update_port_status(ctx, port['port']['id'],
  163. const.PORT_STATUS_ACTIVE)
  164. attrs = port['port']
  165. attrs['status'] = const.PORT_STATUS_ACTIVE
  166. original_port = attrs.copy()
  167. attrs['binding:host_id'] = 'host2'
  168. updated_port = attrs.copy()
  169. network = {'id': attrs['network_id']}
  170. binding = ml2_models.PortBinding(
  171. port_id=original_port['id'],
  172. host=original_port['binding:host_id'],
  173. vnic_type=original_port['binding:vnic_type'],
  174. profile=jsonutils.dumps(original_port['binding:profile']),
  175. vif_type=original_port['binding:vif_type'],
  176. vif_details=original_port['binding:vif_details'])
  177. levels = []
  178. mech_context = driver_context.PortContext(
  179. plugin, ctx, updated_port, network, binding, levels,
  180. original_port=original_port)
  181. plugin._process_port_binding(mech_context, port['port'])
  182. self.assertEqual(const.PORT_STATUS_DOWN, updated_port['status'])
  183. port_dict = plugin.get_port(ctx, port['port']['id'])
  184. self.assertEqual(const.PORT_STATUS_DOWN, port_dict['status'])
  185. def test_distributed_binding(self):
  186. ctx = context.get_admin_context()
  187. with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
  188. port_id = port['port']['id']
  189. # Verify port's VIF type and status.
  190. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
  191. port['port'][portbindings.VIF_TYPE])
  192. self.assertEqual('DOWN', port['port']['status'])
  193. # Update port to bind for a host.
  194. self.plugin.update_distributed_port_binding(ctx, port_id, {'port':
  195. {portbindings.HOST_ID: 'host-ovs-no_filter',
  196. 'device_id': 'router1'}})
  197. # Get port and verify VIF type and status unchanged.
  198. port = self._show('ports', port_id)
  199. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
  200. port['port'][portbindings.VIF_TYPE])
  201. self.assertEqual('DOWN', port['port']['status'])
  202. # Get and verify binding details for host
  203. details = self.plugin.endpoints[0].get_device_details(
  204. ctx, agent_id="theAgentId", device=port_id,
  205. host='host-ovs-no_filter')
  206. self.assertEqual('local', details['network_type'])
  207. # Get port and verify VIF type and changed status.
  208. port = self._show('ports', port_id)
  209. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
  210. port['port'][portbindings.VIF_TYPE])
  211. self.assertEqual('BUILD', port['port']['status'])
  212. # Mark device up.
  213. self.plugin.endpoints[0].update_device_up(
  214. ctx, agent_id="theAgentId", device=port_id,
  215. host='host-ovs-no_filter')
  216. # Get port and verify VIF type and changed status.
  217. port = self._show('ports', port_id)
  218. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
  219. port['port'][portbindings.VIF_TYPE])
  220. self.assertEqual('ACTIVE', port['port']['status'])
  221. # Mark device down.
  222. self.plugin.endpoints[0].update_device_down(
  223. ctx, agent_id="theAgentId", device=port_id,
  224. host='host-ovs-no_filter')
  225. # Get port and verify VIF type and changed status.
  226. port = self._show('ports', port_id)
  227. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
  228. port['port'][portbindings.VIF_TYPE])
  229. self.assertEqual('DOWN', port['port']['status'])
  230. def test_distributed_binding_multi_host_status(self):
  231. ctx = context.get_admin_context()
  232. with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
  233. port_id = port['port']['id']
  234. # Update port to bind for 1st host.
  235. self.plugin.update_distributed_port_binding(ctx, port_id, {'port':
  236. {portbindings.HOST_ID: 'host-ovs-no_filter',
  237. 'device_id': 'router1'}})
  238. # Mark 1st device up.
  239. self.plugin.endpoints[0].update_device_up(
  240. ctx, agent_id="theAgentId", device=port_id,
  241. host='host-ovs-no_filter')
  242. # Get port and verify status is ACTIVE.
  243. port = self._show('ports', port_id)
  244. self.assertEqual('ACTIVE', port['port']['status'])
  245. # Update port to bind for a 2nd host.
  246. self.plugin.update_distributed_port_binding(ctx, port_id, {'port':
  247. {portbindings.HOST_ID: 'host-bridge-filter',
  248. 'device_id': 'router1'}})
  249. # Mark 2nd device up.
  250. self.plugin.endpoints[0].update_device_up(
  251. ctx, agent_id="the2ndAgentId", device=port_id,
  252. host='host-bridge-filter')
  253. # Get port and verify status unchanged.
  254. port = self._show('ports', port_id)
  255. self.assertEqual('ACTIVE', port['port']['status'])
  256. # Mark 1st device down.
  257. self.plugin.endpoints[0].update_device_down(
  258. ctx, agent_id="theAgentId", device=port_id,
  259. host='host-ovs-no_filter')
  260. # Get port and verify status unchanged.
  261. port = self._show('ports', port_id)
  262. self.assertEqual('ACTIVE', port['port']['status'])
  263. # Mark 2nd device down.
  264. self.plugin.endpoints[0].update_device_down(
  265. ctx, agent_id="the2ndAgentId", device=port_id,
  266. host='host-bridge-filter')
  267. # Get port and verify status is DOWN.
  268. port = self._show('ports', port_id)
  269. self.assertEqual('DOWN', port['port']['status'])
  270. def test_distributed_binding_update_unbound_host(self):
  271. ctx = context.get_admin_context()
  272. with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
  273. port_id = port['port']['id']
  274. # Mark device up without first binding on host.
  275. self.plugin.endpoints[0].update_device_up(
  276. ctx, agent_id="theAgentId", device=port_id,
  277. host='host-ovs-no_filter')
  278. # Get port and verify status is still DOWN.
  279. port = self._show('ports', port_id)
  280. self.assertEqual('DOWN', port['port']['status'])