Set lock_path correctly.
[openstack-build/neutron-build.git] / neutron / plugins / ml2 / managers.py
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
16 from oslo_config import cfg
17 from oslo_log import log
18 from oslo_utils import excutils
19 import six
20 import stevedore
21
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
35
36 LOG = log.getLogger(__name__)
37
38 MAX_BINDING_LEVELS = 10
39
40
41 class TypeManager(stevedore.named.NamedExtensionManager):
42     """Manage network segment types using drivers."""
43
44     def __init__(self):
45         # Mapping from type name to DriverManager
46         self.drivers = {}
47
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,
52                                           invoke_on_load=True)
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)
57
58     def _register_types(self):
59         for ext in 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})
68             else:
69                 self.drivers[network_type] = ext
70         LOG.info(_LI("Registered types: %s"), self.drivers.keys())
71
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)
77             else:
78                 LOG.error(_LE("No type driver for tenant network_type: %s. "
79                               "Service terminated!"), network_type)
80                 raise SystemExit(1)
81         LOG.info(_LI("Tenant network_types: %s"), self.tenant_network_types)
82
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)
87             raise SystemExit(1)
88
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)
93
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)
99             return segment
100
101         msg = _("network_type required")
102         raise exc.InvalidInput(error_message=msg)
103
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
108             # at the same time.
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)
117             return segments
118
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)
122
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.
130         return network
131
132     def network_matches_filters(self, network, filters):
133         if not filters:
134             return True
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)
140         else:
141             return True
142         return any(self._match_segment(s, filters) for s in segments)
143
144     def _get_attribute(self, attrs, key):
145         value = attrs.get(key)
146         if value is attributes.ATTR_NOT_SPECIFIED:
147             value = None
148         return value
149
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])
154
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)
161
162     def _extend_network_dict_provider(self, network, segments):
163         if not segments:
164             LOG.error(_LE("Network %s has no segments"), network['id'])
165             for attr in provider.ATTRIBUTES:
166                 network[attr] = None
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]
173         else:
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]
178
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()
183
184     def _add_network_segment(self, session, network_id, segment, mtu,
185                              segment_index=0):
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])
189
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
194         mtu = []
195         with session.begin(subtransactions=True):
196             network_id = network['id']
197             if segments:
198                 for segment_index, segment in enumerate(segments):
199                     segment = self.reserve_provider_segment(
200                         session, segment)
201                     self._add_network_segment(session, network_id, segment,
202                                               mtu, segment_index)
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)
207             else:
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
211
212     def is_partial_segment(self, segment):
213         network_type = segment[api.NETWORK_TYPE]
214         driver = self.drivers.get(network_type)
215         if driver:
216             return driver.obj.is_partial_segment(segment)
217         else:
218             msg = _("network_type value '%s' not supported") % network_type
219             raise exc.InvalidInput(error_message=msg)
220
221     def validate_provider_segment(self, segment):
222         network_type = segment[api.NETWORK_TYPE]
223         driver = self.drivers.get(network_type)
224         if driver:
225             driver.obj.validate_provider_segment(segment)
226         else:
227             msg = _("network_type value '%s' not supported") % network_type
228             raise exc.InvalidInput(error_message=msg)
229
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)
234
235     def _allocate_segment(self, session, network_type):
236         driver = self.drivers.get(network_type)
237         return driver.obj.allocate_tenant_segment(session)
238
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)
242             if segment:
243                 return segment
244         raise exc.NoNetworkAvailable()
245
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)
249         if segment:
250             return segment
251         raise exc.NoNetworkAvailable()
252
253     def release_network_segments(self, session, network_id):
254         segments = db.get_network_segments(session, network_id,
255                                            filter_dynamic=None)
256
257         for segment in segments:
258             network_type = segment.get(api.NETWORK_TYPE)
259             driver = self.drivers.get(network_type)
260             if driver:
261                 driver.obj.release_segment(session, segment)
262             else:
263                 LOG.error(_LE("Failed to release segment '%s' because "
264                               "network type is not supported."), segment)
265
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))
271
272         if dynamic_segment:
273             return dynamic_segment
274
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,
278                                is_dynamic=True)
279         return dynamic_segment
280
281     def release_dynamic_segment(self, session, segment_id):
282         """Delete a dynamic segment."""
283         segment = db.get_segment_by_id(session, segment_id)
284         if segment:
285             driver = self.drivers.get(segment.get(api.NETWORK_TYPE))
286             if driver:
287                 driver.obj.release_segment(session, segment)
288                 db.delete_network_segment(session, segment_id)
289             else:
290                 LOG.error(_LE("Failed to release segment '%s' because "
291                               "network type is not supported."), segment)
292         else:
293             LOG.debug("No segment found with id %(segment_id)s", segment_id)
294
295
296 class MechanismManager(stevedore.named.NamedExtensionManager):
297     """Manage networking mechanisms using drivers."""
298
299     def __init__(self):
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 = []
305
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,
310                                                invoke_on_load=True,
311                                                name_order=True)
312         LOG.info(_LI("Loaded mechanism driver names: %s"), self.names())
313         self._register_mechanisms()
314
315     def _register_mechanisms(self):
316         """Register all mechanism drivers.
317
318         This method should only be called once in the MechanismManager
319         constructor.
320         """
321         for ext in self:
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])
326
327     @property
328     def supported_qos_rule_types(self):
329         if not self.ordered_mech_drivers:
330             return []
331
332         rule_types = set(qos_consts.VALID_RULE_TYPES)
333         binding_driver_found = False
334
335         # Recalculate on every call to allow drivers determine supported rule
336         # types dynamically
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'):
342                     new_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:
346                         LOG.info(
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
352                 else:
353                     # at least one of drivers does not support QoS, meaning
354                     # there are no rule types supported by all of them
355                     LOG.warn(
356                         _LW("%s does not support QoS; "
357                             "no rule types available"),
358                         driver.name)
359                     return []
360
361         if binding_driver_found:
362             rule_types = list(rule_types)
363         else:
364             rule_types = []
365         LOG.debug("Supported QoS rule types "
366                   "(common subset for all mech drivers): %s", rule_types)
367         return rule_types
368
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()
373
374     def _check_vlan_transparency(self, context):
375         """Helper method for checking vlan transparecncy support.
376
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.
381         """
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()
386
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.
390
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.
397         """
398         error = False
399         for driver in self.ordered_mech_drivers:
400             try:
401                 getattr(driver.obj, method_name)(context)
402             except Exception:
403                 LOG.exception(
404                     _LE("Mechanism driver '%(name)s' failed in %(method)s"),
405                     {'name': driver.name, 'method': method_name}
406                 )
407                 error = True
408                 if not continue_on_failure:
409                     break
410         if error:
411             raise ml2_exc.MechanismDriverError(
412                 method=method_name
413             )
414
415     def create_network_precommit(self, context):
416         """Notify all mechanism drivers during network creation.
417
418         :raises: neutron.plugins.ml2.common.MechanismDriverError
419         if any mechanism driver create_network_precommit call fails.
420
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.
425         """
426         self._check_vlan_transparency(context)
427         self._call_on_drivers("create_network_precommit", context)
428
429     def create_network_postcommit(self, context):
430         """Notify all mechanism drivers after network creation.
431
432         :raises: neutron.plugins.ml2.common.MechanismDriverError
433         if any mechanism driver create_network_postcommit call fails.
434
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.
440         """
441         self._call_on_drivers("create_network_postcommit", context)
442
443     def update_network_precommit(self, context):
444         """Notify all mechanism drivers during network update.
445
446         :raises: neutron.plugins.ml2.common.MechanismDriverError
447         if any mechanism driver update_network_precommit call fails.
448
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.
453         """
454         self._call_on_drivers("update_network_precommit", context)
455
456     def update_network_postcommit(self, context):
457         """Notify all mechanism drivers after network update.
458
459         :raises: neutron.plugins.ml2.common.MechanismDriverError
460         if any mechanism driver update_network_postcommit call fails.
461
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.
466         """
467         self._call_on_drivers("update_network_postcommit", context,
468                               continue_on_failure=True)
469
470     def delete_network_precommit(self, context):
471         """Notify all mechanism drivers during network deletion.
472
473         :raises: neutron.plugins.ml2.common.MechanismDriverError
474         if any mechanism driver delete_network_precommit call fails.
475
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.
480         """
481         self._call_on_drivers("delete_network_precommit", context)
482
483     def delete_network_postcommit(self, context):
484         """Notify all mechanism drivers after network deletion.
485
486         :raises: neutron.plugins.ml2.common.MechanismDriverError
487         if any mechanism driver delete_network_postcommit call fails.
488
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
496         network.
497         """
498         self._call_on_drivers("delete_network_postcommit", context,
499                               continue_on_failure=True)
500
501     def create_subnet_precommit(self, context):
502         """Notify all mechanism drivers during subnet creation.
503
504         :raises: neutron.plugins.ml2.common.MechanismDriverError
505         if any mechanism driver create_subnet_precommit call fails.
506
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.
511         """
512         self._call_on_drivers("create_subnet_precommit", context)
513
514     def create_subnet_postcommit(self, context):
515         """Notify all mechanism drivers after subnet creation.
516
517         :raises: neutron.plugins.ml2.common.MechanismDriverError
518         if any mechanism driver create_subnet_postcommit call fails.
519
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.
525         """
526         self._call_on_drivers("create_subnet_postcommit", context)
527
528     def update_subnet_precommit(self, context):
529         """Notify all mechanism drivers during subnet update.
530
531         :raises: neutron.plugins.ml2.common.MechanismDriverError
532         if any mechanism driver update_subnet_precommit call fails.
533
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.
538         """
539         self._call_on_drivers("update_subnet_precommit", context)
540
541     def update_subnet_postcommit(self, context):
542         """Notify all mechanism drivers after subnet update.
543
544         :raises: neutron.plugins.ml2.common.MechanismDriverError
545         if any mechanism driver update_subnet_postcommit call fails.
546
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.
551         """
552         self._call_on_drivers("update_subnet_postcommit", context,
553                               continue_on_failure=True)
554
555     def delete_subnet_precommit(self, context):
556         """Notify all mechanism drivers during subnet deletion.
557
558         :raises: neutron.plugins.ml2.common.MechanismDriverError
559         if any mechanism driver delete_subnet_precommit call fails.
560
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.
565         """
566         self._call_on_drivers("delete_subnet_precommit", context)
567
568     def delete_subnet_postcommit(self, context):
569         """Notify all mechanism drivers after subnet deletion.
570
571         :raises: neutron.plugins.ml2.common.MechanismDriverError
572         if any mechanism driver delete_subnet_postcommit call fails.
573
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
581         subnet.
582         """
583         self._call_on_drivers("delete_subnet_postcommit", context,
584                               continue_on_failure=True)
585
586     def create_port_precommit(self, context):
587         """Notify all mechanism drivers during port creation.
588
589         :raises: neutron.plugins.ml2.common.MechanismDriverError
590         if any mechanism driver create_port_precommit call fails.
591
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.
596         """
597         self._call_on_drivers("create_port_precommit", context)
598
599     def create_port_postcommit(self, context):
600         """Notify all mechanism drivers of port creation.
601
602         :raises: neutron.plugins.ml2.common.MechanismDriverError
603         if any mechanism driver create_port_postcommit call fails.
604
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
609         called in this case.
610         """
611         self._call_on_drivers("create_port_postcommit", context)
612
613     def update_port_precommit(self, context):
614         """Notify all mechanism drivers during port update.
615
616         :raises: neutron.plugins.ml2.common.MechanismDriverError
617         if any mechanism driver update_port_precommit call fails.
618
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.
623         """
624         self._call_on_drivers("update_port_precommit", context)
625
626     def update_port_postcommit(self, context):
627         """Notify all mechanism drivers after port update.
628
629         :raises: neutron.plugins.ml2.common.MechanismDriverError
630         if any mechanism driver update_port_postcommit call fails.
631
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.
636         """
637         self._call_on_drivers("update_port_postcommit", context,
638                               continue_on_failure=True)
639
640     def delete_port_precommit(self, context):
641         """Notify all mechanism drivers during port deletion.
642
643         :raises: neutron.plugins.ml2.common.MechanismDriverError
644         if any mechanism driver delete_port_precommit call fails.
645
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.
650         """
651         self._call_on_drivers("delete_port_precommit", context)
652
653     def delete_port_postcommit(self, context):
654         """Notify all mechanism drivers after port deletion.
655
656         :raises: neutron.plugins.ml2.common.MechanismDriverError
657         if any mechanism driver delete_port_postcommit call fails.
658
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
666         port.
667         """
668         self._call_on_drivers("delete_port_postcommit", context,
669                               continue_on_failure=True)
670
671     def bind_port(self, context):
672         """Attempt to bind a port using registered mechanism drivers.
673
674         :param context: PortContext instance describing the port
675
676         Called outside any transaction to attempt to establish a port
677         binding.
678         """
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})
693
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",
699                   {'port': port_id,
700                    'host': context.host,
701                    'level': level,
702                    'segments': segments_to_bind})
703
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})
709             return False
710
711         for driver in self.ordered_mech_drivers:
712             if not self._check_driver_to_bind(driver, segments_to_bind,
713                                               context._binding_levels):
714                 continue
715             try:
716                 context._prepare_to_bind(segments_to_bind)
717                 driver.obj.bind_port(context)
718                 segment = context._new_bound_segment
719                 if segment:
720                     context._push_binding_level(
721                         models.PortBindingLevel(port_id=port_id,
722                                                 host=context.host,
723                                                 level=level,
724                                                 driver=driver.name,
725                                                 segment_id=segment))
726                     next_segments = context._next_segments_to_bind
727                     if next_segments:
728                         # Continue binding another level.
729                         if self._bind_port_level(context, level + 1,
730                                                  next_segments):
731                             return True
732                         else:
733                             context._pop_binding_level()
734                     else:
735                         # Binding complete.
736                         LOG.debug("Bound port: %(port)s, "
737                                   "host: %(host)s, "
738                                   "vif_type: %(vif_type)s, "
739                                   "vif_details: %(vif_details)s, "
740                                   "binding_levels: %(binding_levels)s",
741                                   {'port': port_id,
742                                    'host': context.host,
743                                    'vif_type': binding.vif_type,
744                                    'vif_details': binding.vif_details,
745                                    'binding_levels': context.binding_levels})
746                         return True
747             except Exception:
748                 LOG.exception(_LE("Mechanism driver %s failed in "
749                                   "bind_port"),
750                               driver.name)
751         LOG.error(_LE("Failed to bind port %(port)s on host %(host)s"),
752                   {'port': context.current['id'],
753                    'host': binding.host})
754
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):
764                 return False
765         return True
766
767     def get_workers(self):
768         workers = []
769         for driver in self.ordered_mech_drivers:
770             workers += driver.obj.get_workers()
771         return workers
772
773
774 class ExtensionManager(stevedore.named.NamedExtensionManager):
775     """Manage extension drivers using drivers."""
776
777     def __init__(self):
778         # Ordered list of extension drivers, defining
779         # the order in which the drivers are called.
780         self.ordered_ext_drivers = []
781
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,
786                                                invoke_on_load=True,
787                                                name_order=True)
788         LOG.info(_LI("Loaded extension driver names: %s"), self.names())
789         self._register_drivers()
790
791     def _register_drivers(self):
792         """Register all extension drivers.
793
794         This method should only be called once in the ExtensionManager
795         constructor.
796         """
797         for ext in self:
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])
801
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()
807
808     def extension_aliases(self):
809         exts = []
810         for driver in self.ordered_ext_drivers:
811             alias = driver.obj.extension_alias
812             if alias:
813                 exts.append(alias)
814                 LOG.info(_LI("Got %(alias)s extension from driver '%(drv)s'"),
815                          {'alias': alias, 'drv': driver.name})
816         return exts
817
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:
821             try:
822                 getattr(driver.obj, method_name)(plugin_context, data, result)
823             except Exception:
824                 with excutils.save_and_reraise_exception():
825                     LOG.info(_LI("Extension driver '%(name)s' failed in "
826                              "%(method)s"),
827                              {'name': driver.name, 'method': method_name})
828
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,
832                                   data, result)
833
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,
837                                   data, result)
838
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,
842                                   data, result)
843
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,
847                                   data, result)
848
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,
852                                   data, result)
853
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,
857                                   data, result)
858
859     def _call_on_dict_driver(self, method_name, session, base_model, result):
860         for driver in self.ordered_ext_drivers:
861             try:
862                 getattr(driver.obj, method_name)(session, base_model, result)
863             except Exception:
864                 LOG.error(_LE("Extension driver '%(name)s' failed in "
865                           "%(method)s"),
866                           {'name': driver.name, 'method': method_name})
867                 raise ml2_exc.ExtensionDriverError(driver=driver.name)
868
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,
872                                   result)
873
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,
877                                   result)
878
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,
882                                   result)