blob: 8ec29791e979bf8d814a572f17b7f1e251c4d54e [file] [log] [blame]
Jakub Josef79ecec32017-02-17 14:36:28 +01001package com.mirantis.mk
2
3/**
4 * Salt functions
5 *
6*/
7
8/**
9 * Salt connection and context parameters
10 *
11 * @param url Salt API server URL
12 * @param credentialsID ID of credentials store entry
13 */
14def connection(url, credentialsId = "salt") {
15 def common = new com.mirantis.mk.Common();
16 params = [
17 "url": url,
18 "credentialsId": credentialsId,
19 "authToken": null,
20 "creds": common.getCredentials(credentialsId)
21 ]
22 params["authToken"] = saltLogin(params)
23
24 return params
25}
26
27/**
28 * Login to Salt API, return auth token
29 *
30 * @param master Salt connection object
31 */
32def saltLogin(master) {
33 data = [
34 'username': master.creds.username,
35 'password': master.creds.password.toString(),
36 'eauth': 'pam'
37 ]
38 authToken = restGet(master, '/login', data)['return'][0]['token']
39 return authToken
40}
41
42/**
43 * Run action using Salt API
44 *
45 * @param master Salt connection object
46 * @param client Client type
47 * @param target Target specification, eg. for compound matches by Pillar
48 * data: ['expression': 'I@openssh:server', 'type': 'compound'])
49 * @param function Function to execute (eg. "state.sls")
50 * @param batch
51 * @param args Additional arguments to function
52 * @param kwargs Additional key-value arguments to function
53 */
54@NonCPS
55def runSaltCommand(master, client, target, function, batch = null, args = null, kwargs = null) {
iberezovskiyd4240b52017-02-20 17:18:28 +040056 def http = new com.mirantis.mk.Http()
Jakub Josef79ecec32017-02-17 14:36:28 +010057
58 data = [
59 'tgt': target.expression,
60 'fun': function,
61 'client': client,
62 'expr_form': target.type,
63 ]
64
65 if (batch) {
66 data['batch'] = batch
67 }
68
69 if (args) {
70 data['arg'] = args
71 }
72
73 if (kwargs) {
74 data['kwarg'] = kwargs
75 }
76
77 headers = [
78 'X-Auth-Token': "${master.authToken}"
79 ]
80
81 return http.sendHttpPostRequest("${master.url}/", data, headers)
82}
83
84def pillarGet(master, target, pillar) {
85 def out = runSaltCommand(master, 'local', target, 'pillar.get', null, [pillar.replace('.', ':')])
86 return out
87}
88
89def enforceState(master, target, state, output = false) {
90 def run_states
91 if (state instanceof String) {
92 run_states = state
93 } else {
94 run_states = state.join(',')
95 }
96
97 def out = runSaltCommand(master, 'local', target, 'state.sls', null, [run_states])
98 try {
99 checkResult(out)
100 } finally {
101 if (output == true) {
102 printResult(out)
103 }
104 }
105 return out
106}
107
108def cmdRun(master, target, cmd) {
109 def out = runSaltCommand(master, 'local', target, 'cmd.run', null, [cmd])
110 return out
111}
112
113def syncAll(master, target) {
114 return runSaltCommand(master, 'local', target, 'saltutil.sync_all')
115}
116
117def enforceHighstate(master, target, output = false) {
118 def out = runSaltCommand(master, 'local', target, 'state.highstate')
119 try {
120 checkResult(out)
121 } finally {
122 if (output == true) {
123 printResult(out)
124 }
125 }
126 return out
127}
128
129def generateNodeKey(master, target, host, keysize = 4096) {
130 args = [host]
131 kwargs = ['keysize': keysize]
132 return runSaltCommand(master, 'wheel', target, 'key.gen_accept', args, kwargs)
133}
134
135def generateNodeMetadata(master, target, host, classes, parameters) {
136 args = [host, '_generated']
137 kwargs = ['classes': classes, 'parameters': parameters]
138 return runSaltCommand(master, 'local', target, 'reclass.node_create', args, kwargs)
139}
140
141def orchestrateSystem(master, target, orchestrate) {
142 return runSaltCommand(master, 'runner', target, 'state.orchestrate', [orchestrate])
143}
144
145def runSaltProcessStep(master, tgt, fun, arg = [], batch = null) {
146 if (batch) {
147 result = runSaltCommand(master, 'local_batch', ['expression': tgt, 'type': 'compound'], fun, String.valueOf(batch), arg)
148 }
149 else {
150 result = runSaltCommand(master, 'local', ['expression': tgt, 'type': 'compound'], fun, batch, arg)
151 }
152 echo("${result}")
153}
154
155/**
156 * Check result for errors and throw exception if any found
157 *
158 * @param result Parsed response of Salt API
159 */
160def checkResult(result) {
161 for (entry in result['return']) {
162 if (!entry) {
163 throw new Exception("Salt API returned empty response: ${result}")
164 }
165 for (node in entry) {
166 for (resource in node.value) {
167 if (resource instanceof String || resource.value.result.toString().toBoolean() != true) {
168 throw new Exception("Salt state on node ${node.key} failed: ${node.value}")
169 }
170 }
171 }
172 }
173}
174
175/**
176 * Print Salt state run results in human-friendly form
177 *
178 * @param result Parsed response of Salt API
179 * @param onlyChanges If true (default), print only changed resources
180 * parsing
181 */
182def printSaltStateResult(result, onlyChanges = true) {
183 def out = [:]
184 for (entry in result['return']) {
185 for (node in entry) {
186 out[node.key] = [:]
187 for (resource in node.value) {
188 if (resource instanceof String) {
189 out[node.key] = node.value
190 } else if (resource.value.result.toString().toBoolean() == false || resource.value.changes || onlyChanges == false) {
191 out[node.key][resource.key] = resource.value
192 }
193 }
194 }
195 }
196
197 for (node in out) {
198 if (node.value) {
199 println "Node ${node.key} changes:"
200 print new groovy.json.JsonBuilder(node.value).toPrettyString()
201 } else {
202 println "No changes for node ${node.key}"
203 }
204 }
205}
206
207/**
208 * Print Salt state run results in human-friendly form
209 *
210 * @param result Parsed response of Salt API
211 * @param onlyChanges If true (default), print only changed resources
212 * parsing
213 */
214def printSaltCommandResult(result, onlyChanges = true) {
215 def out = [:]
216 for (entry in result['return']) {
217 for (node in entry) {
218 out[node.key] = [:]
219 for (resource in node.value) {
220 out[node.key] = node.value
221 }
222 }
223 }
224
225 for (node in out) {
226 if (node.value) {
227 println "Node ${node.key} changes:"
228 print new groovy.json.JsonBuilder(node.value).toPrettyString()
229 } else {
230 println "No changes for node ${node.key}"
231 }
232 }
233}