Source code for reproman.interface.ls

# -*- coding: utf-8 -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the reproman package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Helper utility to list available environments
"""

__docformat__ = 'restructuredtext'

from collections import OrderedDict

from .base import Interface
from .common_opts import resref_type_opt
# import reproman.interface.base  # Needed for test patching
from ..support.param import Parameter
from ..resource import get_manager
from ..ui import ui
from ..support.exceptions import ResourceError
from ..support.exceptions import ResourceNotFoundError
from ..dochelpers import exc_str

from logging import getLogger
lgr = getLogger('reproman.api.ls')


class Ls(Interface):
    """List known computation resources, images and environments

    Examples
    --------

      $ reproman ls
    """

    _params_ = dict(
        verbose=Parameter(
            args=("-v", "--verbose"),
            action="store_true",
            doc="provide more verbose listing",
        ),
        refresh=Parameter(
            args=("-r", "--refresh",),
            action="store_true",
            doc="Refresh the status of the resources listed",
        ),
        resrefs=Parameter(
            args=("resrefs",),
            metavar="RESOURCE",
            nargs="*",
            doc="Restrict the output to this resource name or ID"
        ),
        resref_type=resref_type_opt,
    )

    @staticmethod
    def __call__(resrefs=None, resref_type="auto", verbose=False,
                 refresh=False):
        id_length = 19  # todo: make it possible to output them long
        template = '{:<20} {:<20} {:<%(id_length)s} {!s:<10}' % locals()
        ui.message(template.format('RESOURCE NAME', 'TYPE', 'ID', 'STATUS'))
        ui.message(template.format('-------------', '----', '--', '------'))

        results = OrderedDict()
        manager = get_manager()
        if not resrefs:
            resrefs = (manager.inventory[n]["id"] for n in sorted(manager)
                       if not n.startswith("_"))

        unknown_resrefs = []
        for resref in resrefs:
            try:
                resource = manager.get_resource(resref, resref_type)
                name = resource.name
            except ResourceNotFoundError as e:
                lgr.debug("Resource %s not found: %s", resref, exc_str(e))
                unknown_resrefs.append(resref)
                continue
            except ResourceError as e:
                lgr.warning("Manager did not return a resource for %s: %s",
                            resref, exc_str(e))
                continue

            if refresh:
                try:
                    resource.connect()
                    if not resource.id:
                        resource.status = 'NOT FOUND'
                except Exception as e:
                    lgr.debug("%s resource query error: %s", name, exc_str(e))
                    resource.status = 'CONNECTION ERROR'

                manager.inventory[name].update({'status': resource.status})

            id_ = manager.inventory[name]['id']
            msgargs = (
                name,
                resource.type,
                id_[:id_length],
                resource.status,
            )
            ui.message(template.format(*msgargs))
            results[id_] = msgargs

        if refresh:
            manager.save_inventory()
        else:
            ui.message('Use --refresh option to view updated status.')

        if unknown_resrefs:
            raise ResourceNotFoundError(
                "Could not find the following resources: {}"
                .format(", ".join(unknown_resrefs)))
        return results