Exemple #1
0
    def _fix_backwards_networks(self, cloud):
        # Leave the external_network and internal_network keys in the
        # dict because consuming code might be expecting them.
        networks = []
        # Normalize existing network entries
        for net in cloud.get('networks', []):
            name = net.get('name')
            if not name:
                raise exceptions.ConfigException(
                    'Entry in network list is missing required field "name".')
            network = dict(
                name=name,
                routes_externally=get_boolean(net.get('routes_externally')),
                nat_source=get_boolean(net.get('nat_source')),
                nat_destination=get_boolean(net.get('nat_destination')),
                default_interface=get_boolean(net.get('default_interface')),
            )
            # routes_ipv4_externally defaults to the value of routes_externally
            network['routes_ipv4_externally'] = get_boolean(
                net.get('routes_ipv4_externally',
                        network['routes_externally']))
            # routes_ipv6_externally defaults to the value of routes_externally
            network['routes_ipv6_externally'] = get_boolean(
                net.get('routes_ipv6_externally',
                        network['routes_externally']))
            networks.append(network)

        for key in ('external_network', 'internal_network'):
            external = key.startswith('external')
            if key in cloud and 'networks' in cloud:
                raise exceptions.ConfigException(
                    "Both {key} and networks were specified in the config."
                    " Please remove {key} from the config and use the network"
                    " list to configure network behavior.".format(key=key))
            if key in cloud:
                warnings.warn(
                    "{key} is deprecated. Please replace with an entry in"
                    " a dict inside of the networks list with name: {name}"
                    " and routes_externally: {external}".format(
                        key=key, name=cloud[key], external=external))
                networks.append(
                    dict(name=cloud[key],
                         routes_externally=external,
                         nat_destination=not external,
                         default_interface=external))

        # Validate that we don't have duplicates
        self._validate_networks(networks, 'nat_destination')
        self._validate_networks(networks, 'default_interface')

        cloud['networks'] = networks
        return cloud
 def get_session(self):
     """Return a keystoneauth session based on the auth credentials."""
     if self._keystone_session is None:
         if not self._auth:
             raise exceptions.ConfigException(
                 "Problem with auth parameters")
         (verify, cert) = self.get_requests_verify_args()
         # Turn off urllib3 warnings about insecure certs if we have
         # explicitly configured requests to tell it we do not want
         # cert verification
         if not verify:
             self.log.debug(
                 "Turning off SSL warnings for {full_name}"
                 " since verify=False".format(full_name=self.full_name))
         requestsexceptions.squelch_warnings(insecure_requests=not verify)
         self._keystone_session = self._session_constructor(
             auth=self._auth,
             verify=verify,
             cert=cert,
             timeout=self.config.get('api_timeout'),
             discovery_cache=self._discovery_cache)
         self.insert_user_agent()
         # Using old keystoneauth with new os-client-config fails if
         # we pass in app_name and app_version. Those are not essential,
         # nor a reason to bump our minimum, so just test for the session
         # having the attribute post creation and set them then.
         if hasattr(self._keystone_session, 'app_name'):
             self._keystone_session.app_name = self._app_name
         if hasattr(self._keystone_session, 'app_version'):
             self._keystone_session.app_version = self._app_version
     return self._keystone_session
Exemple #3
0
def _fix_argv(argv):
    # Transform any _ characters in arg names to - so that we don't
    # have to throw billions of compat argparse arguments around all
    # over the place.
    processed = collections.defaultdict(list)
    for index in range(0, len(argv)):
        # If the value starts with '--' and has '-' or '_' in it, then
        # it's worth looking at it
        if re.match('^--.*(_|-)+.*', argv[index]):
            split_args = argv[index].split('=')
            orig = split_args[0]
            new = orig.replace('_', '-')
            if orig != new:
                split_args[0] = new
                argv[index] = "=".join(split_args)
            # Save both for later so we can throw an error about dupes
            processed[new].append(orig)
    overlap = []
    for new, old in processed.items():
        if len(old) > 1:
            overlap.extend(old)
    if overlap:
        raise exceptions.ConfigException(
            "The following options were given: '{options}' which contain"
            " duplicates except that one has _ and one has -. There is"
            " no sane way for us to know what you're doing. Remove the"
            " duplicate option and try again".format(
                options=','.join(overlap)))
Exemple #4
0
 def _expand_vendor_profile(self, name, cloud, our_cloud):
     # Expand a profile if it exists. 'cloud' is an old confusing name
     # for this.
     profile_name = our_cloud.get('profile', our_cloud.get('cloud', None))
     if profile_name and profile_name != self.envvar_key:
         if 'cloud' in our_cloud:
             warnings.warn(
                 "{0} use the keyword 'cloud' to reference a known "
                 "vendor profile. This has been deprecated in favor of the "
                 "'profile' keyword.".format(self.config_filename))
         vendor_filename, vendor_file = self._load_vendor_file()
         if vendor_file and profile_name in vendor_file['public-clouds']:
             _auth_update(cloud, vendor_file['public-clouds'][profile_name])
         else:
             profile_data = vendors.get_profile(profile_name)
             if profile_data:
                 status = profile_data.pop('status', 'active')
                 message = profile_data.pop('message', '')
                 if status == 'deprecated':
                     warnings.warn(
                         "{profile_name} is deprecated: {message}".format(
                             profile_name=profile_name, message=message))
                 elif status == 'shutdown':
                     raise exceptions.ConfigException(
                         "{profile_name} references a cloud that no longer"
                         " exists: {message}".format(
                             profile_name=profile_name, message=message))
                 _auth_update(cloud, profile_data)
             else:
                 # Can't find the requested vendor config, go about business
                 warnings.warn("Couldn't find the vendor profile '{0}', for"
                               " the cloud '{1}'".format(
                                   profile_name, name))
Exemple #5
0
    def _get_base_cloud_config(self, name, profile=None):
        cloud = dict()

        # Only validate cloud name if one was given
        if name and name not in self.cloud_config['clouds']:
            raise exceptions.ConfigException(
                "Cloud {name} was not found.".format(
                    name=name))

        our_cloud = self.cloud_config['clouds'].get(name, dict())
        if profile:
            our_cloud['profile'] = profile

        # Get the defaults
        cloud.update(self.defaults)
        self._expand_vendor_profile(name, cloud, our_cloud)

        if 'auth' not in cloud:
            cloud['auth'] = dict()

        _auth_update(cloud, our_cloud)
        if 'cloud' in cloud:
            del cloud['cloud']

        return cloud
Exemple #6
0
 def _validate_networks(self, networks, key):
     value = None
     for net in networks:
         if value and net[key]:
             raise exceptions.ConfigException(
                 "Duplicate network entries for {key}: {net1} and {net2}."
                 " Only one network can be flagged with {key}".format(
                     key=key, net1=value['name'], net2=net['name']))
         if not value and net[key]:
             value = net
Exemple #7
0
 def test__raise_exception_on_no_cloud(self, mock_cloud, mock_config):
     """
     Test that when os-client-config can't find a named cloud, a
     shade exception is emitted.
     """
     mock_config.return_value.get_one_cloud.side_effect = (
         os_exc.ConfigException())
     self.assertRaises(exc.OpenStackCloudException,
                       inventory.OpenStackInventory,
                       cloud='supercloud')
     mock_config.return_value.get_one_cloud.assert_called_once_with(
         'supercloud')
    def _get_version_request(self, service_type, version):
        """Translate OCC version args to those needed by ksa adapter.

        If no version is requested explicitly and we have a configured version,
        set the version parameter and let ksa deal with expanding that to
        min=ver.0, max=ver.latest.

        If version is set, pass it through.

        If version is not set and we don't have a configured version, default
        to latest.

        If version is set, contains a '.', and default_microversion is not
        set, also pass it as a default microversion.
        """
        version_request = _util.VersionRequest()
        if version == 'latest':
            version_request.max_api_version = 'latest'
            return version_request

        if not version:
            version = self.get_api_version(service_type)

        # Octavia doens't have a version discovery document. Hard-code an
        # exception to this logic for now.
        if not version and service_type not in ('load-balancer', ):
            version_request.max_api_version = 'latest'
        else:
            version_request.version = version

        default_microversion = self.get_default_microversion(service_type)
        implied_microversion = _get_implied_microversion(version)
        if (implied_microversion and default_microversion
                and implied_microversion != default_microversion):
            raise exceptions.ConfigException(
                "default_microversion of {default_microversion} was given"
                " for {service_type}, but api_version looks like a"
                " microversion as well. Please set api_version to just the"
                " desired major version, or omit default_microversion".format(
                    default_microversion=default_microversion,
                    service_type=service_type))
        if implied_microversion:
            default_microversion = implied_microversion
            # If we're inferring a microversion, don't pass the whole
            # string in as api_version, since that tells keystoneauth
            # we're looking for a major api version.
            version_request.version = version[0]

        version_request.default_microversion = default_microversion

        return version_request
Exemple #9
0
 def _expand_regions(self, regions):
     ret = []
     for region in regions:
         if isinstance(region, dict):
             # i.e. must have name key, and only name,values keys
             if 'name' not in region or \
                not {'name', 'values'} >= set(region):
                 raise exceptions.ConfigException(
                     'Invalid region entry at: %s' % region)
             if 'values' not in region:
                 region['values'] = {}
             ret.append(copy.deepcopy(region))
         else:
             ret.append(self._expand_region_name(region))
     return ret
Exemple #10
0
def get_profile(profile_name):
    vendor_defaults = _get_vendor_defaults()
    if profile_name in vendor_defaults:
        return vendor_defaults[profile_name].copy()
    profile_url = urllib.parse.urlparse(profile_name)
    if not profile_url.netloc:
        # This isn't a url, and we already don't have it.
        return
    well_known_url = _WELL_KNOWN_PATH.format(
        scheme=profile_url.scheme,
        netloc=profile_url.netloc,
    )
    response = requests.get(well_known_url)
    if not response.ok:
        raise exceptions.ConfigException(
            "{profile_name} is a remote profile that could not be fetched:"
            " {status_code} {reason}".format(
                profile_name=profile_name,
                status_code=response.status_code,
                reason=response.reason))
        vendor_defaults[profile_name] = None
        return
    vendor_data = response.json()
    name = vendor_data['name']
    # Merge named and url cloud config, but make named config override the
    # config from the cloud so that we can supply local overrides if needed.
    profile = _util.merge_clouds(
        vendor_data['profile'],
        vendor_defaults.get(name, {}))
    # If there is (or was) a profile listed in a named config profile, it
    # might still be here. We just merged in content from a URL though, so
    # pop the key to prevent doing it again in the future.
    profile.pop('profile', None)
    # Save the data under both names so we don't reprocess this, no matter
    # how we're called.
    vendor_defaults[profile_name] = profile
    vendor_defaults[name] = profile
    return profile
Exemple #11
0
    def _get_region(self, cloud=None, region_name=''):
        if region_name is None:
            region_name = ''
        if not cloud:
            return self._expand_region_name(region_name)

        regions = self._get_known_regions(cloud)
        if not regions:
            return self._expand_region_name(region_name)

        if not region_name:
            return regions[0]

        for region in regions:
            if region['name'] == region_name:
                return region

        raise exceptions.ConfigException(
            'Region {region_name} is not a valid region name for cloud'
            ' {cloud}. Valid choices are {region_list}. Please note that'
            ' region names are case sensitive.'.format(
                region_name=region_name,
                region_list=','.join([r['name'] for r in regions]),
                cloud=cloud))
Exemple #12
0
    def register_argparse_arguments(self, parser, argv, service_keys=None):
        """Register all of the common argparse options needed.

        Given an argparse parser, register the keystoneauth Session arguments,
        the keystoneauth Auth Plugin Options and os-cloud. Also, peek in the
        argv to see if all of the auth plugin options should be registered
        or merely the ones already configured.

        :param argparse.ArgumentParser: parser to attach argparse options to
        :param argv: the arguments provided to the application
        :param string service_keys: Service or list of services this argparse
                                    should be specialized for, if known.
                                    The first item in the list will be used
                                    as the default value for service_type
                                    (optional)

        :raises exceptions.ConfigException if an invalid auth-type is requested
        """

        if service_keys is None:
            service_keys = []

        # Fix argv in place - mapping any keys with embedded _ in them to -
        _fix_argv(argv)

        local_parser = argparse_mod.ArgumentParser(add_help=False)

        for p in (parser, local_parser):
            p.add_argument('--os-cloud',
                           metavar='<name>',
                           default=self._get_envvar('OS_CLOUD', None),
                           help='Named cloud to connect to')

        # we need to peek to see if timeout was actually passed, since
        # the keystoneauth declaration of it has a default, which means
        # we have no clue if the value we get is from the ksa default
        # for from the user passing it explicitly. We'll stash it for later
        local_parser.add_argument('--timeout', metavar='<timeout>')

        # We need for get_one to be able to peek at whether a token
        # was passed so that we can swap the default from password to
        # token if it was. And we need to also peek for --os-auth-token
        # for novaclient backwards compat
        local_parser.add_argument('--os-token')
        local_parser.add_argument('--os-auth-token')

        # Peek into the future and see if we have an auth-type set in
        # config AND a cloud set, so that we know which command line
        # arguments to register and show to the user (the user may want
        # to say something like:
        #   openstack --os-cloud=foo --os-oidctoken=bar
        # although I think that user is the cause of my personal pain
        options, _args = local_parser.parse_known_args(argv)
        if options.timeout:
            self._argv_timeout = True

        # validate = False because we're not _actually_ loading here
        # we're only peeking, so it's the wrong time to assert that
        # the rest of the arguments given are invalid for the plugin
        # chosen (for instance, --help may be requested, so that the
        # user can see what options he may want to give
        cloud_region = self.get_one(argparse=options, validate=False)
        default_auth_type = cloud_region.config['auth_type']

        try:
            loading.register_auth_argparse_arguments(parser,
                                                     argv,
                                                     default=default_auth_type)
        except Exception:
            # Hidiing the keystoneauth exception because we're not actually
            # loading the auth plugin at this point, so the error message
            # from it doesn't actually make sense to os-client-config users
            options, _args = parser.parse_known_args(argv)
            plugin_names = loading.get_available_plugin_names()
            raise exceptions.ConfigException(
                "An invalid auth-type was specified: {auth_type}."
                " Valid choices are: {plugin_names}.".format(
                    auth_type=options.os_auth_type,
                    plugin_names=",".join(plugin_names)))

        if service_keys:
            primary_service = service_keys[0]
        else:
            primary_service = None
        loading.register_session_argparse_arguments(parser)
        adapter.register_adapter_argparse_arguments(
            parser, service_type=primary_service)
        for service_key in service_keys:
            # legacy clients have un-prefixed api-version options
            parser.add_argument('--{service_key}-api-version'.format(
                service_key=service_key.replace('_', '-'),
                help=argparse_mod.SUPPRESS))
            adapter.register_service_adapter_argparse_arguments(
                parser, service_type=service_key)

        # Backwards compat options for legacy clients
        parser.add_argument('--http-timeout', help=argparse_mod.SUPPRESS)
        parser.add_argument('--os-endpoint-type', help=argparse_mod.SUPPRESS)
        parser.add_argument('--endpoint-type', help=argparse_mod.SUPPRESS)
Exemple #13
0
    def get_session_client(
            self, service_type, version=None,
            constructor=proxy.Proxy,
            **kwargs):
        """Return a prepped keystoneauth Adapter for a given service.

        This is useful for making direct requests calls against a
        'mounted' endpoint. That is, if you do:

          client = get_session_client('compute')

        then you can do:

          client.get('/flavors')

        and it will work like you think.
        """
        version_request = self._get_version_request(service_type, version)

        kwargs.setdefault('region_name', self.get_region_name(service_type))
        kwargs.setdefault('connect_retries',
                          self.get_connect_retries(service_type))
        kwargs.setdefault('status_code_retries',
                          self.get_status_code_retries(service_type))
        kwargs.setdefault('statsd_prefix', self.get_statsd_prefix())
        kwargs.setdefault('statsd_client', self.get_statsd_client())
        kwargs.setdefault('prometheus_counter', self.get_prometheus_counter())
        kwargs.setdefault(
            'prometheus_histogram', self.get_prometheus_histogram())
        kwargs.setdefault('influxdb_config', self._influxdb_config)
        kwargs.setdefault('influxdb_client', self.get_influxdb_client())
        endpoint_override = self.get_endpoint(service_type)
        version = version_request.version
        min_api_version = (
            kwargs.pop('min_version', None) or version_request.min_api_version)
        max_api_version = (
            kwargs.pop('max_version', None) or version_request.max_api_version)

        # Older neutron has inaccessible discovery document. Nobody noticed
        # because neutronclient hard-codes an append of v2.0. YAY!
        # Also, older octavia has a similar issue.
        if service_type in ('network', 'load-balancer'):
            version = None
            min_api_version = None
            max_api_version = None
            if endpoint_override is None:
                endpoint_override = self._get_hardcoded_endpoint(
                    service_type, constructor)

        client = constructor(
            session=self.get_session(),
            service_type=self.get_service_type(service_type),
            service_name=self.get_service_name(service_type),
            interface=self.get_interface(service_type),
            version=version,
            min_version=min_api_version,
            max_version=max_api_version,
            endpoint_override=endpoint_override,
            default_microversion=version_request.default_microversion,
            rate_limit=self.get_rate_limit(service_type),
            concurrency=self.get_concurrency(service_type),
            **kwargs)
        if version_request.default_microversion:
            default_microversion = version_request.default_microversion
            info = client.get_endpoint_data()
            if not discover.version_between(
                    info.min_microversion,
                    info.max_microversion,
                    default_microversion
            ):
                if self.get_default_microversion(service_type):
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}.".format(
                            service_type=service_type,
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
                else:
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a maximum of"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}. The default"
                        " microversion was set because a microversion"
                        " formatted version string, '{api_version}', was"
                        " passed for the api_version of the service. If it"
                        " was not intended to set a default microversion"
                        " please remove anything other than an integer major"
                        " version from the version setting for"
                        " the service.".format(
                            service_type=service_type,
                            api_version=self.get_api_version(service_type),
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
        return client
Exemple #14
0
def from_conf(conf, session=None, service_types=None, **kwargs):
    """Create a CloudRegion from oslo.config ConfigOpts.

    :param oslo_config.cfg.ConfigOpts conf:
        An oslo.config ConfigOpts containing keystoneauth1.Adapter options in
        sections named according to project (e.g. [nova], not [compute]).
        TODO: Current behavior is to use defaults if no such section exists,
        which may not be what we want long term.
    :param keystoneauth1.session.Session session:
        An existing authenticated Session to use. This is currently required.
        TODO: Load this (and auth) from the conf.
    :param service_types:
        A list/set of service types for which to look for and process config
        opts. If None, all known service types are processed. Note that we will
        not error if a supplied service type can not be processed successfully
        (unless you try to use the proxy, of course). This tolerates uses where
        the consuming code has paths for a given service, but those paths are
        not exercised for given end user setups, and we do not want to generate
        errors for e.g. missing/invalid conf sections in those cases. We also
        don't check to make sure your service types are spelled correctly -
        caveat implementor.
    :param kwargs:
        Additional keyword arguments to be passed directly to the CloudRegion
        constructor.
    :raise openstack.exceptions.ConfigException:
        If session is not specified.
    :return:
        An openstack.config.cloud_region.CloudRegion.
    """
    if not session:
        # TODO(mordred) Fill this in - not needed for first stab with nova
        raise exceptions.ConfigException("A Session must be supplied.")
    config_dict = kwargs.pop('config', config_defaults.get_defaults())
    stm = os_service_types.ServiceTypes()
    for st in stm.all_types_by_service_type:
        if service_types is not None and st not in service_types:
            _disable_service(
                config_dict, st,
                reason="Not in the list of requested service_types.")
            continue
        project_name = stm.get_project_name(st)
        if project_name not in conf:
            if '-' in project_name:
                project_name = project_name.replace('-', '_')

            if project_name not in conf:
                _disable_service(
                    config_dict, st,
                    reason="No section for project '{project}' (service type "
                           "'{service_type}') was present in the config."
                    .format(project=project_name, service_type=st))
                continue
        opt_dict = {}
        # Populate opt_dict with (appropriately processed) Adapter conf opts
        try:
            ks_load_adap.process_conf_options(conf[project_name], opt_dict)
        except Exception as e:
            # NOTE(efried): This is for (at least) a couple of scenarios:
            # (1) oslo_config.cfg.NoSuchOptError when ksa adapter opts are not
            #     registered in this section.
            # (2) TypeError, when opts are registered but bogus (e.g.
            #     'interface' and 'valid_interfaces' are both present).
            # We may want to consider (providing a kwarg giving the caller the
            # option of) blowing up right away for (2) rather than letting them
            # get all the way to the point of trying the service and having
            # *that* blow up.
            reason = ("Encountered an exception attempting to process config "
                      "for project '{project}' (service type "
                      "'{service_type}'): {exception}".format(
                          project=project_name, service_type=st, exception=e))
            _logger.warning("Disabling service '{service_type}': "
                            "{reason}".format(service_type=st, reason=reason))
            _disable_service(config_dict, st, reason=reason)
            continue
        # Load them into config_dict under keys prefixed by ${service_type}_
        for raw_name, opt_val in opt_dict.items():
            config_name = _make_key(raw_name, st)
            config_dict[config_name] = opt_val
    return CloudRegion(
        session=session, config=config_dict, **kwargs)
    def get_session_client(self,
                           service_type,
                           version=None,
                           constructor=adapter.Adapter,
                           **kwargs):
        """Return a prepped keystoneauth Adapter for a given service.

        This is useful for making direct requests calls against a
        'mounted' endpoint. That is, if you do:

          client = get_session_client('compute')

        then you can do:

          client.get('/flavors')

        and it will work like you think.
        """
        version_request = self._get_version_request(service_type, version)

        kwargs.setdefault('connect_retries',
                          self.get_connect_retries(service_type))
        kwargs.setdefault('status_code_retries',
                          self.get_status_code_retries(service_type))

        client = constructor(
            session=self.get_session(),
            service_type=self.get_service_type(service_type),
            service_name=self.get_service_name(service_type),
            interface=self.get_interface(service_type),
            region_name=self.region_name,
            version=version_request.version,
            min_version=version_request.min_api_version,
            max_version=version_request.max_api_version,
            endpoint_override=self.get_endpoint(service_type),
            default_microversion=version_request.default_microversion,
            **kwargs)
        if version_request.default_microversion:
            default_microversion = version_request.default_microversion
            info = client.get_endpoint_data()
            if not discover.version_between(info.min_microversion,
                                            info.max_microversion,
                                            default_microversion):
                if self.get_default_microversion(service_type):
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}.".format(
                            service_type=service_type,
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
                else:
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a maximum of"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}. The default"
                        " microversion was set because a microversion"
                        " formatted version string, '{api_version}', was"
                        " passed for the api_version of the service. If it"
                        " was not intended to set a default microversion"
                        " please remove anything other than an integer major"
                        " version from the version setting for"
                        " the service.".format(
                            service_type=service_type,
                            api_version=self.get_api_version(service_type),
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
        return client
    def get_session_client(self,
                           service_type,
                           version=None,
                           constructor=_adapter.OpenStackSDKAdapter,
                           **kwargs):
        """Return a prepped keystoneauth Adapter for a given service.

        This is useful for making direct requests calls against a
        'mounted' endpoint. That is, if you do:

          client = get_session_client('compute')

        then you can do:

          client.get('/flavors')

        and it will work like you think.
        """
        version_request = self._get_version_request(service_type, version)

        kwargs.setdefault('connect_retries',
                          self.get_connect_retries(service_type))
        kwargs.setdefault('status_code_retries',
                          self.get_status_code_retries(service_type))
        endpoint_override = self.get_endpoint(service_type)
        version = version_request.version
        min_api_version = (kwargs.pop('min_version', None)
                           or version_request.min_api_version)
        max_api_version = (kwargs.pop('max_version', None)
                           or version_request.max_api_version)
        # Older neutron has inaccessible discovery document. Nobody noticed
        # because neutronclient hard-codes an append of v2.0. YAY!
        if service_type == 'network':
            version = None
            min_api_version = None
            max_api_version = None
            if endpoint_override is None:
                network_adapter = constructor(
                    session=self.get_session(),
                    service_type=self.get_service_type(service_type),
                    service_name=self.get_service_name(service_type),
                    interface=self.get_interface(service_type),
                    region_name=self.region_name,
                )
                network_endpoint = network_adapter.get_endpoint()
                if not network_endpoint.rstrip().rsplit('/')[-1] == 'v2.0':
                    if not network_endpoint.endswith('/'):
                        network_endpoint += '/'
                    network_endpoint = urllib.parse.urljoin(
                        network_endpoint, 'v2.0')
                endpoint_override = network_endpoint

        client = constructor(
            session=self.get_session(),
            service_type=self.get_service_type(service_type),
            service_name=self.get_service_name(service_type),
            interface=self.get_interface(service_type),
            region_name=self.region_name,
            version=version,
            min_version=min_api_version,
            max_version=max_api_version,
            endpoint_override=endpoint_override,
            default_microversion=version_request.default_microversion,
            rate_limit=self.get_rate_limit(service_type),
            concurrency=self.get_concurrency(service_type),
            **kwargs)
        if version_request.default_microversion:
            default_microversion = version_request.default_microversion
            info = client.get_endpoint_data()
            if not discover.version_between(info.min_microversion,
                                            info.max_microversion,
                                            default_microversion):
                if self.get_default_microversion(service_type):
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}.".format(
                            service_type=service_type,
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
                else:
                    raise exceptions.ConfigException(
                        "A default microversion for service {service_type} of"
                        " {default_microversion} was requested, but the cloud"
                        " only supports a maximum of"
                        " only supports a minimum of {min_microversion} and"
                        " a maximum of {max_microversion}. The default"
                        " microversion was set because a microversion"
                        " formatted version string, '{api_version}', was"
                        " passed for the api_version of the service. If it"
                        " was not intended to set a default microversion"
                        " please remove anything other than an integer major"
                        " version from the version setting for"
                        " the service.".format(
                            service_type=service_type,
                            api_version=self.get_api_version(service_type),
                            default_microversion=default_microversion,
                            min_microversion=discover.version_to_string(
                                info.min_microversion),
                            max_microversion=discover.version_to_string(
                                info.max_microversion)))
        return client
Exemple #17
0
    def __init__(self,
                 config_files=None,
                 vendor_files=None,
                 override_defaults=None,
                 force_ipv4=None,
                 envvar_prefix=None,
                 secure_files=None,
                 pw_func=None,
                 session_constructor=None,
                 app_name=None,
                 app_version=None,
                 load_yaml_config=True,
                 load_envvars=True):
        self.log = _log.setup_logging('openstack.config')
        self._session_constructor = session_constructor
        self._app_name = app_name
        self._app_version = app_version
        self._load_envvars = load_envvars

        if load_yaml_config:
            self._config_files = config_files or CONFIG_FILES
            self._secure_files = secure_files or SECURE_FILES
            self._vendor_files = vendor_files or VENDOR_FILES
        else:
            self._config_files = []
            self._secure_files = []
            self._vendor_files = []

        config_file_override = self._get_envvar('OS_CLIENT_CONFIG_FILE')
        if config_file_override:
            self._config_files.insert(0, config_file_override)

        secure_file_override = self._get_envvar('OS_CLIENT_SECURE_FILE')
        if secure_file_override:
            self._secure_files.insert(0, secure_file_override)

        self.defaults = self._defaults_module.get_defaults()
        if override_defaults:
            self.defaults.update(override_defaults)

        # First, use a config file if it exists where expected
        self.config_filename, self.cloud_config = self._load_config_file()
        _, secure_config = self._load_secure_file()
        if secure_config:
            self.cloud_config = _merge_clouds(self.cloud_config, secure_config)

        if not self.cloud_config:
            self.cloud_config = {'clouds': {}}
        if 'clouds' not in self.cloud_config:
            self.cloud_config['clouds'] = {}

        # Grab ipv6 preference settings from env
        client_config = self.cloud_config.get('client', {})

        if force_ipv4 is not None:
            # If it's passed in to the constructor, honor it.
            self.force_ipv4 = force_ipv4
        else:
            # Get the backwards compat value
            prefer_ipv6 = get_boolean(
                self._get_envvar(
                    'OS_PREFER_IPV6',
                    client_config.get('prefer_ipv6',
                                      client_config.get('prefer-ipv6', True))))
            force_ipv4 = get_boolean(
                self._get_envvar(
                    'OS_FORCE_IPV4',
                    client_config.get('force_ipv4',
                                      client_config.get('broken-ipv6',
                                                        False))))

            self.force_ipv4 = force_ipv4
            if not prefer_ipv6:
                # this will only be false if someone set it explicitly
                # honor their wishes
                self.force_ipv4 = True

        # Next, process environment variables and add them to the mix
        self.envvar_key = self._get_envvar('OS_CLOUD_NAME', 'envvars')
        if self.envvar_key in self.cloud_config['clouds']:
            raise exceptions.ConfigException(
                '"{0}" defines a cloud named "{1}", but'
                ' OS_CLOUD_NAME is also set to "{1}". Please rename'
                ' either your environment based cloud, or one of your'
                ' file-based clouds.'.format(self.config_filename,
                                             self.envvar_key))

        self.default_cloud = self._get_envvar('OS_CLOUD')

        if load_envvars:
            envvars = self._get_os_environ(envvar_prefix=envvar_prefix)
            if envvars:
                self.cloud_config['clouds'][self.envvar_key] = envvars
                if not self.default_cloud:
                    self.default_cloud = self.envvar_key

        if not self.default_cloud and self.cloud_config['clouds']:
            if len(self.cloud_config['clouds'].keys()) == 1:
                # If there is only one cloud just use it. This matches envvars
                # behavior and allows for much less typing.
                # TODO(mordred) allow someone to mark a cloud as "default" in
                # clouds.yaml.
                # The next/iter thing is for python3 compat where dict.keys
                # returns an iterator but in python2 it's a list.
                self.default_cloud = next(
                    iter(self.cloud_config['clouds'].keys()))

        # Finally, fall through and make a cloud that starts with defaults
        # because we need somewhere to put arguments, and there are neither
        # config files or env vars
        if not self.cloud_config['clouds']:
            self.cloud_config = dict(clouds=dict(defaults=dict(self.defaults)))
            self.default_cloud = 'defaults'

        self._cache_expiration_time = 0
        self._cache_path = CACHE_PATH
        self._cache_class = 'dogpile.cache.null'
        self._cache_arguments = {}
        self._cache_expiration = {}
        if 'cache' in self.cloud_config:
            cache_settings = _util.normalize_keys(self.cloud_config['cache'])

            # expiration_time used to be 'max_age' but the dogpile setting
            # is expiration_time. Support max_age for backwards compat.
            self._cache_expiration_time = cache_settings.get(
                'expiration_time',
                cache_settings.get('max_age', self._cache_expiration_time))

            # If cache class is given, use that. If not, but if cache time
            # is given, default to memory. Otherwise, default to nothing.
            # to memory.
            if self._cache_expiration_time:
                self._cache_class = 'dogpile.cache.memory'
            self._cache_class = self.cloud_config['cache'].get(
                'class', self._cache_class)

            self._cache_path = os.path.expanduser(
                cache_settings.get('path', self._cache_path))
            self._cache_arguments = cache_settings.get('arguments',
                                                       self._cache_arguments)
            self._cache_expiration = cache_settings.get(
                'expiration', self._cache_expiration)

        # Flag location to hold the peeked value of an argparse timeout value
        self._argv_timeout = False

        # Save the password callback
        # password = self._pw_callback(prompt="Password: ")
        self._pw_callback = pw_func