Merge remote-tracking branch 'origin/master' into paas

Change-Id: If911e353cbeb9f278c57c69812cbdfce0ad1e242
Signed-off-by: Mark Beierl <mark.beierl@canonical.com>
diff --git a/osm_lcm/data_utils/lcm_config.py b/osm_lcm/data_utils/lcm_config.py
index 08a8728..ffd236e 100644
--- a/osm_lcm/data_utils/lcm_config.py
+++ b/osm_lcm/data_utils/lcm_config.py
@@ -198,6 +198,20 @@
         self.logger_name = "lcm.prometheus"
 
 
+class TemporalConfig(OsmConfigman):
+    driver: str = None
+    path: str = None
+    host: str = None
+    port: int = None
+    loglevel: str = "DEBUG"
+    logfile: str = None
+    group_id: str = None
+    logger_name: str = None
+
+    def transform(self):
+        self.logger_name = "lcm.temporal"
+
+
 # Main configuration Template
 
 
@@ -210,6 +224,7 @@
     storage: StorageConfig = StorageConfig()
     message: MessageConfig = MessageConfig()
     tsdb: TsdbConfig = TsdbConfig()
+    temporal: TemporalConfig = TemporalConfig()
 
     def transform(self):
         for attribute in dir(self):
diff --git a/osm_lcm/nglcm.cfg b/osm_lcm/nglcm.cfg
new file mode 100644
index 0000000..be9658b
--- /dev/null
+++ b/osm_lcm/nglcm.cfg
@@ -0,0 +1,62 @@
+##
+# Copyright 2018 Telefonica S.A.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+# TODO currently is a pure yaml format. Consider to change it to [ini: style with yaml inside to be coherent with other modules
+
+global:
+    loglevel: DEBUG
+    # logfile:  /app/log  # or /var/log/osm/lcm.log
+    # nologging: True     # do no log to stdout/stderr
+
+VCA:
+    host:   vca
+    port:   17070
+    user:   admin
+    secret: secret
+    cloud:  localhost
+    k8s_cloud: k8scloud
+    helmpath:    /usr/local/bin/helm
+    helm3path:    /usr/local/bin/helm3
+    kubectlpath: /usr/bin/kubectl
+    jujupath:    /usr/local/bin/juju
+    eegrpc_tls_enforce: false
+    # public_key: pubkey
+    # ca_cert: cacert
+    # api_proxy: apiproxy
+    # eegrpcinittimeout: 600
+    # eegrpctimeout: 30
+
+    # loglevel: DEBUG
+    # logfile:  /var/log/osm/lcm-vca.log
+
+database:
+    driver: mongo       # mongo or memory
+    host:   mongo       # hostname or IP
+    port:   27017
+    name:   osm
+    # replicaset: replicaset
+    # user:   user
+    # password:   password
+    # commonkey: "XXXXXX" # password used for encryption of sensible information
+    # loglevel: DEBUG
+    # logfile:  /var/log/osm/lcm-database.log
+
+storage:
+    driver: local       # local filesystem
+    # for local provide file path
+    path:   /app/storage
+    # loglevel: DEBUG
+    # logfile:  /var/log/osm/lcm-storage.log
diff --git a/osm_lcm/nglcm.py b/osm_lcm/nglcm.py
new file mode 100644
index 0000000..6972477
--- /dev/null
+++ b/osm_lcm/nglcm.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+##
+# Copyright 2018 Telefonica S.A.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+
+import asyncio
+import getopt
+import logging
+import logging.handlers
+import sys
+import yaml
+
+from osm_lcm.lcm_utils import LcmException
+
+from osm_common.dbbase import DbException
+from osm_lcm.data_utils.database.database import Database
+from osm_lcm.data_utils.lcm_config import LcmCfg
+from os import path
+from temporalio import workflow
+from temporalio.client import Client
+from temporalio.worker import Worker
+
+
+class NGLcm:
+    main_config = LcmCfg()
+
+    def __init__(self, config_file, loop=None):
+        """
+        Init, Connect to database, filesystem storage, and messaging
+        :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
+        :return: None
+        """
+        self.db = None
+
+        # logging
+        self.logger = logging.getLogger("lcm")
+        # load configuration
+        config = self.read_config_file(config_file)
+        self.main_config.set_from_dict(config)
+        self.main_config.transform()
+        self.main_config.load_from_env()
+        self.logger.critical("Loaded configuration:" + str(self.main_config.to_dict()))
+
+        # logging
+        log_format_simple = (
+            "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
+        )
+        log_formatter_simple = logging.Formatter(
+            log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
+        )
+        if self.main_config.globalConfig.logfile:
+            file_handler = logging.handlers.RotatingFileHandler(
+                self.main_config.globalConfig.logfile,
+                maxBytes=100e6,
+                backupCount=9,
+                delay=0,
+            )
+            file_handler.setFormatter(log_formatter_simple)
+            self.logger.addHandler(file_handler)
+        if not self.main_config.globalConfig.to_dict()["nologging"]:
+            str_handler = logging.StreamHandler()
+            str_handler.setFormatter(log_formatter_simple)
+            self.logger.addHandler(str_handler)
+
+        if self.main_config.globalConfig.to_dict()["loglevel"]:
+            self.logger.setLevel(self.main_config.globalConfig.loglevel)
+
+        # logging other modules
+        for logger in ("message", "database", "storage", "tsdb"):
+            logger_config = self.main_config.to_dict()[logger]
+            logger_module = logging.getLogger(logger_config["logger_name"])
+            if logger_config["logfile"]:
+                file_handler = logging.handlers.RotatingFileHandler(
+                    logger_config["logfile"], maxBytes=100e6, backupCount=9, delay=0
+                )
+                file_handler.setFormatter(log_formatter_simple)
+                logger_module.addHandler(file_handler)
+            if logger_config["loglevel"]:
+                logger_module.setLevel(logger_config["loglevel"])
+        self.logger.critical("starting osm/nglcm")
+
+        try:
+            self.db = Database(self.main_config.to_dict()).instance.db
+        except DbException as e:
+            self.logger.critical(str(e), exc_info=True)
+            raise LcmException(str(e))
+
+    async def start(self):
+        # do some temporal stuff here
+        temporal_api = (
+            f"{self.main_config.temporal.host}:{str(self.main_config.temporal.port)}"
+        )
+        self.logger.info(f"Attempting to register with Temporal at {temporal_api}")
+        client = await Client.connect(temporal_api)
+
+        task_queue = "lcm-task-queue"
+        workflows = [
+            Heartbeat,
+        ]
+        activities = []
+
+        worker = Worker(
+            client, task_queue=task_queue, workflows=workflows, activities=activities
+        )
+
+        self.logger.info(f"Registered for queue {task_queue}")
+        self.logger.info(f"Registered workflows {workflows}")
+        self.logger.info(f"Registered activites {activities}")
+
+        await worker.run()
+
+    def read_config_file(self, config_file):
+        try:
+            with open(config_file) as f:
+                return yaml.safe_load(f)
+        except Exception as e:
+            self.logger.critical("At config file '{}': {}".format(config_file, e))
+            exit(1)
+
+
+@workflow.defn
+class Heartbeat:
+    @workflow.run
+    async def run(self) -> str:
+        return "alive"
+
+
+if __name__ == "__main__":
+    try:
+        opts, args = getopt.getopt(
+            sys.argv[1:], "hc:", ["config=", "help", "health-check"]
+        )
+        # TODO add  "log-socket-host=", "log-socket-port=", "log-file="
+        config_file = None
+        for o, a in opts:
+            if o in ("-c", "--config"):
+                config_file = a
+            else:
+                assert False, "Unhandled option"
+
+        if config_file:
+            if not path.isfile(config_file):
+                print(
+                    "configuration file '{}' does not exist".format(config_file),
+                    file=sys.stderr,
+                )
+                exit(1)
+        else:
+            for config_file in (
+                __file__[: __file__.rfind(".")] + ".cfg",
+                "./lcm.cfg",
+                "/etc/osm/lcm.cfg",
+            ):
+                print(f"{config_file}")
+                if path.isfile(config_file):
+                    break
+            else:
+                print(
+                    "No configuration file 'lcm.cfg' found neither at local folder nor at /etc/osm/",
+                    file=sys.stderr,
+                )
+                exit(1)
+        lcm = NGLcm(config_file)
+        asyncio.run(lcm.start())
+    except (LcmException, getopt.GetoptError) as e:
+        print(str(e), file=sys.stderr)
+        # usage()
+        exit(1)
diff --git a/requirements-dev.in b/requirements-dev.in
index ec714a2..cfa3e71 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -13,8 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-git+https://osm.etsi.org/gerrit/osm/common.git@master#egg=osm-common
--r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+git+https://osm.etsi.org/gerrit/osm/common.git@paas#egg=osm-common
+-r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
 
-git+https://osm.etsi.org/gerrit/osm/N2VC.git@master#egg=n2vc
--r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+git+https://osm.etsi.org/gerrit/osm/N2VC.git@paas#egg=n2vc
+-r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
diff --git a/requirements-dev.txt b/requirements-dev.txt
index b9bb15d..2c85a0f 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -15,167 +15,167 @@
 # limitations under the License.
 #######################################################################################
 aiokafka==0.8.0
-    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
 async-timeout==4.0.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
     #   aiokafka
     #   retrying-async
 bcrypt==4.0.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   paramiko
 cachetools==5.3.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   google-auth
 certifi==2022.12.7
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
     #   requests
 cffi==1.15.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   cryptography
     #   pynacl
 charset-normalizer==2.1.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   requests
 cryptography==39.0.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   paramiko
 dataclasses==0.6
-    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
 google-auth==2.16.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
 idna==3.4
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   requests
 juju==3.0.0
-    # via -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    # via -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
 jujubundlelib==0.5.7
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   theblues
 kafka-python==2.0.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
     #   aiokafka
 kubernetes==25.3.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 macaroonbakery==1.3.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
     #   theblues
 motor==1.3.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
 mypy-extensions==0.4.3
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   typing-inspect
-n2vc @ git+https://osm.etsi.org/gerrit/osm/N2VC.git@master
+n2vc @ git+https://osm.etsi.org/gerrit/osm/N2VC.git@paas
     # via -r requirements-dev.in
 oauthlib==3.2.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   requests-oauthlib
-osm-common @ git+https://osm.etsi.org/gerrit/osm/common.git@master
+osm-common @ git+https://osm.etsi.org/gerrit/osm/common.git@paas
     # via -r requirements-dev.in
 packaging==23.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
     #   aiokafka
 paramiko==2.12.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 protobuf==3.20.3
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   macaroonbakery
 pyasn1==0.4.8
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
     #   pyasn1-modules
     #   rsa
 pyasn1-modules==0.2.8
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   google-auth
 pycparser==2.21
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   cffi
 pycryptodome==3.17
-    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    # via -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
 pymacaroons==0.13.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   macaroonbakery
 pymongo==3.13.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
     #   motor
 pynacl==1.5.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   macaroonbakery
     #   paramiko
     #   pymacaroons
 pyrfc3339==1.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
     #   macaroonbakery
 python-dateutil==2.8.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
 pytz==2022.7.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   pyrfc3339
 pyyaml==5.4.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
-    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
+    #   -r https://osm.etsi.org/gitweb/?p=osm/common.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
     #   jujubundlelib
     #   kubernetes
 requests==2.28.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
     #   macaroonbakery
     #   requests-oauthlib
     #   theblues
 requests-oauthlib==1.3.1
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
 retrying-async==2.0.0
-    # via -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    # via -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
 rsa==4.9
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   google-auth
 six==1.16.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   google-auth
     #   kubernetes
     #   macaroonbakery
@@ -184,32 +184,32 @@
     #   python-dateutil
 theblues==0.5.2
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 toposort==1.9
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 typing-extensions==4.4.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   typing-inspect
 typing-inspect==0.8.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 urllib3==1.26.14
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
     #   requests
 websocket-client==1.5.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   kubernetes
 websockets==7.0
     # via
-    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=master
+    #   -r https://osm.etsi.org/gitweb/?p=osm/N2VC.git;a=blob_plain;f=requirements.txt;hb=paas
     #   juju
 
 # The following packages are considered to be unsafe in a requirements file:
diff --git a/requirements.in b/requirements.in
index 67d3cfe..8a96125 100644
--- a/requirements.in
+++ b/requirements.in
@@ -23,3 +23,4 @@
 pyyaml==5.4.1
 pydantic
 protobuf==3.20.3
+temporalio
diff --git a/requirements.txt b/requirements.txt
index 4ea9719..5d912be 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -73,12 +73,23 @@
     # via
     #   -r requirements.in
     #   grpcio-tools
+    #   temporalio
 pydantic==1.10.4
     # via -r requirements.in
+python-dateutil==2.8.2
+    # via temporalio
 pyyaml==5.4.1
     # via -r requirements.in
+six==1.16.0
+    # via python-dateutil
+temporalio==1.0.0
+    # via -r requirements.in
+types-protobuf==3.20.4.6
+    # via temporalio
 typing-extensions==4.4.0
-    # via pydantic
+    # via
+    #   pydantic
+    #   temporalio
 yarl==1.8.2
     # via aiohttp