blob: f5b2f3a9556c9a1bcf427097975fdccab26ce02c [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
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020025def 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',
Jakub Josefbd927322017-05-30 13:20:27 +000036 'python-openstackclient>=1.7.0,<1.8.0',
Sergey Kolekonovba203982016-12-21 18:32:17 +040037 'oslo.config>=2.2.0,<2.3.0',
38 'oslo.i18n>=2.3.0,<2.4.0',
39 'oslo.serialization>=1.8.0,<1.9.0',
40 'oslo.utils>=1.4.0,<1.5.0',
Ales Komarekd874d482016-12-26 10:33:29 +010041 'docutils>=0.12'
Sergey Kolekonovba203982016-12-21 18:32:17 +040042 ]
43
Tomáš Kukrál381a8c92017-06-21 09:01:52 +020044 def openstack_latest_packages = [
45 'python-openstackclient',
46 'python-heatclient',
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020047 ]
Sergey Kolekonovba203982016-12-21 18:32:17 +040048
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020049 if (version == 'kilo') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040050 requirements = openstack_kilo_packages
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020051 } else if (version == 'liberty') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040052 requirements = openstack_kilo_packages
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020053 } else if (version == 'mitaka') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040054 requirements = openstack_kilo_packages
Tomáš Kukrál381a8c92017-06-21 09:01:52 +020055 } else {
Sergey Kolekonovba203982016-12-21 18:32:17 +040056 requirements = openstack_latest_packages
57 }
58 python.setupVirtualenv(path, 'python2', requirements)
59}
60
61/**
62 * create connection to OpenStack API endpoint
63 *
64 * @param url OpenStack API endpoint address
65 * @param credentialsId Credentials to the OpenStack API
66 * @param project OpenStack project to connect to
67 */
kairat_kushaev0a26bf72017-05-18 13:20:09 +040068def createOpenstackEnv(url, credentialsId, project, project_domain="default",
Tomáš Kukrál381a8c92017-06-21 09:01:52 +020069 project_id="", user_domain="default", api_ver="2", cacert="/etc/ssl/certs/ca-certificates.crt") {
iberezovskiyd4240b52017-02-20 17:18:28 +040070 def common = new com.mirantis.mk.Common()
Ales Komarek0e558ee2016-12-23 13:02:55 +010071 rcFile = "${env.WORKSPACE}/keystonerc"
Sergey Kolekonovba203982016-12-21 18:32:17 +040072 creds = common.getPasswordCredentials(credentialsId)
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030073 rc = """set +x
74export OS_USERNAME=${creds.username}
Ales Komarek0e558ee2016-12-23 13:02:55 +010075export OS_PASSWORD=${creds.password.toString()}
76export OS_TENANT_NAME=${project}
77export OS_AUTH_URL=${url}
78export OS_AUTH_STRATEGY=keystone
kairat_kushaev0a26bf72017-05-18 13:20:09 +040079export OS_PROJECT_NAME=${project}
Jakub Josefbd927322017-05-30 13:20:27 +000080export OS_PROJECT_ID=${project_id}
kairat_kushaev0a26bf72017-05-18 13:20:09 +040081export OS_PROJECT_DOMAIN_ID=${project_domain}
Jakub Josefbd927322017-05-30 13:20:27 +000082export OS_USER_DOMAIN_NAME=${user_domain}
83export OS_API_IDENTITY_VERSION=${api_ver}
Tomáš Kukrál381a8c92017-06-21 09:01:52 +020084export OS_CACERT=${cacert}
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030085set -x
Ales Komarek0e558ee2016-12-23 13:02:55 +010086"""
87 writeFile file: rcFile, text: rc
88 return rcFile
Sergey Kolekonovba203982016-12-21 18:32:17 +040089}
90
91/**
92 * Run command with OpenStack env params and optional python env
93 *
94 * @param cmd Command to be executed
95 * @param env Environmental parameters with endpoint credentials
96 * @param path Optional path to virtualenv with specific clients
97 */
98def runOpenstackCommand(cmd, venv, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +040099 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400100 openstackCmd = ". ${venv}; ${cmd}"
101 if (path) {
102 output = python.runVirtualenvCommand(path, openstackCmd)
103 }
104 else {
105 echo("[Command]: ${openstackCmd}")
106 output = sh (
107 script: openstackCmd,
108 returnStdout: true
109 ).trim()
110 }
111 return output
112}
113
114/**
115 * Get OpenStack Keystone token for current credentials
116 *
117 * @param env Connection parameters for OpenStack API endpoint
118 * @param path Optional path to the custom virtualenv
119 */
120def getKeystoneToken(client, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400121 def python = new com.mirantis.mk.Python()
Jakub Josefbd927322017-05-30 13:20:27 +0000122 cmd = "openstack token issue"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400123 outputTable = runOpenstackCommand(cmd, client, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100124 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400125 return output
126}
127
128/**
Ales Komarek51b7b152017-06-27 11:14:50 +0200129 * Create OpenStack environment file
Sergey Kolekonovba203982016-12-21 18:32:17 +0400130 *
131 * @param env Connection parameters for OpenStack API endpoint
132 * @param path Optional path to the custom virtualenv
133 */
134def createHeatEnv(file, environment = [], original_file = null) {
135 if (original_file) {
136 envString = readFile file: original_file
Tomáš Kukrál03029442017-02-21 17:14:29 +0100137 } else {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400138 envString = "parameters:\n"
139 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100140
Tomáš Kukrálc3964e52017-02-22 14:07:37 +0100141 p = entries(environment)
Tomáš Kukrálb1fe9642017-02-22 11:21:17 +0100142 for (int i = 0; i < p.size(); i++) {
143 envString = "${envString} ${p.get(i)[0]}: ${p.get(i)[1]}\n"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400144 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100145
Tomáš Kukrále19ddea2017-02-21 11:09:40 +0100146 echo("writing to env file:\n${envString}")
Sergey Kolekonovba203982016-12-21 18:32:17 +0400147 writeFile file: file, text: envString
148}
149
150/**
151 * Create new OpenStack Heat stack
152 *
153 * @param env Connection parameters for OpenStack API endpoint
154 * @param template HOT template for the new Heat stack
155 * @param environment Environmentale parameters of the new Heat stack
156 * @param name Name of the new Heat stack
157 * @param path Optional path to the custom virtualenv
Ales Komarek51b7b152017-06-27 11:14:50 +0200158 * @param legacy_env Use old path format [env_name/template_name] to
159 * target env file.
Sergey Kolekonovba203982016-12-21 18:32:17 +0400160 */
Ales Komarek51b7b152017-06-27 11:14:50 +0200161def createHeatStack(client, name, template, params = [], environment = null, path = null, legacy_env = true) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400162 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400163 templateFile = "${env.WORKSPACE}/template/template/${template}.hot"
164 if (environment) {
Ales Komarek51b7b152017-06-27 11:14:50 +0200165 if (legacy_env) {
166 envFile = "${env.WORKSPACE}/template/env/${template}/${name}.env"
167 envSource = "${env.WORKSPACE}/template/env/${template}/${environment}.env"
168 }
169 else {
170 envFile = "${env.WORKSPACE}/template/${name}.env"
171 envSource = "${env.WORKSPACE}/template/env/${environment}.env"
172 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400173 createHeatEnv(envFile, params, envSource)
174 }
175 else {
176 envFile = "${env.WORKSPACE}/template/${name}.env"
177 createHeatEnv(envFile, params)
178 }
179 cmd = "heat stack-create -f ${templateFile} -e ${envFile} ${name}"
180 dir("${env.WORKSPACE}/template/template") {
181 outputTable = runOpenstackCommand(cmd, client, path)
182 }
Ales Komareke11e8792016-12-28 09:42:25 +0100183 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400184
185 i = 1
Ales Komarekdce8b472016-12-30 16:56:07 +0100186
Sergey Kolekonovba203982016-12-21 18:32:17 +0400187 while (true) {
188 status = getHeatStackStatus(client, name, path)
189 echo("[Heat Stack] Status: ${status}, Check: ${i}")
Ales Komarekdce8b472016-12-30 16:56:07 +0100190
191 if (status.indexOf('CREATE_FAILED') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400192 info = getHeatStackInfo(client, name, path)
193 throw new Exception(info.stack_status_reason)
194 }
Ales Komarekdce8b472016-12-30 16:56:07 +0100195 else if (status.indexOf('CREATE_COMPLETE') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400196 info = getHeatStackInfo(client, name, path)
197 echo(info.stack_status_reason)
198 break
199 }
200 sh('sleep 5s')
201 i++
202 }
203 echo("[Heat Stack] Status: ${status}")
204}
205
206/**
Jakub Josefdb4baf22017-05-10 15:16:09 +0200207 * Returns list of stacks for stack name filter
208 *
209 * @param client Connection parameters for OpenStack API endpoint
210 * @param filter Stack name filter
211 * @param path Optional path to the custom virtualenv
212 */
213def getStacksForNameContains(client, filter, path = null){
Jakub Josef6465fca2017-05-10 16:09:20 +0200214 cmd = 'heat stack-list | awk \'NR>3 {print $4}\' | sed \'$ d\' | grep ' + filter + '|| true'
Jakub Josefdb4baf22017-05-10 15:16:09 +0200215 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
216}
217
218
219/**
Jakub Josef5e238a22017-04-19 16:35:15 +0200220 * Get list of stack names with given stack status
221 *
Jakub Josefdb4baf22017-05-10 15:16:09 +0200222 * @param client Connection parameters for OpenStack API endpoint
Jakub Josef5e238a22017-04-19 16:35:15 +0200223 * @param status Stack status
224 * @param path Optional path to the custom virtualenv
225 */
226 def getStacksWithStatus(client, status, path = null) {
227 cmd = 'heat stack-list -f stack_status='+status+' | awk \'NR>3 {print $4}\' | sed \'$ d\''
228 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
229 }
230
231/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400232 * Get life cycle status for existing OpenStack Heat stack
233 *
234 * @param env Connection parameters for OpenStack API endpoint
235 * @param name Name of the managed Heat stack instance
236 * @param path Optional path to the custom virtualenv
237 */
238def getHeatStackStatus(client, name, path = null) {
239 cmd = 'heat stack-list | awk -v stack='+name+' \'{if ($4==stack) print $6}\''
240 return runOpenstackCommand(cmd, client, path)
241}
242
243/**
244 * Get info about existing OpenStack Heat stack
245 *
246 * @param env Connection parameters for OpenStack API endpoint
247 * @param name Name of the managed Heat stack instance
248 * @param path Optional path to the custom virtualenv
249 */
250def getHeatStackInfo(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400251 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400252 cmd = "heat stack-show ${name}"
253 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100254 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400255 return output
256}
257
258/**
259 * Get existing OpenStack Heat stack output parameter
260 *
261 * @param env Connection parameters for OpenStack API endpoint
262 * @param name Name of the managed Heat stack
263 * @param parameter Name of the output parameter
264 * @param path Optional path to the custom virtualenv
265 */
266def getHeatStackOutputParam(env, name, outputParam, path = null) {
267 cmd = "heat output-show ${name} ${outputParam}"
268 output = runOpenstackCommand(cmd, env, path)
Ales Komarekeedc2222017-01-03 10:10:03 +0100269 echo("${cmd}: ${output}")
Ales Komarek7ebd0062017-01-03 10:59:29 +0100270 return "${output}".substring(1, output.length()-1)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400271}
272
273/**
274 * List all resources from existing OpenStack Heat stack
275 *
276 * @param env Connection parameters for OpenStack API endpoint
277 * @param name Name of the managed Heat stack instance
278 * @param path Optional path to the custom virtualenv
279 */
280def getHeatStackResources(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400281 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400282 cmd = "heat resource-list ${name}"
283 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100284 output = python.parseTextTable(outputTable, 'list', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400285 return output
286}
287
288/**
289 * Get info about resource from existing OpenStack Heat stack
290 *
291 * @param env Connection parameters for OpenStack API endpoint
292 * @param name Name of the managed Heat stack instance
293 * @param path Optional path to the custom virtualenv
294 */
295def getHeatStackResourceInfo(env, name, resource, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400296 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400297 cmd = "heat resource-show ${name} ${resource}"
298 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100299 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400300 return output
301}
302
303/**
304 * Update existing OpenStack Heat stack
305 *
306 * @param env Connection parameters for OpenStack API endpoint
307 * @param name Name of the managed Heat stack instance
308 * @param path Optional path to the custom virtualenv
309 */
310def updateHeatStack(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400311 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400312 cmd = "heat stack-update ${name}"
313 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100314 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400315 return output
316}
317
318/**
319 * Delete existing OpenStack Heat stack
320 *
321 * @param env Connection parameters for OpenStack API endpoint
322 * @param name Name of the managed Heat stack instance
323 * @param path Optional path to the custom virtualenv
324 */
325def deleteHeatStack(env, name, path = null) {
326 cmd = "heat stack-delete ${name}"
327 outputTable = runOpenstackCommand(cmd, env, path)
328}
329
330/**
331 * Return list of servers from OpenStack Heat stack
332 *
333 * @param env Connection parameters for OpenStack API endpoint
334 * @param name Name of the managed Heat stack instance
335 * @param path Optional path to the custom virtualenv
336 */
337def getHeatStackServers(env, name, path = null) {
338 resources = heatGetStackResources(env, name, path)
339 servers = []
340 for (resource in resources) {
341 if (resource.resource_type == 'OS::Nova::Server') {
342 resourceName = resource.resource_name
343 server = heatGetStackResourceInfo(env, name, resourceName, path)
344 servers.add(server.attributes.name)
345 }
346 }
347 echo("[Stack ${name}] Servers: ${servers}")
348 return servers
349}