1 # Copyright (c) 2013 OpenStack Foundation
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
16 from oslo_config import cfg
17 from oslo_log import log
18 from oslo_utils import excutils
22 from neutron._i18n import _, _LE, _LI, _LW
23 from neutron.api.v2 import attributes
24 from neutron.common import exceptions as exc
25 from neutron.extensions import external_net
26 from neutron.extensions import multiprovidernet as mpnet
27 from neutron.extensions import portbindings
28 from neutron.extensions import providernet as provider
29 from neutron.extensions import vlantransparent
30 from neutron.plugins.ml2.common import exceptions as ml2_exc
31 from neutron.plugins.ml2 import db
32 from neutron.plugins.ml2 import driver_api as api
33 from neutron.plugins.ml2 import models
34 from neutron.services.qos import qos_consts
36 LOG = log.getLogger(__name__)
38 MAX_BINDING_LEVELS = 10
41 class TypeManager(stevedore.named.NamedExtensionManager):
42 """Manage network segment types using drivers."""
45 # Mapping from type name to DriverManager
48 LOG.info(_LI("Configured type driver names: %s"),
49 cfg.CONF.ml2.type_drivers)
50 super(TypeManager, self).__init__('neutron.ml2.type_drivers',
51 cfg.CONF.ml2.type_drivers,
53 LOG.info(_LI("Loaded type driver names: %s"), self.names())
54 self._register_types()
55 self._check_tenant_network_types(cfg.CONF.ml2.tenant_network_types)
56 self._check_external_network_type(cfg.CONF.ml2.external_network_type)
58 def _register_types(self):
60 network_type = ext.obj.get_type()
61 if network_type in self.drivers:
62 LOG.error(_LE("Type driver '%(new_driver)s' ignored because"
63 " type driver '%(old_driver)s' is already"
64 " registered for type '%(type)s'"),
65 {'new_driver': ext.name,
66 'old_driver': self.drivers[network_type].name,
67 'type': network_type})
69 self.drivers[network_type] = ext
70 LOG.info(_LI("Registered types: %s"), self.drivers.keys())
72 def _check_tenant_network_types(self, types):
73 self.tenant_network_types = []
74 for network_type in types:
75 if network_type in self.drivers:
76 self.tenant_network_types.append(network_type)
78 LOG.error(_LE("No type driver for tenant network_type: %s. "
79 "Service terminated!"), network_type)
81 LOG.info(_LI("Tenant network_types: %s"), self.tenant_network_types)
83 def _check_external_network_type(self, ext_network_type):
84 if ext_network_type and ext_network_type not in self.drivers:
85 LOG.error(_LE("No type driver for external network_type: %s. "
86 "Service terminated!"), ext_network_type)
89 def _process_provider_segment(self, segment):
90 (network_type, physical_network,
91 segmentation_id) = (self._get_attribute(segment, attr)
92 for attr in provider.ATTRIBUTES)
94 if attributes.is_attr_set(network_type):
95 segment = {api.NETWORK_TYPE: network_type,
96 api.PHYSICAL_NETWORK: physical_network,
97 api.SEGMENTATION_ID: segmentation_id}
98 self.validate_provider_segment(segment)
101 msg = _("network_type required")
102 raise exc.InvalidInput(error_message=msg)
104 def _process_provider_create(self, network):
105 if any(attributes.is_attr_set(network.get(attr))
106 for attr in provider.ATTRIBUTES):
107 # Verify that multiprovider and provider attributes are not set
109 if attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
110 raise mpnet.SegmentsSetInConjunctionWithProviders()
111 segment = self._get_provider_segment(network)
112 return [self._process_provider_segment(segment)]
113 elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
114 segments = [self._process_provider_segment(s)
115 for s in network[mpnet.SEGMENTS]]
116 mpnet.check_duplicate_segments(segments, self.is_partial_segment)
119 def _match_segment(self, segment, filters):
120 return all(not filters.get(attr) or segment.get(attr) in filters[attr]
121 for attr in provider.ATTRIBUTES)
123 def _get_provider_segment(self, network):
124 # TODO(manishg): Placeholder method
125 # Code intended for operating on a provider segment should use
126 # this method to extract the segment, even though currently the
127 # segment attributes are part of the network dictionary. In the
128 # future, network and segment information will be decoupled and
129 # here we will do the job of extracting the segment information.
132 def network_matches_filters(self, network, filters):
135 if any(attributes.is_attr_set(network.get(attr))
136 for attr in provider.ATTRIBUTES):
137 segments = [self._get_provider_segment(network)]
138 elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
139 segments = self._get_attribute(network, mpnet.SEGMENTS)
142 return any(self._match_segment(s, filters) for s in segments)
144 def _get_attribute(self, attrs, key):
145 value = attrs.get(key)
146 if value is attributes.ATTR_NOT_SPECIFIED:
150 def extend_network_dict_provider(self, context, network):
151 # this method is left for backward compat even though it would be
152 # easy to change the callers in tree to use the bulk function
153 return self.extend_networks_dict_provider(context, [network])
155 def extend_networks_dict_provider(self, context, networks):
156 ids = [network['id'] for network in networks]
157 net_segments = db.get_networks_segments(context.session, ids)
158 for network in networks:
159 segments = net_segments[network['id']]
160 self._extend_network_dict_provider(network, segments)
162 def _extend_network_dict_provider(self, network, segments):
164 LOG.error(_LE("Network %s has no segments"), network['id'])
165 for attr in provider.ATTRIBUTES:
167 elif len(segments) > 1:
168 network[mpnet.SEGMENTS] = [
169 {provider.NETWORK_TYPE: segment[api.NETWORK_TYPE],
170 provider.PHYSICAL_NETWORK: segment[api.PHYSICAL_NETWORK],
171 provider.SEGMENTATION_ID: segment[api.SEGMENTATION_ID]}
172 for segment in segments]
174 segment = segments[0]
175 network[provider.NETWORK_TYPE] = segment[api.NETWORK_TYPE]
176 network[provider.PHYSICAL_NETWORK] = segment[api.PHYSICAL_NETWORK]
177 network[provider.SEGMENTATION_ID] = segment[api.SEGMENTATION_ID]
179 def initialize(self):
180 for network_type, driver in six.iteritems(self.drivers):
181 LOG.info(_LI("Initializing driver for type '%s'"), network_type)
182 driver.obj.initialize()
184 def _add_network_segment(self, session, network_id, segment, mtu,
186 db.add_network_segment(session, network_id, segment, segment_index)
187 if segment.get(api.MTU, 0) > 0:
188 mtu.append(segment[api.MTU])
190 def create_network_segments(self, context, network, tenant_id):
191 """Call type drivers to create network segments."""
192 segments = self._process_provider_create(network)
193 session = context.session
195 with session.begin(subtransactions=True):
196 network_id = network['id']
198 for segment_index, segment in enumerate(segments):
199 segment = self.reserve_provider_segment(
201 self._add_network_segment(session, network_id, segment,
203 elif (cfg.CONF.ml2.external_network_type and
204 self._get_attribute(network, external_net.EXTERNAL)):
205 segment = self._allocate_ext_net_segment(session)
206 self._add_network_segment(session, network_id, segment, mtu)
208 segment = self._allocate_tenant_net_segment(session)
209 self._add_network_segment(session, network_id, segment, mtu)
210 network[api.MTU] = min(mtu) if mtu else 0
212 def is_partial_segment(self, segment):
213 network_type = segment[api.NETWORK_TYPE]
214 driver = self.drivers.get(network_type)
216 return driver.obj.is_partial_segment(segment)
218 msg = _("network_type value '%s' not supported") % network_type
219 raise exc.InvalidInput(error_message=msg)
221 def validate_provider_segment(self, segment):
222 network_type = segment[api.NETWORK_TYPE]
223 driver = self.drivers.get(network_type)
225 driver.obj.validate_provider_segment(segment)
227 msg = _("network_type value '%s' not supported") % network_type
228 raise exc.InvalidInput(error_message=msg)
230 def reserve_provider_segment(self, session, segment):
231 network_type = segment.get(api.NETWORK_TYPE)
232 driver = self.drivers.get(network_type)
233 return driver.obj.reserve_provider_segment(session, segment)
235 def _allocate_segment(self, session, network_type):
236 driver = self.drivers.get(network_type)
237 return driver.obj.allocate_tenant_segment(session)
239 def _allocate_tenant_net_segment(self, session):
240 for network_type in self.tenant_network_types:
241 segment = self._allocate_segment(session, network_type)
244 raise exc.NoNetworkAvailable()
246 def _allocate_ext_net_segment(self, session):
247 network_type = cfg.CONF.ml2.external_network_type
248 segment = self._allocate_segment(session, network_type)
251 raise exc.NoNetworkAvailable()
253 def release_network_segments(self, session, network_id):
254 segments = db.get_network_segments(session, network_id,
257 for segment in segments:
258 network_type = segment.get(api.NETWORK_TYPE)
259 driver = self.drivers.get(network_type)
261 driver.obj.release_segment(session, segment)
263 LOG.error(_LE("Failed to release segment '%s' because "
264 "network type is not supported."), segment)
266 def allocate_dynamic_segment(self, session, network_id, segment):
267 """Allocate a dynamic segment using a partial or full segment dict."""
268 dynamic_segment = db.get_dynamic_segment(
269 session, network_id, segment.get(api.PHYSICAL_NETWORK),
270 segment.get(api.SEGMENTATION_ID))
273 return dynamic_segment
275 driver = self.drivers.get(segment.get(api.NETWORK_TYPE))
276 dynamic_segment = driver.obj.reserve_provider_segment(session, segment)
277 db.add_network_segment(session, network_id, dynamic_segment,
279 return dynamic_segment
281 def release_dynamic_segment(self, session, segment_id):
282 """Delete a dynamic segment."""
283 segment = db.get_segment_by_id(session, segment_id)
285 driver = self.drivers.get(segment.get(api.NETWORK_TYPE))
287 driver.obj.release_segment(session, segment)
288 db.delete_network_segment(session, segment_id)
290 LOG.error(_LE("Failed to release segment '%s' because "
291 "network type is not supported."), segment)
293 LOG.debug("No segment found with id %(segment_id)s", segment_id)
296 class MechanismManager(stevedore.named.NamedExtensionManager):
297 """Manage networking mechanisms using drivers."""
300 # Registered mechanism drivers, keyed by name.
301 self.mech_drivers = {}
302 # Ordered list of mechanism drivers, defining
303 # the order in which the drivers are called.
304 self.ordered_mech_drivers = []
306 LOG.info(_LI("Configured mechanism driver names: %s"),
307 cfg.CONF.ml2.mechanism_drivers)
308 super(MechanismManager, self).__init__('neutron.ml2.mechanism_drivers',
309 cfg.CONF.ml2.mechanism_drivers,
312 LOG.info(_LI("Loaded mechanism driver names: %s"), self.names())
313 self._register_mechanisms()
315 def _register_mechanisms(self):
316 """Register all mechanism drivers.
318 This method should only be called once in the MechanismManager
322 self.mech_drivers[ext.name] = ext
323 self.ordered_mech_drivers.append(ext)
324 LOG.info(_LI("Registered mechanism drivers: %s"),
325 [driver.name for driver in self.ordered_mech_drivers])
328 def supported_qos_rule_types(self):
329 if not self.ordered_mech_drivers:
332 rule_types = set(qos_consts.VALID_RULE_TYPES)
333 binding_driver_found = False
335 # Recalculate on every call to allow drivers determine supported rule
337 for driver in self.ordered_mech_drivers:
338 driver_obj = driver.obj
339 if driver_obj._supports_port_binding:
340 binding_driver_found = True
341 if hasattr(driver_obj, 'supported_qos_rule_types'):
343 rule_types & set(driver_obj.supported_qos_rule_types)
344 dropped_rule_types = new_rule_types - rule_types
345 if dropped_rule_types:
347 _LI("%(rule_types)s rule types disabled for ml2 "
348 "because %(driver)s does not support them"),
349 {'rule_types': ', '.join(dropped_rule_types),
350 'driver': driver.name})
351 rule_types = new_rule_types
353 # at least one of drivers does not support QoS, meaning
354 # there are no rule types supported by all of them
356 _LW("%s does not support QoS; "
357 "no rule types available"),
361 if binding_driver_found:
362 rule_types = list(rule_types)
365 LOG.debug("Supported QoS rule types "
366 "(common subset for all mech drivers): %s", rule_types)
369 def initialize(self):
370 for driver in self.ordered_mech_drivers:
371 LOG.info(_LI("Initializing mechanism driver '%s'"), driver.name)
372 driver.obj.initialize()
374 def _check_vlan_transparency(self, context):
375 """Helper method for checking vlan transparecncy support.
377 :param context: context parameter to pass to each method call
378 :raises: neutron.extensions.vlantransparent.
379 VlanTransparencyDriverError if any mechanism driver doesn't
380 support vlan transparency.
382 if context.current.get('vlan_transparent'):
383 for driver in self.ordered_mech_drivers:
384 if not driver.obj.check_vlan_transparency(context):
385 raise vlantransparent.VlanTransparencyDriverError()
387 def _call_on_drivers(self, method_name, context,
388 continue_on_failure=False):
389 """Helper method for calling a method across all mechanism drivers.
391 :param method_name: name of the method to call
392 :param context: context parameter to pass to each method call
393 :param continue_on_failure: whether or not to continue to call
394 all mechanism drivers once one has raised an exception
395 :raises: neutron.plugins.ml2.common.MechanismDriverError
396 if any mechanism driver call fails.
399 for driver in self.ordered_mech_drivers:
401 getattr(driver.obj, method_name)(context)
404 _LE("Mechanism driver '%(name)s' failed in %(method)s"),
405 {'name': driver.name, 'method': method_name}
408 if not continue_on_failure:
411 raise ml2_exc.MechanismDriverError(
415 def create_network_precommit(self, context):
416 """Notify all mechanism drivers during network creation.
418 :raises: neutron.plugins.ml2.common.MechanismDriverError
419 if any mechanism driver create_network_precommit call fails.
421 Called within the database transaction. If a mechanism driver
422 raises an exception, then a MechanismDriverError is propogated
423 to the caller, triggering a rollback. There is no guarantee
424 that all mechanism drivers are called in this case.
426 self._check_vlan_transparency(context)
427 self._call_on_drivers("create_network_precommit", context)
429 def create_network_postcommit(self, context):
430 """Notify all mechanism drivers after network creation.
432 :raises: neutron.plugins.ml2.common.MechanismDriverError
433 if any mechanism driver create_network_postcommit call fails.
435 Called after the database transaction. If a mechanism driver
436 raises an exception, then a MechanismDriverError is propagated
437 to the caller, where the network will be deleted, triggering
438 any required cleanup. There is no guarantee that all mechanism
439 drivers are called in this case.
441 self._call_on_drivers("create_network_postcommit", context)
443 def update_network_precommit(self, context):
444 """Notify all mechanism drivers during network update.
446 :raises: neutron.plugins.ml2.common.MechanismDriverError
447 if any mechanism driver update_network_precommit call fails.
449 Called within the database transaction. If a mechanism driver
450 raises an exception, then a MechanismDriverError is propogated
451 to the caller, triggering a rollback. There is no guarantee
452 that all mechanism drivers are called in this case.
454 self._call_on_drivers("update_network_precommit", context)
456 def update_network_postcommit(self, context):
457 """Notify all mechanism drivers after network update.
459 :raises: neutron.plugins.ml2.common.MechanismDriverError
460 if any mechanism driver update_network_postcommit call fails.
462 Called after the database transaction. If any mechanism driver
463 raises an error, then the error is logged but we continue to
464 call every other mechanism driver. A MechanismDriverError is
465 then reraised at the end to notify the caller of a failure.
467 self._call_on_drivers("update_network_postcommit", context,
468 continue_on_failure=True)
470 def delete_network_precommit(self, context):
471 """Notify all mechanism drivers during network deletion.
473 :raises: neutron.plugins.ml2.common.MechanismDriverError
474 if any mechanism driver delete_network_precommit call fails.
476 Called within the database transaction. If a mechanism driver
477 raises an exception, then a MechanismDriverError is propogated
478 to the caller, triggering a rollback. There is no guarantee
479 that all mechanism drivers are called in this case.
481 self._call_on_drivers("delete_network_precommit", context)
483 def delete_network_postcommit(self, context):
484 """Notify all mechanism drivers after network deletion.
486 :raises: neutron.plugins.ml2.common.MechanismDriverError
487 if any mechanism driver delete_network_postcommit call fails.
489 Called after the database transaction. If any mechanism driver
490 raises an error, then the error is logged but we continue to
491 call every other mechanism driver. A MechanismDriverError is
492 then reraised at the end to notify the caller of a failure. In
493 general we expect the caller to ignore the error, as the
494 network resource has already been deleted from the database
495 and it doesn't make sense to undo the action by recreating the
498 self._call_on_drivers("delete_network_postcommit", context,
499 continue_on_failure=True)
501 def create_subnet_precommit(self, context):
502 """Notify all mechanism drivers during subnet creation.
504 :raises: neutron.plugins.ml2.common.MechanismDriverError
505 if any mechanism driver create_subnet_precommit call fails.
507 Called within the database transaction. If a mechanism driver
508 raises an exception, then a MechanismDriverError is propogated
509 to the caller, triggering a rollback. There is no guarantee
510 that all mechanism drivers are called in this case.
512 self._call_on_drivers("create_subnet_precommit", context)
514 def create_subnet_postcommit(self, context):
515 """Notify all mechanism drivers after subnet creation.
517 :raises: neutron.plugins.ml2.common.MechanismDriverError
518 if any mechanism driver create_subnet_postcommit call fails.
520 Called after the database transaction. If a mechanism driver
521 raises an exception, then a MechanismDriverError is propagated
522 to the caller, where the subnet will be deleted, triggering
523 any required cleanup. There is no guarantee that all mechanism
524 drivers are called in this case.
526 self._call_on_drivers("create_subnet_postcommit", context)
528 def update_subnet_precommit(self, context):
529 """Notify all mechanism drivers during subnet update.
531 :raises: neutron.plugins.ml2.common.MechanismDriverError
532 if any mechanism driver update_subnet_precommit call fails.
534 Called within the database transaction. If a mechanism driver
535 raises an exception, then a MechanismDriverError is propogated
536 to the caller, triggering a rollback. There is no guarantee
537 that all mechanism drivers are called in this case.
539 self._call_on_drivers("update_subnet_precommit", context)
541 def update_subnet_postcommit(self, context):
542 """Notify all mechanism drivers after subnet update.
544 :raises: neutron.plugins.ml2.common.MechanismDriverError
545 if any mechanism driver update_subnet_postcommit call fails.
547 Called after the database transaction. If any mechanism driver
548 raises an error, then the error is logged but we continue to
549 call every other mechanism driver. A MechanismDriverError is
550 then reraised at the end to notify the caller of a failure.
552 self._call_on_drivers("update_subnet_postcommit", context,
553 continue_on_failure=True)
555 def delete_subnet_precommit(self, context):
556 """Notify all mechanism drivers during subnet deletion.
558 :raises: neutron.plugins.ml2.common.MechanismDriverError
559 if any mechanism driver delete_subnet_precommit call fails.
561 Called within the database transaction. If a mechanism driver
562 raises an exception, then a MechanismDriverError is propogated
563 to the caller, triggering a rollback. There is no guarantee
564 that all mechanism drivers are called in this case.
566 self._call_on_drivers("delete_subnet_precommit", context)
568 def delete_subnet_postcommit(self, context):
569 """Notify all mechanism drivers after subnet deletion.
571 :raises: neutron.plugins.ml2.common.MechanismDriverError
572 if any mechanism driver delete_subnet_postcommit call fails.
574 Called after the database transaction. If any mechanism driver
575 raises an error, then the error is logged but we continue to
576 call every other mechanism driver. A MechanismDriverError is
577 then reraised at the end to notify the caller of a failure. In
578 general we expect the caller to ignore the error, as the
579 subnet resource has already been deleted from the database
580 and it doesn't make sense to undo the action by recreating the
583 self._call_on_drivers("delete_subnet_postcommit", context,
584 continue_on_failure=True)
586 def create_port_precommit(self, context):
587 """Notify all mechanism drivers during port creation.
589 :raises: neutron.plugins.ml2.common.MechanismDriverError
590 if any mechanism driver create_port_precommit call fails.
592 Called within the database transaction. If a mechanism driver
593 raises an exception, then a MechanismDriverError is propogated
594 to the caller, triggering a rollback. There is no guarantee
595 that all mechanism drivers are called in this case.
597 self._call_on_drivers("create_port_precommit", context)
599 def create_port_postcommit(self, context):
600 """Notify all mechanism drivers of port creation.
602 :raises: neutron.plugins.ml2.common.MechanismDriverError
603 if any mechanism driver create_port_postcommit call fails.
605 Called after the database transaction. Errors raised by
606 mechanism drivers are left to propagate to the caller, where
607 the port will be deleted, triggering any required
608 cleanup. There is no guarantee that all mechanism drivers are
611 self._call_on_drivers("create_port_postcommit", context)
613 def update_port_precommit(self, context):
614 """Notify all mechanism drivers during port update.
616 :raises: neutron.plugins.ml2.common.MechanismDriverError
617 if any mechanism driver update_port_precommit call fails.
619 Called within the database transaction. If a mechanism driver
620 raises an exception, then a MechanismDriverError is propogated
621 to the caller, triggering a rollback. There is no guarantee
622 that all mechanism drivers are called in this case.
624 self._call_on_drivers("update_port_precommit", context)
626 def update_port_postcommit(self, context):
627 """Notify all mechanism drivers after port update.
629 :raises: neutron.plugins.ml2.common.MechanismDriverError
630 if any mechanism driver update_port_postcommit call fails.
632 Called after the database transaction. If any mechanism driver
633 raises an error, then the error is logged but we continue to
634 call every other mechanism driver. A MechanismDriverError is
635 then reraised at the end to notify the caller of a failure.
637 self._call_on_drivers("update_port_postcommit", context,
638 continue_on_failure=True)
640 def delete_port_precommit(self, context):
641 """Notify all mechanism drivers during port deletion.
643 :raises: neutron.plugins.ml2.common.MechanismDriverError
644 if any mechanism driver delete_port_precommit call fails.
646 Called within the database transaction. If a mechanism driver
647 raises an exception, then a MechanismDriverError is propogated
648 to the caller, triggering a rollback. There is no guarantee
649 that all mechanism drivers are called in this case.
651 self._call_on_drivers("delete_port_precommit", context)
653 def delete_port_postcommit(self, context):
654 """Notify all mechanism drivers after port deletion.
656 :raises: neutron.plugins.ml2.common.MechanismDriverError
657 if any mechanism driver delete_port_postcommit call fails.
659 Called after the database transaction. If any mechanism driver
660 raises an error, then the error is logged but we continue to
661 call every other mechanism driver. A MechanismDriverError is
662 then reraised at the end to notify the caller of a failure. In
663 general we expect the caller to ignore the error, as the
664 port resource has already been deleted from the database
665 and it doesn't make sense to undo the action by recreating the
668 self._call_on_drivers("delete_port_postcommit", context,
669 continue_on_failure=True)
671 def bind_port(self, context):
672 """Attempt to bind a port using registered mechanism drivers.
674 :param context: PortContext instance describing the port
676 Called outside any transaction to attempt to establish a port
679 binding = context._binding
680 LOG.debug("Attempting to bind port %(port)s on host %(host)s "
681 "for vnic_type %(vnic_type)s with profile %(profile)s",
682 {'port': context.current['id'],
683 'host': context.host,
684 'vnic_type': binding.vnic_type,
685 'profile': binding.profile})
686 context._clear_binding_levels()
687 if not self._bind_port_level(context, 0,
688 context.network.network_segments):
689 binding.vif_type = portbindings.VIF_TYPE_BINDING_FAILED
690 LOG.error(_LE("Failed to bind port %(port)s on host %(host)s"),
691 {'port': context.current['id'],
692 'host': context.host})
694 def _bind_port_level(self, context, level, segments_to_bind):
695 binding = context._binding
696 port_id = context.current['id']
697 LOG.debug("Attempting to bind port %(port)s on host %(host)s "
698 "at level %(level)s using segments %(segments)s",
700 'host': context.host,
702 'segments': segments_to_bind})
704 if level == MAX_BINDING_LEVELS:
705 LOG.error(_LE("Exceeded maximum binding levels attempting to bind "
706 "port %(port)s on host %(host)s"),
707 {'port': context.current['id'],
708 'host': context.host})
711 for driver in self.ordered_mech_drivers:
712 if not self._check_driver_to_bind(driver, segments_to_bind,
713 context._binding_levels):
716 context._prepare_to_bind(segments_to_bind)
717 driver.obj.bind_port(context)
718 segment = context._new_bound_segment
720 context._push_binding_level(
721 models.PortBindingLevel(port_id=port_id,
726 next_segments = context._next_segments_to_bind
728 # Continue binding another level.
729 if self._bind_port_level(context, level + 1,
733 context._pop_binding_level()
736 LOG.debug("Bound port: %(port)s, "
738 "vif_type: %(vif_type)s, "
739 "vif_details: %(vif_details)s, "
740 "binding_levels: %(binding_levels)s",
742 'host': context.host,
743 'vif_type': binding.vif_type,
744 'vif_details': binding.vif_details,
745 'binding_levels': context.binding_levels})
748 LOG.exception(_LE("Mechanism driver %s failed in "
751 LOG.error(_LE("Failed to bind port %(port)s on host %(host)s"),
752 {'port': context.current['id'],
753 'host': binding.host})
755 def _check_driver_to_bind(self, driver, segments_to_bind, binding_levels):
756 # To prevent a possible binding loop, don't try to bind with
757 # this driver if the same driver has already bound at a higher
758 # level to one of the segments we are currently trying to
759 # bind. Note that it is OK for the same driver to bind at
760 # multiple levels using different segments.
761 for level in binding_levels:
762 if (level.driver == driver and
763 level.segment_id in segments_to_bind):
767 def get_workers(self):
769 for driver in self.ordered_mech_drivers:
770 workers += driver.obj.get_workers()
774 class ExtensionManager(stevedore.named.NamedExtensionManager):
775 """Manage extension drivers using drivers."""
778 # Ordered list of extension drivers, defining
779 # the order in which the drivers are called.
780 self.ordered_ext_drivers = []
782 LOG.info(_LI("Configured extension driver names: %s"),
783 cfg.CONF.ml2.extension_drivers)
784 super(ExtensionManager, self).__init__('neutron.ml2.extension_drivers',
785 cfg.CONF.ml2.extension_drivers,
788 LOG.info(_LI("Loaded extension driver names: %s"), self.names())
789 self._register_drivers()
791 def _register_drivers(self):
792 """Register all extension drivers.
794 This method should only be called once in the ExtensionManager
798 self.ordered_ext_drivers.append(ext)
799 LOG.info(_LI("Registered extension drivers: %s"),
800 [driver.name for driver in self.ordered_ext_drivers])
802 def initialize(self):
803 # Initialize each driver in the list.
804 for driver in self.ordered_ext_drivers:
805 LOG.info(_LI("Initializing extension driver '%s'"), driver.name)
806 driver.obj.initialize()
808 def extension_aliases(self):
810 for driver in self.ordered_ext_drivers:
811 alias = driver.obj.extension_alias
814 LOG.info(_LI("Got %(alias)s extension from driver '%(drv)s'"),
815 {'alias': alias, 'drv': driver.name})
818 def _call_on_ext_drivers(self, method_name, plugin_context, data, result):
819 """Helper method for calling a method across all extension drivers."""
820 for driver in self.ordered_ext_drivers:
822 getattr(driver.obj, method_name)(plugin_context, data, result)
824 with excutils.save_and_reraise_exception():
825 LOG.info(_LI("Extension driver '%(name)s' failed in "
827 {'name': driver.name, 'method': method_name})
829 def process_create_network(self, plugin_context, data, result):
830 """Notify all extension drivers during network creation."""
831 self._call_on_ext_drivers("process_create_network", plugin_context,
834 def process_update_network(self, plugin_context, data, result):
835 """Notify all extension drivers during network update."""
836 self._call_on_ext_drivers("process_update_network", plugin_context,
839 def process_create_subnet(self, plugin_context, data, result):
840 """Notify all extension drivers during subnet creation."""
841 self._call_on_ext_drivers("process_create_subnet", plugin_context,
844 def process_update_subnet(self, plugin_context, data, result):
845 """Notify all extension drivers during subnet update."""
846 self._call_on_ext_drivers("process_update_subnet", plugin_context,
849 def process_create_port(self, plugin_context, data, result):
850 """Notify all extension drivers during port creation."""
851 self._call_on_ext_drivers("process_create_port", plugin_context,
854 def process_update_port(self, plugin_context, data, result):
855 """Notify all extension drivers during port update."""
856 self._call_on_ext_drivers("process_update_port", plugin_context,
859 def _call_on_dict_driver(self, method_name, session, base_model, result):
860 for driver in self.ordered_ext_drivers:
862 getattr(driver.obj, method_name)(session, base_model, result)
864 LOG.error(_LE("Extension driver '%(name)s' failed in "
866 {'name': driver.name, 'method': method_name})
867 raise ml2_exc.ExtensionDriverError(driver=driver.name)
869 def extend_network_dict(self, session, base_model, result):
870 """Notify all extension drivers to extend network dictionary."""
871 self._call_on_dict_driver("extend_network_dict", session, base_model,
874 def extend_subnet_dict(self, session, base_model, result):
875 """Notify all extension drivers to extend subnet dictionary."""
876 self._call_on_dict_driver("extend_subnet_dict", session, base_model,
879 def extend_port_dict(self, session, base_model, result):
880 """Notify all extension drivers to extend port dictionary."""
881 self._call_on_dict_driver("extend_port_dict", session, base_model,