Automated Paсkage versions update for tags

Module repos.py
 - ReposInfo(), walks mirror using HTTP and builds
   all repos map available
 - RepoManager(), using repos map builds package versions map
   either for specific tags or for all of them

Fixes:
 - Progress class clears line remainder on change

Utils:
 - Download GZ file into memory
 - TGZ file interface, CRU (no deletion)

Change-Id: Ifdb37aa4b68fb25f642b2089cf16cd242ed25a0b
Related-PROD: PROD-28199
diff --git a/cfg_checker/helpers/console_utils.py b/cfg_checker/helpers/console_utils.py
index 5c32506..d1db2a1 100644
--- a/cfg_checker/helpers/console_utils.py
+++ b/cfg_checker/helpers/console_utils.py
@@ -2,6 +2,9 @@
 
 
 class Progress(object):
+    _strsize = 0
+    _note_size = 0
+
     def __init__(self, max_index, bar_size=21):
         self.total = max_index
         # bar size in symbols
@@ -9,21 +12,35 @@
 
     def write_progress(self, index, note=''):
         # calc index and percent values
+        _suffix = ''
+        new_size = len(note)
+        if self._note_size > new_size:
+            _suffix = ' '*(self._note_size - new_size)
         _percent = (100 * index) / self.total
         _index = (self.bar_size * index) / self.total
         # clear the line
         sys.stdout.write('\r')
         # print new progress
         _format = "[{:"+str(self.bar_size-1)+"}] {}/{} ({}%) {}"
-        sys.stdout.write(_format.format(
+        _progress_string = _format.format(
             '='*_index,
             index,
             self.total,
             _percent,
-            note
-        ))
+            note + _suffix
+        )
+        sys.stdout.write(_progress_string)
+        # Save new note size and whole string size
+        self._strsize = len(_progress_string)
+        self._note_size = new_size
         sys.stdout.flush()
 
-    @staticmethod
-    def newline():
+    def clearline(self):
+        sys.stdout.write('\r')
+        sys.stdout.write(' '*self._strsize)
+        sys.stdout.write('\r')
+
+    def end(self):
+        self._note_size = 0
+        self._strsize = 0
         sys.stdout.write('\n')
diff --git a/cfg_checker/helpers/tgz.py b/cfg_checker/helpers/tgz.py
new file mode 100644
index 0000000..631125e
--- /dev/null
+++ b/cfg_checker/helpers/tgz.py
@@ -0,0 +1,137 @@
+import os
+import tarfile as tarfile
+import tempfile
+
+from cfg_checker.common import logger_cli
+from cfg_checker.common.exception import ConfigException
+
+
+class TGZFile(object):
+    basefile = None
+
+    def __init__(self, _filepath, label=None):
+        # Check if this filename exists
+        if not os.path.exists(_filepath):
+            # If the archive not exists, create it
+            # simple labelfile for a non-empty archive
+            _labelname = "labelfile"
+            with tempfile.TemporaryFile() as _tempfile:
+                _tempfile.write(label.encode('utf-8'))
+                _tempfile.flush()
+                _tempfile.seek(0)
+                # create tgz
+                with tarfile.open(_filepath, "w:gz") as tgz:
+                    _info = tgz.gettarinfo(
+                        arcname=_labelname,
+                        fileobj=_tempfile
+                    )
+                    tgz.addfile(_info, fileobj=_tempfile)
+                logger_cli.debug("... created file '{}'".format(_filepath))
+                self.basefile = _filepath
+
+        elif not os.path.isfile(_filepath):
+            # if path exists, and it is not a file
+            raise ConfigException(
+                "Supplied path of '{}' is not a file".format(
+                    _filepath
+                )
+            )
+        elif not tarfile.is_tarfile(_filepath):
+            # if file exists, and it is not a tar file
+            raise ConfigException(
+                "Supplied file of '{}' is not a TAR stream".format(
+                    _filepath
+                )
+            )
+        else:
+            self.basefile = _filepath
+
+    def get_file(self, name):
+        if self.has_file(name):
+            with tarfile.open(self.basefile, "r:gz") as tgz:
+                _tgzitem = tgz.extractfile(tgz.getmember(name))
+                return _tgzitem.read()
+        else:
+            return None
+
+    def add_file(self, name, buf=None, replace=False):
+        _files = []
+        with tarfile.open(self.basefile) as r:
+            _files = r.getnames()
+            _exists = name in _files
+            if _exists and not replace:
+                # file exists and replace flag is not set
+                return False
+
+        # check if there is work to do
+        if not buf and not os.path.exists(name):
+            # Nothing to do: no buffer or file to add
+            return False
+        elif name in self.list_files() and not replace:
+            # file already there and replace flag not set
+            return False
+
+        _a = "replace" if replace else "add"
+        logger_cli.debug("... about to {} '{}' ({:.2f}MB) -> '{}'".format(
+            _a,
+            name,
+            float(len(buf))/1024/1024,
+            self.basefile
+        ))
+
+        # unzip tar, add file, zip it back
+        _tmpdir = tempfile.mkdtemp()
+        logger_cli.debug("... created tempdir '{}'".format(_tmpdir))
+        # extract them
+        _files = []
+        with tarfile.open(self.basefile) as r:
+            # all names extracted
+            _files = r.getnames()
+            # extract 'em
+            logger_cli.debug("... extracting contents")
+            r.extractall(_tmpdir)
+
+        # create file
+        if buf:
+            _p = os.path.join(_tmpdir, name)
+            logger_cli.debug("... writing new file to '{}'".format(
+                _p
+            ))
+            if not _exists or replace:
+                with open(_p, "w") as w:
+                    w.write(buf)
+            if not _exists:
+                _files.append(name)
+        # create the archive
+        logger_cli.debug("... rebuilding archive")
+        with tarfile.open(self.basefile, "w:gz") as tgz:
+            for _file in _files:
+                _p = os.path.join(_tmpdir, _file)
+                tgz.add(_p, arcname=_file)
+                os.remove(_p)
+        os.rmdir(_tmpdir)
+        return True
+
+    def list_files(self):
+        # get names
+        with tarfile.open(self.basefile, "r:gz") as tgz:
+            _names = tgz.getnames()
+        # filter filenames only, skip path
+        if any(['/' in _n for _n in _names]):
+            _n = []
+            for f in _names:
+                if '/' in f:
+                    _n.append(f.rsplit('/', 1)[1])
+                else:
+                    _n.append(f)
+            return _n
+        else:
+            return _names
+
+    def has_file(self, name):
+        if name in self.list_files():
+            logger_cli.debug("... '{}' has '{}'".format(self.basefile, name))
+            return True
+        else:
+            logger_cli.debug("... '{}' lacks '{}'".format(self.basefile, name))
+            return False
diff --git a/cfg_checker/helpers/zip.py b/cfg_checker/helpers/zip.py
new file mode 100644
index 0000000..b050030
--- /dev/null
+++ b/cfg_checker/helpers/zip.py
@@ -0,0 +1,8 @@
+import os
+import zipfile
+
+
+class ZIPFile(object):
+    def __init__(self, _filepath, label=None):
+
+        return