blob: 6b1c94a1a363bb6af204bf05a1608aac92430dd8 [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 */
kairat_kushaev0a26bf72017-05-18 13:20:09 +040067def createOpenstackEnv(url, credentialsId, project, project_domain="default",
68 user_domain= "default") {
iberezovskiyd4240b52017-02-20 17:18:28 +040069 def common = new com.mirantis.mk.Common()
Ales Komarek0e558ee2016-12-23 13:02:55 +010070 rcFile = "${env.WORKSPACE}/keystonerc"
Sergey Kolekonovba203982016-12-21 18:32:17 +040071 creds = common.getPasswordCredentials(credentialsId)
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030072 rc = """set +x
73export OS_USERNAME=${creds.username}
Ales Komarek0e558ee2016-12-23 13:02:55 +010074export OS_PASSWORD=${creds.password.toString()}
75export OS_TENANT_NAME=${project}
76export OS_AUTH_URL=${url}
77export OS_AUTH_STRATEGY=keystone
kairat_kushaev0a26bf72017-05-18 13:20:09 +040078//introduce keystone v3 vars
79export OS_PROJECT_NAME=${project}
80export OS_PROJECT_DOMAIN_ID=${project_domain}
81export OS_USER_DOMAIN_ID=${user_domain}
Alexander Tivelkovf89a1882017-01-11 13:29:35 +030082set -x
Ales Komarek0e558ee2016-12-23 13:02:55 +010083"""
84 writeFile file: rcFile, text: rc
85 return rcFile
Sergey Kolekonovba203982016-12-21 18:32:17 +040086}
87
88/**
89 * Run command with OpenStack env params and optional python env
90 *
91 * @param cmd Command to be executed
92 * @param env Environmental parameters with endpoint credentials
93 * @param path Optional path to virtualenv with specific clients
94 */
95def runOpenstackCommand(cmd, venv, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +040096 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +040097 openstackCmd = ". ${venv}; ${cmd}"
98 if (path) {
99 output = python.runVirtualenvCommand(path, openstackCmd)
100 }
101 else {
102 echo("[Command]: ${openstackCmd}")
103 output = sh (
104 script: openstackCmd,
105 returnStdout: true
106 ).trim()
107 }
108 return output
109}
110
111/**
112 * Get OpenStack Keystone token for current credentials
113 *
114 * @param env Connection parameters for OpenStack API endpoint
115 * @param path Optional path to the custom virtualenv
116 */
117def getKeystoneToken(client, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400118 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400119 cmd = "keystone token-get"
120 outputTable = runOpenstackCommand(cmd, client, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100121 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400122 return output
123}
124
125/**
126 * Get OpenStack Keystone token for current credentials
127 *
128 * @param env Connection parameters for OpenStack API endpoint
129 * @param path Optional path to the custom virtualenv
130 */
131def createHeatEnv(file, environment = [], original_file = null) {
132 if (original_file) {
133 envString = readFile file: original_file
Tomáš Kukrál03029442017-02-21 17:14:29 +0100134 } else {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400135 envString = "parameters:\n"
136 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100137
Tomáš Kukrálc3964e52017-02-22 14:07:37 +0100138 p = entries(environment)
Tomáš Kukrálb1fe9642017-02-22 11:21:17 +0100139 for (int i = 0; i < p.size(); i++) {
140 envString = "${envString} ${p.get(i)[0]}: ${p.get(i)[1]}\n"
Sergey Kolekonovba203982016-12-21 18:32:17 +0400141 }
Tomáš Kukrál03029442017-02-21 17:14:29 +0100142
Tomáš Kukrále19ddea2017-02-21 11:09:40 +0100143 echo("writing to env file:\n${envString}")
Sergey Kolekonovba203982016-12-21 18:32:17 +0400144 writeFile file: file, text: envString
145}
146
147/**
148 * Create new OpenStack Heat stack
149 *
150 * @param env Connection parameters for OpenStack API endpoint
151 * @param template HOT template for the new Heat stack
152 * @param environment Environmentale parameters of the new Heat stack
153 * @param name Name of the new Heat stack
154 * @param path Optional path to the custom virtualenv
155 */
156def createHeatStack(client, name, template, params = [], environment = null, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400157 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400158 templateFile = "${env.WORKSPACE}/template/template/${template}.hot"
159 if (environment) {
160 envFile = "${env.WORKSPACE}/template/env/${template}/${name}.env"
161 envSource = "${env.WORKSPACE}/template/env/${template}/${environment}.env"
162 createHeatEnv(envFile, params, envSource)
163 }
164 else {
165 envFile = "${env.WORKSPACE}/template/${name}.env"
166 createHeatEnv(envFile, params)
167 }
168 cmd = "heat stack-create -f ${templateFile} -e ${envFile} ${name}"
169 dir("${env.WORKSPACE}/template/template") {
170 outputTable = runOpenstackCommand(cmd, client, path)
171 }
Ales Komareke11e8792016-12-28 09:42:25 +0100172 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400173
174 i = 1
Ales Komarekdce8b472016-12-30 16:56:07 +0100175
Sergey Kolekonovba203982016-12-21 18:32:17 +0400176 while (true) {
177 status = getHeatStackStatus(client, name, path)
178 echo("[Heat Stack] Status: ${status}, Check: ${i}")
Ales Komarekdce8b472016-12-30 16:56:07 +0100179
180 if (status.indexOf('CREATE_FAILED') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400181 info = getHeatStackInfo(client, name, path)
182 throw new Exception(info.stack_status_reason)
183 }
Ales Komarekdce8b472016-12-30 16:56:07 +0100184 else if (status.indexOf('CREATE_COMPLETE') != -1) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400185 info = getHeatStackInfo(client, name, path)
186 echo(info.stack_status_reason)
187 break
188 }
189 sh('sleep 5s')
190 i++
191 }
192 echo("[Heat Stack] Status: ${status}")
193}
194
195/**
Jakub Josefdb4baf22017-05-10 15:16:09 +0200196 * Returns list of stacks for stack name filter
197 *
198 * @param client Connection parameters for OpenStack API endpoint
199 * @param filter Stack name filter
200 * @param path Optional path to the custom virtualenv
201 */
202def getStacksForNameContains(client, filter, path = null){
Jakub Josef6465fca2017-05-10 16:09:20 +0200203 cmd = 'heat stack-list | awk \'NR>3 {print $4}\' | sed \'$ d\' | grep ' + filter + '|| true'
Jakub Josefdb4baf22017-05-10 15:16:09 +0200204 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
205}
206
207
208/**
Jakub Josef5e238a22017-04-19 16:35:15 +0200209 * Get list of stack names with given stack status
210 *
Jakub Josefdb4baf22017-05-10 15:16:09 +0200211 * @param client Connection parameters for OpenStack API endpoint
Jakub Josef5e238a22017-04-19 16:35:15 +0200212 * @param status Stack status
213 * @param path Optional path to the custom virtualenv
214 */
215 def getStacksWithStatus(client, status, path = null) {
216 cmd = 'heat stack-list -f stack_status='+status+' | awk \'NR>3 {print $4}\' | sed \'$ d\''
217 return runOpenstackCommand(cmd, client, path).trim().tokenize("\n")
218 }
219
220/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400221 * Get life cycle status for existing OpenStack Heat stack
222 *
223 * @param env Connection parameters for OpenStack API endpoint
224 * @param name Name of the managed Heat stack instance
225 * @param path Optional path to the custom virtualenv
226 */
227def getHeatStackStatus(client, name, path = null) {
228 cmd = 'heat stack-list | awk -v stack='+name+' \'{if ($4==stack) print $6}\''
229 return runOpenstackCommand(cmd, client, path)
230}
231
232/**
233 * Get info about existing OpenStack Heat stack
234 *
235 * @param env Connection parameters for OpenStack API endpoint
236 * @param name Name of the managed Heat stack instance
237 * @param path Optional path to the custom virtualenv
238 */
239def getHeatStackInfo(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400240 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400241 cmd = "heat stack-show ${name}"
242 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100243 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400244 return output
245}
246
247/**
248 * Get existing OpenStack Heat stack output parameter
249 *
250 * @param env Connection parameters for OpenStack API endpoint
251 * @param name Name of the managed Heat stack
252 * @param parameter Name of the output parameter
253 * @param path Optional path to the custom virtualenv
254 */
255def getHeatStackOutputParam(env, name, outputParam, path = null) {
256 cmd = "heat output-show ${name} ${outputParam}"
257 output = runOpenstackCommand(cmd, env, path)
Ales Komarekeedc2222017-01-03 10:10:03 +0100258 echo("${cmd}: ${output}")
Ales Komarek7ebd0062017-01-03 10:59:29 +0100259 return "${output}".substring(1, output.length()-1)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400260}
261
262/**
263 * List all resources from existing OpenStack Heat stack
264 *
265 * @param env Connection parameters for OpenStack API endpoint
266 * @param name Name of the managed Heat stack instance
267 * @param path Optional path to the custom virtualenv
268 */
269def getHeatStackResources(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400270 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400271 cmd = "heat resource-list ${name}"
272 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100273 output = python.parseTextTable(outputTable, 'list', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400274 return output
275}
276
277/**
278 * Get info about resource from existing OpenStack Heat stack
279 *
280 * @param env Connection parameters for OpenStack API endpoint
281 * @param name Name of the managed Heat stack instance
282 * @param path Optional path to the custom virtualenv
283 */
284def getHeatStackResourceInfo(env, name, resource, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400285 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400286 cmd = "heat resource-show ${name} ${resource}"
287 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100288 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400289 return output
290}
291
292/**
293 * Update existing OpenStack Heat stack
294 *
295 * @param env Connection parameters for OpenStack API endpoint
296 * @param name Name of the managed Heat stack instance
297 * @param path Optional path to the custom virtualenv
298 */
299def updateHeatStack(env, name, path = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +0400300 def python = new com.mirantis.mk.Python()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400301 cmd = "heat stack-update ${name}"
302 outputTable = runOpenstackCommand(cmd, env, path)
Ales Komareke11e8792016-12-28 09:42:25 +0100303 output = python.parseTextTable(outputTable, 'item', 'prettytable', path)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400304 return output
305}
306
307/**
308 * Delete existing OpenStack Heat stack
309 *
310 * @param env Connection parameters for OpenStack API endpoint
311 * @param name Name of the managed Heat stack instance
312 * @param path Optional path to the custom virtualenv
313 */
314def deleteHeatStack(env, name, path = null) {
315 cmd = "heat stack-delete ${name}"
316 outputTable = runOpenstackCommand(cmd, env, path)
317}
318
319/**
320 * Return list of servers from OpenStack Heat stack
321 *
322 * @param env Connection parameters for OpenStack API endpoint
323 * @param name Name of the managed Heat stack instance
324 * @param path Optional path to the custom virtualenv
325 */
326def getHeatStackServers(env, name, path = null) {
327 resources = heatGetStackResources(env, name, path)
328 servers = []
329 for (resource in resources) {
330 if (resource.resource_type == 'OS::Nova::Server') {
331 resourceName = resource.resource_name
332 server = heatGetStackResourceInfo(env, name, resourceName, path)
333 servers.add(server.attributes.name)
334 }
335 }
336 echo("[Stack ${name}] Servers: ${servers}")
337 return servers
338}