← Back to team overview

sts-sponsors team mailing list archive

[Merge] ~lloydwaltersj/maas-ci/+git/maas-ci-config:fix-external-gh-testers into maas-ci:maas-submodules-sync

 

Jack Lloyd-Walters has proposed merging ~lloydwaltersj/maas-ci/+git/maas-ci-config:fix-external-gh-testers into maas-ci:maas-submodules-sync.

Commit message:
add missing "JOB_NAME" param to ansible-collection job creator.

Requested reviews:
  MAAS Committers (maas-committers)

For more details, see:
https://code.launchpad.net/~lloydwaltersj/maas-ci/+git/maas-ci-config/+merge/442267
-- 
Your team MAAS Committers is requested to review the proposed merge of ~lloydwaltersj/maas-ci/+git/maas-ci-config:fix-external-gh-testers into maas-ci:maas-submodules-sync.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..8b5ec61
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,52 @@
+This is the configuration for all the MAAS CI jobs running
+on MAAS' CI (see http://goo.gl/Dxz6eW for details).
+
+Installation and configuration
+==============================
+
+Install jenkins-job-builder and jenkins-jobs-mattermost.
+
+    $ pip3 install jenkins-job-builder
+    $ pip3 install git+https://github.com/jovandeginste/jenkins-jobs-mattermost/
+
+
+Job definition generation
+=========================
+
+Once you've made changes to the CI config, you can generate the
+job definition using:
+
+  $ jenkins-jobs --conf jenkins/jobs/config.ini test jenkins/jobs/ \
+        -o /tmp/jobs-definitions/
+
+If you want to compare this with what's actually running on the Jenkins
+instance, you need to compare this output with what's on the Jenkins server.
+
+
+Job definition deployment
+=========================
+
+Once you're happy with the changes, you can deploy your changes to Jenkins:
+
+  $ jenkins-jobs --conf jenkins/jobs/config.ini update jenkins/jobs/
+
+Or you can run the update script in jenkins/jobs/
+
+  $ ./update
+
+
+Generating images
+=================
+
+There's a script, bin/generate-images, that should be used to generate
+the images needed for autopkgtest.
+
+It is meant to be run on the Jenkins slave directly, from within this
+repository
+
+It uses autopkgtest-buildvm-ubuntu-cloud to build the images, and it
+passes in custom user-data in order to configure the networking for the
+autopkgtest VM.
+
+The user-data in etc/ is copied from autopkgtest-buildvm-ubuntu-cloud,
+with the addition that it replaces the network configuration at the end.
diff --git a/bin/generate-images b/bin/generate-images
new file mode 100755
index 0000000..c7fdf66
--- /dev/null
+++ b/bin/generate-images
@@ -0,0 +1,75 @@
+#!/usr/bin/python3
+"""Generate the images needed for the CI jobs.
+
+It goes through the definitions in combined.yml and gets all the
+specified releases. If the structure of the job definitiions changes,
+this script needs to be updated to match.
+
+It is meant to be run on the Jenkins slave when no CI tests are in
+progress. To speed up adding new releases, it will only generate images
+that don't already exist. If you want to regenerate an image, you will
+have to remove it first.
+
+It is assume that this repository lives in ~/maas-ci, and the generated images in ~/images.
+"""
+
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+import yaml
+
+
+IMAGES_PATH = Path('../other-os-images')
+IMAGE_NAMES = ['windows-win2012hvr2-amd64-root-dd', 'rhel7-amd64-root-tgz']
+
+
+def main():
+    with open('jenkins/jobs/combined.yml', 'r') as yaml_file:
+        job_definitions = yaml.safe_load(yaml_file)
+    releases = set()
+    for item in job_definitions:
+        if 'job-group' in item:
+            job_group = item['job-group']
+            if 'release' in job_group:
+                releases.add(job_group['release'])
+
+    images_dir = Path('..') / 'images'
+    for release in releases:
+        expected_filename = f'autopkgtest-{release}-amd64.img'
+        if not images_dir.joinpath(expected_filename).exists():
+            print(f'Generating {expected_filename}')
+            user_data_path = Path('etc/user-data')
+            release_user_data_path = Path(f'etc/user-data-{release}')
+            if release_user_data_path.exists():
+                user_data_path = release_user_data_path
+            build_vm_cmd = [
+                'autopkgtest-buildvm-ubuntu-cloud', '-r', release, '-a',
+                'amd64', '-s', '60G', '-p', 'http://squid.internal:3128',
+                '-o', str(images_dir), '--userdata', str(user_data_path),
+                '--verbose']
+            subprocess.run(build_vm_cmd, check=True)
+            print("Copying other OSes images")
+            ubuntu_image_path = images_dir.joinpath(expected_filename)
+            subprocess.run(
+                ['sudo', 'qemu-nbd', '-c', '/dev/nbd0',
+                 str(ubuntu_image_path)], check=True)
+            try:
+                subprocess.run(
+                    ['sudo', 'mount', '-t', 'auto', '/dev/nbd0p1', '/mnt'],
+                    check=True)
+                for image_name in IMAGE_NAMES:
+                    image_path = IMAGES_PATH.joinpath(image_name)
+                    shutil.copyfile(
+                        str(image_path),
+                        Path('/mnt/home/ubuntu').joinpath(image_name))
+                subprocess.run(['sudo', 'umount', '-f', '/mnt'])
+            finally:
+                subprocess.run(
+                    ['sudo', 'qemu-nbd', '-d', '/dev/nbd0'], check=True)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/bin/jenkinstrigger.py b/bin/jenkinstrigger.py
new file mode 100755
index 0000000..71dae21
--- /dev/null
+++ b/bin/jenkinstrigger.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+import sys
+import errno
+
+from bzrlib import plugin
+from bzrlib.branch import Branch
+from jenkinsapi.jenkins import Jenkins
+
+# Load plugins so we can use lp: URLs
+plugin.load_plugins()
+
+JENKINS_URL = 'http://162.213.35.104:8080/'
+JENKINS_USER = 'admin'
+JENKINS_PASSWORD = 'Ieg5eipu'
+
+
+def branch_url_to_filename(branch_url):
+    return branch_url.replace('/', '-')
+
+
+def get_current_revno(branch_url):
+    branch = Branch.open(branch_url)
+    return branch.revno()
+
+
+def get_last_revno(branch_url):
+    try:
+        last_revno = open(branch_url_to_filename(branch_url), "rb").read()
+    except IOError as e:
+        if e.errno == errno.ENOENT:
+            # Can't find the file, assume it's the first run.
+            last_revno = 0
+        else:
+            raise
+    return int(last_revno)
+
+
+def save_revno(branch_url, revno):
+    with open(branch_url_to_filename(branch_url), 'wb') as f:
+        f.write(str(revno))
+
+
+def main(argv):
+    # Usage:
+    # jenkinstrigger.py <job-name>
+    job_name = argv[1]
+    jenkins = Jenkins(JENKINS_URL, username=JENKINS_USER, password=JENKINS_PASSWORD)
+    job = jenkins.get_job(job_name)
+    for param in job.get_params():
+        if param.get('name') == 'MAAS_BRANCH':
+            maas_branch_url = param.get('defaultParameterValue')['value']
+        elif param.get('name') == 'PACKAGING_BRANCH':
+            packaging_url = param.get('defaultParameterValue')['value']
+
+    current_maas_revno = get_current_revno(maas_branch_url)
+    current_packaging_revno = get_current_revno(packaging_url)
+    last_maas_revno = get_last_revno(maas_branch_url)
+    last_packaging_revno = get_last_revno(packaging_url)
+
+    if (current_maas_revno > last_maas_revno or
+        current_packaging_revno > last_packaging_revno):
+
+        params = {
+            'MAAS_BRANCH_REVNO': current_maas_revno,
+            'PACKAGING_REVNO': current_packaging_revno,
+            'MAAS_BRANCH': maas_branch_url,
+            'PACKAGING_BRANCH': packaging_url
+        }
+        print("Building %s with %s" % (job_name, params))
+        job.invoke(build_params=params)
+        save_revno(maas_branch_url, current_maas_revno)
+        save_revno(packaging_url, current_packaging_revno)
+    else:
+        print ("Not running tests because:\n"
+            "%s (revno: %s) <= %s\n"
+            "%s (revno: %s) <= %s\n" % (
+            maas_branch_url, current_maas_revno, last_maas_revno,
+            packaging_url, current_packaging_revno, last_packaging_revno))
+
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/etc/meta-data-orobas b/etc/meta-data-orobas
new file mode 100644
index 0000000..9271c4b
--- /dev/null
+++ b/etc/meta-data-orobas
@@ -0,0 +1,18 @@
+instance-id: nocloud
+package_upgrade: false
+network:
+          version: 2
+          ethernets:
+              ens3:
+                  dhcp4: true
+              ens4:
+                  addresses:
+                  - 10.246.88.6/22
+                  - 2001:67c:1562:800a::6/64
+                  gateway4: 10.246.88.1
+                  gateway6: fe80::1
+                  nameservers:
+                      addresses:
+                      - 10.245.136.1
+                      - fe80::1
+local-hostname: autopkgtest-orobas
diff --git a/etc/orobas.cfg b/etc/orobas.cfg
new file mode 100644
index 0000000..9a1bef6
--- /dev/null
+++ b/etc/orobas.cfg
@@ -0,0 +1,24 @@
+# qemu config file
+[net]
+  type = "bridge"
+  vlan = "1"
+  br = "br0"
+  helper = "/usr/lib/qemu-bridge-helper"
+
+[net]
+  type = "nic"
+  model = "virtio"
+  macaddr = "DE:AD:BE:EF:6B:B3"
+  vlan = "1"
+
+# qemu-system-x86_64 -m 512 -nographic -monitor null -netdev bridge,id=hn0,br=br2,helper=/usr/lib/qemu/qemu-bridge-helper -device virtio-net-pci,netdev=hn0,id=nic1 -drive file=/home/andreserl/images/autopkgtest-bionic-amd64.img,if=virtio -enable-kvm
+#[netdev]
+#  type = "bridge"
+#  id = "hn0"
+#  br = "br2"
+#  helper = "/usr/lib/qemu-bridge-helper"
+#
+#[device]
+#  type = "virtio-net-pci"
+#  netdev = "hn0"
+#  id = "nic1"
diff --git a/etc/region.cfg b/etc/region.cfg
new file mode 100644
index 0000000..2996469
--- /dev/null
+++ b/etc/region.cfg
@@ -0,0 +1,17 @@
+# qemu config file
+
+# smp-opts match Jenkins host which runs testing
+[smp-opts]
+  cpus = "8"
+  sockets = "1"
+  cores = "8"
+  threads = "1"
+
+[device]
+  driver = "virtio-net-pci"
+  netdev = "br0"
+
+[netdev "br0"]
+  type = "bridge"
+  br = "br0"
+  helper = "/usr/lib/qemu-bridge-helper"
diff --git a/etc/user-data b/etc/user-data
new file mode 100644
index 0000000..c6d181d
--- /dev/null
+++ b/etc/user-data
@@ -0,0 +1,35 @@
+#cloud-config
+locale: en_US.UTF-8
+timezone: Etc/UTC
+password: ubuntu
+chpasswd: { expire: False }
+ssh_pwauth: True
+manage_etc_hosts: True
+apt_proxy: http://squid.internal:3128
+write_files:
+  - path: /etc/netplan/99-maas-networking.yaml
+    content: |
+      network:
+           version: 2
+           ethernets:
+               ens3:
+                   dhcp4: false
+
+               ens8:
+                   dhcp4: false
+                   addresses: [10.245.136.6/21]
+                   gateway4: 10.245.136.1
+                   nameservers:
+                       addresses: [10.245.136.1]
+
+runcmd:
+ - sed -i 's/deb-systemd-invoke/true/' /var/lib/dpkg/info/cloud-init.prerm
+ - mount -r /dev/vdb /mnt
+ - env AUTOPKGTEST_SETUP_VM_UPGRADE=true
+     AUTOPKGTEST_APT_SOURCES_FILE=/mnt/sources.list
+     AUTOPKGTEST_KEEP_APT_SOURCES=no,
+     sh -x /mnt/setup-testbed
+ - if grep -q 'net.ifnames=0' /proc/cmdline; then ln -s /dev/null /etc/udev/rules.d/80-net-setup-link.rules; update-initramfs -u; fi
+ - umount /mnt
+ - (while [ ! -e /var/lib/cloud/instance/boot-finished ]; do sleep 1; done;
+    rm /etc/netplan/50-cloud-init.yaml; apt-get -y purge cloud-init; shutdown -P now) &
diff --git a/etc/user-data-xenial b/etc/user-data-xenial
new file mode 100644
index 0000000..b130889
--- /dev/null
+++ b/etc/user-data-xenial
@@ -0,0 +1,40 @@
+#cloud-config
+locale: en_US.UTF-8
+timezone: Etc/UTC
+password: ubuntu
+chpasswd: { expire: False }
+ssh_pwauth: True
+manage_etc_hosts: True
+apt_proxy: http://squid.internal:3128
+write_files:
+  - path: /etc/network/interfaces
+    content: |
+        # The loopback network interface
+        auto lo
+        iface lo inet loopback
+
+        # The primary network interface
+        # Don't bring up ens3 automatically as a way to avoid bug 1349891
+        iface ens3 inet manual
+
+        auto ens4
+        iface ens4 inet static
+                address 10.245.136.6
+                netmask 255.255.248.0
+                network 10.245.136.0
+                broadcast 10.245.143.255
+                up route add default gw 10.245.136.1
+                dns-nameservers 10.245.136.1
+
+
+runcmd:
+ - sed -i 's/deb-systemd-invoke/true/' /var/lib/dpkg/info/cloud-init.prerm
+ - mount -r /dev/vdb /mnt
+ - env AUTOPKGTEST_SETUP_VM_UPGRADE=true
+     AUTOPKGTEST_APT_SOURCES_FILE=/mnt/sources.list
+     AUTOPKGTEST_KEEP_APT_SOURCES=no,
+     sh -x /mnt/setup-testbed
+ - if grep -q 'net.ifnames=0' /proc/cmdline; then ln -s /dev/null /etc/udev/rules.d/80-net-setup-link.rules; update-initramfs -u; fi
+ - umount /mnt
+ - (while [ ! -e /var/lib/cloud/instance/boot-finished ]; do sleep 1; done;
+    apt-get -y purge cloud-init; shutdown -P now) &
diff --git a/jenkins-crontab b/jenkins-crontab
new file mode 100644
index 0000000..813c569
--- /dev/null
+++ b/jenkins-crontab
@@ -0,0 +1,29 @@
+# Edit this file to introduce tasks to be run by cron.
+# 
+# Each task to run has to be defined through a single line
+# indicating with different fields when the task will be run
+# and what command to run for the task
+# 
+# To define the time you can provide concrete values for
+# minute (m), hour (h), day of month (dom), month (mon),
+# and day of week (dow) or use '*' in these fields (for 'any').# 
+# Notice that tasks will be started based on the cron's system
+# daemon's notion of time and timezones.
+# 
+# Output of the crontab jobs (including errors) is sent through
+# email to the user the crontab file belongs to (unless redirected).
+# 
+# For example, you can run a backup of all your user accounts
+# at 5 a.m every week with:
+# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
+# 
+# For more information see the manual pages of crontab(5) and cron(8)
+# 
+# m h  dom mon dow   command
+#*/6 * * * * $HOME/maas-ci/jenkinstrigger.py maas-utopic-1.7 >> $HOME/maas-ci/maas-utopic-1.7.log
+#*/6 * * * * $HOME/maas-ci/jenkinstrigger.py maas-utopic-1.8 >> $HOME/maas-ci/maas-utopic-1.8.log
+*/3 * * * * $HOME/maas-ci/jenkinstrigger.py maas-trusty-1.7 >> $HOME/maas-ci/maas-trusty-1.7.log
+*/2 * * * * $HOME/maas-ci/jenkinstrigger.py maas-trusty-1.9 >> $HOME/maas-ci/maas-trusty-1.9.log
+*/2 * * * * $HOME/maas-ci/jenkinstrigger.py maas-xenial-2.0 >> $HOME/maas-ci/maas-xenial-2.0.log
+*/2 * * * * $HOME/maas-ci/jenkinstrigger.py maas-xenial-trunk >> $HOME/maas-ci/maas-xenial-trunk.log
+*/2 * * * * $HOME/maas-ci/jenkinstrigger.py maas-yakkety-trunk >> $HOME/maas-ci/maas-yakkety-trunk.log
diff --git a/jenkins/jobs/ansible_collection.groovy b/jenkins/jobs/ansible_collection.groovy
new file mode 100644
index 0000000..be73dc5
--- /dev/null
+++ b/jenkins/jobs/ansible_collection.groovy
@@ -0,0 +1,194 @@
+def updateCommitStatus(repo, commit_sha, state, message) {
+    println "applying ${state} to ${repo} ${commit_sha} with message: ${message}"
+    step([
+        $class: "GitHubCommitStatusSetter",
+        reposSource: [$class: "ManuallyEnteredRepositorySource", url: "${repo}"],
+        commitShaSource: [$class: "ManuallyEnteredShaSource", sha: commit_sha],
+        statusResultSource: [
+            $class: "ConditionalStatusResultSource",
+            results: [[$class: "AnyBuildResult", state: state, message: message]]
+        ]
+    ])
+}
+pipeline {
+    agent {
+        docker {
+            image "ubuntu:${params.SERIES}"
+            args '-u 0:0 -m 4g'
+            label 'ci-lab'
+            registryUrl 'https://maas-ci.internal:5443'
+            registryCredentialsId 'maas-ci-registry'
+            reuseNode true
+        }
+    }
+    options {
+        disableConcurrentBuilds()
+        timeout(time: 6, unit: "HOURS")
+    }
+    stages {
+        stage('Prepare') {
+            steps {
+                withCredentials([
+                    sshUserPrivateKey(credentialsId: 'maas-lander-ssh-key', keyFileVariable: 'SSHKEY', usernameVariable: 'SSHUSER')
+                ]) {
+                    sh '''
+                        if [ ! -z \$http_proxy ]; then
+                            echo "Acquire::http::proxy \\"\$http_proxy\\"\\;" > /etc/apt/apt.conf.d/github-ci-proxy
+                            echo "Acquire::https::proxy \\"\$http_proxy\\"\\;" >> /etc/apt/apt.conf.d/github-ci-proxy
+
+                            mkdir -p ~/.ssh/
+                            echo "Host github.com\n  HostName ssh.github.com\n  Port 443\n  ProxyCommand /usr/bin/nc -X connect -x squid.internal %h %p\n" >> ~/.ssh/config
+                        fi
+                       
+                        export DEBIAN_FRONTEND=noninteractive
+                        apt-get -y update
+                        apt install -y git make sudo wget socat netcat python3-pip tox
+                        
+                        useradd ubuntu -d /home/ubuntu
+                        mkdir -p /home/ubuntu
+                        chown ubuntu:ubuntu /home/ubuntu
+                        echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-docker
+                    '''
+                }
+            }
+        }
+        stage('Checkout') {
+            steps{
+                script {
+                    currentBuild.description = "${params.GH_REPO}/${params.GH_BRANCH}"
+                }
+                sh '''
+                    mkdir -p /opt/ansible_collections/maas
+                    git clone ${GH_REPO} --branch ${GH_BRANCH} --depth 1 /opt/ansible_collections/maas/maas
+                    env -C /opt/ansible_collections/maas/maas git show --no-patch
+                '''    
+            }
+        }
+        stage('Fetch commit sha') {
+            steps {
+                script {
+                    env.COMMIT_SHA = sh(script: "git ls-remote ${params.GH_REPO} ${params.GH_BRANCH} | awk \'{print \$1}\'", returnStdout: true).trim()
+                    println "commit: ${env.COMMIT_SHA}"
+                }
+                updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "PENDING", "Tests Executing, Awaiting results")
+            }
+        }
+        stage('Install Dependencies') {
+            steps{
+                sh '''
+                    sudo -E -u ubuntu pip install git+https://github.com/maas/python-libmaas.git
+                '''
+            }
+        }
+        stage('Setup Env') {
+            steps {
+                withCredentials([string(credentialsId: 'jenkins-maas-token', variable: 'MAAS_API_TOKEN')]){
+                    sh '''
+                        export no_proxy="${no_proxy:-'localhost'},${MAAS_HOST}"
+                        export NO_PROXY="${NO_PROXY:-'localhost'},${MAAS_HOST}"
+                        export noproxy="${noproxy:-'localhost'},${MAAS_HOST}"
+                        export NOPROXY="${NOPROXY:-'localhost'},${MAAS_HOST}"
+                        
+                        INTEGRATION_CONFIG_PATH="/opt/ansible_collections/maas/maas/tests/integration/integration_config.yaml"
+                        MAAS_API_TOKEN_KEY="$(echo $MAAS_API_TOKEN | awk -F: '{print $2}')"
+                        MAAS_API_TOKEN_SECRET="$(echo $MAAS_API_TOKEN | awk -F: '{print $3}')"
+                        MAAS_API_TOKEN_CLIENT_KEY="$(echo $MAAS_API_TOKEN | awk -F: '{print $1}')"
+
+                        sudo ln -s /usr/bin/python3 /usr/bin/python
+
+                        cat <<EOF > ${INTEGRATION_CONFIG_PATH}
+                        host: "${MAAS_URL}"
+                        test_vm_host: "${MAAS_TEST_VMHOST}"
+                        test_vm_host_fqdn: "${MAAS_TEST_VMHOST_FQDN}"
+                        test_subnet: "${MAAS_TEST_SUBNET}"
+                        test_domain: "${MAAS_TEST_DOMAIN}"
+                        test_lxd_address: "${MAAS_TEST_LXD_ADDRESS}"
+                        test_virsh_host: "${MAAS_TEST_VIRSH_HOST}"
+                        token_key: "${MAAS_API_TOKEN_KEY}"
+                        token_secret: "${MAAS_API_TOKEN_SECRET}"
+                        customer_key: "${MAAS_API_TOKEN_CLIENT_KEY}"
+                        EOF
+
+                        sudo -E -u ubuntu python3 <<EOF >
+                        import os
+
+                        from maas.client import connect
+
+
+                        maas = connect(os.environ["MAAS_URL"], apikey=os.environ["MAAS_API_TOKEN"])
+
+                        rack_controller = maas.rack_controllers.get(system_id=os.environ["PRIMARY_RACK_CONTROLLER"])
+                        fabric = maas.fabrics.get(id=0)
+                        vlan = fabric.vlans.get_default()
+                        vlan.dhcp_on = True
+                        vlan.primary_rack = rack_controller
+                        vlan.save()
+                        EOF                    
+                    '''
+                }
+            }
+        }
+        stage('Test') {
+            steps {
+                withCredentials([string(credentialsId: 'jenkins-maas-token', variable: 'MAAS_API_TOKEN')]) {
+                    {% if test_lock is defined and test_lock %}
+                    lock('{{ test_lock }}') {
+                        sh '''
+                            export no_proxy="${no_proxy:-'localhost'},${MAAS_HOST}"
+                            export NO_PROXY="${NO_PROXY:-'localhost'},${MAAS_HOST}"
+                            export noproxy="${noproxy:-'localhost'},${MAAS_HOST}"
+                            export NOPROXY="${NOPROXY:-'localhost'},${MAAS_HOST}"
+                            cd /opt/ansible_collections/maas/maas
+                            sudo -E -H -u ubuntu tox -e integration
+                        '''
+                    }
+                    {% else %}
+                    sh '''
+                        export no_proxy="${no_proxy:-'localhost'},${MAAS_HOST}"
+                        export NO_PROXY="${NO_PROXY:-'localhost'},${MAAS_HOST}"
+                        export noproxy="${noproxy:-'localhost'},${MAAS_HOST}"
+                        export NOPROXY="${NOPROXY:-'localhost'},${MAAS_HOST}"
+                        cd /opt/ansible_collections/maas/maas
+                        sudo -E -H -u ubuntu tox -e integration
+                    '''
+                    {% endif %}
+                }
+            }
+        }
+    }
+    post {
+        always {
+            withCredentials([
+                string(credentialsId: 'jenkins-maas-token', variable: 'MAAS_API_TOKEN'),
+                sshUserPrivateKey(credentialsId: 'maas-lander-ssh-key', keyFileVariable: 'SSHKEY', usernameVariable: 'SSHUSER')
+            ]) {
+                sh '''
+                    sudo -E -u ubuntu python3 <<EOF>
+                        import os
+
+                        from maas.client import connect
+
+
+                        maas = connect(os.environ["MAAS_URL"], apikey=os.environ["MAAS_API_TOKEN"])
+
+                        fabric = maas.fabrics.get(id=0)
+                        vlan = fabric.vlans.get_default()
+                        vlan.dhcp_on = False
+                        vlan.save()
+                    EOF
+                '''
+            }    
+        }
+        success {
+            updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "SUCCESS", "Success")
+        }
+        failure {
+            script {
+                if (params.GITHUB_BRANCH == "main" && currentBuild.getBuildCauses("hudson.triggers.TimerTrigger$TimerTriggerCause")) {
+                    mattermostSend (color: 'red', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) [${params.GH_BRANCH}](${params.GH_REPO}/commit/${env.COMMIT_SHA}) :fire: failed")
+                }
+            }
+            updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "FAILURE", "Failed")
+        }    
+    }
+}
diff --git a/jenkins/jobs/ansible_collection.yaml b/jenkins/jobs/ansible_collection.yaml
new file mode 100644
index 0000000..2487e28
--- /dev/null
+++ b/jenkins/jobs/ansible_collection.yaml
@@ -0,0 +1,87 @@
+---
+- project:
+    name: maas-ansible-collection
+    jobs:
+      - 'gh-maas-ansible-collection-creator'
+      - 'gh-maas-ansible-collection-tester'
+
+- job-template:
+    name: gh-maas-ansible-collection-creator
+    description: |
+      Periodically check Github for open Pull Requests against Main that can be tested.
+      Builds gh-maas-ansible-tester aganst each PR
+    project-type: pipeline
+    parameters:
+      - string:
+          name: TARGET_REPO
+          description: Git repo to merge Ansible Collections into
+          default: https://github.com/maas/ansible-collection
+      - string:
+          name: JOB_NAME
+          description: The testing job to trigger when a PR is found.
+          default: gh-maas-ansible-collection-tester
+    triggers:
+      - timed: "H/15 * * * *"
+    dsl: !include-jinja2: gh_repo_tester.groovy
+
+- job-template:
+    name: gh-maas-ansible-collection-tester
+    description: |
+      Run the Ansible Collection Integration Tests
+    project-type: pipeline
+    parameters:
+      - string:
+          name: GH_REPO
+          description: 'Github repo to clone'
+          default: 'ansible-collection'
+      - string:
+          name: GH_BRANCH
+          description: 'Github branch to clone'
+          default: 'main'
+      - string:
+          name: GH_ORG
+          description: 'Github org that owns the repo to clone'
+          default: 'maas'
+      - string:
+          name: SERIES
+          description: 'The Ubuntu series to build and with'
+          default: '22.04'
+      - string:
+          name: GH_USER_NAME
+          description: 'The name to use to commit results and push them'
+          default: 'MAAS Lander'
+      - string:
+          name: GH_USER_EMAIL
+          description: 'The email to use to commit results and push them'
+          default: 'maas-lander@xxxxxxxxxxxxx'
+      - string:
+          name: MAAS_HOST
+          description: "The IP or Hostname used in the MAAS_URL, to be added to NO_PROXY"
+          default: '10.245.136.7'
+      - string:
+          name: PRIMARY_RACK_CONTROLLER
+          description: "The rack controller to manage the VLAN DHCP to be enabled on"
+          default: "ckbafg" 
+      - string:
+          name: MAAS_URL
+          description: "The URL to configure the Ansible Collection to test against"
+          default: "http://10.245.136.7:5240/MAAS";
+      - string:
+          name: MAAS_TEST_VMHOST
+          description: "The existing VM Host to use within the ansible tests"
+          default: "natasha.labmaas"
+      - string:
+          name: MAAS_TEST_DOMAIN
+          description: "The default domain of the MAAS env being tested against"
+          default: "labmaas"
+      - string:
+          name: MAAS_TEST_LXD_ADDRESS
+          description: "The LXD address of a host to be added as a VM Host in the tests"
+          default: "opelt.labmaas:5443"
+      - string:
+          name: MAAS_TEST_VIRSH_HOST
+          description: "The host or address to add as a Virsh host in the tests"
+          default: "arm64.labmaas"
+    triggers:
+      - timed: '@daily'
+    dsl: !include-jinja2: ansible_collection.groovy
diff --git a/jenkins/jobs/ansible_playbook.groovy b/jenkins/jobs/ansible_playbook.groovy
new file mode 100644
index 0000000..cc2a552
--- /dev/null
+++ b/jenkins/jobs/ansible_playbook.groovy
@@ -0,0 +1,91 @@
+def updateCommitStatus(repo, commit_sha, state, message) {
+    println "applying ${state} to ${repo} ${commit_sha} with message: ${message}"
+    step([
+        $class: "GitHubCommitStatusSetter",
+        reposSource: [$class: "ManuallyEnteredRepositorySource", url: "${repo}"],
+        commitShaSource: [$class: "ManuallyEnteredShaSource", sha: commit_sha],
+        statusResultSource: [
+            $class: "ConditionalStatusResultSource",
+            results: [[$class: "AnyBuildResult", state: state, message: message]]
+        ]
+    ])
+}
+pipeline {
+    agent {
+        label 'ci-lab'
+    }
+    options {
+        timeout(time: 6, unit: "HOURS")
+    }
+    stages {
+        stage('Checkout') {
+            steps {
+                sh """
+                rm -rf system-tests
+                git clone ${params.SYSTEMTEST_REPO} --branch ${params.SYSTEMTEST_BRANCH} --depth 1 system-tests
+                env -C system-tests git show --no-patch
+                """
+            }
+        }
+        stage('Setup') {
+            steps {
+                sh """
+                sudo apt-get install -y tox
+                """
+            }
+        }
+        stage('Fetch commit sha') {
+            steps {
+                script {
+                    env.COMMIT_SHA = sh(script: "git ls-remote ${params.GITHUB_REPO} ${params.GITHUB_BRANCH} | awk \'{print \$1}\'", returnStdout: true).trim()
+                    println "commit: ${env.COMMIT_SHA}"
+                }
+                updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "PENDING", "Tests Executing, Awaiting results")
+            }
+        }
+        stage('System Tests') {
+            steps {
+                writeFile file: 'system-tests/config.yaml', text: """
+                proxy:
+                    http: http://squid.internal:3128
+                containers-image: ${params.CONTAINERS_IMAGE}
+                ansible-playbooks:
+                    git-repo: ${params.GITHUB_REPO}
+                    git-branch: ${params.GITHUB_BRANCH}
+                    verbosity: ${params.DEBUG_LEVEL}
+                """
+                dir("system-tests") {
+                    sh """
+                    cat config.yaml
+                    export http_proxy=http://squid.internal:3128/
+                    export https_proxy=http://squid.internal:3128/
+                    tox r -e cog
+                    tox r -e ansible_tests -- ${params.PYTEST_ARGS}
+                    """
+                }
+            }
+        }
+    }
+    post {
+        always {
+            archiveArtifacts artifacts:'system-tests/*.log,system-tests/config.yaml,system-tests/junit*.xml', fingerprint: true
+            junit allowEmptyResults: true, testResults: 'system-tests/junit*.xml'
+            sh """
+                lxc delete ansible-main --force || true
+                lxc delete ansible-host-1 --force || true
+                lxc delete ansible-host-2 --force || true
+            """
+        }
+        success {
+            updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "SUCCESS", "Success")
+        }
+        failure {
+            script {
+                if (params.GITHUB_BRANCH == "main" && currentBuild.getBuildCauses("hudson.triggers.TimerTrigger$TimerTriggerCause")) {
+                    mattermostSend (color: 'red', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) [${params.GITHUB_BRANCH}](${params.GITHUB_REPO}/commit/${env.COMMIT_SHA}) :fire: failed")
+                }
+            }
+            updateCommitStatus(params.TARGET_REPO, env.COMMIT_SHA, "FAILURE", "Failed")
+        }
+    }
+}
diff --git a/jenkins/jobs/ansible_playbook.yaml b/jenkins/jobs/ansible_playbook.yaml
new file mode 100644
index 0000000..d5f5e67
--- /dev/null
+++ b/jenkins/jobs/ansible_playbook.yaml
@@ -0,0 +1,65 @@
+- project:
+    name: ansible-playbook-tests
+    jobs:
+      - ansible-job-creator
+      - ansible-playbook-tests
+
+- job-template:
+    name: ansible-job-creator
+    description: |
+      Periodically check GitHub for open Pull requests against Main that can be tested. Builds ansible-playbook-tests against each PR.
+    project-type: pipeline
+    parameters:
+      - string:
+          name: TARGET_REPO
+          description: Git repo to merge Ansible playbook into
+          default: https://github.com/maas/maas-ansible-playbook
+      - string:
+          name: JOB_NAME
+          description: The testing job to trigger when a PR is found.
+          default: ansible-playbook-tests
+    triggers:
+      - timed: "H/15 * * * *"
+    dsl: !include-jinja2: gh_repo_tester.groovy
+
+- job-template:
+    name: ansible-playbook-tests
+    description: |
+      Run the Ansible Playbook system tests, report the result back to mattermost and GitHub.
+    project-type: pipeline
+    parameters:
+      - string:
+          name: SYSTEMTEST_REPO
+          description: Git repo to pull system tests from
+          default: https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests
+      - string:
+          name: SYSTEMTEST_BRANCH
+          description: Branch in the repo to use
+          default: master
+      - string:
+          name: GITHUB_REPO
+          description: Git repo to pull Ansible playbook from
+          default: https://github.com/maas/maas-ansible-playbook
+      - string:
+          name: GITHUB_BRANCH
+          description: Branch in the Ansible playbook repo to use
+          default: main
+      - string:
+          name: TARGET_REPO
+          description: Git repo to merge Ansible playbook into
+          default: https://github.com/maas/maas-ansible-playbook
+      - string:
+          name: PYTEST_ARGS
+          description: Additional args to pytest
+          default: --log-cli-level info
+      - string:
+          name: DEBUG_LEVEL
+          description: Debug Level to use in the ansible playbooks (ie -v)
+          default: 
+      - string:
+          name: CONTAINERS_IMAGE
+          description: LXD image for containers used in tests
+          default: ubuntu:22.04
+    triggers:
+      - timed: '@daily'
+    dsl: !include-jinja2: ansible_playbook.groovy
diff --git a/jenkins/jobs/combined.yml b/jenkins/jobs/combined.yml
new file mode 100644
index 0000000..2a6526a
--- /dev/null
+++ b/jenkins/jobs/combined.yml
@@ -0,0 +1,1144 @@
+####################################################################################################
+######################################## MACROS ####################################################
+####################################################################################################
+
+- builder:
+    name: maas_adt_git
+    builders:
+      - shell: |
+            #!/bin/bash -ex
+            # Clean-up testing env
+            rm -Rf *
+            export http_proxy="${HTTP_PROXY_ENV}"
+            export https_proxy="${HTTP_PROXY_ENV}"
+            #
+            # Parameter passed to autopkgtest to perform updates in the
+            # testing environment. This does 'apt upgrade'.
+            UPDATES="-U"
+            #
+            # Parameter that defines the RAM size of the VM where MAAS
+            # runs. If it is not set, it will default to 8192 (8G).
+            if [ ! $VM_RAM_SIZE ]; then
+                VM_RAM_SIZE="8192"
+            fi
+            #
+            # If $TEST_PERFORMANCE env variable doesn't exist, set it
+            # here so it doesn't cause the script to fail.
+            if [ ! $TEST_PERFORMANCE ]; then
+                TEST_PERFORMANCE=0
+            fi
+            #
+            # If $CLOUDINIT_PROPOSED env variable doesn't exist, set it
+            # to 0 to ensure it is not mistakenly used.
+            if [ ! $CLOUDINIT_PROPOSED ]; then
+                CLOUDINIT_PROPOSED=0
+            fi
+            #
+            # Build proposed images
+            # Checkout lp:maas-images if it is defined:
+            TEST_CUSTOM_IMAGES=0
+            if [ $MAAS_IMAGES_REPO ]; then
+                TEST_CUSTOM_IMAGES=1
+                git clone ${MAAS_IMAGES_REPO} maas-images
+                PATH=$PATH:./maas-images/bin
+                # Determine what architectures to create streams for
+                STREAM_ARCHES="amd64"
+                if [ $USE_PPC_NODES -ge 1 ]; then
+                    STREAM_ARCHES="$STREAM_ARCHES,ppc64el"
+                fi
+                if [ $USE_ARM64_NODES -ge 1 ]; then
+                    STREAM_ARCHES="$STREAM_ARCHES,arm64"
+                fi
+                # Create streams for supported LTSes
+                STREAM_RELEASES="xenial|bionic|focal"
+                # If deployment release is different (e.g., non-LTS) we add it to the
+                # streams creation
+                if [ ! $DEPLOYMENT_RELEASE ]; then
+                    DEPLOYMENT_RELEASE="$RELEASE"
+                elif [ $DEPLOYMENT_RELEASE != $RELEASE ]; then
+                    STREAM_RELEASES="$STREAM_RELEASES|$DEPLOYMENT_RELEASE"
+                fi
+                # Put the images in $WORKSPACE/../ so that it is a shared dir across jobs
+                workspace_root=$(dirname $WORKSPACE)
+                rm -Rf $workspace_root/www/html/images/*
+                if [ ! -d $workspace_root/www/html/images ]; then
+                    mkdir -p $workspace_root/www/html/images
+                    # NOTE: This may fail, if it does, we may have to create this manually
+                    sudo ln -sf $workspace_root/www/html/images /var/www/html/images || true
+                fi
+                # Create images
+                if [ $CLOUDINIT_PROPOSED -eq 0 ]; then
+                    meph2-cloudimg-sync --config ./maas-images/conf/meph-v3.yaml --target=force --arches=${STREAM_ARCHES} --max=1 $workspace_root/www/html/images/ "release~(${STREAM_RELEASES})" || exit $?
+                else
+                    meph2-cloudimg-sync --config ./maas-images/conf/meph-v3.yaml --target=force --proposed --arches=${STREAM_ARCHES} --max=1 $workspace_root/www/html/images/ "release~(${STREAM_RELEASES})" || exit $?
+                fi
+                # Create & merge bootloaders
+                meph2-import --proposed --no-sign ./maas-images/conf/bootloaders.yaml ./bootloaders || exit $?
+                meph2-util merge --no-sign ./bootloaders $workspace_root/www/html/images || exit $?
+                # Create & merge CentOS
+                if [ $TEST_CENTOS -ge 1 ]; then
+                    meph2-import --no-sign --max=1 ./maas-images/conf/centos.yaml ./centos || exit $?
+                    meph2-util merge --no-sign ./centos $workspace_root/www/html/images || exit $?
+                fi
+            fi
+            #
+            # Checkout qa-lab-tests
+            bzr checkout --lightweight ${TESTS_BRANCH} qa-lab-tests
+            #
+            # If TEST_PERFORMANCE >= 1, we enable the test of performance tests.
+            # As such, we need to use the performance suite as the control file
+            # instead of the default.
+            if [ $TEST_PERFORMANCE -ge 1 ]; then
+                mv qa-lab-tests/performance-control qa-lab-tests/control
+                # When testing performance, don't update the environment
+                # to gain speed.
+                UPDATES=""
+            fi
+            #
+            # Checkout maas branches if needed, otherwise use dummy package
+            # MAAS_BRANCH = e.g. https://git.launchpad.net/maas
+            # MAAS_BRANCH_ID = e.g. master, 2.1, 2.2, etc
+            # MAAS_BRANCH_REVNO = e.g. HEAD, 179e609cb112041c661e4dd249ef039709e005c4
+            if [ $MAAS_PROPOSED -eq 1 ] || [ ! $MAAS_BRANCH ] || [ $CURTIN_PROPOSED -eq 1 ] || [ $CLOUDINIT_PROPOSED -eq 1 ]; then
+                mv qa-lab-tests/dummy maas
+            else
+                git clone --depth 100 --single-branch --branch ${MAAS_BRANCH_ID} ${MAAS_BRANCH} maas
+                cd maas/
+                git checkout ${MAAS_BRANCH_ID}
+                git reset --hard ${MAAS_BRANCH_REVNO}
+                git submodule update --init --recursive
+                cd ../
+            fi
+            # MAAS and Curtin unbuilt trees to pass to adt.
+            UNBUILT_MAAS_TREE=${PWD}/maas
+            if grep "^package-tree" ${UNBUILT_MAAS_TREE}/Makefile;
+            then
+                # From 2.7, the source tarball is no longer a clean
+                # export of the git tree.
+                make -C ${UNBUILT_MAAS_TREE} package-tree
+                PACKAGE_BUILD_AREA=${UNBUILT_MAAS_TREE}/../build-area
+                UNBUILT_MAAS_TREE=$(
+                    find "$PACKAGE_BUILD_AREA" -mindepth 1 -maxdepth 1 -type d | head -n1)
+            fi
+            #
+            # Move working trees into maas branch.
+            # bzr can't branch inside existing branches without a bit of
+            # pain and merge-into plugin doesn't work with new repo format
+            # (see LP: #486245)
+            mv qa-lab-tests ${UNBUILT_MAAS_TREE}/debian/tests
+            #
+            # Obtain revno count from the git revno.
+            export MAAS_BRANCH_REVNO_COUNT=$(git -C maas rev-list --count $MAAS_BRANCH_REVNO)
+            # Overwrite the MAAS_BRANCH_REVNO var with the short revno.
+            export MAAS_BRANCH_REVNO=$(git -C maas rev-parse --short $MAAS_BRANCH_REVNO)
+            # Obtain the version the packaging will use
+            export VER_FOR_PKG="$MAAS_BRANCH_REVNO_COUNT-g$MAAS_BRANCH_REVNO"
+            # XXX: matsubara For now, branch maas-ci-config branch to be able to
+            # source config files. Once the MAAS CI lab is fully charmed and
+            # maas-ci-config works together with the ci-configurator charm,
+            # this won't be necessary anymore.
+            git clone git+ssh://git.launchpad.net/~maas-committers/maas-ci/+git/maas-ci-config
+            source maas-ci-config/maas-ci-common
+            #
+            # Update changelog entry to easily check in the logs the versions
+            # used to build the package.
+            ${UNBUILT_MAAS_TREE}/debian/tests/update_changelog.py ${UNBUILT_MAAS_TREE}/debian/changelog ${VER_FOR_PKG} ci
+            #
+            SETUP_COMMANDS="export http_proxy=${HTTP_PROXY_ENV}; export https_proxy=${HTTP_PROXY_ENV}"
+            # Configure environment setup commands.
+            if [ ! -z $MAAS_PPA ]; then
+                SETUP_COMMANDS="$SETUP_COMMANDS; add-apt-repository -y ${MAAS_PPA}"
+            fi
+            if [ ! -z $CURTIN_PPA ]; then
+                SETUP_COMMANDS="$SETUP_COMMANDS; add-apt-repository -y ${CURTIN_PPA}"
+            fi
+            if [ ! -z $TEST_JUJU ]; then
+                SETUP_COMMANDS="$SETUP_COMMANDS; add-apt-repository -y ppa:juju/devel"
+            fi
+            SETUP_COMMANDS="$SETUP_COMMANDS; apt-get update"
+            # Configure apt environment for proposed.
+            PACKAGES_FROM_PROPOSED=""
+            if [ $CURTIN_PROPOSED -eq 1 ] && [ $MAAS_PROPOSED -eq 1 ]; then
+                PACKAGES_FROM_PROPOSED="--apt-pocket=proposed=src:maas,src:curtin"
+            elif [ $CURTIN_PROPOSED -eq 1 ] && [ $MAAS_PROPOSED -eq 0 ]; then
+                PACKAGES_FROM_PROPOSED="--apt-pocket=proposed=src:curtin"
+            elif [ $CURTIN_PROPOSED -eq 0 ] && [ $MAAS_PROPOSED -eq 1 ]; then
+                PACKAGES_FROM_PROPOSED="--apt-pocket=proposed=src:maas"
+            fi
+
+            #
+            # Run adt.
+            QEMU_CFG=${WORKSPACE}/maas-ci-config/etc/region.cfg
+            QEMU_IMG=${ADT_IMAGES_ROOT}/autopkgtest-${RELEASE}-amd64.img
+            if [ ${CURTIN_PPA} ]; then
+                echo CURTIN_PPA is set - running with Curtin from ${CURTIN_PPA}
+                ${AUTOPKGTEST_ROOT}/runner/autopkgtest ${UPDATES} -ddd --output-dir ${WORKSPACE}/results \
+                --env DEPLOYMENT_RELEASE=${DEPLOYMENT_RELEASE} \
+                --env TEST_JUJU=${TEST_JUJU} \
+                --env TEST_CENTOS=${TEST_CENTOS} \
+                --env TEST_RHEL=${TEST_RHEL} \
+                --env TEST_WINDOWS=${TEST_WINDOWS} \
+                --env TEST_CUSTOM_IMAGES=${TEST_CUSTOM_IMAGES} \
+                --env USE_PPC_NODES=${USE_PPC_NODES} \
+                --env USE_ARM64_NODES=${USE_ARM64_NODES} \
+                --env KEEP_CI_RUNNING=${KEEP_CI_RUNNING} \
+                --env PAUSE_CI=${PAUSE_CI} \
+                --env TEST_PERFORMANCE=${TEST_PERFORMANCE} \
+                --env DEBUG=${DEBUG} \
+                --env SLAVE_NODE=${NODE_NAME} \
+                --setup-commands="$SETUP_COMMANDS" \
+                --timeout-test=20000 \
+                $UNBUILT_MAAS_TREE \
+                -- ${AUTOPKGTEST_ROOT}/virt/autopkgtest-virt-qemu \
+                -d --show-boot \
+                --ram-size ${VM_RAM_SIZE} \
+                --qemu-options -readconfig\ ${QEMU_CFG} ${QEMU_IMG}
+            else
+                echo running with Curtin from archive
+                ${AUTOPKGTEST_ROOT}/runner/autopkgtest ${UPDATES} -ddd --output-dir ${WORKSPACE}/results \
+                --env DEPLOYMENT_RELEASE=${DEPLOYMENT_RELEASE} \
+                --env TEST_JUJU=${TEST_JUJU} \
+                --env TEST_CENTOS=${TEST_CENTOS} \
+                --env TEST_RHEL=${TEST_RHEL} \
+                --env TEST_WINDOWS=${TEST_WINDOWS} \
+                --env TEST_CUSTOM_IMAGES=${TEST_CUSTOM_IMAGES} \
+                --env USE_PPC_NODES=${USE_PPC_NODES} \
+                --env USE_ARM64_NODES=${USE_ARM64_NODES} \
+                --env KEEP_CI_RUNNING=${KEEP_CI_RUNNING} \
+                --env PAUSE_CI=${PAUSE_CI} \
+                --env TEST_PERFORMANCE=${TEST_PERFORMANCE} \
+                --env DEBUG=${DEBUG} \
+                --env SLAVE_NODE=${NODE_NAME} \
+                --setup-commands="$SETUP_COMMANDS" \
+                --timeout-test=20000 \
+                $PACKAGES_FROM_PROPOSED \
+                $UNBUILT_MAAS_TREE \
+                -- ${AUTOPKGTEST_ROOT}/virt/autopkgtest-virt-qemu \
+                -d --show-boot \
+                --ram-size ${VM_RAM_SIZE} \
+                --qemu-options -readconfig\ ${QEMU_CFG} ${QEMU_IMG}
+            fi
+
+            RET=$?
+
+            # EXIT STATUS
+            # 0    all tests passed
+            # 1    unexpected failure (the python interpreter invents this exit status)
+            # 2    at least one test skipped
+            # 4    at least one test failed
+            # 6    at least one test failed and at least one test skipped
+            # 8    no tests in this package
+            # 12   erroneous package
+            # 16   testbed failure
+            # 20   other unexpected failures including bad usage
+
+            # Return "at least one test skipped" as Passed
+            if [ $RET -eq 2 ]; then
+                RET=0
+            fi
+
+            exit $RET
+
+- builder:
+    name: maas_adt_ea
+    builders:
+        - shell: |
+            #!/bin/bash -ex
+            # Clean-up testing env
+            rm -Rf *
+            export http_proxy="${HTTP_PROXY_ENV}"
+            export https_proxy="${HTTP_PROXY_ENV}"
+            # Checkout maas branches
+            # MAAS_BRANCH = e.g. https://git.launchpad.net/maas
+            # MAAS_BRANCH_ID = e.g. master, 2.1, 2.2, etc
+            # MAAS_BRANCH_REVNO = e.g. HEAD, 179e609cb112041c661e4dd249ef039709e005c4
+            git clone --depth 100 --single-branch --branch ${MAAS_BRANCH_ID} ${MAAS_BRANCH} maas
+            cd maas/
+            git checkout ${MAAS_BRANCH_ID}
+            git reset --hard ${MAAS_BRANCH_REVNO}
+            git submodule update --init --recursive
+            cd ../
+            #
+            # Obtain revno count from the git revno.
+            export MAAS_BRANCH_REVNO_COUNT=$(git -C maas rev-list --count $MAAS_BRANCH_REVNO)
+            # Overwrite the MAAS_BRANCH_REVNO var with the short revno.
+            export MAAS_BRANCH_REVNO=$(git -C maas rev-parse --short $MAAS_BRANCH_REVNO)
+            # Obtain the version the packaging will use
+            export VER_FOR_PKG="$MAAS_BRANCH_REVNO_COUNT-g$MAAS_BRANCH_REVNO"
+            #
+            # Checkout qa-lab-tests
+            bzr checkout --lightweight ${TESTS_BRANCH} qa-lab-tests
+            # XXX: matsubara For now, branch maas-ci-config branch to be able to
+            # source config files. Once the MAAS CI lab is fully charmed and
+            # maas-ci-config works together with the ci-configurator charm,
+            # this won't be necessary anymore.
+            git clone git+ssh://git.launchpad.net/~maas-committers/maas-ci/+git/maas-ci-config
+            source maas-ci-config/maas-ci-common
+
+            SETUP_COMMANDS="export http_proxy=${HTTP_PROXY_ENV}; export https_proxy=${HTTP_PROXY_ENV}"
+            # Configure environment setup commands.
+            if [ ! -z $MAAS_PPA ]; then
+                SETUP_COMMANDS="$SETUP_COMMANDS; add-apt-repository -y ${MAAS_PPA}"
+            fi
+            if [ ! -z $CURTIN_PPA ]; then
+                SETUP_COMMANDS="$SETUP_COMMANDS; add-apt-repository -y ${CURTIN_PPA}"
+            fi
+            SETUP_COMMANDS="$SETUP_COMMANDS; apt-get update"
+            #
+            # Move working trees into maas branch.
+            # bzr can't branch inside existing branches without a bit of
+            # pain and merge-into plugin doesn't work with new repo format
+            # (see LP: #486245)
+            mv qa-lab-tests maas/debian/tests
+            #
+            # Update changelog entry to easily check in the logs the versions
+            # used to build the package.
+            ./maas/debian/tests/update_changelog.py maas/debian/changelog ${VER_FOR_PKG} ci
+            # MAAS and Curtin unbuilt trees to pass to adt.
+            UNBUILT_MAAS_TREE=${PWD}/maas
+            if grep "^package-tree" ${UNBUILT_MAAS_TREE}/Makefile;
+            then
+                # From 2.7, the source tarball is no longer a clean
+                # export of the git tree.
+                make -C ${UNBUILT_MAAS_TREE} package-tree
+                PACKAGE_BUILD_AREA=${UNBUILT_MAAS_TREE}/../build-area
+                UNBUILT_MAAS_TREE=$(
+                    find "$PACKAGE_BUILD_AREA" -mindepth 1 -maxdepth 1 -type d | head -n1)
+            fi
+            #
+            # Run adt.
+            QEMU_CFG=${WORKSPACE}/maas-ci-config/etc/region.cfg
+            QEMU_IMG=${ADT_IMAGES_ROOT}/autopkgtest-${RELEASE}-amd64.img
+            if [ ${CURTIN_PPA} ]; then
+                echo CURTIN_PPA is set - running with Curtin from ${CURTIN_PPA}
+                ${AUTOPKGTEST_ROOT}/runner/autopkgtest -U -ddd --output-dir ${WORKSPACE}/results \
+                --env TEST_WINDOWS=1 \
+                --env TEST_CENTOS=1 \
+                --env USE_DELL_R630_SYSTEMS=1 \
+                --env KEEP_CI_RUNNING=${KEEP_CI_RUNNING} \
+                --env PAUSE_CI=${PAUSE_CI} \
+                --env DEBUG=${DEBUG} \
+                --setup-commands="$SETUP_COMMANDS" \
+                $UNBUILT_MAAS_TREE \
+                -- ${AUTOPKGTEST_ROOT}/virt/autopkgtest-virt-qemu \
+                -d --show-boot \
+                --ram-size 3072 \
+                --qemu-options -readconfig\ ${QEMU_CFG} ${QEMU_IMG}
+            else
+                echo running with Curtin from archive
+                ${AUTOPKGTEST_ROOT}/runner/autopkgtest -U -ddd --output-dir ${WORKSPACE}/results \
+                --env TEST_WINDOWS=1 \
+                --env TEST_CENTOS=1 \
+                --env USE_DELL_R630_SYSTEMS=1 \
+                --env KEEP_CI_RUNNING=${KEEP_CI_RUNNING} \
+                --env PAUSE_CI=${PAUSE_CI} \
+                --env DEBUG=${DEBUG} \
+                --setup-commands="$SETUP_COMMANDS" \
+                $UNBUILT_MAAS_TREE \
+                -- ${AUTOPKGTEST_ROOT}/virt/autopkgtest-virt-qemu \
+                -d --show-boot \
+                --ram-size 3072 \
+                --qemu-options -readconfig\ ${QEMU_CFG} ${QEMU_IMG}
+            fi
+
+            RET=$?
+
+            # EXIT STATUS
+            # 0    all tests passed
+            # 1    unexpected failure (the python interpreter invents this exit status)
+            # 2    at least one test skipped
+            # 4    at least one test failed
+            # 6    at least one test failed and at least one test skipped
+            # 8    no tests in this package
+            # 12   erroneous package
+            # 16   testbed failure
+            # 20   other unexpected failures including bad usage
+
+            # Return "at least one test skipped" as Passed
+            if [ $RET -eq 2 ]; then
+                RET=0
+            fi
+
+            exit $RET
+
+- parameter:
+    name: notes
+    parameters:
+        - string:
+            name: Notes
+            description: What and why this build is what it is.
+            default: ''
+
+- parameter:
+    name: debug
+    parameters:
+        - string:
+            name: DEBUG
+            default: '1'
+            description: Whether to enable debug logging in MAAS (0 False, 1 True)
+
+- parameter:
+    name: maas_branch
+    parameters:
+        - string:
+            name: MAAS_BRANCH
+            description: MAAS default git repository (https://git.launchpad.net/maas) or custom (https://git.launchpad.net/~<username>/<repository>)
+            default: '{maas_branch}'
+
+- parameter:
+    name: maas_branch_id
+    parameters:
+        - string:
+            name: MAAS_BRANCH_ID
+            description: Branch inside repository (e.g. master, custom_branch)
+            default: '{maas_branch_id}'
+
+- parameter:
+    name: maas_images_repo
+    parameters:
+        - string:
+            name: MAAS_IMAGES_REPO
+            description: Identifier for the branch MAAS Images will use.
+            default: '{maas_images_repo}'
+
+- parameter:
+    name: maas_repo_url
+    parameters:
+        - string:
+            name: MAAS_REPO_URL
+            description: Identifier for the branch MAAS will use.
+            default: '{maas_repo_url}'
+
+- parameter:
+    name: maas_branch_revno
+    parameters:
+        - string:
+            name: MAAS_BRANCH_REVNO
+            description: Branch revision (e.g. HEAD, c9ad0ba, etc)
+            default: '{maas_branch_revno}'
+
+- parameter:
+    name: tests_branch
+    parameters:
+        - string:
+            name: TESTS_BRANCH
+            description: Which integration tests branch to use.
+            default: '{tests_branch}'
+
+- parameter:
+    name: release
+    parameters:
+        - string:
+            name: RELEASE
+            default: '{release}'
+            description: The release to test MAAS on.
+
+- parameter:
+    name: deployment_release
+    parameters:
+        - string:
+            name: DEPLOYMENT_RELEASE
+            default: '{deployment_release}'
+            description: The release used to deploy with if different from RELEASE (e.g. xenial)
+
+- parameter:
+    name: ppa
+    parameters:
+        - string:
+            name: PPA
+            default: '{ppa}'
+            description: The PPA MAAS will be pulled from.
+
+- parameter:
+    name: architecture
+    parameters:
+        - string:
+            name: ARCH
+            default: amd64
+
+- parameter:
+    name: http_proxy_env
+    parameters:
+        - string:
+            name: HTTP_PROXY_ENV
+            default: http://squid.internal:3128
+            description: Default proxy to use in the lab.
+
+- parameter:
+    name: curtin_ppa
+    parameters:
+        - string:
+            name: CURTIN_PPA
+            default: '{curtin_ppa}'
+            description: PPA to take Curtin from
+
+- parameter:
+    name: curtin_proposed
+    parameters:
+        - string:
+            name: CURTIN_PROPOSED
+            default: '{curtin_proposed}'
+            description: Whether to use curtin from -proposed. 0 False. 1 True.
+
+- parameter:
+    name: cloudinit_proposed
+    parameters:
+        - string:
+            name: CLOUDINIT_PROPOSED
+            default: '{cloudinit_proposed}'
+            description: Whether to use cloudinit from -proposed. 0 False. 1 True.
+
+- parameter:
+    name: maas_proposed
+    parameters:
+        - string:
+            name: MAAS_PROPOSED
+            default: '{maas_proposed}'
+            description: Whether to test MAAS from -proposed. 0 False. 1 True.
+
+- parameter:
+    name: maas_ppa
+    parameters:
+        - string:
+            name: MAAS_PPA
+            default: '{maas_ppa}'
+            description: PPA where to get MAAS from (used to verified releases from PPA).
+
+- parameter:
+    name: test_juju
+    parameters:
+        - string:
+            name: TEST_JUJU
+            default: '{test_juju}'
+            description: Whether to test Juju with this MAAS install
+
+- parameter:
+    name: test_windows
+    parameters:
+        - string:
+            name: TEST_WINDOWS
+            default: '{test_windows}'
+            description: Whether to test Windows with this MAAS install
+
+- parameter:
+    name: use_ppc_nodes
+    parameters:
+        - string:
+            name: USE_PPC_NODES
+            default: '{use_ppc_nodes}'
+            description: Whether to use the PPC nodes in the lab
+
+- parameter:
+    name: use_arm64_nodes
+    parameters:
+        - string:
+            name: USE_ARM64_NODES
+            default: '{use_arm64_nodes}'
+            description: Whether to use the ARM64 nodes in the lab
+
+- parameter:
+    name: keep_ci_running
+    parameters:
+        - string:
+            name: KEEP_CI_RUNNING
+            default: '0'
+            description: Keep the CI running after integration tests have finished (or failed).
+
+- parameter:
+    name: pause_ci
+    parameters:
+        - string:
+            name: PAUSE_CI
+            default: '0'
+            description: Pause CI right after new machines have been powered on for enlistment.
+
+- parameter:
+    name: test_performance
+    parameters:
+        - string:
+            name: TEST_PERFORMANCE
+            default: '{test_performance}'
+            description: Test performance by running pods with VM's per deployed machine.
+
+- parameter:
+    name: vm_ram_size
+    parameters:
+        - string:
+            name: VM_RAM_SIZE
+            default: ''
+            description: RAM size for the VM where MAAS runs on. Defaults to 3072
+
+- parameter:
+    name: test_centos
+    parameters:
+        - string:
+            name: TEST_CENTOS
+            default: '{test_centos}'
+            description: Whether or not to test CentOS
+
+- parameter:
+    name: test_rhel
+    parameters:
+        - string:
+            name: TEST_RHEL
+            default: '{test_rhel}'
+            description: Whether or not to test RHEL
+
+- parameter:
+    name: node_name
+    parameters:
+        - string:
+            name: NODE_NAME
+            default: '{node_name}'
+            description: Define in which node to test
+
+# This is just a parameter that defines a job name when creating a job based on a job group
+# and a template.
+- parameter:
+    name: job_name
+    parameters:
+        - string:
+            name: JOB_NAME
+            default: '{job_name}'
+            description: Just a parameter to define a job name.
+
+
+- publisher:
+    name: archive_build_artifacts
+    publishers:
+        - archive:
+            artifacts: 'results/*stderr,results/*stdout,results/*log,results/log,results/*.crash,results/**/*'
+        - junit:
+            results: 'results/artifacts/nosetests.xml'
+            keep-long-stdio: true
+            allow-empty-results: true
+
+- publisher:
+    name: mattermost_announce
+    publishers:
+      - mattermost:
+          notify-start: false
+          notify-aborted: false
+          notify-success: true
+          notify-notbuilt: false
+          notify-unstable: false
+          notify-failure: true
+          notify-backtonormal: false
+          notify-repeatedfailure: false
+          include-test-summary: false
+          show-commit-list: false
+
+- wrapper:
+    name: build_timeout
+    wrappers:
+        - timeout:
+            timeout: 180
+            fail: true
+            type: absolute
+
+- scm:
+    name: maas-branch
+    scm:
+      - git:
+          url: https://git.launchpad.net/maas
+          branches:
+            - '*/${MAAS_BRANCH_ID}'
+          browser: cgit
+          browser-url: https://git.launchpad.net/maas
+          do-not-fetch-tags: true
+          shallow-clone: true
+          timeout: 15 # bumped from 10 mins to account for PS4½ issues
+
+####################################################################################################
+######################################## JOB TEMPLATES #############################################
+####################################################################################################
+
+- job-template:
+    name: 'proposed-maas-images-manual'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    properties:
+      - build-discarder:
+          days-to-keep: 90
+          artifact-days-to-keep: 30
+    parameters:
+        - release:
+            release: focal
+        - maas_branch:
+            maas_branch: https://git.launchpad.net/maas
+        - maas_branch_id:
+            maas_branch_id: 'master'
+        - maas_branch_revno:
+            maas_branch_revno: 'HEAD'
+        - deployment_release:
+            deployment_release: ''
+        - maas_images_repo:
+            maas_images_repo: 'https://git.launchpad.net/maas-images'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - curtin_proposed:
+            curtin_proposed: '0'
+        - curtin_ppa:
+            curtin_ppa: ''
+        - use_arm64_nodes:
+            use_arm64_nodes: '0'
+        - use_ppc_nodes:
+            use_ppc_nodes: '0'
+        - test_centos:
+            test_centos: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+
+- job-template:
+    name: 'maas-{release}-{maas_branch_id}-git'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    properties:
+      - build-discarder:
+          days-to-keep: 90
+          artifact-days-to-keep: 30
+    parameters:
+        - maas_branch:
+            maas_branch: '{maas_branch}'
+        - maas_branch_id:
+            maas_branch_id: '{maas_branch_id}'
+        - maas_branch_revno:
+            maas_branch_revno: '{maas_branch_revno}'
+        - tests_branch:
+            tests_branch: '{tests_branch}'
+        - maas_ppa:
+            maas_ppa: '{maas_ppa}'
+        - curtin_ppa:
+            curtin_ppa: '{curtin_ppa}'
+        - release:
+            release: '{release}'
+        - test_rhel:
+            test_rhel: '0'
+        - test_centos:
+            test_centos: '1'
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    scm:
+        - maas-branch
+    triggers:
+        - pollscm:
+            cron: "*/15 * * * *"
+            ignore-post-commit-hooks: true
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: 'maas-{release}-{maas_branch_id}-git-ea'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    properties:
+      - build-discarder:
+          days-to-keep: 90
+          artifact-days-to-keep: 30
+    parameters:
+        - maas_branch:
+            maas_branch: '{maas_branch}'
+        - maas_branch_id:
+            maas_branch_id: '{maas_branch_id}'
+        - maas_branch_revno:
+            maas_branch_revno: '{maas_branch_revno}'
+        - maas_ppa:
+            maas_ppa: '{maas_ppa}'
+        - tests_branch:
+            tests_branch: '{tests_branch}'
+        - curtin_ppa:
+            curtin_ppa: '{curtin_ppa}'
+        - release:
+            release: '{release}'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_ea
+    triggers:
+        - pollscm:
+            cron: "H/15 * * * *"
+            ignore-post-commit-hooks: true
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: '{job_name}-manual'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    properties:
+      - build-discarder:
+          days-to-keep: 30
+          artifact-days-to-keep: 30
+    parameters:
+        - maas_branch:
+            maas_branch: https://git.launchpad.net/maas
+        - maas_branch_id:
+            maas_branch_id: '{maas_branch_id}'
+        - maas_branch_revno:
+            maas_branch_revno: 'HEAD'
+        - maas_ppa:
+            maas_ppa: '{maas_ppa}'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - curtin_ppa:
+            curtin_ppa: ''
+        - curtin_proposed:
+            curtin_proposed: '{curtin_proposed}'
+        - release:
+            release: focal
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - test_rhel:
+            test_rhel: '0'
+        - test_centos:
+            test_centos: '1'
+        - test_windows:
+            test_windows: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: '{job_name}-curtin-sru-manual'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    parameters:
+        - release:
+            release: '{release}'
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - curtin_proposed:
+            curtin_proposed: '1'
+        - maas_proposed:
+            maas_proposed: '0'
+        - maas_ppa:
+            maas_ppa: '{maas_ppa}'
+        - test_rhel:
+            test_rhel: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: '{job_name}-cloudinit-sru-manual'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    parameters:
+        - release:
+            release: '{release}'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - maas_images_repo:
+            maas_images_repo: 'https://git.launchpad.net/maas-images'
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - cloudinit_proposed:
+            cloudinit_proposed: '1'
+        - test_centos:
+            test_centos: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        # - test_rhel:
+        #     test_rhel: '0'
+        # - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: '{job_name}-maas-sru-manual'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    parameters:
+        - release:
+            release: '{release}'
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - maas_proposed:
+            maas_proposed: '1'
+        - maas_ppa:
+            maas_ppa: '{maas_ppa}'
+        - curtin_proposed:
+            curtin_proposed: '0'
+        - test_rhel:
+            test_rhel: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: 'maas-{release}-{maas_branch_id}-performance-suite'
+    node: ci-lab
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    parameters:
+        - test_performance:
+            test_performance: '1'
+        - maas_branch:
+            maas_branch: '{maas_branch}'
+        - maas_branch_id:
+            maas_branch_id: '{maas_branch_id}'
+        - maas_branch_revno:
+            maas_branch_revno: '{maas_branch_revno}'
+        - tests_branch:
+            tests_branch: '{tests_branch}'
+        - release:
+            release: '{release}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '0'
+        - use_arm64_nodes:
+            use_arm64_nodes: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+        - vm_ram_size
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+        - mattermost_announce
+
+- job-template:
+    name: '{job_name}-manual-ipv6'
+    node: '{node_name}'
+    auth-token: '5b52a610'
+    wrappers:
+        - build_timeout
+    parameters:
+        - maas_branch:
+            maas_branch: https://git.launchpad.net/maas
+        - maas_branch_id:
+            maas_branch_id: '{maas_branch_id}'
+        - maas_branch_revno:
+            maas_branch_revno: 'HEAD'
+        - tests_branch:
+            tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+        - curtin_ppa:
+            curtin_ppa: ''
+        - curtin_proposed:
+            curtin_proposed: '{curtin_proposed}'
+        - release:
+            release: focal
+        - use_arm64_nodes:
+            use_arm64_nodes: '{use_arm64_nodes}'
+        - use_ppc_nodes:
+            use_ppc_nodes: '{use_ppc_nodes}'
+        - test_rhel:
+            test_rhel: '0'
+        - http_proxy_env
+        - keep_ci_running
+        - pause_ci
+        - debug
+    builders:
+        - maas_adt_git
+    publishers:
+        - archive_build_artifacts
+
+###############################################################################################
+
+- job-group:
+    name: focal-2.9-jobs-git
+    release: focal
+    maas_branch: https://git.launchpad.net/maas
+    maas_branch_id: '2.9'
+    maas_branch_revno: HEAD
+    maas_ppa: 'ppa:maas/2.9'
+    curtin_ppa: ''
+    tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+    use_arm64_nodes: '1'
+    use_ppc_nodes: '1'
+    jobs:
+        - 'maas-{release}-{maas_branch_id}-git'
+        - 'maas-{release}-{maas_branch_id}-git-ea'
+
+- job-group:
+    name: focal-3.0-jobs-git
+    release: focal
+    maas_branch: https://git.launchpad.net/maas
+    maas_branch_id: '3.0'
+    maas_branch_revno: HEAD
+    maas_ppa: 'ppa:maas/3.0'
+    curtin_ppa: ''
+    tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+    use_arm64_nodes: '1'
+    use_ppc_nodes: '1'
+    jobs:
+        - 'maas-{release}-{maas_branch_id}-git'
+        - 'maas-{release}-{maas_branch_id}-git-ea'
+
+- job-group:
+    name: focal-3.1-jobs-git
+    release: focal
+    maas_branch: https://git.launchpad.net/maas
+    maas_branch_id: '3.1'
+    maas_branch_revno: HEAD
+    maas_ppa: 'ppa:maas/3.1'
+    curtin_ppa: ''
+    tests_branch: lp:~maas-maintainers/maas/qa-lab-tests
+    use_arm64_nodes: '1'
+    use_ppc_nodes: '1'
+    jobs:
+        - 'maas-{release}-{maas_branch_id}-git'
+        - 'maas-{release}-{maas_branch_id}-git-ea'
+
+- job-group:
+    name: proposed-sru-manual
+    job_name: SRU-proposed
+    release: bionic
+    use_arm64_nodes: '0'
+    use_ppc_nodes: '0'
+    maas_ppa: ''
+    jobs:
+        - '{job_name}-curtin-sru-manual'
+        - '{job_name}-cloudinit-sru-manual'
+        - '{job_name}-maas-sru-manual'
+
+####################################################################################################
+######################################## PROJECTS ##################################################
+####################################################################################################
+
+# TODO:
+# - Add jobs for Windows
+# - Add jobs for Ubuntu Core
+# - Add jobs for ESXi
+- project:
+    name: maas-qa-lab
+    jobs:
+      - focal-2.9-jobs-git  # job group (creates -git, -ea)
+      - focal-3.0-jobs-git  # job group (creates -git, -ea)
+      - focal-3.1-jobs-git  # job group (creates -git, -ea)
+      - proposed-sru-manual  # job group (creates jobs for SRU testing: -curtin-sru-manual, -maas-sru-manual)
+      - proposed-maas-images-manual
+      # maas-git-manual-ipv6 # configuration for IPv6
+
+- job:
+    name: enable-global-maas-pxe
+    description: |
+      This job enables DHCP for the PXE network on the global MAAS
+      at http://10.245.136.7:5240/MAAS.
+
+      The PXE network is shared between the CI tests and the global
+      MAAS, and this job ensures that DHCP is only enabled for one MAAS
+      at a time. No CI tests will be run while this job is running.
+
+      When you're done using the global MAAS, either disable DHCP for
+      the PXE VLAN in MAAS, or abort this job.
+
+      After the given timeout, this job will be automatically aborted,
+      and DHCP disabled, so that the CI tests my run again.
+
+      NEVER ENABLE DHCP IN THE GLOBAL MAAS MANUALLY, ALWAYS USE THIS JOB
+    project-type: pipeline
+    parameters:
+        - string:
+            name: TIMEOUT
+            default: '60'
+            description: 'Timeout in minutes'
+    dsl: |
+     pipeline {
+        agent {
+            label 'ci-lab'
+        }
+        stages {
+          stage('Enable DHCP') {
+            steps {
+              script {
+                env.PATH += ":/snap/bin"
+              }
+              withCredentials([string(credentialsId: 'jenkins-maas-token', variable: 'API_TOKEN')]) {
+                sh "echo \$PATH"
+                sh "ls /snap/bin"
+                sh "maas login jenkins http://10.245.136.7:5240/MAAS ${API_TOKEN}"
+              }
+              sh "maas jenkins vlan update 0 0 dhcp_on=true primary_rack=ckbafg"
+            }
+          }
+          stage('Wait for DHCP to be disabled') {
+            steps {
+              timeout(time: "${TIMEOUT}" as Integer, unit: 'MINUTES') {
+                  sh """
+                     DHCP_VLAN=''
+                     while [ -z "\$DHCP_VLAN" ];
+                     do
+                         sleep 30;
+                         DHCP_VLAN=\$(maas jenkins vlan read 0 0 | jq -c 'select( .dhcp_on == false)')
+                     done
+                  """
+              }
+            }
+          }
+        }
+        post {
+          always {
+            sh "maas jenkins vlan update 0 0 dhcp_on=false"
+            sh "maas logout jenkins"
+          }
+        }
+     }
diff --git a/jenkins/jobs/config.ini b/jenkins/jobs/config.ini
new file mode 100644
index 0000000..fa1e4ab
--- /dev/null
+++ b/jenkins/jobs/config.ini
@@ -0,0 +1,6 @@
+[jenkins]
+user=admin
+password=c4n0n1c4l
+url=http://maas-integration-ci.internal:8080/
+#password=472af6c7184dd6d96a87b4f9ab6aea68
+#url=http://162.213.35.104:8080/
diff --git a/jenkins/jobs/gh_repo_tester.groovy b/jenkins/jobs/gh_repo_tester.groovy
new file mode 100644
index 0000000..e1b044c
--- /dev/null
+++ b/jenkins/jobs/gh_repo_tester.groovy
@@ -0,0 +1,139 @@
+import groovy.json.JsonSlurper
+
+MAAS_GH_USER = "maas"
+
+def parseJobs(repoName, jsonString) {
+    parsed_jobs = new JsonSlurper().parseText(jsonString)
+    println("Jobs to parse: ${parsed_jobs}")
+    jobs = []
+
+    if (parsed_jobs.size() > 0) {
+        for(parsed_job in parsed_jobs) {
+            job = [:]
+
+            source_repo = parsed_job["head"]["repo"]["html_url"]
+            source_branch = parsed_job["head"]["ref"]
+            source_latest_commit = sh(script: "git ls-remote ${source_repo} ${source_branch} | awk \'{print \$1}\'", returnStdout: true).trim()
+
+            target_repo = parsed_job["base"]["repo"]["html_url"]
+            target_branch = parsed_job["base"]["ref"]
+
+            pr_updated = Date.parse("yyyy-MM-dd'T'HH:mm:ss'Z'", parsed_job["updated_at"])
+
+            // Don't test PRs that aren't targeting the main branch
+            target_repo_name = parsed_job["base"]["repo"]["full_name"]
+            default_branch = parsed_job["base"]["repo"]["default_branch"]
+            correct_repo = (
+                target_branch==default_branch && target_repo_name=="${MAAS_GH_USER}/${repoName}"
+            )
+
+            // Don't test PRs that have the WIP label applied
+            parsed_pr_labels = parsed_job["labels"]
+            wip = false
+            if (parsed_pr_labels.size() > 0) {
+                for (label in parsed_pr_labels) {
+                    if (label["name"]=="WIP") {
+                        wip = true
+                    }
+                }
+            }
+
+            // Don't test PRs that have already been tested (ie: they have a status applied to the commit)
+            commit_status_out = sh(script: "curl -s https://api.github.com/repos/${MAAS_GH_USER}/${repoName}/commits/${source_latest_commit}/statuses";, returnStdout: true).trim()
+            commit_status = new JsonSlurper().parseText(commit_status_out)
+            tested = (commit_status.size() > 0)
+
+            // Reset the tested status if we have left a comment after a test has been run
+            comment_url = parsed_job["comments_url"]
+            comment_out = sh(script: "curl -s ${comment_url}", returnStdout: true).trim()
+            comments = new JsonSlurper().parseText(comment_out)
+            if (tested && comments.size() > 0) {
+                println("Tested PR has comments, checking for retest requests")
+                def latest_status_time = Date.parse("yyyy-MM-dd hh:mm:ss", "2000-01-01 00:00:00")
+                latest_status = ""
+                for (status in commit_status) {
+                    status_time = Date.parse("yyyy-MM-dd'T'HH:mm:ss'Z'", status["updated_at"])
+                    if (status_time > latest_status_time) {
+                        latest_status_time = status_time
+                        latest_status = status["state"]
+                    }
+                }
+                println("Most recent status ${latest_status} at ${latest_status_time}")
+                // if the latest test has been completed, and the comment was left after that
+                if (latest_status != "pending") {
+                    for (comment in comments) {
+                        if (Date.parse("yyyy-MM-dd'T'HH:mm:ss'Z'", comment["created_at"]) > latest_status_time && comment["body"].toLowerCase() == "jenkins: !test") {
+                            println("Retest request made after most recent test result")
+                            tested = false
+                        }
+                    }
+                }
+            }
+
+            // add this commit to be tested
+            if (correct_repo && !wip && !tested) {
+                job["updated"] = pr_updated
+                // The repo and branch to build a job for
+                job["GITHUB_REPO"] = source_repo
+                job["GITHUB_BRANCH"] = source_branch
+                jobs.add(job)
+                println("Job candidate ${job} created")
+            }
+        }
+    }
+    return jobs
+}
+
+def makeBuild(job_name, job) {
+    println("Running job for ${job.GITHUB_REPO} ${job.GITHUB_BRANCH}")
+    return build(
+        job: job_name,
+        parameters: [
+            [$class: 'StringParameterValue', name: 'GITHUB_REPO', value: job.GITHUB_REPO],
+            [$class: 'StringParameterValue', name: 'GITHUB_BRANCH', value: job.GITHUB_BRANCH],
+        ],
+        propagate: false,
+        wait: false,
+        waitForStart: false,
+    )
+}
+
+pipeline {
+    agent {
+        label 'ci-lab'
+    }
+    options {
+        disableConcurrentBuilds()
+    }
+    stages {
+        stage("Fetch Pull Requests") {
+            steps {
+                script {
+                    split_repo_url = params.TARGET_REPO.split("/")
+                    repo_name = split_repo_url[split_repo_url.length-1]
+                    user_name = split_repo_url[split_repo_url.length-2]
+                    output = sh(script: "curl -s https://api.github.com/repos/${user_name}/${repo_name}/pulls?state=open";, returnStdout: true).trim()
+                    if (!output.contains("API rate limit exceeded")) {
+                        env._pr_to_review = output
+                    } else {
+                        println("API Limit exceeded, cannot parse jobs")
+                        env._pr_to_review = "[]"
+                    }
+                    env._gh_repo_name = repo_name
+                }
+            }
+        }
+        stage("Test PRs") {
+            when { not { environment name: "_pr_to_review", value: "[]" }}
+            steps {
+                script {
+                    jobs = parseJobs(env._gh_repo_name, env._pr_to_review)
+                    if (jobs) {
+                        // Only parse one job for now
+                        makeBuild(params.JOB_NAME, jobs[0])
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/jenkins/jobs/labmaas_base_config.inc b/jenkins/jobs/labmaas_base_config.inc
new file mode 100644
index 0000000..a68786a
--- /dev/null
+++ b/jenkins/jobs/labmaas_base_config.inc
@@ -0,0 +1,194 @@
+proxy:
+    use_internal: true
+    http: http://squid.internal:3128
+networks:
+    pxe:
+        cidr: 10.245.136.0/21
+        bridge: br0
+        dynamic:
+            start: 10.245.136.21
+            end: 10.245.136.200
+        reserved:
+            - start: 10.245.136.1
+              end: 10.245.136.19
+              comment: Reserved
+            - start: 10.245.143.100
+              end: 10.245.143.200
+              comment: BMCs
+maas:
+    networks:
+        pxe: 10.245.136.20
+    config:
+        # This values will set with `maas set-config` command
+        upstream_dns: 10.245.136.1
+        dnssec_validation: "no"
+    domain_name: systemtestsmaas
+
+windows_image_file_path: /home/ubuntu/other-os-images/windows-win2012hvr2-amd64-root-dd
+
+o11y:
+    grafana_agent_file_path: >
+        /home/ubuntu/system_tests_assets/agent-linux-amd64_0_28_0
+    o11y_ip: 10.245.136.5
+
+tls:
+    cacerts: ssl-cert-snakeoil.pem
+
+containers-image: ${params.CONTAINERS_IMAGE}
+
+machines:
+    hardware:
+        opelt:
+            power_type: ipmi
+            power_parameters:
+                power_driver: LAN_2_0
+                power_address: 10.245.143.121
+                power_username: root
+                power_password: calvin
+            mac_address: 18:66:da:6d:fb:3c
+        stunky:
+            power_type: ipmi
+            power_parameters:
+                power_driver: LAN_2_0
+                power_address: 10.245.143.127
+                power_username: landscape
+                power_password: insecure
+            mac_address: ec:b1:d7:7f:ef:34
+        natasha:
+            power_type: ipmi
+            power_parameters:
+                power_driver: LAN_2_0
+                power_address: 10.245.143.120
+                power_username: root
+                power_password: calvin
+            mac_address: 18:66:da:75:6b:ee
+            osystem: windows
+        arm64:
+            power_type: ipmi
+            power_parameters:
+                power_driver: LAN_2_0
+                power_address: 10.245.143.114
+                power_username: admin
+                power_password: password
+            mac_address: 1c:1b:0d:0d:52:7c
+            architecture: arm64
+        ppc64le:
+            power_type: ipmi
+            power_parameters:
+                power_driver: LAN_2_0
+                power_address: 10.245.143.113
+                power_username: ""
+                power_password: admin
+            mac_address: 98:be:94:02:6a:c8
+            architecture: ppc64el
+    vms:
+        instances:
+            vm1:
+                mac_address: 00:16:3e:d6:36:18
+                devices:
+                    disk1:
+                        type: disk
+                        source: /home/ubuntu/diego/test_block_device.img
+                        #size: 1MB
+                        path: /mnt/ext2
+                    iface1:
+                        type: nic
+                        nictype: bridged
+                        parent: br0
+                        name: eth1
+                        hwaddr: 00:16:3e:4a:19:6f
+                    # usb1:
+                    #     type: usb
+                    #     productid: "0016"
+                    # pci1:
+                    #     type: pci
+                    #     address: '02:00.1'
+                lxd_profile: prof-maas-test
+            vm2:
+                mac_address: 00:16:3e:d0:54:9f
+                lxd_profile: prof-maas-test
+        power_type: lxd
+        power_parameters:
+            power_address: https://10.157.204.1:8443
+            project: default
+            certificate: |
+                         -----BEGIN CERTIFICATE-----
+                         MIIElzCCAn8CEQD+6NBHtM1z951J2HwbtZwnMA0GCSqGSIb3DQEBDQUAMAAwHhcN
+                         MjIwMjA4MTMwNDI4WhcNMzIwMjA2MTMwNDI4WjATMREwDwYDVQQDDAh0ZXN0aG9z
+                         dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANRvcoT8dofRU2A0ICm0
+                         JzZPJr9C7oQmJhxd/I8Fwzt1Jzom0mhg+8oSFXPI7jzx8kpFexJZHBF8WBUII+0v
+                         RnZx8oeIRtvrL22tKEqeH2dwZxHIpcmoPbj2jMCFR1IFhFGBJD1uDUvkBAYFnguM
+                         tJml5yh+BhhPGc8Ehds/kstq032rWlDjx0Cne+5mebSniz+TW5+uc/7Vy4mIDm3P
+                         IGgytRXDseryvJ0kuLkNZGT38tHTva22skz0K0mRImNgCdjbacsS85DQymANJtP/
+                         TbAZnZ2VGLGJ2jXVBEKcXDRdrT5jusQTdRseciXqbSBuvCcsd+4R85ZZNO4ARDqI
+                         XNWEEinspXfJtBuvx2dtZ1S8K233kz1ds5n3vLirw6LIeNW1b8a200JeDovBzUmt
+                         fu9qSgw+fS2H7k/TnQDzd1AqhcKQpZqZd2eY1d4AbM9L9oEPFPesISNko7U0NpPC
+                         5RfejxL+IMhshKDPWCo50I4Ty80gSPbzRBJUXE4Qq3ZiiqiK8EW0VRobz6ZfDF1A
+                         /2oR6C6QvNd30lzdP1laLLHKBh2A3TTWL4UE+N6eH38LdO2isbh/8ux068sclxzX
+                         Ba34D5ICzjHd4FpBHiNpyTnb8QAaKcOFLr2QGHogRCQI2QygPyB6jaVTTVwkJbKS
+                         kuJBcUjBNmkEnx1VFIqnzDr5AgMBAAEwDQYJKoZIhvcNAQENBQADggIBAL7aLr6Z
+                         Hfqlefng/JMr+ojDJWmdtHTqwa9MviEzHTlzhvTw3JRCMFlIlIrK7wpd7kr9Jmhz
+                         HI3atc1NtrEsMeUsvNmopnlqKWPwcyCYX0juKvoMeTqFDLN7yWPBHEc4NNhXJQte
+                         pee7P02e8+C0g4UXlQ8Hry1LfwhlwHok0LijEbBkX4x0m7RlqqprMfJJsU6wL613
+                         WOcfTEiHkZYB/a/yeIAhdqSi3jMH3m+jbG9Tjfn62pPheSzRpH28A5mvZVP39RUO
+                         JdpEHT7F5pMmauygWBxXT/VCU0TriCMYrexjA1cWOlrT0+VU5Im3+u0l3vUE+2Wc
+                         zKNpt3l3wG3KmLHeJr9/7QyFLMHhEYr4xpMoikIhq75iLz6GqGkgho7Xy7Iz2WZP
+                         YW39eNAYdW8UEnSpdlmsA0gIHeWDVWZFebMk8Jw5AtIr6qTUGRMp4jFzwaQX4Oc2
+                         kf2jqICMKz3nQjT5b5+/w6OKuz1POrzA9hRkp85fDdeD/zwO9v+65qwHk9T+FOM4
+                         xC/VQcoSp5lJmGN6NGCleLhpUbIwX381rCk7pvOPQ5PLWW2gPB61njsdbJ97fWpy
+                         5b7QD4Pcq+eQ2Xy1+F4ToCmW8E6XorUFPUKTInTl4McHbJiuGwmXAkGqLzgJd+Q1
+                         opB3iqj1W36wgZQK6lRoN7X/zugt9m+MZX/6
+                         -----END CERTIFICATE-----
+            key: |
+                 -----BEGIN PRIVATE KEY-----
+                 MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDUb3KE/HaH0VNg
+                 NCAptCc2Tya/Qu6EJiYcXfyPBcM7dSc6JtJoYPvKEhVzyO488fJKRXsSWRwRfFgV
+                 CCPtL0Z2cfKHiEbb6y9trShKnh9ncGcRyKXJqD249ozAhUdSBYRRgSQ9bg1L5AQG
+                 BZ4LjLSZpecofgYYTxnPBIXbP5LLatN9q1pQ48dAp3vuZnm0p4s/k1ufrnP+1cuJ
+                 iA5tzyBoMrUVw7Hq8rydJLi5DWRk9/LR072ttrJM9CtJkSJjYAnY22nLEvOQ0Mpg
+                 DSbT/02wGZ2dlRixido11QRCnFw0Xa0+Y7rEE3UbHnIl6m0gbrwnLHfuEfOWWTTu
+                 AEQ6iFzVhBIp7KV3ybQbr8dnbWdUvCtt95M9XbOZ97y4q8OiyHjVtW/GttNCXg6L
+                 wc1JrX7vakoMPn0th+5P050A83dQKoXCkKWamXdnmNXeAGzPS/aBDxT3rCEjZKO1
+                 NDaTwuUX3o8S/iDIbISgz1gqOdCOE8vNIEj280QSVFxOEKt2YoqoivBFtFUaG8+m
+                 XwxdQP9qEegukLzXd9Jc3T9ZWiyxygYdgN001i+FBPjenh9/C3TtorG4f/LsdOvL
+                 HJcc1wWt+A+SAs4x3eBaQR4jack52/EAGinDhS69kBh6IEQkCNkMoD8geo2lU01c
+                 JCWykpLiQXFIwTZpBJ8dVRSKp8w6+QIDAQABAoICAQCxuqQHGulX7AtjW3jlKzH7
+                 P/Fc5vSCXyBXb1KTnfCe1/7/qeczKKC/iK2l9x9KoelhtgunaCIRhwRyZCMalwjO
+                 o7qTJbKS34sIqWwiMXR4qBOzTzlVI4qwKqXLlDX9K1xujCrzshUxvwyWtTBq3Udj
+                 nOduezFCOTuQdWo/6ko4IaHba/bd4hObxgPripScTeg0QmbPi7bEJ75nzAq2WCn2
+                 wyW5lcZOmNKwbj6Vo9ywlLj0T8BLi6RUuZtVqzUoCvtyEO/L1IkuSWBnR9mKV/h5
+                 MpUpd8n3DywfCZ7M0+BYd18v6WQiE11QWQKLMjwmfD6yT4PvC9nNmcisrlBm4Bs5
+                 h9ESg7/JrfiXdJPa7JJ4JH01/gvQik6BAp+NhZEOg05mi81CE7ECchyDTzjbk5HS
+                 xUbdiTVSWO1zy4mADmNqMs6JgF4JNiSg652sl96nZsXuZ13iQW1I8Y0jR1HCC+Xt
+                 +HgYuSpriL3THyaQX8vWumkhB4AL3jU1/Vh64OoQQfcDJi6IpSiSM+Gs7E8uBAWi
+                 aILf4dxB9liQCv/GYvhFQ7eOx+bcg6orp0hmqL809KAAoe6siT6CVTWa7n7nZWDS
+                 pH4sNXnwwC4cqEwFjmCNXzTv0+rWR0lKc02s5aZbCk7L34O10XdeqP077KXS87+v
+                 DJC1yI7JkOQUfB2ztzj7AQKCAQEA/q0jX7+wbFtn3bQKdlp+qHznaXgyorVFoR7p
+                 0rarF9oP0C2a3hdGoQMANosg/1fpJHmVHrLMVGtBuqhrw8RCjlqe9PxlKeoKnR6I
+                 AWcmt8D45UuucJ1fatsB9SfBasCaLXZZrmCF6bYzm3c4Y4J6uxircsHViMGJWsaA
+                 grw5kPtRbPSCWofYCM0G1x9J+P/ymgZOzGwPtpmzUHXHbN3WVNTl9K9nEwayveUj
+                 QbzSnw9+CyWPFUubhjnap9pboAM7jbRbP71I0u/EboHq/3BeTLuxRTEp0imtvxHo
+                 LTFtFu8Jo9tKg4XUJQHFzuRueG39dK2bl4HtnT7aIfXCX67ZEQKCAQEA1Yoa5Nmw
+                 VDs8CYcScjh4GsWzPZ0GA6NMA1G7I7k71ixWS/VNQ0GxYAZ3y6ItZz4pkKZ/LFPR
+                 WVAak+SR7+7ZxiYFPJ8WnqKJZxvzjr7+Bh3YaFSek0P+sr2OXlz7uO44Kzo0RvMG
+                 6STb+8vhZpDnpMk6TvUWf7FTnYLAoi8kHDhjsJTa3+E5wujr3mnps/vQQyBQrY7s
+                 2gU+5znvve77v5yl1zYHT0gWimdJxMZp4cd4Hiqr3kMME12R+V/XtWz+LgeBViXA
+                 Z0T6Tnc2+bsaestFSNe4dTuIAwoGM2pDmXc874/DwB4piO4QOM2p/ZOLdmYUmlPQ
+                 ogBYkGMIw+oDaQKCAQAwxOsPPOAGAAMF26JdQ7sZfMG72r6nldr9nbPdHAnriWCZ
+                 1wHfIcnur2ptB3uMKkOFLps1w7uJNvjhS7tHQ+AS7pueAm9E9YKOz/fvfNdXPObs
+                 0e9XtWs+RS48yh4p2TQtHIrT77v1I2UCknQD6kqiZXj/gsrnY1hwP68AWhcUAmx3
+                 VuNXfsgJ92kl7OH3gtvsTuTsFI11xD0oXUWRPXH70MEweB5e8FtuLeDwh741o3vZ
+                 mpmp1E62B4ItvozpOXVAD5ehvxeg/TU6jDp6LASC4TZzL5T4n+6btkwly18+kwvf
+                 ivDb+tbDN3GvyuK0wStWGqC/BKyB/jU7Z5qPRCZhAoIBABUVoNgt4mo+uwvZyWl7
+                 x+gk0zDnOzvKuOuu+0JovM7F6/NuEiXs652mpdd2ePMzwRjmR7JRyF8AOM+Xhw1g
+                 0SHuiR/WOX6KX/TNXrwegaiK895BVLMHyLNPYipRFg3Jf8RM5/KFdo44tHvlQqlE
+                 74pm0BoRuxn6oV3xFiItc2xR6Q37dK0caP6kzv1UCd5ao9Ks8ypf7WUNlYtxPgnL
+                 +hGOXxWj4Q7j+E3MKw2B5dyEPIkF/5hfmGalG4+69eqVC3fyB8RA0AGiXvC2drgr
+                 0E6FmZ66phz1NtXN/JTBDlGt41doI5TppYI+t11UeU9vbRrQs4IVeok0bYo8LRZj
+                 GdkCggEAf5myYEBvr8tuhE100Tau5RoKLS0SoKv7qsDpmFyUiNBmQggecB+X+ck4
+                 3/IToBmZRnzu+Fubqt1yMEKv+SO4F3s1W5sLdQsSAzPcum0fSEf94wONZXGkhntB
+                 0rsHnPTd3APbi1c+ZFQ+DV/Krx5yYGW1J8BBQLvFFuvJ8rK4Q+WCejGI8QCfrVU0
+                 95ctKugep8MTzm2xIecqKQOqx7UY9zWil/o4p9HEyzqzkG05+RQPLoAW9kmtcZZt
+                 HXvIdwLsUASyKB/LlM6dESXr9K4r/im3sn+9oza9mbwsRnBIJ9YU9llTw9j5aUlw
+                 Xwjk99gnvag+HswBDB7A7DCTEqhkUQ==
+                 -----END PRIVATE KEY-----
diff --git a/jenkins/jobs/orobas.ini b/jenkins/jobs/orobas.ini
new file mode 100644
index 0000000..38c8dcb
--- /dev/null
+++ b/jenkins/jobs/orobas.ini
@@ -0,0 +1,4 @@
+[jenkins]
+user=admin
+password=admin
+url=http://10.246.88.13:8080/
diff --git a/jenkins/jobs/systemtests.yaml b/jenkins/jobs/systemtests.yaml
new file mode 100644
index 0000000..2193032
--- /dev/null
+++ b/jenkins/jobs/systemtests.yaml
@@ -0,0 +1,301 @@
+- job:
+    name: maas-system-tests
+    description: |
+      Run the MAAS system tests
+    project-type: pipeline
+    triggers:
+      - timed: '@daily'
+    dsl: |
+      pipeline {
+        agent {
+          label 'ci-lab'
+        }
+        options {
+            timeout(time: 6, unit: "HOURS")
+        }
+        parameters {
+          string(name: 'GIT_REPO', defaultValue: 'https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests', description: 'Git repo to pull system tests from')
+          string(name: 'GIT_BRANCH', defaultValue: 'master', description: 'Branch in the repo to use')
+          string(name: 'PYTEST_ARGS', defaultValue: '--log-cli-level info', description: 'Additional args to pytest')
+          string(name: 'MAAS_GIT_BRANCH', defaultValue: 'master', description: 'branch of maas project to build from.')
+          string(name: 'MAAS_PPA', defaultValue: 'ppa:maas-committers/latest-deps', description: 'maas ppa for dependencies.')
+          string(name: 'CONTAINERS_IMAGE', defaultValue: 'ubuntu:22.04', description: 'LXD image for containers used in tests')
+          booleanParam(name: 'REMOVE_CONTAINERS', defaultValue: true, description: 'Should existing containers be torn down before we run?')
+        }
+        stages {
+          stage('Checkout') {
+            steps {
+              sh """
+                lxc list
+                pgrep -a qemu || true
+                rm -rf system-tests
+                git clone ${params.GIT_REPO} --branch ${params.GIT_BRANCH} --depth 10 system-tests
+                env -C system-tests git show --no-patch
+              """
+            }
+          }
+          stage('Clean') {
+            when {
+              expression {
+                return params.REMOVE_CONTAINERS
+              }
+            }
+            steps {
+              sh """
+                lxc delete maas-system-build --force || true
+                lxc delete maas-system-maas --force || true
+                lxc delete maas-client --force || true
+              """
+            }
+          }
+          stage('Setup') {
+            steps {
+              sh """
+                sudo apt-get install -y tox
+              """
+            }
+          }
+          stage('System Tests') {
+            steps {
+                writeFile file: 'system-tests/config.yaml', text: """
+      proxy:
+          use_internal: true
+          http: http://squid.internal:3128
+      networks:
+          pxe:
+              cidr: 10.245.136.0/21
+              bridge: br0
+              dynamic:
+                  start: 10.245.136.21
+                  end: 10.245.136.200
+              reserved:
+                  - start: 10.245.136.1
+                    end: 10.245.136.19
+                    comment: Reserved
+                  - start: 10.245.143.100
+                    end: 10.245.143.200
+                    comment: BMCs
+      maas:
+          networks:
+              pxe: 10.245.136.20
+          config:
+              # This values will set with `maas set-config` command
+              upstream_dns: 10.245.136.1
+              dnssec_validation: "no"
+          domain_name: systemtestsmaas
+
+      windows_image_file_path: /home/ubuntu/other-os-images/windows-win2012hvr2-amd64-root-dd
+
+      tls:
+          cacerts: ssl-cert-snakeoil.pem
+
+      vault:
+          snap-channel: 1.11/stable
+
+      containers-image: ${params.CONTAINERS_IMAGE}
+
+      deb:
+          ppa:
+            - ${params.MAAS_PPA}
+          git_branch: ${params.MAAS_GIT_BRANCH}
+
+      machines:
+          hardware:
+              opelt:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.121
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:6d:fb:3c
+              stunky:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.127
+                      power_username: landscape
+                      power_password: insecure
+                  mac_address: ec:b1:d7:7f:ef:34
+              natasha:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.120
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:75:6b:ee
+                  osystem: windows
+              arm64:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.114
+                      power_username: admin
+                      power_password: password
+                  mac_address: 1c:1b:0d:0d:52:7c
+                  architecture: arm64
+              ppc64le:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.113
+                      power_username: ""
+                      power_password: admin
+                  mac_address: 98:be:94:02:6a:c8
+                  architecture: ppc64el
+          vms:
+              instances:
+                  vm1:
+                      mac_address: 00:16:3e:d6:36:18
+                      devices:
+                          disk1:
+                              type: disk
+                              source: /home/ubuntu/diego/test_block_device.img
+                              #size: 1MB
+                              path: /mnt/ext2
+                          iface1:
+                              type: nic
+                              nictype: bridged
+                              parent: br0
+                              name: eth1
+                              hwaddr: 00:16:3e:4a:19:6f
+                          # usb1:
+                          #     type: usb
+                          #     productid: "0016"
+                          # pci1:
+                          #     type: pci
+                          #     address: '02:00.1'
+                      lxd_profile: prof-maas-test
+                  vm2:
+                      mac_address: 00:16:3e:d0:54:9f
+                      lxd_profile: prof-maas-test
+              power_type: lxd
+              power_parameters:
+                  power_address: https://10.157.204.1:8443
+                  project: default
+                  certificate: |
+                                  -----BEGIN CERTIFICATE-----
+                                  MIIElzCCAn8CEQD+6NBHtM1z951J2HwbtZwnMA0GCSqGSIb3DQEBDQUAMAAwHhcN
+                                  MjIwMjA4MTMwNDI4WhcNMzIwMjA2MTMwNDI4WjATMREwDwYDVQQDDAh0ZXN0aG9z
+                                  dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANRvcoT8dofRU2A0ICm0
+                                  JzZPJr9C7oQmJhxd/I8Fwzt1Jzom0mhg+8oSFXPI7jzx8kpFexJZHBF8WBUII+0v
+                                  RnZx8oeIRtvrL22tKEqeH2dwZxHIpcmoPbj2jMCFR1IFhFGBJD1uDUvkBAYFnguM
+                                  tJml5yh+BhhPGc8Ehds/kstq032rWlDjx0Cne+5mebSniz+TW5+uc/7Vy4mIDm3P
+                                  IGgytRXDseryvJ0kuLkNZGT38tHTva22skz0K0mRImNgCdjbacsS85DQymANJtP/
+                                  TbAZnZ2VGLGJ2jXVBEKcXDRdrT5jusQTdRseciXqbSBuvCcsd+4R85ZZNO4ARDqI
+                                  XNWEEinspXfJtBuvx2dtZ1S8K233kz1ds5n3vLirw6LIeNW1b8a200JeDovBzUmt
+                                  fu9qSgw+fS2H7k/TnQDzd1AqhcKQpZqZd2eY1d4AbM9L9oEPFPesISNko7U0NpPC
+                                  5RfejxL+IMhshKDPWCo50I4Ty80gSPbzRBJUXE4Qq3ZiiqiK8EW0VRobz6ZfDF1A
+                                  /2oR6C6QvNd30lzdP1laLLHKBh2A3TTWL4UE+N6eH38LdO2isbh/8ux068sclxzX
+                                  Ba34D5ICzjHd4FpBHiNpyTnb8QAaKcOFLr2QGHogRCQI2QygPyB6jaVTTVwkJbKS
+                                  kuJBcUjBNmkEnx1VFIqnzDr5AgMBAAEwDQYJKoZIhvcNAQENBQADggIBAL7aLr6Z
+                                  Hfqlefng/JMr+ojDJWmdtHTqwa9MviEzHTlzhvTw3JRCMFlIlIrK7wpd7kr9Jmhz
+                                  HI3atc1NtrEsMeUsvNmopnlqKWPwcyCYX0juKvoMeTqFDLN7yWPBHEc4NNhXJQte
+                                  pee7P02e8+C0g4UXlQ8Hry1LfwhlwHok0LijEbBkX4x0m7RlqqprMfJJsU6wL613
+                                  WOcfTEiHkZYB/a/yeIAhdqSi3jMH3m+jbG9Tjfn62pPheSzRpH28A5mvZVP39RUO
+                                  JdpEHT7F5pMmauygWBxXT/VCU0TriCMYrexjA1cWOlrT0+VU5Im3+u0l3vUE+2Wc
+                                  zKNpt3l3wG3KmLHeJr9/7QyFLMHhEYr4xpMoikIhq75iLz6GqGkgho7Xy7Iz2WZP
+                                  YW39eNAYdW8UEnSpdlmsA0gIHeWDVWZFebMk8Jw5AtIr6qTUGRMp4jFzwaQX4Oc2
+                                  kf2jqICMKz3nQjT5b5+/w6OKuz1POrzA9hRkp85fDdeD/zwO9v+65qwHk9T+FOM4
+                                  xC/VQcoSp5lJmGN6NGCleLhpUbIwX381rCk7pvOPQ5PLWW2gPB61njsdbJ97fWpy
+                                  5b7QD4Pcq+eQ2Xy1+F4ToCmW8E6XorUFPUKTInTl4McHbJiuGwmXAkGqLzgJd+Q1
+                                  opB3iqj1W36wgZQK6lRoN7X/zugt9m+MZX/6
+                                  -----END CERTIFICATE-----
+                  key: |
+                          -----BEGIN PRIVATE KEY-----
+                          MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDUb3KE/HaH0VNg
+                          NCAptCc2Tya/Qu6EJiYcXfyPBcM7dSc6JtJoYPvKEhVzyO488fJKRXsSWRwRfFgV
+                          CCPtL0Z2cfKHiEbb6y9trShKnh9ncGcRyKXJqD249ozAhUdSBYRRgSQ9bg1L5AQG
+                          BZ4LjLSZpecofgYYTxnPBIXbP5LLatN9q1pQ48dAp3vuZnm0p4s/k1ufrnP+1cuJ
+                          iA5tzyBoMrUVw7Hq8rydJLi5DWRk9/LR072ttrJM9CtJkSJjYAnY22nLEvOQ0Mpg
+                          DSbT/02wGZ2dlRixido11QRCnFw0Xa0+Y7rEE3UbHnIl6m0gbrwnLHfuEfOWWTTu
+                          AEQ6iFzVhBIp7KV3ybQbr8dnbWdUvCtt95M9XbOZ97y4q8OiyHjVtW/GttNCXg6L
+                          wc1JrX7vakoMPn0th+5P050A83dQKoXCkKWamXdnmNXeAGzPS/aBDxT3rCEjZKO1
+                          NDaTwuUX3o8S/iDIbISgz1gqOdCOE8vNIEj280QSVFxOEKt2YoqoivBFtFUaG8+m
+                          XwxdQP9qEegukLzXd9Jc3T9ZWiyxygYdgN001i+FBPjenh9/C3TtorG4f/LsdOvL
+                          HJcc1wWt+A+SAs4x3eBaQR4jack52/EAGinDhS69kBh6IEQkCNkMoD8geo2lU01c
+                          JCWykpLiQXFIwTZpBJ8dVRSKp8w6+QIDAQABAoICAQCxuqQHGulX7AtjW3jlKzH7
+                          P/Fc5vSCXyBXb1KTnfCe1/7/qeczKKC/iK2l9x9KoelhtgunaCIRhwRyZCMalwjO
+                          o7qTJbKS34sIqWwiMXR4qBOzTzlVI4qwKqXLlDX9K1xujCrzshUxvwyWtTBq3Udj
+                          nOduezFCOTuQdWo/6ko4IaHba/bd4hObxgPripScTeg0QmbPi7bEJ75nzAq2WCn2
+                          wyW5lcZOmNKwbj6Vo9ywlLj0T8BLi6RUuZtVqzUoCvtyEO/L1IkuSWBnR9mKV/h5
+                          MpUpd8n3DywfCZ7M0+BYd18v6WQiE11QWQKLMjwmfD6yT4PvC9nNmcisrlBm4Bs5
+                          h9ESg7/JrfiXdJPa7JJ4JH01/gvQik6BAp+NhZEOg05mi81CE7ECchyDTzjbk5HS
+                          xUbdiTVSWO1zy4mADmNqMs6JgF4JNiSg652sl96nZsXuZ13iQW1I8Y0jR1HCC+Xt
+                          +HgYuSpriL3THyaQX8vWumkhB4AL3jU1/Vh64OoQQfcDJi6IpSiSM+Gs7E8uBAWi
+                          aILf4dxB9liQCv/GYvhFQ7eOx+bcg6orp0hmqL809KAAoe6siT6CVTWa7n7nZWDS
+                          pH4sNXnwwC4cqEwFjmCNXzTv0+rWR0lKc02s5aZbCk7L34O10XdeqP077KXS87+v
+                          DJC1yI7JkOQUfB2ztzj7AQKCAQEA/q0jX7+wbFtn3bQKdlp+qHznaXgyorVFoR7p
+                          0rarF9oP0C2a3hdGoQMANosg/1fpJHmVHrLMVGtBuqhrw8RCjlqe9PxlKeoKnR6I
+                          AWcmt8D45UuucJ1fatsB9SfBasCaLXZZrmCF6bYzm3c4Y4J6uxircsHViMGJWsaA
+                          grw5kPtRbPSCWofYCM0G1x9J+P/ymgZOzGwPtpmzUHXHbN3WVNTl9K9nEwayveUj
+                          QbzSnw9+CyWPFUubhjnap9pboAM7jbRbP71I0u/EboHq/3BeTLuxRTEp0imtvxHo
+                          LTFtFu8Jo9tKg4XUJQHFzuRueG39dK2bl4HtnT7aIfXCX67ZEQKCAQEA1Yoa5Nmw
+                          VDs8CYcScjh4GsWzPZ0GA6NMA1G7I7k71ixWS/VNQ0GxYAZ3y6ItZz4pkKZ/LFPR
+                          WVAak+SR7+7ZxiYFPJ8WnqKJZxvzjr7+Bh3YaFSek0P+sr2OXlz7uO44Kzo0RvMG
+                          6STb+8vhZpDnpMk6TvUWf7FTnYLAoi8kHDhjsJTa3+E5wujr3mnps/vQQyBQrY7s
+                          2gU+5znvve77v5yl1zYHT0gWimdJxMZp4cd4Hiqr3kMME12R+V/XtWz+LgeBViXA
+                          Z0T6Tnc2+bsaestFSNe4dTuIAwoGM2pDmXc874/DwB4piO4QOM2p/ZOLdmYUmlPQ
+                          ogBYkGMIw+oDaQKCAQAwxOsPPOAGAAMF26JdQ7sZfMG72r6nldr9nbPdHAnriWCZ
+                          1wHfIcnur2ptB3uMKkOFLps1w7uJNvjhS7tHQ+AS7pueAm9E9YKOz/fvfNdXPObs
+                          0e9XtWs+RS48yh4p2TQtHIrT77v1I2UCknQD6kqiZXj/gsrnY1hwP68AWhcUAmx3
+                          VuNXfsgJ92kl7OH3gtvsTuTsFI11xD0oXUWRPXH70MEweB5e8FtuLeDwh741o3vZ
+                          mpmp1E62B4ItvozpOXVAD5ehvxeg/TU6jDp6LASC4TZzL5T4n+6btkwly18+kwvf
+                          ivDb+tbDN3GvyuK0wStWGqC/BKyB/jU7Z5qPRCZhAoIBABUVoNgt4mo+uwvZyWl7
+                          x+gk0zDnOzvKuOuu+0JovM7F6/NuEiXs652mpdd2ePMzwRjmR7JRyF8AOM+Xhw1g
+                          0SHuiR/WOX6KX/TNXrwegaiK895BVLMHyLNPYipRFg3Jf8RM5/KFdo44tHvlQqlE
+                          74pm0BoRuxn6oV3xFiItc2xR6Q37dK0caP6kzv1UCd5ao9Ks8ypf7WUNlYtxPgnL
+                          +hGOXxWj4Q7j+E3MKw2B5dyEPIkF/5hfmGalG4+69eqVC3fyB8RA0AGiXvC2drgr
+                          0E6FmZ66phz1NtXN/JTBDlGt41doI5TppYI+t11UeU9vbRrQs4IVeok0bYo8LRZj
+                          GdkCggEAf5myYEBvr8tuhE100Tau5RoKLS0SoKv7qsDpmFyUiNBmQggecB+X+ck4
+                          3/IToBmZRnzu+Fubqt1yMEKv+SO4F3s1W5sLdQsSAzPcum0fSEf94wONZXGkhntB
+                          0rsHnPTd3APbi1c+ZFQ+DV/Krx5yYGW1J8BBQLvFFuvJ8rK4Q+WCejGI8QCfrVU0
+                          95ctKugep8MTzm2xIecqKQOqx7UY9zWil/o4p9HEyzqzkG05+RQPLoAW9kmtcZZt
+                          HXvIdwLsUASyKB/LlM6dESXr9K4r/im3sn+9oza9mbwsRnBIJ9YU9llTw9j5aUlw
+                          Xwjk99gnvag+HswBDB7A7DCTEqhkUQ==
+                          -----END PRIVATE KEY-----
+              """
+              dir("system-tests") {
+                sh """
+                  cat config.yaml
+                  export http_proxy=http://squid.internal:3128/
+                  export https_proxy=http://squid.internal:3128/
+                  export TOX_PARALLEL_NO_SPINNER=1
+                  tox -e cog
+                  tox -e env_builder -- ${params.PYTEST_ARGS} | tee env_builder.out
+                  tox -e general_tests -- 
+                  tox -p all -e vm1,natasha -- ${params.PYTEST_ARGS}
+                  tox -p all -e opelt,arm64,ppc64le,vm2 -- ${params.PYTEST_ARGS}
+                """
+              }
+            }
+          }
+        }
+        post {
+          always {
+            script {
+              buildInfo = sh (returnStdout: true, script: "awk '/BEGIN_SYSTEMTESTS_META/{ f = 1; next } /END_SYSTEMTESTS_META/ { f = 0 } f' system-tests/env_builder.out")
+              currentBuild.description = "$buildInfo"
+            }
+            sh """
+               export http_proxy=http://squid.internal:3128/
+               export https_proxy=http://squid.internal:3128/
+               env -C system-tests tox -e collect_sos_report
+            """
+            archiveArtifacts artifacts: 'system-tests/sosreport/*,system-tests/*.log,system-tests/config.yaml,system-tests/junit*.xml', fingerprint: true
+            junit allowEmptyResults: true, testResults: 'system-tests/junit*.xml'
+            sh """
+               lxc delete --force maas-system-build || true
+               lxc delete --force maas-system-maas || true
+               lxc delete --force maas-client || true
+            """
+          }
+          success {
+            mattermostSend (color: 'green', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) :success: was successful")
+          }
+          failure {
+            mattermostSend (color: 'red', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) :fire: failed")
+          }
+        }
+      }
diff --git a/jenkins/jobs/systemtests_config_generator.groovy b/jenkins/jobs/systemtests_config_generator.groovy
new file mode 100644
index 0000000..6d7d210
--- /dev/null
+++ b/jenkins/jobs/systemtests_config_generator.groovy
@@ -0,0 +1,92 @@
+def gen_config_cmd = 'tox -e generate_config --'
+pipeline {
+  agent {
+    label 'ci-lab'
+  }
+  options {
+      timeout(time: 6, unit: "HOURS")
+  }
+  environment {
+    TOX_PARALLEL_NO_SPINNER = '1'
+  }
+  stages {
+    stage('Checkout') {
+      steps {
+        script {
+          sh """
+          rm -rf system-tests;
+          git clone ${params.SYSTEMTESTS_GIT_REPO} --branch ${params.SYSTEMTESTS_GIT_BRANCH} --depth 1 system-tests;
+          cd system-tests && git show --no-patch;
+        """
+        }
+      }
+    }
+    stage('Setup') {
+      steps {
+        script {
+          sh '''
+          sudo apt-get install -y tox;
+        '''
+        }
+        script {
+            sh '''
+            cd system-tests
+            tox -e cog
+            '''
+        }
+        script {
+          writeFile file: 'system-tests/base_config.yaml', text: """
+{{ labmaas_base_config.format() }}
+"""
+        }
+      }
+    }
+    stage('Generate configuration') {
+      steps {
+        script {
+          if (params.INSTALLATION_METHOD == 'SNAP') {
+            gen_config_cmd = "${gen_config_cmd} --snap --snap-channel ${params.MAAS_SNAP_CHANNEL} --test-db-channel ${params.TEST_DB_SNAP_CHANNEL}"
+          } else {
+            gen_config_cmd = "${gen_config_cmd} --deb --git-repo ${params.MAAS_GIT_REPO} --git-branch ${params.MAAS_GIT_BRANCH} --ppa ${params.MAAS_PPA}"
+          }
+          if (params.ENABLE_TLS) {
+            gen_config_cmd = "${gen_config_cmd} --tls"
+          }
+          if (params.ENABLE_OBSERVABILITY) {
+            gen_config_cmd = "${gen_config_cmd} --o11y"
+          }
+          if (params.ENABLE_HW_SYNC_TEST) {
+            gen_config_cmd = "${gen_config_cmd} --hw-sync"
+          }
+          if (params.GEN_CONFIG_ARGS) {
+            gen_config_cmd = "${gen_config_cmd} ${params.GEN_CONFIG_ARGS}"
+          }
+
+          sh """
+            cd system-tests
+            cat base_config.yaml
+            ${gen_config_cmd} --containers-image=${params.CONTAINERS_IMAGE} base_config.yaml config.yaml
+            """
+
+          archiveArtifacts artifacts: 'system-tests/config.yaml', fingerprint: true
+        }
+      }
+    }
+    stage('Launch System Tests') {
+      steps {
+        script {
+          build(job: 'maas-system-tests-executor',
+                wait: false,
+                parameters: [
+                  string(name:'SYSTEMTESTS_GIT_REPO', value:"${params.SYSTEMTESTS_GIT_REPO}"),
+                  string(name:'SYSTEMTESTS_GIT_BRANCH', value:"${params.SYSTEMTESTS_GIT_BRANCH}"),
+                  string(name:'PYTEST_ARGS', value:"${params.PYTEST_ARGS}"),
+                  booleanParam(name:'REMOVE_CONTAINERS', value:"${params.REMOVE_CONTAINERS}"),
+                ],
+          )
+        }
+      }
+    }
+  }
+}
+
diff --git a/jenkins/jobs/systemtests_config_generator.yaml b/jenkins/jobs/systemtests_config_generator.yaml
new file mode 100644
index 0000000..f8ee769
--- /dev/null
+++ b/jenkins/jobs/systemtests_config_generator.yaml
@@ -0,0 +1,82 @@
+- project:
+    name: maas-system-tests-config-generator
+    jobs:
+      - maas-system-tests-config-generator
+
+- job-template:
+    name: maas-system-tests-config-generator
+    description: |
+      Generate config for MAAS system tests
+    project-type: pipeline
+    parameters:
+      - string:
+          description: Git repo to pull system tests from.
+          name: SYSTEMTESTS_GIT_REPO
+          default: https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests
+      - string:
+          description: Branch in the repo to use.
+          name: SYSTEMTESTS_GIT_BRANCH
+          default: master
+      - choice:
+          name: INSTALLATION_METHOD
+          choices:
+            - SNAP
+            - DEB
+          description: Which MAAS installation method use for this execution.
+      - string:
+          description: Git repo to pull MAAS from.
+          name: MAAS_GIT_REPO
+          default: https://git.launchpad.net/maas
+      - string:
+          description: Branch in the repo to build from.
+          name: MAAS_GIT_BRANCH
+          default: master
+      - string:
+          description: MAAS PPA for dependencies.
+          name: MAAS_PPA
+          default: ppa:maas-committers/latest-deps
+      - string:
+          description: Channel to use to install MAAS snap.
+          name: MAAS_SNAP_CHANNEL
+          default: latest/edge
+      - string:
+          description: Channel for maas-test-db snap. Leave empty to use the same channel as MAAS snap.
+          name: TEST_DB_SNAP_CHANNEL
+          default: latest/edge
+      - string:
+          description: LXD image for containers used in tests
+          name: CONTAINERS_IMAGE
+          default: ubuntu:22.04
+      - bool:
+          description: Enable TLS when testing.
+          name: ENABLE_TLS
+          default: false
+      - bool:
+          description: Enable telemetry when testing.
+          name: ENABLE_OBSERVABILITY
+          default: false
+      - bool:
+          description: Execute hardware sync feature tests
+          name: ENABLE_HW_SYNC_TEST
+          default: false
+      - bool:
+          description: Should existing containers be torn down before we run?
+          name: REMOVE_CONTAINERS
+          default: true
+      - string:
+          description: Additional args to gen_config (--ppa --architecture --machine etc)
+          name: GEN_CONFIG_ARGS
+          default: ""
+      - string:
+          description: Additional args to pytest
+          name: PYTEST_ARGS
+          default: --log-cli-level info
+
+    triggers:
+      - timed: '@daily'
+    properties:
+      - copyartifact:
+          projects: "maas-system-tests-executor*"
+    dsl: !include-jinja2: systemtests_config_generator.groovy
+
+    labmaas_base_config: !include-jinja2: labmaas_base_config.inc
diff --git a/jenkins/jobs/systemtests_executor.groovy b/jenkins/jobs/systemtests_executor.groovy
new file mode 100644
index 0000000..3638f8f
--- /dev/null
+++ b/jenkins/jobs/systemtests_executor.groovy
@@ -0,0 +1,113 @@
+pipeline {
+    agent {
+        label 'ci-lab'
+    }
+    options {
+        timeout(time: 6, unit: "HOURS")
+    }
+
+    environment {
+        TOX_PARALLEL_NO_SPINNER = '1'
+    }
+
+    stages {
+        stage('Clean') {
+            when {
+                expression {
+                    return params.REMOVE_CONTAINERS
+                }
+            }
+            steps {
+                script {
+                    sh '''
+                    lxc delete maas-system-build --force || true;
+                    lxc delete maas-system-maas --force || true;
+                    lxc delete maas-client --force || true;
+                    '''
+                }
+            }
+        }
+        stage('Checkout') {
+            steps {
+                script {
+                    sh """
+                    lxc list;
+                    pgrep -a qemu || true;
+                    rm -rf system-tests;
+                    git clone ${params.SYSTEMTESTS_GIT_REPO} --branch ${params.SYSTEMTESTS_GIT_BRANCH} --depth 10 system-tests;
+                    cd system-tests && git show --no-patch;
+                    """
+                }
+            }
+        }
+        stage('Copy config.yaml') {
+            steps {
+                script {
+                    copyArtifacts(projectName: currentBuild.upstreamBuilds[0].fullProjectName,
+                                  selector: upstream(),
+                                  filter: 'system-tests/config.yaml',
+                    )
+                }
+            }
+        }
+        stage('Cog') {
+            steps {
+                script {
+                    sh 'env -C system-tests tox run -e cog'
+                }
+            }
+        }
+        stage('Filter envs') {
+            steps {
+                script {
+                    sh 'env -C system-tests tox run -e filter_envs -- --output-file=envs1 vm1,natasha'
+                    sh 'env -C system-tests tox run -e filter_envs -- --output-file=envs2 opelt,arm64,ppc64le,vm2'
+                }
+            }
+        }
+        stage('System Tests') {
+            steps {
+                script {
+                    sh """
+                    cd system-tests
+                    cat config.yaml
+                    tox run -e env_builder -- ${params.PYTEST_ARGS} | tee env_builder.out
+                    tox run -e general_tests -- ${params.PYTEST_ARGS}
+                    """
+                    status1 = sh(script:"""
+                    cd system-tests
+                    if test -s envs1; then tox run-parallel -e \$(cat envs1) -- ${params.PYTEST_ARGS}; else true; fi
+                    """, returnStatus: true)
+                    status2 = sh(script:"""
+                    cd system-tests
+                    if test -s envs2; then tox run-parallel -e \$(cat envs2) -- ${params.PYTEST_ARGS}; else true; fi
+                    """, returnStatus: true)
+                    if (status1 || status2) {
+                        sh 'exit 1'
+                    }
+                }
+            }
+        }
+    }
+    post {
+        always {
+            script {
+              buildInfo = sh (returnStdout: true, script: "awk '/BEGIN_SYSTEMTESTS_META/{ f = 1; next } /END_SYSTEMTESTS_META/ { f = 0 } f' system-tests/env_builder.out")
+              currentBuild.description = "$buildInfo"
+            }
+            sh 'env -C system-tests tox run -e collect_sos_report || true'
+            archiveArtifacts(
+                artifacts:'system-tests/sosreport/*,' +
+                          'system-tests/*.log,' +
+                          'system-tests/config.yaml' +
+                          'system-tests/junit*.xml',
+                fingerprint: true
+            )
+            junit allowEmptyResults: true, testResults: 'system-tests/junit*.xml'
+            sh(script:'lxc delete --force maas-system-build;' +
+                      'lxc delete --force maas-system-maas;' +
+                      'lxc delete --force maas-client',
+               returnStatus: true)
+        }
+    }
+}
diff --git a/jenkins/jobs/systemtests_executor.yaml b/jenkins/jobs/systemtests_executor.yaml
new file mode 100644
index 0000000..87500bb
--- /dev/null
+++ b/jenkins/jobs/systemtests_executor.yaml
@@ -0,0 +1,29 @@
+- project:
+    name: maas-system-tests-executor
+    jobs:
+      - maas-system-tests-executor
+
+- job-template:
+    name: maas-system-tests-executor
+    description: |
+      Exexcute MAAS system tests
+    project-type: pipeline
+    parameters:
+      - string:
+          description: Git repo to pull system tests from
+          name: SYSTEMTESTS_GIT_REPO
+          default: https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests
+      - string:
+          description: Branch in the repo to use
+          name: SYSTEMTESTS_GIT_BRANCH
+          default: master
+      - string:
+          description: Additional args to pytest
+          name: PYTEST_ARGS
+          default: --log-cli-level info
+      - bool:
+          description: Should existing containers be torn down before we run?
+          name: REMOVE_CONTAINERS
+          default: true
+
+    dsl: !include-jinja2: systemtests_executor.groovy
diff --git a/jenkins/jobs/systemtests_snap.yaml b/jenkins/jobs/systemtests_snap.yaml
new file mode 100644
index 0000000..1f5edcf
--- /dev/null
+++ b/jenkins/jobs/systemtests_snap.yaml
@@ -0,0 +1,300 @@
+- job:
+    name: maas-system-tests-snap
+    description: |
+      Run the MAAS system tests, installed from snap
+    project-type: pipeline
+    triggers:
+      - timed: '@daily'
+    dsl: |
+      pipeline {
+        agent {
+          label 'ci-lab'
+        }
+        options {
+            timeout(time: 6, unit: "HOURS")
+        }
+        parameters {
+          string(name: 'GIT_REPO', defaultValue: 'https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests', description: 'Git repo to pull system tests from')
+          string(name: 'GIT_BRANCH', defaultValue: 'master', description: 'Branch in the repo to use')
+          string(name: 'PYTEST_ARGS', defaultValue: '--log-cli-level info', description: 'Additional args to pytest')
+          string(name: 'MAAS_SNAP_CHANNEL', defaultValue: 'latest/edge', description: 'Channel of maas snap.')
+          string(name: 'TEST_DB_SNAP_CHANNEL', defaultValue: '', description: 'Channel of maas-test-db snap. Leave empty to use same channel of maas snap.')
+          string(name: 'CONTAINERS_IMAGE', defaultValue: 'ubuntu:22.04', description: 'LXD image for containers used in tests')
+          booleanParam(name: 'REMOVE_CONTAINERS', defaultValue: true, description: 'Should existing containers be torn down before we run?')
+        }
+        stages {
+          stage('Checkout') {
+            steps {
+              sh """
+                lxc list
+                pgrep -a qemu || true
+                rm -rf system-tests
+                git clone ${params.GIT_REPO} --branch ${params.GIT_BRANCH} --depth 10 system-tests
+                env -C system-tests git show --no-patch
+              """
+            }
+          }
+          stage('Clean') {
+            when {
+              expression {
+                return params.REMOVE_CONTAINERS
+              }
+            }
+            steps {
+              sh """
+                lxc delete maas-system-build --force || true
+                lxc delete maas-system-maas --force || true
+                lxc delete maas-client --force || true
+              """
+            }
+          }
+          stage('Setup') {
+            steps {
+              sh """
+                sudo apt-get install -y tox
+              """
+            }
+          }
+          stage('System Tests') {
+            steps {
+              writeFile file: 'system-tests/config.yaml', text: """
+      proxy:
+          use_internal: true
+          http: http://squid.internal:3128
+      networks:
+          pxe:
+              cidr: 10.245.136.0/21
+              bridge: br0
+              dynamic:
+                  start: 10.245.136.21
+                  end: 10.245.136.200
+              reserved:
+                  - start: 10.245.136.1
+                    end: 10.245.136.19
+                    comment: Reserved
+                  - start: 10.245.143.100
+                    end: 10.245.143.200
+                    comment: BMCs
+      maas:
+          networks:
+              pxe: 10.245.136.20
+          config:
+              # This values will set with `maas set-config` command
+              upstream_dns: 10.245.136.1
+              dnssec_validation: "no"
+          domain_name: systemtestsmaas
+
+      windows_image_file_path: /home/ubuntu/other-os-images/windows-win2012hvr2-amd64-root-dd
+
+      tls:
+          cacerts: ssl-cert-snakeoil.pem
+
+      vault:
+          snap-channel: 1.11/stable
+
+      containers-image: ${params.CONTAINERS_IMAGE}
+
+      machines:
+          hardware:
+              opelt:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.121
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:6d:fb:3c
+              stunky:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.127
+                      power_username: landscape
+                      power_password: insecure
+                  mac_address: ec:b1:d7:7f:ef:34
+              natasha:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.120
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:75:6b:ee
+                  osystem: windows
+              arm64:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.114
+                      power_username: admin
+                      power_password: password
+                  mac_address: 1c:1b:0d:0d:52:7c
+                  architecture: arm64
+              ppc64le:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.113
+                      power_username: ""
+                      power_password: admin
+                  mac_address: 98:be:94:02:6a:c8
+                  architecture: ppc64el
+          vms:
+              instances:
+                  vm1:
+                      mac_address: 00:16:3e:d6:36:18
+                      devices:
+                          disk1:
+                              type: disk
+                              source: /home/ubuntu/diego/test_block_device.img
+                              #size: 1MB
+                              path: /mnt/ext2
+                          iface1:
+                              type: nic
+                              nictype: bridged
+                              parent: br0
+                              name: eth1
+                              hwaddr: 00:16:3e:4a:19:6f
+                          # usb1:
+                          #     type: usb
+                          #     productid: "0016"
+                          # pci1:
+                          #     type: pci
+                          #     address: '02:00.1'
+                      lxd_profile: prof-maas-test
+                  vm2:
+                      mac_address: 00:16:3e:d0:54:9f
+                      lxd_profile: prof-maas-test
+              power_type: lxd
+              power_parameters:
+                  power_address: https://10.157.204.1:8443
+                  project: default
+                  certificate: |
+                                  -----BEGIN CERTIFICATE-----
+                                  MIIElzCCAn8CEQD+6NBHtM1z951J2HwbtZwnMA0GCSqGSIb3DQEBDQUAMAAwHhcN
+                                  MjIwMjA4MTMwNDI4WhcNMzIwMjA2MTMwNDI4WjATMREwDwYDVQQDDAh0ZXN0aG9z
+                                  dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANRvcoT8dofRU2A0ICm0
+                                  JzZPJr9C7oQmJhxd/I8Fwzt1Jzom0mhg+8oSFXPI7jzx8kpFexJZHBF8WBUII+0v
+                                  RnZx8oeIRtvrL22tKEqeH2dwZxHIpcmoPbj2jMCFR1IFhFGBJD1uDUvkBAYFnguM
+                                  tJml5yh+BhhPGc8Ehds/kstq032rWlDjx0Cne+5mebSniz+TW5+uc/7Vy4mIDm3P
+                                  IGgytRXDseryvJ0kuLkNZGT38tHTva22skz0K0mRImNgCdjbacsS85DQymANJtP/
+                                  TbAZnZ2VGLGJ2jXVBEKcXDRdrT5jusQTdRseciXqbSBuvCcsd+4R85ZZNO4ARDqI
+                                  XNWEEinspXfJtBuvx2dtZ1S8K233kz1ds5n3vLirw6LIeNW1b8a200JeDovBzUmt
+                                  fu9qSgw+fS2H7k/TnQDzd1AqhcKQpZqZd2eY1d4AbM9L9oEPFPesISNko7U0NpPC
+                                  5RfejxL+IMhshKDPWCo50I4Ty80gSPbzRBJUXE4Qq3ZiiqiK8EW0VRobz6ZfDF1A
+                                  /2oR6C6QvNd30lzdP1laLLHKBh2A3TTWL4UE+N6eH38LdO2isbh/8ux068sclxzX
+                                  Ba34D5ICzjHd4FpBHiNpyTnb8QAaKcOFLr2QGHogRCQI2QygPyB6jaVTTVwkJbKS
+                                  kuJBcUjBNmkEnx1VFIqnzDr5AgMBAAEwDQYJKoZIhvcNAQENBQADggIBAL7aLr6Z
+                                  Hfqlefng/JMr+ojDJWmdtHTqwa9MviEzHTlzhvTw3JRCMFlIlIrK7wpd7kr9Jmhz
+                                  HI3atc1NtrEsMeUsvNmopnlqKWPwcyCYX0juKvoMeTqFDLN7yWPBHEc4NNhXJQte
+                                  pee7P02e8+C0g4UXlQ8Hry1LfwhlwHok0LijEbBkX4x0m7RlqqprMfJJsU6wL613
+                                  WOcfTEiHkZYB/a/yeIAhdqSi3jMH3m+jbG9Tjfn62pPheSzRpH28A5mvZVP39RUO
+                                  JdpEHT7F5pMmauygWBxXT/VCU0TriCMYrexjA1cWOlrT0+VU5Im3+u0l3vUE+2Wc
+                                  zKNpt3l3wG3KmLHeJr9/7QyFLMHhEYr4xpMoikIhq75iLz6GqGkgho7Xy7Iz2WZP
+                                  YW39eNAYdW8UEnSpdlmsA0gIHeWDVWZFebMk8Jw5AtIr6qTUGRMp4jFzwaQX4Oc2
+                                  kf2jqICMKz3nQjT5b5+/w6OKuz1POrzA9hRkp85fDdeD/zwO9v+65qwHk9T+FOM4
+                                  xC/VQcoSp5lJmGN6NGCleLhpUbIwX381rCk7pvOPQ5PLWW2gPB61njsdbJ97fWpy
+                                  5b7QD4Pcq+eQ2Xy1+F4ToCmW8E6XorUFPUKTInTl4McHbJiuGwmXAkGqLzgJd+Q1
+                                  opB3iqj1W36wgZQK6lRoN7X/zugt9m+MZX/6
+                                  -----END CERTIFICATE-----
+                  key: |
+                          -----BEGIN PRIVATE KEY-----
+                          MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDUb3KE/HaH0VNg
+                          NCAptCc2Tya/Qu6EJiYcXfyPBcM7dSc6JtJoYPvKEhVzyO488fJKRXsSWRwRfFgV
+                          CCPtL0Z2cfKHiEbb6y9trShKnh9ncGcRyKXJqD249ozAhUdSBYRRgSQ9bg1L5AQG
+                          BZ4LjLSZpecofgYYTxnPBIXbP5LLatN9q1pQ48dAp3vuZnm0p4s/k1ufrnP+1cuJ
+                          iA5tzyBoMrUVw7Hq8rydJLi5DWRk9/LR072ttrJM9CtJkSJjYAnY22nLEvOQ0Mpg
+                          DSbT/02wGZ2dlRixido11QRCnFw0Xa0+Y7rEE3UbHnIl6m0gbrwnLHfuEfOWWTTu
+                          AEQ6iFzVhBIp7KV3ybQbr8dnbWdUvCtt95M9XbOZ97y4q8OiyHjVtW/GttNCXg6L
+                          wc1JrX7vakoMPn0th+5P050A83dQKoXCkKWamXdnmNXeAGzPS/aBDxT3rCEjZKO1
+                          NDaTwuUX3o8S/iDIbISgz1gqOdCOE8vNIEj280QSVFxOEKt2YoqoivBFtFUaG8+m
+                          XwxdQP9qEegukLzXd9Jc3T9ZWiyxygYdgN001i+FBPjenh9/C3TtorG4f/LsdOvL
+                          HJcc1wWt+A+SAs4x3eBaQR4jack52/EAGinDhS69kBh6IEQkCNkMoD8geo2lU01c
+                          JCWykpLiQXFIwTZpBJ8dVRSKp8w6+QIDAQABAoICAQCxuqQHGulX7AtjW3jlKzH7
+                          P/Fc5vSCXyBXb1KTnfCe1/7/qeczKKC/iK2l9x9KoelhtgunaCIRhwRyZCMalwjO
+                          o7qTJbKS34sIqWwiMXR4qBOzTzlVI4qwKqXLlDX9K1xujCrzshUxvwyWtTBq3Udj
+                          nOduezFCOTuQdWo/6ko4IaHba/bd4hObxgPripScTeg0QmbPi7bEJ75nzAq2WCn2
+                          wyW5lcZOmNKwbj6Vo9ywlLj0T8BLi6RUuZtVqzUoCvtyEO/L1IkuSWBnR9mKV/h5
+                          MpUpd8n3DywfCZ7M0+BYd18v6WQiE11QWQKLMjwmfD6yT4PvC9nNmcisrlBm4Bs5
+                          h9ESg7/JrfiXdJPa7JJ4JH01/gvQik6BAp+NhZEOg05mi81CE7ECchyDTzjbk5HS
+                          xUbdiTVSWO1zy4mADmNqMs6JgF4JNiSg652sl96nZsXuZ13iQW1I8Y0jR1HCC+Xt
+                          +HgYuSpriL3THyaQX8vWumkhB4AL3jU1/Vh64OoQQfcDJi6IpSiSM+Gs7E8uBAWi
+                          aILf4dxB9liQCv/GYvhFQ7eOx+bcg6orp0hmqL809KAAoe6siT6CVTWa7n7nZWDS
+                          pH4sNXnwwC4cqEwFjmCNXzTv0+rWR0lKc02s5aZbCk7L34O10XdeqP077KXS87+v
+                          DJC1yI7JkOQUfB2ztzj7AQKCAQEA/q0jX7+wbFtn3bQKdlp+qHznaXgyorVFoR7p
+                          0rarF9oP0C2a3hdGoQMANosg/1fpJHmVHrLMVGtBuqhrw8RCjlqe9PxlKeoKnR6I
+                          AWcmt8D45UuucJ1fatsB9SfBasCaLXZZrmCF6bYzm3c4Y4J6uxircsHViMGJWsaA
+                          grw5kPtRbPSCWofYCM0G1x9J+P/ymgZOzGwPtpmzUHXHbN3WVNTl9K9nEwayveUj
+                          QbzSnw9+CyWPFUubhjnap9pboAM7jbRbP71I0u/EboHq/3BeTLuxRTEp0imtvxHo
+                          LTFtFu8Jo9tKg4XUJQHFzuRueG39dK2bl4HtnT7aIfXCX67ZEQKCAQEA1Yoa5Nmw
+                          VDs8CYcScjh4GsWzPZ0GA6NMA1G7I7k71ixWS/VNQ0GxYAZ3y6ItZz4pkKZ/LFPR
+                          WVAak+SR7+7ZxiYFPJ8WnqKJZxvzjr7+Bh3YaFSek0P+sr2OXlz7uO44Kzo0RvMG
+                          6STb+8vhZpDnpMk6TvUWf7FTnYLAoi8kHDhjsJTa3+E5wujr3mnps/vQQyBQrY7s
+                          2gU+5znvve77v5yl1zYHT0gWimdJxMZp4cd4Hiqr3kMME12R+V/XtWz+LgeBViXA
+                          Z0T6Tnc2+bsaestFSNe4dTuIAwoGM2pDmXc874/DwB4piO4QOM2p/ZOLdmYUmlPQ
+                          ogBYkGMIw+oDaQKCAQAwxOsPPOAGAAMF26JdQ7sZfMG72r6nldr9nbPdHAnriWCZ
+                          1wHfIcnur2ptB3uMKkOFLps1w7uJNvjhS7tHQ+AS7pueAm9E9YKOz/fvfNdXPObs
+                          0e9XtWs+RS48yh4p2TQtHIrT77v1I2UCknQD6kqiZXj/gsrnY1hwP68AWhcUAmx3
+                          VuNXfsgJ92kl7OH3gtvsTuTsFI11xD0oXUWRPXH70MEweB5e8FtuLeDwh741o3vZ
+                          mpmp1E62B4ItvozpOXVAD5ehvxeg/TU6jDp6LASC4TZzL5T4n+6btkwly18+kwvf
+                          ivDb+tbDN3GvyuK0wStWGqC/BKyB/jU7Z5qPRCZhAoIBABUVoNgt4mo+uwvZyWl7
+                          x+gk0zDnOzvKuOuu+0JovM7F6/NuEiXs652mpdd2ePMzwRjmR7JRyF8AOM+Xhw1g
+                          0SHuiR/WOX6KX/TNXrwegaiK895BVLMHyLNPYipRFg3Jf8RM5/KFdo44tHvlQqlE
+                          74pm0BoRuxn6oV3xFiItc2xR6Q37dK0caP6kzv1UCd5ao9Ks8ypf7WUNlYtxPgnL
+                          +hGOXxWj4Q7j+E3MKw2B5dyEPIkF/5hfmGalG4+69eqVC3fyB8RA0AGiXvC2drgr
+                          0E6FmZ66phz1NtXN/JTBDlGt41doI5TppYI+t11UeU9vbRrQs4IVeok0bYo8LRZj
+                          GdkCggEAf5myYEBvr8tuhE100Tau5RoKLS0SoKv7qsDpmFyUiNBmQggecB+X+ck4
+                          3/IToBmZRnzu+Fubqt1yMEKv+SO4F3s1W5sLdQsSAzPcum0fSEf94wONZXGkhntB
+                          0rsHnPTd3APbi1c+ZFQ+DV/Krx5yYGW1J8BBQLvFFuvJ8rK4Q+WCejGI8QCfrVU0
+                          95ctKugep8MTzm2xIecqKQOqx7UY9zWil/o4p9HEyzqzkG05+RQPLoAW9kmtcZZt
+                          HXvIdwLsUASyKB/LlM6dESXr9K4r/im3sn+9oza9mbwsRnBIJ9YU9llTw9j5aUlw
+                          Xwjk99gnvag+HswBDB7A7DCTEqhkUQ==
+                          -----END PRIVATE KEY-----
+
+      snap:
+          maas_channel: ${params.MAAS_SNAP_CHANNEL}
+          test_db_channel: ${params.TEST_DB_SNAP_CHANNEL}
+              """
+              dir("system-tests") {
+                sh """
+                  cat config.yaml
+                  export http_proxy=http://squid.internal:3128/
+                  export https_proxy=http://squid.internal:3128/
+                  export TOX_PARALLEL_NO_SPINNER=1
+                  tox -e cog
+                  tox -e env_builder -- ${params.PYTEST_ARGS} | tee env_builder.out
+                  tox -e general_tests -- ${params.PYTEST_ARGS}
+                  tox -p all -e vm1,natasha -- ${params.PYTEST_ARGS}
+                  tox -p all -e opelt,arm64,ppc64le,vm2 -- ${params.PYTEST_ARGS}
+                """
+              }
+            }
+          }
+        }
+        post {
+          always {
+            script {
+              buildInfo = sh (returnStdout: true, script: "awk '/BEGIN_SYSTEMTESTS_META/{ f = 1; next } /END_SYSTEMTESTS_META/ { f = 0 } f' system-tests/env_builder.out")
+              currentBuild.description = "$buildInfo"
+            }
+            sh """
+              export http_proxy=http://squid.internal:3128/
+              export https_proxy=http://squid.internal:3128/
+              env -C system-tests tox -e collect_sos_report
+            """
+            archiveArtifacts artifacts: 'system-tests/sosreport/*,system-tests/*.log,system-tests/config.yaml,system-tests/junit*.xml', fingerprint: true
+            junit allowEmptyResults: true, testResults: 'system-tests/junit*.xml'
+            sh """
+              lxc delete maas-system-build --force || true
+              lxc delete maas-system-maas --force || true
+              lxc delete maas-client --force || true
+            """
+          }
+          success {
+            mattermostSend (color: 'green', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) :success: was successful")
+          }
+          failure {
+            mattermostSend (color: 'red', message: "[${env.JOB_NAME} #${env.BUILD_NUMBER}](${env.BUILD_URL}) :fire: failed")
+          }
+        }
+      }
diff --git a/jenkins/jobs/systemtests_v3.yaml b/jenkins/jobs/systemtests_v3.yaml
new file mode 100644
index 0000000..4c03c02
--- /dev/null
+++ b/jenkins/jobs/systemtests_v3.yaml
@@ -0,0 +1,340 @@
+- job:
+    name: maas-system-tests-wip
+    description: |
+      Run the MAAS system tests
+    project-type: pipeline
+    triggers:
+      - timed: '@daily'
+    dsl: |
+      def cd_tox = "env -C system-tests tox"
+      def gen_config_cmd = "${cd_tox} -e generate_config --"
+      pipeline {
+        agent {
+          label 'ci-lab'
+        }
+        options {
+            timeout(time: 6, unit: "HOURS")
+        }
+        parameters {
+          string(name: 'SYSTEMTESTS_GIT_REPO', defaultValue: 'https://git.launchpad.net/~maas-committers/maas-ci/+git/system-tests', description: 'Git repo to pull system tests from')
+          string(name: 'SYSTEMTESTS_GIT_BRANCH', defaultValue: 'master', description: 'Branch in the repo to use')
+
+          choice(name: 'INSTALLATION_METTHOD', choices: ['SNAP', 'DEB'], description: 'Which MAAS installation method use for this execution.')
+          string(name: 'MAAS_GIT_REPO', defaultValue: 'https://git.launchpad.net/maas', description: 'Git repo to pull MAAS of.')
+          string(name: 'MAAS_GIT_BRANCH', defaultValue: 'master', description: 'branch of MAAS project to build from.')
+          string(name: 'MAAS_PPA', defaultValue: 'ppa:maas-committers/latest-deps', description: 'MAAS ppa for dependencies.')
+
+          string(name: 'MAAS_SNAP_CHANNEL', defaultValue: 'latest/edge', description: 'Channel of MAAS snap.')
+          string(name: 'TEST_DB_SNAP_CHANNEL', defaultValue: '', description: 'Channel of maas-test-db snap. Leave empty to use same channel of MAAS snap.')
+
+          string(name: 'CONTAINERS_IMAGE', defaultValue: 'ubuntu:22.04', description: 'LXD image for containers used in tests')
+          booleanParam(name: 'ENABLE_TLS', defaultValue: false, description: 'During MAAS installation enable TLS.')
+          booleanParam(name: 'ENABLE_OBSERVABILITY', defaultValue: false, description: 'During MAAS installation enable telemtry and send it to our O11y stack.')
+          booleanParam(name: 'ENABLE_HW_SYNC_TEST', defaultValue: false, description: 'Execute HW SYNC feature tests')
+
+          string(name: 'GEN_CONFIG_ARGS', defaultValue: '', description: 'Additional args to gen_onfig (--ppa --architecture --machine etc)')
+          string(name: 'PYTEST_ARGS', defaultValue: '--log-cli-level info', description: 'Additional args to pytest')
+
+          booleanParam(name: 'REMOVE_CONTAINERS', defaultValue: true, description: 'Should existing containers be torn down before we run?')
+        }
+        environment {
+          http_proxy ='http://squid.internal:3128/'
+          https_proxy='http://squid.internal:3128/'
+          TOX_PARALLEL_NO_SPINNER='1'
+        }
+        stages {
+          stage('Checkout') {
+            steps {
+              sh """
+                lxc list
+                pgrep -a qemu || true
+                rm -rf system-tests
+                git clone ${params.SYSTEMTESTS_GIT_REPO} --branch ${params.SYSTEMTESTS_GIT_BRANCH} --depth 10 system-tests
+                cd system-tests && git show --no-patch
+              """
+            }
+          }
+          stage('Clean') {
+            when {
+              expression {
+                return params.REMOVE_CONTAINERS
+              }
+            }
+            steps {
+              sh """
+                lxc delete maas-system-build --force || true
+                lxc delete maas-system-maas --force || true
+                lxc delete maas-client --force || true
+              """
+            }
+          }
+          stage('Setup') {
+            steps {
+              sh """
+                sudo apt-get install -y tox
+              """
+              writeFile file: 'system-tests/base_config.yaml', text: """
+      proxy:
+          use_internal: true
+          http: http://squid.internal:3128
+      networks:
+          pxe:
+              cidr: 10.245.136.0/21
+              bridge: br0
+              dynamic:
+                  start: 10.245.136.21
+                  end: 10.245.136.200
+              reserved:
+                  - start: 10.245.136.1
+                    end: 10.245.136.19
+                    comment: Reserved
+                  - start: 10.245.143.100
+                    end: 10.245.143.200
+                    comment: BMCs
+      maas:
+          networks:
+              pxe: 10.245.136.20
+          config:
+              # This values will set with `maas set-config` command
+              upstream_dns: 10.245.136.1
+              dnssec_validation: "no"
+          domain_name: systemtestsmaas
+
+      windows_image_file_path: /home/ubuntu/other-os-images/windows-win2012hvr2-amd64-root-dd
+
+      tls:
+          cacerts: ssl-cert-snakeoil.pem
+
+      vault:
+          snap-channel: 1.11/stable
+
+      containers-image: ${params.CONTAINERS_IMAGE}
+
+      machines:
+          hardware:
+              opelt:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.121
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:6d:fb:3c
+              stunky:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.127
+                      power_username: landscape
+                      power_password: insecure
+                  mac_address: ec:b1:d7:7f:ef:34
+              natasha:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.120
+                      power_username: root
+                      power_password: calvin
+                  mac_address: 18:66:da:75:6b:ee
+                  osystem: windows
+              arm64:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.114
+                      power_username: admin
+                      power_password: password
+                  mac_address: 1c:1b:0d:0d:52:7c
+                  architecture: arm64
+              ppc64le:
+                  power_type: ipmi
+                  power_parameters:
+                      power_driver: LAN_2_0
+                      power_address: 10.245.143.113
+                      power_username: ""
+                      power_password: admin
+                  mac_address: 98:be:94:02:6a:c8
+                  architecture: ppc64el
+          vms:
+              instances:
+                  vm1:
+                      mac_address: 00:16:3e:d6:36:18
+                      devices:
+                          disk1:
+                              type: disk
+                              source: /home/ubuntu/diego/test_block_device.img
+                              #size: 1MB
+                              path: /mnt/ext2
+                          iface1:
+                              type: nic
+                              nictype: bridged
+                              parent: br0
+                              name: eth1
+                              hwaddr: 00:16:3e:4a:19:6f
+                          # usb1:
+                          #     type: usb
+                          #     productid: "0016"
+                          # pci1:
+                          #     type: pci
+                          #     address: '02:00.1'
+                      lxd_profile: prof-maas-test
+                  vm2:
+                      mac_address: 00:16:3e:d0:54:9f
+                      lxd_profile: prof-maas-test
+              power_type: lxd
+              power_parameters:
+                  power_address: https://10.157.204.1:8443
+                  project: default
+                  certificate: |
+                                  -----BEGIN CERTIFICATE-----
+                                  MIIElzCCAn8CEQD+6NBHtM1z951J2HwbtZwnMA0GCSqGSIb3DQEBDQUAMAAwHhcN
+                                  MjIwMjA4MTMwNDI4WhcNMzIwMjA2MTMwNDI4WjATMREwDwYDVQQDDAh0ZXN0aG9z
+                                  dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANRvcoT8dofRU2A0ICm0
+                                  JzZPJr9C7oQmJhxd/I8Fwzt1Jzom0mhg+8oSFXPI7jzx8kpFexJZHBF8WBUII+0v
+                                  RnZx8oeIRtvrL22tKEqeH2dwZxHIpcmoPbj2jMCFR1IFhFGBJD1uDUvkBAYFnguM
+                                  tJml5yh+BhhPGc8Ehds/kstq032rWlDjx0Cne+5mebSniz+TW5+uc/7Vy4mIDm3P
+                                  IGgytRXDseryvJ0kuLkNZGT38tHTva22skz0K0mRImNgCdjbacsS85DQymANJtP/
+                                  TbAZnZ2VGLGJ2jXVBEKcXDRdrT5jusQTdRseciXqbSBuvCcsd+4R85ZZNO4ARDqI
+                                  XNWEEinspXfJtBuvx2dtZ1S8K233kz1ds5n3vLirw6LIeNW1b8a200JeDovBzUmt
+                                  fu9qSgw+fS2H7k/TnQDzd1AqhcKQpZqZd2eY1d4AbM9L9oEPFPesISNko7U0NpPC
+                                  5RfejxL+IMhshKDPWCo50I4Ty80gSPbzRBJUXE4Qq3ZiiqiK8EW0VRobz6ZfDF1A
+                                  /2oR6C6QvNd30lzdP1laLLHKBh2A3TTWL4UE+N6eH38LdO2isbh/8ux068sclxzX
+                                  Ba34D5ICzjHd4FpBHiNpyTnb8QAaKcOFLr2QGHogRCQI2QygPyB6jaVTTVwkJbKS
+                                  kuJBcUjBNmkEnx1VFIqnzDr5AgMBAAEwDQYJKoZIhvcNAQENBQADggIBAL7aLr6Z
+                                  Hfqlefng/JMr+ojDJWmdtHTqwa9MviEzHTlzhvTw3JRCMFlIlIrK7wpd7kr9Jmhz
+                                  HI3atc1NtrEsMeUsvNmopnlqKWPwcyCYX0juKvoMeTqFDLN7yWPBHEc4NNhXJQte
+                                  pee7P02e8+C0g4UXlQ8Hry1LfwhlwHok0LijEbBkX4x0m7RlqqprMfJJsU6wL613
+                                  WOcfTEiHkZYB/a/yeIAhdqSi3jMH3m+jbG9Tjfn62pPheSzRpH28A5mvZVP39RUO
+                                  JdpEHT7F5pMmauygWBxXT/VCU0TriCMYrexjA1cWOlrT0+VU5Im3+u0l3vUE+2Wc
+                                  zKNpt3l3wG3KmLHeJr9/7QyFLMHhEYr4xpMoikIhq75iLz6GqGkgho7Xy7Iz2WZP
+                                  YW39eNAYdW8UEnSpdlmsA0gIHeWDVWZFebMk8Jw5AtIr6qTUGRMp4jFzwaQX4Oc2
+                                  kf2jqICMKz3nQjT5b5+/w6OKuz1POrzA9hRkp85fDdeD/zwO9v+65qwHk9T+FOM4
+                                  xC/VQcoSp5lJmGN6NGCleLhpUbIwX381rCk7pvOPQ5PLWW2gPB61njsdbJ97fWpy
+                                  5b7QD4Pcq+eQ2Xy1+F4ToCmW8E6XorUFPUKTInTl4McHbJiuGwmXAkGqLzgJd+Q1
+                                  opB3iqj1W36wgZQK6lRoN7X/zugt9m+MZX/6
+                                  -----END CERTIFICATE-----
+                  key: |
+                          -----BEGIN PRIVATE KEY-----
+                          MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDUb3KE/HaH0VNg
+                          NCAptCc2Tya/Qu6EJiYcXfyPBcM7dSc6JtJoYPvKEhVzyO488fJKRXsSWRwRfFgV
+                          CCPtL0Z2cfKHiEbb6y9trShKnh9ncGcRyKXJqD249ozAhUdSBYRRgSQ9bg1L5AQG
+                          BZ4LjLSZpecofgYYTxnPBIXbP5LLatN9q1pQ48dAp3vuZnm0p4s/k1ufrnP+1cuJ
+                          iA5tzyBoMrUVw7Hq8rydJLi5DWRk9/LR072ttrJM9CtJkSJjYAnY22nLEvOQ0Mpg
+                          DSbT/02wGZ2dlRixido11QRCnFw0Xa0+Y7rEE3UbHnIl6m0gbrwnLHfuEfOWWTTu
+                          AEQ6iFzVhBIp7KV3ybQbr8dnbWdUvCtt95M9XbOZ97y4q8OiyHjVtW/GttNCXg6L
+                          wc1JrX7vakoMPn0th+5P050A83dQKoXCkKWamXdnmNXeAGzPS/aBDxT3rCEjZKO1
+                          NDaTwuUX3o8S/iDIbISgz1gqOdCOE8vNIEj280QSVFxOEKt2YoqoivBFtFUaG8+m
+                          XwxdQP9qEegukLzXd9Jc3T9ZWiyxygYdgN001i+FBPjenh9/C3TtorG4f/LsdOvL
+                          HJcc1wWt+A+SAs4x3eBaQR4jack52/EAGinDhS69kBh6IEQkCNkMoD8geo2lU01c
+                          JCWykpLiQXFIwTZpBJ8dVRSKp8w6+QIDAQABAoICAQCxuqQHGulX7AtjW3jlKzH7
+                          P/Fc5vSCXyBXb1KTnfCe1/7/qeczKKC/iK2l9x9KoelhtgunaCIRhwRyZCMalwjO
+                          o7qTJbKS34sIqWwiMXR4qBOzTzlVI4qwKqXLlDX9K1xujCrzshUxvwyWtTBq3Udj
+                          nOduezFCOTuQdWo/6ko4IaHba/bd4hObxgPripScTeg0QmbPi7bEJ75nzAq2WCn2
+                          wyW5lcZOmNKwbj6Vo9ywlLj0T8BLi6RUuZtVqzUoCvtyEO/L1IkuSWBnR9mKV/h5
+                          MpUpd8n3DywfCZ7M0+BYd18v6WQiE11QWQKLMjwmfD6yT4PvC9nNmcisrlBm4Bs5
+                          h9ESg7/JrfiXdJPa7JJ4JH01/gvQik6BAp+NhZEOg05mi81CE7ECchyDTzjbk5HS
+                          xUbdiTVSWO1zy4mADmNqMs6JgF4JNiSg652sl96nZsXuZ13iQW1I8Y0jR1HCC+Xt
+                          +HgYuSpriL3THyaQX8vWumkhB4AL3jU1/Vh64OoQQfcDJi6IpSiSM+Gs7E8uBAWi
+                          aILf4dxB9liQCv/GYvhFQ7eOx+bcg6orp0hmqL809KAAoe6siT6CVTWa7n7nZWDS
+                          pH4sNXnwwC4cqEwFjmCNXzTv0+rWR0lKc02s5aZbCk7L34O10XdeqP077KXS87+v
+                          DJC1yI7JkOQUfB2ztzj7AQKCAQEA/q0jX7+wbFtn3bQKdlp+qHznaXgyorVFoR7p
+                          0rarF9oP0C2a3hdGoQMANosg/1fpJHmVHrLMVGtBuqhrw8RCjlqe9PxlKeoKnR6I
+                          AWcmt8D45UuucJ1fatsB9SfBasCaLXZZrmCF6bYzm3c4Y4J6uxircsHViMGJWsaA
+                          grw5kPtRbPSCWofYCM0G1x9J+P/ymgZOzGwPtpmzUHXHbN3WVNTl9K9nEwayveUj
+                          QbzSnw9+CyWPFUubhjnap9pboAM7jbRbP71I0u/EboHq/3BeTLuxRTEp0imtvxHo
+                          LTFtFu8Jo9tKg4XUJQHFzuRueG39dK2bl4HtnT7aIfXCX67ZEQKCAQEA1Yoa5Nmw
+                          VDs8CYcScjh4GsWzPZ0GA6NMA1G7I7k71ixWS/VNQ0GxYAZ3y6ItZz4pkKZ/LFPR
+                          WVAak+SR7+7ZxiYFPJ8WnqKJZxvzjr7+Bh3YaFSek0P+sr2OXlz7uO44Kzo0RvMG
+                          6STb+8vhZpDnpMk6TvUWf7FTnYLAoi8kHDhjsJTa3+E5wujr3mnps/vQQyBQrY7s
+                          2gU+5znvve77v5yl1zYHT0gWimdJxMZp4cd4Hiqr3kMME12R+V/XtWz+LgeBViXA
+                          Z0T6Tnc2+bsaestFSNe4dTuIAwoGM2pDmXc874/DwB4piO4QOM2p/ZOLdmYUmlPQ
+                          ogBYkGMIw+oDaQKCAQAwxOsPPOAGAAMF26JdQ7sZfMG72r6nldr9nbPdHAnriWCZ
+                          1wHfIcnur2ptB3uMKkOFLps1w7uJNvjhS7tHQ+AS7pueAm9E9YKOz/fvfNdXPObs
+                          0e9XtWs+RS48yh4p2TQtHIrT77v1I2UCknQD6kqiZXj/gsrnY1hwP68AWhcUAmx3
+                          VuNXfsgJ92kl7OH3gtvsTuTsFI11xD0oXUWRPXH70MEweB5e8FtuLeDwh741o3vZ
+                          mpmp1E62B4ItvozpOXVAD5ehvxeg/TU6jDp6LASC4TZzL5T4n+6btkwly18+kwvf
+                          ivDb+tbDN3GvyuK0wStWGqC/BKyB/jU7Z5qPRCZhAoIBABUVoNgt4mo+uwvZyWl7
+                          x+gk0zDnOzvKuOuu+0JovM7F6/NuEiXs652mpdd2ePMzwRjmR7JRyF8AOM+Xhw1g
+                          0SHuiR/WOX6KX/TNXrwegaiK895BVLMHyLNPYipRFg3Jf8RM5/KFdo44tHvlQqlE
+                          74pm0BoRuxn6oV3xFiItc2xR6Q37dK0caP6kzv1UCd5ao9Ks8ypf7WUNlYtxPgnL
+                          +hGOXxWj4Q7j+E3MKw2B5dyEPIkF/5hfmGalG4+69eqVC3fyB8RA0AGiXvC2drgr
+                          0E6FmZ66phz1NtXN/JTBDlGt41doI5TppYI+t11UeU9vbRrQs4IVeok0bYo8LRZj
+                          GdkCggEAf5myYEBvr8tuhE100Tau5RoKLS0SoKv7qsDpmFyUiNBmQggecB+X+ck4
+                          3/IToBmZRnzu+Fubqt1yMEKv+SO4F3s1W5sLdQsSAzPcum0fSEf94wONZXGkhntB
+                          0rsHnPTd3APbi1c+ZFQ+DV/Krx5yYGW1J8BBQLvFFuvJ8rK4Q+WCejGI8QCfrVU0
+                          95ctKugep8MTzm2xIecqKQOqx7UY9zWil/o4p9HEyzqzkG05+RQPLoAW9kmtcZZt
+                          HXvIdwLsUASyKB/LlM6dESXr9K4r/im3sn+9oza9mbwsRnBIJ9YU9llTw9j5aUlw
+                          Xwjk99gnvag+HswBDB7A7DCTEqhkUQ==
+                          -----END PRIVATE KEY-----
+            """
+            }
+          }
+          stage('Generate configuration') {
+            steps {
+               if (params.INSTALLATION_METTHOD == 'SNAP') {
+                 gen_config_cmd = "${gen_config_cmd} --snap --snap-channel ${params.MAAS_SNAP_CHANNEL}"
+                 if (params.TEST_DB_SNAP_CHANNEL) {
+                   gen_config_cmd ="${gen_config_cmd} --test-db-channel ${params.TEST_DB_SNAP_CHANNEL}"
+                 }
+               } else {
+                 gen_config_cmd = "${gen_config_cmd} --deb --git-repo ${params.MAAS_GIT_REPO} --git-branch ${params.MAAS_GIT_BRANCH} --ppa ${params.MAAS_PPA}"
+               }
+               if (params.ENABLE_TLS) {
+                 gen_config_cmd = "${gen_config_cmd} --tls"
+               }
+               if (params.ENABLE_OBSERVABILITY) {
+                 gen_config_cmd = "${gen_config_cmd} --o11y"
+               }
+               if (params.ENABLE_HW_SYNC_TEST) {
+                 gen_config_cmd = "${gen_config_cmd} --hw-sync"
+               }
+               if (params.GEN_CONFIG_ARGS) {
+                 gen_config_cmd = "${gen_config_cmd} ${params.GEN_CONFIG_ARGS}"
+               }
+
+               sh """
+                 ${gen_config_cmd} --containers-image=${params.CONTAINERS_IMAGE} base_config.yaml config.yaml
+                 """
+            }
+          }
+          stage('System Tests') {
+            environment {
+              COG = sh (returnStdout: true, script:"${cd_tox} -e cog").trim()
+              ENVS1 = sh (returnStdout: true, script: "${cd_tox} -q -e filter_envs vm1,natasha | head -1").trim()
+              ENVS2 = sh (returnStdout: true, script: "${cd_tox} -q -e filter_envs opelt,arm64,ppc64le,vm2 | head -1").trim()
+            }
+
+            steps {
+                sh """
+                cd system-tests
+                cat config.yaml
+                tox -e env_builder,general_tests -- ${params.PYTEST_ARGS}
+                """
+                if (ENVS1) {
+                sh """
+                    ${cd_tox} -p all -e ${ENVS1} -- ${params.PYTEST_ARGS}
+                """
+                }
+                if (ENVS2) {
+                sh """
+                    ${cd_tox} -p all -e ${ENVS2} -- ${params.PYTEST_ARGS}
+                """
+                }
+            }
+          }
+        }
+        post {
+          always {
+            sh """
+              ${cd_tox} -e collect_sos_report
+            """
+            archiveArtifacts artifacts: 'system-tests/sosreport/*,system-tests/*.log,system-tests/config.yaml,system-tests/junit*.xml', fingerprint: true
+            junit allowEmptyResults: true, testResults: 'system-tests/junit*.xml'
+            sh """
+               lxc delete --force maas-system-build || true
+               lxc delete --force maas-system-maas || true
+               lxc delete --force maas-client || true
+            """
+          }
+        }
+      }
diff --git a/jenkins/jobs/update b/jenkins/jobs/update
new file mode 100755
index 0000000..d286ac6
--- /dev/null
+++ b/jenkins/jobs/update
@@ -0,0 +1,10 @@
+#!/bin/bash -e
+case $1 in
+    orobas)
+	config="orobas.ini"
+	;;
+    *)
+	config="config.ini"
+	;;
+esac
+jenkins-jobs --conf $config --ignore-cache update .
diff --git a/maas-ci-common b/maas-ci-common
new file mode 100644
index 0000000..1023ead
--- /dev/null
+++ b/maas-ci-common
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Common environment variables used to configure jenkins and jenkins-slave for
+# MAAS CI.
+
+AUTOPKGTEST_ROOT=${JENKINS_HOME}/autopkgtest
+MAAS_CI_ROOT=${JENKINS_HOME}/maas-ci
+ADT_IMAGES_ROOT=${JENKINS_HOME}/images
diff --git a/prometheus-setup.md b/prometheus-setup.md
new file mode 100644
index 0000000..cce73af
--- /dev/null
+++ b/prometheus-setup.md
@@ -0,0 +1,126 @@
+# Configuring Prometheus and Grafana for collecting MAAS CI runs metrics
+
+The following should be run from the `jenkins-slave-2` CI machine.
+
+## Setting up the container
+
+- Create a container
+
+  ```
+  lxc init ubuntu-daily:b maas-performance
+  lxc edit maas-performance
+  ```
+
+- Set the following in the `devices` section:
+
+  ```
+  devices:
+    eth0:
+      nictype: bridged
+      parent: br0
+      type: nic
+  ```
+  
+  to have the container access the host bridge.  Then start the container and
+  connect to it:
+
+  ```
+  lxc start maas-performance
+  lxc shell maas-performance
+  ```
+  
+- Create a `/etc/netplan/eth0.yaml` file to configure the static interface,
+  with the following content:
+
+  ```
+  network:
+    version: 2
+    ethernets:
+      eth0:
+        dhcp4: false
+        addresses: [10.245.136.203/21]
+        gateway4: 10.245.136.1
+        nameservers:
+          addresses: [10.245.136.1]
+  ```
+  
+  and run `netplan apply`.  The specified IP address is reserved in the CI
+  config so that the deployed MAAS won't use it.
+
+- Edit `/etc/environment` and add the following:
+
+  ```
+  http_proxy=http://squid.internal:3128
+  https_proxy=http://squid.internal:3128
+  no_proxy="10.157.204.9,10.157.204.52,10.157.204.227,10.157.204.239,10.245.136.6"
+  ```
+
+
+## Installing and configuring Prometheus and Grafana
+
+- Install the snaps:
+
+  ```
+  snap install prometheus
+  snap install promreg
+  snap install grafana --edge
+  snap connect prometheus:content promreg:content
+  ```
+
+- Edit `/var/snap/promreg/common/promreg.yaml` changing `AuthToken: promreg`
+  and `ListenAddress: 0.0.0.0`.
+  
+- Run `snap restart promreg`.
+
+- Edit `/var/snap/prometheus/current/prometheus.yml` and add the following job
+  definition:
+  
+  ```
+  - job_name: 'prometheus-registration'
+    file_sd_configs:
+      - files:
+        - /var/snap/prometheus/current/promreg/targets.yaml
+  
+  - job_name: 'maas'
+    static_configs:
+      - targets:
+        - 10.245.136.6:5239
+        - 10.245.136.6:5249
+        
+  - job_name: 'maas-stats'
+    metrics_path: /MAAS/metrics
+    static_configs:
+      - targets:
+        - 10.245.136.6:5240
+  ```
+
+- Edit `/var/snap/prometheus/current/daemon_arguments` and set `ARGS` to the following:
+
+  ```
+  ARGS="--web.listen-address=:9093"
+  ```
+  
+- Run `snap restart prometheus`. Make sure it's working via `curl http://localhost:9093`
+
+- Create `/var/snap/grafana/current/conf/grafana.ini` with the following content:
+
+  ```
+  [server]
+  http_port = 3003
+
+  [sercurity]
+  admin_user = admin
+  ```
+
+- Run `snap restart grafana`. Make sure it's working via `curl http://localhost:3003`
+
+
+## Setting up the dashboards
+
+- Log in to Grafana with `admin/admin` credentials.
+- Add a Prometheus source with URL `http://localhost:9093`.
+- Import the dashboards from the [MAAS performance][maas-performance]
+  repo. They're located under `grafana/dashboards`.
+
+
+[maas-performance]: https://code.launchpad.net/~maas-committers/maas/+git/maas-performance
diff --git a/rebuild-ci.sh b/rebuild-ci.sh
new file mode 100755
index 0000000..428e350
--- /dev/null
+++ b/rebuild-ci.sh
@@ -0,0 +1,210 @@
+#!/bin/bash
+
+PROFILE="jenkins"
+CONTAINER="jenkins"
+PROXY="http://squid.internal:3128";
+# config for orobas. Temporary
+#IP_CIDR="10.246.88.13/22"
+#GATEWAY="10.246.88.1"
+#DNS="10.246.88.1"
+#LXD_STORAGE_POOL="default"
+#LXD_BRIDGE="br0"
+IP_CIDR="10.245.136.4/21"
+GATEWAY="10.245.136.1"
+DNS="10.245.136.1"
+LXD_STORAGE_POOL="sdb"
+LXD_BRIDGE="br0"
+
+function info (){
+    echo "INFO: $1"
+}
+
+function configure_lxd_profile() {
+    # define
+    # TODO:
+    # - we could create a storage pool on another disk to host the containers.
+    # - lxc storage create sdb zfs source=/dev/sdb
+    # create profile
+    lxc profile create $PROFILE
+    # configure profile
+    cat << EOF | lxc profile edit $PROFILE
+    config: {}
+    description: Profile for Jenkins Master
+    devices:
+      eth0:
+        name: eth0
+        nictype: bridged
+        parent: "$LXD_BRIDGE"
+        type: nic
+      root:
+        path: /
+        pool: $LXD_STORAGE_POOL
+        type: disk
+    name: $PROFILE
+    config:
+      environment.http_proxy: "$PROXY"
+      environment.https_proxy: "$PROXY"
+      user.vendor-data: |
+        #cloud-config
+        apt:
+          proxy: "$PROXY"
+        apt_proxy: "$PROXY"
+EOF
+}
+
+function configure_lxd_container() {
+    info "Configuring $CONTAINER"
+    lxc launch --profile $PROFILE ubuntu-daily:bionic $CONTAINER
+    sleep 2
+
+    info "configuring networking"
+    lxc exec $CONTAINER -- /bin/bash -c "cat <<EOF > /etc/netplan/50-cloud-init.yaml
+# Not really setup by cloud-init
+network:
+    version: 2
+    ethernets:
+       eth0:
+           addresses:
+               - $IP_CIDR
+           gateway4: $GATEWAY
+           nameservers:
+               addresses:
+                   - $DNS
+EOF"
+    lxc exec $CONTAINER -- netplan apply
+}
+
+function configure_lxd_container_jenkins() {
+    info "configuring & installing jenkins"
+    lxc exec $CONTAINER -- /bin/bash -c "wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -"
+    lxc exec $CONTAINER -- /bin/bash -c "echo 'deb https://pkg.jenkins.io/debian binary/' | tee -a /etc/apt/sources.list.d/jenkins.list"
+    lxc exec $CONTAINER -- apt-get update
+    lxc exec $CONTAINER -- apt-get install openjdk-8-jdk -y
+    lxc exec $CONTAINER -- apt-get install jenkins -y
+}
+
+function configure_slave_maas_lander() {
+    info "Configure slave to access LP as 'maas-lander'"
+    mkdir -p ~/.ssh/
+    cat << EOF > ~/.ssh/id_rsa-maas-lander
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA145HSiczzCZ42x8DMrjcNLyu2ViD5b9Bb6KXC4D3CTZTXfUl
+xEv92o976xvANObajsdUHHByyG26hy09e1AYq/o1aDvsKsZlgxlIVowo3qFkrxlY
+Mv8T3YBPLJxsLItw3yZ3qUFc+kiPjL1VsJZ8nWYEf6CLs0zHpnVKXVOoO1618GQr
+XgT4VaO7le1KaK/cy09haGDnUFSoceYYKIGZvhb9xUJLptQZE6NByjPIQhT75ODI
+YRvNF8x5iYIl9j4BtByPdKqudpr6/g7mqmIrpLHMakl5+XC6blDsnZ8D2dN3alkv
+kukfUKgJe9YX248hsJEnRpCn/hRoRfsMgiJePQIDAQABAoIBAHu6bW1BB1hdlO2h
+5YIN3khfLbYQOtV7bKIZn724rxQYnM3H03/TZsk3pxeS/EbhY/6kp8ETQq+NXI4P
+B8Vqel7s5g8Ipkz/SHFx57XWCfeQiGyBjcDn7Y0tA0VGHEmNWKKeP/1lSWtYjx1n
+6HHOT4VBc7+WddnbbpY7qQ3c4SoB6wrXii6MvtLojqYvOVAbaQyQJJyRapz5S6zK
+nOk3bfxrHQvt+ZQAcqDlQ9e2xBchYDQVeBNp8tR/b8xuEPzUUfewJLINgWKjJiW9
+mdSgPsu2GncrBpznpf84pW+X1aIvSYb092+UFm2a95S8bNkKU8Tu2HZBlUbea8PH
+o2jJlXkCgYEA+p8ScYgsdGHw9fLI5AqyfxIpdCaDjRBZQhyGoNQw6n/Q0s5QPoIy
+aE5XVWcUcRpp65vVz3QbwxDEc/N5R+vdxds0P3ylh7+ojamJTu6DOrBpOKL07ule
+vCcT4YfO51iCqshUtc9g+cRXZs0R22nr01PD8c7ALI+0uia68z0lgBsCgYEA3C6N
+1ih+/WMhulXjy1IhSjuxJ33mgR5uyQJR1Jb2qA7VgPLeeBuz85bRQq9L2R9UH84S
+/OTf4DoAOJJqSB1eBgELSeyna2RsP9Z6BtW69YAcmbs75WVrhFzxXUqBERlylSQk
+6CQrbo6UFMEdDChPZE7D1djC/1BZ3ToGvGbwcIcCgYEApV/D/tB5bQ6XvuNGtlts
+rzbfzboAQlwQWfSSzZtW79YJiKEFCEpHUtanAD61mXBxhjvdWTfnfc8Zot19IP09
+6OodTutEGxBX+6psZX4zb82qkOnOr7ukfIlYsBA6ciPQjTsF0raV6hoqBja5WsxJ
+BbiXan8gBgeJnPAjmo+1qBMCgYA8obk/MxGdNzIvfL1o3On75ionhNz3y2iYg8IC
+97telg8nHjoy+vX36x0e7uTFGoJw66+A4onf1jj/WxpXV3bv0lPIfJmx0gqZHbem
+sC52slut3chlqCMOZQW2OfEGw2oxNa3QGz22iR6wBGm6UlNifOoitjkkU30blYIL
+WZF2ewKBgQDRKCcar4lxSw1uq3xXrXk5c9BGrcq5/8/Fg1qGEMLsB/Q+i6GiSCcv
+gyXemK+Daiuku6l8g/IXJKoDhX4gxuzZCtYgA6Q3AKV+RSESw3So85wjZUCpfTzj
+WbBVbnDC4rgeyR82y2AagpvSGAERE6IjJ90QXDwxl6lFhS8TwactjQ==
+-----END RSA PRIVATE KEY-----
+EOF
+    chmod 0400 ~/.ssh/id_rsa-maas-lander
+
+    cat << EOF > ~/.ssh/config
+Host bazaar.launchpad.net
+    IdentityFile /home/ubuntu/.ssh/id_rsa-maas-lander
+        User maas-lander
+EOF
+
+    mkdir -p ~/.bazaar
+    cat << EOF > ~/.bazaar/authentication.conf
+[Launchpad]
+host = .launchpad.net
+scheme = ssh
+user = maas-lander
+EOF
+
+    cat << EOF > ~/.bazaar/bazaar.conf
+[DEFAULT]
+launchpad_username = maas-lander
+EOF
+}
+
+function configure_slave_kvm() {
+    sudo apt-get install qemu-kvm libvirt-bin bridge-utils openjdk-8-jre-headless -y
+    mkdir -p /etc/qemu
+    sudo bash -c 'echo "allow br0" >> /etc/qemu/bridge.conf'
+    sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper
+    sudo ln -sf /usr/lib/qemu/qemu-bridge-helper /usr/lib/qemu-bridge-helper
+}
+
+function configure_slave_download_autopkgtest() {
+    cd ~/
+    git clone https://anonscm.debian.org/git/autopkgtest/autopkgtest.git
+}
+
+function configure_slave_images() {
+    bzr branch lp:~maas-maintainers/maas/maas-ci-config
+    mkdir -p ~/images/
+    ./autopkgtest/tools/autopkgtest-buildvm-ubuntu-cloud \
+	    -v -r bionic -s 20G -a amd64 \
+	    -o ./images/ -p $PROXY \
+	    --metadata ~/maas-ci-config/etc/meta-data-orobas
+
+}
+
+function configure_lxd_container_candid() {
+    info "Configuring and installing Candid"
+    lxc exec $CONTAINER -- git clone http://git.launchpad.net/maas
+    lxc exec $CONTAINER -- /bin/bash -c "echo $CONTAINER.maasci > /etc/hostname"
+    lxc exec $CONTAINER -- /bin/bash -c "echo $IP $CONTAINER.maasci $CONTAINER >> /etc/hosts"
+    lxc exec $CONTAINER -- cp ./maas/utilities/ldap-setup.sh ./
+    lxc exec $CONTAINER -- cp ./maas/utilities/candid-setup.sh ./
+    lxc exec $CONTAINER -- /bin/bash ./ldap-setup.sh
+    lxc exec $CONTAINER -- /bin/bash -c "no_proxy=$CONTAINER.maasci ./candid-setup.sh"
+}
+
+case "$1" in
+    master)
+        configure_lxd_profile
+        configure_lxd_container
+        configure_lxd_container_jenkins
+        info "Jenkins is installed. Now configure jenkins"
+        info "  - use proxy: $PROXY"
+	info "  - Install extra plugins: Git, Global Variable String Parameter, Rebuilder, Build with parameters, IRC"
+	info "  - Add a slave node"
+	;;
+    slave)
+        configure_slave_maas_lander
+        configure_slave_kvm
+        configure_slave_download_autopkgtest
+        configure_slave_images
+	;;
+    candid)
+        CONTAINER="candid"
+        IP_CIDR="10.245.136.5/21"
+        IP="10.245.136.5"
+        configure_lxd_container
+        configure_lxd_container_candid
+        info "###### Please do the following items manually ######"
+        info "# There are some steps that need to be done manually so that the"
+        info "# MAAS istance can actually authenticate via candid:"
+        info "#   - Branch lp:~maas-maintainers/maas/qa-lab-tests"
+        info "#   - Update config.py:CANDID_MAAS_AGENT with candid's maas.agent"
+        info "#   - Update config.py:CANDID_CA_CERT with candid's ~/cert/ca.crt"
+        info "# Once updated, please commit changes and submit them."
+	;;
+    *)
+        info "###### No argument passed. Please pass:"
+        info "# candid - to re-install candid container"
+        info "# master - to re-install jenkins master inside a container"
+        info "# slave - to configure the host as a slave"
+    # TODO: Create jobs
+esac
diff --git a/ssh/config b/ssh/config
new file mode 100644
index 0000000..6ddd9e5
--- /dev/null
+++ b/ssh/config
@@ -0,0 +1,30 @@
+# Replace $HOME with the Jenkins home directory as SSH config
+# does not support environment variables
+Host localhost
+    IdentityFile $HOME/.ssh/id_rsa.pub
+    CheckHostIP no
+    UserKnownHostsFile /dev/null
+    StrictHostKeyChecking no
+    User ubuntu
+
+Host bazaar.launchpad.net
+    IdentityFile $HOME/.ssh/id_rsa_maas-lander
+    User maas-lander
+
+Host region
+    HostName localhost
+    IdentityFile /var/tmp/adt/disks/adtkey
+    CheckHostIP no
+    UserKnownHostsFile /dev/null
+    StrictHostKeyChecking no
+    Port 54323
+    User ubuntu
+
+Host cluster
+    HostName localhost
+    IdentityFile /var/tmp/adt/disks/adtkey
+    CheckHostIP no
+    UserKnownHostsFile /dev/null
+    StrictHostKeyChecking no
+    Port 54324
+    User ubuntu