blob: fecf31485d7aa38fb6aabf3c90494a20936e9e07 [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
Jakub Josef9973a7f2017-06-20 13:43:48 +02002import com.cloudbees.groovy.cps.NonCPS
Sergey Kolekonovba203982016-12-21 18:32:17 +04003/**
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álc6a94c62017-06-19 14:44:24 +020044 def openstack_ocata_packages = [
45 'pbr!=2.1.0,>=2.0.0',
46 'six>=1.9.0',
47 'Babel!=2.4.0,>=2.3.4',
48 'cliff>=2.6.0',
49 'keystoneauth1>=2.21.0',
50 'openstacksdk>=0.9.16',
51 'osc-lib>=1.5.1',
52 'oslo.i18n!=3.15.2,>=2.1.0',
53 'oslo.utils>=3.20.0',
54 'python-glanceclient>=2.7.0',
55 'python-keystoneclient>=3.8.0',
56 'python-novaclient>=9.0.0',
57 'python-cinderclient>=2.1.0',
58 ]
Sergey Kolekonovba203982016-12-21 18:32:17 +040059
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020060 def openstack_latest_packages = openstack_ocata_packages
61
62 if (version == 'kilo') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040063 requirements = openstack_kilo_packages
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020064 } else if (version == 'liberty') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040065 requirements = openstack_kilo_packages
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020066 } else if (version == 'mitaka') {
Sergey Kolekonovba203982016-12-21 18:32:17 +040067 requirements = openstack_kilo_packages
Tomáš Kukrálc6a94c62017-06-19 14:44:24 +020068 } else if (version == 'ocata') {
69 requirements = openstack_ocata_packages
Sergey Kolekonovba203982016-12-21 18:32:17 +040070 }
71 else {
72 requirements = openstack_latest_packages
73 }
74 python.setupVirtualenv(path, 'python2', requirements)
75}
76
77/**
78 * create connection to OpenStack API endpoint
79 *
80 * @param url OpenStack API endpoint address
81 * @param credentialsId Credentials to the OpenStack API
82 * @param project OpenStack project to connect to
83 */
kairat_kushaev0a26bf72017-05-18 13:20:09 +040084def createOpenstackEnv(url, credentialsId, project, project_domain="default",
Jakub Josefbd927322017-05-30 13:20:27 +000085 project_id="", user_domain="default", api_ver="2") {
iberezovskiyd4240b52017-02-20 17:18:28 +040086 def common = new com.mirantis.mk.Common()
Ales Komarek0e558ee2016-12-23 13:02:55 +010087 rcFile = "${env.WORKSPACE}/keystonerc"
Sergey Kolekonovba203982016-12-21 18:32:17 +040088 creds = common.getPasswordCredentials(credentialsId)
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030089 rc = """set +x
90export OS_USERNAME=${creds.username}
Ales Komarek0e558ee2016-12-23 13:02:55 +010091export OS_PASSWORD=${creds.password.toString()}
92export OS_TENANT_NAME=${project}
93export OS_AUTH_URL=${url}
94export OS_AUTH_STRATEGY=keystone
kairat_kushaev0a26bf72017-05-18 13:20:09 +040095export OS_PROJECT_NAME=${project}
Jakub Josefbd927322017-05-30 13:20:27 +000096export OS_PROJECT_ID=${project_id}
kairat_kushaev0a26bf72017-05-18 13:20:09 +040097export OS_PROJECT_DOMAIN_ID=${project_domain}
Jakub Josefbd927322017-05-30 13:20:27 +000098export OS_USER_DOMAIN_NAME=${user_domain}
99export OS_API_IDENTITY_VERSION=${api_ver}
Alexander Tivelkovf89a1882017-01-11 13:29:35 +0300100set -x
Ales Komarek0e558ee2016-12-23 13:02:55 +0100101"""
102 writeFile file: rcFile, text: rc
103 return rcFile
Sergey Kolekonovba203982016-12-21 18:32:17 +0400104}
105
106/**
107 * Run command with OpenStack env params and optional python env
108 *
109 * @param cmd Command to be executed
110 * @param env Environmental parameters with endpoint credentials
111 * @param path Optional path to virtualenv with specific clients
112 */
113def runOpenstackCommand(cmd, venv, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400114 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400115 openstackCmd = ". ${venv}; ${cmd}"
116 if (path) {
117 output = python.runVirtualenvCommand(path, openstackCmd)
118 }
119 else {
120 echo("[Command]: ${openstackCmd}")
121 output = sh (
122 script: openstackCmd,
123 returnStdout: true
124 ).trim()
125 }
126 return output
127}
128
129/**
130 * Get OpenStack Keystone token for current credentials
131 *
132 * @param env Connection parameters for OpenStack API endpoint
133 * @param path Optional path to the custom virtualenv
134 */
135def getKeystoneToken(client, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400136 def python = new com.mirantis.mk.Python()
Jakub Josefbd927322017-05-30 13:20:27 +0000137 cmd = "openstack token issue"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400138 outputTable = runOpenstackCommand(cmd, client, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100139 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400140 return output
141}
142
143/**
144 * Get OpenStack Keystone token for current credentials
145 *
146 * @param env Connection parameters for OpenStack API endpoint
147 * @param path Optional path to the custom virtualenv
148 */
149def createHeatEnv(file, environment = [], original_file = null) {
150 if (original_file) {
151 envString = readFile file: original_file
Tomáš Kukrál03029442017-02-21 17:14:29 +0100152 } else {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400153 envString = "parameters:\n"
154 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100155
Tomáš Kukrálc3964e52017-02-22 14:07:37 +0100156 p = entries(environment)
Tomáš Kukrálb1fe9642017-02-22 11:21:17 +0100157 for (int i = 0; i < p.size(); i++) {
158 envString = "${envString} ${p.get(i)[0]}: ${p.get(i)[1]}\n"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400159 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100160
Tomáš Kukrále19ddea2017-02-21 11:09:40 +0100161 echo("writing to env file:\n${envString}")
Sergey Kolekonovba203982016-12-21 18:32:17 +0400162 writeFile file: file, text: envString
163}
164
165/**
166 * Create new OpenStack Heat stack
167 *
168 * @param env Connection parameters for OpenStack API endpoint
169 * @param template HOT template for the new Heat stack
170 * @param environment Environmentale parameters of the new Heat stack
171 * @param name Name of the new Heat stack
172 * @param path Optional path to the custom virtualenv
173 */
174def createHeatStack(client, name, template, params = [], environment = null, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400175 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400176 templateFile = "${env.WORKSPACE}/template/template/${template}.hot"
177 if (environment) {
178 envFile = "${env.WORKSPACE}/template/env/${template}/${name}.env"
179 envSource = "${env.WORKSPACE}/template/env/${template}/${environment}.env"
180 createHeatEnv(envFile, params, envSource)
181 }
182 else {
183 envFile = "${env.WORKSPACE}/template/${name}.env"
184 createHeatEnv(envFile, params)
185 }
186 cmd = "heat stack-create -f ${templateFile} -e ${envFile} ${name}"
187 dir("${env.WORKSPACE}/template/template") {
188 outputTable = runOpenstackCommand(cmd, client, path)
189 }
Ales Komareke11e8792016-12-28 09:42:25 +0100190 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400191
192 i = 1
Ales Komarekdce8b472016-12-30 16:56:07 +0100193
Sergey Kolekonovba203982016-12-21 18:32:17 +0400194 while (true) {
195 status = getHeatStackStatus(client, name, path)
196 echo("[Heat Stack] Status: ${status}, Check: ${i}")
Ales Komarekdce8b472016-12-30 16:56:07 +0100197
198 if (status.indexOf('CREATE_FAILED') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400199 info = getHeatStackInfo(client, name, path)
200 throw new Exception(info.stack_status_reason)
201 }
Ales Komarekdce8b472016-12-30 16:56:07 +0100202 else if (status.indexOf('CREATE_COMPLETE') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400203 info = getHeatStackInfo(client, name, path)
204 echo(info.stack_status_reason)
205 break
206 }
207 sh('sleep 5s')
208 i++
209 }
210 echo("[Heat Stack] Status: ${status}")
211}
212
213/**
Jakub Josefdb4baf22017-05-10 15:16:09 +0200214 * Returns list of stacks for stack name filter
215 *
216 * @param client Connection parameters for OpenStack API endpoint
217 * @param filter Stack name filter
218 * @param path Optional path to the custom virtualenv
219 */
220def getStacksForNameContains(client, filter, path = null){
Jakub Josef6465fca2017-05-10 16:09:20 +0200221 cmd = 'heat stack-list | awk \'NR>3 {print $4}\' | sed \'$ d\' | grep ' + filter + '|| true'
Jakub Josefdb4baf22017-05-10 15:16:09 +0200222 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
223}
224
225
226/**
Jakub Josef5e238a22017-04-19 16:35:15 +0200227 * Get list of stack names with given stack status
228 *
Jakub Josefdb4baf22017-05-10 15:16:09 +0200229 * @param client Connection parameters for OpenStack API endpoint
Jakub Josef5e238a22017-04-19 16:35:15 +0200230 * @param status Stack status
231 * @param path Optional path to the custom virtualenv
232 */
233 def getStacksWithStatus(client, status, path = null) {
234 cmd = 'heat stack-list -f stack_status='+status+' | awk \'NR>3 {print $4}\' | sed \'$ d\''
235 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
236 }
237
238/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400239 * Get life cycle status for existing OpenStack Heat stack
240 *
241 * @param env Connection parameters for OpenStack API endpoint
242 * @param name Name of the managed Heat stack instance
243 * @param path Optional path to the custom virtualenv
244 */
245def getHeatStackStatus(client, name, path = null) {
246 cmd = 'heat stack-list | awk -v stack='+name+' \'{if ($4==stack) print $6}\''
247 return runOpenstackCommand(cmd, client, path)
248}
249
250/**
251 * Get info about existing OpenStack Heat stack
252 *
253 * @param env Connection parameters for OpenStack API endpoint
254 * @param name Name of the managed Heat stack instance
255 * @param path Optional path to the custom virtualenv
256 */
257def getHeatStackInfo(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400258 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400259 cmd = "heat stack-show ${name}"
260 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100261 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400262 return output
263}
264
265/**
266 * Get existing OpenStack Heat stack output parameter
267 *
268 * @param env Connection parameters for OpenStack API endpoint
269 * @param name Name of the managed Heat stack
270 * @param parameter Name of the output parameter
271 * @param path Optional path to the custom virtualenv
272 */
273def getHeatStackOutputParam(env, name, outputParam, path = null) {
274 cmd = "heat output-show ${name} ${outputParam}"
275 output = runOpenstackCommand(cmd, env, path)
Ales Komarekeedc2222017-01-03 10:10:03 +0100276 echo("${cmd}: ${output}")
Ales Komarek7ebd0062017-01-03 10:59:29 +0100277 return "${output}".substring(1, output.length()-1)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400278}
279
280/**
281 * List all resources from existing OpenStack Heat stack
282 *
283 * @param env Connection parameters for OpenStack API endpoint
284 * @param name Name of the managed Heat stack instance
285 * @param path Optional path to the custom virtualenv
286 */
287def getHeatStackResources(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400288 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400289 cmd = "heat resource-list ${name}"
290 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100291 output = python.parseTextTable(outputTable, 'list', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400292 return output
293}
294
295/**
296 * Get info about resource from existing OpenStack Heat stack
297 *
298 * @param env Connection parameters for OpenStack API endpoint
299 * @param name Name of the managed Heat stack instance
300 * @param path Optional path to the custom virtualenv
301 */
302def getHeatStackResourceInfo(env, name, resource, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400303 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400304 cmd = "heat resource-show ${name} ${resource}"
305 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100306 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400307 return output
308}
309
310/**
311 * Update existing OpenStack Heat stack
312 *
313 * @param env Connection parameters for OpenStack API endpoint
314 * @param name Name of the managed Heat stack instance
315 * @param path Optional path to the custom virtualenv
316 */
317def updateHeatStack(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400318 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400319 cmd = "heat stack-update ${name}"
320 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100321 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400322 return output
323}
324
325/**
326 * Delete existing OpenStack Heat stack
327 *
328 * @param env Connection parameters for OpenStack API endpoint
329 * @param name Name of the managed Heat stack instance
330 * @param path Optional path to the custom virtualenv
331 */
332def deleteHeatStack(env, name, path = null) {
333 cmd = "heat stack-delete ${name}"
334 outputTable = runOpenstackCommand(cmd, env, path)
335}
336
337/**
338 * Return list of servers from OpenStack Heat stack
339 *
340 * @param env Connection parameters for OpenStack API endpoint
341 * @param name Name of the managed Heat stack instance
342 * @param path Optional path to the custom virtualenv
343 */
344def getHeatStackServers(env, name, path = null) {
345 resources = heatGetStackResources(env, name, path)
346 servers = []
347 for (resource in resources) {
348 if (resource.resource_type == 'OS::Nova::Server') {
349 resourceName = resource.resource_name
350 server = heatGetStackResourceInfo(env, name, resourceName, path)
351 servers.add(server.attributes.name)
352 }
353 }
354 echo("[Stack ${name}] Servers: ${servers}")
355 return servers
356}