[SEBA-494] Validating core version matched version requested by synchronizer

Change-Id: Id7018394c70559eae0e424e1e231d8e70b0496de
diff --git a/lib/xos-config/xosconfig/default.py b/lib/xos-config/xosconfig/default.py
index 2e37229..4f6ec23 100644
--- a/lib/xos-config/xosconfig/default.py
+++ b/lib/xos-config/xosconfig/default.py
@@ -14,7 +14,10 @@
 
 DEFAULT_VALUES = {
     "xos_dir": "/opt/xos",
-    "desired_state": "load", # synchronizers - default to "load"
+    "desired_state": "load",  # synchronizers - default to "load"
+    # by default version in not set,
+    # we can't make it mandatory as we're reading multiple files in the synchronizers
+    "core_version": None,
     # The configuration below inherits from the standard config of the Python logging module
     # See: https://docs.python.org/2/library/logging.config.html
     # multistructlog supports this config in all of its generality
diff --git a/lib/xos-config/xosconfig/synchronizer-config-schema.yaml b/lib/xos-config/xosconfig/synchronizer-config-schema.yaml
index 357e8d9..cba845f 100644
--- a/lib/xos-config/xosconfig/synchronizer-config-schema.yaml
+++ b/lib/xos-config/xosconfig/synchronizer-config-schema.yaml
@@ -15,6 +15,8 @@
 map:
   name:
     type: str
+  core_version:
+    type: str
   desired_state:
     type: str
   xos_dir:
diff --git a/lib/xos-synchronizer/xossynchronizer/loadmodels.py b/lib/xos-synchronizer/xossynchronizer/loadmodels.py
index 7acce41..1c1f8bd 100644
--- a/lib/xos-synchronizer/xossynchronizer/loadmodels.py
+++ b/lib/xos-synchronizer/xossynchronizer/loadmodels.py
@@ -70,6 +70,13 @@
                     item.filename = fn
                     item.contents = open(os.path.join(migrations_dir, fn)).read()
 
+        # loading core requested version from synchronizer config
+        core_version = Config.get("core_version")
+        if core_version is None:
+            log.warn("Core version is not set in the config file")
+
+        request.core_version = core_version
+
         result = self.api.dynamicload.LoadModels(request)
 
         return result
diff --git a/lib/xos-synchronizer/xossynchronizer/modelaccessor.py b/lib/xos-synchronizer/xossynchronizer/modelaccessor.py
index c2d5114..72fc3c5 100644
--- a/lib/xos-synchronizer/xossynchronizer/modelaccessor.py
+++ b/lib/xos-synchronizer/xossynchronizer/modelaccessor.py
@@ -41,7 +41,6 @@
 orig_sigint = None
 model_accessor = None
 
-
 class ModelAccessor(object):
     def __init__(self):
         self.all_model_classes = self.get_all_model_classes()
@@ -208,7 +207,7 @@
 
     if Config.get("models_dir"):
         version = autodiscover_version_of_main(max_parent_depth=0) or "unknown"
-        log.info("Service version is %s" % version)
+        log.info("Service version is %s" % version, core_version=Config.get("core_version"))
         try:
             if Config.get("desired_state") == "load":
                 ModelLoadClient(client).upload_models(
@@ -246,6 +245,23 @@
                 client.connected = False
                 client.connect()
                 return
+
+            elif (
+                hasattr(e, "code")
+                and callable(e.code)
+                and hasattr(e.code(), "name")
+                and (e.code().name) == "INVALID_ARGUMENT"
+            ):
+                # in this case there is a version mismatch between the service and the core,
+                # shut down the process so it's clear something is wrong
+                log.error(e.details())
+
+                # kill the process so the operator is aware something is wrong
+                log.info("shutting down")
+                exit_while_inside_reactor(reactor, 1)
+                return
+
+
             log.exception("failed to onboard models")
             # If it's some other error, then we don't need to force a reconnect. Just try the LoadModels() again.
             reactor.callLater(10, functools.partial(grpcapi_reconnect, client, reactor))