#!/usr/bin/env python
#
#    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.

import os
import sys

import keystoneclient.exceptions as kc_exception
from keystoneclient.v3 import client
from oslo_config import cfg
from oslo_log import log as logging

from heat import version

logger = logging.getLogger(__name__)

DEBUG = False
USERNAME = os.environ.get('OS_USERNAME')
PASSWORD = os.environ.get('OS_PASSWORD')
AUTH_URL = os.environ.get('OS_AUTH_URL', '').replace('v2.0', 'v3')
TENANT_NAME = os.environ.get('OS_TENANT_NAME')

opts = [
    cfg.StrOpt('stack-user-domain-name',
               default="heat",
               help="Name of domain to create for stack users."),
    cfg.StrOpt('stack-domain-admin',
               default="heat_stack_admin",
               help="Keystone username with roles sufficient to manage users"
                    " and projects in the stack-user-domain"),
    cfg.StrOpt('stack-domain-admin-password',
               secret=True,
               help="Password to set for stack-domain-admin"),
    cfg.BoolOpt('insecure',
                default=False,
                help="If set, then the server's certificate will not "
                     "be verified."),
    cfg.StrOpt('os-cacert',
               help='Optional CA cert file to use in SSL connections.'),
    cfg.StrOpt('os-cert',
               help='Optional PEM-formatted certificate chain file.'),
    cfg.StrOpt('os-key',
               help='Optional PEM-formatted file that contains the '
                    'private key.'),

]

# Setup logging before registering additional options
logging.register_options(cfg.CONF)
extra_log_level_defaults = ['urllib3.connectionpool=WARNING']
logging.set_defaults(
    logging_context_format_string="%(levelname)s (%(module)s:"
                                  "%(lineno)d) %(message)s",
    default_log_levels=(logging.get_default_log_levels() +
                       extra_log_level_defaults))
logging.setup(cfg.CONF, 'heat-keystone-setup-domain',
              version.version_info.version_string())

# Register additional options
cfg.CONF.register_cli_opts(opts)
cfg.CONF(sys.argv[1:],
         project='heat',
         prog='heat-keystone-setup-domain',
         version=version.version_info.version_string())

HEAT_DOMAIN_NAME = os.environ.get(
    'HEAT_DOMAIN', cfg.CONF.stack_user_domain_name)
HEAT_DOMAIN_ADMIN = os.environ.get('HEAT_DOMAIN_ADMIN',
                                   cfg.CONF.stack_domain_admin)
HEAT_DOMAIN_PASSWORD = os.environ.get('HEAT_DOMAIN_PASSWORD',
                                      cfg.CONF.stack_domain_admin_password)
HEAT_DOMAIN_DESCRIPTION = 'Contains users and projects created by heat'

logger.debug("USERNAME=%s" % USERNAME)
logger.debug("AUTH_URL=%s" % AUTH_URL)

CACERT = os.environ.get('OS_CACERT', cfg.CONF.os_cacert)
CERT = os.environ.get('OS_CERT', cfg.CONF.os_cert)
KEY = os.environ.get('OS_KEY', cfg.CONF.os_key)
insecure = cfg.CONF.insecure


def main():
    client_kwargs = {
        'debug': DEBUG,
        'username': USERNAME,
        'password': PASSWORD,
        'auth_url': AUTH_URL,
        'endpoint': AUTH_URL,
        'tenant_name': TENANT_NAME
    }

    if insecure:
        client_kwargs['verify'] = False
    else:
        client_kwargs.update({
            'cacert': CACERT,
            'cert': CERT,
            'key': KEY
        })

    c = client.Client(**client_kwargs)
    c.authenticate()

    # Create the heat domain
    logger.info("Creating domain %s" % HEAT_DOMAIN_NAME)
    try:
        heat_domain = c.domains.create(name=HEAT_DOMAIN_NAME,
                                       description=HEAT_DOMAIN_DESCRIPTION)
    except kc_exception.Conflict:
        logger.warning("Domain %s already exists" % HEAT_DOMAIN_NAME)
        heat_domain = c.domains.list(name=HEAT_DOMAIN_NAME)[0]
        if heat_domain.name != HEAT_DOMAIN_NAME:
            logger.error("Unexpected filtered list response, please upgrade "
                         "keystoneclient to >= 0.5")
            sys.exit(1)
    except kc_exception.Forbidden:
        logger.error("User '%s' is not authorized to perform this "
                     "operation, please try with other OS_USERNAME setting." %
                     USERNAME)
        sys.exit(1)

    # Create heat domain admin user
    if not HEAT_DOMAIN_PASSWORD:
        logger.error("Must export HEAT_DOMAIN_PASSWORD or use"
                     " --stack-domain-admin-password")
        sys.exit(1)

    try:
        domain_admin = c.users.create(name=HEAT_DOMAIN_ADMIN,
                                      password=HEAT_DOMAIN_PASSWORD,
                                      domain=heat_domain,
                                      description="Heat domain admin")
    except kc_exception.Conflict:
        logger.warning("User %s already exists" % HEAT_DOMAIN_ADMIN)
        domain_admin = c.users.list(name=HEAT_DOMAIN_ADMIN,
                                    domain=heat_domain)[0]

    # Make the user a domain admin
    roles_list = c.roles.list()
    # FIXME(shardy): seems filtering roles by name currently doesn't work
    admin_role = [r for r in roles_list
                  if r.name == 'admin'][0]
    c.roles.grant(role=admin_role, user=domain_admin, domain=heat_domain)

    print("\nPlease update your heat.conf with the following in [DEFAULT]\n")
    print("stack_user_domain_id=%s" % heat_domain.id)
    print("stack_domain_admin=%s" % HEAT_DOMAIN_ADMIN)
    print("stack_domain_admin_password=%s" % HEAT_DOMAIN_PASSWORD)


if __name__ == "__main__":
    main()
