[VOL-3780] Unifying tucson POD pipelines and migrating to voltha charts

Change-Id: I010087d59ae9745904aa1effa0b4bbad5e5b84f1
diff --git a/jjb/defaults.yaml b/jjb/defaults.yaml
index bfc9f8f..34cb3e1 100644
--- a/jjb/defaults.yaml
+++ b/jjb/defaults.yaml
@@ -84,7 +84,7 @@
     modern-branches-regexp: '^(master|cord-7.0|cord-6.1|cord-6.0|seba-1.0|voltha-2.3|voltha-2.4)$'
     platform-branches-regexp: '^(master|cord-7.0|cord-6.1)$'
     seba-branches-regexp: '^(master|cord-7.0|seba-1.0)$'
-    kind-voltha-regexp: '^(voltha-2.7)$'
+    kind-voltha-regexp: '^(voltha-2.6|voltha-2.7)$'
 
     # matchs all project repos
     all-projects-regexp: '.*'
diff --git a/jjb/pipeline/voltha/master/bbsim-tests.groovy b/jjb/pipeline/voltha/master/bbsim-tests.groovy
index 63d2582..2e2179c 100644
--- a/jjb/pipeline/voltha/master/bbsim-tests.groovy
+++ b/jjb/pipeline/voltha/master/bbsim-tests.groovy
@@ -23,51 +23,13 @@
       remote: 'https://gerrit.opencord.org/ci-management.git'
 ])
 
-// TODO move this in a keyword so it can be shared across pipelines
-def customImageFlags(project) {
-  def chart = "unknown"
-  def image = "unknown"
-  switch(project) {
-    case "ofagent-go":
-      chart = "voltha"
-      image = "ofagent"
-    break
-    case "voltha-go":
-      chart = "voltha"
-      image = "rw_core"
-    break
-    case "voltha-openonu-adapter-go":
-      chart = "voltha-adapter-openonu"
-      image = "adapter_open_onu_go"
-    break
-    // TODO remove after 2.7
-    case "voltha-openonu-adapter":
-      chart = "voltha-adapter-openonu"
-      image = "adapter_open_onu"
-    break
-    // TODO end
-    case "voltha-openolt-adapter":
-      chart = "voltha-adapter-openolt"
-      image = "adapter_open_olt"
-    break
-    case "bbsim":
-      // BBSIM has a different format that voltha, return directly
-      return "--set images.bbsim.tag=citest,images.bbsim.pullPolicy=Never,images.bbsim.registry='' "
-    break
-    default:
-      return ""
-  }
-
-  return "--set ${chart}.images.${image}.tag=citest,${chart}.images.${image}.pullPolicy=Never,${chart}.images.${image}.registry='' "
-}
-
 def test_workflow(name) {
   timeout(time: 10, unit: 'MINUTES') {
     stage('Deploy - '+ name + ' workflow') {
         def extraHelmFlags = "${extraHelmFlags} --set global.log_level=DEBUG,onu=1,pon=1 "
 
         if (gerritProject != "") {
-          extraHelmFlags = extraHelmFlags + customImageFlags("${gerritProject}")
+          extraHelmFlags = extraHelmFlags + getVolthaImageFlags("${gerritProject}")
         }
 
         def localCharts = false
diff --git a/jjb/pipeline/voltha/master/tucson-build-and-test.groovy b/jjb/pipeline/voltha/master/tucson-build-and-test.groovy
new file mode 100644
index 0000000..d400681
--- /dev/null
+++ b/jjb/pipeline/voltha/master/tucson-build-and-test.groovy
@@ -0,0 +1,312 @@
+
+// Copyright 2017-present Open Networking Foundation
+//
+// 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.
+// used to deploy VOLTHA and configure ONOS physical PODs
+// NOTE we are importing the library even if it's global so that it's
+// easier to change the keywords during a replay
+library identifier: 'cord-jenkins-libraries@master',
+    retriever: modernSCM([
+      $class: 'GitSCMSource',
+      remote: 'https://gerrit.opencord.org/ci-management.git'
+])
+def infraNamespace = "infra"
+def volthaNamespace = "voltha"
+def clusterName = "kind-ci"
+pipeline {
+  /* no label, executor is determined by JJB */
+  agent {
+    label "${params.buildNode}"
+  }
+  options {
+    timeout(time: 120, unit: 'MINUTES')
+  }
+  environment {
+    PATH="$PATH:$WORKSPACE/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
+    KUBECONFIG="$HOME/.kube/kind-${clusterName}"
+    VOLTCONFIG="$HOME/.volt/config"
+  }
+  stages{
+    stage('Download Code') {
+      steps {
+        getVolthaCode([
+          branch: "${branch}",
+          gerritProject: "${gerritProject}",
+          gerritRefspec: "${gerritRefspec}",
+          volthaSystemTestsChange: "${volthaSystemTestsChange}",
+          volthaHelmChartsChange: "${volthaHelmChartsChange}",
+        ])
+      }
+    }
+    stage ("Parse deployment configuration file") {
+      steps {
+        sh returnStdout: true, script: "rm -rf ${configBaseDir}"
+        sh returnStdout: true, script: "git clone -b master ${cordRepoUrl}/${configBaseDir}"
+        script {
+          if ( params.workflow.toUpperCase() == "DT" ) {
+            deployment_config = readYaml file: "${configBaseDir}/${configDeploymentDir}/${configFileName}-DT.yaml"
+          }
+          else if ( params.workflow.toUpperCase() == "TT" ) {
+            deployment_config = readYaml file: "${configBaseDir}/${configDeploymentDir}/${configFileName}-TT.yaml"
+          }
+          else {
+            deployment_config = readYaml file: "${configBaseDir}/${configDeploymentDir}/${configFileName}.yaml"
+          }
+        }
+      }
+    }
+    stage('Clean up') {
+      steps {
+        timeout(15) {
+          script {
+            helmTeardown(["default", infraNamespace, volthaNamespace])
+          }
+          timeout(1) {
+            sh returnStdout: false, script: '''
+            # remove orphaned port-forward from different namespaces
+            ps aux | grep port-forw | grep -v grep | awk '{print $2}' | xargs --no-run-if-empty kill -9
+            '''
+          }
+        }
+      }
+    }
+    stage('Build patch') {
+      steps {
+        // NOTE that the correct patch has already been checked out
+        // during the getVolthaCode step
+        buildVolthaComponent("${gerritProject}")
+      }
+    }
+    stage('Create K8s Cluster') {
+      steps {
+        script {
+          def clusterExists = sh returnStdout: true, script: """
+          kind get clusters | grep ${clusterName} | wc -l
+          """
+          if (clusterExists.trim() == "0") {
+            createKubernetesCluster([nodes: 3, name: clusterName])
+          }
+        }
+      }
+    }
+    stage('Load image in kind nodes') {
+      steps {
+        loadToKind()
+      }
+    }
+    stage('Install Voltha')  {
+      steps {
+        timeout(20) {
+          script {
+            imageFlags = getVolthaImageFlags(gerritProject)
+            // if we're downloading a voltha-helm-charts patch, then install from a local copy of the charts
+            def localCharts = false
+            if (volthaHelmChartsChange != "" || gerritProject == "voltha-helm-charts") {
+              localCharts = true
+            }
+            def extraHelmFlags = "-f $WORKSPACE/${configBaseDir}/${configKubernetesDir}/voltha/${configFileName}.yml ${imageFlags} "
+            // NOTE temporary workaround expose ONOS node ports (pod-config needs to be updated to contain these values)
+            extraHelmFlags = extraHelmFlags + "--set onos-classic.onosSshPort=30115 " +
+            "--set onos-classic.onosApiPort=30120 " +
+            "--set onos-classic.onosOfPort=31653 " +
+            "--set onos-classic.individualOpenFlowNodePorts=true "
+            volthaDeploy([
+              workflow: workFlow.toLowerCase(),
+              extraHelmFlags: extraHelmFlags,
+              localCharts: localCharts,
+              kubeconfig: "$WORKSPACE/${configBaseDir}/${configKubernetesDir}/${configFileName}.conf",
+              onosReplica: 3,
+              atomixReplica: 3,
+              kafkaReplica: 3,
+              etcdReplica: 3,
+              ])
+          }
+          // start logging
+          sh """
+          mkdir -p $WORKSPACE/${workFlow}
+          _TAG=kail-${workFlow} kail -n infra -n voltha > $WORKSPACE/${workFlow}/onos-voltha-combined.log &
+          """
+          sh """
+          JENKINS_NODE_COOKIE="dontKillMe" bash -c "while true; do kubectl port-forward --address 0.0.0.0 -n ${volthaNamespace} svc/voltha-voltha-api 55555:55555; done"&
+          JENKINS_NODE_COOKIE="dontKillMe" bash -c "while true; do kubectl port-forward --address 0.0.0.0 -n ${infraNamespace} svc/voltha-infra-etcd 2379:2379; done"&
+          JENKINS_NODE_COOKIE="dontKillMe" bash -c "while true; do kubectl port-forward --address 0.0.0.0 -n ${infraNamespace} svc/voltha-infra-kafka 9092:9092; done"&
+          ps aux | grep port-forward
+          """
+          getPodsInfo("$WORKSPACE")
+        }
+      }
+    }
+    stage('Deploy Kafka Dump Chart') {
+      steps {
+        script {
+          sh returnStdout: false, script: """
+              helm repo add cord https://charts.opencord.org
+              helm repo update
+              if helm version -c --short|grep v2 -q; then
+                helm install -n voltha-kafka-dump cord/voltha-kafka-dump
+              else
+                helm install voltha-kafka-dump cord/voltha-kafka-dump
+              fi
+          """
+        }
+      }
+    }
+    stage('Push Tech-Profile') {
+      when {
+        expression { params.profile != "Default" }
+      }
+      steps {
+        sh returnStdout: false, script: """
+        etcd_container=\$(kubectl get pods -n voltha | grep voltha-etcd-cluster | awk 'NR==1{print \$1}')
+        kubectl cp $WORKSPACE/voltha-system-tests/tests/data/TechProfile-${profile}.json voltha/\$etcd_container:/tmp/flexpod.json
+        kubectl exec -it \$etcd_container -n voltha -- /bin/sh -c 'cat /tmp/flexpod.json | ETCDCTL_API=3 etcdctl put service/voltha/technology_profiles/XGS-PON/64'
+        """
+      }
+    }
+
+    stage('Push Sadis-config') {
+      steps {
+        sh returnStdout: false, script: """
+        ssh-keygen -R [${deployment_config.nodes[0].ip}]:30115
+        ssh-keyscan -p 30115 -H ${deployment_config.nodes[0].ip} >> ~/.ssh/known_hosts
+        sshpass -p karaf ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 30115 karaf@${deployment_config.nodes[0].ip} "log:set TRACE org.opencord.dhcpl2relay"
+        sshpass -p karaf ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 30115 karaf@${deployment_config.nodes[0].ip} "log:set TRACE org.opencord.aaa"
+        sshpass -p karaf ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 30115 karaf@${deployment_config.nodes[0].ip} "log:set TRACE org.opencord.olt"
+        sshpass -p karaf ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 30115 karaf@${deployment_config.nodes[0].ip} "log:set DEBUG org.onosproject.net.flowobjective.impl.FlowObjectiveManager"
+        sshpass -p karaf ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 30115 karaf@${deployment_config.nodes[0].ip} "log:set DEBUG org.onosproject.net.flowobjective.impl.InOrderFlowObjectiveManager"
+
+        if [[ "${workFlow.toUpperCase()}" == "DT" ]]; then
+          curl -sSL --user karaf:karaf -X POST -H Content-Type:application/json http://${deployment_config.nodes[0].ip}:30120/onos/v1/network/configuration --data @$WORKSPACE/voltha-system-tests/tests/data/${configFileName}-sadis-DT.json
+        elif [[ "${workFlow.toUpperCase()}" == "TT" ]]; then
+          curl -sSL --user karaf:karaf -X POST -H Content-Type:application/json http://${deployment_config.nodes[0].ip}:30120/onos/v1/network/configuration --data @$WORKSPACE/voltha-system-tests/tests/data/${configFileName}-sadis-TT.json
+        else
+          # this is the ATT case, rename the file in *-sadis-ATT.json so that we can avoid special cases and just load the file
+          curl -sSL --user karaf:karaf -X POST -H Content-Type:application/json http://${deployment_config.nodes[0].ip}:30120/onos/v1/network/configuration --data @$WORKSPACE/voltha-system-tests/tests/data/${configFileName}-sadis.json
+        fi
+        """
+      }
+    }
+    stage('Reinstall OLT software') {
+      when {
+        expression { params.reinstallOlt }
+      }
+      steps {
+        script {
+          deployment_config.olts.each { olt ->
+            sh returnStdout: false, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'dpkg --remove asfvolt16 && dpkg --purge asfvolt16'"
+            waitUntil {
+              olt_sw_present = sh returnStdout: true, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'dpkg --list | grep asfvolt16 | wc -l'"
+              return olt_sw_present.toInteger() == 0
+            }
+            if ( params.branch == 'voltha-2.3' ) {
+              oltDebVersion = oltDebVersionVoltha23
+            } else {
+              oltDebVersion = oltDebVersionMaster
+            }
+            sh returnStdout: false, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'dpkg --install ${oltDebVersion}'"
+            waitUntil {
+              olt_sw_present = sh returnStdout: true, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'dpkg --list | grep asfvolt16 | wc -l'"
+              return olt_sw_present.toInteger() == 1
+            }
+            if ( olt.fortygig ) {
+              // If the OLT is connected to a 40G switch interface, set the NNI port to be downgraded
+              sh returnStdout: false, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'echo port ce128 sp=40000 >> /broadcom/qax.soc ; /opt/bcm68620/svk_init.sh'"
+            }
+          }
+        }
+      }
+    }
+
+    stage('Restart OLT processes') {
+      steps {
+        script {
+          deployment_config.olts.each { olt ->
+            sh returnStdout: false, script: """
+            ssh-keyscan -H ${olt.ip} >> ~/.ssh/known_hosts
+            sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'rm -f /var/log/openolt.log; rm -f /var/log/dev_mgmt_daemon.log; reboot'
+            sleep 120
+            """
+            waitUntil {
+              onu_discovered = sh returnStdout: true, script: "sshpass -p ${olt.pass} ssh -l ${olt.user} ${olt.ip} 'grep \"onu discover indication\" /var/log/openolt.log | wc -l'"
+              return onu_discovered.toInteger() > 0
+            }
+          }
+        }
+      }
+    }
+    stage('Run E2E Tests') {
+      steps {
+        script {
+          if ( params.workflow.toUpperCase() == "DT" ) {
+            robotConfigFile = "${configBaseDir}/${configDeploymentDir}/${configFileName}-DT.yaml"
+          }
+          else if ( params.workflow.toUpperCase() == "TT" ) {
+            robotConfigFile = "${configBaseDir}/${configDeploymentDir}/${configFileName}-TT.yaml"
+          }
+          else {
+            robotConfigFile = "${configBaseDir}/${configDeploymentDir}/${configFileName}.yaml"
+          }
+        }
+        sh returnStdout: false, script: """
+        mkdir -p $WORKSPACE/RobotLogs
+
+        export ROBOT_CONFIG_FILE="$WORKSPACE/${robotConfigFile}"
+        export ROBOT_MISC_ARGS="${params.extraRobotArgs} --removekeywords wuks -d $WORKSPACE/RobotLogs -v container_log_dir:$WORKSPACE "
+        export ROBOT_FILE="Voltha_PODTests.robot"
+
+        # If the Gerrit comment contains a line with "functional tests" then run the full
+        # functional test suite.  This covers tests tagged either 'sanity' or 'functional'.
+        # Note: Gerrit comment text will be prefixed by "Patch set n:" and a blank line
+        REGEX="functional tests"
+        if [[ "${gerritComment}" =~ \$REGEX ]]; then
+          ROBOT_MISC_ARGS+="-i functional"
+        fi
+        # Likewise for dataplane tests
+        REGEX="dataplane tests"
+        if [[ "${gerritComment}" =~ \$REGEX ]]; then
+          ROBOT_MISC_ARGS+="-i dataplane"
+        fi
+
+        make -C $WORKSPACE/voltha-system-tests voltha-test || true
+        """
+      }
+    }
+  }
+  post {
+    always {
+      // stop logging
+      sh """
+        P_IDS="\$(ps e -ww -A | grep "_TAG=kail-${workFlow}" | grep -v grep | awk '{print \$1}')"
+        if [ -n "\$P_IDS" ]; then
+          echo \$P_IDS
+          for P_ID in \$P_IDS; do
+            kill -9 \$P_ID
+          done
+        fi
+        gzip $WORKSPACE/${workFlow}/onos-voltha-combined.log || true
+      """
+      step([$class: 'RobotPublisher',
+        disableArchiveOutput: false,
+        logFileName: 'RobotLogs/log*.html',
+        otherFiles: '',
+        outputFileName: 'RobotLogs/output*.xml',
+        outputPath: '.',
+        passThreshold: 100,
+        reportFileName: 'RobotLogs/report*.html',
+        unstableThreshold: 0]);
+      archiveArtifacts artifacts: '**/*.txt,**/*.gz,*.gz'
+    }
+  }
+}
+
+// refs/changes/06/24206/5
diff --git a/jjb/pipeline/voltha-dt-physical-build-and-tests.groovy b/jjb/pipeline/voltha/voltha-2.7/voltha-dt-physical-build-and-tests.groovy
similarity index 100%
rename from jjb/pipeline/voltha-dt-physical-build-and-tests.groovy
rename to jjb/pipeline/voltha/voltha-2.7/voltha-dt-physical-build-and-tests.groovy
diff --git a/jjb/pipeline/voltha-physical-build-and-tests.groovy b/jjb/pipeline/voltha/voltha-2.7/voltha-physical-build-and-tests.groovy
similarity index 98%
rename from jjb/pipeline/voltha-physical-build-and-tests.groovy
rename to jjb/pipeline/voltha/voltha-2.7/voltha-physical-build-and-tests.groovy
index 2ccbb2a..e719a08 100644
--- a/jjb/pipeline/voltha-physical-build-and-tests.groovy
+++ b/jjb/pipeline/voltha/voltha-2.7/voltha-physical-build-and-tests.groovy
@@ -96,11 +96,9 @@
         getVolthaCode([
           branch: "${branch}",
           gerritProject: "${gerritProject}",
-          // FIXE fatal: couldn't find remote ref 23842/2
-          // gerritRefspec: "${gerritChangeNumber}/${gerritPatchsetNumber}",
           gerritRefspec: "${gerritRefspec}",
-          // volthaSystemTestsChange: "${volthaSystemTestsChange}",
-          // volthaHelmChartsChange: "${volthaHelmChartsChange}",
+          volthaSystemTestsChange: "${volthaSystemTestsChange}",
+          volthaHelmChartsChange: "${volthaHelmChartsChange}",
         ])
       }
     }
diff --git a/jjb/voltha-e2e.yaml b/jjb/voltha-e2e.yaml
index a3c86f8..d5b8bad 100755
--- a/jjb/voltha-e2e.yaml
+++ b/jjb/voltha-e2e.yaml
@@ -216,46 +216,67 @@
           time-trigger: "@daily"
           work-flow: DT
 
-      # Per-patchset Pod builds on Tucson pod
+      # Per-patchset Pod builds on Tucson pod (master)
       - 'verify_physical_voltha_patchset_auto':
           name: 'verify_physical_voltha_patchset_auto'
-          build-node: 'tucson-pod'
-          config-pod: 'tucson-pod'
-          oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
-          oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
-          profile: 'Default'
           default-test-args: '-i sanityORDeleteOLT -i PowerSwitch -X'
 
-      # Per-patchset Pod builds on Tucson pod
-      - 'verify_physical_voltha_patchset_manual':
-          name: 'verify_physical_voltha_patchset_manual'
-          build-node: 'tucson-pod'
-          config-pod: 'tucson-pod'
+      # Per-patchset Pod builds on Tucson pod (voltha-2.7)
+      - 'verify_physical_voltha_patchset_auto':
+          name: 'verify_physical_voltha_patchset_auto_voltha-2.7'
           oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
           oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+          default-test-args: '-i sanityORDeleteOLT -i PowerSwitch -X'
+          pipeline-script: 'voltha/voltha-2.7/voltha-physical-build-and-tests.groovy'
+          branch-pattern: voltha-2.7
+
+      # Per-patchset Pod builds on Tucson pod (master)
+      - 'verify_physical_voltha_patchset_manual':
+          name: 'verify_physical_voltha_patchset_manual'
           profile: 'Default'
           trigger-string: 'hardware test'
           default-test-args: '-i sanityORDeleteOLT -i PowerSwitch -X'
+          branch-pattern: master
 
 
-      # Per-patchset Pod builds on Tucson pod
+      # Per-patchset Pod builds on Tucson pod  (master)
       - 'verify_physical_voltha_patchset_manual':
           name: 'verify_physical_voltha_patchset_manual_DT'
-          build-node: 'tucson-pod'
-          config-pod: 'tucson-pod-DT'
+          workflow: 'dt'
+          profile: 'Default'
+          pipeline-script: 'voltha/voltha-2.7/voltha-dt-physical-build-and-tests.groovy'
+          trigger-string: 'DT hardware test'
+          default-test-args: '-i sanityDt -i PowerSwitch -X'
+          branch-pattern: master
+
+      # Per-patchset Pod builds on Tucson pod (voltha-2.7)
+      - 'verify_physical_voltha_patchset_manual':
+          name: 'verify_physical_voltha_patchset_manual_voltha-2.7'
           oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
           oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
           profile: 'Default'
-          pipeline-script: 'voltha-dt-physical-build-and-tests.groovy'
+          pipeline-script: 'voltha/voltha-2.7/voltha-physical-build-and-tests.groovy'
+          trigger-string: 'hardware test'
+          default-test-args: '-i sanityORDeleteOLT -i PowerSwitch -X'
+          branch-pattern: voltha-2.7
+
+
+      # Per-patchset Pod builds on Tucson pod  (voltha-2.7)
+      - 'verify_physical_voltha_patchset_manual':
+          name: 'verify_physical_voltha_patchset_manual_DT_voltha-2.7'
+          workflow: 'dt'
+          oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+          oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+          profile: 'Default'
+          pipeline-script: 'voltha/voltha-2.7/voltha-physical-build-and-tests.groovy'
           trigger-string: 'DT hardware test'
           default-test-args: '-i sanityDt -i PowerSwitch -X'
-
+          branch-pattern: voltha-2.7
 
       # Manual build job for Tucson pod
       # Allow local testing without disrupting above job
       - 'build_physical_voltha_manual':
           name: 'build_tucson-pod_manual'
-          build-node: 'tucson-pod'
           config-pod: 'tucson-pod'
           manualBranch: 'master'
           oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
@@ -482,11 +503,6 @@
           description: 'Download a change for gerrit in the voltha-system-tests repo, example value: "refs/changes/79/18779/13"'
 
       - string:
-          name: volthaSystemTestsChange
-          default: '{volthaSystemTestsChange}'
-          description: 'Download a change for gerrit in the voltha-system-tests repo, example value: "refs/changes/79/18779/13"'
-
-      - string:
           name: volthaHelmChartsChange
           default: '{volthaHelmChartsChange}'
           description: 'Download a change for gerrit in the voltha-helm-charts repo, example value: "refs/changes/79/18779/13"'
@@ -531,25 +547,9 @@
                   pattern: '{all-files-regexp}'
 
 # POD Per Patchset Pipeline Jobs
-
-- job-template:
-    name: '{name}'
-    id: verify_physical_voltha_patchset_auto
-    description: |
-                  <!-- Managed by Jenkins Job Builder -->
-                  Automated build on POD {config-pod} using {pipeline-script} <br /><br />
-                  Created from job-template {id} from ci-management/jjb/voltha-e2e.yaml <br />
-                  Created by Andy Bavier, andy@opennetworking.org <br />
-                  Copyright (c) 2019 Open Networking Foundation (ONF)
-    sandbox: true
-    pipeline-script: 'voltha-physical-build-and-tests.groovy'
-    default-test-args: '-i sanityORDeleteOLT -X'
-
-    properties:
-      - cord-infra-properties:
-          build-days-to-keep: '{build-days-to-keep}'
-          artifact-num-to-keep: '{artifact-num-to-keep}'
-
+# to use these parameters in a job: `<<: *voltha-physical-patchset-parameters`
+- voltha-physical-patchset-parameters: &voltha-physical-patchset-parameters
+    name: voltha-physical-patchset-parameters
     parameters:
       - string:
           name: buildNode
@@ -557,14 +557,9 @@
           description: 'Pod management node'
 
       - string:
-          name: manifestUrl
-          default: '{gerrit-server-url}/{voltha-test-manifest-repo}'
-          description: 'URL to the repo manifest'
-
-      - string:
           name: branch
           default: '$GERRIT_BRANCH'
-          description: 'Name of the branch to use'
+          description: 'Name of the repo branch to use'
 
       - string:
           name: gerritProject
@@ -572,61 +567,56 @@
           description: 'Name of the Gerrit project'
 
       - string:
-          name: gerritChangeNumber
-          default: '$GERRIT_CHANGE_NUMBER'
-          description: 'Changeset number in Gerrit'
-
-      - string:
-          name: gerritPatchsetNumber
-          default: '$GERRIT_PATCHSET_NUMBER'
-          description: 'PatchSet number in Gerrit'
-
-      - string:
           name: gerritRefspec
           default: '$GERRIT_REFSPEC'
           description: 'RefSpec number in Gerrit'
 
       - string:
+          name: gerritComment
+          default: '$GERRIT_EVENT_COMMENT_TEXT'
+          description: 'RefSpec number in Gerrit'
+
+      - string:
           name: cordRepoUrl
           default: '{gerrit-server-url}'
           description: 'The URL of the CORD Project repository'
 
       - string:
+          name: configBaseDir
+          default: 'pod-configs'
+          description: 'The directory inside the POD configs repository'
+
+      - string:
+          name: configDeploymentDir
+          default: 'deployment-configs'
+          description: 'The deployment configs folder'
+
+      - string:
+          name: configKubernetesDir
+          default: 'kubernetes-configs'
+          description: 'The kubernetes config folder'
+
+      - string:
+          name: configFileName
+          default: '{config-pod}'
+          description: 'The config file'
+
+      - string:
           name: podName
           default: '{config-pod}'
 
       - string:
-          name: deploymentConfigFile
-          default: 'pod-configs/deployment-configs/{config-pod}.yaml'
-          description: 'Path of deployment config file'
-
-      - string:
-          name: kindVolthaValuesFile
-          default: 'pod-configs/kubernetes-configs/voltha/{config-pod}.yml'
-          description: 'Path of kind-voltha values override file'
-
-      - string:
           name: sadisConfigFile
           default: 'voltha-system-tests/tests/data/{config-pod}-sadis.json'
           description: 'Path of SADIS config to load'
 
       - string:
-          name: localConfigDir
-          default: null
-          description: 'If specified, config file paths are relative to this dir; otherwise $WORKSPACE'
-
-      - string:
-          name: configRepo
-          default: 'pod-configs'
-          description: 'A repository containing the config files, will be checked out if specified'
-
-      - string:
           name: oltDebVersionMaster
           default: '{oltDebVersionMaster}'
           description: 'OLT Software version to install for master branch builds'
 
       - string:
-          name: oltDebVersion
+          name: oltDebVersionVoltha23
           default: '{oltDebVersionVoltha23}'
           description: 'OLT Software version to install for voltha-2.3 branch builds'
 
@@ -646,14 +636,94 @@
           description: "Re-install OLT software"
 
       - string:
+          name: extraRobotArgs
+          default: '{default-test-args}'
+          description: 'Arguments to pass to robot'
+
+      - string:
+          name: volthaSystemTestsChange
+          default: '{volthaSystemTestsChange}'
+          description: 'Download a change for gerrit in the voltha-system-tests repo, example value: "refs/changes/79/18779/13"'
+
+      - string:
+          name: volthaHelmChartsChange
+          default: '{volthaHelmChartsChange}'
+          description: 'Download a change for gerrit in the voltha-helm-charts repo, example value: "refs/changes/79/18779/13"'
+
+      - string:
+          name: workflow
+          default: '{workflow}'
+          description: 'Installs and uses the specified work flow on the POD'
+
+      # deprecated parameters, remove after VOLTHA-2.8 is released
+      - string:
+          name: deploymentConfigFile
+          default: 'pod-configs/deployment-configs/{config-pod}.yaml'
+          description: 'Path of deployment config file'
+
+      - string:
+          name: kindVolthaValuesFile
+          default: 'pod-configs/kubernetes-configs/voltha/{config-pod}.yml'
+          description: 'Path of kind-voltha values override file'
+
+      - string:
+          name: manifestUrl
+          default: '{gerrit-server-url}/{voltha-test-manifest-repo}'
+          description: 'URL to the repo manifest'
+
+      - string:
+          name: gerritChangeNumber
+          default: '$GERRIT_CHANGE_NUMBER'
+          description: 'Changeset number in Gerrit'
+
+      - string:
+          name: gerritPatchsetNumber
+          default: '$GERRIT_PATCHSET_NUMBER'
+          description: 'PatchSet number in Gerrit'
+
+      - string:
+          name: localConfigDir
+          default: null
+          description: 'If specified, config file paths are relative to this dir; otherwise $WORKSPACE'
+
+      - string:
+          name: configRepo
+          default: 'pod-configs'
+          description: 'A repository containing the config files, will be checked out if specified'
+
+      - string:
           name: manualBranch
           default: '{manualBranch}'
           description: "If a non-empty string, build manually as a specific branch, not with a specific patchset"
 
-      - string:
-          name: extraRobotArgs
-          default: '{default-test-args}'
-          description: 'Arguments to pass to robot'
+- job-template:
+    name: '{name}'
+    id: verify_physical_voltha_patchset_auto
+    description: |
+                  <!-- Managed by Jenkins Job Builder -->
+                  Automated build on POD {config-pod} using {pipeline-script} <br /><br />
+                  Created from job-template {id} from ci-management/jjb/voltha-e2e.yaml <br />
+                  Created by Andy Bavier, andy@opennetworking.org <br />
+                  Copyright (c) 2019 Open Networking Foundation (ONF)
+    sandbox: true
+    pipeline-script: 'voltha/master/tucson-build-and-test.groovy'
+    default-test-args: '-i sanityORDeleteOLT -X'
+    branch-pattern: '{all-branches-regexp}'
+    build-node: 'tucson-pod'
+    config-pod: 'tucson-pod'
+    workflow: 'att'
+    profile: 'Default'
+    oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+    oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+    volthaSystemTestsChange: ''
+    volthaHelmChartsChange: ''
+
+    properties:
+      - cord-infra-properties:
+          build-days-to-keep: '{build-days-to-keep}'
+          artifact-num-to-keep: '{artifact-num-to-keep}'
+
+    <<: *voltha-physical-patchset-parameters
 
     project-type: pipeline
     concurrent: true
@@ -690,119 +760,25 @@
                   Created by Andy Bavier, andy@opennetworking.org <br />
                   Copyright (c) 2019 Open Networking Foundation (ONF)
     sandbox: true
-    pipeline-script: 'voltha-physical-build-and-tests.groovy'
+    build-node: 'tucson-pod'
+    config-pod: 'tucson-pod'
+    oltDebVersionMaster: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+    oltDebVersionVoltha23: 'openolt_asfvolt16-3.3.3-1a5d68b50d8bcc5ba6cb1630d3294c30c37cd2f5-40G-NNI.deb'
+    pipeline-script: 'voltha/master/tucson-build-and-test.groovy'
     trigger-string: 'hardware test'
     default-test-args: '-i sanityORDeleteOLT -X'
+    branch-pattern: '{all-branches-regexp}'
+    volthaSystemTestsChange: ''
+    volthaHelmChartsChange: ''
+    workflow: 'att'
 
     properties:
       - cord-infra-properties:
           build-days-to-keep: '{build-days-to-keep}'
           artifact-num-to-keep: '{artifact-num-to-keep}'
 
-    parameters:
-      - string:
-          name: buildNode
-          default: '{build-node}'
-          description: 'Pod management node'
+    <<: *voltha-physical-patchset-parameters
 
-      - string:
-          name: manifestUrl
-          default: '{gerrit-server-url}/{voltha-test-manifest-repo}'
-          description: 'URL to the repo manifest'
-
-      - string:
-          name: branch
-          default: '$GERRIT_BRANCH'
-          description: 'Name of the repo branch to use'
-
-      - string:
-          name: gerritProject
-          default: '$GERRIT_PROJECT'
-          description: 'Name of the Gerrit project'
-
-      - string:
-          name: gerritChangeNumber
-          default: '$GERRIT_CHANGE_NUMBER'
-          description: 'Changeset number in Gerrit'
-
-      - string:
-          name: gerritPatchsetNumber
-          default: '$GERRIT_PATCHSET_NUMBER'
-          description: 'PatchSet number in Gerrit'
-
-      - string:
-          name: gerritRefspec
-          default: '$GERRIT_REFSPEC'
-          description: 'RefSpec number in Gerrit'
-
-      - string:
-          name: cordRepoUrl
-          default: '{gerrit-server-url}'
-          description: 'The URL of the CORD Project repository'
-
-      - string:
-          name: podName
-          default: '{config-pod}'
-
-      - string:
-          name: deploymentConfigFile
-          default: 'pod-configs/deployment-configs/{config-pod}.yaml'
-          description: 'Path of deployment config file'
-
-      - string:
-          name: kindVolthaValuesFile
-          default: 'pod-configs/kubernetes-configs/voltha/{config-pod}.yml'
-          description: 'Path of kind-voltha values override file'
-
-      - string:
-          name: sadisConfigFile
-          default: 'voltha-system-tests/tests/data/{config-pod}-sadis.json'
-          description: 'Path of SADIS config to load'
-
-      - string:
-          name: localConfigDir
-          default: null
-          description: 'If specified, config file paths are relative to this dir; otherwise $WORKSPACE'
-
-      - string:
-          name: configRepo
-          default: 'pod-configs'
-          description: 'A repository containing the config files, will be checked out if specified'
-
-      - string:
-          name: oltDebVersionMaster
-          default: '{oltDebVersionMaster}'
-          description: 'OLT Software version to install for master branch builds'
-
-      - string:
-          name: oltDebVersionVoltha23
-          default: '{oltDebVersionVoltha23}'
-          description: 'OLT Software version to install for voltha-2.3 branch builds'
-
-      - string:
-          name: profile
-          default: '{profile}'
-          description: 'Technology Profile pushed to the ETCD'
-
-      - string:
-          name: notificationEmail
-          default: 'andy@opennetworking.org'
-          description: ''
-
-      - bool:
-          name: reinstallOlt
-          default: true
-          description: "Re-install OLT software"
-
-      - string:
-          name: manualBranch
-          default: '{manualBranch}'
-          description: "If a non-empty string, build manually on a specific branch, not with a specific patchset"
-
-      - string:
-          name: extraRobotArgs
-          default: '{default-test-args}'
-          description: 'Arguments to pass to robot'
 
     project-type: pipeline
     concurrent: true
@@ -827,7 +803,7 @@
               project-pattern: '^(voltha-go|voltha-openolt-adapter|voltha-openonu-adapter|voltha-openonu-adapter-go|voltha-api-server|voltha-system-tests|ofagent-py|ofagent-go|voltha-onos|kind-voltha|voltha-helm-charts)$'
               branches:
                 - branch-compare-type: REG_EXP
-                  branch-pattern: '{all-branches-regexp}'
+                  branch-pattern: '{branch-pattern}'
 
 
 - job-template:
@@ -840,7 +816,7 @@
                   Created by Andy Bavier, andy@opennetworking.org <br />
                   Copyright (c) 2019 Open Networking Foundation (ONF)
     sandbox: true
-    pipeline-script: 'voltha-physical-build-and-tests.groovy'
+    pipeline-script: 'voltha/voltha-2.7/voltha-physical-build-and-tests.groovy'
 
     properties:
       - cord-infra-properties:
diff --git a/jjb/voltha-scale.yaml b/jjb/voltha-scale.yaml
index 72032ce..eca1d68 100644
--- a/jjb/voltha-scale.yaml
+++ b/jjb/voltha-scale.yaml
@@ -390,7 +390,7 @@
 
 # list of parameters for the VOLTHA Jobs,
 # used as anchor so that can be shared across multiple jobs
-# to use in a job: `parameters: *voltha-build-job-parameters`
+# to use in a job: `parameters: *voltha-scale-job-parameters`
 - voltha-scale-job-parameters: &voltha-scale-job-parameters
     name: voltha-scale-job-parameters
     parameters: