diff options
36 files changed, 2808 insertions, 665 deletions
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/new-kernel-branch.md b/.github/ISSUE_TEMPLATE/new-kernel-branch.md new file mode 100644 index 0000000..7e91170 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-kernel-branch.md @@ -0,0 +1,57 @@ +--- +name: New kernel branch +about: Add a kernel git branch to kernelci.org +title: Add branch BRANCH from TREE +labels: '' +assignees: '' + +--- + +Each git kernel branch is monitored every hour by kernelci.org. Whenever a new +revision is detected, it will be built for a number of combinations of +architectures, defconfigs and compilers. Then a build report will be sent, +some tests will be run and test reports will also be sent. + +Please provide the information described below in order to add a new branch to +kernelci.org: + +- **How much build coverage do you need for your branch?** + +Generally speaking, a good rule is to build fewer variants for branches that +are "further" away from mainline and closer to individual developers. This can +be fine-tuned with arbitrary filters, but essentially there are 3 main options: + +1. Build everything, including allmodconfig, which amounts to about 220 builds. +This is we do with linux-next. + +2. Skip a few things such as allmodconfig as it's very long to build and +doesn't really boot, and also architectures that are less useful such as MIPS +which saves 80 builds and doesn't have much test platforms in KernelCI. This +is we do with some subsystems such as linux-media. + +3. Build only the main defconfig for each architecture to save a lot of build +power, get the fastest results and highest boots/builds ratio. This is what do +with some maintainer branches such as linusw' GPIO branch. + +⇨ Choice: + +- **How often do you expect this branch to be updated?** + +If you push once a week or less, it's easier to allow for many build variants +as this reduces the build load on average. Conversely, if you push several +times every day then a small set of builds should be used. + +It's also possible to increase the build capacity if needed but this comes with +a cost. Avoiding unnecessary builds is always a good way to reduce turnaround +time and not waste resources. + +⇨ Estimated frequency: + +- **Who should the email reports be sent to?** + +Standard email reports inlude builds and basic tests that are run on all +platforms. Please provide a list of email recipients for these. Typical ones +are the regular KernelCI reports list, kernel mailing lists associated with the +changes going into the branch and related maintainers. + +⇨ Recipients: diff --git a/.github/ISSUE_TEMPLATE/new-kernelci-api-tokens.md b/.github/ISSUE_TEMPLATE/new-kernelci-api-tokens.md new file mode 100644 index 0000000..f9316e2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-kernelci-api-tokens.md @@ -0,0 +1,44 @@ +--- +name: New KernelCI API tokens +about: Request to get some KernelCI API tokens +title: Request API tokens for USER +labels: '' +assignees: '' + +--- + +KernelCI uses kernelci-backend to manage its database which contains all the data about builds and tests. There are 2 main instances, a production one for kernelci.org and a test one for staging.kernelci.org. Separate tokens can be provided for either or both, with several permissions to choose from. KernelCI labs will also typically need a token to be able to push their test results. + +Please answer the questions below to request some API tokens: + +**Contact details** + +⇨ User name: + +⇨ Email address: + +If this is for a lab token: + +⇨ Lab owner first and last names: + +⇨ Lab name: + +**Production** + +The production instance is the one behind `https://kernelci.org`. Production tokens are only provided for labs that are able to send useful data, or with read-only permissions to create dashboards or consume the results data in any way (stats, reports...). Uses of the kernelci.org production data should ideally be made public. + +The URL of the production API server is `https://api.kernelci.org`. + +Do you need a token to access the production API? If so, is this to be able to read the data or also send some test results from a lab? + +⇨ Read-only or also to to push results: + +**Staging** + +The URL of the staging API server is `https://staging-api.kernelci.org`. + +The staging instance is used for experimental features without impacting the production instance. This is useful for anything new that needs to be tested in a full KernelCI environment with results publicly available on `https://staging.kernelci.org` but not sent to regular mailing lists. + +Do you need a token to access the staging API? If so, is this to be able to read the data or send some test results from a lab? + +⇨ Read-only or also to push results: diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..31308fb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +dist: xenial + +language: python + +python: + - "2.7" + +install: + - pip install -r requirements.txt + - pip install pycodestyle + +script: + - make test + - pycodestyle kernelci + - pycodestyle kci_* diff --git a/build-configs.yaml b/build-configs.yaml index c41cf23..75b70dc 100644 --- a/build-configs.yaml +++ b/build-configs.yaml @@ -19,6 +19,9 @@ trees: ardb: url: "git://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git" + arm64: + url: "git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git" + arnd: url: "https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git" @@ -58,6 +61,9 @@ trees: krzysztof: url: "https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux.git" + lee: + url: "https://git.kernel.org/pub/scm/linux/kernel/git/lee/linux.git" + linaro-android: url: "https://android-git.linaro.org/git/kernel/linaro-android.git" @@ -100,6 +106,9 @@ trees: samsung: url: "https://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git" + sashal: + url: "https://git.kernel.org/pub/scm/linux/kernel/git/sashal/linux-stable.git" + soc: url: "https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git" @@ -112,6 +121,9 @@ trees: tegra: url: "https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git" + thermal: + url: "https://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux.git" + ulfh: url: "https://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git" @@ -149,7 +161,7 @@ fragments: - 'CONFIG_VIDEO_VIVID_MAX_DEVS=64' x86_kvm_guest: - path: "arch/x86/configs/kvm_guest.config" + path: "kernel/configs/kvm_guest.config" build_environments: @@ -196,64 +208,67 @@ build_environments: # Default config with full build coverage build_configs_defaults: - - gcc-8: - - build_environment: gcc-8 - - fragments: &default_fragments - - 'debug' - - 'kselftest' - - 'tinyconfig' - - architectures: &default_architectures - - arc: &arc_arch - base_defconfig: 'nsim_hs_defconfig' - extra_configs: ['allnoconfig'] - filters: &arc_default_filters - # remove any non-ARCv2 defconfigs since we only have ARCv2 toolchain - - blacklist: - defconfig: - - 'axs101_defconfig' - - 'nps_defconfig' - - 'nsim_700_defconfig' - - 'nsimosci_defconfig' - - 'tb10x_defconfig' - - arm: &arm_arch - base_defconfig: 'multi_v7_defconfig' - extra_configs: - - 'allnoconfig' - - 'multi_v7_defconfig+CONFIG_CPU_BIG_ENDIAN=y' - - 'multi_v7_defconfig+CONFIG_SMP=n' - - 'multi_v7_defconfig+CONFIG_EFI=y+CONFIG_ARM_LPAE=y' - - arm64: &arm64_arch - extra_configs: - - 'allmodconfig' - - 'allnoconfig' - - 'defconfig+CONFIG_CPU_BIG_ENDIAN=y' - - 'defconfig+CONFIG_RANDOMIZE_BASE=y' - - i386: &i386_arch - base_defconfig: 'i386_defconfig' - extra_configs: ['allnoconfig'] - - mips: &mips_arch - base_defconfig: '32r2el_defconfig' - extra_configs: ['allnoconfig'] - filters: &mips_default_filters - - blacklist: {defconfig: ['generic_defconfig']} - - riscv: &riscv_arch - extra_configs: ['allnoconfig'] - - x86_64: &x86_64_arch - base_defconfig: 'x86_64_defconfig' - extra_configs: ['allmodconfig', 'allnoconfig'] - fragments: [x86_kvm_guest] - + variants: + gcc-8: + build_environment: gcc-8 + + fragments: &default_fragments + - 'debug' + - 'kselftest' + - 'tinyconfig' + + architectures: &default_architectures + + arc: &arc_arch + base_defconfig: 'nsim_hs_defconfig' + extra_configs: ['allnoconfig'] + filters: &arc_default_filters + # remove any non-ARCv2 defconfigs since we only have ARCv2 toolchain + - blacklist: + defconfig: + - 'axs101_defconfig' + - 'nps_defconfig' + - 'nsim_700_defconfig' + - 'nsimosci_defconfig' + - 'tb10x_defconfig' + + arm: &arm_arch + base_defconfig: 'multi_v7_defconfig' + extra_configs: + - 'allmodconfig' + - 'allnoconfig' + - 'multi_v7_defconfig+CONFIG_CPU_BIG_ENDIAN=y' + - 'multi_v7_defconfig+CONFIG_SMP=n' + - 'multi_v7_defconfig+CONFIG_EFI=y+CONFIG_ARM_LPAE=y' + + arm64: &arm64_arch + extra_configs: + - 'allmodconfig' + - 'allnoconfig' + - 'defconfig+CONFIG_CPU_BIG_ENDIAN=y' + - 'defconfig+CONFIG_RANDOMIZE_BASE=y' + + i386: &i386_arch + base_defconfig: 'i386_defconfig' + extra_configs: ['allnoconfig'] + + mips: &mips_arch + base_defconfig: '32r2el_defconfig' + extra_configs: ['allnoconfig'] + filters: &mips_default_filters + - blacklist: {defconfig: ['generic_defconfig']} + + riscv: &riscv_arch + extra_configs: ['allnoconfig'] + + x86_64: &x86_64_arch + base_defconfig: 'x86_64_defconfig' + extra_configs: ['allmodconfig', 'allnoconfig'] + fragments: [x86_kvm_guest] + + reference: + tree: mainline + branch: 'master' # Minimum architecture defconfigs arch_defconfigs: &arch_defconfigs @@ -283,6 +298,12 @@ arch_defconfigs: &arch_defconfigs - regex: { defconfig: 'x86_64_defconfig' } +minimal_variants: &minimal_variants + gcc-8: + build_environment: gcc-8 + architectures: *arch_defconfigs + + # Build fewer kernel configs with stable branches stable_variants: &stable_variants gcc-8: @@ -386,6 +407,10 @@ build_configs: tree: android branch: 'android-4.9-q' + android_4.9-q-release: + tree: android + branch: 'android-4.9-q-release' + android_4.14-p: tree: android branch: 'android-4.14-p' @@ -398,6 +423,10 @@ build_configs: tree: android branch: 'android-4.14-q' + android_4.14-q-release: + tree: android + branch: 'android-4.14-q-release' + android_4.14-r: tree: android branch: 'android-4.14-r' @@ -406,6 +435,10 @@ build_configs: tree: android branch: 'android-4.19-q' + android_4.19-q-release: + tree: android + branch: 'android-4.19-q-release' + android_4.19-r: tree: android branch: 'android-4.19-r' @@ -441,6 +474,19 @@ build_configs: - 'multi_v7_defconfig+CONFIG_THUMB2_KERNEL=y+CONFIG_RANDOMIZE_BASE=y' - 'omap2plus_defconfig+CONFIG_RANDOMIZE_BASE=y' + arm64: + tree: arm64 + branch: 'for-kernelci' + variants: + gcc-8: + build_environment: gcc-8 + architectures: + arm64: + base_defconfig: 'defconfig' + extra_configs: + - 'allmodconfig' + - 'allnoconfig' + arnd: tree: arnd branch: 'to-build' @@ -502,6 +548,17 @@ build_configs: tree: krzysztof branch: 'for-next' + lee_android_3.18: + tree: lee + branch: 'android-3.18-preview' + variants: + gcc-8: + build_environment: gcc-8 + architectures: + x86_64: *x86_64_arch + arm64: *arm64_arch + arm: *arm_arch + linaro-android: tree: linaro-android branch: 'linaro-android-llct' @@ -509,26 +566,17 @@ build_configs: linusw_devel: tree: linusw branch: 'devel' - variants: - gcc-8: - build_environment: gcc-8 - architectures: *arch_defconfigs + variants: *minimal_variants linusw_fixes: tree: linusw branch: 'fixes' - variants: - gcc-8: - build_environment: gcc-8 - architectures: *arch_defconfigs + variants: *minimal_variants linusw_for-next: tree: linusw branch: 'for-next' - variants: - gcc-8: - build_environment: gcc-8 - architectures: *arch_defconfigs + variants: *minimal_variants lsk_for-test: tree: lsk @@ -709,6 +757,11 @@ build_configs: tree: samsung branch: 'for-next' + sashal_stable-next: + tree: sashal + branch: 'stable-next' + variants: *stable_variants + # Disabled as the branch name contains a '/'. See discussion here: # https://github.com/kernelci/kernelci-core/issues/23 # @@ -760,50 +813,92 @@ build_configs: branch: 'linux-5.2.y' variants: *stable_variants + stable_5.3: + tree: stable + branch: 'linux-5.3.y' + variants: *stable_variants + stable-rc_3.18: tree: stable-rc branch: 'linux-3.18.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-3.18.y' stable-rc_4.4: tree: stable-rc branch: 'linux-4.4.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-4.4.y' stable-rc_4.9: tree: stable-rc branch: 'linux-4.9.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-4.9.y' stable-rc_4.14: tree: stable-rc branch: 'linux-4.14.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-4.14.y' stable-rc_4.19: tree: stable-rc branch: 'linux-4.19.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-4.19.y' stable-rc_5.0: tree: stable-rc branch: 'linux-5.0.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-5.0.y' stable-rc_5.1: tree: stable-rc branch: 'linux-5.1.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-5.1.y' stable-rc_5.2: tree: stable-rc branch: 'linux-5.2.y' variants: *stable_variants + reference: + tree: stable + branch: 'linux-5.2.y' + + stable-rc_5.3: + tree: stable-rc + branch: 'linux-5.3.y' + variants: *stable_variants + reference: + tree: stable + branch: 'linux-5.3.y' tegra: tree: tegra branch: 'for-next' + thermal: + tree: thermal + branch: 'testing' + variants: *minimal_variants + ulfh: tree: ulfh branch: 'next' @@ -811,7 +906,4 @@ build_configs: vireshk: tree: vireshk branch: 'for-kernelci' - variants: - gcc-8: - build_environment: gcc-8 - architectures: *arch_defconfigs + variants: *minimal_variants diff --git a/jenkins/bisect.jpl b/jenkins/bisect.jpl index f8eb68d..b0eae35 100644 --- a/jenkins/bisect.jpl +++ b/jenkins/bisect.jpl @@ -53,8 +53,10 @@ BUILD_ENVIRONMENT Name of the build environment LAB Name of the lab in which to run the bisection tests -PLAN (boot) - Name of the test plan +TEST_PLAN + Name of the KernelCI test plan (e.g. baseline) +TEST_PLAN_VARIANT: + Name of the KernelCI test plan variant (e.g. baseline_qemu) TEST_RUNS (1) Number of LAVA jobs to run before considering pass or fail. KCI_API_URL (https://api.kernelci.org) @@ -84,7 +86,6 @@ TREES_WHITELIST @Library('kernelci') _ -import org.kernelci.build.Kernel import org.kernelci.util.Job /* Working around some seemingly broken Python set-up... */ @@ -210,7 +211,8 @@ git symbolic-ref HEAD refs/heads/${params.KERNEL_BRANCH} def buildKernel(kdir, kci_core) { def output = "${kdir}/build-${params.ARCH}-${params.BUILD_ENVIRONMENT}" dir(kci_core) { - sh(script: "rm -f ${env._BUILD_JSON}") + sh(script: "rm -f ${env._BMETA_JSON}") + sh(script: "rm -f ${env._DTBS_JSON}") sh(script: """\ for d in \$(find ${kdir} -name "build-*" -type d); do @@ -262,14 +264,10 @@ push_kernel \ """) } - sh(script: """\ -./kci_build \ -publish_kernel \ ---kdir=${kdir} \ ---json-path=${env._BUILD_JSON} \ -""") - - stash(name: env._BUILD_JSON, includes: env._BUILD_JSON) + dir("${kdir}/_install_") { + stash(name: env._BMETA_JSON, includes: env._BMETA_JSON) + stash(name: env._DTBS_JSON, includes: env.DTBS_JSON) + } } } @@ -284,41 +282,68 @@ def buildRevision(kdir, kci_core, git_rev, name) { * kernel test with LAVA v2 */ -def submitJob(kci_core, describe, hook) { +def fetchLabInfo(kci_core) { dir(kci_core) { - sh(script: "rm -rf ${env._BUILD_JSON}; rm -rf data; mkdir data") - unstash(env._BUILD_JSON) - sh(script: """ -./lava-v2-jobs-from-api.py \ + def token = "${params.LAB}-lava-api" + + withCredentials([string(credentialsId: token, variable: 'SECRET')]) { + sh(script: """\ +./kci_test \ +get_lab_info \ --lab=${params.LAB} \ ---builds=${env._BUILD_JSON} \ ---storage=${params.KCI_STORAGE_URL} \ ---plans=${params.PLAN} \ ---jobs=data \ ---arch=${params.ARCH} \ ---tree=${params.KERNEL_TREE} \ ---describe=${describe} \ ---branch=${params.KERNEL_BRANCH} \ ---defconfig_full=${params.DEFCONFIG} \ ---priority=${params.LAVA_PRIORITY} \ ---callback=${params.LAVA_CALLBACK} \ ---callback-url=${hook.getURL()} \ ---callback-dataset=results \ ---callback-type=custom \ ---targets=${params.TARGET} +--lab-json=${env._LAB_JSON} \ +--user=kernel-ci \ +--token=${SECRET} \ """) + } + stash(name: env._LAB_JSON, includes: env._LAB_JSON) + } +} + +def submitJob(kci_core, describe, hook) { + dir(kci_core) { + sh(script: """\ +rm -f ${env._BMETA_JSON} \ +rm -f ${env._DTBS_JSON} \ +rm -f ${env._LAB_JSON} \ +rm -rf data; mkdir data \ +""") + unstash(env._BMETA_JSON) + unstash(env._DTBS_JSON) + unstash(env._LAB_JSON) def egg_cache = eggCache() def token = "${params.LAB}-lava-api" + /* ToDo: deal with params.LAVA_PRIORITY or drop it */ + withCredentials([string(credentialsId: token, variable: 'SECRET')]) { - sh(script: """ -PYTHON_EGG_CACHE=${egg_cache} \ -./lava-v2-submit-jobs.py \ ---username=kernel-ci \ + sh(script: """ \ +./kci_test \ +generate \ +--bmeta-json=${env._BMETA_JSON} \ +--dtbs-json=${env._DTBS_JSON} \ +--lab-json=${env._LAB_JSON} \ +--storage=${params.KCI_STORAGE_URL} \ +--lab=${params.LAB} \ +--user=kernel-ci \ --token=${SECRET} \ +--output=data \ +--callback-id=${params.LAVA_CALLBACK} \ +--callback-url=${hook.getURL()} \ +--callback-dataset=results \ +--callback-type=custom \ +--target=${params.TARGET} \ +--plan=${params.TEST_PLAN_VARIANT} \ +""") + + sh(script: """ \ +./kci_test \ +submit \ --lab=${params.LAB} \ ---jobs=data +--user=kernel-ci \ +--token=${SECRET} \ +--jobs=data/* \ """) } } @@ -381,7 +406,7 @@ def runTest(kci_core, describe, expected=0, runs=0) { * bisection */ -def findMergeBase(kdir, good, bad) { +def findMergeBase(kdir, kci_core, good, bad) { def base = good dir(kdir) { @@ -390,16 +415,32 @@ def findMergeBase(kdir, good, bad) { script: "git merge-base --is-ancestor ${base} HEAD") if (good_base != 0) { - def ref = "${params.REF_KERNEL_TREE}/${params.REF_KERNEL_BRANCH}" + def ref_url = params.REF_KERNEL_URL + def ref_tree = params.REF_KERNEL_TREE + def ref_branch = params.REF_KERNEL_BRANCH + + if (!(ref_url && ref_tree && ref_branch)) { + dir(kci_core) { + ref_config = sh(script: """\ + ./kci_build get_reference --tree-name ${params.KERNEL_TREE} \ + --branch ${params.KERNEL_BRANCH}""", returnStdout: true).trim().tokenize("\n") + if (ref_config.size() > 0) { + ref_url = ref_config[0] + ref_tree = ref_config[1] + ref_branch = ref_config[2] + } + } + } + def ref = "${ref_tree}/${ref_branch}" print("Good commit not in current branch, finding base in ${ref}") print("""\ Reference: - Tree: ${params.REF_KERNEL_TREE} - URL: ${params.REF_KERNEL_URL} - Branch: ${params.REF_KERNEL_BRANCH}""") + Tree: ${ref_tree} + URL: ${ref_url} + Branch: ${ref_branch}""") - setRemote(kdir, params.REF_KERNEL_TREE, params.REF_KERNEL_URL) + setRemote(kdir, ref_tree, ref_url) base = sh(script: "git merge-base ${bad} ${ref}", returnStdout: true).trim() print("Merge base: ${base}") @@ -451,7 +492,9 @@ def bisectNext(kdir, status) { */ def pushResults(kci_core, kdir, checks, params_summary) { - def subject = "${params.KERNEL_TREE}/${params.KERNEL_BRANCH} ${params.PLAN} bisection: ${params.KERNEL_NAME} on ${params.TARGET}" + def subject = "\ +${params.KERNEL_TREE}/${params.KERNEL_BRANCH} bisection: \ +${params.TEST_PLAN} on ${params.TARGET}" dir(kci_core) { withCredentials([string(credentialsId: params.KCI_TOKEN_ID, @@ -536,10 +579,11 @@ def bisection(kci_core, kdir, checks) { kci_core: { cloneKciCore(kci_core) }, kdir: { cloneLinux(kdir) }, ) + fetchLabInfo(kci_core) } bad = params.BAD_COMMIT - good = findMergeBase(kdir, params.GOOD_COMMIT, bad) + good = findMergeBase(kdir, kci_core, params.GOOD_COMMIT, bad) } stage("Check pass") { @@ -631,7 +675,9 @@ def bisection(kci_core, kdir, checks) { node("docker && bisection") { /* Global pipeline constants */ - env._BUILD_JSON = "build-data.json" + env._BMETA_JSON = "bmeta.json" + env._DTBS_JSON = "dtbs.json" + env._LAB_JSON = "lab-info.json" def j = new Job() def kci_core = "${env.WORKSPACE}/kernelci-core" @@ -648,14 +694,15 @@ node("docker && bisection") { Lab: ${params.LAB} Defconfig: ${params.DEFCONFIG} Compiler: ${params.BUILD_ENVIRONMENT} - Plan: ${params.PLAN}""" + Plan: ${params.TEST_PLAN} + Variant: ${params.TEST_PLAN_VARIANT}""" print("""\ Good: ${params.GOOD_COMMIT} Bad: ${params.BAD_COMMIT} ${params_summary}""") - if ((params.PLAN != 'boot') && (params.PLAN != 'simple')) { - echo "Only doing boot and simple plans for now, aborting." + if (params.TEST_PLAN != 'boot') { + echo "Only doing boot bisections for now, aborting." currentBuild.result = 'ABORTED' return } diff --git a/jenkins/build-trigger.jpl b/jenkins/build-trigger.jpl index 9e23b6f..20e159d 100644 --- a/jenkins/build-trigger.jpl +++ b/jenkins/build-trigger.jpl @@ -28,6 +28,8 @@ PUBLISH (boolean) Publish build results via the KernelCI backend API EMAIL (boolean) Send build results via email +LABS_WHITELIST + List of labs to include in the tests, all labs will be tested by default. KCI_API_URL (https://api.kernelci.org) URL of the KernelCI backend API KCI_TOKEN_ID @@ -45,7 +47,6 @@ ALLOW_REBUILD (false) */ @Library('kernelci') _ -import org.kernelci.build.Kernel import org.kernelci.util.Job def configAlreadyBuilt(config, kci_core) { @@ -53,7 +54,7 @@ def configAlreadyBuilt(config, kci_core) { dir(kci_core) { new_commit = sh( - script: """ + script: """\ ./kci_build \ check_new_commit \ --config=${config} \ @@ -73,13 +74,23 @@ update_mirror \ --mirror=${mirror} \ """) - sh(script: """\ + while (true) { + try { + sh(script: """\ ./kci_build \ update_repo \ --config=${config} \ --kdir=${kdir} \ --mirror=${mirror} \ """) + break + } catch (error) { + print("Failed to update repo: ${error}") + print("Removing clone and retrying") + sh(script: "rm -rf ${kdir}") + sleep 1 + } + } def describe_raw = sh(script: """\ ./kci_build \ @@ -143,21 +154,10 @@ list_kernel_configs \ } } -def addBuildOpts(config, kci_core, opts) { - dir(kci_core) { - opts['config'] = config - - def opts_raw = sh( - script: """\ -./kci_build \ -tree_branch \ ---config=${config} \ -""", returnStdout: true).trim() - def opt_list = opts_raw.tokenize('\n') - opts['tree'] = opt_list[0] - opts['git_url'] = opt_list[1] - opts['branch'] = opt_list[2] +def listArchitectures(kci_core, config) { + def arch_list = [] + dir(kci_core) { def raw_variants = sh( script: """\ ./kci_build \ @@ -166,7 +166,6 @@ list_variants \ """, returnStdout: true).trim() def variants = raw_variants.tokenize('\n') - def arch_list = [] for (String variant: variants) { def raw_variant_arch_list = sh( script: """\ @@ -181,11 +180,61 @@ arch_list \ if (!arch_list.contains(arch)) arch_list.add(arch) } - opts['arch_list'] = arch_list } + + return arch_list } -def buildKernelStep(job, arch, defconfig, build_env, opts) { +def addBuildOpts(config, kci_core, opts) { + dir(kci_core) { + opts['config'] = config + + def opts_raw = sh( + script: """\ +./kci_build \ +tree_branch \ +--config=${config} \ +""", returnStdout: true).trim() + def opt_list = opts_raw.tokenize('\n') + opts['tree'] = opt_list[0] + opts['git_url'] = opt_list[1] + opts['branch'] = opt_list[2] + } +} + +def scheduleTests(build_job_name, build_job_number, labs, kci_core) { + dir(kci_core) { + def labs_str = "" + for (lab in labs) + labs_str += "${lab} " + + def str_params = [ + 'LABS': labs_str.trim(), + 'TRIGGER_JOB_NAME': env.JOB_NAME, + 'TRIGGER_JOB_NUMBER': env.BUILD_NUMBER, + 'BUILD_JOB_NAME': build_job_name, + 'BUILD_JOB_NUMBER': "${build_job_number}", + ] + def params = [] + + def j = new Job() + j.addStrParams(params, str_params) + build(job: 'test-runner', parameters: params, propagate: false) + } +} + +def buildKernelStep(job, arch, defconfig, build_env, opts, labs, kci_core) { + def node_label = "builder" + def parallel_builds = "4" + + if (defconfig.matches(".*allmodconfig.*")) { + node_label = "big-config-builder" + parallel_builds = "" + } else if (defconfig.matches("^defconfig.*") && arch == "arm64") { + node_label = "medium-config-builder" + parallel_builds = "" + } + def str_params = [ 'ARCH': arch, 'DEFCONFIG': defconfig, @@ -195,13 +244,20 @@ def buildKernelStep(job, arch, defconfig, build_env, opts) { 'SRC_TARBALL': opts['tarball_url'], 'BUILD_CONFIG': opts['config'], 'BUILD_ENVIRONMENT': build_env, + 'NODE_LABEL': node_label, + 'PARALLEL_BUILDS': parallel_builds, ] def job_params = [] def j = new Job() j.addStrParams(job_params, str_params) - return { build(job: job, parameters: job_params, propagate: false) } + return { + def res = build(job: job, parameters: job_params, propagate: false) + print("${res.number}: ${arch} ${defconfig} ${build_env} ${res.result}") + if (res.result == "SUCCESS") + scheduleTests(job, res.number, labs, kci_core) + } } def buildsComplete(job, opts, arch) { @@ -225,10 +281,10 @@ def buildsComplete(job, opts, arch) { node("docker && build-trigger") { def j = new Job() - def k = new Kernel() def kci_core = "${env.WORKSPACE}/kernelci-core" def kdir = "${env.WORKSPACE}/configs/${params.BUILD_CONFIG}" def mirror = "${env.WORKSPACE}/linux.git" + def labs_info = "${env.WORKSPACE}/labs" def docker_image = "${params.DOCKER_BASE}base" def opts = [:] def configs = [] @@ -238,11 +294,57 @@ node("docker && build-trigger") { Container: ${docker_image}""") j.dockerPullWithRetry(docker_image).inside() { + def labs = [] + stage("Init") { timeout(time: 15, unit: 'MINUTES') { j.cloneKciCore( kci_core, params.KCI_CORE_URL, params.KCI_CORE_BRANCH) } + + sh(script: "rm -rf ${labs_info}; mkdir -p ${labs_info}") + + dir(kci_core) { + def raw_lab_names = sh( + script: "./kci_test list_labs", returnStdout: true).trim() + def all_lab_names = raw_lab_names.tokenize('\n') + def labs_list = [] + + if (params.LABS_WHITELIST) { + def whitelist = params.LABS_WHITELIST.tokenize(' ') + + for (lab in all_lab_names) + if (whitelist.contains(lab)) + labs_list.add(lab) + } else { + labs_list = all_lab_names + } + + for (lab in labs_list) { + def lab_json = "${labs_info}/${lab}.json" + def token = "${lab}-lava-api" + try { + withCredentials([string(credentialsId: token, + variable: 'SECRET')]) { + sh(script: """\ +./kci_test \ +get_lab_info \ +--lab=${lab} \ +--lab-json=${lab_json} \ +--user=kernel-ci \ +--token=${SECRET} \ +""") + } + labs.add(lab) + } catch (error) { + print("Error with ${lab}: ${error}") + } + } + } + + dir(labs_info) { + archiveArtifacts("*.json") + } } if (params.ALLOW_REBUILD != true) { @@ -276,7 +378,8 @@ node("docker && build-trigger") { print(step_name) builds[step_name] = buildKernelStep( - "kernel-build", arch, defconfig, build_env, opts) + "kernel-build", arch, defconfig, build_env, opts, labs, + kci_core) i += 1 } @@ -286,7 +389,8 @@ node("docker && build-trigger") { stage("Complete") { /* ToDo: convert kernel-arch-complete as a stage in this job */ - for (String arch: opts['arch_list']) { + def arch_list = listArchitectures(kci_core, params.BUILD_CONFIG) + for (String arch: arch_list) { buildsComplete("kernel-arch-complete", opts, arch) } } diff --git a/jenkins/build.jpl b/jenkins/build.jpl index ed1b106..05bdeeb 100644 --- a/jenkins/build.jpl +++ b/jenkins/build.jpl @@ -38,6 +38,8 @@ COMMIT_ID Git commit SHA1 at the revision of the snapshot BUILD_ENVIRONMENT Name of the build environment +NODE_LABEL + Label to use to choose a node on which to run this job PUBLISH (boolean) Publish build results via the KernelCI backend API EMAIL (boolean) @@ -59,10 +61,11 @@ PARALLEL_BUILDS @Library('kernelci') _ -import org.kernelci.build.Kernel import org.kernelci.util.Job def buildConfig(kdir, kci_core) { + def jopt = "" + if (params.PARALLEL_BUILDS) jopt = "-j${params.PARALLEL_BUILDS}" @@ -108,11 +111,14 @@ publish_kernel \ """) } } + + dir("linux/_install_") { + archiveArtifacts("*.json") + } } -node("docker && builder") { +node("docker" && params.NODE_LABEL) { def j = new Job() - def k = new Kernel() def kci_core = "${env.WORKSPACE}/kernelci-core" def kdir = "${env.WORKSPACE}/linux" def docker_image = null @@ -135,7 +141,13 @@ node("docker && builder") { j.dockerPullWithRetry(docker_image).inside() { stage("Init") { timeout(time: 30, unit: 'MINUTES') { - k.downloadTarball(kdir, params.SRC_TARBALL) + dir(kci_core) { + sh(script: """./kci_build pull_tarball \ + --kdir ${kdir} \ + --url ${params.SRC_TARBALL} \ + --retries=12 \ + """) + } } } diff --git a/jenkins/stretch-v4l2.jpl b/jenkins/buster-v4l2.jpl index 9ef7b48..c88163b 100644 --- a/jenkins/stretch-v4l2.jpl +++ b/jenkins/buster-v4l2.jpl @@ -11,12 +11,12 @@ DOCKER_BASE def r = new RootFS() -def config = ['name':"stretch-v4l2", +def config = ['name':"buster-v4l2", 'arch_list':["armhf", "arm64", "amd64"], - 'debian_release':"stretch", + 'debian_release':"buster", 'extra_packages':"", 'extra_packages_remove':"bash e2fsprogs e2fslibs", - 'script':"scripts/stretch-v4l2.sh", + 'script':"scripts/buster-v4l2.sh", 'test_overlay': "overlays/v4l2", 'docker_image': "${params.DOCKER_BASE}debos", ] diff --git a/jenkins/debian/debos/scripts/stretch-v4l2.sh b/jenkins/debian/debos/scripts/buster-v4l2.sh index 81015d4..b971274 100755 --- a/jenkins/debian/debos/scripts/stretch-v4l2.sh +++ b/jenkins/debian/debos/scripts/buster-v4l2.sh @@ -66,7 +66,7 @@ cd /tmp rm -rf /tmp/tests apt-get remove --purge -y ${BUILD_DEPS} -apt-get remove --purge -y perl-modules-5.24 +apt-get remove --purge -y perl-modules-5.28 apt-get autoremove --purge -y apt-get clean diff --git a/jenkins/dockerfiles/debos/Dockerfile b/jenkins/dockerfiles/debos/Dockerfile index 69f7010..492ffde 100644 --- a/jenkins/dockerfiles/debos/Dockerfile +++ b/jenkins/dockerfiles/debos/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch-slim +FROM debian:buster-slim ARG DEBIAN_FRONTEND=noninteractive diff --git a/jenkins/kernel-arch-complete.sh b/jenkins/kernel-arch-complete.sh index bb0b40b..cdad9ff 100755 --- a/jenkins/kernel-arch-complete.sh +++ b/jenkins/kernel-arch-complete.sh @@ -121,8 +121,8 @@ if [[ BUILDS_FINISHED -eq 1 ]]; then curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "send_to": ["krzk@kernel.org", "kgene.kim@samsung.com", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 12600}' ${API}/send elif [ "$TREE_NAME" == "agross" ]; then echo "Sending results to Andy Gross" - curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["agross@codeaurora.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 10}' ${API}/send - curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "send_to": ["agross@codeaurora.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 12600}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["agross@kernel.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 10}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "send_to": ["agross@kernel.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 12600}' ${API}/send elif [ "$TREE_NAME" == "broonie-regmap" ]; then echo "Sending results to Mark Brown" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["broonie@kernel.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 10}' ${API}/send @@ -151,6 +151,7 @@ if [[ BUILDS_FINISHED -eq 1 ]]; then echo "Sending results to Ard Biesheuvel" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["ard.biesheuvel@linaro.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 10}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "send_to": ["ard.biesheuvel@linaro.org", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 12600}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan: "baseline-uefi", "send_to": ["ard.biesheuvel@linaro.org", "anders.roxell@linaro.org", "fellows@kernelci.org"], "format": ["txt""], "delay": 10}' ${API}/send elif [ "$TREE_NAME" == "evalenti" ]; then echo "Sending results to Eduardo Valentin" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["edubezval@gmail.com", "fellows@kernelci.org"], "format": ["txt", "html"], "delay": 10}' ${API}/send @@ -179,6 +180,9 @@ if [[ BUILDS_FINISHED -eq 1 ]]; then curl -XPOST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["kernel-team+kernelci@android.com", "gregkh@google.com", "fellows@kernelci.org"], "delay": 12600}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "send_to": ["tom.gall@linaro.org", "sumit.semwal@linaro.org", "amit.pundir@linaro.org", "arnd.bergmann@linaro.org", "anmar.oueja@linaro.org"], "format": ["txt"], "delay": 10}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "send_to": ["tom.gall@linaro.org", "sumit.semwal@linaro.org", "amit.pundir@linaro.org", "arnd.bergmann@linaro.org", "anmar.oueja@linaro.org"], "format": ["txt"], "delay": 12600}' ${API}/send + if [ "$BRANCH" == "android-3.18" ]; then + curl -XPOST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["lee.jones@linaro.org"], "delay": 60}' ${API}/send + fi elif [ "$TREE_NAME" == "mattface" ]; then echo "Sending results to Matt" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["matt@mattface.org"], "delay": 60}' ${API}/send @@ -190,7 +194,7 @@ if [[ BUILDS_FINISHED -eq 1 ]]; then curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 1800}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "v4l2-compliance-vivid", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 2700}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "v4l2-compliance-uvc", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 3600}' ${API}/send - curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "igt_drm_kms", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 3600}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "igt-drm-kms", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 3600}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "cros-ec", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 3600}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "sleep", "send_to": ["guillaume.tucker@collabora.com"], "format": ["txt"], "delay": 3600}' ${API}/send elif [ "$TREE_NAME" == "tomeu" ]; then @@ -218,14 +222,36 @@ if [[ BUILDS_FINISHED -eq 1 ]]; then echo "Sending results for vireshk's tree" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["vireshk@kernel.org"], "delay": 60}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["vireshk@kernel.org"], "delay": 2700}' ${API}/send + elif [ "$TREE_NAME" == "sashal" ]; then + echo "Sending results for sashal's tree" + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["sashal@kernel.org", "kernel-build-reports@lists.linaro.org"], "delay": 60}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["sashal@kernel.org", "kernel-build-reports@lists.linaro.org"], "delay": 2700}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["sashal@kernel.org", "kernel-build-reports@lists.linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send + elif [ "$TREE_NAME" == "thermal" ]; then + echo "Sending results for the thermal tree" + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["daniel.lezcano@linaro.org", "kernel-build-reports@lists.linaro.org"], "delay": 60}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["daniel.lezcano@linaro.org", "kernel-build-reports@lists.linaro.org"], "delay": 2700}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["daniel.lezcano@linaro.org", "kernel-build-reports@lists.linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send + elif [ "$TREE_NAME" == "arm64" ]; then + echo "Sending results for the arm64 tree" + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["will@kernel.org", "catalin.marinas@arm.com", "linux-arm-kernel@lists.infradead.org", "kernel-build-reports@lists.linaro.org"], "delay": 60}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["will@kernel.org", "catalin.marinas@arm.com", "linux-arm-kernel@lists.infradead.org", "kernel-build-reports@lists.linaro.org"], "delay": 2700}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["will@kernel.org", "catalin.marinas@arm.com", "linux-arm-kernel@lists.infradead.org", "kernel-build-reports@lists.linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send + elif [ "$TREE_NAME" == "lee" ]; then + echo "Sending results for lee's tree" + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["lee.jones@linaro org", "kernel-build-reports@lists.linaro.org"], "delay": 60}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["lee.jones@linaro org", "kernel-build-reports@lists.linaro.org"], "delay": 2700}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["lee.jones@linaro org", "kernel-build-reports@lists.linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send elif [ "$TREE_NAME" == "kernelci" ]; then echo "Sending results to kernelci folks" curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "build_report": 1, "format": ["txt"], "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "delay": 0}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "boot_report": 1, "format": ["txt"], "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "delay": 1800}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 1800}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline-uefi", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 1800}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "baseline-fastboot", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 1800}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "v4l2-compliance-vivid", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "v4l2-compliance-uvc", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send - curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "igt_drm_kms", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send + curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "igt-drm-kms", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "cros-ec", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send curl -X POST -H "Authorization: $EMAIL_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"job": "'$TREE_NAME'", "kernel": "'$GIT_DESCRIBE'", "git_branch": "'$BRANCH'", "report_type": "test", "plan": "sleep", "send_to": ["gtucker@collabora.com", "mgalka@collabora.com", "alexandra.pereira@collabora.com", "dan.rue@linaro.org", "matthew.hart@linaro.org", "broonie@kernel.org", "kernelci@baylibre.com", "anders.roxell@linaro.org"], "format": ["txt"], "delay": 2700}' ${API}/send else diff --git a/jenkins/lava-boot-v2.sh b/jenkins/lava-boot-v2.sh index f8ab5be..7434d07 100755 --- a/jenkins/lava-boot-v2.sh +++ b/jenkins/lava-boot-v2.sh @@ -18,22 +18,22 @@ if [ ${LAB} = "lab-tbaker" ] || [ ${LAB} = "lab-tbaker-dev" ]; then python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp kselftest --token ${API_TOKEN} python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_TBAKER_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-mhart" ] || [ ${LAB} = "lab-mhart-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_MHART_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-collabora" ] || [ ${LAB} = "lab-collabora-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp cros-ec igt_drm_kms kselftest simple sleep usb v4l2-compliance-vivid v4l2-compliance-uvc --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp cros-ec igt-drm-kms kselftest simple sleep usb v4l2-compliance-vivid v4l2-compliance-uvc --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_COLLABORA_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-baylibre" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi kselftest --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline baseline-fastboot boot boot-kvm boot-kvm-uefi kselftest --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_BAYLIBRE_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-baylibre-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi kselftest --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline baseline-fastboot boot boot-kvm boot-kvm-uefi kselftest --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_BAYLIBRE_TOKEN} --lab ${LAB} # for dev lab, also send results to BayLibre kernelCI development backend - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback kernelci-dev-baylibre-callback --callback-url http://kernelci.dev.baylibre.com:8081 --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans simple usb --token ${API_TOKEN} --priority low + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback kernelci-dev-baylibre-callback --callback-url http://kernelci.dev.baylibre.com:8081 --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline simple usb --token ${API_TOKEN} --priority low python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_BAYLIBRE_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-free-electrons" ] || [ ${LAB} = "lab-free-electrons-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_FREE_ELECTRONS_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-broonie" ]; then python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp --token ${API_TOKEN} @@ -42,18 +42,18 @@ elif [ ${LAB} = "lab-embeddedbits" ]; then python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp --token ${API_TOKEN} python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_EMBEDDEDBITS_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-pengutronix" ] || [ ${LAB} = "lab-pengutronix-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi --token ${API_TOKEN} + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi --token ${API_TOKEN} python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_PENGUTRONIX_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-linaro-lkft" ] || [ ${LAB} = "lab-linaro-lkft-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot uefi_arm uefi_arm64 uefi_i386 uefi_x86_64 uefi_x86-mixed --token ${API_TOKEN} --priority low + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline baseline-uefi boot --token ${API_TOKEN} --priority low python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_LINARO_LKFT_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-theobroma-systems" ] || [ ${LAB} = "lab-theobroma-systems-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot kselftest --token ${API_TOKEN} + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot kselftest --token ${API_TOKEN} python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_THEOBROMA_SYSTEMS_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-drue" ] || [ ${LAB} = "lab-drue-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple sleep --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple sleep --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_DRUE_TOKEN} --lab ${LAB} elif [ ${LAB} = "lab-clabbe" ] || [ ${LAB} = "lab-clabbe-dev" ]; then - python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple sleep --token ${API_TOKEN} --priority medium + python lava-v2-jobs-from-api.py --defconfigs ${DEFCONFIG_COUNT} --callback ${CALLBACK} --api ${API} --storage ${STORAGE} --lab ${LAB} --describe ${GIT_DESCRIBE} --tree ${TREE} --branch ${BRANCH} --arch ${ARCH} --plans baseline boot boot-kvm boot-kvm-uefi boot-nfs boot-nfs-mp simple sleep --token ${API_TOKEN} --priority medium python lava-v2-submit-jobs.py --username kernel-ci --jobs ${LAB} --token ${LAVA_CLABBE_TOKEN} --lab ${LAB} fi diff --git a/jenkins/monitor.jpl b/jenkins/monitor.jpl index f82a199..953420e 100644 --- a/jenkins/monitor.jpl +++ b/jenkins/monitor.jpl @@ -49,7 +49,7 @@ def checkConfig(config, kci_core, trees_dir) { def commit = sh( script: """ ${kci_core}/kci_build \ ---build-configs=${kci_core}/build-configs.yaml \ +--yaml-configs=${kci_core}/build-configs.yaml \ check_new_commit \ --config=${config} \ --storage=${params.KCI_STORAGE_URL} \ diff --git a/jenkins/test-runner.jpl b/jenkins/test-runner.jpl new file mode 100644 index 0000000..19daff2 --- /dev/null +++ b/jenkins/test-runner.jpl @@ -0,0 +1,178 @@ +#!/usr/bin/env groovy + +/* + Copyright (C) 2019 Collabora Limited + Author: Guillaume Tucker <guillaume.tucker@collabora.com> + + This module is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* ---------------------------------------------------------------------------- + * Jenkins parameters + +LABS + Name of the labs where to submit tests +TRIGGER_JOB_NAME + Name of the parent trigger job +TRIGGER_JOB_NUMBER + Number of the parent trigger job +BUILD_JOB_NAME + Name of the job that built the kernel +BUILD_JOB_NUMBER + Number of the job that built the kernel +KCI_CORE_URL (https://github.com/kernelci/kernelci-core.git) + URL of the kernelci-core repository +KCI_CORE_BRANCH (master) + Name of the branch to use in the kernelci-core repository +KCI_STORAGE_URL (https://storage.kernelci.org/) + URL of the KernelCI storage server +DOCKER_BASE (kernelci/build-) + Dockerhub base address used for the build images +CALLBACK_ID (kernel-ci-callback) + Identifier of the callback to look up an authentication token +CALLBACK_URL (https://api.kernelci.org) + Base URL where to send the callbacks +*/ + +@Library('kernelci') _ +import org.kernelci.util.Job + +def getArtifacts(artifacts) +{ + dir(artifacts) { + copyArtifacts( + projectName: params.BUILD_JOB_NAME, + selector: specific("${params.BUILD_JOB_NUMBER}") + ) + + if (params.TRIGGER_JOB_NAME) { + copyArtifacts( + projectName: params.TRIGGER_JOB_NAME, + selector: specific("${params.TRIGGER_JOB_NUMBER}") + ) + } + } +} + +def generateJobs(kci_core, lab, artifacts, jobs_dir) +{ + def token = "${lab}-lava-api" + + dir(kci_core) { + withCredentials([string(credentialsId: token, variable: 'SECRET')]) { + sh(script: """\ +./kci_test \ +generate \ +--bmeta-json=${artifacts}/bmeta.json \ +--dtbs-json=${artifacts}/dtbs.json \ +--lab-json=${artifacts}/${lab}.json \ +--storage=${params.KCI_STORAGE_URL} \ +--lab=${lab} \ +--user=kernel-ci \ +--token=${SECRET} \ +--output=${jobs_dir} \ +--callback-id=${params.CALLBACK_ID} \ +--callback-url=${params.CALLBACK_URL} \ +""") + } + } +} + +def submitJobs(kci_core, lab, jobs_dir) +{ + def token = "${lab}-lava-api" + + dir(kci_core) { + withCredentials([string(credentialsId: token, variable: 'SECRET')]) { + sh(script: """\ +./kci_test \ +submit \ +--lab=${lab} \ +--user=kernel-ci \ +--token=${SECRET} \ +--jobs=${jobs_dir}/* \ +""") + } + } +} + +node("docker && test-runner") { + def j = new Job() + def kci_core = "${env.WORKSPACE}/kernelci-core" + def jobs_dir = "${env.WORKSPACE}/jobs" + def artifacts = "${env.WORKSPACE}/artifacts" + def docker_image = "${params.DOCKER_BASE}base" + def labs = params.LABS.tokenize(' ') + def labs_submit = [] + + print("""\ + Labs: ${params.LABS} + Container: ${docker_image}""") + + j.dockerPullWithRetry(docker_image).inside() { + stage("Init") { + sh(script: "rm -rf ${artifacts}") + sh(script: "rm -rf ${jobs_dir}") + + timeout(time: 15, unit: 'MINUTES') { + parallel( + kci_core: { + j.cloneKciCore( + kci_core, + params.KCI_CORE_URL, params.KCI_CORE_BRANCH) + }, + artifacts: { + getArtifacts(artifacts) + }, + ) + } + + print("Artifacts:") + sh(script: "ls -l ${artifacts}") + + print("Build meta-data:") + sh(script: "cat ${artifacts}/bmeta.json") + } + + stage("Generate") { + for (lab in labs) { + def lab_dir = "${jobs_dir}/${lab}" + generateJobs(kci_core, lab, artifacts, lab_dir) + labs_submit.add(lab) + } + } + + stage("Submit") { + def steps = [:] + def i = 0 + + for (lab in labs_submit) { + def lab_name = "${lab}" + def lab_dir = "${jobs_dir}/${lab}" + def step_name = "${i} ${lab}" + + print(step_name) + + steps[step_name] = { + submitJobs(kci_core, lab_name, lab_dir) + } + + i += 1 + } + + parallel(steps) + } + } +} @@ -17,159 +17,13 @@ # along with this library; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import argparse -import os -import subprocess import sys -import time +from kernelci.cli import Args, Command, parse_args import kernelci.build import kernelci.config.build -# ----------------------------------------------------------------------------- -# Standard arguments that can be used in sub-commands -# - -class Args(object): - config= { - 'name': '--config', - 'help': "Build config name", - } - - variant = { - 'name': '--variant', - 'help': "Build config variant name", - } - - build_env = { - 'name': '--build-env', - 'help': "Build environment name", - } - - storage = { - 'name': '--storage', - 'help': "Storage URL", - 'default': "https://storage.kernelci.org", - 'required': False, - } - - api = { - 'name': '--api', - 'help': "Backend API URL", - 'default': "https://api.kernelci.org", - 'required': False, - } - - token = { - 'name': '--token', - 'help': "Backend API token", - } - - commit = { - 'name': '--commit', - 'help': "Git commit checksum", - } - - describe = { - 'name': '--describe', - 'help': "Git describe", - } - - describe_verbose = { - 'name': '--describe-verbose', - 'help': "Verbose version of git describe", - } - - tree_name = { - 'name': '--tree-name', - 'help': "Name of a kernel tree", - } - - tree_url = { - 'name': '--tree-url', - 'help': "URL of a kernel tree", - } - - branch = { - 'name': '--branch', - 'help': "Name of a kernel branch in a tree", - } - - mirror = { - 'name': '--mirror', - 'help': "Path to the local kernel git mirror", - } - - kdir = { - 'name': '--kdir', - 'help': "Path to the kernel checkout directory", - } - - arch = { - 'name': '--arch', - 'help': "CPU architecture name", - } - - defconfig = { - 'name': '--defconfig', - 'help': "Kernel defconfig name", - } - - j = { - 'name': '-j', - 'help': "Number of parallel build processes", - } - - verbose = { - 'name': '--verbose', - 'help': "Verbose output", - 'action': 'store_true', - } - - output = { - 'name': '--output', - 'help': "Path the output directory", - } - - json_path = { - 'name': '--json-path', - 'help': "Path to the JSON file", - } - - -# ----------------------------------------------------------------------------- -# Commands -# - -class Command(object): - help = None - args = None - opt_args = None - - def __init__(self, sub_parser, name): - if not self.help: - raise AttributeError("Missing help message for {}".format(name)) - self._parser = sub_parser.add_parser(name, help=self.help) - if self.args: - for arg in self.args: - self._add_arg(arg, True) - if self.opt_args: - for arg in self.opt_args: - self._add_arg(arg, False) - self._parser.set_defaults(func=self) - - def __call__(self, *args, **kw): - raise NotImplementedError("Command not implemented") - - def _add_arg(self, arg, required=True): - kw = dict(arg) - arg_name = kw.pop('name') - if required: - kw.setdefault('required', True) - self._parser.add_argument(arg_name, **kw) - - class cmd_list_configs(Command): help = "List the build configurations" @@ -235,7 +89,6 @@ class cmd_update_repo(Command): return True - class cmd_describe(Command): help = "Print the git commit, describe and verbose describe from kdir" args = [Args.config, Args.kdir] @@ -357,6 +210,21 @@ class cmd_get_build_env(Command): return True +class cmd_get_reference(Command): + help = "Print reference tree and branch for bisections" + args = [Args.tree_name, Args.branch] + + def __call__(self, configs, args): + for conf in configs['build_configs'].itervalues(): + if conf.tree.name == args.tree_name and conf.branch == args.branch: + if conf.reference: + print(conf.reference.tree.url) + print(conf.reference.tree.name) + print(conf.reference.branch) + return True + return False + + class cmd_show_build_env(Command): help = "Show parameters of a given build environment" args = [Args.build_env] @@ -395,7 +263,7 @@ class cmd_build_kernel(Command): class cmd_install_kernel(Command): - help = "Install the kernel binaries and build.json locally" + help = "Install the kernel binaries and bmeta.json locally" args = [Args.kdir] opt_args = [Args.config, Args.tree_name, Args.tree_url, Args.branch, Args.commit, Args.describe, Args.describe_verbose, @@ -449,27 +317,19 @@ Invalid arguments, please provide at least one of these sets of options: args.kdir, api=args.api, token=args.token, json_path=args.json_path) -# ----------------------------------------------------------------------------- -# Main -# + +class cmd_pull_tarball(Command): + help = "Downloads and untars kernel sources" + args = [Args.kdir, Args.url] + opt_args = [Args.filename, Args.retries] + + def __call__(self, configs, args): + return kernelci.build.pull_tarball(args.kdir, args.url, + args.filename, args.retries) + if __name__ == '__main__': - parser = argparse.ArgumentParser("kci_build") - parser.add_argument("--build-configs", default="build-configs.yaml", - help="Path to build configs file") - sub_parser = parser.add_subparsers(title="Commands", - help="List of available commands") - - commands = dict() - for k in globals().keys(): - split = k.split('cmd_') - if len(split) == 2: - obj = globals().get(k) - if issubclass(obj, Command): - cmd_name = split[1] - commands[cmd_name] = obj(sub_parser, cmd_name) - - args = parser.parse_args() - configs = kernelci.config.build.from_yaml(args.build_configs) + args = parse_args("kci_build", "build-configs.yaml", globals()) + configs = kernelci.config.build.from_yaml(args.yaml_configs) status = args.func(configs, args) sys.exit(0 if status is True else 1) diff --git a/kci_test b/kci_test new file mode 100755 index 0000000..e10f07c --- /dev/null +++ b/kci_test @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# Copyright (C) 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import argparse +import glob +import json +import os +import sys + +from kernelci.cli import Args, Command, parse_args +import kernelci.config.lab +import kernelci.config.test +import kernelci.build +import kernelci.lab +import kernelci.test + + +# ----------------------------------------------------------------------------- +# Commands +# + +class cmd_list_jobs(Command): + help = "List all the jobs that need to be run for a given build and lab" + args = [Args.bmeta_json, Args.dtbs_json, Args.lab] + opt_args = [Args.user, Args.token, Args.lab_json] + + def __call__(self, test_configs, lab_configs, args): + bmeta, dtbs = kernelci.build.load_json(args.bmeta_json, args.dtbs_json) + + if bmeta['status'] != "PASS": + return True + + lab = lab_configs['labs'][args.lab] + api = kernelci.lab.get_api(lab, args.user, args.token, args.lab_json) + + configs = kernelci.test.match_configs( + test_configs['test_configs'], bmeta, dtbs, lab) + for device_type, plan in configs: + if not api.device_type_online(device_type): + continue + print(' '.join([device_type.name, plan.name])) + + return True + + +class cmd_list_plans(Command): + help = "List all the existing test plan names" + + def __call__(self, test_configs, lab_configs, args): + plans = set(plan.name for plan in test_configs['test_plans'].values()) + for plan in sorted(plans): + print(plan) + return True + + +class cmd_list_labs(Command): + help = "List all the existing lab names" + + def __call__(self, test_configs, lab_configs, args): + for lab in sorted(lab_configs['labs'].keys()): + print(lab) + return True + + +class cmd_get_lab_info(Command): + help = "Get the information about a lab into a JSON file" + args = [Args.lab, Args.lab_json] + opt_args = [Args.user, Args.token] + + def __call__(self, test_configs, lab_configs, args): + lab = lab_configs['labs'][args.lab] + lab_api = kernelci.lab.get_api(lab, args.user, args.token) + data = { + 'lab': lab.name, + 'lab_type': lab.lab_type, + 'url': lab.url, + 'devices': lab_api.devices, + } + with open(args.lab_json, 'w') as json_file: + json.dump(data, json_file, indent=4, sort_keys=True) + return True + + +class cmd_generate(Command): + help = "Generate the job definition for a given build" + args = [Args.bmeta_json, Args.dtbs_json, Args.storage, Args.lab] + opt_args = [Args.plan, Args.target, Args.output, + Args.lab_json, Args.user, Args.token, + Args.callback_id, Args.callback_dataset, + Args.callback_type, Args.callback_url] + + def __call__(self, test_configs, lab_configs, args): + if args.callback_id and not args.callback_url: + print("--callback-url is required with --callback-id") + return False + + bmeta, dtbs = kernelci.build.load_json(args.bmeta_json, args.dtbs_json) + + if bmeta['status'] != "PASS": + return True + + lab = lab_configs['labs'][args.lab] + api = kernelci.lab.get_api(lab, args.user, args.token, args.lab_json) + + if args.target and args.plan: + target = test_configs['device_types'][args.target] + plan = test_configs['test_plans'][args.plan] + jobs_list = [(target, plan)] + else: + jobs_list = [] + configs = kernelci.test.match_configs( + test_configs['test_configs'], bmeta, dtbs, lab) + for device_type, plan in configs: + if not api.device_type_online(device_type): + continue + if args.target and device_type.name != args.target: + continue + if args.plan and plan.name != args.plan: + continue + jobs_list.append((device_type, plan)) + + # ToDo: deal with a JSON file containing a list of builds to iterate + # over, such as the one produced by "kci_build publish" or saved as a + # result of a new command "kci_build get_meta" to download meta-data + # from the backend API. + callback_opts = { + 'id': args.callback_id, + 'dataset': args.callback_dataset, + 'type': args.callback_type, + 'url': args.callback_url, + } + if args.output and not os.path.exists(args.output): + os.makedirs(args.output) + for target, plan in jobs_list: + params = kernelci.test.get_params( + bmeta, target, plan, args.storage) + job = api.generate(params, target, plan, callback_opts) + if args.output: + file_name = api.job_file_name(params) + output_file = os.path.join(args.output, file_name) + print(output_file) + with open(output_file, 'wb') as output: + output.write(job) + else: + print("# Job: {}".format(params['name'])) + print(job) + return True + + +class cmd_submit(Command): + help = "Submit job definitions to a lab" + args = [Args.lab, Args.user, Args.token, Args.jobs] + + def __call__(self, test_configs, lab_configs, args): + lab = lab_configs['labs'][args.lab] + lab_api = kernelci.lab.get_api(lab, args.user, args.token) + job_paths = glob.glob(args.jobs) + res = True + for path in job_paths: + if not os.path.isfile(path): + continue + with open(path, 'rb') as job_file: + job = job_file.read() + try: + job_id = lab_api.submit(job) + print("{} {}".format(job_id, path)) + except Exception as e: + print("ERROR {}: {}".format(path, e)) + res = False + return res + + +# ----------------------------------------------------------------------------- +# Main +# + +if __name__ == '__main__': + parser = argparse.ArgumentParser("kci_test") + parser.add_argument("--yaml-test-configs", default="test-configs.yaml", + help="Path to the YAML test configs file") + parser.add_argument("--yaml-lab-configs", default="lab-configs.yaml", + help="Path to the YAML lab configs file") + kernelci.cli.add_subparser(parser, globals()) + args = parser.parse_args() + test_configs = kernelci.config.test.from_yaml(args.yaml_test_configs) + lab_configs = kernelci.config.lab.from_yaml(args.yaml_lab_configs) + status = args.func(test_configs, lab_configs, args) + sys.exit(0 if status is True else 1) diff --git a/kernelci/build.py b/kernelci/build.py index 598786f..969b548 100644 --- a/kernelci/build.py +++ b/kernelci/build.py @@ -16,13 +16,14 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import fnmatch +import itertools import json import os import platform import requests import shutil import stat -import subprocess +import tarfile import tempfile import time import urlparse @@ -141,7 +142,8 @@ def check_new_commit(config, storage): def _update_remote(config, path): - shell_cmd(""" + shell_cmd(""" +set -e cd {path} if git remote | grep -e '^{remote}$'; then git remote set-url {remote} {url} @@ -156,6 +158,7 @@ fi def _fetch_tags(path, url=TORVALDS_GIT_URL): shell_cmd(""" +set -e cd {path} git fetch --tags {url} """.format(path=path, url=url)) @@ -169,6 +172,7 @@ def update_mirror(config, path): """ if not os.path.exists(path): shell_cmd(""" +set -e mkdir -p {path} cd {path} git init --bare @@ -186,19 +190,19 @@ def update_repo(config, path, ref=None): """ if not os.path.exists(path): ref_opt = '--reference={ref}'.format(ref=ref) if ref else '' - shell_cmd(""" -git clone {ref} -o {remote} {url} {path} -""".format(ref=ref_opt, remote=config.tree.name, - url=config.tree.url, path=path)) + shell_cmd("git clone {ref} -o {remote} {url} {path}".format( + ref=ref_opt, remote=config.tree.name, + url=config.tree.url, path=path)) _update_remote(config, path) - _fetch_tags(path) + _fetch_tags(path, config.tree.url) + _fetch_tags(path, TORVALDS_GIT_URL) shell_cmd(""" +set -e cd {path} git reset --hard git clean -fd -git fetch --tags {remote} git checkout --detach {remote}/{branch} """.format(path=path, remote=config.tree.name, branch=config.branch)) @@ -211,8 +215,9 @@ def head_commit(path): The returned value is the git SHA of the current HEAD of the branch checked out in the local git repository. """ - cmd = """\ -cd {path} && + cmd = """ +set -e +cd {path} git log --pretty=format:%H -n1 """.format(path=path) commit = shell_cmd(cmd) @@ -229,9 +234,10 @@ def git_describe(tree_name, path): currently checked out in the local git repository. """ describe_args = r"--match=v\*" if tree_name == "soc" else "" - cmd = """\ -cd {path} && \ -git describe {describe_args} \ + cmd = """ +set -e +cd {path} +git describe {describe_args} """.format(path=path, describe_args=describe_args) describe = shell_cmd(cmd) return describe.strip().replace('/', '_') @@ -246,9 +252,10 @@ def git_describe_verbose(path): the commit currently checked out in the local git repository. This is typically based on a mainline kernel version tag. """ - cmd = r"""\ -cd {path} && -git describe --match=v[1-9]\* \ + cmd = r""" +set -e +cd {path} +git describe --match=v[1-9]\* """.format(path=path) describe = shell_cmd(cmd) return describe.strip() @@ -261,7 +268,8 @@ def add_kselftest_fragment(path, frag_path='kernel/configs/kselftest.config'): *frag_path* is the path where to create the fragment within the repo """ shell_cmd(r""" -cd {path} && +set -e +cd {path} find \ tools/testing/selftests \ -name config \ @@ -281,9 +289,13 @@ def make_tarball(kdir, tarball_name): *kdir* is the path to the local kernel source directory *tarball_name* is the name of the tarball file to create """ - cmd = "tar -czf {name} --exclude=.git -C {kdir} .".format( - kdir=kdir, name=tarball_name) - subprocess.check_output(cmd, shell=True) + cwd = os.getcwd() + os.chdir(kdir) + _, dirs, files = next(os.walk('.')) + with tarfile.open(os.path.join(cwd, tarball_name), "w:gz") as tarball: + for item in itertools.chain(dirs, files): + tarball.add(item, filter=lambda f: f if f.name != '.git' else None) + os.chdir(cwd) def generate_config_fragment(frag, kdir): @@ -341,6 +353,33 @@ def push_tarball(config, kdir, storage, api, token): return tarball_url +def _download_file(url, dest_filename, chunk_size=1024): + resp = requests.get(url, stream=True) + if resp.status_code == 200: + with open(dest_filename, 'wb') as out_file: + for chunk in resp.iter_content(chunk_size): + out_file.write(chunk) + return True + else: + return False + + +def pull_tarball(kdir, url, dest_filename, retries): + if os.path.exists(kdir): + shutil.rmtree(kdir) + os.makedirs(kdir) + for i in range(1, retries + 1): + if _download_file(url, dest_filename): + break + if i < retries: + time.sleep(2 ** i) + else: + return False + with tarfile.open(dest_filename, 'r:*') as tarball: + tarball.extractall(kdir) + return True + + def _add_frag_configs(kdir, frag_list, frag_paths, frag_configs): for frag in frag_list: if os.path.exists(os.path.join(kdir, frag.path)): @@ -450,44 +489,58 @@ def _run_make(kdir, arch, target=None, jopt=None, silent=True, cc='gcc', return shell_cmd(cmd, True) -def _make_defconfig(defconfig, kwargs, fragments, verbose, log_file): +def _make_defconfig(defconfig, kwargs, extras, verbose, log_file): kdir, output_path = (kwargs.get(k) for k in ('kdir', 'output')) result = True + defconfig_kwargs = dict(kwargs) + defconfig_opts = dict(defconfig_kwargs['opts']) + defconfig_kwargs['opts'] = defconfig_opts tmpfile_fd, tmpfile_path = tempfile.mkstemp(prefix='kconfig-') tmpfile = os.fdopen(tmpfile_fd, 'w') + tmpfile_used = False defs = defconfig.split('+') target = defs.pop(0) for d in defs: - if d.startswith('CONFIG_'): + if d.startswith('KCONFIG_'): + config, value = d.split('=') + defconfig_opts[config] = value + extras.append(d) + elif d.startswith('CONFIG_'): tmpfile.write(d + '\n') - fragments.append(d) + extras.append(d) + tmpfile_used = True else: frag_path = os.path.join(kdir, d) if os.path.exists(frag_path): with open(frag_path) as frag: tmpfile.write("\n# fragment from : {}\n".format(d)) tmpfile.writelines(frag) - fragments.append(os.path.basename(os.path.splitext(d)[0])) + tmpfile_used = True + extras.append(os.path.basename(os.path.splitext(d)[0])) + else: + print("Fragment not found: {}".format(frag_path)) + result = False tmpfile.flush() - if not _run_make(target=target, **kwargs): + if not _run_make(target=target, **defconfig_kwargs): result = False - if result and fragments: + if result and tmpfile_used: kconfig_frag_name = 'frag.config' kconfig_frag = os.path.join(output_path, kconfig_frag_name) shutil.copy(tmpfile_path, kconfig_frag) os.chmod(kconfig_frag, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) rel_path = os.path.relpath(output_path, kdir) - cmd = """\ + cmd = """ +set -e cd {kdir} export ARCH={arch} export HOSTCC={cc} export CC={cc} export CROSS_COMPILE={cross} -scripts/kconfig/merge_config.sh -O {output} '{base}' '{frag}' {redir} \ +scripts/kconfig/merge_config.sh -O {output} '{base}' '{frag}' {redir} """.format(kdir=kdir, arch=kwargs['arch'], cc=kwargs['cc'], cross=kwargs['cross_compile'], output=rel_path, base=os.path.join(rel_path, '.config'), @@ -555,10 +608,10 @@ def build_kernel(build_env, kdir, arch, defconfig=None, jopt=None, } start_time = time.time() - fragments = [] + defconfig_extras = [] if defconfig: result = _make_defconfig( - defconfig, kwargs, fragments, verbose, log_file) + defconfig, kwargs, defconfig_extras, verbose, log_file) elif os.path.exists(dot_config): print("Re-using {}".format(dot_config)) result = True @@ -599,7 +652,7 @@ def build_kernel(build_env, kdir, arch, defconfig=None, jopt=None, bmeta = { 'build_threads': jopt, 'build_time': round(build_time, 2), - 'build_result': 'PASS' if result is True else 'FAIL', + 'status': 'PASS' if result is True else 'FAIL', 'arch': arch, 'cross_compile': cross_compile, 'compiler': cc, @@ -614,7 +667,12 @@ def build_kernel(build_env, kdir, arch, defconfig=None, jopt=None, defconfig_target = defconfig.split('+')[0] bmeta.update({ 'defconfig': defconfig_target, - 'defconfig_full': '+'.join([defconfig_target] + fragments), + 'defconfig_full': '+'.join([defconfig_target] + defconfig_extras), + }) + else: + bmeta.update({ + 'defconfig': 'none', + 'defconfig_full': 'none', }) vmlinux_file = os.path.join(output_path, 'vmlinux') @@ -710,15 +768,19 @@ def install_kernel(kdir, tree_name, tree_url, git_branch, git_commit=None, dts_dir = os.path.join(boot_dir, 'dts') dtbs = os.path.join(install_path, 'dtbs') + dtb_list = [] for root, _, files in os.walk(dts_dir): for f in fnmatch.filter(files, '*.dtb'): dtb_path = os.path.join(root, f) dtb_rel = os.path.relpath(dtb_path, dts_dir) + dtb_list.append(dtb_rel) dest_path = os.path.join(dtbs, dtb_rel) dest_dir = os.path.dirname(dest_path) if not os.path.exists(dest_dir): os.makedirs(dest_dir) shutil.copy(dtb_path, dest_path) + with open(os.path.join(install_path, 'dtbs.json'), 'w') as json_file: + json.dump({'dtbs': sorted(dtb_list)}, json_file, indent=4) modules_tarball = None if mod_path: @@ -731,8 +793,9 @@ def install_kernel(kdir, tree_name, tree_url, git_branch, git_commit=None, build_env = bmeta['build_environment'] defconfig_full = bmeta['defconfig_full'] + defconfig_dir = defconfig_full.replace('/', '-') publish_path = '/'.join([ - tree_name, git_branch, describe, arch, defconfig_full, build_env, + tree_name, git_branch, describe, arch, defconfig_dir, build_env, ]) bmeta.update({ @@ -752,7 +815,7 @@ def install_kernel(kdir, tree_name, tree_url, git_branch, git_commit=None, 'file_server_resource': publish_path, }) - with open(os.path.join(install_path, 'build.json'), 'w') as json_file: + with open(os.path.join(install_path, 'bmeta.json'), 'w') as json_file: json.dump(bmeta, json_file, indent=4, sort_keys=True) return True @@ -774,7 +837,7 @@ def push_kernel(kdir, api, token, install='_install_'): """ install_path = os.path.join(kdir, install) - with open(os.path.join(install_path, 'build.json')) as f: + with open(os.path.join(install_path, 'bmeta.json')) as f: bmeta = json.load(f) artifacts = {} @@ -811,53 +874,56 @@ def publish_kernel(kdir, install='_install_', api=None, token=None, """ install_path = os.path.join(kdir, install) - with open(os.path.join(install_path, 'build.json')) as f: + with open(os.path.join(install_path, 'bmeta.json')) as f: bmeta = json.load(f) - data = {k: bmeta[v] for k, v in { - 'path': 'file_server_resource', - 'file_server_resource': 'file_server_resource', - 'job': 'job', - 'git_branch': 'git_branch', - 'arch': 'arch', - 'kernel': 'git_describe', - 'build_environment': 'build_environment', - 'defconfig': 'defconfig', - 'defconfig_full': 'defconfig_full', - }.iteritems()} - if json_path: - json_data = dict(data) - for k in ['kernel_image', 'modules', 'git_commit', 'git_url']: - json_data[k] = bmeta[k] - json_data['status'] = bmeta['build_result'] - dtb_data = [] - if bmeta['dtb_dir']: - dtb_dir = os.path.join(install_path, bmeta['dtb_dir']) - for root, dirs, files in os.walk(dtb_dir): - if root != dtb_dir: - rel = os.path.relpath(root, dtb_dir) - files = list(os.path.join(rel, dtb) for dtb in files) - dtb_data += files - json_data['dtb_dir_data'] = dtb_data + with open(os.path.join(install_path, 'dtbs.json')) as f: + dtbs = json.load(f)['dtbs'] + bmeta['dtb_dir_data'] = dtbs try: with open(json_path, 'r') as json_file: full_json = json.load(json_file) - full_json.append(json_data) + full_json.append(bmeta) except Exception as e: - full_json = [json_data] + full_json = [bmeta] with open(json_path, 'w') as json_file: json.dump(full_json, json_file) if api and token: + data = {k: bmeta[v] for k, v in { + 'path': 'file_server_resource', + 'file_server_resource': 'file_server_resource', + 'job': 'job', + 'git_branch': 'git_branch', + 'arch': 'arch', + 'kernel': 'git_describe', + 'build_environment': 'build_environment', + 'defconfig': 'defconfig', + 'defconfig_full': 'defconfig_full', + }.iteritems()} headers = { 'Authorization': token, 'Content-Type': 'application/json', } - url = urlparse.urljoin(api, '/build') - json_data = json.dumps(data) - resp = requests.post(url, headers=headers, data=json_data) + data_json = json.dumps(data) + resp = requests.post(url, headers=headers, data=data_json) resp.raise_for_status() return True + + +def load_json(bmeta_json, dtbs_json): + """Load the build meta-data from JSON files and return dictionaries + + *bmeta_json* is the path to a kernel build meta-data JSON file + *dtbs_json* is the path to a kernel dtbs JSON file + + The returned value is a 2-tuple with the bmeta and dtbs data. + """ + with open(bmeta_json) as json_file: + bmeta = json.load(json_file) + with open(dtbs_json) as json_file: + dtbs = json.load(json_file)['dtbs'] + return bmeta, dtbs diff --git a/kernelci/cli.py b/kernelci/cli.py new file mode 100644 index 0000000..af43525 --- /dev/null +++ b/kernelci/cli.py @@ -0,0 +1,321 @@ +# Copyright (C) 2018, 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import argparse + + +# ----------------------------------------------------------------------------- +# Standard arguments that can be used in sub-commands +# + +class Args(object): + """A list of all the common command line argument options + + All the members of this class are arguments that can be reused in various + command line tools. They are dictionaries with at least a `name` + attribute, and all the other ones are passed as keyword arguments to the + add_argument() method of the parser object from argparse. There should + also always be a `help` attribute, as this is needed by the Command class. + """ + + config = { + 'name': '--config', + 'help': "Build config name", + } + + variant = { + 'name': '--variant', + 'help': "Build config variant name", + } + + build_env = { + 'name': '--build-env', + 'help': "Build environment name", + } + + storage = { + 'name': '--storage', + 'help': "Storage URL", + 'default': "https://storage.kernelci.org", + 'required': False, + } + + api = { + 'name': '--api', + 'help': "Backend API URL", + 'default': "https://api.kernelci.org", + 'required': False, + } + + user = { + 'name': '--user', + 'help': "User name", + } + + token = { + 'name': '--token', + 'help': "Backend API token", + } + + callback_id = { + 'name': '--callback-id', + 'help': "Callback identifier used to look up an authentication token", + } + + callback_dataset = { + 'name': '--callback-dataset', + 'help': "Dataset to include in a lab callback", + 'default': 'all', + } + + callback_type = { + 'name': '--callback-type', + 'help': "Type of callback URL", + 'default': 'kernelci', + } + + callback_url = { + 'name': '--callback-url', + 'help': "Base URL for the callback", + } + + jobs = { + 'name': '--jobs', + 'help': "File pattern with jobs to submit", + } + + commit = { + 'name': '--commit', + 'help': "Git commit checksum", + } + + describe = { + 'name': '--describe', + 'help': "Git describe", + } + + describe_verbose = { + 'name': '--describe-verbose', + 'help': "Verbose version of git describe", + } + + tree_name = { + 'name': '--tree-name', + 'help': "Name of a kernel tree", + } + + tree_url = { + 'name': '--tree-url', + 'help': "URL of a kernel tree", + } + + branch = { + 'name': '--branch', + 'help': "Name of a kernel branch in a tree", + } + + mirror = { + 'name': '--mirror', + 'help': "Path to the local kernel git mirror", + } + + kdir = { + 'name': '--kdir', + 'help': "Path to the kernel checkout directory", + } + + arch = { + 'name': '--arch', + 'help': "CPU architecture name", + } + + bmeta_json = { + 'name': '--bmeta-json', + 'help': "Path to the build.json file", + } + + dtbs_json = { + 'name': '--dtbs-json', + 'help': "Path to the dtbs.json file", + } + + lab_json = { + 'name': '--lab-json', + 'help': "Path to a JSON file with lab-specific info", + } + + lab = { + 'name': '--lab', + 'help': "Name of a test lab", + } + + target = { + 'name': '--target', + 'help': "Name of a target platform", + } + + defconfig = { + 'name': '--defconfig', + 'help': "Kernel defconfig name", + } + + j = { + 'name': '-j', + 'help': "Number of parallel build processes", + } + + verbose = { + 'name': '--verbose', + 'help': "Verbose output", + 'action': 'store_true', + } + + output = { + 'name': '--output', + 'help': "Path the output directory", + } + + json_path = { + 'name': '--json-path', + 'help': "Path to the JSON file", + } + + plan = { + 'name': '--plan', + 'help': "Test plan name", + } + + url = { + 'name': '--url', + 'help': "Kernel sources download URL", + } + + filename = { + 'name': '--filename', + 'help': "Kernel sources destination filename", + 'required': False, + 'default': 'linux-src.tar.gz', + } + + retries = { + 'name': '--retries', + 'help': 'Number of retries before download fails', + 'required': False, + 'default': 1, + 'type': int, + } + + +class Command(object): + """A command helper class. + + It contains several class attributes: + + *help* is the help message passed to tbe sub-parser + *args* is a list of required arguments dictionaries to add to the parser + *opt_args* is a list of optional arguments to add to the parser + """ + + help = None + args = None + opt_args = None + + def __init__(self, sub_parser, name): + """This class is to facilitate creating command line utilities + + Each command uses a separate sub-parser to be able to have a different + set of arguments. A Command object is callable like a function, so it + is also possible to simply have a function in the command line tool + instead. + + *sub_parser* is a sub-parser from argparse for this particular command. + *name* is the name of the command as used on the command line. + + """ + if not self.help: + raise AttributeError("Missing help message for {}".format(name)) + self._parser = sub_parser.add_parser(name, help=self.help) + if self.args: + for arg in self.args: + self._add_arg(arg, True) + if self.opt_args: + for arg in self.opt_args: + self._add_arg(arg, False) + self._parser.set_defaults(func=self) + + def __call__(self, *args, **kw): + raise NotImplementedError("Command not implemented") + + def _add_arg(self, arg, required=True): + kw = dict(arg) + arg_name = kw.pop('name') + if required: + kw.setdefault('required', True) + self._parser.add_argument(arg_name, **kw) + + +def make_parser(title, default_yaml): + """Helper to make a parser object from argparse. + + *title* is the title of the parser + *default_yaml* is the default YAML config file name to use + """ + parser = argparse.ArgumentParser(title) + parser.add_argument("--yaml-configs", default=default_yaml, + help="Path to the YAML configs file") + return parser + + +def add_subparser(parser, glob): + """Helper to add a sub-parser to add sub-commands + + All the global attributes from `glob` starting with `cmd_` are added as + sub-commands to the parser. + + *parser* is the main parser object from argparse + *glob* is the globals dictionary + """ + sub_parser = parser.add_subparsers(title="Commands", + help="List of available commands") + commands = dict() + for k in glob.keys(): + split = k.split('cmd_') + if len(split) == 2: + obj = glob.get(k) + if issubclass(obj, Command): + cmd_name = split[1] + commands[cmd_name] = obj(sub_parser, cmd_name) + + +def parse_args(title, default_yaml, glob): + """Helper function to parse the command line arguments + + This will create a parser and automatically add the sub-commands from the + global attributes `glob` and return the parsed arguments. + + *title* is the parser title + + *default_yaml* is the name of the default YAML configuration file to use + with the command line utility + + *glob* is the dictionary with all the global attributes where to look for + commands starting with `cmd_` + """ + parser = make_parser(title, default_yaml) + add_subparser(parser, glob) + args = parser.parse_args() + return args diff --git a/kernelci/config/build.py b/kernelci/config/build.py index 9a7a686..daeb5f0 100644 --- a/kernelci/config/build.py +++ b/kernelci/config/build.py @@ -49,6 +49,33 @@ class Tree(YAMLObject): return self._url +class Reference(YAMLObject): + """Kernel reference tree and branch model.""" + + def __init__(self, tree, branch): + """Reference is a tree and branch used for bisections + + *tree* is a Tree object + *branch* is the branch name to be used from the tree + """ + self._tree = tree + self._branch = branch + + @classmethod + def from_yaml(cls, reference, trees): + kw = cls._kw_from_yaml(reference, ['tree', 'branch']) + kw['tree'] = trees[kw['tree']] + return cls(**kw) + + @property + def tree(self): + return self._tree + + @property + def branch(self): + return self._branch + + class Fragment(YAMLObject): """Kernel config fragment model.""" @@ -290,7 +317,7 @@ class BuildVariant(YAMLObject): class BuildConfig(YAMLObject): """Build configuration model.""" - def __init__(self, name, tree, branch, variants): + def __init__(self, name, tree, branch, variants, reference=None): """A build configuration defines the actual kernels to be built. *name* is the name of the build configuration. It is arbitrary and @@ -304,11 +331,17 @@ class BuildConfig(YAMLObject): *variants* is a list of BuildVariant objects, to define all the variants to build for this tree / branch combination. + + *reference* is a Reference object which defines the tree and branch for + bisections when no base commit is found for the good and + bad revisions. It can also be None if no reference branch + can be used with this build configuration. """ self._name = name self._tree = tree self._branch = branch self._variants = variants + self._reference = reference @classmethod def from_yaml(cls, config, name, trees, fragments, build_envs, defaults): @@ -318,12 +351,16 @@ class BuildConfig(YAMLObject): kw.update(cls._kw_from_yaml( config, ['name', 'tree', 'branch'])) kw['tree'] = trees[kw['tree']] - config_variants = config.get('variants', defaults) + default_variants = defaults.get('variants', {}) + config_variants = config.get('variants', default_variants) variants = [ BuildVariant.from_yaml(variant, name, fragments, build_envs) for name, variant in config_variants.iteritems() ] kw['variants'] = {v.name: v for v in variants} + reference = config.get('reference', defaults.get('reference')) + if reference: + kw['reference'] = Reference.from_yaml(reference, trees) return cls(**kw) @property @@ -345,6 +382,10 @@ class BuildConfig(YAMLObject): def get_variant(self, name): return self._variants[name] + @property + def reference(self): + return self._reference + def from_yaml(yaml_path): with open(yaml_path) as f: diff --git a/kernelci/config/lab.py b/kernelci/config/lab.py new file mode 100644 index 0000000..91c0267 --- /dev/null +++ b/kernelci/config/lab.py @@ -0,0 +1,111 @@ +# Copyright (C) 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import yaml + +from kernelci.config import FilterFactory, YAMLObject + + +class Lab(YAMLObject): + """Test lab model.""" + + def __init__(self, name, lab_type, url, filters=None): + """A lab object contains all the information relative to a test lab. + + *name* is the name used to refer to the lab in meta-data. + *lab_type* is the name of the type of lab, essentially indicating the + type of software used by the lab. + *url* is the URL to reach the lab API. + *filters* is a list of Filter objects associated with this lab. + """ + self._name = name + self._lab_type = lab_type + self._url = url + self._filters = filters or list() + + @classmethod + def from_yaml(cls, lab, kw): + return cls(**kw) + + @property + def name(self): + return self._name + + @property + def lab_type(self): + return self._lab_type + + @property + def url(self): + return self._url + + def match(self, data): + return all(f.match(**data) for f in self._filters) + + +class Lab_LAVA(Lab): + + def __init__(self, priority='medium', *args, **kwargs): + super(Lab_LAVA, self).__init__(*args, **kwargs) + self._priority = priority + + @property + def priority(self): + return self._priority + + @classmethod + def from_yaml(cls, lab, kw): + priority = lab.get('priority') + if priority: + kw['priority'] = priority + return cls(**kw) + + +class LabFactory(YAMLObject): + """Factory to create lab objects from YAML data.""" + + _lab_types = { + 'lava': Lab_LAVA, + } + + @classmethod + def from_yaml(cls, name, lab): + lab_type = lab.get('lab_type') + kw = cls._kw_from_yaml(lab, ['url']) + kw.update({ + 'name': name, + 'lab_type': lab_type, + 'filters': FilterFactory.from_data(lab), + }) + lab_cls = cls._lab_types[lab_type] if lab_type else Lab + return lab_cls.from_yaml(lab, kw) + + +def from_yaml(yaml_path): + with open(yaml_path) as f: + data = yaml.load(f) + + labs = { + name: LabFactory.from_yaml(name, lab) + for name, lab in data['labs'].iteritems() + } + + config_data = { + 'labs': labs, + } + + return config_data diff --git a/kernelci/config/test.py b/kernelci/config/test.py index b01c5e8..f21b578 100644 --- a/kernelci/config/test.py +++ b/kernelci/config/test.py @@ -128,6 +128,14 @@ class DeviceType_arm64(DeviceType): super(DeviceType_arm64, self).__init__(name, mach, arch, *args, **kw) +class DeviceType_riscv(DeviceType): + + def __init__(self, name, mach, arch='riscv', *args, **kw): + """RISCV device type with a device tree.""" + kw.setdefault('dtb', '{}/{}.dtb'.format(mach, name)) + super(DeviceType_riscv, self).__init__(name, mach, arch, *args, **kw) + + class DeviceTypeFactory(YAMLObject): """Factory to create device types from YAML data.""" @@ -136,6 +144,7 @@ class DeviceTypeFactory(YAMLObject): 'mips-dtb': DeviceType_mips, 'arm-dtb': DeviceType_arm, 'arm64-dtb': DeviceType_arm64, + 'riscv-dtb': DeviceType_riscv, } @classmethod @@ -272,10 +281,11 @@ class RootFS(YAMLObject): class TestPlan(YAMLObject): """Test plan model.""" - _pattern = '{plan}/{category}-{method}-{protocol}-{rootfs}-{plan}-template.jinja2' + _pattern = \ + '{plan}/{category}-{method}-{protocol}-{rootfs}-{plan}-template.jinja2' - def __init__(self, name, rootfs, params=None, category='generic', - filters=None, pattern=None): + def __init__(self, name, rootfs, base_name=None, params=None, + category='generic', filters=None, pattern=None): """A test plan is an arbitrary group of test cases to be run. *name* is the overall arbitrary test plan name, used when looking for @@ -283,6 +293,9 @@ class TestPlan(YAMLObject): *rootfs* is a RootFS object to be used to run this test plan. + *base_name* is the name of the base test plan which this test plan + configuration refers to. + *params" is a dictionary with parameters to pass to the test job generator. @@ -294,9 +307,11 @@ class TestPlan(YAMLObject): *pattern* is a string pattern to create the path to the job template file, see TestPlan._pattern for the default value with the regular template file naming scheme. + """ self._name = name self._rootfs = rootfs + self._base_name = base_name or name self._params = params or dict() self._category = category self._filters = filters or list() @@ -308,6 +323,7 @@ class TestPlan(YAMLObject): kw = { 'name': name, 'rootfs': file_systems[test_plan['rootfs']], + 'base_name': test_plan.get('base_name'), 'filters': FilterFactory.from_data(test_plan, default_filters), } kw.update(cls._kw_from_yaml(test_plan, [ @@ -323,6 +339,10 @@ class TestPlan(YAMLObject): return self._rootfs @property + def base_name(self): + return self._base_name + + @property def params(self): return dict(self._params) @@ -341,8 +361,6 @@ class TestPlan(YAMLObject): plan=self.name) def match(self, config): - if self.name == 'boot': - return True return all(f.match(**config) for f in self._filters) @@ -381,10 +399,12 @@ class TestConfig(YAMLObject): def test_plans(self): return self._test_plans - def match(self, arch, plan, flags, config): + def match(self, arch, flags, config, plan=None): return ( - plan in self._test_plans and - self._test_plans[plan].match(config) and + (plan is None or ( + plan in self._test_plans and + self._test_plans[plan].match(config) + )) and self.device_type.arch == arch and self.device_type.match(flags, config) and all(f.match(**config) for f in self._filters) diff --git a/kernelci/lab/__init__.py b/kernelci/lab/__init__.py new file mode 100644 index 0000000..d70e44d --- /dev/null +++ b/kernelci/lab/__init__.py @@ -0,0 +1,99 @@ +# Copyright (C) 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import importlib +import json +import urlparse +import xmlrpclib + + +class LabAPI(object): + """Remote API to a test lab""" + + def __init__(self, config): + """A test lab API object can be used to remotely interact with a lab + + *config* is a kernelci.config.lab.Lab object + """ + self._config = config + self._server = None + self._devices = None + + @property + def config(self): + return self._config + + @property + def devices(self): + if self._devices is None: + self._devices = self._get_devices() + return self._devices + + def _get_devices(self): + return list() + + def connect(self, user=None, token=None): + """Connect to the remote server API + + *user* is the name of the user to connect to the lab + *token* is the token associated with the user to connect to the lab + """ + if user and token: + url = urlparse.urlparse(self.config.url) + api_url = "{scheme}://{user}:{token}@{loc}{path}".format( + scheme=url.scheme, user=user, token=token, + loc=url.netloc, path=url.path) + else: + api_url = self.config.url + self._server = xmlrpclib.ServerProxy(api_url) + + def import_devices(self, data): + self._devices = data + + def device_type_online(self, device_type_name): + return True + + def job_file_name(self, params): + return params['name'] + + def match(self, filter_data): + return self.config.match(filter_data) + + def generate(self, params, target, plan, callback_opts): + raise NotImplementedError("Lab.generate() is required") + + def submit(self, job): + raise NotImplementedError("Lab.submit() is required") + + +def get_api(lab, user=None, token=None, lab_json=None): + """Get the LabAPI object for a given lab config. + + *lab* is a kernelci.config.lab.Lab object + *user* is the name of the user to connect to the remote lab + *token* is the associated token to connect to the remote lab + *lab_json* is the path to a JSON file with cached lab information + """ + m = importlib.import_module('.'.join(['kernelci', 'lab', lab.lab_type])) + api = m.get_api(lab) + if lab_json: + with open(lab_json) as json_file: + devices = json.load(json_file)['devices'] + api.import_devices(devices) + if user and token: + api.connect(user, token) + return api diff --git a/kernelci/lab/lava.py b/kernelci/lab/lava.py new file mode 100644 index 0000000..5f3df29 --- /dev/null +++ b/kernelci/lab/lava.py @@ -0,0 +1,146 @@ +# Copyright (C) 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# Copyright (C) 2019 Linaro Limited +# Author: Dan Rue <dan.rue@linaro.org> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from jinja2 import Environment, FileSystemLoader +import os +from kernelci.lab import LabAPI + +DEVICE_ONLINE_STATUS = ['idle', 'running', 'reserved'] + + +def get_device_type_by_name(name, device_types, aliases=[]): + """ + Return a device type named name. In the case of an alias, resolve the + alias to a device_type + + Example: + IN: + name = "x15" + device_types = ['x15', 'beaglebone-black'], + aliases = [ + {'name': 'x15', 'device_type': 'am57xx-beagle-x15'} + ] + OUT: + 'am57xx-beagle-x15' + + """ + for device_type in device_types: + if device_type == name: + return device_type + for alias in aliases: + if alias["name"] == name: + for device_type in device_types: + if alias["device_type"] == device_type: + return device_type + return None + + +class LAVA(LabAPI): + """Interface to a LAVA lab + + This implementation of kernelci.lab.LabAPI is to communicate with LAVA + labs. It can retrieve some information such as the list of devices and + their online status, generate and submit jobs with callback parameters. + One special thing it can deal with is job priorities, which is only + available in kernelci.config.lab.lab_LAVA objects. + """ + + def _get_aliases(self): + aliases = [] + for alias in self._server.scheduler.aliases.list(): + aliases.append(self._server.scheduler.aliases.show(alias)) + return aliases + + def _get_devices(self): + all_devices = self._server.scheduler.all_devices() + all_aliases = [] + for alias in self._server.scheduler.aliases.list(): + all_aliases.append(self._server.scheduler.aliases.show(alias)) + device_types = {} + for device in all_devices: + name, device_type, status, _, _ = device + device_list = device_types.setdefault(device_type, list()) + device_list.append({ + 'name': name, + 'online': status in DEVICE_ONLINE_STATUS, + }) + device_type_online = { + device_type: any(device['online'] for device in devices) + for device_type, devices in device_types.iteritems() + } + return { + 'device_type_online': device_type_online, + 'aliases': all_aliases, + } + + def _add_callback_params(self, params, opts): + callback_id = opts.get('id') + if not callback_id: + return + callback_type = opts.get('type') + if callback_type == 'kernelci': + lava_cb = 'boot' if params['plan'] == 'boot' else 'test' + # ToDo: consolidate this to just have to pass the callback_url + params['callback_name'] = '/'.join(['lava', lava_cb]) + params.update({ + 'callback': callback_id, + 'callback_url': opts['url'], + 'callback_dataset': opts['dataset'], + 'callback_type': callback_type, + }) + + def device_type_online(self, device_type): + devices = self.devices['device_type_online'] + device_type_name = get_device_type_by_name( + device_type.base_name, devices.keys(), self.devices['aliases']) + return self.devices['device_type_online'].get(device_type_name, False) + + def job_file_name(self, params): + return '.'.join([params['name'], 'yaml']) + + def generate(self, params, target, plan, callback_opts): + short_template_file = plan.get_template_path(target.boot_method) + template_file = os.path.join('templates', short_template_file) + if not os.path.exists(template_file): + print("Template not found: {}".format(template_file)) + return None + devices = self.devices['device_type_online'] + base_name = params['base_device_type'] + params.update({ + 'template_file': template_file, + 'priority': self.config.priority, + 'lab_name': self.config.name, + 'base_device_type': get_device_type_by_name( + base_name, devices.keys(), self.devices['aliases']), + }) + self._add_callback_params(params, callback_opts) + jinja2_env = Environment(loader=FileSystemLoader('templates'), + extensions=["jinja2.ext.do"]) + template = jinja2_env.get_template(short_template_file) + data = template.render(params) + return data + + def submit(self, job): + return self._server.scheduler.submit_job(job) + + +def get_api(lab): + """Get a LAVA lab API object""" + return LAVA(lab) diff --git a/kernelci/test.py b/kernelci/test.py new file mode 100644 index 0000000..b3095af --- /dev/null +++ b/kernelci/test.py @@ -0,0 +1,145 @@ +# Copyright (C) 2019 Collabora Limited +# Author: Guillaume Tucker <guillaume.tucker@collabora.com> +# +# This module is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import urlparse + + +def match_configs(configs, bmeta, dtbs, lab): + """Filter the test configs for a given kernel build and lab. + + *configs* is a list of all the initial test configs + *bmeta* is a dictionary with the kernel build meta-data + *dtbs* is the list of dtb files included in the kernel build + *lab* is a Lab object instance + + The returned value is a list with a subset of the configs that match the + provided kernel build meta-data and lab filters. + """ + defconfig = bmeta['defconfig_full'] + arch = bmeta['arch'] + + filters = { + 'arch': arch, + 'defconfig': defconfig, + 'kernel': bmeta['git_describe'], + 'build_environment': bmeta['build_environment'], + 'tree': bmeta['job'], + } + + flags = { + 'big_endian': 'BIG_ENDIAN' in defconfig, + 'lpae': 'LPAE' in defconfig, + } + + match = set() + + for test_config in configs: + if not test_config.match(arch, flags, filters): + continue + dtb = test_config.device_type.dtb + if dtb and dtb not in dtbs: + continue + for plan_name, plan in test_config.test_plans.iteritems(): + if not plan.match(filters): + continue + filters['plan'] = plan_name + if lab.match(filters): + match.add((test_config.device_type, plan)) + + return match + + +def get_params(bmeta, target, plan_config, storage): + """Get a dictionary with all the test parameters to run a test job + + *bmeta* is a dictionary with the kernel build meta-data + *target* is the name of the target platform to run the test + *plan_config* is a TestPlan object for the test plan to run + *storage* is the URL of the storage server + """ + arch = target.arch + dtb = dtb_full = target.dtb + if dtb: + dtb = os.path.basename(dtb) # hack for dtbs in subfolders + file_server_resource = bmeta['file_server_resource'] + job_px = file_server_resource.replace('/', '-') + url_px = file_server_resource + job_name = '-'.join([job_px, dtb or 'no-dtb', + target.name, plan_config.name]) + base_url = urlparse.urljoin(storage, '/'.join([url_px, ''])) + kernel_img = bmeta['kernel_image'] + kernel_url = urlparse.urljoin(storage, '/'.join([url_px, kernel_img])) + if dtb_full and dtb_full.endswith('.dtb'): + dtb_url = urlparse.urljoin( + storage, '/'.join([url_px, 'dtbs', dtb_full])) + platform = dtb.split('.')[0] + else: + dtb_url = None + platform = target.name + modules = bmeta.get('modules') + modules_url = ( + urlparse.urljoin(storage, '/'.join([url_px, modules])) + if modules else None + ) + rootfs = plan_config.rootfs + defconfig = bmeta['defconfig_full'] + defconfig_base = ''.join(defconfig.split('+')[:1]) + endian = 'big' if 'BIG_ENDIAN' in defconfig else 'little' + describe = bmeta['git_describe'] + + params = { + 'name': job_name, + 'dtb_url': dtb_url, + 'dtb_short': dtb, + 'dtb_full': dtb_full, + 'platform': platform, + 'mach': target.mach, + 'kernel_url': kernel_url, + 'image_type': 'kernel-ci', + 'image_url': base_url, + 'modules_url': modules_url, + 'plan': plan_config.base_name, + 'plan_variant': plan_config.name, + 'kernel': describe, + 'tree': bmeta['job'], + 'defconfig': defconfig, + 'arch_defconfig': '-'.join([arch, defconfig]), + 'fastboot': str(target.get_flag('fastboot')).lower(), + 'device_type': target.name, + 'base_device_type': target.base_name, + 'base_url': base_url, + 'endian': endian, + 'arch': arch, + 'git_branch': bmeta['git_branch'], + 'git_commit': bmeta['git_commit'], + 'git_describe': describe, + 'git_url': bmeta['git_url'], + 'defconfig_base': defconfig_base, + 'initrd_url': rootfs.get_url('ramdisk', arch, endian), + 'kernel_image': kernel_img, + 'nfsrootfs_url': rootfs.get_url('nfs', arch, endian), + 'context': target.context, + 'rootfs_prompt': rootfs.prompt, + 'file_server_resource': file_server_resource, + 'build_environment': bmeta['build_environment'], + } + + params.update(plan_config.params) + params.update(target.params) + + return params diff --git a/lab-configs.yaml b/lab-configs.yaml new file mode 100644 index 0000000..0989808 --- /dev/null +++ b/lab-configs.yaml @@ -0,0 +1,124 @@ +labs: + + # ToDo: also run jobs with callbacks sent to BayLibre's KernelCI backend + lab-baylibre: + lab_type: lava + url: 'http://lava.baylibre.com:10080/RPC2/' + filters: + - blacklist: {tree: [drm-tip]} + - whitelist: + plan: + - baseline + - baseline-fastboot + - boot + - kselftest + + lab-clabbe: + lab_type: lava + url: 'https://lava.montjoie.ovh/RPC2/' + filters: + - whitelist: + plan: + - baseline + - boot + - boot-nfs + - simple + - sleep + tree: + - kernelci + - next + - stable-rc + + lab-collabora: + lab_type: lava + url: 'https://lava.collabora.co.uk/RPC2/' + filters: + - blacklist: {tree: [android]} + - whitelist: + plan: + - baseline + - boot + - cros-ec + - igt-drm-kms + - sleep + - usb + - v4l2-compliance-uvc + - v4l2-compliance-vivid + + lab-drue: + lab_type: lava + url: 'https://lava.therub.org/RPC2/' + filters: + - whitelist: + plan: + - baseline + - boot + - boot-nfs + - simple + - sleep + + lab-free-electrons: + lab_type: lava + url: 'https://lab.free-electrons.com/RPC2/' + filters: + - blacklist: {tree: [drm-tip, linaro-android]} + - whitelist: + plan: + - baseline + - boot + - boot-nfs + + lab-linaro-lkft: + lab_type: lava + url: 'https://lkft.validation.linaro.org/RPC2/' + priority: low + filters: + - combination: + keys: ['tree', 'branch'] + values: + - ['stable', 'linux-4.4.y'] + - ['stable', 'linux-4.9.y'] + - ['stable', 'linux-4.14.y'] + - ['stable-rc', 'linux-4.4.y'] + - ['stable-rc', 'linux-4.9.y'] + - ['stable-rc', 'linux-4.14.y'] + - ['next', 'master'] + - ['next', 'pending-fixes'] + - ['kernelci', 'kernelci.org'] + - ['kernelci', 'staging.kernelci.org'] + - whitelist: + plan: + - baseline + - baseline-uefi + - boot + + lab-mhart: + lab_type: lava + url: 'http://lava.streamtester.net/RPC2/' + filters: + - blacklist: {tree: ['android', 'drm-tip', 'linaro-android']} + - whitelist: + plan: + - baseline + - boot + - boot-nfs + - simple + + lab-pengutronix: + lab_type: lava + url: 'https://hekla.openlab.pengutronix.de/RPC2/' + filters: + - whitelist: + plan: + - baseline + - boot + + lab-theobroma-systems: + lab_type: lava + url: 'https://lava.theobroma-systems.com/RPC2/' + filters: + - whitelist: + plan: + - baseline + - boot + - kselftest diff --git a/lava-v2-jobs-from-api.py b/lava-v2-jobs-from-api.py index c28bdd7..c809901 100755 --- a/lava-v2-jobs-from-api.py +++ b/lava-v2-jobs-from-api.py @@ -72,7 +72,7 @@ def get_builds(api, token, config): return builds -def add_callback_params(params, config, plan): +def add_callback_params(params, config, plan_config): callback = config.get('callback') if not callback: return @@ -80,7 +80,7 @@ def add_callback_params(params, config, plan): callback_type = config.get('callback_type') if callback_type == 'kernelci': - lava_cb = 'boot' if plan == 'boot' else 'test' + lava_cb = 'boot' if plan_config.base_name == 'boot' else 'test' params['callback_name'] = '/'.join(['lava', lava_cb]) params.update({ @@ -91,8 +91,8 @@ def add_callback_params(params, config, plan): }) -def get_job_params(config, test_config, defconfig, opts, build, plan): - short_template_file = test_config.get_template_path(plan) +def get_job_params(config, test_config, defconfig, opts, build, plan_config): + short_template_file = test_config.get_template_path(plan_config.name) template_file = os.path.join('templates', short_template_file) if not os.path.exists(template_file): print("Template not found: {}".format(template_file)) @@ -101,13 +101,14 @@ def get_job_params(config, test_config, defconfig, opts, build, plan): arch = config.get('arch') storage = config.get('storage') device_type = test_config.device_type - test_plan = test_config.test_plans[plan] defconfig_base = ''.join(defconfig.split('+')[:1]) dtb = dtb_full = opts['dtb_full'] = opts['dtb'] = device_type.dtb # hack for arm64 dtbs in subfolders if arch == 'arm64' and dtb: dtb = opts['dtb'] = os.path.basename(dtb) + if arch == 'riscv' and dtb: + dtb = opts['dtb'] = os.path.basename(dtb) file_server_resource = build.get('file_server_resource') if file_server_resource: @@ -119,7 +120,7 @@ def get_job_params(config, test_config, defconfig, opts, build, plan): url_px = '/'.join(parts) job_name = '-'.join([job_name_prefix, dtb or 'no-dtb', - device_type.name, plan]) + device_type.name, plan_config.name]) base_url = urlparse.urljoin(storage, '/'.join([url_px, ''])) kernel_url = urlparse.urljoin( @@ -137,7 +138,7 @@ def get_job_params(config, test_config, defconfig, opts, build, plan): else: modules_url = None - rootfs = test_plan.rootfs + rootfs = plan_config.rootfs job_params = { 'name': job_name, @@ -150,7 +151,7 @@ def get_job_params(config, test_config, defconfig, opts, build, plan): 'image_type': 'kernel-ci', 'image_url': base_url, 'modules_url': modules_url, - 'plan': plan, + 'plan': plan_config.base_name, 'kernel': config.get('describe'), 'tree': config.get('tree'), 'defconfig': defconfig, @@ -175,19 +176,18 @@ def get_job_params(config, test_config, defconfig, opts, build, plan): 'lab_name': config.get('lab'), 'context': device_type.context, 'rootfs_prompt': rootfs.prompt, - 'plan_name': test_plan.name, 'file_server_resource': file_server_resource, 'build_environment': build.get('build_environment'), } - job_params.update(test_plan.params) + job_params.update(plan_config.params) job_params.update(device_type.params) - add_callback_params(job_params, config, plan) + add_callback_params(job_params, config, plan_config) return job_params -def add_jobs(jobs, config, tests, opts, build, plan, arch, defconfig): +def add_jobs(jobs, config, tests, opts, build, plan_config, arch, defconfig): filters = { 'arch': arch, 'defconfig': defconfig, @@ -207,7 +207,7 @@ def add_jobs(jobs, config, tests, opts, build, plan, arch, defconfig): print("device not in targets: {}".format( test_config.device_type, targets)) continue - if not test_config.match(arch, plan, flags, filters): + if not test_config.match(arch, flags, filters, plan_config.name): print("test config did not match: {}".format( test_config.device_type)) continue @@ -216,12 +216,12 @@ def add_jobs(jobs, config, tests, opts, build, plan, arch, defconfig): print("dtb not in builds: {}".format(dtb)) continue job_params = get_job_params( - config, test_config, defconfig, opts, build, plan) + config, test_config, defconfig, opts, build, plan_config) if job_params: jobs.append(job_params) -def get_jobs_from_builds(config, builds, tests): +def get_jobs_from_builds(config, builds, tests, plans): arch = config.get('arch') cwd = os.getcwd() jobs = [] @@ -233,7 +233,7 @@ def get_jobs_from_builds(config, builds, tests): defconfig = build['defconfig_full'] print("Working on build: {}".format(build.get('file_server_resource'))) - for plan in config.get('plans'): + for plan in plans: opts = { 'arch_defconfig': '-'.join([arch, defconfig]), 'endian': 'big' if 'BIG_ENDIAN' in defconfig else 'little', @@ -284,10 +284,19 @@ def main(args): print("Number of builds: {}".format(len(builds))) config_data = kernelci.config.test.from_yaml(config.get('test_configs')) + + plan_configs = config_data['test_plans'] + base_plans = config.get('plans') + plans = list( + plan_config + for plan_config in plan_configs.values() + if plan_config.base_name in base_plans + ) + tests = config_data['test_configs'] print("Number of test configs: {}".format(len(tests))) - jobs = get_jobs_from_builds(config, builds, tests) + jobs = get_jobs_from_builds(config, builds, tests, plans) print("Number of jobs: {}".format(len(jobs))) write_jobs(config, jobs) diff --git a/src/org/kernelci/build/Kernel.groovy b/src/org/kernelci/build/Kernel.groovy deleted file mode 100644 index e156546..0000000 --- a/src/org/kernelci/build/Kernel.groovy +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 2018 Collabora Limited - Author: Guillaume Tucker <guillaume.tucker@collabora.com> - - This module is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1 of the License, or (at your option) - any later version. - - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License - along with this library; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - - -package org.kernelci.build - -def downloadTarball(kdir, url, filename="linux-src.tar.gz") { - sh(script: "rm -rf ${kdir}") - dir(kdir) { - /* Storing the tarball is done asynchronously on the server, so it may - * not be ready by the time we try to download it. Give it a - * minute by retrying periodically. */ - def retry_sleep_s = 5 - def retries = 12 - def downloaded = false - - while (!downloaded) { - def stat = sh(returnStatus: true, - script: "\ -wget \ ---no-hsts \ ---progress=dot:giga \ ---retry-connrefused \ ---waitretry=5 \ ---read-timeout=20 \ ---timeout=15 \ ---tries 20 \ ---continue \ --O ${filename} \ -${url}") - - if (!stat) { - downloaded = true - } else { - retries -= 1 - - /* Only retry if server returned an HTTP error (status 8) */ - if (!retries || (stat != 8)) { - throw new hudson.AbortException( - "Failed to download tarball") - } - - echo "retries: ${retries}" - sleep(time: retry_sleep_s, unit: 'SECONDS') - } - } - - sh(script: "tar xzf ${filename}") - } -} diff --git a/templates/base/kernel-ci-base.jinja2 b/templates/base/kernel-ci-base.jinja2 index 99babae..c6a08da 100644 --- a/templates/base/kernel-ci-base.jinja2 +++ b/templates/base/kernel-ci-base.jinja2 @@ -16,6 +16,7 @@ metadata: platform.name: {{ platform }} platform.mach: {{ mach }} test.plan: {{ plan }} + test.plan_variant: {{ plan_variant }} git.commit: {{ git_commit }} git.describe: {{ git_describe }} git.branch: {{ git_branch }} diff --git a/templates/baseline/baseline.jinja2 b/templates/baseline/baseline.jinja2 index d514f28..e0b856a 100644 --- a/templates/baseline/baseline.jinja2 +++ b/templates/baseline/baseline.jinja2 @@ -1,6 +1,48 @@ - test: +{%- if test_namespace %} + namespace: {{ test_namespace }} +{%- endif %} timeout: - minutes: 10 + minutes: 1 + definitions: + - repository: + metadata: + format: Lava-Test Test Definition 1.0 + name: baseline + description: "baseline test plan" + os: + - debian + scope: + - functional + environment: + - lava-test-shell + run: + steps: + - > + for level in warn err; do + dmesg --level=$level --notime -x -k > dmesg.$level + done + - > + for level in crit alert emerg; do + dmesg --level=$level --notime -x -k > dmesg.$level + test -s dmesg.$level && res=fail || res=pass + count=$(cat dmesg.$level | wc -l) + lava-test-case $level \ + --result $res \ + --measurement $count \ + --units lines + done + - cat dmesg.emerg dmesg.alert dmesg.crit dmesg.err dmesg.warn + from: inline + name: dmesg + path: inline/dmesg.yaml + +- test: +{%- if test_namespace %} + namespace: {{ test_namespace }} +{%- endif %} + timeout: + minutes: 1 definitions: - repository: metadata: diff --git a/templates/baseline/generic-fastboot-baseline-template.jinja2 b/templates/baseline/generic-fastboot-baseline-template.jinja2 new file mode 100644 index 0000000..691d33e --- /dev/null +++ b/templates/baseline/generic-fastboot-baseline-template.jinja2 @@ -0,0 +1,9 @@ +{% extends 'boot-fastboot/generic-fastboot-boot-template.jinja2' %} +{% block actions %} +{{ super () }} + +{% set test_namespace = "target" %} + +{% include 'baseline/baseline.jinja2' %} + +{% endblock %} diff --git a/templates/boot-fastboot/generic-fastboot-boot-template.jinja2 b/templates/boot-fastboot/generic-fastboot-boot-template.jinja2 new file mode 100644 index 0000000..d91f925 --- /dev/null +++ b/templates/boot-fastboot/generic-fastboot-boot-template.jinja2 @@ -0,0 +1,95 @@ +{%- extends 'base/kernel-ci-base.jinja2' %} +{% block metadata %} +{{ super() }} +{% endblock %} +{% block main %} +{{ super() }} +tags: + - fastboot +protocols: + lava-lxc: + name: lxc-{{ platform }}-kernelci-fastboot + template: debian + distribution: debian + release: buster +{% endblock %} +{% block actions %} +actions: +{%- block deploy_lxc %} + +- deploy: + namespace: host + timeout: + minutes: 15 + to: lxc + packages: + - android-tools-adb + - android-tools-fastboot + - cpio + - mkbootimg +{%- endblock %} +{%- block boot_lxc %} + +- boot: + namespace: host + prompts: + - 'root@(.*):/#' + timeout: + minutes: 5 + method: lxc +{%- endblock %} +{%- block deploy_url %} + +- deploy: + timeout: + minutes: 40 + namespace: target + to: download + images: + kernel: + url: {{ kernel_url }} +{%- block kernel_image_type %} + type: {{ kernel_image.lower() }} +{%- endblock %} +{%- if initrd_url %} + ramdisk: + url: {{ initrd_url }} +{%- endif %} +{%- if modules_url %} + modules: + url: {{ modules_url }} +{%- endif %} +{%- if dtb_url %} + dtb: + url: {{ dtb_url }} +{%- endif %} +{%- endblock %} +{%- block test_lxc %} + +{% include "/boot-fastboot/mkbootimg.jinja2" %} +{%- endblock %} +{%- block deploy_boot %} + +- deploy: + timeout: + minutes: 40 + to: fastboot + namespace: target + images: + boot: + url: lxc:///boot.img +{%- endblock %} +{%- block boot %} + +- boot: + namespace: target + prompts: + - '/ #' + timeout: + minutes: 15 + method: fastboot + transfer_overlay: + download_command: udhcpc ; ip addr show ; wget + unpack_command: tar -C / -zxvpf +{%- endblock %} +{% endblock %} diff --git a/templates/boot-fastboot/mkbootimg.jinja2 b/templates/boot-fastboot/mkbootimg.jinja2 new file mode 100644 index 0000000..988f04b --- /dev/null +++ b/templates/boot-fastboot/mkbootimg.jinja2 @@ -0,0 +1,20 @@ +- test: + namespace: host + timeout: + minutes: 30 + definitions: + - from: inline + name: {{ device_type }}-images + path: inline/{{ device_type }}-images.yaml + repository: + metadata: + description: {{ device_type }} images + format: Lava-Test Test Definition 1.0 + name: {{ device_type }}-images + run: + steps: + - cd /lava-lxc + - tar xf modules.tar.xz + - gunzip rootfs.cpio.gz + - find lib/ | cpio -o --format=newc --append -F rootfs.cpio + - mkbootimg --kernel {{ kernel_image }} --second {{ dtb_short }} --ramdisk rootfs.cpio --cmdline {{ cmdline }} --kernel_offset {{ kernel_addr }} --ramdisk_offset {{ ramdisk_addr }} --second_offset {{ dtb_addr }} -o boot.img diff --git a/templates/igt/igt.jinja2 b/templates/igt/igt.jinja2 index 67cef60..f9a0eb9 100644 --- a/templates/igt/igt.jinja2 +++ b/templates/igt/igt.jinja2 @@ -5,7 +5,7 @@ - repository: metadata: format: Lava-Test Test Definition 1.0 - name: {{ plan_name }} + name: {{ plan }} description: "IGT test plan" os: - oe @@ -17,6 +17,6 @@ /usr/bin/igt-parser.sh {{ tests|wordwrap(1, False, "\n ") }} from: inline - name: {{ plan_name }} - path: inline/{{ plan_name }}.yaml + name: {{ plan }} + path: inline/{{ plan }}.yaml lava-signal: kmsg diff --git a/templates/v4l2-compliance/v4l2-compliance.jinja2 b/templates/v4l2-compliance/v4l2-compliance.jinja2 index 2260aeb..ec521ca 100644 --- a/templates/v4l2-compliance/v4l2-compliance.jinja2 +++ b/templates/v4l2-compliance/v4l2-compliance.jinja2 @@ -5,7 +5,7 @@ - repository: metadata: format: Lava-Test Test Definition 1.0 - name: v4l2 + name: {{ plan }} description: "v4l2 test plan" os: - debian @@ -15,5 +15,5 @@ steps: - /usr/bin/v4l2-parser.sh {{ v4l2_driver }} from: inline - name: {{ plan_name }} - path: inline/v4l2.yaml + name: {{ plan }} + path: inline/{{ plan }}.yaml diff --git a/test-configs.yaml b/test-configs.yaml index cd01e99..7683b1c 100644 --- a/test-configs.yaml +++ b/test-configs.yaml @@ -5,7 +5,7 @@ file_system_types: buildroot: - url: 'https://storage.kernelci.org/images/rootfs/buildroot/kci-2019.02-2-gefc755ba4a02' + url: 'https://storage.kernelci.org/images/rootfs/buildroot/kci-2019.02-8-gd700ebb99e8f' arch_map: arm64be: [{arch: arm64, endian: big}] @@ -41,15 +41,15 @@ file_systems: debian_buster_ramdisk: type: debian - ramdisk: 'buster/20190830.0/{arch}/rootfs.cpio.gz' + ramdisk: 'buster/20191115.0/{arch}/rootfs.cpio.gz' debian_buster-igt_ramdisk: type: debian - ramdisk: 'buster-igt/20190830.0/{arch}/rootfs.cpio.gz' + ramdisk: 'buster-igt/20191025.0/{arch}/rootfs.cpio.gz' - debian_stretch-v4l2_ramdisk: + debian_buster-v4l2_ramdisk: type: debian - ramdisk: 'stretch-v4l2/20190830.0/{arch}/rootfs.cpio.gz' + ramdisk: 'buster-v4l2/20191115.0/{arch}/rootfs.cpio.gz' test_plan_default_filters: @@ -79,13 +79,60 @@ test_plans: filters: - blacklist: *kselftest_defconfig_clang_environment_filter + baseline_qemu: + base_name: baseline + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + filters: + - blacklist: *kselftest_defconfig_clang_environment_filter + + baseline-fastboot: + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-fastboot-baseline-template.jinja2' + params: + job_timeout: '50' + + baseline-uefi_arm: + base_name: baseline-uefi + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + params: + bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/QEMU_EFI.fd-ARM-RELEASE-111bbcf87621' + + baseline-uefi_arm64: + base_name: baseline-uefi + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + params: + bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/QEMU_EFI.fd-AARCH64-RELEASE-111bbcf87621' + + baseline-uefi_x86_64: + base_name: baseline-uefi + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + params: + bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-X64-RELEASE-111bbcf87621' + + baseline-uefi_i386: + base_name: baseline-uefi + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + params: + bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-IA32-RELEASE-111bbcf87621' + + baseline-uefi_x86-mixed: + base_name: baseline-uefi + rootfs: buildroot_baseline_ramdisk + pattern: 'baseline/generic-qemu-baseline-template.jinja2' + params: + bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-IA32X64-RELEASE-111bbcf87621' + boot: rootfs: buildroot_ramdisk filters: - blacklist: *kselftest_defconfig_clang_environment_filter - boot_nfs: - name: 'boot-nfs' + boot-nfs: rootfs: buildroot_nfs pattern: 'boot-nfs/{category}-{method}-{protocol}-nfs-template.jinja2' filters: @@ -108,7 +155,7 @@ test_plans: - blacklist: *clang_environment_filter boot_qemu: - name: boot + base_name: boot rootfs: buildroot_ramdisk pattern: 'boot/generic-qemu-boot-template.jinja2' filters: @@ -117,7 +164,7 @@ test_plans: cros-ec: rootfs: debian_buster_ramdisk - igt_drm_kms: + igt-drm-kms: rootfs: debian_buster-igt_ramdisk pattern: 'igt/{category}-{method}-{protocol}-{rootfs}-igt.jinja2' params: @@ -143,56 +190,24 @@ test_plans: rootfs: debian_buster_ramdisk simple_qemu: - name: simple + base_name: simple rootfs: debian_buster_ramdisk pattern: 'simple/generic-qemu-simple-template.jinja2' sleep: rootfs: debian_buster_ramdisk - uefi_arm: - rootfs: buildroot_ramdisk - pattern: 'boot/generic-qemu-boot-template.jinja2' - params: - bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/QEMU_EFI.fd-ARM-RELEASE-111bbcf87621' - - uefi_arm64: - rootfs: buildroot_ramdisk - pattern: 'boot/generic-qemu-boot-template.jinja2' - params: - bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/QEMU_EFI.fd-AARCH64-RELEASE-111bbcf87621' - - uefi_x86_64: - rootfs: buildroot_ramdisk - pattern: 'boot/generic-qemu-boot-template.jinja2' - params: - bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-X64-RELEASE-111bbcf87621' - - uefi_i386: - rootfs: buildroot_ramdisk - pattern: 'boot/generic-qemu-boot-template.jinja2' - params: - bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-IA32-RELEASE-111bbcf87621' - - uefi_x86-mixed: - rootfs: buildroot_ramdisk - pattern: 'boot/generic-qemu-boot-template.jinja2' - params: - bios_url: 'https://storage.kernelci.org/images/uefi/111bbcf87621/OVMF.fd-IA32X64-RELEASE-111bbcf87621' - usb: rootfs: debian_buster_ramdisk - v4l2_compliance_uvc: - name: v4l2-compliance-uvc - rootfs: debian_stretch-v4l2_ramdisk + v4l2-compliance-uvc: + rootfs: debian_buster-v4l2_ramdisk # FIXME - this is not very sustainable, improve template naming scheme pattern: 'v4l2-compliance/{category}-{method}-{protocol}-{rootfs}-v4l2-compliance-template.jinja2' params: {v4l2_driver: "uvcvideo"} - v4l2_compliance_qemu_vivid: - name: v4l2-compliance-vivid - rootfs: debian_stretch-v4l2_ramdisk + v4l2-compliance-vivid: + rootfs: debian_buster-v4l2_ramdisk pattern: 'v4l2-compliance/generic-qemu-v4l2-compliance-template.jinja2' params: {v4l2_driver: "vivid"} filters: @@ -437,6 +452,16 @@ device_types: class: arm64-dtb boot_method: uboot + hifive-unleashed-a00: + mach: sifive + class: riscv-dtb + boot_method: uboot + filters: + # no console in tinyconfig + - blacklist: + defconfig: ['tinyconfig'] + kernel: ['v4.', 'v5.1', 'v5.2'] + hsdk: mach: arc class: arc-dtb @@ -532,6 +557,11 @@ device_types: - 'multi_v7_defconfig+kselftest' kernel: ['v3.18'] + imx8mn-ddr4-evk: + mach: freescale + class: arm64-dtb + boot_method: uboot + jetson-tk1: mach: tegra class: arm-dtb @@ -578,6 +608,12 @@ device_types: boot_method: uboot filters: - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} + flags: ['fastboot'] + params: + kernel_addr: '0x01080000' + dtb_addr: '0x1000000' + ramdisk_addr: '0x6000000' + cmdline: '"console=ttyAML0,115200 consoleblank=0 earlycon"' meson-g12a-u200: mach: amlogic @@ -590,6 +626,7 @@ device_types: mach: amlogic class: arm64-dtb boot_method: uboot + flags: ['fastboot'] filters: - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} @@ -600,6 +637,13 @@ device_types: filters: - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} + meson-g12b-a311d-khadas-vim3: + mach: amlogic + class: arm64-dtb + boot_method: uboot + filters: + - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} + meson-gxbb-nanopi-k2: mach: amlogic class: arm64-dtb @@ -672,6 +716,14 @@ device_types: filters: - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} + meson-gxm-q200: + mach: amlogic + class: arm64-dtb + boot_method: uboot + flags: ['big_endian'] + filters: + - blacklist: {defconfig: ['allnoconfig', 'allmodconfig']} + minnowboard-turbot-E3826: mach: x86 arch: x86_64 @@ -767,7 +819,7 @@ device_types: guestfs_interface: 'virtio' machine: 'virt,gic-version=2' filters: - - whitelist: {defconfig: ['multi_v7_defconfig', 'vexpress_defconfig']} + - whitelist: {defconfig: ['multi_v7_defconfig']} qemu_arm-virt-gicv3: base_name: qemu @@ -780,7 +832,7 @@ device_types: guestfs_interface: 'virtio' machine: 'virt,gic-version=3' filters: - - whitelist: {defconfig: ['multi_v7_defconfig', 'vexpress_defconfig']} + - whitelist: {defconfig: ['multi_v7_defconfig']} qemu_arm64-virt-gicv2: base_name: qemu @@ -900,6 +952,11 @@ device_types: - blacklist: *allmodconfig_filter - blacklist: {defconfig: ['multi_v7_defconfig+CONFIG_SMP=n']} + sun4i-a10-olinuxino-lime: + mach: allwinner + class: arm-dtb + boot_method: uboot + sun50i-a64-bananapi-m64: mach: allwinner class: arm64-dtb @@ -943,6 +1000,11 @@ device_types: class: arm-dtb boot_method: uboot + sun7i-a20-olinuxino-lime2: + mach: allwinner + class: arm-dtb + boot_method: uboot + sun8i-a23-evb: mach: allwinner class: arm-dtb @@ -1072,86 +1134,125 @@ device_types: test_configs: - device_type: alpine-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: am57xx-beagle-x15 test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - simple - sleep - device_type: apq8016-sbc test_plans: + - baseline - boot - kselftest - device_type: ar9331-dpt-module test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: armada-370-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-370-rd - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-375-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-385-db-ap - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-388-clearfog - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-388-gp - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-398-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-3720-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-7040-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-8040-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-xp-db - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-xp-gp - test_plans: [boot_nfs] + test_plans: + - baseline + - boot-nfs - device_type: armada-xp-linksys-mamba - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: armada-xp-openblocks-ax3-4 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: arndale test_plans: [boot, kselftest] - device_type: at91-sama5d2-xplained - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: at91-sama5d4-xplained - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: bcm2836-rpi-2-b test_plans: + - baseline - boot - sleep - usb - device_type: bcm2837-rpi-3-b test_plans: + - baseline - boot - kselftest - simple @@ -1159,218 +1260,332 @@ test_configs: - device_type: bcm2837-rpi-3-b-32 test_plans: + - baseline - boot - kselftest - simple - device_type: beaglebone-black test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - simple - device_type: beagle-xm - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: cubietruck - test_plans: [boot, boot_nfs] + test_plans: + - baseline + - boot + - boot-nfs - device_type: d03 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: da850-lcdk - test_plans: [boot] - - - device_type: dra7-evm test_plans: + - baseline - boot - - kselftest - - simple - device_type: dove-cubox test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep + - device_type: dra7-evm + test_plans: + - baseline + - boot + - kselftest + - simple + - device_type: hi6220-hikey test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - simple - device_type: hip07-d05 - test_plans: [boot, boot_nfs] + test_plans: + - baseline + - boot + - boot-nfs + + - device_type: hifive-unleashed-a00 + test_plans: + - baseline + - boot - device_type: hsdk - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: imx23-olinuxino test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx27-phytec-phycard-s-rdk test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx28-duckbill test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx53-qsrb test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx6dl-riotboard test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx6q-nitrogen6x - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: imx6ul-pico-hobbit test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: imx6q-sabrelite test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - usb + - device_type: imx8mn-ddr4-evk + test_plans: + - baseline + - boot + - boot-nfs + - kselftest + - device_type: jetson-tk1 - test_plans: [boot, boot_nfs, kselftest] + test_plans: + - baseline + - boot + - boot-nfs + - kselftest - device_type: kirkwood-db-88f6282 - test_plans: [boot_nfs] + test_plans: + - baseline + - boot-nfs - device_type: kirkwood-openblocks_a7 - test_plans: [boot, boot_nfs] + test_plans: + - baseline + - boot + - boot-nfs - device_type: meson8b-odroidc1 - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: meson-g12a-sei510 - test_plans: [boot, simple] + test_plans: + - baseline + - baseline-fastboot + - boot + - simple - device_type: meson-g12a-u200 - test_plans: [boot, simple] + test_plans: + - baseline + - boot + - simple - device_type: meson-g12a-x96-max - test_plans: [boot, simple] + test_plans: + - baseline + - boot + - simple - device_type: meson-g12b-odroid-n2 - test_plans: [boot, simple] + test_plans: + - baseline + - boot + - simple + + - device_type: meson-g12b-a311d-khadas-vim3 + test_plans: + - baseline + - boot + - boot-nfs + - kselftest + - simple - device_type: meson-gxbb-nanopi-k2 test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxbb-odroidc2 - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: meson-gxbb-p200 - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: meson-gxl-s805x-libretech-ac test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxl-s905d-p230 test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxl-s805x-p241 test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxl-s905x-khadas-vim test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxl-s905x-libretech-cc test_plans: + - baseline - boot - kselftest - simple - device_type: meson-gxm-khadas-vim2 test_plans: + - baseline + - boot + - boot-nfs + - kselftest + - simple + + - device_type: meson-gxm-q200 + test_plans: + - baseline - boot + - boot-nfs - kselftest - simple - - boot_nfs - device_type: minnowboard-turbot-E3826 test_plans: + - baseline - boot - simple - device_type: mustang test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - device_type: odroid-x2 - test_plans: [boot, boot_nfs, kselftest] + test_plans: + - baseline + - boot + - boot-nfs + - kselftest - device_type: odroid-xu3 test_plans: + - baseline - boot - - boot_nfs - - igt_drm_kms + - boot-nfs + - igt-drm-kms - kselftest - sleep - usb - device_type: orion5x-rd88f5182-nas - test_plans: [boot_nfs] + test_plans: + - baseline + - boot-nfs - device_type: ox820-cloudengines-pogoplug-series-3 - test_plans: [boot, boot_nfs] + test_plans: + - baseline + - boot + - boot-nfs - device_type: panda - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: panda-es - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: peach-pi test_plans: + - baseline - boot - cros-ec - kselftest @@ -1378,58 +1593,70 @@ test_configs: - usb - device_type: qcom-qdf2400 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: qemu_arm-virt-gicv2 test_plans: + - baseline_qemu + - baseline-uefi_arm - boot_qemu - - uefi_arm - device_type: qemu_arm-virt-gicv3 test_plans: + - baseline_qemu + - baseline-uefi_arm - boot_qemu - simple_qemu - - uefi_arm - - v4l2_compliance_qemu_vivid + - v4l2-compliance-vivid - device_type: qemu_arm64-virt-gicv2 test_plans: + - baseline_qemu + - baseline-uefi_arm64 - boot_qemu - - uefi_arm64 - device_type: qemu_arm64-virt-gicv3 test_plans: + - baseline_qemu + - baseline-uefi_arm64 - boot_qemu - simple_qemu - - uefi_arm64 - - v4l2_compliance_qemu_vivid + - v4l2-compliance-vivid - device_type: qemu_i386 test_plans: - - boot - - uefi_i386 + - baseline_qemu + - baseline-uefi_i386 + - boot_qemu - simple_qemu - device_type: qemu_x86_64 test_plans: + - baseline_qemu + - baseline-uefi_x86_64 + - baseline-uefi_x86-mixed - boot_qemu - simple_qemu - - uefi_x86_64 - - uefi_x86-mixed - - v4l2_compliance_qemu_vivid + - v4l2-compliance-vivid - device_type: r8a7791-porter - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: r8a7795-salvator-x test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - simple - device_type: r8a7796-m3ulcb test_plans: + - baseline - boot - simple - usb @@ -1439,140 +1666,214 @@ test_configs: - baseline - boot - cros-ec - - v4l2_compliance_uvc + - sleep + - v4l2-compliance-uvc - device_type: rk3399-gru-kevin - test_plans: [igt_drm_kms] + test_plans: [igt-drm-kms] filters: - blacklist: {kernel: ['v3.', 'v4.4', 'v4.9', 'v4.14']} - device_type: rk3399-puma-haikou - test_plans: [boot, kselftest] + test_plans: + - baseline + - boot + - kselftest - device_type: rk3288-rock2-square test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - usb - device_type: rk3328-rock64 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: rk3288-veyron-jaq test_plans: - baseline - boot - - boot_nfs + - boot-nfs - cros-ec - - igt_drm_kms + - igt-drm-kms - sleep - usb - - v4l2_compliance_uvc + - v4l2-compliance-uvc - device_type: socfpga-cyclone5-socrates test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - sleep - device_type: snow test_plans: + - baseline + - boot + - boot-nfs + - kselftest + + - device_type: sun4i-a10-olinuxino-lime + test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest + - simple - device_type: sun50i-a64-bananapi-m64 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun50i-h5-libretech-all-h3-cc - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun50i-h6-orangepi-one-plus - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun50i-h6-pine-h64 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun5i-a13-olinuxino-micro - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun5i-gr8-chip - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun5i-gr8-chip-pro - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun7i-a20-cubieboard2 - test_plans: [boot] + test_plans: + - baseline + - boot + + - device_type: sun7i-a20-olinuxino-lime2 + test_plans: + - baseline + - boot + - boot-nfs + - kselftest + - simple - device_type: sun8i-a23-evb - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-a33-sinlinx-sina33 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-a33-olinuxino - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-a83t-bananapi-m3 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-a83t-allwinner-h8homlet-v2 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-h2-plus-orangepi-zero - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-h2-plus-orangepi-r1 - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-h2-plus-libretech-all-h3-cc - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-h3-libretech-all-h3-cc - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: sun8i-h3-orangepi-pc - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: synquacer-acpi - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: synquacer-dtb - test_plans: [boot] + test_plans: + - baseline + - boot - device_type: tegra124-nyan-big test_plans: + - baseline - boot - cros-ec - - igt_drm_kms + - igt-drm-kms - sleep - usb - device_type: x86-atom330 test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - kselftest - simple - device_type: x86-celeron test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - simple - device_type: x86-pentium4 test_plans: + - baseline - boot - - boot_nfs + - boot-nfs - simple - device_type: x86-x5-z8350 - test_plans: [boot, boot_nfs] + test_plans: + - baseline + - boot + - boot-nfs - device_type: zynq-zc702 - test_plans: [boot] + test_plans: + - baseline + - boot |