def register_model(cls): '''Registers model into the lookup so it can be found later by name or by table name. ''' if not cls.is_first_class(): raise RuntimeError( _('Non first-class {0} model cannot be registered').format( cls.__name__, ), ) if cls.__name__ in _lookup_by_class_name: raise RuntimeError( _('Cannot register class named {0}, ' 'another class with same name exists').format(cls.__name__), ) if cls.table_name in _lookup_by_table_name: raise RuntimeError( _('Cannot register {0} to table {1}, ' 'already occupied by {2}').format( cls.__name__, cls.table_name, _lookup_by_table_name[cls.table_name].__name__, ), ) _registered_models.add(cls) _lookup_by_class_name[cls.__name__] = cls _lookup_by_table_name[cls.table_name] = cls for model in cls.iterate_embedded_model_types(): _registered_models.add(model) _lookup_by_class_name[model.__name__] = model return cls
def _get_data_from_binding_profile(self, context, port): if (df_const.DF_PORT_BINDING_PROFILE not in port or not attr.is_attr_set( port[df_const.DF_PORT_BINDING_PROFILE])): return None, None parent_name = ( port[df_const.DF_PORT_BINDING_PROFILE].get('parent_name')) tag = port[df_const.DF_PORT_BINDING_PROFILE].get('tag') if not any((parent_name, tag)): # An empty profile is fine. return None, None if not all((parent_name, tag)): # If one is set, they both must be set. msg = _('Invalid binding:profile. parent_name and tag are ' 'both required.') raise n_exc.InvalidInput(error_message=msg) if not isinstance(parent_name, six.string_types): msg = _('Invalid binding:profile. parent_name "%s" must be ' 'a string.') % parent_name raise n_exc.InvalidInput(error_message=msg) try: tag = int(tag) if tag < 0 or tag > 4095: raise ValueError except ValueError: msg = _('Invalid binding:profile. tag "%s" must be ' 'an int between 1 and 4096, inclusive.') % tag raise n_exc.InvalidInput(error_message=msg) # Make sure we can successfully look up the port indicated by # parent_name. Just let it raise the right exception if there is a # problem. self.get_port(context, parent_name) return parent_name, tag
def validate(self, value): super(DhcpOptsDictField, self).validate(value) if not value: return for key, inner_val in value.items(): if not dhcp.is_tag_valid(key): raise errors.ValidationError( _('Key {} is not a vaild dhcp opt').format(key)) if not isinstance(inner_val, six.string_types): raise errors.ValidationError( _('Value {value} to key {key} is not a string').format( key=key, value=inner_val))
def validate(self, value): if self.required and not value: raise errors.ValidationError(_('Field is required!')) if value is None: return for elem in value: if elem not in self._valid_values: raise errors.ValidationError( _('{value} is not one of: [{valid_values}]').format( value=value, valid_values=', '.join(self._valid_values)))
def create_flow_classifier_precommit(self, context): flow_classifier = context.current source_port = flow_classifier.get('logical_source_port') dest_port = flow_classifier.get('logical_destination_port') if source_port is None and dest_port is None: raise fc_exc.FlowClassifierBadRequest(message=_( 'Either logical_source_port or logical_destination_port ' 'have to be specified'), ) if source_port is not None and dest_port is not None: raise fc_exc.FlowClassifierBadRequest(message=_( 'Both logical_source_port and logical_destination_port ' 'cannot be specified'), )
class DragonflowException(Exception): """Base Dragonflow Exception. To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. """ message = _("An unknown exception occurred.") def __init__(self, **kwargs): try: super(DragonflowException, self).__init__(self.message % kwargs) self.msg = self.message % kwargs except Exception: with excutils.save_and_reraise_exception() as ctxt: if not self.use_fatal_exceptions(): ctxt.reraise = False # at least get the core message out if something happened super(DragonflowException, self).__init__(self.message) if six.PY2: def __unicode__(self): return unicode(self.msg) def __str__(self): return self.msg def use_fatal_exceptions(self): return False
def proxy_request(self, req): headers = self.get_headers(req) url = urlparse.urlunsplit( (self.get_scheme(req), self.get_host(req), self.get_path_info(req), self.get_query_string(req), '')) h = self.create_http_client(req) resp, content = h.request(url, method=req.method, headers=headers, body=req.body) if resp.status == 200: LOG.debug(str(resp)) return self.create_response(req, resp, content) elif resp.status == 403: LOG.warning( _LW('The remote metadata server responded with Forbidden. This ' 'response usually occurs when shared secrets do not match.' )) return webob.exc.HTTPForbidden() elif resp.status == 400: return webob.exc.HTTPBadRequest() elif resp.status == 404: return webob.exc.HTTPNotFound() elif resp.status == 409: return webob.exc.HTTPConflict() elif resp.status == 500: msg = _LW( 'Remote metadata server experienced an internal server error.') LOG.warning(msg) explanation = six.text_type(msg) return webob.exc.HTTPInternalServerError(explanation=explanation) else: raise Exception(_('Unexpected response code: %s') % resp.status)
def _get_flow_classifier_table(self, flow_classifier): if flow_classifier.source_port is not None: return constants.L2_LOOKUP_TABLE elif flow_classifier.dest_port is not None: return constants.EGRESS_TABLE else: raise ValueError( _('Neither source not destination port specified'))
def validate(self, value): super(IpProto, self).validate(value) if value is None: return if value < 0 or value > 255: raise errors.ValidationError( _('IP protocol value must to be in the' ' range [0,255] ({val} supplied )').format(val=value))
def _get_port_chain_driver(self, port_chain): proto = port_chain.protocol if proto == sfc.PROTO_MPLS: return self.mpls_driver else: raise RuntimeError( _('Unsupported portchain proto {0}').format(proto), )
def __init__(self, field, *args, **kwargs): super(ListOfField, self).__init__(items_types=field.types, *args, **kwargs) if not isinstance(field, fields.BaseField): raise TypeError( _('field must be an instance of BaseField. Got: %s') % (type(field))) self.field = field
def register_cookie_bits(name, length, is_local=False, app_name=None): """Register this many cookie bits for the given 'task'. There are two types of cookies: global and local. Global cookies are global accross all applications. All applications share the information, and the cookie bits can only be assigned once. Local cookies are local to a specific application. That application is responsible to the data encoded in the cookie. Therefore, local cookie bits can be reused between applications, i.e. different applications can use the same local cookie bits to write different things. This function raises an error if there are not enough bits to allocate. :param name: The name of the 'task' :type name: string :param length: The length of the cookie to allocate :type length: int :param is_local: The cookie space is local, as defined above. :type is_local: bool :param app_name: Owner application of the cookie (None for global) :type app_name: string """ if not is_local: app_name = GLOBAL_APP_NAME shift = 0 max_bits = _cookie_max_bits_global else: shift = _cookie_max_bits_global max_bits = _cookie_max_bits_local if not app_name: raise TypeError(_("app_name must be provided if is_local is True")) if (app_name, name) in _cookies: LOG.info("Cookie for %(app_name)s/%(name)s already registered.", { "app_name": app_name, "name": name }) return start = _cookies_used_bits[app_name] if start + length > max_bits: LOG.error( "Out of cookie space: " "offset: %(offset)d length: %(length)d", { "offset": start, "length": length }) raise exceptions.OutOfCookieSpaceException() _cookies_used_bits[app_name] = start + length start += shift mask = (_cookie_mask_all >> (_cookie_max_bits - length)) << start _cookies[(app_name, name)] = CookieBitPair(start, mask) LOG.info( "Registered cookie for %(app_name)s/%(name)s, " "mask: %(mask)x, offset: %(offset)d, length: %(length)d", { "app_name": app_name, "name": name, "mask": mask, "offset": start, "length": length })
def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None, field_labels=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') :param field_labels: Labels to use in the heading of the table, default to fields. """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] field_labels = field_labels or fields if len(field_labels) != len(fields): raise ValueError( _("Field labels list %(labels)s has different number " "of elements than fields list %(fields)s"), { 'labels': field_labels, 'fields': fields }) if sortby_index is None: kwargs = {} else: kwargs = {'sortby': field_labels[sortby_index]} pt = prettytable.PrettyTable(field_labels) pt.align = 'l' for o in objs: row = [] for field in fields: data = '-' if field in formatters: data = formatters[field](o) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = o.get(field_name, '') if data is None: data = '-' row.append(data) pt.add_row(row) if six.PY3: print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) else: print(encodeutils.safe_encode(pt.get_string(**kwargs)))
def load(self, *args, **kwargs): for app in self.apps_list: app_class_name = self.apps_location_prefix + "." + app try: app_class = importutils.import_class(app_class_name) app = app_class(*args, **kwargs) self.apps.append(app) except ImportError as e: LOG.exception(_LE("Error loading application by class, %s"), e) raise ImportError(_("Application class not found."))
def __call__(self, req): try: LOG.debug("Request: %s", req) return self.proxy_request(req) except Exception: LOG.exception(_LE("Unexpected error.")) msg = _('An unknown error has occurred. ' 'Please try your request again.') explanation = six.text_type(msg) return webob.exc.HTTPInternalServerError(explanation=explanation)
def _get_app_class(self, app_type): """Get an application class (Python class) by app name""" mgr = stevedore.NamedExtensionManager( 'dragonflow.controller.apps', [app_type], invoke_on_load=False, ) for ext in mgr: return ext.plugin else: raise RuntimeError(_('Failed to load app {0}').format(app_type))
def __init__(self, **kwargs): for key in kwargs: if key not in self._field_names: raise TypeError( _('{field} is not a field of {model}').format( field=key, model=type(self).__name__, )) super(_CommonBase, self).__init__(**kwargs) self._set_fields = set(kwargs.keys()).intersection(self._field_names) self._is_object_stale = False
def switch_features_handler(self, ev): self.external_ofport = self.vswitch_api.get_port_ofport( self.ex_peer_patch_port) # install static strategy flows if self.external_host_ip is None: raise Exception( _('Please set external_host_ip conf. parameter ' 'to enable SNAT application')) else: self.install_strategy_based_flows()
def switch_features_handler(self, ev): self._setup_patch_ports() self.external_bridge_mac = self.vswitch_api.get_port_mac_in_use( self.external_network_bridge) or const.EMPTY_MAC # install static strategy flows if self.external_host_ip is None: raise Exception(_('Please set external_host_ip conf. parameter ' 'to enable SNAT application')) else: self.install_strategy_based_flows()
def _create_matches(self, flow_classifier): params = {} if flow_classifier.source_port is not None: lport = flow_classifier.source_port params['reg6'] = lport.unique_key params['reg3'] = (_SRC_NOT_DONE, _SRC_MASK) if flow_classifier.dest_port is not None: lport = flow_classifier.dest_port params['reg7'] = lport.unique_key params['reg3'] = (_DST_NOT_DONE, _DST_MASK) if flow_classifier.ether_type is not None: if flow_classifier.ether_type == lib_constants.IPv4: params.update(_create_ipv4_params(flow_classifier)) elif flow_classifier.ether_type == lib_constants.IPv6: params.update(_create_ipv6_params(flow_classifier)) else: raise RuntimeError( _('Unsupported ethertype {0}').format( flow_classifier.ether_type)) param_list = [params] if flow_classifier.protocol is not None: if flow_classifier.protocol == lib_constants.PROTO_NAME_TCP: l4_params = _create_tcp_params(flow_classifier) elif flow_classifier.protocol == lib_constants.PROTO_NAME_UDP: l4_params = _create_udp_params(flow_classifier) else: raise RuntimeError( _('Unsupported protocol {0}').format( flow_classifier.protocol)) param_list = _multiply_params(param_list, l4_params) return (self.parser.OFPMatch(**p) for p in param_list)
def get_one(self, obj, index): if index not in (None, self._id_index): keys = self.get_keys(obj, index) obj_id = _take_one(keys) if obj_id is not None and _take_one(keys) is not None: raise ValueError(_('More than one result available')) else: obj_id = obj.id try: return self._get_by_id(obj_id) except KeyError: return None
def _create_dp_alloc(self, specification): """ Allocate the tables and registers for the given application (given by its specification) """ public_mapping = specification.public_mapping.copy() unmapped_vars = self._public_variables.difference(public_mapping) # Convert to set() so the result won't be a frozenset() unmapped_regs = set(REGS).difference( public_mapping.values(), ).difference( specification.private_mapping.values(), ) while unmapped_vars and unmapped_regs: public_mapping[unmapped_vars.pop()] = unmapped_regs.pop() if unmapped_vars: raise RuntimeError( _("Can't allocate enough registers for variables"), ) states_dict = { state: next(self._table_generator) for state in specification.states } states = app_base.AttributeDict(**states_dict) exitpoints_dict = { exit.name: next(self._table_generator) for exit in specification.exitpoints } exitpoints = app_base.AttributeDict(**exitpoints_dict) entrypoints_dict = { entry.name: states[entry.target] for entry in specification.entrypoints } entrypoints = app_base.AttributeDict(**entrypoints_dict) return app_base.DpAlloc( states=states, exitpoints=exitpoints, entrypoints=entrypoints, full_mapping=public_mapping, )
def load_driver(driver_cfg, namespace): try: # Try to resolve by alias mgr = driver.DriverManager(namespace, driver_cfg) class_to_load = mgr.driver except RuntimeError: e1_info = sys.exc_info() # try with name try: class_to_load = importutils.import_class(driver_cfg) except (ImportError, ValueError): LOG.error(_LE("Error loading class %(class)s by alias e: %(e)s") % {'class': driver_cfg, 'e': e1_info}, exc_info=e1_info) LOG.error(_LE("Error loading class by class name"), exc_info=True) raise ImportError(_("Class not found.")) return class_to_load()
def load_driver(driver_cfg, namespace, *args, **kwargs): try: # Try to resolve by alias mgr = driver.DriverManager(namespace, driver_cfg) class_to_load = mgr.driver except RuntimeError: e1_info = sys.exc_info() # try with name try: class_to_load = importutils.import_class(driver_cfg) except (ImportError, ValueError): LOG.error("Error loading class %(class)s by alias e: %(e)s", {'class': driver_cfg, 'e': e1_info}, exc_info=e1_info) LOG.error("Error loading class by class name", exc_info=True) raise ImportError(_("Class not found.")) return class_to_load(*args, **kwargs)
def register_event(model, event): '''The decorator marks the method to be registered to the specified event :param model: Model holding the event :type model: Class :param event: Event name that method well be registerd to :type event: String ''' if event not in model.get_events(): raise RuntimeError( _('{0} is not an event of {1}').format(event, model)) def decorator(func): if not hasattr(func, '_register_events'): func._register_events = [] func._register_events.append((model, event)) return func return decorator
def _create_ref(proxy_type, value, lazy): """Create a proxy object based on: * ID. * Another proxy instance. * Actual object of the proxied type. In case where object is passed (rather than ID), the ID is extracted from the relevant field. """ if isinstance(value, six.string_types): obj_id = value elif isinstance(value, (proxy_type, proxy_type.get_proxied_model())): obj_id = value.id else: raise ValueError( _('Reference field should only be initialized by ID or ' 'model instance/reference')) return proxy_type(id=obj_id, lazy=lazy)
class Checks(upgradecheck.UpgradeCommands): """Various upgrade checks should be added as separate methods in this class and added to _upgrade_checks tuple. """ def _check_placeholder(self): # This is just a placeholder for upgrade checks, it should be # removed when the actual checks are added return upgradecheck.Result(upgradecheck.Code.SUCCESS) # The format of the check functions is to return an # oslo_upgradecheck.upgradecheck.Result # object with the appropriate # oslo_upgradecheck.upgradecheck.Code and details set. # If the check hits warnings or failures then those should be stored # in the returned Result's "details" attribute. The # summary will be rolled up at the end of the check() method. _upgrade_checks = ( # In the future there should be some real checks added here (_('Placeholder'), _check_placeholder), )
def _pre_delete_port(self, port, port_check): """Do some preliminary operations before deleting the port.""" LOG.debug("Deleting port %s", port['id']) if not port_check: return if port['device_owner'] in ['network:router_interface', 'network:router_gateway', 'network:floatingip']: fixed_ips = port['fixed_ips'] if fixed_ips: reason = _('has device owner %s') % port['device_owner'] raise n_exc.ServicePortInUse(port_id=port['id'], reason=reason) else: LOG.debug("Port %(port_id)s has owner %(port_owner)s, but " "no IP address, so it can be deleted", {'port_id': port['id'], 'port_owner': port['device_owner']})
def __init__(self, *args, **kwargs): super(ChassisSNATApp, self).__init__(*args, **kwargs) LOG.info("Loading SNAT application ... ") self.external_network_bridge = ( cfg.CONF.df_snat_app.external_network_bridge) self.chassis = None # new application configuration self.external_host_ip = cfg.CONF.df.external_host_ip self.enable_goto_flows = cfg.CONF.df_snat_app.enable_goto_flows # create mac address based on given 'external_host_ip' if self.external_host_ip is not None: split_ip = self.external_host_ip.split('.') ip2mac = '{:02x}:{:02x}:{:02x}:{:02x}'.format(*map(int, split_ip)) self.external_host_mac = const.CHASSIS_MAC_PREFIX + ip2mac else: raise Exception( _('Please set external_host_ip conf. parameter ' 'to enable SNAT application'))
def get_one(self, obj, index): if index not in (None, self._id_index): #有index且index有效时,先获取keys keys = self.get_keys(obj, index) #取第一个obj对应的obj_id obj_id = _take_one(keys) if obj_id is not None and _take_one(keys) is not None: #此种情况下不容许出现多个obj raise ValueError(_('More than one result available')) else: #直接自obj中取id号 obj_id = obj.id try: #通过id号比对对象 return self._get_by_id(obj_id) except KeyError: #如果不存在,则返回None return None
def iter_models_by_dependency_order(first_class_only=True): '''Iterate over all registered models The models are returned in an order s.t. a model never preceeds its dependencies. ''' unsorted_models = {} # Gather all models and their dependencies for model in iter_models(first_class_only=first_class_only): dependencies = model.dependencies(first_class_only) if first_class_only: dependencies = { dep for dep in dependencies if dep.is_first_class() } unsorted_models[model] = dependencies # Perform a topological sort sorted_models = [] while unsorted_models: # Split models to those that still depend on something and those # that no more depend on models in unsorted_models dependent_models = set(k for k, v in unsorted_models.items() if v) independent_models = set(unsorted_models.keys()) - dependent_models # If we still have unsorted models yet nothing is independent, we have # dependency cycle if not independent_models: raise RuntimeError(_('Models form a dependency cycle')) # Move independent models to sorted list for model in independent_models: sorted_models.append(model) del unsorted_models[model] # Remove independent models from remaining dependency lists for model in dependent_models: unsorted_models[model] -= independent_models return sorted_models
from neutron.common import constants as common_const from ryu.lib.mac import haddr_to_bin from dragonflow._i18n import _ from dragonflow.controller.common.arp_responder import ArpResponder from dragonflow.controller.common import constants as const from dragonflow.controller.df_base_app import DFlowApp from oslo_config import cfg DF_L2_APP_OPTS = [ cfg.BoolOpt( 'l2_responder', default=True, help=_('Install OVS flows to respond to ARP requests.')) ] # TODO(gsagie) currently the number set in Ryu for this # (OFPP_IN_PORT) is not working, use this until resolved # NOTE(yamamoto): Many of Nicira extensions, including # NXAST_RESUBMIT_TABLE, take 16-bit (OpenFlow 1.0 style) port number, # regardless of the OpenFlow version being used. OF_IN_PORT = 0xfff8 class L2App(DFlowApp): def __init__(self, *args, **kwargs): super(L2App, self).__init__(*args, **kwargs) self.local_networks = {}
from ryu.lib import addrconv from ryu.lib.packet import dhcp from ryu.lib.packet import ethernet from ryu.lib.packet import ipv4 from ryu.lib.packet import packet as ryu_packet from ryu.lib.packet import udp from ryu.ofproto import ether from dragonflow._i18n import _, _LI, _LE, _LW from dragonflow.controller.common import constants as const from dragonflow.controller.df_base_app import DFlowApp DF_DHCP_OPTS = [ cfg.ListOpt('df_dns_servers', default=['8.8.8.8', '8.8.4.4'], help=_('Comma-separated list of the DNS servers which will be used.')), cfg.IntOpt('df_default_network_device_mtu', default=1460, help=_('default MTU setting for interface.')), ] LOG = log.getLogger(__name__) DHCP_DOMAIN_NAME_OPT = 15 DHCP_INTERFACE_MTU_OPT = 26 DHCP_DISCOVER = 1 DHCP_OFFER = 2 DHCP_REQUEST = 3 DHCP_ACK = 5 class DHCPApp(DFlowApp):
from neutron.common import constants as n_const from ryu.lib.packet import arp from ryu.lib.packet import packet from ryu.ofproto import ether from oslo_config import cfg from oslo_log import log from oslo_service import loopingcall LOG = log.getLogger(__name__) DF_DNAT_APP_OPTS = [ cfg.StrOpt('external_network_bridge', default='br-ex', help=_("Name of bridge used for external network traffic")), cfg.StrOpt('int_peer_patch_port', default='patch-ex', help=_("Peer patch port in integration bridge for external " "bridge.")), cfg.StrOpt('ex_peer_patch_port', default='patch-int', help=_("Peer patch port in external bridge for integration " "bridge.")), cfg.IntOpt('send_arp_interval', default=5, help=_("Polling interval for arp request in seconds")) ] FIP_GW_RESOLVING_STATUS = 'resolving' class DNATApp(DFlowApp):
from ryu.lib.packet import dhcp from ryu.lib.packet import ethernet from ryu.lib.packet import ipv4 from ryu.lib.packet import packet as ryu_packet from ryu.lib.packet import udp from ryu.ofproto import ether from dragonflow.common import utils as df_utils from dragonflow._i18n import _, _LI, _LE, _LW from dragonflow.controller.common import constants as const from dragonflow.controller.df_base_app import DFlowApp DF_DHCP_OPTS = [ cfg.ListOpt('df_dns_servers', default=['8.8.8.8', '8.8.4.4'], help=_('Comma-separated list of the DNS servers which will be used.')), cfg.IntOpt('df_default_network_device_mtu', default=1460, help=_('default MTU setting for interface.')), cfg.IntOpt('df_dhcp_max_rate_per_sec', default=3, help=_('Port Max rate of DHCP messages per second')), cfg.IntOpt('df_dhcp_block_time_in_sec', default=100, help=_('Time to block port that passe the max rate')), ] LOG = log.getLogger(__name__) DHCP_DOMAIN_NAME_OPT = 15 DHCP_INTERFACE_MTU_OPT = 26 DHCP_DISCOVER = 1 DHCP_OFFER = 2 DHCP_REQUEST = 3
# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from dragonflow._i18n import _ df_opts = [ cfg.IPOpt('remote_db_ip', default='127.0.0.1', help=_('The remote db server ip address')), cfg.PortOpt('remote_db_port', default=4001, help=_('The remote db server port')), cfg.ListOpt('remote_db_hosts', default=['$remote_db_ip:$remote_db_port'], help=_('Remote DB cluster host:port pairs.')), cfg.StrOpt('nb_db_class', default='dragonflow.db.drivers.etcd_db_driver.EtcdDbDriver', help=_('The driver class for the NB DB driver')), cfg.IPOpt('local_ip', default='127.0.0.1', help=_('Local host VTEP IP')), cfg.StrOpt('tunnel_type', default='geneve', help=_('The encapsulation type for the tunnel')),
from dragonflow.common import common_params from dragonflow.common import constants as df_common_const from dragonflow.common import exceptions as df_exceptions from dragonflow.common import extensions from dragonflow.db import api_nb from dragonflow.db.neutron import lockedobjects_db as lock_db from dragonflow.neutron.common import constants as df_const LOG = log.getLogger(__name__) cfg.CONF.register_opts(common_params.df_opts, 'df') router_distributed_opts = [ cfg.BoolOpt('router_distributed', default=False, help=_("System-wide flag to determine the type of router " "that tenants can create. Only admin can override.")), ] cfg.CONF.register_opts(router_distributed_opts) class DFPlugin(db_base_plugin_v2.NeutronDbPluginV2, securitygroups_db.SecurityGroupDbMixin, l3_agentschedulers_db.L3AgentSchedulerDbMixin, l3_gwmode_db.L3_NAT_db_mixin, l3_attrs_db.ExtraAttributesMixin, external_net_db.External_net_db_mixin, portbindings_db.PortBindingMixin, extradhcpopt_db.ExtraDhcpOptMixin, extraroute_db.ExtraRoute_db_mixin, agentschedulers_db.DhcpAgentSchedulerDbMixin):