class Command(object):
    """Base class for commands
    """
    description = ""
    """Description of the command"""
    aliases = []
    """Command aliases"""
    _options = None
    _args = None

    def __init__(self, name):
        self.parser = ArgumentParser(prog=name, description=self.description)
        self.add_arguments_to_parser(self.parser)
        self._is_piped = False

    def current_path(self, resource):
        """Return current path for resource

        :param resource: resource or collection
        :type resource: Resource|Collection

        :rtype: str
        """
        return text_type(resource.path.relative_to(Context().shell.current_path))

    @property
    def is_piped(self):
        """Return True if the command result is beeing piped
        to another command.

        :rtype: bool
        """
        return not sys.stdout.isatty() or self._is_piped

    @is_piped.setter
    def is_piped(self, value):
        self._is_piped = value

    @classproperty
    def options(cls):
        if cls._options is not None:
            return cls._options
        cls._options = OrderedDict()
        for attr, option in sorted(
                inspect.getmembers(cls, lambda o: isinstance(o, Option)),
                key=lambda i: i[1]._creation_idx):
            option.attr = attr
            cls._options[text_type(attr)] = option
        return cls._options

    @classproperty
    def args(cls):
        if cls._args is not None:
            return cls._args
        cls._args = OrderedDict()
        for attr, arg in sorted(
                inspect.getmembers(cls, lambda o: isinstance(o, Arg)),
                key=lambda i: i[1]._creation_idx):
            arg.attr = attr
            cls._args[text_type(attr)] = arg
        return cls._args

    @classmethod
    def add_arguments_to_parser(cls, parser):
        for (arg_name, arg) in cls.args.items():
            parser.add_argument(arg_name, **arg.kwargs)
        for (option_name, option) in cls.options.items():
            parser.add_argument(*option.option_strings, **option.kwargs)

    def parse_and_call(self, *args):
        args = self.parser.parse_args(args=args)
        return self.__call__(**vars(args))

    @abc.abstractmethod
    def __call__(self, **kwargs):
        """Command must implement this method.

        The command must return an unicode string
        (unicode in python2 or str in python3)

        :param kwargs: options of the command

        :rtype: unicode | str
        """ class Arg(BaseOption):
    pass class Option(BaseOption):

    def __init__(self, short_name=None, **kwargs):
        BaseOption.__init__(self, **kwargs)
        self.short_name = short_name

    @property
    def need_value(self):
        return self.kwargs.get('action') not in ('store_true', 'store_false')

    @property
    def long_name(self):
        return '--%s' % self.attr.replace('_', '-')

    @property
    def option_strings(self):
        return [n for n in (self.long_name, self.short_name) if n is not None] 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 SchemaVersionNotAvailable(SchemaError):
    def __init__(self, version):
        self.version = version
        msg = "Schema version %s is not available" % self.version
        Exception.__init__(self, msg) class ResourceNotDefined(SchemaError):
    def __init__(self, resource_name):
        self.resource_name = resource_name
        msg = "Resource '%s' is not defined in the schema" % self.resource_name
        Exception.__init__(self, msg) def get_last_schema_version():
    """Return the last available schema version. Version are
    lexicographically sorted. If no version is available, return None.

    :rtype: str or None
    """
    versions = list_available_schema_version()
    if len(versions) > 0:
        return sorted(versions)[-1]
    else:
        return None def format_tree(tree):
    """Format a python tree structure

    Given the python tree::

        tree = {
            'node': ['ROOT', 'This is the root of the tree'],
            'childs': [{
                'node': 'A1',
                'childs': [{
                    'node': 'B1',
                    'childs': [{
                        'node': 'C1'
                    }]
                },
                {
                    'node': 'B2'
                }]
            },
            {
                'node': 'A2',
                'childs': [{
                    'node': 'B3',
                    'childs': [{
                        'node': ['C2', 'This is a leaf']
                    },
                    {
                        'node': 'C3'
                    }]
                }]
            },
            {
                'node': ['A3', 'This is a node'],
                'childs': [{
                    'node': 'B2'
                }]
            }]
        }

    `format_tree` will return::

        ROOT            This is the root of the tree
        ├── A1
        │   ├── B1
        │   │   └── C1
        │   └── B2
        ├── A2
        │   └── B3
        │       ├── C2  This is a leaf
        │       └── C3
        └── A3          This is a node
            └── B2

    """

    def _traverse_tree(tree, parents=None):
        tree['parents'] = parents
        childs = tree.get('childs', [])
        nb_childs = len(childs)
        for index, child in enumerate(childs):
            child_parents = list(parents) + [index == nb_childs - 1]
            tree['childs'][index] = _traverse_tree(
                tree['childs'][index],
                parents=child_parents)
        return tree

    tree = _traverse_tree(tree, parents=[])

    def _get_rows_data(tree, rows):
        prefix = ''
        for p in tree['parents'][:-1]:
            if p is False:
                prefix += '│   '
            else:
                prefix += '    '
        if not tree['parents']:
            pass
        elif tree['parents'][-1] is True:
            prefix += '└── '
        else:
            prefix += '├── '
        if isinstance(tree['node'], string_types):
            tree['node'] = [tree['node']]
        rows.append([prefix + tree['node'][0]] + tree['node'][1:])
        for child in tree.get('childs', []):
            rows = _get_rows_data(child, rows)
        return rows

    rows = _get_rows_data(tree, [])
    return format_table(rows) class CommandError(Exception):
    pass
from six import string_types, text_type
from ..command import Command, Arg, Option
from ..schema import create_schema_from_version, list_available_schema_version, SchemaVersionNotAvailable, ResourceNotDefined, get_last_schema_version
from ..utils import format_tree
from ..exceptions import CommandError
# -*- coding: utf-8 -*-
from __future__ import unicode_literals



class Schema(Command):
    """View resources links using contrail schema definition.

    .. code-block:: bash

        admin@localhost:/> schema -v 2.21 virtual-network
        virtual-network
        ├── parent
        │   └── project
        ├── children
        │   ├── access-control-list
        │   ├── routing-instance
        │   ├── alias-ip-pool
        │   └── floating-ip-pool
        ├── refs
        │   ├── route-table
        │   ├── network-policy
        │   ├── qos-config
        │   └── network-ipam
        ├── back_refs
        │   ├── instance-ip
        │   ├── physical-router
        │   ├── logical-router
        │   └── virtual-machine-interface
        └── properties
            ├── import-route-target-list
            ├── export-route-target-list
            ├── provider-properties
            ├── multi-policy-service-chains-enabled
            ├── flood-unknown-unicast
            ├── virtual-network-network-id
            ├── router-external
            ├── virtual-network-properties
            ├── route-target-list
            ├── is-shared
            ├── external-ipam
            ├── id-perms
            ├── perms2
            └── display-name
    """
    description = "Explore schema resources"
    schema_version = Option('-v',
                            type=str,
                            default=get_last_schema_version(),

                            choices=list_available_schema_version(),