blob: f144a587428b461a648b07436f5f989408bb22b6 [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
2
3/**
4 *
5 * Python functions
6 *
7 */
8
9/**
10 * Install python virtualenv
11 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000012 * @param path Path to virtualenv
13 * @param python Version of Python (python/python3)
14 * @param reqs Environment requirements in list format
15 * @param reqs_path Environment requirements path in str format
Sergey Kolekonovba203982016-12-21 18:32:17 +040016 */
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000017def setupVirtualenv(path, python = 'python2', reqs = [], reqs_path = null, clean = false, useSystemPackages = false) {
Tomáš Kukrál69c25452017-07-27 14:59:40 +020018 def common = new com.mirantis.mk.Common()
19
Jakub Josef87a8a3c2018-01-26 12:11:11 +010020 def offlineDeployment = env.getEnvironment().containsKey("OFFLINE_DEPLOYMENT") && env["OFFLINE_DEPLOYMENT"].toBoolean()
Mykyta Karpin1e4bfc92017-11-01 14:38:25 +020021 def virtualenv_cmd = "virtualenv ${path} --python ${python}"
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000022 if (useSystemPackages) {
Jakub Josef996f4ef2017-10-24 13:20:43 +020023 virtualenv_cmd += " --system-site-packages"
24 }
Tomáš Kukrál69c25452017-07-27 14:59:40 +020025 if (clean) {
26 common.infoMsg("Cleaning venv directory " + path)
27 sh("rm -rf \"${path}\"")
28 }
29
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000030 if (offlineDeployment) {
31 virtualenv_cmd += " --no-download"
Jakub Josef87a8a3c2018-01-26 12:11:11 +010032 }
Tomáš Kukrál69c25452017-07-27 14:59:40 +020033 common.infoMsg("[Python ${path}] Setup ${python} environment")
Sergey Kolekonovba203982016-12-21 18:32:17 +040034 sh(returnStdout: true, script: virtualenv_cmd)
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000035 if (!offlineDeployment) {
36 try {
37 runVirtualenvCommand(path, "pip install -U setuptools pip")
38 } catch (Exception e) {
39 common.warningMsg("Setuptools and pip cannot be updated, you might be offline but OFFLINE_DEPLOYMENT global property not initialized!")
40 }
Yuriy Taraday67352e92017-10-12 10:54:23 +000041 }
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000042 if (reqs_path == null) {
Vladislav Naumov11103862017-07-19 17:02:39 +030043 def args = ""
44 for (req in reqs) {
45 args = args + "${req}\n"
46 }
47 writeFile file: "${path}/requirements.txt", text: args
48 reqs_path = "${path}/requirements.txt"
Sergey Kolekonovba203982016-12-21 18:32:17 +040049 }
Jakub Josefa2491ad2018-01-15 16:26:27 +010050 runVirtualenvCommand(path, "pip install -r ${reqs_path}", true)
Sergey Kolekonovba203982016-12-21 18:32:17 +040051}
52
53/**
54 * Run command in specific python virtualenv
55 *
azvyagintsev386e94e2019-06-13 13:39:04 +030056 * @param path Path to virtualenv
57 * @param cmd Command to be executed
Jakub Josefe2f4ebb2018-01-15 16:11:51 +010058 * @param silent dont print any messages (optional, default false)
azvyagintsev386e94e2019-06-13 13:39:04 +030059 * @param flexAnswer return answer like a dict, with format ['status' : int, 'stderr' : str, 'stdout' : str ]
Sergey Kolekonovba203982016-12-21 18:32:17 +040060 */
azvyagintsev386e94e2019-06-13 13:39:04 +030061def runVirtualenvCommand(path, cmd, silent = false, flexAnswer = false) {
Tomáš Kukrál69c25452017-07-27 14:59:40 +020062 def common = new com.mirantis.mk.Common()
azvyagintsev386e94e2019-06-13 13:39:04 +030063 def res
64 def virtualenv_cmd = "set +x; . ${path}/bin/activate; ${cmd}"
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +000065 if (!silent) {
Jakub Josefe2f4ebb2018-01-15 16:11:51 +010066 common.infoMsg("[Python ${path}] Run command ${cmd}")
67 }
azvyagintsev386e94e2019-06-13 13:39:04 +030068 if (flexAnswer) {
69 res = common.shCmdStatus(virtualenv_cmd)
70 } else {
71 res = sh(
72 returnStdout: true,
73 script: virtualenv_cmd
74 ).trim()
75 }
76 return res
Sergey Kolekonovba203982016-12-21 18:32:17 +040077}
78
Ales Komarekd874d482016-12-26 10:33:29 +010079/**
Dennis Dmitriev8ac1fe72019-08-01 06:16:49 +030080 * Another command runner to control outputs and exit code
81 *
82 * - always print the executing command to control the pipeline execution
83 * - always allows to get the stdout/stderr/status in the result, even with enabled console enabled
84 * - throws an exception with stderr content, so it could be read from the job status and processed
85 *
86 * @param cmd String, command to be executed
87 * @param virtualenv String, path to Python virtualenv (optional, default: '')
88 * @param verbose Boolean, true: (default) mirror stdout to console and to the result['stdout'] at the same time,
89 * false: store stdout only to result['stdout']
90 * @param check_status Boolean, true: (default) throw an exception which contains result['stderr'] if exit code is not 0,
91 * false: only print stderr if not empty, and return the result
92 * @return Map, ['status' : int, 'stderr' : str, 'stdout' : str ]
93 */
94def runCmd(String cmd, String virtualenv='', Boolean verbose=true, Boolean check_status=true) {
95 def common = new com.mirantis.mk.Common()
96
97 def script
98 def redirect_output
99 def result = [:]
100 def stdout_path = sh(script: '#!/bin/bash +x\nmktemp', returnStdout: true).trim()
101 def stderr_path = sh(script: '#!/bin/bash +x\nmktemp', returnStdout: true).trim()
102
103 if (verbose) {
104 // show stdout to console and store to stdout_path
105 redirect_output = " 1> >(tee -a ${stdout_path}) 2>${stderr_path}"
106 } else {
107 // only store stdout to stdout_path
108 redirect_output = " 1>${stdout_path} 2>${stderr_path}"
109 }
110
111 if (virtualenv) {
112 common.infoMsg("Run shell command in Python virtualenv [${virtualenv}]:\n" + cmd)
113 script = """#!/bin/bash +x
114 . ${virtualenv}/bin/activate
115 ( ${cmd.stripIndent()} ) ${redirect_output}
116 """
117 } else {
118 common.infoMsg('Run shell command:\n' + cmd)
119 script = """#!/bin/bash +x
120 ( ${cmd.stripIndent()} ) ${redirect_output}
121 """
122 }
123
124 result['status'] = sh(script: script, returnStatus: true)
125 result['stdout'] = readFile(stdout_path)
126 result['stderr'] = readFile(stderr_path)
127 def cleanup_script = """#!/bin/bash +x
128 rm ${stdout_path} || true
129 rm ${stderr_path} || true
130 """
131 sh(script: cleanup_script)
132
133 if (result['status'] != 0 && check_status) {
134 def error_message = '\nScript returned exit code: ' + result['status'] + '\n<<<<<< STDERR: >>>>>>\n' + result['stderr']
135 common.errorMsg(error_message)
136 common.printMsg('', 'reset')
137 throw new Exception(error_message)
138 }
139
140 if (result['stderr'] && verbose) {
141 def warning_message = '\nScript returned exit code: ' + result['status'] + '\n<<<<<< STDERR: >>>>>>\n' + result['stderr']
142 common.warningMsg(warning_message)
143 common.printMsg('', 'reset')
144 }
145
146 return result
147}
148
149/**
Ales Komarekd874d482016-12-26 10:33:29 +0100150 * Install docutils in isolated environment
151 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000152 * @param path Path where virtualenv is created
Ales Komarekd874d482016-12-26 10:33:29 +0100153 */
154def setupDocutilsVirtualenv(path) {
155 requirements = [
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000156 'docutils',
Ales Komarekd874d482016-12-26 10:33:29 +0100157 ]
158 setupVirtualenv(path, 'python2', requirements)
159}
160
161
Sergey Kolekonovba203982016-12-21 18:32:17 +0400162@NonCPS
163def loadJson(rawData) {
164 return new groovy.json.JsonSlurperClassic().parseText(rawData)
165}
166
167/**
168 * Parse content from markup-text tables to variables
169 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000170 * @param tableStr String representing the table
171 * @param mode Either list (1st row are keys) or item (key, value rows)
172 * @param format Format of the table
Sergey Kolekonovba203982016-12-21 18:32:17 +0400173 */
Ales Komarekd874d482016-12-26 10:33:29 +0100174def parseTextTable(tableStr, type = 'item', format = 'rest', path = none) {
Ales Komarek0e558ee2016-12-23 13:02:55 +0100175 parserFile = "${env.WORKSPACE}/textTableParser.py"
176 parserScript = """import json
177import argparse
178from docutils.parsers.rst import tableparser
179from docutils import statemachine
180
181def parse_item_table(raw_data):
182 i = 1
183 pretty_raw_data = []
184 for datum in raw_data:
185 if datum != "":
186 if datum[3] != ' ' and i > 4:
187 pretty_raw_data.append(raw_data[0])
188 if i == 3:
189 pretty_raw_data.append(datum.replace('-', '='))
190 else:
191 pretty_raw_data.append(datum)
192 i += 1
193 parser = tableparser.GridTableParser()
194 block = statemachine.StringList(pretty_raw_data)
195 docutils_data = parser.parse(block)
196 final_data = {}
197 for line in docutils_data[2]:
198 key = ' '.join(line[0][3]).strip()
199 value = ' '.join(line[1][3]).strip()
200 if key != "":
201 try:
202 value = json.loads(value)
203 except:
204 pass
205 final_data[key] = value
206 i+=1
207 return final_data
208
209def parse_list_table(raw_data):
210 i = 1
211 pretty_raw_data = []
212 for datum in raw_data:
213 if datum != "":
214 if datum[3] != ' ' and i > 4:
215 pretty_raw_data.append(raw_data[0])
216 if i == 3:
217 pretty_raw_data.append(datum.replace('-', '='))
218 else:
219 pretty_raw_data.append(datum)
220 i += 1
221 parser = tableparser.GridTableParser()
222 block = statemachine.StringList(pretty_raw_data)
223 docutils_data = parser.parse(block)
224 final_data = []
225 keys = []
226 for line in docutils_data[1]:
227 for item in line:
228 keys.append(' '.join(item[3]).strip())
229 for line in docutils_data[2]:
230 final_line = {}
231 key = ' '.join(line[0][3]).strip()
232 value = ' '.join(line[1][3]).strip()
233 if key != "":
234 try:
235 value = json.loads(value)
236 except:
237 pass
238 final_data[key] = value
239 i+=1
240 return final_data
241
242def parse_list_table(raw_data):
243 i = 1
244 pretty_raw_data = []
245 for datum in raw_data:
246 if datum != "":
247 if datum[3] != ' ' and i > 4:
248 pretty_raw_data.append(raw_data[0])
249 if i == 3:
250 pretty_raw_data.append(datum.replace('-', '='))
251 else:
252 pretty_raw_data.append(datum)
253 i += 1
254 parser = tableparser.GridTableParser()
255 block = statemachine.StringList(pretty_raw_data)
256 docutils_data = parser.parse(block)
257 final_data = []
258 keys = []
259 for line in docutils_data[1]:
260 for item in line:
261 keys.append(' '.join(item[3]).strip())
262 for line in docutils_data[2]:
263 final_line = {}
264 i = 0
265 for item in line:
266 value = ' '.join(item[3]).strip()
267 try:
268 value = json.loads(value)
269 except:
270 pass
271 final_line[keys[i]] = value
272 i += 1
273 final_data.append(final_line)
274 return final_data
275
276def read_table_file(file):
277 table_file = open(file, 'r')
Ales Komarekc000c152016-12-23 15:32:54 +0100278 raw_data = table_file.read().split('\\n')
Ales Komarek0e558ee2016-12-23 13:02:55 +0100279 table_file.close()
280 return raw_data
281
282parser = argparse.ArgumentParser()
283parser.add_argument('-f','--file', help='File with table data', required=True)
284parser.add_argument('-t','--type', help='Type of table (list/item)', required=True)
285args = vars(parser.parse_args())
286
287raw_data = read_table_file(args['file'])
288
289if args['type'] == 'list':
290 final_data = parse_list_table(raw_data)
291else:
292 final_data = parse_item_table(raw_data)
293
294print json.dumps(final_data)
295"""
296 writeFile file: parserFile, text: parserScript
Sergey Kolekonovba203982016-12-21 18:32:17 +0400297 tableFile = "${env.WORKSPACE}/prettytable.txt"
298 writeFile file: tableFile, text: tableStr
Ales Komarekd874d482016-12-26 10:33:29 +0100299
300 cmd = "python ${parserFile} --file '${tableFile}' --type ${type}"
301 if (path) {
Ales Komarekc6d28dd2016-12-28 12:59:38 +0100302 rawData = runVirtualenvCommand(path, cmd)
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000303 } else {
304 rawData = sh(
Ales Komarekd874d482016-12-26 10:33:29 +0100305 script: cmd,
306 returnStdout: true
307 ).trim()
308 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400309 data = loadJson(rawData)
310 echo("[Parsed table] ${data}")
311 return data
312}
313
314/**
315 * Install cookiecutter in isolated environment
316 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000317 * @param path Path where virtualenv is created
Sergey Kolekonovba203982016-12-21 18:32:17 +0400318 */
319def setupCookiecutterVirtualenv(path) {
320 requirements = [
321 'cookiecutter',
Jakub Josef4df78272017-04-26 14:36:36 +0200322 'jinja2==2.8.1',
Dmitry Pyzhovb883a2d2018-12-14 16:42:52 +0300323 'PyYAML==3.12',
324 'python-gnupg==0.4.3'
Sergey Kolekonovba203982016-12-21 18:32:17 +0400325 ]
326 setupVirtualenv(path, 'python2', requirements)
327}
328
329/**
330 * Generate the cookiecutter templates with given context
331 *
Jakub Josef4e10c372017-04-26 14:13:50 +0200332 * @param template template
333 * @param context template context
334 * @param path Path where virtualenv is created (optional)
335 * @param templatePath path to cookiecutter template repo (optional)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400336 */
Jakub Josef4e10c372017-04-26 14:13:50 +0200337def buildCookiecutterTemplate(template, context, outputDir = '.', path = null, templatePath = ".") {
azvyagintsev7a123aa2019-01-09 21:38:56 +0200338 def common = new com.mirantis.mk.Common()
Tomáš Kukráldad7b462017-03-27 13:53:05 +0200339 configFile = "default_config.yaml"
Tomáš Kukrál6de85042017-04-12 17:49:05 +0200340 writeFile file: configFile, text: context
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000341 common.warningMsg('Old Cookiecutter env detected!')
342 command = ". ${path}/bin/activate; if [ -f ${templatePath}/generate.py ]; then python ${templatePath}/generate.py --config-file ${configFile} --template ${template} --output-dir ${outputDir}; else cookiecutter --config-file ${configFile} --output-dir ${outputDir} --overwrite-if-exists --verbose --no-input ${template}; fi"
343 output = sh(returnStdout: true, script: command)
344 common.infoMsg('[Cookiecutter build] Result:' + output)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400345}
346
347/**
Denis Egorenko6c2e3ae2018-10-17 16:45:56 +0400348 *
349 * @param context - context template
350 * @param contextName - context template name
351 * @param saltMasterName - hostname of Salt Master node
352 * @param virtualenv - pyvenv with CC and dep's
353 * @param templateEnvDir - root of CookieCutter
354 * @return
355 */
356def generateModel(context, contextName, saltMasterName, virtualenv, modelEnv, templateEnvDir, multiModels = true) {
Denis Egorenkofa2c6752018-10-18 15:51:45 +0400357 def common = new com.mirantis.mk.Common()
Denis Egorenko6c2e3ae2018-10-17 16:45:56 +0400358 def generatedModel = multiModels ? "${modelEnv}/${contextName}" : modelEnv
359 def templateContext = readYaml text: context
360 def clusterDomain = templateContext.default_context.cluster_domain
361 def clusterName = templateContext.default_context.cluster_name
362 def outputDestination = "${generatedModel}/classes/cluster/${clusterName}"
363 def templateBaseDir = templateEnvDir
364 def templateDir = "${templateEnvDir}/dir"
365 def templateOutputDir = templateBaseDir
366 dir(templateEnvDir) {
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000367 if (fileExists(new File(templateEnvDir, 'tox.ini').toString())) {
Aleksey Zvyagintsev07b07ba2019-02-28 13:34:13 +0000368 def tempContextFile = new File(templateEnvDir, 'tempContext.yaml').toString()
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000369 writeFile file: tempContextFile, text: context
370 common.warningMsg('Generating models using context:\n')
371 print(context)
372 withEnv(["CONFIG_FILE=$tempContextFile",
Aleksey Zvyagintsev07b07ba2019-02-28 13:34:13 +0000373 "OUTPUT_DIR=${modelEnv}",
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000374 ]) {
375 print('[Cookiecutter build] Result:\n' +
376 sh(returnStdout: true, script: 'tox -ve generate_auto'))
Aleksey Zvyagintsev4c745e52019-02-21 10:30:02 +0000377 }
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000378 } else {
379 common.warningMsg("Old format: Generating model from context ${contextName}")
380 def productList = ["infra", "cicd", "kdt", "opencontrail", "kubernetes", "openstack", "oss", "stacklight", "ceph"]
381 for (product in productList) {
382 // get templateOutputDir and productDir
383 templateOutputDir = "${templateEnvDir}/output/${product}"
384 productDir = product
385 templateDir = "${templateEnvDir}/cluster_product/${productDir}"
386 // Bw for 2018.8.1 and older releases
387 if (product.startsWith("stacklight") && (!fileExists(templateDir))) {
388 common.warningMsg("Old release detected! productDir => 'stacklight2' ")
389 productDir = "stacklight2"
390 templateDir = "${templateEnvDir}/cluster_product/${productDir}"
391 }
392 // generate infra unless its explicitly disabled
393 if ((product == "infra" && templateContext.default_context.get("infra_enabled", "True").toBoolean())
394 || (templateContext.default_context.get(product + "_enabled", "False").toBoolean())) {
395
396 common.infoMsg("Generating product " + product + " from " + templateDir + " to " + templateOutputDir)
397
398 sh "rm -rf ${templateOutputDir} || true"
399 sh "mkdir -p ${templateOutputDir}"
400 sh "mkdir -p ${outputDestination}"
401
402 buildCookiecutterTemplate(templateDir, context, templateOutputDir, virtualenv, templateBaseDir)
403 sh "mv -v ${templateOutputDir}/${clusterName}/* ${outputDestination}"
404 } else {
405 common.warningMsg("Product " + product + " is disabled")
406 }
407 }
408
409 def localRepositories = templateContext.default_context.local_repositories
410 localRepositories = localRepositories ? localRepositories.toBoolean() : false
411 def offlineDeployment = templateContext.default_context.offline_deployment
412 offlineDeployment = offlineDeployment ? offlineDeployment.toBoolean() : false
413 if (localRepositories && !offlineDeployment) {
414 def mcpVersion = templateContext.default_context.mcp_version
415 def aptlyModelUrl = templateContext.default_context.local_model_url
416 def ssh = new com.mirantis.mk.Ssh()
417 dir(path: modelEnv) {
418 ssh.agentSh "git submodule add \"${aptlyModelUrl}\" \"classes/cluster/${clusterName}/cicd/aptly\""
419 if (!(mcpVersion in ["nightly", "testing", "stable"])) {
420 ssh.agentSh "cd \"classes/cluster/${clusterName}/cicd/aptly\";git fetch --tags;git checkout ${mcpVersion}"
421 }
422 }
423 }
424
425 def nodeFile = "${generatedModel}/nodes/${saltMasterName}.${clusterDomain}.yml"
426 def nodeString = """classes:
427- cluster.${clusterName}.infra.config
428parameters:
429 _param:
430 linux_system_codename: xenial
431 reclass_data_revision: master
432 linux:
433 system:
434 name: ${saltMasterName}
435 domain: ${clusterDomain}
436 """
437 sh "mkdir -p ${generatedModel}/nodes/"
438 writeFile(file: nodeFile, text: nodeString)
439 }
Denis Egorenko6c2e3ae2018-10-17 16:45:56 +0400440 }
441}
442
443/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400444 * Install jinja rendering in isolated environment
445 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000446 * @param path Path where virtualenv is created
Sergey Kolekonovba203982016-12-21 18:32:17 +0400447 */
448def setupJinjaVirtualenv(path) {
449 requirements = [
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000450 'jinja2-cli',
451 'pyyaml',
Sergey Kolekonovba203982016-12-21 18:32:17 +0400452 ]
453 setupVirtualenv(path, 'python2', requirements)
454}
455
456/**
457 * Generate the Jinja templates with given context
458 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000459 * @param path Path where virtualenv is created
Sergey Kolekonovba203982016-12-21 18:32:17 +0400460 */
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000461def jinjaBuildTemplate(template, context, path = none) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400462 contextFile = "jinja_context.yml"
463 contextString = ""
464 for (parameter in context) {
465 contextString = "${contextString}${parameter.key}: ${parameter.value}\n"
466 }
467 writeFile file: contextFile, text: contextString
468 cmd = "jinja2 ${template} ${contextFile} --format=yaml"
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000469 data = sh(returnStdout: true, script: cmd)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400470 echo(data)
471 return data
472}
Oleg Grigorovbec45582017-09-12 20:29:24 +0300473
474/**
475 * Install salt-pepper in isolated environment
476 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000477 * @param path Path where virtualenv is created
478 * @param url SALT_MASTER_URL
479 * @param credentialsId Credentials to salt api
Oleg Grigorovbec45582017-09-12 20:29:24 +0300480 */
Jakub Josefc9b6d662018-02-21 16:21:03 +0100481def setupPepperVirtualenv(path, url, credentialsId) {
chnydaa0dbb252017-10-05 10:46:09 +0200482 def common = new com.mirantis.mk.Common()
483
484 // virtualenv setup
Mykyta Karpin81756c92018-03-02 13:03:26 +0200485 // pin pepper till https://mirantis.jira.com/browse/PROD-18188 is fixed
486 requirements = ['salt-pepper>=0.5.2,<0.5.4']
Jakub Josefc9b6d662018-02-21 16:21:03 +0100487 setupVirtualenv(path, 'python2', requirements, null, true, true)
chnydabcfff182017-11-29 10:24:36 +0100488
chnydaa0dbb252017-10-05 10:46:09 +0200489 // pepperrc creation
490 rcFile = "${path}/pepperrc"
491 creds = common.getPasswordCredentials(credentialsId)
492 rc = """\
493[main]
494SALTAPI_EAUTH=pam
495SALTAPI_URL=${url}
496SALTAPI_USER=${creds.username}
497SALTAPI_PASS=${creds.password.toString()}
498"""
499 writeFile file: rcFile, text: rc
500 return rcFile
Jakub Josefd067f612017-09-26 13:42:56 +0200501}
Oleh Hryhorov44569fb2017-10-26 17:04:55 +0300502
503/**
504 * Install devops in isolated environment
505 *
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000506 * @param path Path where virtualenv is created
507 * @param clean Define to true is the venv have to cleaned up before install a new one
Oleh Hryhorov44569fb2017-10-26 17:04:55 +0300508 */
Aleksey Zvyagintsevc4f66f62019-02-21 10:39:34 +0000509def setupDevOpsVenv(venv, clean = false) {
Oleh Hryhorov44569fb2017-10-26 17:04:55 +0300510 requirements = ['git+https://github.com/openstack/fuel-devops.git']
511 setupVirtualenv(venv, 'python2', requirements, null, false, clean)
512}