Rework setting handling
authorJonatan Schlag <jonatan.schlag@ipfire.org>
Sat, 8 Sep 2018 17:59:36 +0000 (19:59 +0200)
committerJonatan Schlag <jonatan.schlag@ipfire.org>
Sat, 8 Sep 2018 17:59:36 +0000 (19:59 +0200)
We now using a seperated class for saving our settings, which makes
working with settings from multiple sources much easier.

Signed-off-by: Jonatan Schlag <jonatan.schlag@ipfire.org>
nitsi.in
src/nitsi/settings.py
src/nitsi/test.py

index 1c5fd17..021d563 100755 (executable)
--- a/nitsi.in
+++ b/nitsi.in
@@ -2,11 +2,12 @@
 
 import argparse
 import logging
+import os
 
 from nitsi.logger import init_logging, Log_Formatter
 from nitsi.recipe import RecipeExeption
 import nitsi.test
-import nitsi.settings
+from nitsi.settings import settings_parse_copy_from, NitsiSettings
 
 logger = logging.getLogger("nitsi")
 
@@ -79,18 +80,22 @@ def main():
         logger.addHandler(fh)
         logger.debug("We now logging everything to {}/general.log".format(log_dir))
 
-        # Build up settings dict
-        settings = {}
+        # Build up settings
+        settings = nitsi.settings.NitsiSettings(priority_list=["cmd", "settings-file", "nitsi-default"])
+
         if args.name:
-            settings["name"] = args.name
+            settings.set_config_value("name", args.name, type="cmd")
         if args.desc:
-            settings["description"] = args.desc
+            settings.set_config_value("description", args.desc, type="cmd")
         if args.copy_to:
-            settings["copy_to"] = args.copy_to
+            settings.set_config_value("copy_to", args.copy_to, type="cmd")
         if args.copy_from:
-            settings["copy_from"] =  nitsi.settings.settings_parse_copy_from(args.copy_from)
+            settings.set_config_value("copy_from", nitsi.settings.settings_parse_copy_from(args.copy_from), type="cmd")
         if args.virtual_environ_path:
-            settings["virtual_environ_path"] = args.virtual_environ_path
+            path = args.virtual_environ_path
+            if not os.path.isabs(path):
+                path = os.path.abspath(path)
+            settings.set_config_value("virtual_environ_path", path, type="cmd")
 
         # here we run a test
         try:
@@ -99,8 +104,7 @@ def main():
                 dir=args.dir,
                 recipe_file=args.recipe,
                 settings_file=args.settings,
-                cmd_settings=settings)
-            currenttest.read_settings()
+                settings=settings)
             currenttest.virtual_environ_setup_stage_1()
             currenttest.load_recipe()
             currenttest.virtual_environ_setup_stage_2()
index b2dd71d..91f763e 100644 (file)
@@ -2,6 +2,7 @@
 
 import logging
 import os
+import configparser
 
 logger = logging.getLogger("nitsi.settings")
 
@@ -37,4 +38,107 @@ def settings_parse_copy_from(copy_from, path=None):
             logger.debug("'{}' will be copied into all images".format(file))
             tmp.append(file)
 
-    return tmp
\ No newline at end of file
+    return tmp
+
+class CommonSettings():
+    def __init__(self, priority_list=[]):
+        if "cache" in priority_list:
+            raise SettingsException("Cache is reserved and so an invalid type")
+
+        self.priority_list = ["cache"] + priority_list
+
+        self._settings = {}
+
+    def set_config_value(self, key, value, type=None):
+        self.check_type(type)
+
+        # Add value to the dict
+        self._settings[type].setdefault(key, value)
+        logger.debug("Added key '{}' with value '{}' of type {} to settings".format(key, value, type))
+
+        # If this key is in the cache and type is not "cache" we need to refresh the cache
+        if "cache" in self._settings and type != "cache":
+            if key in self._settings["cache"]:
+                logger.debug("Removing key '{}' of cache because of new value".format(key))
+                self._settings["cache"].pop(key)
+
+    def get_config_value(self, key):
+        # loop through the priority list and try to find the config value
+
+        for type in self.priority_list:
+            if type in self._settings:
+                if key in self._settings[type]:
+                    logger.debug("Found key '{}' in '{}'".format(key, type))
+                    value = self._settings[type].get(key)
+                    break
+
+        # Update cache:
+        if type != "cache":
+            self.set_config_value(key, value, type="cache")
+
+        return value
+
+    # checks if a type passed to a set_* function is valid
+    def check_type(self, type):
+        if type == None:
+            raise SettingsException("Type for a new config value cannot be None")
+
+        if type not in self.priority_list:
+            raise SettingsException("Type {} is not a valid type".format(type))
+
+        # Add a new type to the settings dict
+        if type not in self._settings:
+            self._settings.setdefault(type, {})
+
+# A settings class with some nitsi defaults
+class NitsiSettings(CommonSettings):
+    def __init__(self, priority_list=[]):
+        super().__init__(priority_list)
+
+        # Set default settings
+        self.set_config_value("name", "", type="nitsi-default")
+        self.set_config_value("description", "", type="nitsi-default")
+        self.set_config_value("copy_from", None, type="nitsi-default")
+        self.set_config_value("copy_to", None, type="nitsi-default")
+        self.set_config_value("virtual_environ_path", None, type="nitsi-default")
+        self.set_config_value("interactive_error_handling", False, type="nitsi-default")
+
+    def set_config_values_from_file(self, file, type):
+        self.check_type(type)
+
+        # Check that file is an valid file
+        if not os.path.isfile(file):
+            raise SettingsException("No such file: {}".format(file))
+
+        # Check that we are use an absolut path
+        if not os.path.isabs(file):
+            file = os.path.abspath(file)
+
+        try:
+            config = configparser.ConfigParser()
+            config.read(file)
+        except BaseException as e:
+            raise e
+
+        if "GENERAL" in config:
+            for key in ["name", "description", "copy_to", "copy_from"]:
+                if key in config["GENERAL"]:
+                    # Handle the copy from setting in a special way
+                    if key == "copy_from":
+                        self.set_config_value(key, settings_parse_copy_from(config["GENERAL"][key], path=os.path.dirname(file)), type=type)
+                    else:
+                        self.set_config_value(key, config["GENERAL"][key], type=type)
+
+
+        if "VIRTUAL_ENVIRONMENT" in config:
+            if "path" in config["VIRTUAL_ENVIRONMENT"]:
+                path = config["VIRTUAL_ENVIRONMENT"]["path"]
+                if not os.path.isabs(path):
+                    path  = os.path.normpath(os.path.dirname(file) + "/" + path)
+                self.set_config_value("virtual_environ_path", path, type=type)
+
+
+    def check_config_values(self):
+        # Check if we get at least a valid a valid path to virtual environ
+        if not os.path.isdir(self.get_config_value("virtual_environ_path")):
+            raise SettingsException("No path for virtual environment found.")
index 92d403b..9766d2b 100755 (executable)
@@ -19,19 +19,10 @@ class TestException(Exception):
         self.message = message
 
 class Test():
-    def __init__(self, log_path, dir=None, recipe_file=None, settings_file=None, cmd_settings=None):
+    def __init__(self, log_path, dir=None, recipe_file=None, settings_file=None, settings=None):
         # init settings var
-        self.settings = {}
+        self.settings = settings
 
-        # Set default values for the settings dict
-        self.settings["name"] = ""
-        self.settings["description"] = ""
-        self.settings["copy_from"] = None
-        self.settings["copy_to"] = None
-        self.settings["virtual_environ_path"] = None
-        self.settings["interactive_error_handling"] = True
-
-        self.cmd_settings = cmd_settings
         self.log_path = log_path
 
         # Init all vars with None
@@ -96,52 +87,15 @@ class Test():
         else:
             self.log = logger.getChild(os.path.basename(self.recipe_file))
 
-    def read_settings(self):
+        # Parse config and settings:
         if self.settings_file:
-            self.log.debug("Going to read all settings from the ini file")
-            try:
-                self.config = configparser.ConfigParser()
-                self.config.read(self.settings_file)
-            except BaseException as e:
-                self.log.error("Failed to parse the config")
-                raise e
-
-            self.settings["name"] = self.config.get("GENERAL","name", fallback="")
-            self.settings["description"] = self.config.get("GENERAL", "description",  fallback="")
-            self.settings["copy_to"] = self.config.get("GENERAL", "copy_to", fallback=None)
-            self.settings["copy_from"] = self.config.get("GENERAL", "copy_from", fallback=None)
-            self.settings["virtual_environ_path"] = self.config.get("VIRTUAL_ENVIRONMENT", "path", fallback=None)
-
-            # We need to parse some settings here because they are loaded from a settings file
-            if not os.path.isabs(self.settings["virtual_environ_path"]):
-                self.settings["virtual_environ_path"] = os.path.normpath(os.path.dirname(
-                    self.settings_file) + "/" + self.settings["virtual_environ_path"])
-
-            # Parse copy_from setting
-            if self.settings["copy_from"]:
-                self.settings["copy_from"] = settings.settings_parse_copy_from(self.settings["copy_from"],
-                path=os.path.dirname(self.settings_file))
-
-        # Update all settings from the cmd
-        self.settings.update(self.cmd_settings)
-
-        if not os.path.isabs(self.settings["virtual_environ_path"]):
-            self.settings["virtual_environ_path"] = os.path.abspath(self.settings["virtual_environ_path"])
-
-
-        # Check if we get at least a valid a valid path to virtual environ
-        if not self.settings["virtual_environ_path"]:
-            self.log.error("No path for virtual environment found.")
-            raise TestException("No path for virtual environment found.")
-
-        # Print all settings for debugging purpose
-        self.log.debug("Settings are:")
-        for key in self.settings:
-            self.log.debug("{}: {}".format(key, self.settings[key]))
+            self.settings.set_config_values_from_file(self.settings_file, type="settings-file")#
 
+        # Check settings
+        self.settings.check_config_values()
 
     def virtual_environ_setup_stage_1(self):
-        self.virtual_environ = virtual_environ.VirtualEnviron(self.settings["virtual_environ_path"])
+        self.virtual_environ = virtual_environ.VirtualEnviron(self.settings.get_config_value("virtual_environ_path"))
 
         self.virtual_networks = self.virtual_environ.get_networks()
 
@@ -173,8 +127,8 @@ class Test():
             self.virtual_machines[name].define()
             self.virtual_machines[name].create_snapshot()
             # We can only copy files when we know which and to which dir
-            if self.settings["copy_from"] and self.settings["copy_to"]:
-                self.virtual_machines[name].copy_in(self.settings["copy_from"], self.settings["copy_to"])
+            if self.settings.get_config_value("copy_from") and self.settings.get_config_value("copy_to"):
+                self.virtual_machines[name].copy_in(self.settings.get_config_value("copy_from"), self.settings.get_config_value("copy_to"))
             self.virtual_machines[name].start()
 
         # Time to which all serial output log entries are relativ
@@ -209,7 +163,7 @@ class Test():
     # in an interactive way
     # returns False when the test should exit right now, and True when the test should go on
     def interactive_error_handling(self):
-        if not self.settings["interactive_error_handling"]:
+        if not self.settings.get_config_value("interactive_error_handling"):
             return False
 
         _cmd = cmd.CMD(intro="You are droppped into an interative debugging shell because of the previous errors",