#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         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.

"""Routines for configuring Knob."""
import os

from eventlet.green import socket
from oslo_config import cfg
from oslo_log import log as logging
from oslo_middleware import cors
from osprofiler import opts as profiler

from knob.common import exception
from knob.common.i18n import _
from knob.common.i18n import _LW
from knob.common import wsgi


LOG = logging.getLogger(__name__)
paste_deploy_group = cfg.OptGroup('paste_deploy')
paste_deploy_opts = [
    cfg.StrOpt('flavor',
               help=_("The flavor to use.")),
    cfg.StrOpt('api_paste_config', default="api-paste.ini",
               help=_("The API paste config file to use."))]


# these options define baseline defaults that apply to all clients
default_client_opts = [
    cfg.StrOpt('endpoint_type',
               default='publicURL',
               help=_(
                   'Type of endpoint in Identity service catalog to use '
                   'for communication with the OpenStack service.')),
    cfg.StrOpt('ca_file',
               help=_('Optional CA cert file to use in SSL connections.')),
    cfg.StrOpt('cert_file',
               help=_('Optional PEM-formatted certificate chain file.')),
    cfg.StrOpt('key_file',
               help=_('Optional PEM-formatted file that contains the '
                      'private key.')),
    cfg.BoolOpt('insecure',
                default=False,
                help=_("If set, then the server's certificate will not "
                       "be verified.")),
   cfg.IntOpt('client_retry_limit',
               default=2,
               help=_('Number of times to retry when a client encounters an '
                      'expected intermittent error. Set to 0 to disable '
                      'retries.')),
   cfg.IntOpt('max_interface_check_attempts',
               min=1,
               default=10,
               help=_('Number of times to check whether an interface has '
                      'been attached or detached.')),
   cfg.StrOpt('host',
               default=socket.gethostname(),
               help=_('Name of the engine node. '
                      'This can be an opaque identifier. '
                      'It is not necessarily a hostname, FQDN, '
                      'or IP address.'))]

keystone_group = cfg.OptGroup('keystone')
keystone_opts = [
    cfg.StrOpt('auth_url',
               default='',
               help=_('Unversioned keystone url in format like'
                      ' http://0.0.0.0:35357/v3')),
    cfg.StrOpt('password',
               default='',
               help=_('Privilidged user password'))]

gate_group = cfg.OptGroup('gate')
gate_opts = [
    cfg.StrOpt('user',
               help=_('Username to access gate VM')),
    cfg.StrOpt('image',
               help=_('Image name to deploy VM from')),
    cfg.StrOpt('flavor',
               help=_('Gate VM flavor')),
    cfg.StrOpt('security_groups',
               default='default',
               help=_('Image name to deploy VM from')),
    cfg.StrOpt('accessible_network',
               default='',
               help=_('Network accesible from outside'))]


def list_opts():
    yield paste_deploy_group.name, paste_deploy_opts
    yield keystone_group, keystone_opts
    yield gate_group, gate_opts
    yield None, default_client_opts

cfg.CONF.register_group(paste_deploy_group)
cfg.CONF.register_group(keystone_group)
cfg.CONF.register_group(gate_group)
profiler.set_defaults(cfg.CONF)

for group, opts in list_opts():
    cfg.CONF.register_opts(opts, group=group)


def _get_deployment_flavor():
    """Retrieves the paste_deploy.flavor config item.

    Item formatted appropriately for appending to the application name.
    """
    flavor = cfg.CONF.paste_deploy.flavor
    return '' if not flavor else ('-' + flavor)


def _get_deployment_config_file():
    """Retrieves the deployment_config_file config item.

    Item formatted as an absolute pathname.
    """
    config_path = cfg.CONF.find_file(
        cfg.CONF.paste_deploy['api_paste_config'])
    if config_path is None:
        return None

    return os.path.abspath(config_path)


def load_paste_app(app_name=None):
    """Builds and returns a WSGI app from a paste config file.

    We assume the last config file specified in the supplied ConfigOpts
    object is the paste config file.

    :param app_name: name of the application to load

    :raises RuntimeError when config file cannot be located or application
            cannot be loaded from config file
    """
    if app_name is None:
        app_name = cfg.CONF.prog

    # append the deployment flavor to the application name,
    # in order to identify the appropriate paste pipeline
    app_name += _get_deployment_flavor()

    conf_file = _get_deployment_config_file()
    if conf_file is None:
        raise RuntimeError(_("Unable to locate config file [%s]") %
                           cfg.CONF.paste_deploy['api_paste_config'])

    try:
        app = wsgi.paste_deploy_app(conf_file, app_name, cfg.CONF)

        # Log the options used when starting if we're in debug mode...
        if cfg.CONF.debug:
            cfg.CONF.log_opt_values(logging.getLogger(app_name),
                                    logging.DEBUG)

        return app
    except (LookupError, ImportError) as e:
        raise RuntimeError(_("Unable to load %(app_name)s from "
                             "configuration file %(conf_file)s."
                             "\nGot: %(e)r") % {'app_name': app_name,
                                                'conf_file': conf_file,
                                                'e': e})



def get_ssl_options(client):
    # Look for the ssl options in the [clients_${client}] section
    cacert = get_client_option(client, 'ca_file')
    insecure = get_client_option(client, 'insecure')
    cert = get_client_option(client, 'cert_file')
    key = get_client_option(client, 'key_file')
    if insecure:
        verify = False
    else:
        verify = cacert or True
    if cert and key:
        cert = (cert, key)
    return {'verify': verify, 'cert': cert}


def set_config_defaults():
    """This method updates all configuration default values."""
    # CORS Defaults
    # TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/
    cfg.set_defaults(cors.CORS_OPTS,
                     allow_headers=['X-Auth-Token',
                                    'X-Identity-Status',
                                    'X-Roles',
                                    'X-Service-Catalog',
                                    'X-User-Id',
                                    'X-Tenant-Id',
                                    'X-OpenStack-Request-ID'],
                     expose_headers=['X-Auth-Token',
                                     'X-Subject-Token',
                                     'X-Service-Token',
                                     'X-OpenStack-Request-ID'],
                     allow_methods=['GET',
                                    'PUT',
                                    'POST',
                                    'DELETE',
                                    'PATCH']
                     )
