Network check fixes

- Proper network mapping
- Proper reclass lookup
- VIP detection
- Simple error gathering
- IP shown as 'exploded', i.e. in CIDR format
- MTU matching and detection
- Errors class for handling errors, including codes and indices
- Summary and detailed errors view
- Flake8 refactoring

Change-Id: I8ee37d345bdc21c7ad930bf8305acd28f8c121c8
Related-PROD: PROD-28199
diff --git a/cfg_checker/helpers/errors.py b/cfg_checker/helpers/errors.py
new file mode 100644
index 0000000..b124315
--- /dev/null
+++ b/cfg_checker/helpers/errors.py
@@ -0,0 +1,123 @@
+from cfg_checker.common import logger
+from cfg_checker.common.exception import ErrorMappingException
+
+
+class ErrorIndex(object):
+    _area_code = ""
+    _delimiter = ""
+    _index = 0
+    _errors = {}
+    _types = {
+        0: "Unknown error"
+    }
+
+    def __init__(self, area_code, delimiter='-'):
+        self._area_code = area_code
+        self._delimiter = delimiter
+        self._index += 1
+
+    def _format_error_code(self, index):
+        _t = "{:02d}".format(self._errors[index]['type'])
+        _i = "{:04d}".format(index)
+        _fmt = self._delimiter.join([self._area_code, _t, _i])
+        return _fmt
+
+    def _format_error(self, index):
+        # error code
+        _code = self._format_error_code(index)
+        # prepare data as string list
+        _d = self._errors[index]['data']
+        _data = ["{}: {}".format(_k, _v) for _k, _v in _d.iteritems()]
+        # format message
+        _msg = "### {}: {}\n{}".format(
+            _code,
+            self._get_error_type_text(self._errors[index]['type']),
+            "\n".join(_data)
+        )
+        return _msg
+
+    def _get_error_type_text(self, err_type):
+        if err_type not in self._types:
+            raise ErrorMappingException(
+                "type code {} not found".format(err_type)
+            )
+        else:
+            return self._types[err_type]
+
+    def get_error_code(self, index):
+        if index in self._errors.keys():
+            return self._format_error(index)
+        else:
+            raise ErrorMappingException(
+                "no error found for index {}".format(index)
+            )
+
+    def add_error_type(self, err_type, message):
+        if err_type in self._types:
+            raise ErrorMappingException(
+                "type code {} reserved for {}".format(
+                    err_type,
+                    self._types[err_type]
+                )
+            )
+        else:
+            self._types[err_type] = message
+
+    def add_error(self, err_type, **kwargs):
+        # check error type
+        if err_type not in self._types.keys():
+            logger.error(
+                "Error type not listed: '{}'; unknown used".format(err_type)
+            )
+            err_type = 0
+        _err = {
+            "type": err_type,
+            "data": kwargs
+        }
+        self._errors[self._index] = _err
+        self._index += 1
+
+    def get_errors_total(self):
+        return self._index-1
+
+    def get_indices(self):
+        return self._errors.keys()
+
+    def get_error(self, index):
+        if index in self._errors.keys():
+            return self._format_error(index)
+        else:
+            return "Unknown error index of {}".format(index)
+
+    def get_summary(self, print_zeros=True):
+        # create summary with counts per error type
+        _list = []
+        for _type in self._types.keys():
+            _len = len(
+                filter(
+                    lambda i: self._errors[i]['type'] == _type,
+                    self._errors
+                )
+            )
+            if _len:
+                _num_str = "{:5d}".format(_len)
+            elif print_zeros:
+                _num_str = "{:>5s}".format("-")
+            else:
+                continue
+            _list.append(
+                "{}: {}".format(
+                    _num_str,
+                    self._types[_type]
+                )
+            )
+
+        return "\n".join(_list)
+
+    def get_errors_as_list(self):
+        # create list of strings with error messages
+        _list = []
+        for _idx in range(0, self._index - 1):
+            _list.append("{}".format(self.get_error(_idx)))
+
+        return _list