blob: 9dfcc55bf64103e379c6ccc446ea6bba88a0420d [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
2
3/**
4 *
5 * Openstack functions
6 *
7 */
8
9/**
Tomáš Kukrálc3964e52017-02-22 14:07:37 +010010 * Convert maps
11 *
12 */
13
14@NonCPS def entries(m) {
15 return m.collect {k, v -> [k, v]}
16}
17
18/**
Sergey Kolekonovba203982016-12-21 18:32:17 +040019 * Install OpenStack service clients in isolated environment
20 *
21 * @param path Path where virtualenv is created
22 * @param version Version of the OpenStack clients
23 */
24
25def setupOpenstackVirtualenv(path, version = 'kilo'){
iberezovskiyd4240b52017-02-20 17:18:28 +040026 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +040027
28 def openstack_kilo_packages = [
29 'python-cinderclient>=1.3.1,<1.4.0',
30 'python-glanceclient>=0.19.0,<0.20.0',
31 'python-heatclient>=0.6.0,<0.7.0',
32 'python-keystoneclient>=1.6.0,<1.7.0',
33 'python-neutronclient>=2.2.6,<2.3.0',
34 'python-novaclient>=2.19.0,<2.20.0',
35 'python-swiftclient>=2.5.0,<2.6.0',
36 'oslo.config>=2.2.0,<2.3.0',
37 'oslo.i18n>=2.3.0,<2.4.0',
38 'oslo.serialization>=1.8.0,<1.9.0',
39 'oslo.utils>=1.4.0,<1.5.0',
Ales Komarekd874d482016-12-26 10:33:29 +010040 'docutils>=0.12'
Sergey Kolekonovba203982016-12-21 18:32:17 +040041 ]
42
43 def openstack_latest_packages = openstack_kilo_packages
44
45 if(version == 'kilo') {
46 requirements = openstack_kilo_packages
47 }
48 else if(version == 'liberty') {
49 requirements = openstack_kilo_packages
50 }
51 else if(version == 'mitaka') {
52 requirements = openstack_kilo_packages
53 }
54 else {
55 requirements = openstack_latest_packages
56 }
57 python.setupVirtualenv(path, 'python2', requirements)
58}
59
60/**
61 * create connection to OpenStack API endpoint
62 *
63 * @param url OpenStack API endpoint address
64 * @param credentialsId Credentials to the OpenStack API
65 * @param project OpenStack project to connect to
66 */
Sergey Kolekonovba203982016-12-21 18:32:17 +040067def createOpenstackEnv(url, credentialsId, project) {
iberezovskiyd4240b52017-02-20 17:18:28 +040068 def common = new com.mirantis.mk.Common()
Ales Komarek0e558ee2016-12-23 13:02:55 +010069 rcFile = "${env.WORKSPACE}/keystonerc"
Sergey Kolekonovba203982016-12-21 18:32:17 +040070 creds = common.getPasswordCredentials(credentialsId)
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030071 rc = """set +x
72export OS_USERNAME=${creds.username}
Ales Komarek0e558ee2016-12-23 13:02:55 +010073export OS_PASSWORD=${creds.password.toString()}
74export OS_TENANT_NAME=${project}
75export OS_AUTH_URL=${url}
76export OS_AUTH_STRATEGY=keystone
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030077set -x
Ales Komarek0e558ee2016-12-23 13:02:55 +010078"""
79 writeFile file: rcFile, text: rc
80 return rcFile
Sergey Kolekonovba203982016-12-21 18:32:17 +040081}
82
83/**
84 * Run command with OpenStack env params and optional python env
85 *
86 * @param cmd Command to be executed
87 * @param env Environmental parameters with endpoint credentials
88 * @param path Optional path to virtualenv with specific clients
89 */
90def runOpenstackCommand(cmd, venv, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +040091 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +040092 openstackCmd = ". ${venv}; ${cmd}"
93 if (path) {
94 output = python.runVirtualenvCommand(path, openstackCmd)
95 }
96 else {
97 echo("[Command]: ${openstackCmd}")
98 output = sh (
99 script: openstackCmd,
100 returnStdout: true
101 ).trim()
102 }
103 return output
104}
105
106/**
107 * Get OpenStack Keystone token for current credentials
108 *
109 * @param env Connection parameters for OpenStack API endpoint
110 * @param path Optional path to the custom virtualenv
111 */
112def getKeystoneToken(client, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400113 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400114 cmd = "keystone token-get"
115 outputTable = runOpenstackCommand(cmd, client, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100116 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400117 return output
118}
119
120/**
121 * Get OpenStack Keystone token for current credentials
122 *
123 * @param env Connection parameters for OpenStack API endpoint
124 * @param path Optional path to the custom virtualenv
125 */
126def createHeatEnv(file, environment = [], original_file = null) {
127 if (original_file) {
128 envString = readFile file: original_file
Tomáš Kukrál03029442017-02-21 17:14:29 +0100129 } else {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400130 envString = "parameters:\n"
131 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100132
Tomáš Kukrálc3964e52017-02-22 14:07:37 +0100133 p = entries(environment)
Tomáš Kukrálb1fe9642017-02-22 11:21:17 +0100134 for (int i = 0; i < p.size(); i++) {
135 envString = "${envString} ${p.get(i)[0]}: ${p.get(i)[1]}\n"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400136 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100137
Tomáš Kukrále19ddea2017-02-21 11:09:40 +0100138 echo("writing to env file:\n${envString}")
Sergey Kolekonovba203982016-12-21 18:32:17 +0400139 writeFile file: file, text: envString
140}
141
142/**
143 * Create new OpenStack Heat stack
144 *
145 * @param env Connection parameters for OpenStack API endpoint
146 * @param template HOT template for the new Heat stack
147 * @param environment Environmentale parameters of the new Heat stack
148 * @param name Name of the new Heat stack
149 * @param path Optional path to the custom virtualenv
150 */
151def createHeatStack(client, name, template, params = [], environment = null, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400152 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400153 templateFile = "${env.WORKSPACE}/template/template/${template}.hot"
154 if (environment) {
155 envFile = "${env.WORKSPACE}/template/env/${template}/${name}.env"
156 envSource = "${env.WORKSPACE}/template/env/${template}/${environment}.env"
157 createHeatEnv(envFile, params, envSource)
158 }
159 else {
160 envFile = "${env.WORKSPACE}/template/${name}.env"
161 createHeatEnv(envFile, params)
162 }
163 cmd = "heat stack-create -f ${templateFile} -e ${envFile} ${name}"
164 dir("${env.WORKSPACE}/template/template") {
165 outputTable = runOpenstackCommand(cmd, client, path)
166 }
Ales Komareke11e8792016-12-28 09:42:25 +0100167 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400168
169 i = 1
Ales Komarekdce8b472016-12-30 16:56:07 +0100170
Sergey Kolekonovba203982016-12-21 18:32:17 +0400171 while (true) {
172 status = getHeatStackStatus(client, name, path)
173 echo("[Heat Stack] Status: ${status}, Check: ${i}")
Ales Komarekdce8b472016-12-30 16:56:07 +0100174
175 if (status.indexOf('CREATE_FAILED') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400176 info = getHeatStackInfo(client, name, path)
177 throw new Exception(info.stack_status_reason)
178 }
Ales Komarekdce8b472016-12-30 16:56:07 +0100179 else if (status.indexOf('CREATE_COMPLETE') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400180 info = getHeatStackInfo(client, name, path)
181 echo(info.stack_status_reason)
182 break
183 }
184 sh('sleep 5s')
185 i++
186 }
187 echo("[Heat Stack] Status: ${status}")
188}
189
190/**
Jakub Josef5e238a22017-04-19 16:35:15 +0200191 * Get list of stack names with given stack status
192 *
193 * @param env Connection parameters for OpenStack API endpoint
194 * @param status Stack status
195 * @param path Optional path to the custom virtualenv
196 */
197 def getStacksWithStatus(client, status, path = null) {
198 cmd = 'heat stack-list -f stack_status='+status+' | awk \'NR>3 {print $4}\' | sed \'$ d\''
199 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
200 }
201
202/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400203 * Get life cycle status for existing OpenStack Heat stack
204 *
205 * @param env Connection parameters for OpenStack API endpoint
206 * @param name Name of the managed Heat stack instance
207 * @param path Optional path to the custom virtualenv
208 */
209def getHeatStackStatus(client, name, path = null) {
210 cmd = 'heat stack-list | awk -v stack='+name+' \'{if ($4==stack) print $6}\''
211 return runOpenstackCommand(cmd, client, path)
212}
213
214/**
215 * Get info about existing OpenStack Heat stack
216 *
217 * @param env Connection parameters for OpenStack API endpoint
218 * @param name Name of the managed Heat stack instance
219 * @param path Optional path to the custom virtualenv
220 */
221def getHeatStackInfo(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400222 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400223 cmd = "heat stack-show ${name}"
224 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100225 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400226 return output
227}
228
229/**
230 * Get existing OpenStack Heat stack output parameter
231 *
232 * @param env Connection parameters for OpenStack API endpoint
233 * @param name Name of the managed Heat stack
234 * @param parameter Name of the output parameter
235 * @param path Optional path to the custom virtualenv
236 */
237def getHeatStackOutputParam(env, name, outputParam, path = null) {
238 cmd = "heat output-show ${name} ${outputParam}"
239 output = runOpenstackCommand(cmd, env, path)
Ales Komarekeedc2222017-01-03 10:10:03 +0100240 echo("${cmd}: ${output}")
Ales Komarek7ebd0062017-01-03 10:59:29 +0100241 return "${output}".substring(1, output.length()-1)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400242}
243
244/**
245 * List all resources from existing OpenStack Heat stack
246 *
247 * @param env Connection parameters for OpenStack API endpoint
248 * @param name Name of the managed Heat stack instance
249 * @param path Optional path to the custom virtualenv
250 */
251def getHeatStackResources(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400252 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400253 cmd = "heat resource-list ${name}"
254 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100255 output = python.parseTextTable(outputTable, 'list', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400256 return output
257}
258
259/**
260 * Get info about resource from existing OpenStack Heat stack
261 *
262 * @param env Connection parameters for OpenStack API endpoint
263 * @param name Name of the managed Heat stack instance
264 * @param path Optional path to the custom virtualenv
265 */
266def getHeatStackResourceInfo(env, name, resource, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400267 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400268 cmd = "heat resource-show ${name} ${resource}"
269 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100270 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400271 return output
272}
273
274/**
275 * Update existing OpenStack Heat stack
276 *
277 * @param env Connection parameters for OpenStack API endpoint
278 * @param name Name of the managed Heat stack instance
279 * @param path Optional path to the custom virtualenv
280 */
281def updateHeatStack(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400282 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400283 cmd = "heat stack-update ${name}"
284 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100285 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400286 return output
287}
288
289/**
290 * Delete existing OpenStack Heat stack
291 *
292 * @param env Connection parameters for OpenStack API endpoint
293 * @param name Name of the managed Heat stack instance
294 * @param path Optional path to the custom virtualenv
295 */
296def deleteHeatStack(env, name, path = null) {
297 cmd = "heat stack-delete ${name}"
298 outputTable = runOpenstackCommand(cmd, env, path)
299}
300
301/**
302 * Return list of servers from OpenStack Heat stack
303 *
304 * @param env Connection parameters for OpenStack API endpoint
305 * @param name Name of the managed Heat stack instance
306 * @param path Optional path to the custom virtualenv
307 */
308def getHeatStackServers(env, name, path = null) {
309 resources = heatGetStackResources(env, name, path)
310 servers = []
311 for (resource in resources) {
312 if (resource.resource_type == 'OS::Nova::Server') {
313 resourceName = resource.resource_name
314 server = heatGetStackResourceInfo(env, name, resourceName, path)
315 servers.add(server.attributes.name)
316 }
317 }
318 echo("[Stack ${name}] Servers: ${servers}")
319 return servers
320}