Esempio n. 1
0
def monkey_patch():
    # If CONF.monkey_patch is not True, this function do nothing.
    if not CONF.monkey_patch:
        return
    # Get list of modules and decorators
    for module_and_decorator in CONF.monkey_patch_modules:
        module, decorator_name = module_and_decorator.split(':')
        # import decorator function
        decorator = importutils.import_class(decorator_name)
        __import__(module)
        # Retrieve module information using pyclbr
        module_data = pyclbr.readmodule_ex(module)
        for key in module_data.keys():
            # set the decorator for the class methods
            if isinstance(module_data[key], pyclbr.Class):
                clz = importutils.import_class("%s.%s" % (module, key))
                for method, func in inspect.getmembers(clz, inspect.ismethod):
                    setattr(
                        clz, method,
                        decorator("%s.%s.%s" % (module, key, method), func))
            # set the decorator for the function
            if isinstance(module_data[key], pyclbr.Function):
                func = importutils.import_class("%s.%s" % (module, key))
                setattr(sys.modules[module], key,
                        decorator("%s.%s" % (module, key), func))
Esempio n. 2
0
 def __init__(self,
              host,
              binary,
              topic,
              manager,
              report_interval=None,
              periodic_enable=None,
              periodic_fuzzy_delay=None,
              periodic_interval_max=None,
              db_allowed=True,
              *args,
              **kwargs):
     super(Service, self).__init__()
     self.host = host
     self.binary = binary
     self.topic = topic
     self.manager_class_name = manager
     manager_class = importutils.import_class(self.manager_class_name)
     self.manager = manager_class(host=self.host, *args, **kwargs)
     self.rpcserver = None
     self.report_interval = report_interval
     self.periodic_enable = periodic_enable
     self.periodic_fuzzy_delay = periodic_fuzzy_delay
     self.periodic_interval_max = periodic_interval_max
     self.saved_args, self.saved_kwargs = args, kwargs
     self.backdoor_port = None
     """
     conductor.API需要进行进一步实现;
     """
     self.conductor_api = conductor.API(use_local=db_allowed)
     self.conductor_api.wait_until_ready(context.get_admin_context())
     """
Esempio n. 3
0
 def __init__(self, host, binary, topic, manager, report_interval=None,
              periodic_enable=None, periodic_fuzzy_delay=None,
              periodic_interval_max=None, db_allowed=True,
              *args, **kwargs):
     super(Service, self).__init__()
     self.host = host
     self.binary = binary
     self.topic = topic
     self.manager_class_name = manager
     manager_class = importutils.import_class(self.manager_class_name)
     self.manager = manager_class(host=self.host, *args, **kwargs)
     self.rpcserver = None
     self.report_interval = report_interval
     self.periodic_enable = periodic_enable
     self.periodic_fuzzy_delay = periodic_fuzzy_delay
     self.periodic_interval_max = periodic_interval_max
     self.saved_args, self.saved_kwargs = args, kwargs
     self.backdoor_port = None
     """
     conductor.API需要进行进一步实现;
     """
     self.conductor_api = conductor.API(use_local=db_allowed)
     self.conductor_api.wait_until_ready(context.get_admin_context())
     
     """
Esempio n. 4
0
    def load_extension(self, ext_factory):
        """
        Execute an extension factory.

        Loads an extension.  The 'ext_factory' is the name of a
        callable that will be imported and called with one
        argument--the extension manager.  The factory callable is
        expected to call the register() method at least once.
        """

        LOG.debug(_("Loading extension %s"), ext_factory)

        if isinstance(ext_factory, six.string_types):
            # Load the factory
            factory = importutils.import_class(ext_factory)
        else:
            factory = ext_factory

        # Call it
        LOG.debug(_("Calling extension factory %s"), ext_factory)
        factory(self)
Esempio n. 5
0
    def load_extension(self, ext_factory):
        """
        Execute an extension factory.

        Loads an extension.  The 'ext_factory' is the name of a
        callable that will be imported and called with one
        argument--the extension manager.  The factory callable is
        expected to call the register() method at least once.
        """

        LOG.debug(_("Loading extension %s"), ext_factory)

        if isinstance(ext_factory, six.string_types):
            # Load the factory
            factory = importutils.import_class(ext_factory)
        else:
            factory = ext_factory

        # Call it
        LOG.debug(_("Calling extension factory %s"), ext_factory)
        factory(self)
Esempio n. 6
0
def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None):
    """
    Registers all standard API extensions.
    注册所有标准的API扩展功能。
    @@@@注:在这里还只是实现API扩展功能的注册,还没有进行实质的功能扩展方法的调用;
    """
    """
    ======================================================================================
    ext_mgr = <nova.api.openstack.compute.extensions.ExtensionManager object at 0x33d9190>
    __path__ = ['/usr/lib/python2.7/site-packages/nova/api/openstack/compute/contrib']
    LOG = <nova.openstack.common.log.ContextAdapter object at 0x295d610>
    __package__ = nova.api.openstack.compute.contrib
    ======================================================================================
    """

    # Walk through all the modules in our directory...
    our_dir = path[0]
    """
    our_dir = /usr/lib/python2.7/site-packages/nova/api/openstack/compute/contrib
    """
    """
    1.获取contrib目录下所有的文件;
    2.过滤出以.py作为后缀的文件;
    3.针对每一个.py文件,获取其具有和其文件名相同的类名的类(每一个类都有),
      作为加载扩展API功能的入口;
    4.加载并调用所获取的每一个类,进行时间日期等参数的初始化;
      (这里还没有进行实质的功能扩展方法的调用;)
    """
    for dirpath, dirnames, filenames in os.walk(our_dir):
        """
        ======================================================================================
        filenames = 
        ['instance_actions.py', 'extended_hypervisors.pyo', 
         'extended_services_delete.pyo', 'os_networks.pyc', 
         'extended_floating_ips.pyc', 'migrations.pyo', 
         'server_external_events.pyc', 'floating_ips.pyc', 
         'user_data.py', 'os_networks.pyo', 'rescue.pyo', 
         'extended_virtual_interfaces_net.pyo', 'extended_floating_ips.pyo', 
         'baremetal_ext_status.pyo', 'server_start_stop.pyo', 
         'os_tenant_networks.pyc', 'scheduler_hints.py', 'image_size.py', 
         'rescue.py', 'block_device_mapping_v2_boot.pyo', 'quotas.pyc', 
         '__init__.pyo', 'extended_services.pyc', 'multinic.pyc', 'services.py', 
         'extended_services.py', 'extended_hypervisors.py', 'extended_quotas.pyo', 
         'preserve_ephemeral_rebuild.pyc', 'extended_status.pyo', 
         'baremetal_ext_status.py', 'security_groups.pyc', 
         'volume_attachment_update.py', 'floating_ip_pools.py', 
         'server_password.py', 'floating_ips_bulk.pyc', 'volumes.pyo', 
         'keypairs.pyo', 'flavormanage.pyc', 'extended_hypervisors.pyc', 
         'flavorextradata.pyc', 'extended_services.pyo', 'attach_interfaces.pyc', 
         'server_usage.pyo', 'networks_associate.pyo', 'multinic.pyo', 'evacuate.py', 
         'consoles.py', 'config_drive.pyo', 'hide_server_addresses.py', 
         'extended_availability_zone.py', 'security_group_default_rules.pyc', 
         'admin_actions.py', 'extended_services_delete.py', 
         'extended_availability_zone.pyo', 'aggregates.py', 
         'used_limits_for_admin.pyo', 'fixed_ips.pyc', 'cells.pyc', 
         'quota_classes.pyo', 'baremetal_ext_status.pyc', 'agents.pyo', 
         'deferred_delete.pyc', 'os_tenant_networks.py', 'cloudpipe.py', 
         'instance_usage_audit_log.pyc', 'extended_ips_mac.py', 
         'flavor_disabled.pyc', 'extended_quotas.py', 'cloudpipe.pyo', 
         'hosts.py', 'flavormanage.py', 'multiple_create.pyc', 'evacuate.pyo', 
         'console_output.pyc', 'shelve.pyc', 'volume_attachment_update.pyc', 
         'extended_services_delete.pyc', 'hypervisors.pyo', 'user_data.pyc', 
         'instance_usage_audit_log.pyo', 'floating_ips.py', 'baremetal_nodes.pyc', 
         'used_limits_for_admin.py', 'deferred_delete.py', 'cells.py', 
         'cloudpipe_update.py', 'cloudpipe.pyc', 'security_groups.pyo', 
         'fping.pyo', 'security_group_default_rules.py', 'virtual_interfaces.pyc', 
         'cloudpipe_update.pyo', 'availability_zone.pyc', 'used_limits.py', 
         'migrations.py', 'extended_floating_ips.py', 
         'extended_virtual_interfaces_net.py', 'floating_ip_dns.py', 
         'assisted_volume_snapshots.pyo', 'services.pyo', 'server_diagnostics.py', 
         'scheduler_hints.pyc', 'multinic.py', 'aggregates.pyo', 
         'flavor_disabled.py', 'quota_classes.py', 'flavor_rxtx.pyo', 
         'instance_usage_audit_log.py', 'server_groups.pyc', 'fixed_ips.py', 
         'extended_server_attributes.pyc', 'assisted_volume_snapshots.pyc', 
         'certificates.pyo', 'extended_ips_mac.pyo', 'baremetal_nodes.pyo', 
         'scheduler_hints.pyo', 'server_diagnostics.pyo', 
         'hide_server_addresses.pyc', 'floating_ips_bulk.pyo', 'consoles.pyo', 
         'baremetal_nodes.py', 'services.pyc', 'flavor_disabled.pyo', 
         'server_external_events.py', 'flavorextraspecs.py', 'agents.py', 
         'console_auth_tokens.pyc', 'flavorextradata.pyo', 'image_size.pyc', 
         'flavor_access.py', 'preserve_ephemeral_rebuild.py', 
         'instance_actions.pyo', 'volume_attachment_update.pyo', 'flavor_rxtx.pyc', 
         'extended_server_attributes.pyo', 'availability_zone.pyo', 
         'disk_config.pyc', '__init__.py', 'console_output.py', 'extended_ips.py', 
         'extended_availability_zone.pyc', 'hide_server_addresses.pyo', 
         'server_groups.pyo', 'image_size.pyo', 'createserverext.pyc', 
         'floating_ip_pools.pyo', 'quotas.py', 'server_diagnostics.pyc', 
         'disk_config.pyo', 'availability_zone.py', 'console_auth_tokens.pyo', 
         'server_usage.py', 'createserverext.py', 'server_start_stop.py', 
         'floating_ip_dns.pyo', 'admin_actions.pyc', 'multiple_create.py', 
         'keypairs.py', 'cell_capacities.pyc', 'console_auth_tokens.py', 
         'flavor_rxtx.py', 'preserve_ephemeral_rebuild.pyo', 'hypervisors.pyc', 
         'extended_volumes.pyc', 'hosts.pyc', 'floating_ip_pools.pyc', 
         'user_quotas.pyo', 'floating_ip_dns.pyc', 'createserverext.pyo', 
         'consoles.pyc', 'flavor_access.pyo', 'extended_ips.pyo', 
         'multiple_create.pyo', 'cells.pyo', 'flavor_swap.pyo', 
         'config_drive.py', 'deferred_delete.pyo', 'simple_tenant_usage.py', 
         'extended_ips_mac.pyc', 'flavor_swap.pyc', 'cloudpipe_update.pyc', 
         'extended_server_attributes.py', 'fping.py', 'user_quotas.py', 
         'hosts.pyo', 'flavorextraspecs.pyc', 'simple_tenant_usage.pyc', 
         'migrations.pyc', 'instance_actions.pyc', 'virtual_interfaces.pyo', 
         'extended_quotas.pyc', 'rescue.pyc', 'floating_ips_bulk.py', 
         'keypairs.pyc', 'certificates.pyc', 'disk_config.py', 
         'console_output.pyo', 'extended_volumes.py', 'fixed_ips.pyo', 
         'agents.pyc', 'fping.pyc', 'quota_classes.pyc', 'volumes.pyc', 
         'block_device_mapping_v2_boot.pyc', 'config_drive.pyc', 
         'virtual_interfaces.py', 'user_quotas.pyc', 'used_limits.pyc', 
         'certificates.py', 'flavor_swap.py', 'security_groups.py', 
         'extended_ips.pyc', 'extended_status.pyc', 'server_groups.py', 
         'used_limits_for_admin.pyc', 'floating_ips.pyo', 'aggregates.pyc', 
         'block_device_mapping_v2_boot.py', 'server_external_events.pyo', 
         'used_limits.pyo', 'networks_associate.py', 'hypervisors.py', 
         'server_usage.pyc', 'admin_actions.pyo', 'quotas.pyo', '__init__.pyc', 
         'shelve.pyo', 'os_tenant_networks.pyo', 'flavorextradata.py', 
         'server_password.pyo', 'cell_capacities.py', 
         'extended_virtual_interfaces_net.pyc', 'extended_status.py', 
         'server_password.pyc', 'volumes.py', 'evacuate.pyc', 
         'extended_volumes.pyo', 'networks_associate.pyc', 
         'attach_interfaces.pyo', 'server_start_stop.pyc', 'flavorextraspecs.pyo', 
         'attach_interfaces.py', 'flavormanage.pyo', 'shelve.py', 'user_data.pyo', 
         'simple_tenant_usage.pyo', 'cell_capacities.pyo', 
         'security_group_default_rules.pyo', 'assisted_volume_snapshots.py', 
         'flavor_access.pyc', 'os_networks.py']
        ======================================================================================
        """

        # Compute the relative package name from the dirpath
        relpath = os.path.relpath(dirpath, our_dir)
        if relpath == '.':
            relpkg = ''
        else:
            relpkg = '.%s' % '.'.join(relpath.split(os.sep))

        # Now, consider each file in turn, only considering .py files
        for fname in filenames:
            root, ext = os.path.splitext(fname)

            # Skip __init__ and anything that's not .py
            if ext != '.py' or root == '__init__':
                continue

            # Try loading it
            classname = "%s%s" % (root[0].upper(), root[1:])
            classpath = ("%s%s.%s.%s" % (package, relpkg, root, classname))

            if ext_list is not None and classname not in ext_list:
                logger.debug("Skipping extension: %s" % classpath)
                continue

            try:
                ext_mgr.load_extension(classpath)
            except Exception as exc:
                logger.warn(
                    _('Failed to load extension %(classpath)s: '
                      '%(exc)s'), {
                          'classpath': classpath,
                          'exc': exc
                      })

        # Now, let's consider any subdirectories we may have...
        subdirs = []
        for dname in dirnames:
            # Skip it if it does not have __init__.py
            if not os.path.exists(os.path.join(dirpath, dname, '__init__.py')):
                continue

            # If it has extension(), delegate...
            ext_name = "%s%s.%s.extension" % (package, relpkg, dname)
            try:
                ext = importutils.import_class(ext_name)
            except ImportError:
                # extension() doesn't exist on it, so we'll explore
                # the directory for ourselves
                subdirs.append(dname)
            else:
                try:
                    ext(ext_mgr)
                except Exception as exc:
                    logger.warn(
                        _('Failed to load extension %(ext_name)s:'
                          '%(exc)s'), {
                              'ext_name': ext_name,
                              'exc': exc
                          })

        # Update the list of directories we'll explore...
        dirnames[:] = subdirs
Esempio n. 7
0
def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None):
    """
    Registers all standard API extensions.
    注册所有标准的API扩展功能。
    @@@@注:在这里还只是实现API扩展功能的注册,还没有进行实质的功能扩展方法的调用;
    """
    """
    ======================================================================================
    ext_mgr = <nova.api.openstack.compute.extensions.ExtensionManager object at 0x33d9190>
    __path__ = ['/usr/lib/python2.7/site-packages/nova/api/openstack/compute/contrib']
    LOG = <nova.openstack.common.log.ContextAdapter object at 0x295d610>
    __package__ = nova.api.openstack.compute.contrib
    ======================================================================================
    """

    # Walk through all the modules in our directory...
    our_dir = path[0]
    """
    our_dir = /usr/lib/python2.7/site-packages/nova/api/openstack/compute/contrib
    """
    """
    1.获取contrib目录下所有的文件;
    2.过滤出以.py作为后缀的文件;
    3.针对每一个.py文件,获取其具有和其文件名相同的类名的类(每一个类都有),
      作为加载扩展API功能的入口;
    4.加载并调用所获取的每一个类,进行时间日期等参数的初始化;
      (这里还没有进行实质的功能扩展方法的调用;)
    """
    for dirpath, dirnames, filenames in os.walk(our_dir):
        """
        ======================================================================================
        filenames = 
        ['instance_actions.py', 'extended_hypervisors.pyo', 
         'extended_services_delete.pyo', 'os_networks.pyc', 
         'extended_floating_ips.pyc', 'migrations.pyo', 
         'server_external_events.pyc', 'floating_ips.pyc', 
         'user_data.py', 'os_networks.pyo', 'rescue.pyo', 
         'extended_virtual_interfaces_net.pyo', 'extended_floating_ips.pyo', 
         'baremetal_ext_status.pyo', 'server_start_stop.pyo', 
         'os_tenant_networks.pyc', 'scheduler_hints.py', 'image_size.py', 
         'rescue.py', 'block_device_mapping_v2_boot.pyo', 'quotas.pyc', 
         '__init__.pyo', 'extended_services.pyc', 'multinic.pyc', 'services.py', 
         'extended_services.py', 'extended_hypervisors.py', 'extended_quotas.pyo', 
         'preserve_ephemeral_rebuild.pyc', 'extended_status.pyo', 
         'baremetal_ext_status.py', 'security_groups.pyc', 
         'volume_attachment_update.py', 'floating_ip_pools.py', 
         'server_password.py', 'floating_ips_bulk.pyc', 'volumes.pyo', 
         'keypairs.pyo', 'flavormanage.pyc', 'extended_hypervisors.pyc', 
         'flavorextradata.pyc', 'extended_services.pyo', 'attach_interfaces.pyc', 
         'server_usage.pyo', 'networks_associate.pyo', 'multinic.pyo', 'evacuate.py', 
         'consoles.py', 'config_drive.pyo', 'hide_server_addresses.py', 
         'extended_availability_zone.py', 'security_group_default_rules.pyc', 
         'admin_actions.py', 'extended_services_delete.py', 
         'extended_availability_zone.pyo', 'aggregates.py', 
         'used_limits_for_admin.pyo', 'fixed_ips.pyc', 'cells.pyc', 
         'quota_classes.pyo', 'baremetal_ext_status.pyc', 'agents.pyo', 
         'deferred_delete.pyc', 'os_tenant_networks.py', 'cloudpipe.py', 
         'instance_usage_audit_log.pyc', 'extended_ips_mac.py', 
         'flavor_disabled.pyc', 'extended_quotas.py', 'cloudpipe.pyo', 
         'hosts.py', 'flavormanage.py', 'multiple_create.pyc', 'evacuate.pyo', 
         'console_output.pyc', 'shelve.pyc', 'volume_attachment_update.pyc', 
         'extended_services_delete.pyc', 'hypervisors.pyo', 'user_data.pyc', 
         'instance_usage_audit_log.pyo', 'floating_ips.py', 'baremetal_nodes.pyc', 
         'used_limits_for_admin.py', 'deferred_delete.py', 'cells.py', 
         'cloudpipe_update.py', 'cloudpipe.pyc', 'security_groups.pyo', 
         'fping.pyo', 'security_group_default_rules.py', 'virtual_interfaces.pyc', 
         'cloudpipe_update.pyo', 'availability_zone.pyc', 'used_limits.py', 
         'migrations.py', 'extended_floating_ips.py', 
         'extended_virtual_interfaces_net.py', 'floating_ip_dns.py', 
         'assisted_volume_snapshots.pyo', 'services.pyo', 'server_diagnostics.py', 
         'scheduler_hints.pyc', 'multinic.py', 'aggregates.pyo', 
         'flavor_disabled.py', 'quota_classes.py', 'flavor_rxtx.pyo', 
         'instance_usage_audit_log.py', 'server_groups.pyc', 'fixed_ips.py', 
         'extended_server_attributes.pyc', 'assisted_volume_snapshots.pyc', 
         'certificates.pyo', 'extended_ips_mac.pyo', 'baremetal_nodes.pyo', 
         'scheduler_hints.pyo', 'server_diagnostics.pyo', 
         'hide_server_addresses.pyc', 'floating_ips_bulk.pyo', 'consoles.pyo', 
         'baremetal_nodes.py', 'services.pyc', 'flavor_disabled.pyo', 
         'server_external_events.py', 'flavorextraspecs.py', 'agents.py', 
         'console_auth_tokens.pyc', 'flavorextradata.pyo', 'image_size.pyc', 
         'flavor_access.py', 'preserve_ephemeral_rebuild.py', 
         'instance_actions.pyo', 'volume_attachment_update.pyo', 'flavor_rxtx.pyc', 
         'extended_server_attributes.pyo', 'availability_zone.pyo', 
         'disk_config.pyc', '__init__.py', 'console_output.py', 'extended_ips.py', 
         'extended_availability_zone.pyc', 'hide_server_addresses.pyo', 
         'server_groups.pyo', 'image_size.pyo', 'createserverext.pyc', 
         'floating_ip_pools.pyo', 'quotas.py', 'server_diagnostics.pyc', 
         'disk_config.pyo', 'availability_zone.py', 'console_auth_tokens.pyo', 
         'server_usage.py', 'createserverext.py', 'server_start_stop.py', 
         'floating_ip_dns.pyo', 'admin_actions.pyc', 'multiple_create.py', 
         'keypairs.py', 'cell_capacities.pyc', 'console_auth_tokens.py', 
         'flavor_rxtx.py', 'preserve_ephemeral_rebuild.pyo', 'hypervisors.pyc', 
         'extended_volumes.pyc', 'hosts.pyc', 'floating_ip_pools.pyc', 
         'user_quotas.pyo', 'floating_ip_dns.pyc', 'createserverext.pyo', 
         'consoles.pyc', 'flavor_access.pyo', 'extended_ips.pyo', 
         'multiple_create.pyo', 'cells.pyo', 'flavor_swap.pyo', 
         'config_drive.py', 'deferred_delete.pyo', 'simple_tenant_usage.py', 
         'extended_ips_mac.pyc', 'flavor_swap.pyc', 'cloudpipe_update.pyc', 
         'extended_server_attributes.py', 'fping.py', 'user_quotas.py', 
         'hosts.pyo', 'flavorextraspecs.pyc', 'simple_tenant_usage.pyc', 
         'migrations.pyc', 'instance_actions.pyc', 'virtual_interfaces.pyo', 
         'extended_quotas.pyc', 'rescue.pyc', 'floating_ips_bulk.py', 
         'keypairs.pyc', 'certificates.pyc', 'disk_config.py', 
         'console_output.pyo', 'extended_volumes.py', 'fixed_ips.pyo', 
         'agents.pyc', 'fping.pyc', 'quota_classes.pyc', 'volumes.pyc', 
         'block_device_mapping_v2_boot.pyc', 'config_drive.pyc', 
         'virtual_interfaces.py', 'user_quotas.pyc', 'used_limits.pyc', 
         'certificates.py', 'flavor_swap.py', 'security_groups.py', 
         'extended_ips.pyc', 'extended_status.pyc', 'server_groups.py', 
         'used_limits_for_admin.pyc', 'floating_ips.pyo', 'aggregates.pyc', 
         'block_device_mapping_v2_boot.py', 'server_external_events.pyo', 
         'used_limits.pyo', 'networks_associate.py', 'hypervisors.py', 
         'server_usage.pyc', 'admin_actions.pyo', 'quotas.pyo', '__init__.pyc', 
         'shelve.pyo', 'os_tenant_networks.pyo', 'flavorextradata.py', 
         'server_password.pyo', 'cell_capacities.py', 
         'extended_virtual_interfaces_net.pyc', 'extended_status.py', 
         'server_password.pyc', 'volumes.py', 'evacuate.pyc', 
         'extended_volumes.pyo', 'networks_associate.pyc', 
         'attach_interfaces.pyo', 'server_start_stop.pyc', 'flavorextraspecs.pyo', 
         'attach_interfaces.py', 'flavormanage.pyo', 'shelve.py', 'user_data.pyo', 
         'simple_tenant_usage.pyo', 'cell_capacities.pyo', 
         'security_group_default_rules.pyo', 'assisted_volume_snapshots.py', 
         'flavor_access.pyc', 'os_networks.py']
        ======================================================================================
        """

        # Compute the relative package name from the dirpath
        relpath = os.path.relpath(dirpath, our_dir)
        if relpath == ".":
            relpkg = ""
        else:
            relpkg = ".%s" % ".".join(relpath.split(os.sep))

        # Now, consider each file in turn, only considering .py files
        for fname in filenames:
            root, ext = os.path.splitext(fname)

            # Skip __init__ and anything that's not .py
            if ext != ".py" or root == "__init__":
                continue

            # Try loading it
            classname = "%s%s" % (root[0].upper(), root[1:])
            classpath = "%s%s.%s.%s" % (package, relpkg, root, classname)

            if ext_list is not None and classname not in ext_list:
                logger.debug("Skipping extension: %s" % classpath)
                continue

            try:
                ext_mgr.load_extension(classpath)
            except Exception as exc:
                logger.warn(
                    _("Failed to load extension %(classpath)s: " "%(exc)s"), {"classpath": classpath, "exc": exc}
                )

        # Now, let's consider any subdirectories we may have...
        subdirs = []
        for dname in dirnames:
            # Skip it if it does not have __init__.py
            if not os.path.exists(os.path.join(dirpath, dname, "__init__.py")):
                continue

            # If it has extension(), delegate...
            ext_name = "%s%s.%s.extension" % (package, relpkg, dname)
            try:
                ext = importutils.import_class(ext_name)
            except ImportError:
                # extension() doesn't exist on it, so we'll explore
                # the directory for ourselves
                subdirs.append(dname)
            else:
                try:
                    ext(ext_mgr)
                except Exception as exc:
                    logger.warn(
                        _("Failed to load extension %(ext_name)s:" "%(exc)s"), {"ext_name": ext_name, "exc": exc}
                    )

        # Update the list of directories we'll explore...
        dirnames[:] = subdirs