blob: c85493680e6f9c0ab564975806b25f57f4f3f2a3 [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
2
3/**
4 *
5 * SaltStack functions
6 *
7 */
8
9/**
10 * Login to Salt API and return auth token
11 *
12 * @param url Salt API server URL
13 * @param params Salt connection params
14 */
15def getSaltToken(url, params) {
16 def http = new com.mirantis.mk.http()
17 data = [
18 'username': params.creds.username,
19 'password': params.creds.password.toString(),
20 'eauth': 'pam'
21 ]
22 authToken = http.sendHttpGetRequest("${url}/login", data, ['Accept': '*/*'])['return'][0]['token']
23 return authToken
24}
25
26/**
27 * Salt connection and context parameters
28 *
29 * @param url Salt API server URL
30 * @param credentialsID ID of credentials store entry
31 */
32def createSaltConnection(url, credentialsId) {
33 def common = new com.mirantis.mk.common()
34 params = [
35 "url": url,
36 "credentialsId": credentialsId,
37 "authToken": null,
38 "creds": common.getPasswordCredentials(credentialsId)
39 ]
40 params["authToken"] = getSaltToken(url, params)
41
42 return params
43}
44
45/**
46 * Run action using Salt API
47 *
48 * @param master Salt connection object
49 * @param client Client type
50 * @param target Target specification, eg. for compound matches by Pillar
51 * data: ['expression': 'I@openssh:server', 'type': 'compound'])
52 * @param function Function to execute (eg. "state.sls")
53 * @param args Additional arguments to function
54 * @param kwargs Additional key-value arguments to function
55 */
Ales Komarekeeed8652017-01-03 15:20:21 +010056@NonCPS
Ales Komarekddb44d02017-01-04 13:17:04 +010057def runSaltCommand(master, client, target, function, batch = null, args = null, kwargs = null) {
Ales Komarekbfd10f42017-01-03 13:40:12 +010058 def http = new com.mirantis.mk.http()
59
Sergey Kolekonovba203982016-12-21 18:32:17 +040060 data = [
61 'tgt': target.expression,
62 'fun': function,
63 'client': client,
64 'expr_form': target.type,
65 ]
66
Ales Komarekddb44d02017-01-04 13:17:04 +010067 if (batch) {
68 data['batch'] = batch
69 }
70
Sergey Kolekonovba203982016-12-21 18:32:17 +040071 if (args) {
72 data['arg'] = args
73 }
Ales Komarekddb44d02017-01-04 13:17:04 +010074
Sergey Kolekonovba203982016-12-21 18:32:17 +040075 if (kwargs) {
76 data['kwarg'] = kwargs
77 }
78
79 headers = [
80 'X-Auth-Token': "${master.authToken}"
81 ]
82
83 return http.sendHttpPostRequest("${master.url}/", data, headers)
84}
85
86def getSaltPillar(master, target, pillar) {
87 def out = runSaltCommand(master, 'local', target, 'pillar.get', [pillar.replace('.', ':')])
88 return out
89}
90
91def enforceSaltState(master, target, state, output = false) {
92 def run_states
93 if (state instanceof String) {
94 run_states = state
95 } else {
96 run_states = state.join(',')
97 }
98
Ales Komarekddb44d02017-01-04 13:17:04 +010099 def out = runSaltCommand(master, 'local', target, 'state.sls', null, [run_states])
Sergey Kolekonovba203982016-12-21 18:32:17 +0400100 try {
101 checkSaltResult(out)
102 } finally {
103 if (output == true) {
104 printSaltResult(out)
105 }
106 }
107 return out
108}
109
110def runSaltCmd(master, target, cmd) {
Ales Komarekddb44d02017-01-04 13:17:04 +0100111 return runSaltCommand(master, 'local', target, 'cmd.run', null, [cmd])
Sergey Kolekonovba203982016-12-21 18:32:17 +0400112}
113
114def syncSaltAll(master, target) {
115 return runSaltCommand(master, 'local', target, 'saltutil.sync_all')
116}
117
118def enforceSaltApply(master, target, output = false) {
119 def out = runSaltCommand(master, 'local', target, 'state.highstate')
120 try {
121 checkSaltResult(out)
122 } finally {
123 if (output == true) {
124 printSaltResult(out)
125 }
126 }
127 return out
128}
129
130def generateSaltNodeKey(master, target, host, keysize = 4096) {
131 args = [host]
132 kwargs = ['keysize': keysize]
Ales Komarekddb44d02017-01-04 13:17:04 +0100133 return runSaltCommand(master, 'wheel', target, 'key.gen_accept', null, args, kwargs)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400134}
135
136def generateSaltNodeMetadata(master, target, host, classes, parameters) {
137 args = [host, '_generated']
138 kwargs = ['classes': classes, 'parameters': parameters]
Ales Komarekddb44d02017-01-04 13:17:04 +0100139 return runSaltCommand(master, 'local', target, 'reclass.node_create', null, args, kwargs)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400140}
141
142def orchestrateSaltSystem(master, target, orchestrate) {
Ales Komarekddb44d02017-01-04 13:17:04 +0100143 return runSaltCommand(master, 'runner', target, 'state.orchestrate', null, [orchestrate])
Sergey Kolekonovba203982016-12-21 18:32:17 +0400144}
145
146/**
147 * Check result for errors and throw exception if any found
148 *
149 * @param result Parsed response of Salt API
150 */
151def checkSaltResult(result) {
152 for (entry in result['return']) {
153 if (!entry) {
154 throw new Exception("Salt API returned empty response: ${result}")
155 }
156 for (node in entry) {
157 for (resource in node.value) {
158 if (resource instanceof String || resource.value.result.toString().toBoolean() != true) {
159 throw new Exception("Salt state on node ${node.key} failed: ${node.value}")
160 }
161 }
162 }
163 }
164}
165
166/**
167 * Print Salt run results in human-friendly form
168 *
169 * @param result Parsed response of Salt API
170 * @param onlyChanges If true (default), print only changed resources
171 * @param raw Simply pretty print what we have, no additional
172 * parsing
173 */
174def printSaltResult(result, onlyChanges = true, raw = false) {
175 if (raw == true) {
176 print new groovy.json.JsonBuilder(result).toPrettyString()
177 } else {
178 def out = [:]
179 for (entry in result['return']) {
180 for (node in entry) {
181 out[node.key] = [:]
182 for (resource in node.value) {
183 if (resource instanceof String) {
184 out[node.key] = node.value
185 } else if (resource.value.result.toString().toBoolean() == false || resource.value.changes || onlyChanges == false) {
186 out[node.key][resource.key] = resource.value
187 }
188 }
189 }
190 }
191
192 for (node in out) {
193 if (node.value) {
194 println "Node ${node.key} changes:"
195 print new groovy.json.JsonBuilder(node.value).toPrettyString()
196 } else {
197 println "No changes for node ${node.key}"
198 }
199 }
200 }
201}
202
Sergey Kolekonovba203982016-12-21 18:32:17 +0400203
Ales Komarekddb44d02017-01-04 13:17:04 +0100204def runSaltProcessStep(master, tgt, fun, arg = [], batch = null) {
205 if (batch) {
206 result = runSaltCommand(master, 'local_batch', ['expression': tgt, 'type': 'compound'], fun, String.valueOf(batch), arg)
207 }
208 else {
209 result = runSaltCommand(master, 'local', ['expression': tgt, 'type': 'compound'], fun, batch, arg)
210 }
211 echo("${result}")
Sergey Kolekonovba203982016-12-21 18:32:17 +0400212}
213
Sergey Kolekonovba203982016-12-21 18:32:17 +0400214
Ales Komarekddb44d02017-01-04 13:17:04 +0100215def validateFoundationInfra(master) {
216 runSaltProcessStep(master, 'I@salt:master', 'cmd.run', ['salt-key'])
217 runSaltProcessStep(master, 'I@salt:minion', 'test.version')
218 runSaltProcessStep(master, 'I@salt:master', 'cmd.run', ['reclass-salt --top'])
219 runSaltProcessStep(master, 'I@reclass:storage', 'reclass.inventory')
220 runSaltProcessStep(master, 'I@salt:minion', 'state.show_top')
221}
Sergey Kolekonovba203982016-12-21 18:32:17 +0400222
Ales Komarekddb44d02017-01-04 13:17:04 +0100223
224def installFoundationInfra(master) {
225 runSaltProcessStep(master, 'I@salt:master', 'state.sls', ['salt.master,reclass'])
226 runSaltProcessStep(master, 'I@linux:system', 'saltutil.refresh_pillar')
227 runSaltProcessStep(master, 'I@linux:system', 'saltutil.sync_all')
228 runSaltProcessStep(master, 'I@linux:system', 'state.sls', ['linux,openssh,salt.minion,ntp'])
229}
230
231def installOpenstackInfra(master) {
232 // Install keepaliveds
233 runSaltProcessStep(master, 'I@keepalived:cluster', 'state.sls', ['keepalived'], 1)
234 // Check the keepalived VIPs
235 runSaltProcessStep(master, 'I@keepalived:cluster', 'cmd.run', ['ip a | grep 172.16.10.2'])
236 // Install glusterfs
237 runSaltProcessStep(master, 'I@glusterfs:server', 'state.sls', ['glusterfs.server.service'])
238 runSaltProcessStep(master, 'I@glusterfs:server', 'state.sls', ['glusterfs.server.setup'], 1)
239 runSaltProcessStep(master, 'I@glusterfs:server', 'cmd.run', ['gluster peer status'])
240 runSaltProcessStep(master, 'I@glusterfs:server', 'cmd.run', ['gluster volume status'])
241 // Install rabbitmq
242 runSaltProcessStep(master, 'I@rabbitmq:server', 'state.sls', ['rabbitmq'])
243 // Check the rabbitmq status
244 runSaltProcessStep(master, 'I@rabbitmq:server', 'cmd.run', ['rabbitmqctl cluster_status'])
245 // Install galera
246 runSaltProcessStep(master, 'I@galera:master', 'state.sls', ['galera'])
247 runSaltProcessStep(master, 'I@galera:slave', 'state.sls', ['galera'])
248 // Check galera status
249 runSaltProcessStep(master, 'I@galera:master', 'mysql.status')
250 runSaltProcessStep(master, 'I@galera:slave', 'mysql.status')
251 // Install haproxy
252 runSaltProcessStep(master, 'I@haproxy:proxy', 'state.sls', ['haproxy'])
253 runSaltProcessStep(master, 'I@haproxy:proxy', 'service.status', ['haproxy'])
254 runSaltProcessStep(master, 'I@haproxy:proxy', 'service.restart', ['rsyslog'])
255 // Install memcached
256 runSaltProcessStep(master, 'I@memcached:server', 'state.sls', ['memcached'])
257}
258
259
260def installOpenstackMkControl(master) {
261 // setup keystone service
262 runSaltProcessStep(master, 'I@keystone:server', 'state.sls', ['keystone.server'], 1)
263 // populate keystone services/tenants/roles/users
264 runSaltProcessStep(master, 'I@keystone:client', 'state.sls', ['keystone.client'])
265 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; keystone service-list'])
266 // Install glance and ensure glusterfs clusters
267 runSaltProcessStep(master, 'I@glance:server', 'state.sls', ['glance.server'], 1)
268 runSaltProcessStep(master, 'I@glance:server', 'state.sls', ['glusterfs.client'])
269 // Update fernet tokens before doing request on keystone server
270 runSaltProcessStep(master, 'I@keystone:server', 'state.sls', ['keystone.server'])
271 // Check glance service
272 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; glance image-list'])
273 // Install and check nova service
274 runSaltProcessStep(master, 'I@nova:controller', 'state.sls', ['nova'], 1)
275 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; nova service-list'])
276 // Install and check cinder service
277 runSaltProcessStep(master, 'I@cinder:controller', 'state.sls', ['cinder'], 1)
278 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; cinder list'])
279 // Install neutron service
280 runSaltProcessStep(master, 'I@neutron:server', 'state.sls', ['neutron'], 1)
281 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; neutron agent-list'])
282 // Install heat service
283 runSaltProcessStep(master, 'I@heat:server', 'state.sls', ['heat'], 1)
284 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; heat resource-type-list'])
285 // Install horizon dashboard
286 runSaltProcessStep(master, 'I@horizon:server', 'state.sls', ['horizon'])
287 runSaltProcessStep(master, 'I@nginx:server', 'state.sls', ['nginx'])
288}
289
290
291def installOpenstackMkNetwork(master) {
292 // Install opencontrail database services
293 runSaltProcessStep(master, 'I@opencontrail:database', 'state.sls', ['opencontrail.database'], 1)
294 // Install opencontrail control services
295 runSaltProcessStep(master, 'I@opencontrail:control', 'state.sls', ['opencontrail'], 1)
296 // Provision opencontrail control services
297 runSaltProcessStep(master, 'I@opencontrail:control:id:1', 'cmd.run', ['/usr/share/contrail-utils/provision_control.py --api_server_ip 172.16.10.254 --api_server_port 8082 --host_name ctl01 --host_ip 172.16.10.101 --router_asn 64512 --admin_password workshop --admin_user admin --admin_tenant_name admin --oper add'])
298 runSaltProcessStep(master, 'I@opencontrail:control:id:1', 'cmd.run', ['/usr/share/contrail-utils/provision_control.py --api_server_ip 172.16.10.254 --api_server_port 8082 --host_name ctl02 --host_ip 172.16.10.102 --router_asn 64512 --admin_password workshop --admin_user admin --admin_tenant_name admin --oper add'])
299 runSaltProcessStep(master, 'I@opencontrail:control:id:1', 'cmd.run', ['/usr/share/contrail-utils/provision_control.py --api_server_ip 172.16.10.254 --api_server_port 8082 --host_name ctl03 --host_ip 172.16.10.103 --router_asn 64512 --admin_password workshop --admin_user admin --admin_tenant_name admin --oper add'])
300 // Test opencontrail
301 runSaltProcessStep(master, 'I@opencontrail:control', 'cmd.run', ['contrail-status'])
302 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; neutron net-list'])
303 runSaltProcessStep(master, 'I@keystone:server', 'cmd.run', ['. /root/keystonerc; nova net-list'])
304}
305
306
307def installOpenstackMkCompute(master) {
308 // Configure compute nodes
309 runSaltProcessStep(master, 'I@nova:compute', 'state.apply')
310 runSaltProcessStep(master, 'I@nova:compute', 'state.apply')
311 // Provision opencontrail virtual routers
312 runSaltProcessStep(master, 'I@opencontrail:control:id:1', 'cmd.run', ['/usr/share/contrail-utils/provision_vrouter.py --host_name cmp01 --host_ip 172.16.10.105 --api_server_ip 172.16.10.254 --oper add --admin_user admin --admin_password workshop --admin_tenant_name admin'])
313 runSaltProcessStep(master, 'I@nova:compute', 'system.reboot')
314}
315
316
317def installOpenstackMcpInfra(master) {
318 // Comment nameserver
319 runSaltProcessStep(master, 'I@kubernetes:master', 'cmd.run', ["sed -i 's/nameserver 10.254.0.10/#nameserver 10.254.0.10/g' /etc/resolv.conf"])
320 // Install glusterfs
321 runSaltProcessStep(master, 'I@glusterfs:server', 'state.sls', ['glusterfs.server.service'])
322 // Install keepalived
323 runSaltProcessStep(master, 'I@keepalived:cluster', 'state.sls', ['keepalived'], 1)
324 // Check the keepalived VIPs
325 runSaltProcessStep(master, 'I@keepalived:cluster', 'cmd.run', ['ip a | grep 172.16.10.2'])
326 // Setup glusterfs
327 runSaltProcessStep(master, 'I@glusterfs:server', 'state.sls', ['glusterfs.server.setup'], 1)
328 runSaltProcessStep(master, 'I@glusterfs:server', 'cmd.run', ['gluster peer status'])
329 runSaltProcessStep(master, 'I@glusterfs:server', 'cmd.run', ['gluster volume status'])
330 // Install haproxy
331 runSaltProcessStep(master, 'I@haproxy:proxy', 'state.sls', ['haproxy'])
332 runSaltProcessStep(master, 'I@haproxy:proxy', 'service.status', ['haproxy'])
333 // Install docker
334 runSaltProcessStep(master, 'I@docker:host', 'state.sls', ['docker.host'])
335 runSaltProcessStep(master, 'I@docker:host', 'cmd.run', ['docker ps'])
336 // Install bird
337 runSaltProcessStep(master, 'I@bird:server', 'state.sls', ['bird'])
338 // Install etcd
339 runSaltProcessStep(master, 'I@etcd:server', 'state.sls', ['etcd.server.service'])
340 runSaltProcessStep(master, 'I@etcd:server', 'cmd.run', ['etcdctl cluster-health'])
341}
342
343
344def installOpenstackMcpControl(master) {
345 // Pull Calico image
346 runSaltProcessStep(master, 'I@kubernetes:pool', 'dockerng.pull', ['calico/node:latest'])
347 // Install Kubernetes and Calico
348 runSaltProcessStep(master, 'I@kubernetes:master', 'state.sls', ['kubernetes.master.service,kubernetes.master.kube-addons'])
349 runSaltProcessStep(master, 'I@kubernetes:pool', 'state.sls', ['kubernetes.pool'])
350 runSaltProcessStep(master, 'I@kubernetes:pool', 'cmd.run', ['calicoctl status'])
351 // Setup NAT for Calico
352 runSaltProcessStep(master, 'I@kubernetes:master', 'state.sls', ['etcd.server.setup'])
353 // Run whole k8s controller
354 runSaltProcessStep(master, 'I@kubernetes:master', 'state.sls', ['kubernetes.controller'])
355 // Run whole k8s controller
356 runSaltProcessStep(master, 'I@kubernetes:master', 'state.sls', ['kubernetes'], 1)
357 // Revert comment nameserver
358 runSaltProcessStep(master, 'I@kubernetes:master', 'cmd.run', ["sed -i 's/nameserver 10.254.0.10/#nameserver 10.254.0.10/g' /etc/resolv.conf"])
359}
360
361
362def installOpenstackMcpCompute(master) {
363 // Install opencontrail
364 runSaltProcessStep(master, 'I@opencontrail:compute', 'state.sls', ['opencontrail'])
365 // Reboot compute nodes
366 runSaltProcessStep(master, 'I@opencontrail:compute', 'system.reboot')
367}
368
369
370def installStacklightControl(master) {
371 runSaltProcessStep(master, 'I@elasticsearch:server', 'state.sls', ['elasticsearch.server'], 1)
372 runSaltProcessStep(master, 'I@influxdb:server', 'state.sls', ['influxdb'], 1)
373 runSaltProcessStep(master, 'I@kibana:server', 'state.sls', ['kibana.server'], 1)
374 runSaltProcessStep(master, 'I@grafana:server', 'state.sls', ['grafana'], 1)
375 runSaltProcessStep(master, 'I@nagios:server', 'state.sls', ['nagios'], 1)
376 runSaltProcessStep(master, 'I@elasticsearch:client', 'state.sls', ['elasticsearch.client'], 1)
377 runSaltProcessStep(master, 'I@kibana:client', 'state.sls', ['kibana.client'], 1)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400378}
379
380/**
381 * Print Salt state run results in human-friendly form
382 *
383 * @param result Parsed response of Salt API
384 * @param onlyChanges If true (default), print only changed resources
385 * parsing
386 */
387def printSaltStateResult(result, onlyChanges = true) {
388 def out = [:]
389 for (entry in result['return']) {
390 for (node in entry) {
391 out[node.key] = [:]
392 for (resource in node.value) {
393 if (resource instanceof String) {
394 out[node.key] = node.value
395 } else if (resource.value.result.toString().toBoolean() == false || resource.value.changes || onlyChanges == false) {
396 out[node.key][resource.key] = resource.value
397 }
398 }
399 }
400 }
401
402 for (node in out) {
403 if (node.value) {
404 println "Node ${node.key} changes:"
405 print new groovy.json.JsonBuilder(node.value).toPrettyString()
406 } else {
407 println "No changes for node ${node.key}"
408 }
409 }
410}
411
412/**
413 * Print Salt state run results in human-friendly form
414 *
415 * @param result Parsed response of Salt API
416 * @param onlyChanges If true (default), print only changed resources
417 * parsing
418 */
419def printSaltCommandResult(result, onlyChanges = true) {
420 def out = [:]
421 for (entry in result['return']) {
422 for (node in entry) {
423 out[node.key] = [:]
424 for (resource in node.value) {
425 out[node.key] = node.value
426 }
427 }
428 }
429
430 for (node in out) {
431 if (node.value) {
432 println "Node ${node.key} changes:"
433 print new groovy.json.JsonBuilder(node.value).toPrettyString()
434 } else {
435 println "No changes for node ${node.key}"
436 }
437 }
438}