"""VirshListAll - command ``virsh --readonly list --all``
=========================================================

This module provides VM status using output of command ``virsh --readonly list --all``.
"""
from collections import namedtuple

from insights.specs import Specs
from .. import CommandParser, parser
from . import parse_fixed_table, keyword_search


@parser(Specs.virsh_list_all)
class VirshListAll(CommandParser):
    """Parsing output of ``virsh --readonly list --all``.

    Typical output of ``virsh --readonly list --all`` command is::

        Id    Name                           State
        ----------------------------------------------------
        2     rhel7.4                        running
        4     rhel7.0                        paused
        -     centos6.8-router               shut off
        -     cfme-5.7.13                    shut off
        -     cfme-rhos-5.9.0.15             shut off
        -     fedora-24-kernel               shut off
        -     fedora-saio_fedoraSaio         shut off
        -     fedora24-misc                  shut off
        -     freebsd11.0                    shut off
        -     guixSD                         shut off
        -     miq-gap-1                      shut off
        -     rhel7.2                        shut off
        -     RHOSP10                        shut off


    Examples:

        >>> len(output.search(state='shut off')) == 11
        True
        >>> len(output.search(id=None)) == 11
        True
        >>> len(output.search(id=2)) == 1
        True
        >>> output.search(name='rhel7.4') == [{'state': 'running', 'id': 2, 'name': 'rhel7.4'}]
        True
        >>> output.get_vm_state('rhel7.0') == 'paused'
        True
        >>> output.get_vm_state('rhel9.0') is None
        True
        >>> 'cfme' in output
        False
        >>> 'cfme-5.7.13' in output
        True

    Attributes:
        fields (list): List of ``KeyValue`` namedtupules for each line
                       in the command.

        cols (list): List id key value pair derived from the command.

        keywords (list): keywords present in the command, each
                        keyword is converted to lowercase.

    """
    keyvalue = namedtuple('KeyValue',
                          ['name', 'state', 'id', 'name_lower'])
    """namedtuple: Represent name value pair as a namedtuple with case."""
    def _cleanup(self):
        for col in self.cols:
            if col['id'] == '-':
                col['id'] = None
            else:
                col['id'] = (lambda x: int(x) if x.isdigit() else x)(col['id'])

    def parse_content(self, content):
        self.fields = []
        self.cols = []
        self.keywords = []
        if not content:
            return

        self.cols = parse_fixed_table(content,
                                      heading_ignore=['Id', 'Name', 'State'],
                                      header_substitute=[('Id', 'id'), ('Name', 'name'), ('State', 'state')])[1:]  # noqa
        self._cleanup()

        for item in self.cols:
            self.fields.append(self.keyvalue(item['name'], item['state'], item['id'], item['name'].lower()))  # noqa
        self.keywords = [name.name_lower for name in self.fields]

    def __contains__(self, keyword):
        return keyword.lower() in self.keywords

    def __iter__(self):
        return iter(self.fields)

    def search(self, **kw):
        '''Search item based on key value pair.

        Example:

            >>> len(output.search(state='shut off')) == 11
            True
            >>> len(output.search(id=None)) == 11
            True
            >>> len(output.search(id=2)) == 1
            True
        '''
        return keyword_search(self.cols, **kw)

    def get_vm_state(self, vmname):
        '''Get VM state associated with vmname

        Typical output is ``virsh --readonly list --all`` command::

             Id    Name                           State
            ----------------------------------------------------
             2     rhel7.4                        running
             4     rhel7.0                        paused


        Example:

            >>> output.get_vm_state('rhel7.0')
            'paused'

        Args:

            vmname (str): A key. For ex. ``rhel7.0``.

        Returns:

            str: State of VM. Returns None if, ``vmname`` does not exist.
        '''
        if vmname.lower() in self.keywords:
            return self.search(name=vmname)[0]['state']
        return None

insights/parsers/virsh_list_all.py
"""VirshListAll - command ``virsh --readonly list --all``
=========================================================

This module provides VM status using output of command ``virsh --readonly list --all``.
"""
from collections import namedtuple

from insights.specs import Specs
from .. import CommandParser, parser
from . import parse_fixed_table, keyword_search


@parser(Specs.virsh_list_all)
class VirshListAll(CommandParser):
    """Parsing output of ``virsh --readonly list --all``.

    Typical output of ``virsh --readonly list --all`` command is::

        Id    Name                           State
        ----------------------------------------------------
        2     rhel7.4                        running
        4     rhel7.0                        paused
        -     centos6.8-router               shut off
        -     cfme-5.7.13                    shut off
        -     cfme-rhos-5.9.0.15             shut off
        -     fedora-24-kernel               shut off
        -     fedora-saio_fedoraSaio         shut off
        -     fedora24-misc                  shut off
        -     freebsd11.0                    shut off
        -     guixSD                         shut off
        -     miq-gap-1                      shut off
        -     rhel7.2                        shut off
        -     RHOSP10                        shut off


    Examples:

        >>> len(output.search(state='shut off')) == 11
        True
        >>> len(output.search(id=None)) == 11
        True
        >>> len(output.search(id=2)) == 1
        True
        >>> output.search(name='rhel7.4') == [{'state': 'running', 'id': 2, 'name': 'rhel7.4'}]
        True
        >>> output.get_vm_state('rhel7.0') == 'paused'
        True
        >>> output.get_vm_state('rhel9.0') is None
        True
        >>> 'cfme' in output
        False
        >>> 'cfme-5.7.13' in output
        True

    Attributes:
        fields (list): List of ``KeyValue`` namedtupules for each line
                       in the command.

        cols (list): List id key value pair derived from the command.

        keywords (list): keywords present in the command, each
                        keyword is converted to lowercase.

    """
    keyvalue = namedtuple('KeyValue',
                          ['name', 'state', 'id', 'name_lower'])
    """namedtuple: Represent name value pair as a namedtuple with case."""
    def _cleanup(self):
        for col in self.cols:
            if col['id'] == '-':
                col['id'] = None
            else:
                col['id'] = (lambda x: int(x) if x.isdigit() else x)(col['id'])

    def parse_content(self, content):
        self.fields = []
        self.cols = []
        self.keywords = []
        # Check and remove any error message, or empty lines. This to
        # prevent any ValueError exceptions when parse_fixed_table is
        # called below.
        content = [l for l in content if not l.startswith("error: ") and l != ""]
        if not content:
            return

        self.cols = parse_fixed_table(content,
                                      heading_ignore=['Id', 'Name', 'State'],
                                      header_substitute=[('Id', 'id'), ('Name', 'name'), ('State', 'state')])[1:]  # noqa
        self._cleanup()

        for item in self.cols:
            self.fields.append(self.keyvalue(item['name'], item['state'], item['id'], item['name'].lower()))  # noqa
        self.keywords = [name.name_lower for name in self.fields]

    def __contains__(self, keyword):
        return keyword.lower() in self.keywords

    def __iter__(self):
        return iter(self.fields)

    def search(self, **kw):
        '''Search item based on key value pair.

        Example:

            >>> len(output.search(state='shut off')) == 11
            True
            >>> len(output.search(id=None)) == 11
            True
            >>> len(output.search(id=2)) == 1
            True
        '''
        return keyword_search(self.cols, **kw)

    def get_vm_state(self, vmname):
        '''Get VM state associated with vmname

        Typical output is ``virsh --readonly list --all`` command::

             Id    Name                           State
            ----------------------------------------------------
             2     rhel7.4                        running
             4     rhel7.0                        paused


        Example:

            >>> output.get_vm_state('rhel7.0')
            'paused'

        Args:

            vmname (str): A key. For ex. ``rhel7.0``.

        Returns:

            str: State of VM. Returns None if, ``vmname`` does not exist.
        '''
        if vmname.lower() in self.keywords:
            return self.search(name=vmname)[0]['state']
        return None

insights/parsers/virsh_list_all.py
diff --git a/insights/parsers/tests/test_virsh_list_all.py b/insights/parsers/tests/test_virsh_list_all.py
index 12b83397e1..060b85e1ab 100644
--- a/insights/parsers/tests/test_virsh_list_all.py
+++ b/insights/parsers/tests/test_virsh_list_all.py
@@ -7,6 +7,12 @@
 BLANK = """
 """.strip()
 
+ERROR = """
+error: Failed to reconnect to the hypervisor
+error: no valid connection
+error: internal error Unable to locate libvirtd daemon in /usr/sbin (to override, set $LIBVIRTD_PATH to the name of the libvirtd binary)
+""".strip()
+
 NO_RESULT = """
  Id    Name                           State
 ----------------------------------------------------
@@ -31,6 +37,13 @@
 """.strip()
 
 
+def assert_if_none(output):
+    assert output.fields == []
+    assert output.cols == []
+    assert output.keywords == []
+    assert output.get_vm_state('NORHEL') is None
+
+
 def test_virsh_output():
     output = virsh_list_all.VirshListAll(context_wrap(OUTPUT))
     assert len(output.search(state='shut off')) == 11
@@ -45,19 +58,15 @@ def test_virsh_output():
 
 
 def test_virsh_output_no_vms():
-    output = virsh_list_all.VirshListAll(context_wrap(NO_RESULT))
-    assert output.fields == []
-    assert output.cols == []
-    assert output.keywords == []
-    assert output.get_vm_state('NORHEL') is None
+    assert_if_none(virsh_list_all.VirshListAll(context_wrap(NO_RESULT)))
 
 
 def test_virsh_output_blank():
-    output = virsh_list_all.VirshListAll(context_wrap(BLANK))
-    assert output.fields == []
-    assert output.cols == []
-    assert output.keywords == []
-    assert output.get_vm_state('NORHEL') is None
+    assert_if_none(virsh_list_all.VirshListAll(context_wrap(BLANK)))
+
+
+def test_virsh_output_error():
+    assert_if_none(virsh_list_all.VirshListAll(context_wrap(ERROR)))
 
 
 def test_virsh_list_all_documentation():
diff --git a/insights/parsers/virsh_list_all.py b/insights/parsers/virsh_list_all.py
index 0157f130d7..f8d3913363 100644
--- a/insights/parsers/virsh_list_all.py
+++ b/insights/parsers/virsh_list_all.py
@@ -76,6 +76,10 @@ def parse_content(self, content):
         self.fields = []
         self.cols = []
         self.keywords = []
+        # Check and remove any error message, or empty lines. This to
+        # prevent any ValueError exceptions when parse_fixed_table is
+        # called below.
+        content = [l for l in content if not l.startswith("error: ") and l != ""]
         if not content:
             return
 

The virsh_list_all parser is raising ValueError exceptions in production
The VirshListAll parser is throwing a large number of the exception ValueError("Line containing 'Id,Name,State' was not found in table",) in production.
