#!/usr/bin/env python
# common.py - Common bits for SDK examples

# 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.

"""
SDK Examples Common

This is a collection of common functions used by the example scripts.

common.object_parser() provides the common set of command-line arguments
used in the library CLIs for setting up authentication.  This should make
playing with the example scripts against a running OpenStack simpler.

Typical environment variables to set and export for authentication include:

    OS_PROJECT_NAME=FooFighter
    OS_PASSWORD=nirvana
    OS_AUTH_URL=https://foofighters.com:35357/v3
    OS_USERNAME=davegrohl
    OS_REGION_NAME=Seattle

"""

import argparse
import logging
import os
import subprocess
import sys
import traceback
import uuid

from openstack import user_preference
from openstack import utils

_logger = logging.getLogger(__name__)


def find_resource_cls(opts):
    argument = opts.argument
    if argument.find('/') > 0:
        # called with file e.g.: openstack/network/v2/network.py
        args = argument.split('/')
        args[-1] = args[-1].replace('.py', '')
        from_str = '.'.join(args)
        class_str = args[-1].title()
        class_str = class_str.replace('_', '')
    else:
        # called with path e.g.: openstack.network.v2.network.Network
        args = argument.rpartition('.')
        from_str = args[0]
        class_str = args[2]
    __import__(from_str)
    mod = sys.modules[from_str]
    return getattr(mod, class_str)


def get_data_option(opts):
    if not opts.data:
        return opts.data
    try:
        iddy = uuid.UUID(opts.data)
        return {'id': iddy}
    except ValueError:
        data = opts.data
        if data.startswith('openstack.'):
            fullname = data.split('(')[0]
            classname = fullname.split('.')[-1]
            modulename = fullname.replace('.' + classname, '')
            data = data.replace('openstack.',
                                '__import__("' + modulename + '").')
        return eval(data)


def get_open_fds():
    '''Return the open file descriptors for current process

    .. warning: will only work on UNIX-like os-es.
    '''
    pid = os.getpid()
    procs = subprocess.check_output(
        ["lsof", '-w', '-Fftn0', "-p", str(pid)]
    )
    print('procs: %s' % procs)
    print('netstat: %s' % subprocess.check_output(['netstat', '-nlt']))
    procs_list = filter(
        lambda s: s and s[0] == 'f' and s[1].isdigit(),
        procs.split('\n')
    )
    return [d.replace('\000', '|') for d in procs_list]


class UserPreferenceAction(argparse.Action):
    """A custom action to parse user preferences as key=value pairs

    Stores results in users preferences object.
    """
    pref = user_preference.UserPreference()

    @classmethod
    def env(cls, *vars):
        for v in vars:
            values = os.environ.get(v, None)
            if values is None:
                continue
            cls.set_option(v, values)
            return cls.pref
        return cls.pref

    @classmethod
    def set_option(cls, var, values):
        if var == 'OS_REGION_NAME':
            var = 'region'
        var = var.replace('--os-api-', '')
        var = var.replace('OS_API_', '')
        var = var.lower()
        for kvp in values.split(','):
            if '=' in kvp:
                service, value = kvp.split('=')
            else:
                service = cls.pref.ALL
                value = kvp
            if var == 'name':
                cls.pref.set_name(service, value)
            elif var == 'region':
                cls.pref.set_region(service, value)
            elif var == 'version':
                cls.pref.set_version(service, value)
            elif var == 'visibility':
                cls.pref.set_visibility(service, value)

    def __call__(self, parser, namespace, values, option_string=None):
        if getattr(namespace, self.dest, None) is None:
            setattr(namespace, self.dest, UserPreferenceAction.pref)
        self.set_option(option_string, values)


def env(*vars, **kwargs):
    """Search for the first defined of possibly many env vars

    Returns the first environment variable defined in vars, or
    returns the default defined in kwargs.

    """
    for v in vars:
        value = os.environ.get(v, None)
        if value:
            return value
    return kwargs.get('default', '')


def option_parser():
    """Set up some of the common CLI options

    These are the basic options that match the library CLIs so
    command-line/environment setups for those also work with these
    demonstration programs.

    """

    parser = argparse.ArgumentParser(
        description='A demonstration framework')
    # Global arguments
    parser.add_argument(
        '--os-auth-plugin',
        dest='auth_plugin',
        metavar='<auth-plugin>',
        default=env('OS_AUTH_PLUGIN', default=None),
        help='Authentication plugin (Env: OS_AUTH_PLUGIN)',
    )
    parser.add_argument(
        '--os-auth-url',
        dest='auth_url',
        metavar='<auth-url>',
        default=env('OS_AUTH_URL'),
        help='Authentication URL (Env: OS_AUTH_URL)',
    )
    parser.add_argument(
        '--os-project-name',
        dest='project_name',
        metavar='<auth-project-name>',
        default=env('OS_PROJECT_NAME', default=env('OS_TENANT_NAME')),
        help='Project name of the requested project-level'
             'authorization scope (Env: OS_PROJECT_NAME)',
    )
    parser.add_argument(
        '--os-domain-name',
        dest='domain_name',
        metavar='<auth-domain-name>',
        default=env('OS_DOMAIN_NAME'),
        help='Domain name for scope of '
             'authorization (Env: OS_DOMAIN_NAME)',
    )
    parser.add_argument(
        '--os-project-domain-name',
        dest='project_domain_name',
        metavar='<auth-project-domain-name>',
        default=env('OS_PROJECT_DOMAIN_NAME'),
        help='Project domain name for scope of '
             'authorization (Env: OS_PROJECT_DOMAIN_NAME)',
    )
    parser.add_argument(
        '--os-user-domain-name',
        dest='user_domain_name',
        metavar='<auth-user-domain-name>',
        default=env('OS_USER_DOMAIN_NAME'),
        help='User domain name for scope of '
             'authorization (Env: OS_USER_DOMAIN_NAME)',
    )
    parser.add_argument(
        '--os-username',
        dest='username',
        metavar='<auth-username>',
        default=env('OS_USERNAME'),
        help='Authentication username (Env: OS_USERNAME)',
    )
    parser.add_argument(
        '--os-password',
        dest='password',
        metavar='<auth-password>',
        default=env('OS_PASSWORD'),
        help='Authentication password (Env: OS_PASSWORD)',
    )
    parser.add_argument(
        '--os-access-info',
        dest='access_info',
        metavar='<access-info>',
        default=env('OS_ACCESS_INFO'),
        help='Access info (Env: OS_ACCESS_INFO)',
    )
    parser.add_argument(
        '--os-api-name',
        dest='user_preferences',
        metavar='<service>=<name>',
        action=UserPreferenceAction,
        default=UserPreferenceAction.env('OS_API_NAME'),
        help='Desired API names defaults to env[OS_API_NAME]',
    )
    parser.add_argument(
        '--os-api-region',
        dest='user_preferences',
        metavar='<service>=<region>',
        action=UserPreferenceAction,
        default=UserPreferenceAction.env('OS_API_REGION', 'OS_REGION_NAME'),
        help='Desired API region defaults to env[OS_API_REGION]',
    )
    parser.add_argument(
        '--os-api-version',
        dest='user_preferences',
        metavar='<service>=<version>',
        action=UserPreferenceAction,
        default=UserPreferenceAction.env('OS_API_VERSION'),
        help='Desired API versions defaults to env[OS_API_VERSION]',
    )
    parser.add_argument(
        '--os-api-visibility',
        dest='user_preferences',
        metavar='<service>=<visibility>',
        action=UserPreferenceAction,
        default=UserPreferenceAction.env('OS_API_VISIBILITY'),
        help='Desired API visibility defaults to env[OS_API_VISIBILITY]',
    )
    verify_group = parser.add_mutually_exclusive_group()
    verify_group.add_argument(
        '--os-cacert',
        dest='verify',
        metavar='<ca-bundle-file>',
        default=env('OS_CACERT', default=True),
        help='CA certificate bundle file (Env: OS_CACERT)',
    )
    verify_group.add_argument(
        '--verify',
        action='store_true',
        help='Verify server certificate (default)',
    )
    verify_group.add_argument(
        '--insecure',
        dest='verify',
        action='store_false',
        help='Disable server certificate verification',
    )
    parser.add_argument(
        '--os-token',
        dest='token',
        metavar='<token>',
        default=env('OS_TOKEN', default=None),
        help='Defaults to env[OS_TOKEN]',
    )
    parser.add_argument(
        '--os-trust-id',
        dest='trust_id',
        metavar='<trust_id>',
        default=env('OS_TRUST_ID', default=None),
        help='Defaults to env[OS_TRUST_ID]',
    )
    parser.add_argument(
        '--data',
        metavar='<data>',
        default={},
        help='Json data for command.',
    )
    parser.add_argument(
        '-v', '--verbose',
        action='count',
        dest='verbose_level',
        default=1,
        help='Increase verbosity of output. Can be repeated.',
    )
    parser.add_argument(
        '--debug',
        default=False,
        action='store_true',
        help='show tracebacks on errors',
    )
    parser.add_argument(
        'argument',
        default=None,
        nargs='?',
        help='Argument to use.',
    )
    return parser


def setup():
    opts = option_parser().parse_args()
    utils.enable_logging(opts.debug)
    return opts


def main(opts, run):
    try:
        return run(opts)
    except Exception as e:
        if opts.debug:
            _logger.error(traceback.format_exc(e))
        else:
            _logger.error('Exception raised: ' + str(e))
        return 1