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/modules/packages/versions.py b/cfg_checker/modules/packages/versions.py
index a2bd083..f12d0b7 100644
--- a/cfg_checker/modules/packages/versions.py
+++ b/cfg_checker/modules/packages/versions.py
@@ -1,5 +1,6 @@
 import csv
 import os
+import re
 
 from cfg_checker.common import config, const, logger_cli
 from cfg_checker.common.settings import pkg_dir
@@ -10,7 +11,7 @@
     _list = {}
 
     dummy_desc = {
-        "component": "unlisted",
+        "section": "unlisted",
         "app": "-",
         "repo": "-",
         "versions": {}
@@ -34,7 +35,7 @@
                 # package_name,component,application_or_service,repo,openstack_release,2018.4.0,2018.11.0,2019.2.0,2019.2.1,2019.2.2
                 # reassign for code readability
                 _pkg = row[0]
-                _component = row[1]
+                _section = row[1]
                 _app = row[2]
                 _repo = row[3]
                 # if release cell empty - use keyword 'any'
@@ -59,7 +60,7 @@
                     # update pkg data in list
                     self._list.update({
                         _pkg: {
-                            "component": _component,
+                            "section": _section,
                             "app": _app,
                             "repo": _repo,
                             "versions": {}
@@ -124,16 +125,16 @@
     def __init__(self, version_string):
         # save
         if len(version_string) < 1:
-            self.epoch = None
-            self.upstream = None
-            self.debian = None
+            self.epoch = "0"
+            self.upstream = "0"
+            self.debian = ''
             self.version = 'n/a'
             return
         else:
             # do parse the main versions
             _v = version_string
             # colon presence, means epoch present
-            _e = _v.split(':', 1)[0] if ':' in _v else ''
+            _e = _v.split(':', 1)[0] if ':' in _v else "0"
             # if epoch was there, upstream should be cut
             _m = _v if ':' not in _v else _v.split(':', 1)[1]
             # dash presence, means debian present
@@ -186,9 +187,25 @@
         return self._cmp_fragment(_lhf, _rhf)
 
     def _cmp_lex(self, lf, rf):
-        # cast each item into its ORD value
-        _lhf = [ord(n) for n in lf]
-        _rhf = [ord(n) for n in rf]
+        def split_rev(_s):
+            _out = []
+            _list = re.split(r'(\d+)', _s)
+            # iterate and cast into num of possible
+            for idx in range(0, len(_list)):
+                try:
+                    # try to convert it to number
+                    _out.append(int(_list[idx]))
+                except ValueError:
+                    # not a number
+                    _ords = [ord(n) for n in _list[idx]]
+                    _out += _ords
+            return _out
+        # split string into letters and numbers
+        # and cast each item into its ORD value
+        _lhf = split_rev(lf)
+        _rhf = split_rev(rf)
+        # _lhf = [ord(n) for n in lf]
+        # _rhf = [ord(n) for n in rf]
 
         return self._cmp_fragment(_lhf, _rhf)
     # end of cmps
@@ -196,14 +213,20 @@
     # main part compared using splitted numbers
     # if equal, revision is compared using lexical comparizon
     def __lt__(self, v):
-        if self._cmp_num(self.epoch, v.epoch) < 0:
-            return True
-        elif self._cmp_num(self.upstream, v.upstream) < 0:
-            return True
-        elif self._cmp_lex(self.upstream_rev, v.upstream_rev) < 0:
-            return True
-        else:
-            return False
+        _e = self._cmp_num(self.epoch, v.epoch)
+        _u = self._cmp_num(self.upstream, v.upstream)
+        _ul = self._cmp_lex(self.upstream_rev, v.upstream_rev)
+        _d = self._cmp_num(self.debian, v.debian)
+        _dl = self._cmp_lex(self.debian_rev, v.debian_rev)
+        for n in [_e, _u, _ul, _d, _dl]:
+            if n == 0:
+                continue
+            elif n < 0:
+                return True
+            elif n > 0:
+                return False
+        # if all is equal, it is still false
+        return False
 
     def __eq__(self, v):
         # compare all portions
@@ -211,18 +234,26 @@
         _result.append(self._cmp_num(self.epoch, v.epoch))
         _result.append(self._cmp_num(self.upstream, v.upstream))
         _result.append(self._cmp_lex(self.upstream_rev, v.upstream_rev))
+        _result.append(self._cmp_num(self.debian, v.debian))
+        _result.append(self._cmp_lex(self.debian_rev, v.debian_rev))
         # if there is any non-zero, its not equal
         return not any(_result)
 
     def __gt__(self, v):
-        if self._cmp_num(self.epoch, v.epoch) > 0:
-            return True
-        elif self._cmp_num(self.upstream, v.upstream) > 0:
-            return True
-        elif self._cmp_lex(self.upstream_rev, v.upstream_rev) > 0:
-            return True
-        else:
-            return False
+        _e = self._cmp_num(self.epoch, v.epoch)
+        _u = self._cmp_num(self.upstream, v.upstream)
+        _ul = self._cmp_lex(self.upstream_rev, v.upstream_rev)
+        _d = self._cmp_num(self.debian, v.debian)
+        _dl = self._cmp_lex(self.debian_rev, v.debian_rev)
+        for n in [_e, _u, _ul, _d, _dl]:
+            if n == 0:
+                continue
+            elif n > 0:
+                return True
+            elif n < 0:
+                return False
+        # if all is equal, it is still false
+        return False
 
     def update_parts(self, target, status):
         # updating parts of version statuses