Shaker CVP tests
Pipeline for running Shaker network testing
against Openstack cluster
Change-Id: Ib1c02f4b5930a0658126cd9d9953f0876542b11c
Related-task: https://mirantis.jira.com/browse/PROD-24883
diff --git a/cvp-shaker.groovy b/cvp-shaker.groovy
new file mode 100644
index 0000000..85201ff
--- /dev/null
+++ b/cvp-shaker.groovy
@@ -0,0 +1,157 @@
+/**
+ *
+ * Launch CVP Shaker network tests
+ *
+ * Expected parameters:
+
+ * SALT_MASTER_URL URL of Salt master
+ * SALT_MASTER_CREDENTIALS Credentials that are used in this Jenkins for accessing Salt master (usually "salt")
+ * IMAGE Docker image link to use for running container with Shaker.
+ * SHAKER_PARAMS Yaml context which contains parameters for running Shaker
+ *
+ */
+
+/*
+SHAKER_PARAMS yaml example:
+---
+ SHAKER_SERVER_ENDPOINT: '10.13.0.15:5999'
+ SHAKER_SCENARIOS: 'scenarios/essential'
+ SKIP_LIST: ''
+ image_builder:
+ - SHAKER_FLAVOR_DISK=4
+ - SHAKER_FLAVOR_RAM=512
+ - SHAKER_FLAVOR_VCPUS=1
+ - SHAKER_IMAGE_BUILDER_MODE='dib'
+ shaker:
+ - SHAKER_AGENT_JOIN_TIMEOUT=300
+ - SHAKER_AGENT_LOSS_TIMEOUT=120
+ - SCENARIO_AVAILABILITY_ZONE='nova,internal'
+ - SCENARIO_COMPUTE_NODES=2
+ - SHAKER_EXTERNAL_NET='public'
+
+Where:
+ "SHAKER_SERVER_ENDPOINT" - Address for Shaker server connections (host:port). Should be accessible
+ from tenant's VM network (usually equals to public address of cicd node)
+ "SHAKER_SCENARIOS" - Path to shaker scenarios in the cvp-shaker docker image
+ (can be directory or specific file). Main categories are
+ scenarios/essential/l2
+ scenarios/essential/l3
+ scenarios/additional/cross_az
+ scenarios/additional/external
+ scenarios/additional/qos
+ "SKIP_LIST" - Comma-separated list of Shaker scenarios to skip, directories or files inside scenarios/
+ of cvp-shaker, e.g. "dense_l2.yaml,full_l2.yaml,l3"
+ "image_builder" - shaker-image-builder env variables
+ SHAKER_FLAVOR_DISK=4
+ SHAKER_FLAVOR_RAM=512
+ SHAKER_FLAVOR_VCPUS=1
+ SHAKER_IMAGE_BUILDER_MODE='dib'
+ "shaker" - main shaker runner env variables
+ SHAKER_AGENT_JOIN_TIMEOUT=300
+ SHAKER_AGENT_LOSS_TIMEOUT=120
+ SCENARIO_AVAILABILITY_ZONE='nova,internal'
+ SCENARIO_COMPUTE_NODES=2
+ SHAKER_EXTERNAL_NET='public'
+For the more detailed description of the last two categories please refer to the shaker documentation
+https://pyshaker.readthedocs.io/en/latest/tools.html
+*/
+
+common = new com.mirantis.mk.Common()
+salt = new com.mirantis.mk.Salt()
+validate = new com.mirantis.mcp.Validate()
+salt_testing = new com.mirantis.mk.SaltModelTesting()
+
+
+def IMAGE = (env.getProperty('IMAGE')) ?: 'docker-prod-local.docker.mirantis.net/mirantis/cvp/cvp-shaker:proposed'
+def SLAVE_NODE = (env.getProperty('SLAVE_NODE')) ?: 'docker'
+def SHAKER_PARAMS = readYaml(text: env.getProperty('SHAKER_PARAMS')) ?: [:]
+def artifacts_dir = 'validation_artifacts'
+def configRun = [:]
+
+node (SLAVE_NODE) {
+ try{
+ stage('Initialization') {
+ def workdir = '/opt/shaker/'
+ def container_artifacts = '/artifacts'
+ def html_file = "${container_artifacts}/shaker-report.html"
+ def log_file = "${container_artifacts}/shaker.log"
+ def cmd_shaker_args = "--debug --cleanup-on-error " +
+ "--log-file ${log_file} --report ${html_file}"
+
+ sh "mkdir -p ${artifacts_dir}"
+
+ // Get Openstack credentials
+ def saltMaster = salt.connection(SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
+ keystone_creds = validate._get_keystone_creds_v3(saltMaster)
+ if (!keystone_creds) {
+ keystone_creds = validate._get_keystone_creds_v2(saltMaster)
+ }
+
+ // Get shaker env variables
+ def general_params = [
+ SHAKER_SERVER_ENDPOINT: (SHAKER_PARAMS.get('SHAKER_SERVER_ENDPOINT')),
+ SHAKER_SCENARIOS: (SHAKER_PARAMS.get('SHAKER_SCENARIOS')) ?: 'scenarios/essential',
+ SKIP_LIST: (SHAKER_PARAMS.get('SKIP_LIST'))
+ ]
+ if (! general_params['SHAKER_SERVER_ENDPOINT']) {
+ throw new Exception("SHAKER_SERVER_ENDPOINT address was not set in the SHAKER_PARAMS")
+ }
+ def builder_vars = SHAKER_PARAMS.get("image_builder") ?: [
+ "SHAKER_FLAVOR_DISK=4",
+ "SHAKER_FLAVOR_RAM=512",
+ "SHAKER_FLAVOR_VCPUS=1"
+ ]
+ def shaker_vars = SHAKER_PARAMS.get("shaker") ?: []
+ def env_vars_list = general_params.collect{ "${it.key}=${it.value}" }
+ env_vars_list = env_vars_list + keystone_creds + builder_vars + shaker_vars
+
+ // Get shaker scenarios cmd
+ def scen_cmd = validate.bundle_up_scenarios(
+ workdir +
+ general_params['SHAKER_SCENARIOS'].replaceAll("^/+", ""),
+ general_params['SKIP_LIST']
+ )
+
+ // Define docker commands
+ def commands = [
+ '001_build_image': "shaker-image-builder --debug",
+ '002_run_shaker': scen_cmd + "-print0" +
+ "|paste -zsd ',' - " +
+ "|xargs --null " +
+ "shaker ${cmd_shaker_args} --scenario "
+ ]
+ def commands_list = commands.collectEntries{ [ (it.key) : { sh("${it.value}") } ] }
+
+ configRun = [
+ 'image': IMAGE,
+ 'baseRepoPreConfig': false,
+ 'dockerMaxCpus': 2,
+ // suppress sudo resolve warnings
+ // which break image build
+ 'dockerHostname': 'localhost',
+ 'dockerExtraOpts': [
+ "--network=host",
+ "--privileged",
+ "-v ${env.WORKSPACE}/${artifacts_dir}/:${container_artifacts}"
+ ],
+ 'envOpts' : env_vars_list,
+ 'runCommands' : commands_list
+ ]
+ }
+
+ stage('Run Shaker tests') {
+ salt_testing.setupDockerAndTest(configRun)
+ }
+
+ stage('Collect results') {
+ archiveArtifacts artifacts: "${artifacts_dir}/*"
+ }
+
+ } catch (Throwable e) {
+ // If there was an error or exception thrown, the build failed
+ currentBuild.result = "FAILURE"
+ throw e
+ } finally {
+ sh "rm -rf ${artifacts_dir}"
+ }
+}