diff --git a/codeco-experimentation-framework/containers/resource-manager/Dockerfile b/codeco-experimentation-framework/containers/resource-manager/Dockerfile
index 0ce232f307ab5d90f3538371f859252c22e07bca..d88979b86939dcceda630b69c96abb03bff45749 100644
--- a/codeco-experimentation-framework/containers/resource-manager/Dockerfile
+++ b/codeco-experimentation-framework/containers/resource-manager/Dockerfile
@@ -45,3 +45,15 @@ ADD playbooks/files/acm/* /opt/clusterslice/playbooks/files/acm/
 ADD configuration.sh /opt/clusterslice/
 ADD basic_functions.sh /opt/clusterslice/
 ADD benchmarking.sh /opt/clusterslice/
+ADD ansible/ansible.cfg /opt/clusterslice/ansible/
+ADD ansible/ansible-jaeger.cfg /opt/clusterslice/ansible/
+ADD ansible/callback_plugins/codeco_plugin.py /opt/clusterslice/ansible/callback_plugins/
+
+# Install pip
+RUN apt-get update && apt-get install -y python3-pip
+
+# Install the OpenTelemetry Python packages
+RUN pip3 install opentelemetry-api opentelemetry-exporter-otlp opentelemetry-sdk
+
+# Install community.general collection in Ansible
+RUN ansible-galaxy collection install community.general
diff --git a/codeco-experimentation-framework/containers/resource-manager/ansible/callback_plugins/codeco_plugin.py b/codeco-experimentation-framework/containers/resource-manager/ansible/callback_plugins/codeco_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1ef75e1afecdd18efe42e832640f778ba2be959
--- /dev/null
+++ b/codeco-experimentation-framework/containers/resource-manager/ansible/callback_plugins/codeco_plugin.py
@@ -0,0 +1,108 @@
+import time
+import os
+from ansible.plugins.callback import CallbackBase
+from datetime import timedelta
+
+class CallbackModule(CallbackBase):
+    """
+    Custom callback plugin to calculate execution time and track task status.
+    """
+    CALLBACK_VERSION = 1.0
+    CALLBACK_TYPE = 'notification'
+    CALLBACK_NAME = 'codeco_plugin'
+
+    def __init__(self):
+        super(CallbackModule, self).__init__()
+        self.task_start_time = None
+        self.task_stats = {}
+        self.processed_tasks = set()  # To track processed tasks and avoid duplicates
+        self.experiment_name = os.getenv('EXPERIMENT_NAME', '')
+        self.node_name = os.getenv('NODE_NAME', '')
+        self.cluster_number = os.getenv('CLUSTER_NUMBER', '')
+        self.logfile = f'/opt/clusterslice/results/{self.experiment_name}/{self.node_name}_{self.cluster_number}_output.txt'
+        self._ensure_log_directory()
+
+    def _ensure_log_directory(self):
+        # Ensure the results directory exists, create if not
+        log_dir = os.path.dirname(self.logfile)
+        if not os.path.exists(log_dir):
+            os.makedirs(log_dir)
+
+    def _write_to_logfile(self, content):
+        # Write content to the logfile
+        with open(self.logfile, 'a', buffering=1) as log_file:
+            log_file.write(content + '\n')
+
+    def v2_playbook_on_task_start(self, task, is_conditional):
+        # Record task start time without logging
+        self.task_start_time = time.time()
+        task_name = task.get_name().strip()
+        #self._display.display(f"Task '{task_name}' is being executed.", color='cyan')
+
+    def v2_runner_on_ok(self, result):
+        task_name = result.task_name
+        task_host = result._host  # Get the host for uniqueness
+
+        # Create a unique key for this task based on name and host
+        task_key = f"{task_name}_{task_host}"
+
+        # Check if this task has been processed before
+        if task_key not in self.processed_tasks:
+            task_duration = self._get_task_duration()
+            self._log_task_result(task_name, "SUCCESS", task_duration, task_host)
+            self.processed_tasks.add(task_key)  # Mark task as processed
+
+    def v2_runner_on_failed(self, result, ignore_errors=False):
+        task_name = result.task_name
+        task_host = result._host  # Get the host for uniqueness
+
+        # Create a unique key for this task based on name and host
+        task_key = f"{task_name}_{task_host}"
+
+        # Check if this task has been processed before
+        if task_key not in self.processed_tasks:
+            task_duration = self._get_task_duration()
+            self._log_task_result(task_name, "FAILED", task_duration, task_host)
+            self.processed_tasks.add(task_key)  # Mark task as processed
+
+    def v2_runner_on_skipped(self, result):
+        task_name = result.task_name
+        task_host = result._host  # Get the host for uniqueness
+
+        # Create a unique key for this task based on name and host
+        task_key = f"{task_name}_{task_host}"
+
+        # Check if this task has been processed before
+        if task_key not in self.processed_tasks:
+            task_duration = self._get_task_duration()
+            self._log_task_result(task_name, "SKIPPED", task_duration, task_host)
+            self.processed_tasks.add(task_key)  # Mark task as processed
+
+    def v2_playbook_on_stats(self, stats):
+        # Output the summary of all task execution times
+        self._display.display("Playbook execution complete.", color='blue')
+        for task_name, info in self.task_stats.items():
+            status = info["status"]
+            host = info["host"]
+            duration = str(timedelta(seconds=info["duration"]))
+            self._display.display(f"Task '{task_name}': {status} - Duration: {duration}", color='green' if status == 'SUCCESS' else 'red')
+            # Keep statistics in log file
+            log_entry = f"{host}, {task_name}, {status}, {duration}"
+            # Write log entry to the logfile
+            self._write_to_logfile(log_entry)
+    def _get_task_duration(self):
+        """
+        Helper function to calculate the duration of the task
+        """
+        return time.time() - self.task_start_time
+
+    def _log_task_result(self, task_name, status, duration, host):
+        """
+        Helper function to log the result and duration of the task
+        """
+        self.task_stats[task_name] = {
+            "status": status,
+            "duration": duration,
+            "host": host
+        }
+        self._display.display(f"Task '{task_name}' {status}. Duration: {timedelta(seconds=duration)}", color='green' if status == 'SUCCESS' else 'red')
diff --git a/codeco-experimentation-framework/count_cluster_deployment.sh b/codeco-experimentation-framework/count_cluster_deployment.sh
new file mode 100755
index 0000000000000000000000000000000000000000..492676b43f4095cf68cc4b1238976ced2382adc2
--- /dev/null
+++ b/codeco-experimentation-framework/count_cluster_deployment.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# This script counts the number of cluster deployments
+
+# Define the file name
+FILE="cluster-deployment"
+
+# Check if the file exists
+if [ ! -f "$FILE" ]; then
+  # If it doesn't exist, create the file and set initial count to 0
+  echo 0 > "$FILE"
+fi
+
+# Read the current count from the file
+count=$(cat "$FILE")
+
+# Increment the count by 1
+count=$((count + 1))
+
+# Save the updated count back to the file
+echo "$count" > "$FILE"
+
+# Optionally, print the current count to the console
+echo "Deploying cluster number $count."
+
+# Setting appropriate variable
+cluster_number=$count