CONFIG_DIR = os.path.expanduser('~/.config/contrail-api-cli') def printo(msg, encoding=None, errors='replace', std_type='stdout'):
    """Write msg on stdout. If no encoding is specified
    the detected encoding of stdout is used. If the encoding
    can't encode some chars they are replaced by '?'

    :param msg: message
    :type msg: unicode on python2 | str on python3
    """
    std = getattr(sys, std_type, sys.stdout)
    if encoding is None:
        try:
            encoding = std.encoding
        except AttributeError:
            encoding = None
    # Fallback to ascii if no encoding is found
    if encoding is None:
        encoding = 'ascii'
    # https://docs.python.org/3/library/sys.html#sys.stdout
    # write in the binary buffer directly in python3
    if hasattr(std, 'buffer'):
        std = std.buffer
    std.write(msg.encode(encoding, errors=errors))
    std.write(b'\n')
    std.flush() class CommandManager(object):

    def __init__(self, load_default=True):
        """Load commands from namespace

        :param load_default: tell the manager to load the default
                             namespace for commands (contrail_api_cli.command).
                             (True by default)
        :type load_default: bool
        """
        self.mgrs = []
        if load_default is True:
            self.load_namespace('contrail_api_cli.command')

    def load_namespace(self, ns):
        """Load commands from namespace.

        :param ns: namespace name
        :type ns: str
        """
        mgr = extension.ExtensionManager(namespace=ns,
                                         verify_requirements=True,
                                         on_load_failure_callback=self._on_failure)
        # Load commands from ns
        for ext in mgr.extensions:
            try:
                obj = ext.plugin(ext.name)
            except Exception as err:
                self._on_failure(self, ext, err)
                obj = None
            ext.obj = obj

        self.mgrs.append(mgr)

    def unload_namespace(self, ns):
        self.mgrs = [mgr for mgr in self.mgrs if not mgr.namespace == ns]

    def _on_failure(self, mgr, entrypoint, exc):
        print('Cannot load command %s: %s' % (entrypoint.name,
                                              exc))

    def get(self, name):
        """Return command instance of loaded
        commands by name

        :param name: name of the command
        :type name: str
        """
        for cmd_name, cmd in self.list:
            if cmd_name == name:
                return cmd
        raise CommandNotFound('Command %s not found. Type help for all commands' % name)

    @property
    def extensions(self):
        return itertools.chain(*[mgr.extensions for mgr in self.mgrs])

    @property
    def list(self):
        """Generator of command instances

        :rtype: (name, Command)
        """
        for ext in self.extensions:
            # don't return ext that failed
            # to load earlier
            if ext.obj is None:
                continue
            yield (ext.name, ext.obj)

    def add(self, name, cmd):
        ext = extension.Extension(name, None, cmd.__class__, cmd)
        self.mgrs[0].extensions.append(ext)

    @classmethod
    def register_argparse_commands(cls, parser, argv):
        in_parser = argparse.ArgumentParser(add_help=False)
        for p in (parser, in_parser):
            p.add_argument('--ns',
                           help='Command namespaces to load (default: %(default)s)',
                           metavar='COMMAND_NAMESPACE',
                           action='append',
                           default=[])
        options, _ = in_parser.parse_known_args(argv)
        mgr = CommandManager()
        for ns in options.ns:
            mgr.load_namespace(ns)
        subparsers = parser.add_subparsers(dest='subcmd')
        for cmd_name, cmd in mgr.list:
            subparser = subparsers.add_parser(cmd_name, help=cmd.description)
            cmd.add_arguments_to_parser(subparser)
        return mgr

    def get_completions(self, word_before_cursor, context, option=None):
        for cmd_name, cmd in self.list:
            if cmd_name.startswith(word_before_cursor):
                yield Completion(cmd_name,
                                 -len(word_before_cursor),
                                 display_meta=cmd.description) class CommandError(Exception):
    pass class NotFound(GreenletExit):

    def __init__(self, resource=None, collection=None):
        super(GreenletExit, self).__init__()
        self.r = resource
        self.c = collection

    def __str__(self):
        return "No resource found" class Exists(GreenletExit):

    def __init__(self, resources=None):
        self.resources = []
        if resources is not None:
            self.resources = resources

    @property
    def _paths(self):
        return ", ".join([text_type(c.path) for c in self.resources])

    def __repr__(self):
        return "%s(%s)" % (self.__class__.__name__,
                           ", ".join([repr(r) for r in self.resources])) def create_schema_from_version(version):
    """Provide a version of the schema to create it. Use
    list_available_schema_version to discover available versions.

    """
    schema_directory = _get_schema_version_path(version)
    return create_schema_from_xsd_directory(schema_directory, version) def list_available_schema_version():
    """To discover available schema versions."""
    return listdir(default_schemas_directory_path) class DummySchema(object):

    def __init__(self):
        DummyResourceSchema()

    @property
    def version(self):
        return "dummy"

    def resource(self, resource_name):
        if resource_name not in self.all_resources():
            raise ResourceNotDefined(resource_name)
        return DummyResourceSchema()

    def all_resources(self):
        return DummyResourceSchema().children class SchemaError(Exception):
    pass class Context(object):
    _schema = None
    _shell = ShellContext
    _session = None

    @property
    def schema(self):
        if self._schema is None:
            raise SchemaNotInitialized("The schema must be first initialized")
        else:
            return self._schema

    @schema.setter
    def schema(self, schema):
        self._schema = schema

    @property
    def session(self):
        if self._session is None:
            raise SessionNotInitialized("The session must be first initialized")
        else:
            return self._session

    @session.setter
    def session(self, session):
        self._session = session

    @property
    def shell(self):
        return self._shell
from .utils import CONFIG_DIR, printo
from six import text_type
from keystoneauth1.loading import cli
from keystoneauth1.exceptions.http import HttpError, HTTPClientError
from .manager import CommandManager
from .exceptions import CommandError, NotFound, Exists
from .schema import create_schema_from_version, list_available_schema_version, DummySchema, SchemaError
from .context import Context
from . import client
import os
import sys
import argparse
import logging
import logging.config
from __future__ import unicode_literals






logger = logging.getLogger(__name__)


def get_subcommand_kwargs(mgr, name, namespace):
    """Get subcommand options from global parsed
    arguments.
    """
    subcmd = mgr.get(name)
    subcmd_kwargs = {}
    for opt in list(subcmd.args.values()) + list(subcmd.options.values()):
        if hasattr(namespace, opt.dest):
            subcmd_kwargs[opt.dest] = getattr(namespace, opt.dest)
    return (subcmd, subcmd_kwargs)


def main():
    argv = sys.argv[1:]

    # early setup for logging
    if '-d' in argv or '--debug' in argv:
        logging.basicConfig(level=logging.DEBUG)
    if '--logging-conf' in argv:
        try:
            path = argv[argv.index('--logging-conf') + 1]
            logging.config.fileConfig(path)
        except IndexError:
            pass

    parser = argparse.ArgumentParser()
    parser.add_argument('--debug', '-d',
                        action="store_true", default=False)
    parser.add_argument('--schema-version',
                        default=os.environ.get('CONTRAIL_API_VERSION', None),
                        choices=list_available_schema_version(),
                        help="schema version used by contrail-api server (default=%(default)s)")
    parser.add_argument('--logging-conf',
                        help="python logging configuration file")
    parser.add_argument('--config-dir',
                        help="path of configuration directory (default=%(default)s)",
                        default=os.environ.get('CONTRAIL_API_CLI_CONFIG_DIR', CONFIG_DIR))

    # contrail api session options
    client.register_argparse_arguments(parser)
    # Default auth plugin will be http unless OS_AUTH_PLUGIN envvar is set
    cli.register_argparse_arguments(parser, argv, default="http")
    # Add commands to the parser given the namespaces list
    mgr = CommandManager.register_argparse_commands(parser, argv)

    options = parser.parse_args()

    if not os.path.exists(options.config_dir):
        os.makedirs(options.config_dir)

    Context().session = client.load_from_argparse_arguments(options)

    if options.schema_version:
        Context().schema = create_schema_from_version(options.schema_version)
    else:
        Context().schema = DummySchema()

    try:
        subcmd, subcmd_kwargs = get_subcommand_kwargs(mgr, options.subcmd, options)
        logger.debug('Calling %s with %s' % (subcmd, subcmd_kwargs))
        result = subcmd(**subcmd_kwargs)

    except (HTTPClientError, HttpError, CommandError, SchemaError, Exists, NotFound) as e: