Package report/repo parser integration

 - parser able to filter package versions using keywords
 - warning message on missing tag
 - on the fly versions lookup (excluding '*.hotfix')
 - updated versions compare routine
 - lexical compare uses numbers, not ordinal values
 - updated release version detection
 - final report lists pkg section/app if no description given
 - final report shows repo info for detected release version

Fixes:
 - shorter alternate entrpoints: mcp-pkg, mcp-net, cmp-reclass
 - flake8 syntax
 - proper mirantis/non-mirantis versions getting
 - exit on unexpected arguments
 - salt-master class now gets linux codename by default and architecture

Change-Id: I0a2daadca8a1acaecafc8680226dc00d20cc24ce
Related-PROD: PROD-28199
diff --git a/cfg_checker/common/other.py b/cfg_checker/common/other.py
index d9e434a..2620d05 100644
--- a/cfg_checker/common/other.py
+++ b/cfg_checker/common/other.py
@@ -38,7 +38,7 @@
             return (True, _message) if message else True
 
         # node role code checks
-        _code = re.findall("[a-zA-Z]+", fqdn.split('.')[0])
+        _code = re.findall(r"[a-zA-Z]+", fqdn.split('.')[0])
         if len(_code) > 0:
             if _code[0] in all_roles_map:
                 return _result()
@@ -70,7 +70,7 @@
     def get_node_code(self, fqdn):
         # validate
         _isvalid, _message = self.validate_name(fqdn, message=True)
-        _code = re.findall("[a-zA-Z]+?(?=(?:[0-9]+$)|$)", fqdn.split('.')[0])
+        _code = re.findall(r"[a-zA-Z]+?(?=(?:[0-9]+$)|$)", fqdn.split('.')[0])
         # check if it is valid and raise if not
         if _isvalid:
             # try to match it with ones in map
diff --git a/cfg_checker/common/salt_utils.py b/cfg_checker/common/salt_utils.py
index 4dcbd30..7bd6ce7 100644
--- a/cfg_checker/common/salt_utils.py
+++ b/cfg_checker/common/salt_utils.py
@@ -46,13 +46,22 @@
     _ssh_cmd.append(_salt_cmd)
     _ssh_cmd = " ".join(_ssh_cmd)
     logger_cli.debug("... calling salt: '{}'".format(_ssh_cmd))
-    _result = shell(_ssh_cmd)
-    if len(_result) < 1:
-        raise InvalidReturnException("# Empty value returned for '{}".format(
-            _ssh_cmd
-        ))
-    else:
-        return _extract_password(_result)
+    try:
+        _result = shell(_ssh_cmd)
+        if len(_result) < 1:
+            raise InvalidReturnException(
+                "# Empty value returned for '{}".format(
+                    _ssh_cmd
+                )
+            )
+        else:
+            return _extract_password(_result)
+    except OSError as e:
+        raise SaltException(
+            "Salt error calling '{}': '{}'\n"
+            "\nConsider checking 'SALT_ENV' "
+            "and '<pkg>/etc/<env>.env' files".format(_ssh_cmd, e.strerror)
+        )
 
 
 def get_local_password():
@@ -60,8 +69,16 @@
 
     :return: password string
     """
-    _cmd = "salt-call --out=json pillar.get _param:salt_api_password"
-    _result = shell(_cmd)
+    _cmd = "salt-call"
+    _args = "--out=json pillar.get _param:salt_api_password"
+    try:
+        _result = shell(" ".join([_cmd, _args]))
+    except OSError as e:
+        raise SaltException(
+            "Salt error calling '{}': '{}'\n"
+            "\nConsider checking 'SALT_ENV' "
+            "and '<pkg>/etc/<env>.env' files".format(_cmd, e.strerror)
+        )
     return _extract_password(_result)