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.

mech_openvswitch.py 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 os
  16. import uuid
  17. from neutron_lib.api.definitions import portbindings
  18. from neutron_lib.api.definitions import provider_net
  19. from neutron_lib.callbacks import events
  20. from neutron_lib.callbacks import registry
  21. from neutron_lib import constants
  22. from oslo_config import cfg
  23. from oslo_log import log
  24. from neutron._i18n import _
  25. from neutron.agent import securitygroups_rpc
  26. from neutron.conf.plugins.ml2.drivers.openvswitch import mech_ovs_conf
  27. from neutron.plugins.ml2.drivers import mech_agent
  28. from neutron.plugins.ml2.drivers.openvswitch.agent.common \
  29. import constants as a_const
  30. from neutron.services.logapi.drivers.openvswitch import driver as log_driver
  31. from neutron.services.qos.drivers.openvswitch import driver as ovs_qos_driver
  32. LOG = log.getLogger(__name__)
  33. IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall."
  34. "OVSHybridIptablesFirewallDriver")
  35. mech_ovs_conf.register_ovs_mech_driver_opts()
  36. class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
  37. """Attach to networks using openvswitch L2 agent.
  38. The OpenvswitchMechanismDriver integrates the ml2 plugin with the
  39. openvswitch L2 agent. Port binding with this driver requires the
  40. openvswitch agent to be running on the port's host, and that agent
  41. to have connectivity to at least one segment of the port's
  42. network.
  43. """
  44. resource_provider_uuid5_namespace = uuid.UUID(
  45. '87ee7d5c-73bb-11e8-9008-c4d987b2a692')
  46. def __init__(self):
  47. sg_enabled = securitygroups_rpc.is_firewall_enabled()
  48. hybrid_plug_required = (
  49. not cfg.CONF.SECURITYGROUP.firewall_driver or
  50. cfg.CONF.SECURITYGROUP.firewall_driver in (
  51. IPTABLES_FW_DRIVER_FULL, 'iptables_hybrid')
  52. ) and sg_enabled
  53. vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled,
  54. portbindings.OVS_HYBRID_PLUG: hybrid_plug_required,
  55. portbindings.VIF_DETAILS_CONNECTIVITY:
  56. portbindings.CONNECTIVITY_L2}
  57. # NOTE(moshele): Bind DIRECT (SR-IOV) port allows
  58. # to offload the OVS flows using tc to the SR-IOV NIC.
  59. # We are using OVS mechanism driver because the openvswitch (>=2.8.0)
  60. # support hardware offload via tc and that allow us to manage the VF by
  61. # OpenFlow control plane using representor net-device.
  62. super(OpenvswitchMechanismDriver, self).__init__(
  63. constants.AGENT_TYPE_OVS,
  64. portbindings.VIF_TYPE_OVS,
  65. vif_details)
  66. # TODO(lajoskatona): move this blacklisting to
  67. # SimpleAgentMechanismDriverBase. By that e blacklisting and validation
  68. # of the vnic_types would be available for all mechanism drivers.
  69. self.supported_vnic_types = self.blacklist_supported_vnic_types(
  70. vnic_types=[portbindings.VNIC_NORMAL,
  71. portbindings.VNIC_DIRECT,
  72. portbindings.VNIC_SMARTNIC],
  73. blacklist=cfg.CONF.OVS_DRIVER.vnic_type_blacklist
  74. )
  75. LOG.info("%s's supported_vnic_types: %s",
  76. self.agent_type, self.supported_vnic_types)
  77. ovs_qos_driver.register()
  78. log_driver.register()
  79. def get_allowed_network_types(self, agent):
  80. return (agent['configurations'].get('tunnel_types', []) +
  81. [constants.TYPE_LOCAL, constants.TYPE_FLAT,
  82. constants.TYPE_VLAN])
  83. def get_mappings(self, agent):
  84. return agent['configurations'].get('bridge_mappings', {})
  85. def get_standard_device_mappings(self, agent):
  86. """Return the agent's bridge mappings in a standard way.
  87. The common format for OVS and SRIOv mechanism drivers:
  88. {'physnet_name': ['device_or_bridge_1', 'device_or_bridge_2']}
  89. :param agent: The agent
  90. :returns A dict in the format: {'physnet_name': ['bridge_or_device']}
  91. :raises ValueError: if there is no bridge_mappings key in
  92. agent['configurations']
  93. """
  94. if 'bridge_mappings' in agent['configurations']:
  95. return {k: [v] for k, v in
  96. agent['configurations']['bridge_mappings'].items()}
  97. else:
  98. raise ValueError(_('Cannot standardize bridge mappings of agent '
  99. 'type: %s'), agent['agent_type'])
  100. def check_vlan_transparency(self, context):
  101. """Currently Openvswitch driver doesn't support vlan transparency."""
  102. return False
  103. def bind_port(self, context):
  104. vnic_type = context.current.get(portbindings.VNIC_TYPE,
  105. portbindings.VNIC_NORMAL)
  106. profile = context.current.get(portbindings.PROFILE)
  107. capabilities = []
  108. if profile:
  109. capabilities = profile.get('capabilities', [])
  110. if (vnic_type == portbindings.VNIC_DIRECT and
  111. 'switchdev' not in capabilities):
  112. LOG.debug("Refusing to bind due to unsupported vnic_type: %s with "
  113. "no switchdev capability", portbindings.VNIC_DIRECT)
  114. return
  115. super(OpenvswitchMechanismDriver, self).bind_port(context)
  116. def get_supported_vif_type(self, agent):
  117. caps = agent['configurations'].get('ovs_capabilities', {})
  118. if (any(x in caps.get('iface_types', []) for x
  119. in [a_const.OVS_DPDK_VHOST_USER,
  120. a_const.OVS_DPDK_VHOST_USER_CLIENT]) and
  121. agent['configurations'].get('datapath_type') ==
  122. a_const.OVS_DATAPATH_NETDEV):
  123. return portbindings.VIF_TYPE_VHOST_USER
  124. return self.vif_type
  125. def get_vif_type(self, context, agent, segment):
  126. if (context.current.get(portbindings.VNIC_TYPE) ==
  127. portbindings.VNIC_DIRECT):
  128. return portbindings.VIF_TYPE_OVS
  129. return self.get_supported_vif_type(agent)
  130. def get_vhost_mode(self, iface_types):
  131. # NOTE(sean-k-mooney): this function converts the ovs vhost user
  132. # driver mode into the qemu vhost user mode. If OVS is the server,
  133. # qemu is the client and vice-versa.
  134. if (a_const.OVS_DPDK_VHOST_USER_CLIENT in iface_types):
  135. return portbindings.VHOST_USER_MODE_SERVER
  136. return portbindings.VHOST_USER_MODE_CLIENT
  137. def get_vif_details(self, context, agent, segment):
  138. vif_details = self._pre_get_vif_details(agent, context)
  139. self._set_bridge_name(context.current, vif_details, agent)
  140. return vif_details
  141. @staticmethod
  142. def _set_bridge_name(port, vif_details, agent):
  143. # REVISIT(rawlin): add BridgeName as a nullable column to the Port
  144. # model and simply check here if it's set and insert it into the
  145. # vif_details.
  146. def set_bridge_name_inner(bridge_name):
  147. vif_details[portbindings.VIF_DETAILS_BRIDGE_NAME] = bridge_name
  148. bridge_name = agent['configurations'].get('integration_bridge')
  149. if bridge_name:
  150. vif_details[portbindings.VIF_DETAILS_BRIDGE_NAME] = bridge_name
  151. registry.publish(
  152. a_const.OVS_BRIDGE_NAME, events.BEFORE_READ,
  153. set_bridge_name_inner,
  154. payload=events.EventPayload(None, metadata={'port': port}))
  155. def _pre_get_vif_details(self, agent, context):
  156. a_config = agent['configurations']
  157. vif_type = self.get_vif_type(context, agent, segment=None)
  158. if vif_type != portbindings.VIF_TYPE_VHOST_USER:
  159. details = dict(self.vif_details)
  160. hybrid = portbindings.OVS_HYBRID_PLUG
  161. if hybrid in a_config:
  162. # we only override the vif_details for hybrid plugging set
  163. # in the constructor if the agent specifically requests it
  164. details[hybrid] = a_config[hybrid]
  165. else:
  166. sock_path = self.agent_vhu_sockpath(agent, context.current['id'])
  167. caps = a_config.get('ovs_capabilities', {})
  168. mode = self.get_vhost_mode(caps.get('iface_types', []))
  169. details = {portbindings.CAP_PORT_FILTER: False,
  170. portbindings.OVS_HYBRID_PLUG: False,
  171. portbindings.VHOST_USER_MODE: mode,
  172. portbindings.VHOST_USER_OVS_PLUG: True,
  173. portbindings.VHOST_USER_SOCKET: sock_path}
  174. details[portbindings.OVS_DATAPATH_TYPE] = a_config.get(
  175. 'datapath_type', a_const.OVS_DATAPATH_SYSTEM)
  176. return details
  177. @staticmethod
  178. def agent_vhu_sockpath(agent, port_id):
  179. """Return the agent's vhost-user socket path for a given port"""
  180. sockdir = agent['configurations'].get('vhostuser_socket_dir',
  181. a_const.VHOST_USER_SOCKET_DIR)
  182. sock_name = (constants.VHOST_USER_DEVICE_PREFIX + port_id)[:14]
  183. return os.path.join(sockdir, sock_name)
  184. @staticmethod
  185. def provider_network_attribute_updates_supported():
  186. return [provider_net.SEGMENTATION_ID]