Add bbsim-tests action

This is for the initial action. Workflow to follow.

Signed-off-by: Eric Ball <eball@linuxfoundation.org>
Change-Id: I70f46f6e8933a2a8a465c7b6b96767e8ee618c97
diff --git a/.github/actions/bbsim-tests/action.yaml b/.github/actions/bbsim-tests/action.yaml
new file mode 100644
index 0000000..72803ac
--- /dev/null
+++ b/.github/actions/bbsim-tests/action.yaml
@@ -0,0 +1,734 @@
+---
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: 2026 Open Networking Foundation Contributors
+
+name: "bbsim-tests"
+description: "VOLTHA BBSim E2E Tests - Simulates OLT/ONUs using bbsim and runs robot framework tests"
+
+inputs:
+  branch:
+    description: "Branch to test (master, voltha-2.15, etc.)"
+    required: true
+    type: string
+  test-targets:
+    description: "YAML string defining test targets to run"
+    required: true
+    type: string
+  gerrit-project:
+    description: "Gerrit project name if building a patch"
+    required: false
+    type: string
+    default: ""
+  gerrit-refspec:
+    description: "Gerrit refspec if building a patch"
+    required: false
+    type: string
+    default: ""
+  voltha-system-tests-change:
+    description: "Gerrit change number for voltha-system-tests"
+    required: false
+    type: string
+    default: ""
+  voltha-helm-charts-change:
+    description: "Gerrit change number for voltha-helm-charts"
+    required: false
+    type: string
+    default: ""
+  extra-helm-flags:
+    description: "Additional Helm flags for deployment"
+    required: false
+    type: string
+    default: ""
+  log-level:
+    description: "Log level for VOLTHA components (DEBUG, INFO, WARN, ERROR)"
+    required: false
+    type: string
+    default: "WARN"
+  timeout:
+    description: "Timeout in minutes for the entire action"
+    required: false
+    type: string
+    default: "240"
+  cluster-name:
+    description: "Name of the kind cluster"
+    required: false
+    type: string
+    default: "kind-ci"
+  docker-registry:
+    description: "Docker registry to use"
+    required: false
+    type: string
+    default: "linuxfoundation.jfrog.io/voltha-docker"
+  olts:
+    description: "Number of OLTs to simulate"
+    required: false
+    type: string
+    default: "1"
+  with-monitoring:
+    description: "Enable monitoring with prometheus"
+    required: false
+    type: boolean
+    default: false
+  enable-mac-learning:
+    description: "Enable MAC learning in VOLTHA"
+    required: false
+    type: boolean
+    default: false
+  extra-robot-args:
+    description: "Additional arguments for Robot Framework"
+    required: false
+    type: string
+    default: ""
+
+outputs:
+  test-results:
+    description: "Path to test results"
+    value: ${{ steps.test-execution.outputs.results-path }}
+
+runs:
+  using: "composite"
+  steps:
+    # -----------------------------------------------------------------------
+    # Setup environment variables
+    # -----------------------------------------------------------------------
+    - name: Setup environment
+      shell: bash
+      run: |
+        echo "KUBECONFIG=$HOME/.kube/kind-${{ inputs.cluster-name }}" >> $GITHUB_ENV
+        echo "VOLTCONFIG=$HOME/.volt/config" >> $GITHUB_ENV
+        echo "PATH=$PATH:$GITHUB_WORKSPACE/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" >> $GITHUB_ENV
+        echo "DIAGS_PROFILE=VOLTHA_PROFILE" >> $GITHUB_ENV
+        echo "SSHPASS=karaf" >> $GITHUB_ENV
+        mkdir -p $GITHUB_WORKSPACE/bin
+
+    # -----------------------------------------------------------------------
+    # Install dependencies
+    # -----------------------------------------------------------------------
+    - name: Install system dependencies
+      shell: bash
+      run: |
+        echo "Installing system dependencies..."
+        sudo apt-get update
+        sudo apt-get install -y \
+          curl \
+          wget \
+          git \
+          make \
+          jq \
+          sshpass \
+          python3 \
+          python3-pip \
+          python3-venv \
+          rsync
+
+    # -----------------------------------------------------------------------
+    # Install kubectl
+    # -----------------------------------------------------------------------
+    - name: Install kubectl
+      shell: bash
+      run: |
+        echo "Installing kubectl..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kubectl" ]; then
+          KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
+          curl -Lo "$GITHUB_WORKSPACE/bin/kubectl" \
+            "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
+          chmod +x "$GITHUB_WORKSPACE/bin/kubectl"
+        fi
+        kubectl version --client
+
+    # -----------------------------------------------------------------------
+    # Install Helm
+    # -----------------------------------------------------------------------
+    - name: Install Helm
+      shell: bash
+      run: |
+        echo "Installing Helm..."
+        if ! command -v helm &> /dev/null; then
+          curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
+        fi
+        helm version
+
+    # -----------------------------------------------------------------------
+    # Install kind
+    # -----------------------------------------------------------------------
+    - name: Install kind
+      shell: bash
+      run: |
+        echo "Installing kind..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kind" ]; then
+          KIND_VERSION="v0.20.0"
+          curl -Lo "$GITHUB_WORKSPACE/bin/kind" \
+            "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64"
+          chmod +x "$GITHUB_WORKSPACE/bin/kind"
+        fi
+        kind version
+
+    # -----------------------------------------------------------------------
+    # Install kail
+    # -----------------------------------------------------------------------
+    - name: Install kail
+      shell: bash
+      run: |
+        echo "Installing kail..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kail" ]; then
+          KAIL_VERSION="v0.17.4"
+          wget -O /tmp/kail.tar.gz \
+            "https://github.com/boz/kail/releases/download/${KAIL_VERSION}/kail_${KAIL_VERSION}_linux_amd64.tar.gz"
+          tar -xzf /tmp/kail.tar.gz -C "$GITHUB_WORKSPACE/bin" kail
+          chmod +x "$GITHUB_WORKSPACE/bin/kail"
+          rm /tmp/kail.tar.gz
+        fi
+
+    # -----------------------------------------------------------------------
+    # Install voltctl
+    # -----------------------------------------------------------------------
+    - name: Install voltctl
+      shell: bash
+      run: |
+        echo "Installing voltctl..."
+        if [ "${{ inputs.gerrit-project }}" != "voltctl" ]; then
+          if [ ! -f "$GITHUB_WORKSPACE/bin/voltctl" ]; then
+            VOLTCTL_VERSION="1.8.45"
+            curl -Lo "$GITHUB_WORKSPACE/bin/voltctl" \
+              "https://github.com/opencord/voltctl/releases/download/v${VOLTCTL_VERSION}/voltctl-${VOLTCTL_VERSION}-linux-amd64"
+            chmod +x "$GITHUB_WORKSPACE/bin/voltctl"
+          fi
+        fi
+
+    # -----------------------------------------------------------------------
+    # Checkout VOLTHA repositories
+    # -----------------------------------------------------------------------
+    - name: Checkout voltha-system-tests
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/voltha-system-tests
+        ref: ${{ inputs.branch }}
+        path: voltha-system-tests
+
+    - name: Apply voltha-system-tests patch
+      if: inputs.voltha-system-tests-change != ''
+      shell: bash
+      working-directory: voltha-system-tests
+      run: |
+        echo "Applying voltha-system-tests change ${{ inputs.voltha-system-tests-change }}"
+        git fetch https://gerrit.lfbroadband.org/voltha-system-tests \
+          refs/changes/${{ inputs.voltha-system-tests-change }}
+        git checkout FETCH_HEAD
+
+    - name: Checkout voltha-helm-charts
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/voltha-helm-charts
+        ref: ${{ inputs.branch }}
+        path: voltha-helm-charts
+
+    - name: Apply voltha-helm-charts patch
+      if: inputs.voltha-helm-charts-change != ''
+      shell: bash
+      working-directory: voltha-helm-charts
+      run: |
+        echo "Applying voltha-helm-charts change ${{ inputs.voltha-helm-charts-change }}"
+        git fetch https://gerrit.lfbroadband.org/voltha-helm-charts \
+          refs/changes/${{ inputs.voltha-helm-charts-change }}
+        git checkout FETCH_HEAD
+
+    # -----------------------------------------------------------------------
+    # Checkout and build gerrit project if specified
+    # -----------------------------------------------------------------------
+    - name: Checkout gerrit project
+      if: inputs.gerrit-project != ''
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/${{ inputs.gerrit-project }}
+        ref: ${{ inputs.gerrit-refspec }}
+        path: ${{ inputs.gerrit-project }}
+
+    - name: Build gerrit project
+      if: inputs.gerrit-project != ''
+      shell: bash
+      working-directory: ${{ inputs.gerrit-project }}
+      run: |
+        echo "Building ${{ inputs.gerrit-project }}..."
+        if [ -f "Makefile" ]; then
+          make docker-build
+        fi
+
+    - name: Build and install voltctl from source
+      if: inputs.gerrit-project == 'voltctl'
+      shell: bash
+      working-directory: voltctl
+      run: |
+        echo "Building voltctl from source..."
+        make build
+        cp voltctl "$GITHUB_WORKSPACE/bin/"
+        chmod +x "$GITHUB_WORKSPACE/bin/voltctl"
+
+    # -----------------------------------------------------------------------
+    # Create kind cluster
+    # -----------------------------------------------------------------------
+    - name: Check if kind cluster exists
+      id: cluster-check
+      shell: bash
+      run: |
+        if kind get clusters | grep -q "^${{ inputs.cluster-name }}$"; then
+          echo "exists=true" >> $GITHUB_OUTPUT
+        else
+          echo "exists=false" >> $GITHUB_OUTPUT
+        fi
+
+    - name: Create kind cluster
+      if: steps.cluster-check.outputs.exists == 'false'
+      shell: bash
+      run: |
+        echo "Creating kind cluster ${{ inputs.cluster-name }}..."
+        cat <<EOF > /tmp/kind-config.yaml
+        kind: Cluster
+        apiVersion: kind.x-k8s.io/v1alpha4
+        nodes:
+          - role: control-plane
+          - role: worker
+          - role: worker
+        EOF
+
+        kind create cluster --name ${{ inputs.cluster-name }} --config /tmp/kind-config.yaml
+
+        # Configure kubeconfig
+        mkdir -p $HOME/.kube
+        kind export kubeconfig --name ${{ inputs.cluster-name }} --kubeconfig $HOME/.kube/kind-${{ inputs.cluster-name }}
+
+        # Wait for cluster to be ready
+        kubectl wait --for=condition=Ready nodes --all --timeout=300s
+
+    # -----------------------------------------------------------------------
+    # Load images to kind if building a component
+    # -----------------------------------------------------------------------
+    - name: Load images to kind
+      if: inputs.gerrit-project != '' && inputs.gerrit-project != 'voltctl'
+      shell: bash
+      run: |
+        echo "Loading images to kind cluster..."
+        PROJECT="${{ inputs.gerrit-project }}"
+        IMAGE_NAME="${PROJECT//-/_}"
+
+        # Find the built image
+        IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "voltha/${PROJECT}|opencord/${PROJECT}" | head -1)
+
+        if [ -n "$IMAGE" ]; then
+          echo "Loading image: $IMAGE"
+          kind load docker-image "$IMAGE" --name ${{ inputs.cluster-name }}
+        else
+          echo "Warning: No image found for project $PROJECT"
+        fi
+
+    # -----------------------------------------------------------------------
+    # Setup Helm repositories
+    # -----------------------------------------------------------------------
+    - name: Setup Helm repositories
+      shell: bash
+      run: |
+        echo "Adding Helm repositories..."
+        helm repo add onf https://charts.lfbroadband.org
+        helm repo add cord https://charts.lfbroadband.org
+        helm repo update
+
+    # -----------------------------------------------------------------------
+    # Run tests
+    # -----------------------------------------------------------------------
+    - name: Install PyYAML for test parsing
+      shell: bash
+      run: |
+        pip3 install pyyaml
+
+    - name: Create test execution script
+      shell: bash
+      run: |
+        # Create the test execution script inline
+        cat > /tmp/execute_test.sh <<'EXECUTE_TEST_SCRIPT'
+        #!/bin/bash
+        set -euo pipefail
+
+        # Get environment variables
+        TEST_TARGET="${TEST_TARGET:-functional-single-kind-dt}"
+        WORKFLOW="${WORKFLOW:-dt}"
+        TEST_FLAGS="${TEST_FLAGS:-}"
+        TEARDOWN="${TEARDOWN:-true}"
+        TEST_LOGGING="${TEST_LOGGING:-True}"
+        VGC_ENABLED="${VGC_ENABLED:-false}"
+        INFRA_NAMESPACE="${INFRA_NAMESPACE:-default}"
+        VOLTHA_NAMESPACE="${VOLTHA_NAMESPACE:-voltha}"
+        LOGS_DIR="${LOGS_DIR:-$GITHUB_WORKSPACE/logs}"
+        EXTRA_HELM_FLAGS="${EXTRA_HELM_FLAGS:-}"
+        LOG_LEVEL="${LOG_LEVEL:-WARN}"
+        DOCKER_REGISTRY="${DOCKER_REGISTRY:-linuxfoundation.jfrog.io/voltha-docker}"
+        OLTS="${OLTS:-1}"
+        WITH_MONITORING="${WITH_MONITORING:-false}"
+        ENABLE_MAC_LEARNING="${ENABLE_MAC_LEARNING:-false}"
+        EXTRA_ROBOT_ARGS="${EXTRA_ROBOT_ARGS:-}"
+        BRANCH="${BRANCH:-master}"
+        GERRIT_PROJECT="${GERRIT_PROJECT:-}"
+
+        # Helper functions
+        banner() {
+            echo ""
+            echo "** -----------------------------------------------------------------------"
+            echo "** $1"
+            echo "** -----------------------------------------------------------------------"
+            echo ""
+        }
+
+        cleanup_port_forward() {
+            echo "Cleaning up port-forward processes..."
+            pkill -f "kubectl.*port-forward" || true
+        }
+
+        get_pods_info() {
+            local logs_dir=$1
+            mkdir -p "$logs_dir"
+            kubectl get pods --all-namespaces -o wide > "$logs_dir/pods.txt" || true
+            kubectl get nodes -o wide > "$logs_dir/nodes.txt" || true
+            kubectl describe pods -n "$VOLTHA_NAMESPACE" > "$logs_dir/voltha-pods-describe.txt" || true
+            kubectl describe pods -n "$INFRA_NAMESPACE" > "$logs_dir/infra-pods-describe.txt" || true
+        }
+
+        # Stage: Cleanup (if teardown enabled)
+        if [ "$TEARDOWN" = "true" ]; then
+            banner "Cleanup - Helm Teardown"
+            cleanup_port_forward
+            for namespace in default "$INFRA_NAMESPACE" "$VOLTHA_NAMESPACE"; do
+                echo "Cleaning up Helm releases in namespace: $namespace"
+                helm list -n "$namespace" -q | xargs -r helm uninstall -n "$namespace" || true
+            done
+            echo "Waiting for pods to terminate..."
+            kubectl wait --for=delete pods --all -n "$VOLTHA_NAMESPACE" --timeout=120s || true
+            kubectl wait --for=delete pods --all -n "$INFRA_NAMESPACE" --timeout=120s || true
+        fi
+
+        # Stage: Deploy Common Infrastructure
+        banner "Deploying Common Infrastructure"
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Deploying monitoring stack..."
+            helm install nem-monitoring onf/nem-monitoring \
+                --set prometheus.alertmanager.enabled=false \
+                --set prometheus.pushgateway.enabled=false \
+                --set kpi_exporter.enabled=false \
+                --set dashboards.xos=false \
+                --set dashboards.onos=false \
+                --set dashboards.aaa=false \
+                --set dashboards.voltha=false \
+                --wait || true
+        fi
+
+        # Stage: Deploy VOLTHA
+        if [ "$TEARDOWN" = "true" ]; then
+            banner "Deploying VOLTHA"
+            ONOS_LOG="${LOGS_DIR}/onos-voltha-startup-combined.log"
+            mkdir -p "$LOGS_DIR"
+            touch "$ONOS_LOG"
+            _TAG=kail-startup kail -n "${INFRA_NAMESPACE}" -n "${VOLTHA_NAMESPACE}" > "$ONOS_LOG" &
+            KAIL_PID=$!
+
+            LOCAL_CHARTS="false"
+            if [ -d "$GITHUB_WORKSPACE/voltha-helm-charts" ]; then
+                LOCAL_CHARTS="true"
+            fi
+
+            HELM_FLAGS="--set global.log_level=${LOG_LEVEL^^}"
+            if [ "$VGC_ENABLED" != "true" ]; then
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosSshPort=30115"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosApiPort=30120"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosOfPort=31653"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.individualOpenFlowNodePorts=true"
+            fi
+            if [ -n "$EXTRA_HELM_FLAGS" ]; then
+                HELM_FLAGS="$HELM_FLAGS $EXTRA_HELM_FLAGS"
+            fi
+            if [ -n "$TEST_FLAGS" ]; then
+                HELM_FLAGS="$HELM_FLAGS $TEST_FLAGS"
+            fi
+
+            if [ -n "$GERRIT_PROJECT" ] && [ "$GERRIT_PROJECT" != "voltctl" ]; then
+                IMAGE_TAG="citest"
+                case "$GERRIT_PROJECT" in
+                    voltha-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha.image.tag=$IMAGE_TAG"
+                        ;;
+                    voltha-openolt-adapter)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha-adapter-openolt.image.tag=$IMAGE_TAG"
+                        ;;
+                    voltha-openonu-adapter-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha-adapter-openonu.image.tag=$IMAGE_TAG"
+                        ;;
+                    ofagent-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha.ofagent.image.tag=$IMAGE_TAG"
+                        ;;
+                    bbsim)
+                        HELM_FLAGS="$HELM_FLAGS --set bbsim.image.tag=$IMAGE_TAG"
+                        ;;
+                esac
+            fi
+
+            echo "Deploying VOLTHA with workflow: $WORKFLOW"
+            echo "Helm flags: $HELM_FLAGS"
+
+            if [ "$LOCAL_CHARTS" = "true" ]; then
+                echo "Using local charts from $GITHUB_WORKSPACE/voltha-helm-charts"
+                helm dependency update "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-infra" || true
+                helm dependency update "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-stack" || true
+                helm install voltha-infra "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-infra" \
+                    -n "$INFRA_NAMESPACE" --create-namespace --wait --timeout 120m
+                helm install voltha "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-stack" \
+                    -n "$VOLTHA_NAMESPACE" --create-namespace \
+                    --set global.stack_name=voltha \
+                    --set global.voltha_infra_name=voltha-infra \
+                    --set global.voltha_infra_namespace="$INFRA_NAMESPACE" \
+                    $(echo "$HELM_FLAGS") --wait --timeout 120m
+            else
+                echo "Using charts from Helm repository"
+                helm install voltha-infra onf/voltha-infra \
+                    -n "$INFRA_NAMESPACE" --create-namespace --wait --timeout 120m
+                helm install voltha onf/voltha-stack \
+                    -n "$VOLTHA_NAMESPACE" --create-namespace \
+                    --set global.stack_name=voltha \
+                    --set global.voltha_infra_name=voltha-infra \
+                    --set global.voltha_infra_namespace="$INFRA_NAMESPACE" \
+                    $(echo "$HELM_FLAGS") --wait --timeout 120m
+            fi
+
+            for i in $(seq 0 $((OLTS - 1))); do
+                echo "Deploying bbsim${i}..."
+                if [ "$LOCAL_CHARTS" = "true" ]; then
+                    helm install "bbsim${i}" "$GITHUB_WORKSPACE/voltha-helm-charts/bbsim" \
+                        -n "$VOLTHA_NAMESPACE" --set olt_id="${i}" --wait || true
+                else
+                    helm install "bbsim${i}" onf/bbsim \
+                        -n "$VOLTHA_NAMESPACE" --set olt_id="${i}" --wait || true
+                fi
+            done
+
+            if [ -n "${KAIL_PID:-}" ]; then
+                kill "$KAIL_PID" || true
+                wait "$KAIL_PID" 2>/dev/null || true
+            fi
+            gzip -f "$ONOS_LOG" || true
+
+            echo "Setting up port forwarding..."
+            kubectl port-forward --address 0.0.0.0 -n "$INFRA_NAMESPACE" svc/voltha-infra-kafka 9092:9092 &
+            bbsim_dmi_port=50075
+            for i in $(seq 0 $((OLTS - 1))); do
+                kubectl port-forward --address 0.0.0.0 -n "$VOLTHA_NAMESPACE" "svc/bbsim${i}" "${bbsim_dmi_port}:50075" &
+                ((bbsim_dmi_port++))
+            done
+            if [ "$WITH_MONITORING" = "true" ]; then
+                kubectl port-forward --address 0.0.0.0 -n default svc/nem-monitoring-prometheus-server 31301:80 &
+            fi
+            if [ "$VGC_ENABLED" = "true" ]; then
+                kubectl port-forward --address 0.0.0.0 -n "$VOLTHA_NAMESPACE" svc/voltha-voltha-go-controller 8181:8181 &
+            fi
+            sleep 5
+        fi
+
+        # Stage: Run Tests
+        banner "Running test ${TEST_TARGET} on workflow ${WORKFLOW}"
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Collecting initial memory consumption..."
+            mkdir -p "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}"
+            cd "$GITHUB_WORKSPACE/voltha-system-tests"
+            if [ -f "requirements.txt" ]; then
+                python3 -m venv .venv || true
+                source .venv/bin/activate || true
+                pip install -r requirements.txt || true
+                if [ -f "scripts/mem_consumption.py" ]; then
+                    python scripts/mem_consumption.py \
+                        -o "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}" \
+                        -a 0.0.0.0:31301 -n "$VOLTHA_NAMESPACE" || true
+                fi
+            fi
+        fi
+
+        echo "Running Robot Framework tests..."
+        mkdir -p "$LOGS_DIR"
+        export ROBOT_MISC_ARGS="-d ${LOGS_DIR} ${EXTRA_ROBOT_ARGS}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v ONOS_SSH_PORT:30115"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v ONOS_REST_PORT:30120"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v NAMESPACE:${VOLTHA_NAMESPACE}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v INFRA_NAMESPACE:${INFRA_NAMESPACE}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v container_log_dir:${LOGS_DIR}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v logging:${TEST_LOGGING}"
+        export ROBOT_MISC_ARGS
+        export KVSTOREPREFIX="voltha/voltha_voltha"
+        cd "$GITHUB_WORKSPACE/voltha-system-tests"
+        make "${TEST_TARGET}" || TEST_RESULT=$?
+
+        get_pods_info "$LOGS_DIR"
+
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Collecting final memory consumption..."
+            cd "$GITHUB_WORKSPACE/voltha-system-tests"
+            source .venv/bin/activate || true
+            if [ -f "scripts/mem_consumption.py" ]; then
+                python scripts/mem_consumption.py \
+                    -o "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}" \
+                    -a 0.0.0.0:31301 -n "$VOLTHA_NAMESPACE" || true
+            fi
+        fi
+
+        echo "Compressing logs..."
+        cd "$LOGS_DIR"
+        gzip *-combined.log 2>/dev/null || true
+        banner "Test execution completed"
+        exit ${TEST_RESULT:-0}
+        EXECUTE_TEST_SCRIPT
+
+        chmod +x /tmp/execute_test.sh
+
+    - name: Execute tests
+      id: test-execution
+      shell: bash
+      run: |
+        echo "Executing tests..."
+
+        # Create logs directory
+        mkdir -p $GITHUB_WORKSPACE/logs
+        echo "results-path=$GITHUB_WORKSPACE/logs" >> $GITHUB_OUTPUT
+
+        # Parse test targets from YAML
+        cat > /tmp/test-targets.yaml <<'EOF'
+        ${{ inputs.test-targets }}
+        EOF
+
+        # Run each test target
+        python3 - <<'PYTHON_SCRIPT'
+        import yaml
+        import subprocess
+        import os
+        import sys
+
+        with open('/tmp/test-targets.yaml', 'r') as f:
+            tests = yaml.safe_load(f)
+
+        if not tests:
+            print("No tests defined")
+            sys.exit(0)
+
+        workspace = os.environ['GITHUB_WORKSPACE']
+        test_failures = []
+
+        for idx, test in enumerate(tests):
+            target = test.get('target', '')
+            workflow = test.get('workflow', '')
+            flags = test.get('flags', '')
+            teardown = test.get('teardown', True)
+            logging = test.get('logging', True)
+            vgc_enabled = test.get('vgcEnabled', False)
+
+            print(f"\n** -----------------------------------------------------------------------")
+            print(f"** Executing test {target} on workflow {workflow}")
+            print(f"** -----------------------------------------------------------------------\n")
+
+            # Set environment variables
+            env = os.environ.copy()
+            env['TEST_TARGET'] = target
+            env['WORKFLOW'] = workflow
+            env['TEST_FLAGS'] = flags
+            env['TEARDOWN'] = str(teardown).lower()
+            env['TEST_LOGGING'] = 'True' if logging else 'False'
+            env['VGC_ENABLED'] = 'true' if vgc_enabled else 'false'
+            env['INFRA_NAMESPACE'] = 'default'
+            env['VOLTHA_NAMESPACE'] = 'voltha'
+            env['LOGS_DIR'] = f"{workspace}/logs/{target}"
+            env['EXTRA_HELM_FLAGS'] = "${{ inputs.extra-helm-flags }}"
+            env['LOG_LEVEL'] = "${{ inputs.log-level }}"
+            env['DOCKER_REGISTRY'] = "${{ inputs.docker-registry }}"
+            env['OLTS'] = "${{ inputs.olts }}"
+            env['WITH_MONITORING'] = "${{ inputs.with-monitoring }}"
+            env['ENABLE_MAC_LEARNING'] = "${{ inputs.enable-mac-learning }}"
+            env['EXTRA_ROBOT_ARGS'] = "${{ inputs.extra-robot-args }}"
+            env['BRANCH'] = "${{ inputs.branch }}"
+            env['GERRIT_PROJECT'] = "${{ inputs.gerrit-project }}"
+
+            # Create logs directory for this test
+            os.makedirs(env['LOGS_DIR'], exist_ok=True)
+
+            # Execute the test using the inline script
+            script_path = "/tmp/execute_test.sh"
+
+            print(f"Running test script: {script_path}")
+
+            try:
+                result = subprocess.run(
+                    ['/bin/bash', script_path],
+                    env=env,
+                    check=True
+                )
+                print(f"\nTest {target} completed successfully\n")
+            except subprocess.CalledProcessError as e:
+                print(f"\nTest {target} failed with exit code {e.returncode}\n")
+                test_failures.append(target)
+                # Continue with other tests
+                continue
+            except Exception as e:
+                print(f"\nTest {target} failed with exception: {e}\n")
+                test_failures.append(target)
+                continue
+
+        if test_failures:
+            print(f"\n** Failed tests: {', '.join(test_failures)}")
+            sys.exit(1)
+        else:
+            print("\n** All tests passed successfully")
+
+        PYTHON_SCRIPT
+
+    # -----------------------------------------------------------------------
+    # Collect artifacts
+    # -----------------------------------------------------------------------
+    - name: Collect pod information
+      if: always()
+      shell: bash
+      run: |
+        echo "Collecting pod information..."
+        mkdir -p $GITHUB_WORKSPACE/logs/artifacts
+
+        kubectl get pods --all-namespaces -o wide > $GITHUB_WORKSPACE/logs/artifacts/pods.txt || true
+        kubectl get nodes -o wide > $GITHUB_WORKSPACE/logs/artifacts/nodes.txt || true
+        kubectl get events --all-namespaces --sort-by='.lastTimestamp' > $GITHUB_WORKSPACE/logs/artifacts/events.txt || true
+
+    - name: Collect VOLTHA logs
+      if: always()
+      shell: bash
+      run: |
+        echo "Collecting VOLTHA logs..."
+        kubectl logs -n voltha -l app.kubernetes.io/part-of=voltha \
+          > $GITHUB_WORKSPACE/logs/artifacts/voltha.log || true
+
+    - name: Cleanup port-forward processes
+      if: always()
+      shell: bash
+      run: |
+        echo "Cleaning up port-forward processes..."
+        pkill -f "kubectl.*port-forward" || true
+
+    - name: Upload test results
+      if: always()
+      uses: actions/upload-artifact@v4
+      with:
+        name: bbsim-test-results-${{ github.run_id }}
+        path: |
+          ${{ github.workspace }}/logs/**/*.log
+          ${{ github.workspace }}/logs/**/*.gz
+          ${{ github.workspace }}/logs/**/*.txt
+          ${{ github.workspace }}/logs/**/*.html
+          ${{ github.workspace }}/logs/**/*.xml
+          ${{ github.workspace }}/logs/**/voltha-pods-mem-consumption-*/*
+        retention-days: 30
+        if-no-files-found: warn
+
+    # -----------------------------------------------------------------------
+    # Cleanup
+    # -----------------------------------------------------------------------
+    - name: Delete kind cluster
+      if: always()
+      shell: bash
+      run: |
+        # Note: Cluster deletion is optional and can be controlled by workflow
+        echo "To delete cluster manually, run: kind delete cluster --name ${{ inputs.cluster-name }}"