Updates on error logging and handling

- iterative error log storage
- config like value storage
- updates logging format for improved readablility

Change-Id: I171a1b44452c1225340a7d7b1f7593ab9b8ce7c2
Related-PROD: PROD-28199
diff --git a/cfg_checker/common/config_file.py b/cfg_checker/common/config_file.py
new file mode 100644
index 0000000..87f4759
--- /dev/null
+++ b/cfg_checker/common/config_file.py
@@ -0,0 +1,72 @@
+import configparser
+import os
+
+from cfg_checker.common import logger_cli
+
+
+class ConfigFile(object):
+    _truth = ['true', '1', 't', 'y', 'yes', 'yeah', 'yup',
+              'certainly', 'uh-huh']
+    _config = None
+    _section_name = None
+    _config_filepath = None
+
+    def __init__(self, section_name, filepath=None):
+        self._section_name = section_name
+        self._config = configparser.ConfigParser()
+        if filepath is not None:
+            self._config_filepath = self._ensure_abs_path(filepath)
+            self._config.read(self._config_filepath)
+        else:
+            logger_cli.debug("... previous iteration conf not found")
+
+    def force_reload_config(self, path):
+        _path = self._ensure_abs_path(path)
+        self._config.read(_path)
+
+    def save_config(self, filepath=None):
+        if filepath:
+            self._config_filepath = filepath
+        with open(self._config_filepath, "w") as configfile:
+            self._config.write(configfile)
+
+    @staticmethod
+    def _ensure_abs_path(path):
+        if path.startswith('~'):
+            path = os.path.expanduser(path)
+        else:
+            # keep it safe, create var :)
+            path = path
+
+        # make sure it is absolute
+        if not os.path.isabs(path):
+            return os.path.abspath(path)
+        else:
+            return path
+
+    def _ensure_boolean(self, _value):
+        if _value.lower() in self._truth:
+            return True
+        else:
+            return False
+
+    def get_value(self, key, value_type=None):
+        if not value_type:
+            # return str by default
+            return self._config.get(self._section_name, key)
+        elif value_type == int:
+            return self._config.getint(self._section_name, key)
+        elif value_type == bool:
+            return self._config.getboolean(self._section_name, key)
+
+    def set_value(self, key, value):
+        _v = None
+        if not isinstance(value, str):
+            _v = str(value)
+        else:
+            _v = value
+
+        if self._section_name not in self._config.sections():
+            self._config.add_section(self._section_name)
+
+        self._config[self._section_name][key] = _v
diff --git a/cfg_checker/common/file_utils.py b/cfg_checker/common/file_utils.py
new file mode 100644
index 0000000..d508121
--- /dev/null
+++ b/cfg_checker/common/file_utils.py
@@ -0,0 +1,84 @@
+import grp
+import os
+import pwd
+import time
+
+from cfg_checker.common import config
+
+_default_time_format = config.date_format
+
+
+def remove_file(filename):
+    os.remove(filename)
+    # open('filename', 'w').close()
+
+
+def write_str_to_file(filename, _str):
+    with open(filename, 'w') as fo:
+        fo.write(_str)
+
+
+def append_str_to_file(filename, _str):
+    with open(filename, 'a') as fa:
+        fa.write(_str)
+
+
+def write_lines_to_file(filename, source_list):
+    with open(filename, 'w') as fw:
+        fw.write("\n".join(source_list) + "\n")
+
+
+def append_lines_to_file(filename, source_list):
+    _buf = "\n".join(source_list)
+    with open(filename, 'a') as fw:
+        fw.write(_buf + "\n")
+
+
+def append_line_to_file(filename, _str):
+    with open(filename, 'a') as fa:
+        fa.write(_str+'\n')
+
+
+def read_file(filename):
+    _buf = None
+    with open(filename, 'rb') as fr:
+        _buf = fr.read()
+    return _buf
+
+
+def read_file_as_lines(filename):
+    _list = []
+    with open(filename, 'r') as fr:
+        for line in fr:
+            _list.append(line)
+    return _list
+
+
+def get_file_info_fd(fd, time_format=_default_time_format):
+
+    def format_time(unixtime):
+        return time.strftime(
+            time_format,
+            time.gmtime(unixtime)
+        )
+
+    (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = \
+        os.fstat(fd.fileno())
+
+    _dict = {
+        'fd': fd.fileno(),
+        'mode': oct(mode & 0777),
+        'device': hex(dev),
+        'inode': ino,
+        'hard_links': nlink,
+        'owner_id': uid,
+        'owner_name': pwd.getpwuid(uid).pw_name,
+        'owner_group_name': grp.getgrgid(gid).gr_name,
+        'owner_group_id': gid,
+        'size': size,
+        'access_time': format_time(atime),
+        'modification_time': format_time(mtime),
+        'creation_time': format_time(ctime)
+    }
+
+    return _dict
diff --git a/cfg_checker/common/salt_utils.py b/cfg_checker/common/salt_utils.py
index 8b1b47f..2927e28 100644
--- a/cfg_checker/common/salt_utils.py
+++ b/cfg_checker/common/salt_utils.py
@@ -45,7 +45,7 @@
 
     _ssh_cmd.append(_salt_cmd)
     _ssh_cmd = " ".join(_ssh_cmd)
-    logger_cli.debug("...calling salt: '{}'".format(_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(