| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 1 | package com.mirantis.mk | 
 | 2 |  | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 3 | import com.cloudbees.groovy.cps.NonCPS | 
| Jakub Josef | b77c081 | 2017-03-27 14:11:01 +0200 | [diff] [blame] | 4 | import java.util.stream.Collectors | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 5 | /** | 
 | 6 |  * Salt functions | 
 | 7 |  * | 
 | 8 | */ | 
 | 9 |  | 
 | 10 | /** | 
 | 11 |  * Salt connection and context parameters | 
 | 12 |  * | 
 | 13 |  * @param url                 Salt API server URL | 
 | 14 |  * @param credentialsID       ID of credentials store entry | 
 | 15 |  */ | 
 | 16 | def connection(url, credentialsId = "salt") { | 
| Tomáš Kukrál | 6c04bd0 | 2017-03-01 22:18:52 +0100 | [diff] [blame] | 17 |     def common = new com.mirantis.mk.Common() | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 18 |     params = [ | 
 | 19 |         "url": url, | 
 | 20 |         "credentialsId": credentialsId, | 
 | 21 |         "authToken": null, | 
 | 22 |         "creds": common.getCredentials(credentialsId) | 
 | 23 |     ] | 
 | 24 |     params["authToken"] = saltLogin(params) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 25 |     return params | 
 | 26 | } | 
 | 27 |  | 
 | 28 | /** | 
 | 29 |  * Login to Salt API, return auth token | 
 | 30 |  * | 
 | 31 |  * @param master   Salt connection object | 
 | 32 |  */ | 
 | 33 | def saltLogin(master) { | 
| Tomáš Kukrál | 7bec053 | 2017-02-20 15:39:31 +0100 | [diff] [blame] | 34 |     def http = new com.mirantis.mk.Http() | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 35 |     data = [ | 
 | 36 |         'username': master.creds.username, | 
 | 37 |         'password': master.creds.password.toString(), | 
 | 38 |         'eauth': 'pam' | 
 | 39 |     ] | 
| Tomáš Kukrál | 7bec053 | 2017-02-20 15:39:31 +0100 | [diff] [blame] | 40 |     authToken = http.restGet(master, '/login', data)['return'][0]['token'] | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 41 |     return authToken | 
 | 42 | } | 
 | 43 |  | 
 | 44 | /** | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 45 |  * Run action using Salt API (using plain HTTP request from Jenkins master) or Pepper (from slave shell) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 46 |  * | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 47 |  * @param saltId   Salt Connection object or pepperEnv (the command will be sent using the selected method) (determines if command will be sent with Pepper of Salt API ) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 48 |  * @param client   Client type | 
 | 49 |  * @param target   Target specification, eg. for compound matches by Pillar | 
 | 50 |  *                 data: ['expression': 'I@openssh:server', 'type': 'compound']) | 
 | 51 |  * @param function Function to execute (eg. "state.sls") | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 52 |  * @param batch    Batch param to salt (integer or string with percents) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 53 |  * @param args     Additional arguments to function | 
 | 54 |  * @param kwargs   Additional key-value arguments to function | 
| Jiri Broulik | 48544be | 2017-06-14 18:33:54 +0200 | [diff] [blame] | 55 |  * @param timeout  Additional argument salt api timeout | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 56 |  * @param read_timeout http session read timeout | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 57 |  */ | 
 | 58 | @NonCPS | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 59 | def runSaltCommand(saltId, client, target, function, batch = null, args = null, kwargs = null, timeout = -1, read_timeout = -1) { | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 60 |  | 
 | 61 |     data = [ | 
 | 62 |         'tgt': target.expression, | 
 | 63 |         'fun': function, | 
 | 64 |         'client': client, | 
 | 65 |         'expr_form': target.type, | 
 | 66 |     ] | 
| Jakub Josef | 5f83821 | 2017-04-06 12:43:58 +0200 | [diff] [blame] | 67 |     if(batch != null && ( (batch instanceof Integer && batch > 0) || (batch instanceof String && batch.contains("%")))){ | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 68 |         data['client']= "local_batch" | 
 | 69 |         data['batch'] = batch | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 70 |     } | 
 | 71 |  | 
 | 72 |     if (args) { | 
 | 73 |         data['arg'] = args | 
 | 74 |     } | 
 | 75 |  | 
 | 76 |     if (kwargs) { | 
 | 77 |         data['kwarg'] = kwargs | 
 | 78 |     } | 
 | 79 |  | 
| Jiri Broulik | 48544be | 2017-06-14 18:33:54 +0200 | [diff] [blame] | 80 |     if (timeout != -1) { | 
 | 81 |         data['timeout'] = timeout | 
 | 82 |     } | 
 | 83 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 84 |     // Command will be sent using HttpRequest | 
 | 85 |     if (saltId instanceof HashMap && saltId.containsKey("authToken") ) { | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 86 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 87 |         def headers = [ | 
 | 88 |           'X-Auth-Token': "${saltId.authToken}" | 
 | 89 |         ] | 
 | 90 |  | 
 | 91 |         def http = new com.mirantis.mk.Http() | 
 | 92 |         return http.sendHttpPostRequest("${saltId.url}/", data, headers, read_timeout) | 
 | 93 |     } else if (saltId instanceof HashMap) { | 
 | 94 |         throw new Exception("Invalid saltId") | 
 | 95 |     } | 
 | 96 |  | 
 | 97 |     // Command will be sent using Pepper | 
 | 98 |     return runPepperCommand(data, saltId) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 99 | } | 
 | 100 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 101 | /** | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 102 |  * Return pillar for given saltId and target | 
 | 103 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 104 |  * @param target Get pillar target | 
 | 105 |  * @param pillar pillar name (optional) | 
 | 106 |  * @return output of salt command | 
 | 107 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 108 | def getPillar(saltId, target, pillar = null) { | 
| Tomáš Kukrál | d258970 | 2017-03-10 16:30:46 +0100 | [diff] [blame] | 109 |     if (pillar != null) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 110 |         return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'pillar.get', null, [pillar.replace('.', ':')]) | 
| Tomáš Kukrál | d258970 | 2017-03-10 16:30:46 +0100 | [diff] [blame] | 111 |     } else { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 112 |         return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'pillar.data') | 
| Ales Komarek | a3c7e50 | 2017-03-13 11:20:44 +0100 | [diff] [blame] | 113 |     } | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 114 | } | 
 | 115 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 116 | /** | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 117 |  * Return grain for given saltId and target | 
 | 118 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 119 |  * @param target Get grain target | 
 | 120 |  * @param grain grain name (optional) | 
 | 121 |  * @return output of salt command | 
 | 122 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 123 | def getGrain(saltId, target, grain = null) { | 
| Ales Komarek | cec24d4 | 2017-03-08 10:25:45 +0100 | [diff] [blame] | 124 |     if(grain != null) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 125 |         return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'grains.item', null, [grain]) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 126 |     } else { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 127 |         return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'grains.items') | 
| Ales Komarek | cec24d4 | 2017-03-08 10:25:45 +0100 | [diff] [blame] | 128 |     } | 
| Ales Komarek | cec24d4 | 2017-03-08 10:25:45 +0100 | [diff] [blame] | 129 | } | 
 | 130 |  | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 131 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 132 | /** | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 133 |  * Enforces state on given saltId and target | 
 | 134 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 135 |  * @param target State enforcing target | 
 | 136 |  * @param state Salt state | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 137 |  * @param excludedStates states which will be excluded from main state (default empty string) | 
 | 138 |  * @param output print output (optional, default true) | 
 | 139 |  * @param failOnError throw exception on salt state result:false (optional, default true) | 
 | 140 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 141 |  * @param optional Optional flag (if true pipeline will continue even if no minions for target found) | 
 | 142 |  * @param read_timeout http session read timeout (optional, default -1 - disabled) | 
 | 143 |  * @param retries Retry count for salt state. (optional, default -1 - no retries) | 
 | 144 |  * @param queue salt queue parameter for state.sls calls (optional, default true) - CANNOT BE USED WITH BATCH | 
 | 145 |  * @param saltArgs additional salt args eq. ["runas=aptly"] | 
 | 146 |  * @return output of salt command | 
 | 147 |  */ | 
 | 148 | def enforceStateWithExclude(saltId, target, state, excludedStates = "", output = true, failOnError = true, batch = null, optional = false, read_timeout=-1, retries=-1, queue=true, saltArgs=[]) { | 
 | 149 |     saltArgs << "exclude=${excludedStates}" | 
 | 150 |     return enforceState(saltId, target, state, output, failOnError, batch, optional, read_timeout, retries, queue, saltArgs) | 
 | 151 | } | 
 | 152 |  | 
 | 153 | /* Enforces state on given saltId and target | 
 | 154 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
 | 155 |  * @param target State enforcing target | 
 | 156 |  * @param state Salt state | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 157 |  * @param output print output (optional, default true) | 
 | 158 |  * @param failOnError throw exception on salt state result:false (optional, default true) | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 159 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 160 |  * @param optional Optional flag (if true pipeline will continue even if no minions for target found) | 
| Petr Michalec | de0ff32 | 2017-10-04 09:32:14 +0200 | [diff] [blame] | 161 |  * @param read_timeout http session read timeout (optional, default -1 - disabled) | 
 | 162 |  * @param retries Retry count for salt state. (optional, default -1 - no retries) | 
 | 163 |  * @param queue salt queue parameter for state.sls calls (optional, default true) - CANNOT BE USED WITH BATCH | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 164 |  * @param saltArgs additional salt args eq. ["runas=aptly", exclude="opencontrail.database"] | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 165 |  * @return output of salt command | 
 | 166 |  */ | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 167 | def enforceState(saltId, target, state, output = true, failOnError = true, batch = null, optional = false, read_timeout=-1, retries=-1, queue=true, saltArgs = []) { | 
| Tomáš Kukrál | 6c04bd0 | 2017-03-01 22:18:52 +0100 | [diff] [blame] | 168 |     def common = new com.mirantis.mk.Common() | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 169 |     // add state to salt args | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 170 |     if (state instanceof String) { | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 171 |         saltArgs << state | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 172 |     } else { | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 173 |         saltArgs << state.join(',') | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 174 |     } | 
 | 175 |  | 
| Jakub Josef | 84f0168 | 2018-02-07 14:26:19 +0100 | [diff] [blame] | 176 |     common.infoMsg("Running state ${state} on ${target}") | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 177 |     def out | 
| Petr Michalec | de0ff32 | 2017-10-04 09:32:14 +0200 | [diff] [blame] | 178 |     def kwargs = [:] | 
 | 179 |  | 
 | 180 |     if (queue && batch == null) { | 
 | 181 |       kwargs["queue"] = true | 
 | 182 |     } | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 183 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 184 |     if (optional == false || testTarget(saltId, target)){ | 
| Richard Felkl | 03203d6 | 2017-11-01 17:57:32 +0100 | [diff] [blame] | 185 |         if (retries > 0){ | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 186 |             retry(retries){ | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 187 |                 // we have to reverse order in saltArgs because salt state have to be first | 
 | 188 |                 out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'state.sls', batch, saltArgs.reverse(), kwargs, -1, read_timeout) | 
 | 189 |                 // failOnError should be passed as true because we need to throw exception for retry block handler | 
 | 190 |                 checkResult(out, true, output, true, true) //disable ask on error because we are using retry here | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 191 |             } | 
| Petr Michalec | de0ff32 | 2017-10-04 09:32:14 +0200 | [diff] [blame] | 192 |         } else { | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 193 |             // we have to reverse order in saltArgs because salt state have to be first | 
 | 194 |             out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'state.sls', batch, saltArgs.reverse(), kwargs, -1, read_timeout) | 
| Richard Felkl | 03203d6 | 2017-11-01 17:57:32 +0100 | [diff] [blame] | 195 |             checkResult(out, failOnError, output) | 
| Vasyl Saienko | e36ab7c | 2017-07-17 14:35:48 +0300 | [diff] [blame] | 196 |         } | 
| Oleg Iurchenko | 7eb2150 | 2017-11-28 18:53:43 +0200 | [diff] [blame] | 197 |         waitForMinion(out) | 
| Martin Polreich | 1c77afa | 2017-07-18 11:27:02 +0200 | [diff] [blame] | 198 |         return out | 
| Martin Polreich | 1c77afa | 2017-07-18 11:27:02 +0200 | [diff] [blame] | 199 |     } else { | 
 | 200 |         common.infoMsg("No Minions matched the target given, but 'optional' param was set to true - Pipeline continues. ") | 
 | 201 |     } | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 202 | } | 
 | 203 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 204 | /** | 
 | 205 |  * Run command on salt minion (salt cmd.run wrapper) | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 206 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 207 |  * @param target Get pillar target | 
 | 208 |  * @param cmd command | 
| Jakub Josef | 053df39 | 2017-05-03 15:51:05 +0200 | [diff] [blame] | 209 |  * @param checkResponse test command success execution (default true) | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 210 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 211 |  * @param output do you want to print output | 
| chnyda | 205a92b | 2018-01-11 17:07:32 +0100 | [diff] [blame] | 212 |  * @param saltArgs additional salt args eq. ["runas=aptly"] | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 213 |  * @return output of salt command | 
 | 214 |  */ | 
| chnyda | 205a92b | 2018-01-11 17:07:32 +0100 | [diff] [blame] | 215 | def cmdRun(saltId, target, cmd, checkResponse = true, batch=null, output = true, saltArgs = []) { | 
| Tomáš Kukrál | 6c04bd0 | 2017-03-01 22:18:52 +0100 | [diff] [blame] | 216 |     def common = new com.mirantis.mk.Common() | 
| Jakub Josef | 053df39 | 2017-05-03 15:51:05 +0200 | [diff] [blame] | 217 |     def originalCmd = cmd | 
| Tomáš Kukrál | dfd4b49 | 2017-03-02 12:08:50 +0100 | [diff] [blame] | 218 |     common.infoMsg("Running command ${cmd} on ${target}") | 
| Jakub Josef | 053df39 | 2017-05-03 15:51:05 +0200 | [diff] [blame] | 219 |     if (checkResponse) { | 
 | 220 |       cmd = cmd + " && echo Salt command execution success" | 
 | 221 |     } | 
| chnyda | 205a92b | 2018-01-11 17:07:32 +0100 | [diff] [blame] | 222 |  | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 223 |     // add cmd name to salt args list | 
| chnyda | 205a92b | 2018-01-11 17:07:32 +0100 | [diff] [blame] | 224 |     saltArgs << cmd | 
 | 225 |  | 
 | 226 |     def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.run', batch, saltArgs.reverse()) | 
| Jakub Josef | 053df39 | 2017-05-03 15:51:05 +0200 | [diff] [blame] | 227 |     if (checkResponse) { | 
 | 228 |         // iterate over all affected nodes and check success return code | 
| Jiri Broulik | 16e9ce7 | 2017-05-17 13:28:31 +0200 | [diff] [blame] | 229 |         if (out["return"]){ | 
 | 230 |             for(int i=0;i<out["return"].size();i++){ | 
 | 231 |                 def node = out["return"][i]; | 
| Jakub Josef | 053df39 | 2017-05-03 15:51:05 +0200 | [diff] [blame] | 232 |                 for(int j=0;j<node.size();j++){ | 
 | 233 |                     def nodeKey = node.keySet()[j] | 
 | 234 |                     if (!node[nodeKey].contains("Salt command execution success")) { | 
 | 235 |                         throw new Exception("Execution of cmd ${originalCmd} failed. Server returns: ${node[nodeKey]}") | 
 | 236 |                     } | 
 | 237 |                 } | 
 | 238 |             } | 
 | 239 |         }else{ | 
 | 240 |             throw new Exception("Salt Api response doesn't have return param!") | 
 | 241 |         } | 
 | 242 |     } | 
| Jiri Broulik | 16e9ce7 | 2017-05-17 13:28:31 +0200 | [diff] [blame] | 243 |     if (output == true) { | 
 | 244 |         printSaltCommandResult(out) | 
 | 245 |     } | 
 | 246 |     return out | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 247 | } | 
 | 248 |  | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 249 | /** | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 250 |  * Checks if salt minion is in a list of salt master's accepted keys | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 251 |  * @usage minionPresent(saltId, 'I@salt:master', 'ntw', true, null, true, 200, 3) | 
 | 252 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 253 |  * @param target Get pillar target | 
 | 254 |  * @param minion_name unique identification of a minion in salt-key command output | 
 | 255 |  * @param waitUntilPresent return after the minion becomes present (default true) | 
 | 256 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 257 |  * @param output print salt command (default true) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 258 |  * @param maxRetries finite number of iterations to check status of a command (default 200) | 
 | 259 |  * @param answers how many minions should return (optional, default 1) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 260 |  * @return output of salt command | 
 | 261 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 262 | def minionPresent(saltId, target, minion_name, waitUntilPresent = true, batch=null, output = true, maxRetries = 200, answers = 1) { | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 263 |     minion_name = minion_name.replace("*", "") | 
 | 264 |     def common = new com.mirantis.mk.Common() | 
 | 265 |     def cmd = 'salt-key | grep ' + minion_name | 
 | 266 |     if (waitUntilPresent){ | 
 | 267 |         def count = 0 | 
 | 268 |         while(count < maxRetries) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 269 |             def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, 5) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 270 |             if (output) { | 
 | 271 |                 printSaltCommandResult(out) | 
 | 272 |             } | 
 | 273 |             def valueMap = out["return"][0] | 
 | 274 |             def result = valueMap.get(valueMap.keySet()[0]) | 
 | 275 |             def resultsArray = result.tokenize("\n") | 
 | 276 |             def size = resultsArray.size() | 
 | 277 |             if (size >= answers) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 278 |                 return out | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 279 |             } | 
 | 280 |             count++ | 
 | 281 |             sleep(time: 500, unit: 'MILLISECONDS') | 
 | 282 |             common.infoMsg("Waiting for ${cmd} on ${target} to be in correct state") | 
 | 283 |         } | 
 | 284 |     } else { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 285 |         def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, 5) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 286 |         if (output) { | 
 | 287 |             printSaltCommandResult(out) | 
 | 288 |         } | 
 | 289 |         return out | 
 | 290 |     } | 
 | 291 |     // otherwise throw exception | 
 | 292 |     common.errorMsg("Status of command ${cmd} on ${target} failed, please check it.") | 
 | 293 |     throw new Exception("${cmd} signals failure of status check!") | 
 | 294 | } | 
 | 295 |  | 
 | 296 | /** | 
| Jiri Broulik | f8f9694 | 2018-02-15 10:03:42 +0100 | [diff] [blame] | 297 |  * Checks if salt minion is in a list of salt master's accepted keys | 
 | 298 |  * @usage minionPresent(saltId, 'I@salt:master', 'I@salt:minion', true, null, true, 200, 3) | 
 | 299 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
 | 300 |  * @param target Performs tests on this target node | 
 | 301 |  * @param target_minions all targeted minions to test (for ex. I@salt:minion) | 
 | 302 |  * @param waitUntilPresent return after the minion becomes present (default true) | 
 | 303 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 304 |  * @param output print salt command (default true) | 
 | 305 |  * @param maxRetries finite number of iterations to check status of a command (default 200) | 
 | 306 |  * @param answers how many minions should return (optional, default 1) | 
 | 307 |  * @return output of salt command | 
 | 308 |  */ | 
 | 309 | def minionsPresent(saltId, target = 'I@salt:master', target_minions = '', waitUntilPresent = true, batch=null, output = true, maxRetries = 200, answers = 1) { | 
 | 310 |     def target_hosts = getMinionsSorted(pepperEnv, target_minions) | 
 | 311 |     for (t in target_hosts) { | 
 | 312 |         def tgt = salt.stripDomainName(t) | 
 | 313 |         salt.minionPresent(pepperEnv, target, tgt, waitUntilPresent, batch, output, maxRetries, answers) | 
 | 314 |     } | 
 | 315 | } | 
 | 316 |  | 
 | 317 | /** | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 318 |  * You can call this function when salt-master already contains salt keys of the target_nodes | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 319 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 320 |  * @param target Should always be salt-master | 
 | 321 |  * @param target_nodes unique identification of a minion or group of salt minions | 
 | 322 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 323 |  * @param wait timeout for the salt command if minions do not return (default 10) | 
 | 324 |  * @param maxRetries finite number of iterations to check status of a command (default 200) | 
 | 325 |  * @return output of salt command | 
 | 326 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 327 | def minionsReachable(saltId, target, target_nodes, batch=null, wait = 10, maxRetries = 200) { | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 328 |     def common = new com.mirantis.mk.Common() | 
 | 329 |     def cmd = "salt -t${wait} -C '${target_nodes}' test.ping" | 
 | 330 |     common.infoMsg("Checking if all ${target_nodes} minions are reachable") | 
 | 331 |     def count = 0 | 
 | 332 |     while(count < maxRetries) { | 
 | 333 |         Calendar timeout = Calendar.getInstance(); | 
 | 334 |         timeout.add(Calendar.SECOND, wait); | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 335 |         def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, wait) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 336 |         Calendar current = Calendar.getInstance(); | 
 | 337 |         if (current.getTime().before(timeout.getTime())) { | 
 | 338 |            printSaltCommandResult(out) | 
 | 339 |            return out | 
 | 340 |         } | 
 | 341 |         common.infoMsg("Not all of the targeted '${target_nodes}' minions returned yet. Waiting ...") | 
 | 342 |         count++ | 
 | 343 |         sleep(time: 500, unit: 'MILLISECONDS') | 
 | 344 |     } | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 345 | } | 
 | 346 |  | 
 | 347 | /** | 
 | 348 |  * Run command on salt minion (salt cmd.run wrapper) | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 349 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 350 |  * @param target Get pillar target | 
 | 351 |  * @param cmd name of a service | 
 | 352 |  * @param correct_state string that command must contain if status is in correct state (optional, default 'running') | 
| Jiri Broulik | cf1f233 | 2017-07-25 11:30:03 +0200 | [diff] [blame] | 353 |  * @param find bool value if it is suppose to find some string in the output or the cmd should return empty string (optional, default true) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 354 |  * @param waitUntilOk return after the minion becomes present (optional, default true) | 
 | 355 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 356 |  * @param output print salt command (default true) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 357 |  * @param maxRetries finite number of iterations to check status of a command (default 200) | 
 | 358 |  * @param answers how many minions should return (optional, default 0) | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 359 |  * @return output of salt command | 
 | 360 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 361 | def commandStatus(saltId, target, cmd, correct_state='running', find = true, waitUntilOk = true, batch=null, output = true, maxRetries = 200, answers = 0) { | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 362 |     def common = new com.mirantis.mk.Common() | 
 | 363 |     common.infoMsg("Checking if status of verification command ${cmd} on ${target} is in correct state") | 
 | 364 |     if (waitUntilOk){ | 
 | 365 |         def count = 0 | 
 | 366 |         while(count < maxRetries) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 367 |             def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, 5) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 368 |             if (output) { | 
 | 369 |                 printSaltCommandResult(out) | 
 | 370 |             } | 
| Jakub Josef | 115a78f | 2017-07-18 15:04:00 +0200 | [diff] [blame] | 371 |             def resultMap = out["return"][0] | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 372 |             def success = 0 | 
 | 373 |             if (answers == 0){ | 
 | 374 |                 answers = resultMap.size() | 
 | 375 |             } | 
 | 376 |             for (int i=0;i<answers;i++) { | 
 | 377 |                 result = resultMap.get(resultMap.keySet()[i]) | 
 | 378 |                 // if the goal is to find some string in output of the command | 
 | 379 |                 if (find) { | 
 | 380 |                     if(result == null || result instanceof Boolean || result.isEmpty()) { result='' } | 
 | 381 |                     if (result.toLowerCase().contains(correct_state.toLowerCase())) { | 
 | 382 |                         success++ | 
 | 383 |                         if (success == answers) { | 
 | 384 |                             return out | 
 | 385 |                         } | 
| Jiri Broulik | d0c2757 | 2017-07-24 20:01:10 +0200 | [diff] [blame] | 386 |                     } | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 387 |                 // else the goal is to not find any string in output of the command | 
 | 388 |                 } else { | 
 | 389 |                     if(result instanceof String && result.isEmpty()) { | 
 | 390 |                         success++ | 
 | 391 |                         if (success == answers) { | 
 | 392 |                             return out | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 393 |                         } | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 394 |                     } | 
 | 395 |                 } | 
 | 396 |             } | 
 | 397 |             count++ | 
 | 398 |             sleep(time: 500, unit: 'MILLISECONDS') | 
 | 399 |             common.infoMsg("Waiting for ${cmd} on ${target} to be in correct state") | 
 | 400 |         } | 
 | 401 |     } else { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 402 |         def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, 5) | 
| Jiri Broulik | 71512bc | 2017-08-04 10:00:18 +0200 | [diff] [blame] | 403 |         def resultMap = out["return"][0] | 
 | 404 |         if (output) { | 
 | 405 |             printSaltCommandResult(out) | 
 | 406 |         } | 
 | 407 |         for (int i=0;i<resultMap.size();i++) { | 
 | 408 |             result = resultMap.get(resultMap.keySet()[i]) | 
 | 409 |         // if the goal is to find some string in output of the command | 
 | 410 |             if (find) { | 
 | 411 |                 if(result == null || result instanceof Boolean || result.isEmpty()) { result='' } | 
 | 412 |                 if (result.toLowerCase().contains(correct_state.toLowerCase())) { | 
| Jiri Broulik | d0c2757 | 2017-07-24 20:01:10 +0200 | [diff] [blame] | 413 |                     return out | 
 | 414 |                 } | 
 | 415 |  | 
 | 416 |             // else the goal is to not find any string in output of the command | 
 | 417 |             } else { | 
 | 418 |                 if(result instanceof String && result.isEmpty()) { | 
 | 419 |                     return out | 
 | 420 |                 } | 
 | 421 |             } | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 422 |         } | 
 | 423 |     } | 
 | 424 |     // otherwise throw exception | 
| Jiri Broulik | d0c2757 | 2017-07-24 20:01:10 +0200 | [diff] [blame] | 425 |     common.errorMsg("Status of command ${cmd} on ${target} failed, please check it.") | 
| Jiri Broulik | 2c69f3d | 2017-07-18 14:23:58 +0200 | [diff] [blame] | 426 |     throw new Exception("${cmd} signals failure of status check!") | 
 | 427 | } | 
 | 428 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 429 | /** | 
 | 430 |  * Perform complete salt sync between master and target | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 431 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 432 |  * @param target Get pillar target | 
 | 433 |  * @return output of salt command | 
 | 434 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 435 | def syncAll(saltId, target) { | 
 | 436 |     return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'saltutil.sync_all') | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 437 | } | 
 | 438 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 439 | /** | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 440 |  * Perform complete salt refresh between master and target | 
 | 441 |  * Method will call saltutil.refresh_pillar, saltutil.refresh_grains and saltutil.sync_all | 
 | 442 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
 | 443 |  * @param target Get pillar target | 
 | 444 |  * @return output of salt command | 
 | 445 |  */ | 
 | 446 | def fullRefresh(saltId, target){ | 
 | 447 |     runSaltProcessStep(saltId, target, 'saltutil.refresh_pillar', [], null, true) | 
 | 448 |     runSaltProcessStep(saltId, target, 'saltutil.refresh_grains', [], null, true) | 
 | 449 |     runSaltProcessStep(saltId, target, 'saltutil.sync_all', [], null, true) | 
 | 450 | } | 
 | 451 |  | 
 | 452 | /** | 
 | 453 |  * Enforce highstate on given targets | 
 | 454 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
 | 455 |  * @param target Highstate enforcing target | 
 | 456 |  * @param excludedStates states which will be excluded from main state (default empty string) | 
 | 457 |  * @param output print output (optional, default true) | 
 | 458 |  * @param failOnError throw exception on salt state result:false (optional, default true) | 
 | 459 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
 | 460 |  * @param saltArgs additional salt args eq. ["runas=aptly", exclude="opencontrail.database"] | 
 | 461 |  * @return output of salt command | 
 | 462 |  */ | 
 | 463 | def enforceHighstateWithExclude(saltId, target, excludedStates = "", output = false, failOnError = true, batch = null, saltArgs = []) { | 
 | 464 |     saltArgs << "exclude=${excludedStates}" | 
 | 465 |     return enforceHighstate(saltId, target, output, failOnError, batch, saltArgs) | 
 | 466 | } | 
 | 467 | /** | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 468 |  * Enforce highstate on given targets | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 469 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 470 |  * @param target Highstate enforcing target | 
 | 471 |  * @param output print output (optional, default true) | 
 | 472 |  * @param failOnError throw exception on salt state result:false (optional, default true) | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 473 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 474 |  * @return output of salt command | 
 | 475 |  */ | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 476 | def enforceHighstate(saltId, target, output = false, failOnError = true, batch = null, saltArgs = []) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 477 |     def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'state.highstate', batch) | 
| Alexander Noskov | 657ccfc | 2017-07-14 11:35:52 +0000 | [diff] [blame] | 478 |     def common = new com.mirantis.mk.Common() | 
 | 479 |  | 
| Marek Celoud | 6336611 | 2017-07-25 17:27:24 +0200 | [diff] [blame] | 480 |     common.infoMsg("Running state highstate on ${target}") | 
| Alexander Noskov | 657ccfc | 2017-07-14 11:35:52 +0000 | [diff] [blame] | 481 |  | 
| Jakub Josef | 374beb7 | 2017-04-27 15:45:09 +0200 | [diff] [blame] | 482 |     checkResult(out, failOnError, output) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 483 |     return out | 
 | 484 | } | 
 | 485 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 486 | /** | 
| Ales Komarek | 5276ebe | 2017-03-16 08:46:34 +0100 | [diff] [blame] | 487 |  * Get running minions IDs according to the target | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 488 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Ales Komarek | 5276ebe | 2017-03-16 08:46:34 +0100 | [diff] [blame] | 489 |  * @param target Get minions target | 
 | 490 |  * @return list of active minions fitin | 
 | 491 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 492 | def getMinions(saltId, target) { | 
 | 493 |     def minionsRaw = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'test.ping') | 
| Ales Komarek | 5276ebe | 2017-03-16 08:46:34 +0100 | [diff] [blame] | 494 |     return new ArrayList<String>(minionsRaw['return'][0].keySet()) | 
 | 495 | } | 
 | 496 |  | 
| Jiri Broulik | f8f9694 | 2018-02-15 10:03:42 +0100 | [diff] [blame] | 497 | /** | 
 | 498 |  * Get sorted running minions IDs according to the target | 
 | 499 |  * @param saltId Salt Connection object or pepperEnv | 
 | 500 |  * @param target Get minions target | 
 | 501 |  * @return list of sorted active minions fitin | 
 | 502 |  */ | 
 | 503 | def getMinionsSorted(saltId, target) { | 
 | 504 |     return getMinions(saltId, target).sort() | 
 | 505 | } | 
 | 506 |  | 
 | 507 | /** | 
 | 508 |  * Get first out of running minions IDs according to the target | 
 | 509 |  * @param saltId Salt Connection object or pepperEnv | 
 | 510 |  * @param target Get minions target | 
 | 511 |  * @return first of active minions fitin | 
 | 512 |  */ | 
 | 513 | def getFirstMinion(saltId, target) { | 
 | 514 |     def minionsSorted = getMinionsSorted(saltId, target) | 
 | 515 |     return minionsSorted[0].split("\\.")[0] | 
 | 516 | } | 
 | 517 |  | 
 | 518 | /** | 
 | 519 |  * Get running salt minions IDs without it's domain name part and its numbering identifications | 
 | 520 |  * @param saltId Salt Connection object or pepperEnv | 
 | 521 |  * @param target Get minions target | 
 | 522 |  * @return list of active minions fitin without it's domain name part name numbering | 
 | 523 |  */ | 
 | 524 | def getMinionsGeneralName(saltId, target) { | 
 | 525 |     def minionsSorted = getMinionsSorted(saltId, target) | 
 | 526 |     return stripDomainName(minionsSorted[0]).replaceAll('\\d+$', "") | 
 | 527 | } | 
 | 528 |  | 
 | 529 | /** | 
 | 530 |  * Get domain name of the env | 
 | 531 |  * @param saltId Salt Connection object or pepperEnv | 
 | 532 |  * @return domain name | 
 | 533 |  */ | 
 | 534 | def getDomainName(saltId) { | 
 | 535 |     return getReturnValues(getPillar(saltId, 'I@salt:master', '_param:cluster_domain')) | 
 | 536 | } | 
 | 537 |  | 
 | 538 | /** | 
 | 539 |  * Remove domain name from Salt minion ID | 
 | 540 |  * @param name String of Salt minion ID | 
 | 541 |  * @return Salt minion ID without its domain name | 
 | 542 |  */ | 
 | 543 | def stripDomainName(name) { | 
 | 544 |     return name.split("\\.")[0] | 
 | 545 | } | 
 | 546 |  | 
 | 547 | /** | 
 | 548 |  * Gets return values of a salt command | 
 | 549 |  * @param output String of Salt minion ID | 
 | 550 |  * @return Return values of a salt command | 
 | 551 |  */ | 
 | 552 | def getReturnValues(output) { | 
 | 553 |     if(output.containsKey("return") && !output.get("return").isEmpty()) { | 
 | 554 |         return output['return'][0].values()[0] | 
 | 555 |     } | 
 | 556 |     def common = new com.mirantis.mk.Common() | 
 | 557 |     common.errorMsg('output does not contain return key') | 
 | 558 |     return '' | 
 | 559 | } | 
 | 560 |  | 
 | 561 | /** | 
 | 562 |  * Get minion ID of one of KVM nodes | 
 | 563 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
 | 564 |  * @return Salt minion ID of one of KVM nodes in env | 
 | 565 |  */ | 
 | 566 | def getKvmMinionId(saltId) { | 
 | 567 |     return getReturnValues(getGrain(saltId, 'I@salt:control', 'id')).values()[0] | 
 | 568 | } | 
 | 569 |  | 
 | 570 | /** | 
 | 571 |  * Get Salt minion ID of KVM node hosting 'name' VM | 
 | 572 |  * @param saltId Salt Connection object or pepperEnv | 
 | 573 |  * @param name Name of the VM (for ex. ctl01) | 
 | 574 |  * @return Salt minion ID of KVM node hosting 'name' VM | 
 | 575 |  */ | 
 | 576 | def getNodeProvider(saltId, name) { | 
 | 577 |     def kvm = getKvmMinionId(saltId) | 
 | 578 |     return getReturnValues(getPillar(saltId, "${kvm}", "salt:control:cluster:internal:node:${name}:provider")) | 
 | 579 | } | 
 | 580 |  | 
| Ales Komarek | 5276ebe | 2017-03-16 08:46:34 +0100 | [diff] [blame] | 581 |  | 
 | 582 | /** | 
| Tomáš Kukrál | b12ff9f | 2017-07-12 12:32:34 +0200 | [diff] [blame] | 583 |  * Test if there are any minions to target | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 584 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Tomáš Kukrál | b12ff9f | 2017-07-12 12:32:34 +0200 | [diff] [blame] | 585 |  * @param target Target to test | 
| vrovachev | 1c4770b | 2017-07-05 13:25:21 +0400 | [diff] [blame] | 586 |  * @return bool indicating if target was succesful | 
| Tomáš Kukrál | b12ff9f | 2017-07-12 12:32:34 +0200 | [diff] [blame] | 587 |  */ | 
 | 588 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 589 | def testTarget(saltId, target) { | 
 | 590 |     return getMinions(saltId, target).size() > 0 | 
| Tomáš Kukrál | b12ff9f | 2017-07-12 12:32:34 +0200 | [diff] [blame] | 591 | } | 
 | 592 |  | 
 | 593 | /** | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 594 |  * Generates node key using key.gen_accept call | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 595 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 596 |  * @param target Key generating target | 
 | 597 |  * @param host Key generating host | 
 | 598 |  * @param keysize generated key size (optional, default 4096) | 
 | 599 |  * @return output of salt command | 
 | 600 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 601 | def generateNodeKey(saltId, target, host, keysize = 4096) { | 
 | 602 |     return runSaltCommand(saltId, 'wheel', target, 'key.gen_accept', [host], ['keysize': keysize]) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 603 | } | 
 | 604 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 605 | /** | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 606 |  * Generates node reclass metadata | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 607 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 608 |  * @param target Metadata generating target | 
 | 609 |  * @param host Metadata generating host | 
 | 610 |  * @param classes Reclass classes | 
 | 611 |  * @param parameters Reclass parameters | 
 | 612 |  * @return output of salt command | 
 | 613 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 614 | def generateNodeMetadata(saltId, target, host, classes, parameters) { | 
 | 615 |     return runSaltCommand(saltId, 'local', target, 'reclass.node_create', [host, '_generated'], ['classes': classes, 'parameters': parameters]) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 616 | } | 
 | 617 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 618 | /** | 
 | 619 |  * Run salt orchestrate on given targets | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 620 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 621 |  * @param target Orchestration target | 
 | 622 |  * @param orchestrate Salt orchestrate params | 
 | 623 |  * @return output of salt command | 
 | 624 |  */ | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 625 | def orchestrateSystem(saltId, target, orchestrate) { | 
 | 626 |     return runSaltCommand(saltId, 'runner', target, 'state.orchestrate', [orchestrate]) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 627 | } | 
 | 628 |  | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 629 | /** | 
 | 630 |  * Run salt process step | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 631 |  * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 632 |  * @param tgt Salt process step target | 
 | 633 |  * @param fun Salt process step function | 
 | 634 |  * @param arg process step arguments (optional, default []) | 
| Jakub Josef | 2f25cf2 | 2017-03-28 13:34:57 +0200 | [diff] [blame] | 635 |  * @param batch salt batch parameter integer or string with percents (optional, default null - disable batch) | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 636 |  * @param output print output (optional, default true) | 
| Jiri Broulik | 48544be | 2017-06-14 18:33:54 +0200 | [diff] [blame] | 637 |  * @param timeout  Additional argument salt api timeout | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 638 |  * @return output of salt command | 
 | 639 |  */ | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 640 | def runSaltProcessStep(saltId, tgt, fun, arg = [], batch = null, output = true, timeout = -1, kwargs = null) { | 
| Tomáš Kukrál | 6c04bd0 | 2017-03-01 22:18:52 +0100 | [diff] [blame] | 641 |     def common = new com.mirantis.mk.Common() | 
| Jiri Broulik | 48544be | 2017-06-14 18:33:54 +0200 | [diff] [blame] | 642 |     def salt = new com.mirantis.mk.Salt() | 
| Tomáš Kukrál | adb4ecd | 2017-03-02 10:06:36 +0100 | [diff] [blame] | 643 |     def out | 
 | 644 |  | 
| Marek Celoud | 6336611 | 2017-07-25 17:27:24 +0200 | [diff] [blame] | 645 |     common.infoMsg("Running step ${fun} ${arg} on ${tgt}") | 
| Tomáš Kukrál | 6c04bd0 | 2017-03-01 22:18:52 +0100 | [diff] [blame] | 646 |  | 
| Filip Pytloun | f0435c0 | 2017-03-02 17:48:54 +0100 | [diff] [blame] | 647 |     if (batch == true) { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 648 |         out = runSaltCommand(saltId, 'local_batch', ['expression': tgt, 'type': 'compound'], fun, String.valueOf(batch), arg, kwargs, timeout) | 
| Tomáš Kukrál | adb4ecd | 2017-03-02 10:06:36 +0100 | [diff] [blame] | 649 |     } else { | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 650 |         out = runSaltCommand(saltId, 'local', ['expression': tgt, 'type': 'compound'], fun, batch, arg, kwargs, timeout) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 651 |     } | 
| Tomáš Kukrál | adb4ecd | 2017-03-02 10:06:36 +0100 | [diff] [blame] | 652 |  | 
| Tomáš Kukrál | f5dda64 | 2017-03-02 14:22:59 +0100 | [diff] [blame] | 653 |     if (output == true) { | 
| Jiri Broulik | 48544be | 2017-06-14 18:33:54 +0200 | [diff] [blame] | 654 |         salt.printSaltCommandResult(out) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 655 |     } | 
| Jiri Broulik | ae19c26 | 2017-05-16 19:06:52 +0200 | [diff] [blame] | 656 |     return out | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 657 | } | 
 | 658 |  | 
 | 659 | /** | 
 | 660 |  * Check result for errors and throw exception if any found | 
 | 661 |  * | 
 | 662 |  * @param result    Parsed response of Salt API | 
| Jakub Josef | 8021c00 | 2017-03-27 15:41:28 +0200 | [diff] [blame] | 663 |  * @param failOnError Do you want to throw exception if salt-call fails (optional, default true) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 664 |  * @param printResults Do you want to print salt results (optional, default true) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 665 |  * @param printOnlyChanges If true (default), print only changed resources | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 666 |  * @param disableAskOnError Flag for disabling ASK_ON_ERROR feature (optional, default false) | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 667 |  */ | 
| Jakub Josef | 432e9d9 | 2018-02-06 18:28:37 +0100 | [diff] [blame] | 668 | def checkResult(result, failOnError = true, printResults = true, printOnlyChanges = true, disableAskOnError = false) { | 
| Jakub Josef | 5ade54c | 2017-03-10 16:14:01 +0100 | [diff] [blame] | 669 |     def common = new com.mirantis.mk.Common() | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 670 |     if(result != null){ | 
 | 671 |         if(result['return']){ | 
 | 672 |             for (int i=0;i<result['return'].size();i++) { | 
 | 673 |                 def entry = result['return'][i] | 
 | 674 |                 if (!entry) { | 
 | 675 |                     if (failOnError) { | 
 | 676 |                         throw new Exception("Salt API returned empty response: ${result}") | 
 | 677 |                     } else { | 
 | 678 |                         common.errorMsg("Salt API returned empty response: ${result}") | 
| Jakub Josef | ece32af | 2017-03-14 19:20:08 +0100 | [diff] [blame] | 679 |                     } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 680 |                 } | 
 | 681 |                 for (int j=0;j<entry.size();j++) { | 
 | 682 |                     def nodeKey = entry.keySet()[j] | 
 | 683 |                     def node=entry[nodeKey] | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 684 |                     def outputResources = [] | 
| Jakub Josef | 4714594 | 2018-04-04 17:30:38 +0200 | [diff] [blame^] | 685 |                     def errorResources = [] | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 686 |                     common.infoMsg("Node ${nodeKey} changes:") | 
 | 687 |                     if(node instanceof Map || node instanceof List){ | 
 | 688 |                         for (int k=0;k<node.size();k++) { | 
 | 689 |                             def resource; | 
 | 690 |                             def resKey; | 
 | 691 |                             if(node instanceof Map){ | 
 | 692 |                                 resKey = node.keySet()[k] | 
 | 693 |                             }else if(node instanceof List){ | 
 | 694 |                                 resKey = k | 
 | 695 |                             } | 
 | 696 |                             resource = node[resKey] | 
| Jakub Josef | c4c4020 | 2017-04-28 12:04:24 +0200 | [diff] [blame] | 697 |                            // print | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 698 |                             if(printResults){ | 
 | 699 |                                 if(resource instanceof Map && resource.keySet().contains("result")){ | 
 | 700 |                                     //clean unnesaccary fields | 
 | 701 |                                     if(resource.keySet().contains("__run_num__")){ | 
 | 702 |                                         resource.remove("__run_num__") | 
 | 703 |                                     } | 
 | 704 |                                     if(resource.keySet().contains("__id__")){ | 
 | 705 |                                         resource.remove("__id__") | 
 | 706 |                                     } | 
 | 707 |                                     if(resource.keySet().contains("pchanges")){ | 
 | 708 |                                         resource.remove("pchanges") | 
 | 709 |                                     } | 
 | 710 |                                     if(!resource["result"] || (resource["result"] instanceof String && resource["result"] != "true")){ | 
 | 711 |                                         if(resource["result"] != null){ | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 712 |                                             outputResources.add(String.format("Resource: %s\n\u001B[31m%s\u001B[0m", resKey, common.prettify(resource))) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 713 |                                         }else{ | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 714 |                                             outputResources.add(String.format("Resource: %s\n\u001B[33m%s\u001B[0m", resKey, common.prettify(resource))) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 715 |                                         } | 
 | 716 |                                     }else{ | 
 | 717 |                                         if(!printOnlyChanges || resource.changes.size() > 0){ | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 718 |                                             outputResources.add(String.format("Resource: %s\n\u001B[32m%s\u001B[0m", resKey, common.prettify(resource))) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 719 |                                         } | 
 | 720 |                                     } | 
 | 721 |                                 }else{ | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 722 |                                     outputResources.add(String.format("Resource: %s\n\u001B[36m%s\u001B[0m", resKey, common.prettify(resource))) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 723 |                                 } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 724 |                             } | 
| Jakub Josef | c4c4020 | 2017-04-28 12:04:24 +0200 | [diff] [blame] | 725 |                             common.debugMsg("checkResult: checking resource: ${resource}") | 
 | 726 |                             if(resource instanceof String || (resource["result"] != null && !resource["result"]) || (resource["result"] instanceof String && resource["result"] == "false")){ | 
| Jakub Josef | 4714594 | 2018-04-04 17:30:38 +0200 | [diff] [blame^] | 727 |                                 errorResources.add(resource) | 
| Jakub Josef | c4c4020 | 2017-04-28 12:04:24 +0200 | [diff] [blame] | 728 |                             } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 729 |                         } | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 730 |                     }else if(node!=null && node!=""){ | 
| Jakub Josef | 62f6c84 | 2017-08-04 16:36:35 +0200 | [diff] [blame] | 731 |                         outputResources.add(String.format("Resource: %s\n\u001B[36m%s\u001B[0m", nodeKey, common.prettify(node))) | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 732 |                     } | 
 | 733 |                     if(printResults && !outputResources.isEmpty()){ | 
| Jakub Josef | 4714594 | 2018-04-04 17:30:38 +0200 | [diff] [blame^] | 734 |                         println outputResources.stream().collect(Collectors.joining("\n")) | 
 | 735 |                     } | 
 | 736 |                     if(!errorResources.isEmpty()){ | 
 | 737 |                         for(resource in errorResources){ | 
 | 738 |                             def prettyResource = common.prettify(resource) | 
 | 739 |                             if (!disableAskOnError && env["ASK_ON_ERROR"] && env["ASK_ON_ERROR"] == "true") { | 
 | 740 |                                 timeout(time:1, unit:'HOURS') { | 
 | 741 |                                    input message: "False result on ${nodeKey} found, resource ${prettyResource}. \nDo you want to continue?" | 
 | 742 |                                 } | 
 | 743 |                             } else { | 
 | 744 |                                 def errorMsg = "Salt state on node ${nodeKey} failed. Resource: ${prettyResource}" | 
 | 745 |                                 if (failOnError) { | 
 | 746 |                                     throw new Exception(errorMsg) | 
 | 747 |                                 } else { | 
 | 748 |                                     common.errorMsg(errorMsg) | 
 | 749 |                                 } | 
 | 750 |                             } | 
 | 751 |                         } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 752 |                     } | 
 | 753 |                 } | 
| Jakub Josef | 52f69f7 | 2017-03-14 15:18:08 +0100 | [diff] [blame] | 754 |             } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 755 |         }else{ | 
 | 756 |             common.errorMsg("Salt result hasn't return attribute! Result: ${result}") | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 757 |         } | 
| Jakub Josef | 52f69f7 | 2017-03-14 15:18:08 +0100 | [diff] [blame] | 758 |     }else{ | 
| Jakub Josef | a87941c | 2017-04-20 17:14:58 +0200 | [diff] [blame] | 759 |         common.errorMsg("Cannot check salt result, given result is null") | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 760 |     } | 
 | 761 | } | 
 | 762 |  | 
 | 763 | /** | 
| Oleg Iurchenko | 7eb2150 | 2017-11-28 18:53:43 +0200 | [diff] [blame] | 764 | * Parse salt API output to check minion restart and wait some time to be sure minion is up. | 
 | 765 | * See https://mirantis.jira.com/browse/PROD-16258 for more details | 
 | 766 | * TODO: change sleep to more tricky procedure. | 
 | 767 | * | 
 | 768 | * @param result    Parsed response of Salt API | 
 | 769 | */ | 
 | 770 | def waitForMinion(result) { | 
 | 771 |     def common = new com.mirantis.mk.Common() | 
| Oleg Iurchenko | 3eedc78 | 2017-12-12 11:49:29 +0200 | [diff] [blame] | 772 |     //In order to prevent multiple sleeps use bool variable to catch restart for any minion. | 
| Oleg Iurchenko | 7eb2150 | 2017-11-28 18:53:43 +0200 | [diff] [blame] | 773 |     def isMinionRestarted = false | 
| Oleg Iurchenko | 3eedc78 | 2017-12-12 11:49:29 +0200 | [diff] [blame] | 774 |     if(result != null){ | 
 | 775 |         if(result['return']){ | 
 | 776 |             for (int i=0;i<result['return'].size();i++) { | 
 | 777 |                 def entry = result['return'][i] | 
 | 778 |                 // exit in case of empty response. | 
 | 779 |                 if (!entry) { | 
 | 780 |                     return | 
 | 781 |                 } | 
 | 782 |                 // Loop for nodes | 
 | 783 |                 for (int j=0;j<entry.size();j++) { | 
 | 784 |                     def nodeKey = entry.keySet()[j] | 
 | 785 |                     def node=entry[nodeKey] | 
 | 786 |                     if(node instanceof Map || node instanceof List){ | 
 | 787 |                         // Loop for node resources | 
 | 788 |                         for (int k=0;k<node.size();k++) { | 
 | 789 |                             def resource; | 
 | 790 |                             def resKey; | 
 | 791 |                             if(node instanceof Map){ | 
 | 792 |                                 resKey = node.keySet()[k] | 
 | 793 |                             }else if(node instanceof List){ | 
 | 794 |                                 resKey = k | 
 | 795 |                             } | 
 | 796 |                             resource = node[resKey] | 
 | 797 |                             if(resKey.contains("salt_minion_service_restart") && resource instanceof Map && resource.keySet().contains("result")){ | 
 | 798 |                                 if((resource["result"] instanceof Boolean && resource["result"]) || (resource["result"] instanceof String && resource["result"] == "true")){ | 
 | 799 |                                     if(resource.changes.size() > 0){ | 
 | 800 |                                         isMinionRestarted=true | 
 | 801 |                                     } | 
 | 802 |                                 } | 
 | 803 |                             } | 
 | 804 |                         } | 
 | 805 |                     } | 
 | 806 |                 } | 
 | 807 |             } | 
| Oleg Iurchenko | 7eb2150 | 2017-11-28 18:53:43 +0200 | [diff] [blame] | 808 |         } | 
 | 809 |     } | 
| Oleg Iurchenko | 7eb2150 | 2017-11-28 18:53:43 +0200 | [diff] [blame] | 810 |     if (isMinionRestarted){ | 
 | 811 |         common.infoMsg("Salt minion service restart detected. Sleep 10 seconds to wait minion restart") | 
 | 812 |         sleep(10) | 
 | 813 |     } | 
 | 814 | } | 
 | 815 |  | 
 | 816 | /** | 
| Jakub Josef | 7852fe1 | 2017-03-15 16:02:41 +0100 | [diff] [blame] | 817 |  * Print salt command run results in human-friendly form | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 818 |  * | 
 | 819 |  * @param result        Parsed response of Salt API | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 820 |  */ | 
| Filip Pytloun | d2f1bbe | 2017-02-27 19:03:51 +0100 | [diff] [blame] | 821 | def printSaltCommandResult(result) { | 
| Jakub Josef | 871bf15 | 2017-03-14 20:13:41 +0100 | [diff] [blame] | 822 |     def common = new com.mirantis.mk.Common() | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 823 |     if(result != null){ | 
 | 824 |         if(result['return']){ | 
 | 825 |             for (int i=0; i<result['return'].size(); i++) { | 
 | 826 |                 def entry = result['return'][i] | 
 | 827 |                 for (int j=0; j<entry.size(); j++) { | 
 | 828 |                     common.debugMsg("printSaltCommandResult: printing salt command entry: ${entry}") | 
 | 829 |                     def nodeKey = entry.keySet()[j] | 
 | 830 |                     def node=entry[nodeKey] | 
| Jakub Josef | bceaa32 | 2017-06-13 18:28:27 +0200 | [diff] [blame] | 831 |                     common.infoMsg(String.format("Node %s changes:\n%s",nodeKey, common.prettify(node))) | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 832 |                 } | 
| Jakub Josef | 8a715bf | 2017-03-14 21:39:01 +0100 | [diff] [blame] | 833 |             } | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 834 |         }else{ | 
 | 835 |             common.errorMsg("Salt result hasn't return attribute! Result: ${result}") | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 836 |         } | 
| Jakub Josef | 8a715bf | 2017-03-14 21:39:01 +0100 | [diff] [blame] | 837 |     }else{ | 
| Jakub Josef | d9afd0e | 2017-03-15 19:19:23 +0100 | [diff] [blame] | 838 |         common.errorMsg("Cannot print salt command result, given result is null") | 
| Jakub Josef | 52f69f7 | 2017-03-14 15:18:08 +0100 | [diff] [blame] | 839 |     } | 
| Jakub Josef | 79ecec3 | 2017-02-17 14:36:28 +0100 | [diff] [blame] | 840 | } | 
| Tomáš Kukrál | b12eedd | 2017-04-21 10:45:13 +0200 | [diff] [blame] | 841 |  | 
 | 842 |  | 
 | 843 | /** | 
 | 844 |  * Return content of file target | 
 | 845 |  * | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 846 |  * @param saltId    Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Tomáš Kukrál | b12eedd | 2017-04-21 10:45:13 +0200 | [diff] [blame] | 847 |  * @param target    Compound target (should target only one host) | 
 | 848 |  * @param file      File path to read (/etc/hosts for example) | 
 | 849 |  */ | 
 | 850 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 851 | def getFileContent(saltId, target, file) { | 
 | 852 |     result = cmdRun(saltId, target, "cat ${file}") | 
| Tomáš Kukrál | f1a692a | 2017-08-11 13:29:28 +0200 | [diff] [blame] | 853 |     return result['return'][0].values()[0].replaceAll('Salt command execution success','') | 
| Tomáš Kukrál | b12eedd | 2017-04-21 10:45:13 +0200 | [diff] [blame] | 854 | } | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 855 |  | 
 | 856 | /** | 
 | 857 |  * Set override parameters in Salt cluster metadata | 
 | 858 |  * | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 859 |  * @param saltId         Salt Connection object or pepperEnv (the command will be sent using the selected method) | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 860 |  * @param salt_overrides YAML formatted string containing key: value, one per line | 
| Matthew Mosesohn | e564684 | 2017-07-19 16:54:57 +0300 | [diff] [blame] | 861 |  * @param reclass_dir    Directory where Reclass git repo is located | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 862 |  */ | 
 | 863 |  | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 864 | def setSaltOverrides(saltId, salt_overrides, reclass_dir="/srv/salt/reclass") { | 
| Tomáš Kukrál | f178f05 | 2017-07-11 11:31:00 +0200 | [diff] [blame] | 865 |     def common = new com.mirantis.mk.Common() | 
| Mykyta Karpin | 1c165e2 | 2017-08-22 18:27:01 +0300 | [diff] [blame] | 866 |     def salt_overrides_map = readYaml text: salt_overrides | 
| Tomáš Kukrál | 243cf84 | 2017-07-11 13:11:56 +0200 | [diff] [blame] | 867 |     for (entry in common.entries(salt_overrides_map)) { | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 868 |          def key = entry[0] | 
 | 869 |          def value = entry[1] | 
 | 870 |  | 
 | 871 |          common.debugMsg("Set salt override ${key}=${value}") | 
| Mykyta Karpin | d4a42d0 | 2017-11-16 16:24:37 +0200 | [diff] [blame] | 872 |          runSaltProcessStep(saltId, 'I@salt:master', 'reclass.cluster_meta_set', [key, value], false) | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 873 |     } | 
| chnyda | a0dbb25 | 2017-10-05 10:46:09 +0200 | [diff] [blame] | 874 |     runSaltProcessStep(saltId, 'I@salt:master', 'cmd.run', ["git -C ${reclass_dir} update-index --skip-worktree classes/cluster/overrides.yml"]) | 
| Matthew Mosesohn | 9e88085 | 2017-07-04 21:17:53 +0300 | [diff] [blame] | 875 | } | 
| Oleg Grigorov | bec4558 | 2017-09-12 20:29:24 +0300 | [diff] [blame] | 876 |  | 
 | 877 | /** | 
 | 878 | * Execute salt commands via salt-api with | 
 | 879 | * CLI client salt-pepper | 
 | 880 | * | 
 | 881 | * @param data   Salt command map | 
 | 882 | * @param venv   Path to virtualenv with | 
 | 883 | */ | 
 | 884 |  | 
 | 885 | def runPepperCommand(data, venv)   { | 
| Jakub Josef | 03d4d5a | 2017-12-20 16:35:09 +0100 | [diff] [blame] | 886 |     def common = new com.mirantis.mk.Common() | 
| Oleg Grigorov | bec4558 | 2017-09-12 20:29:24 +0300 | [diff] [blame] | 887 |     def python = new com.mirantis.mk.Python() | 
 | 888 |     def dataStr = new groovy.json.JsonBuilder(data).toString() | 
| chnyda | 4901a04 | 2017-11-16 12:14:56 +0100 | [diff] [blame] | 889 |  | 
| Jakub Josef | a2491ad | 2018-01-15 16:26:27 +0100 | [diff] [blame] | 890 |     def pepperCmdFile = "${venv}/pepper-cmd.json" | 
 | 891 |     writeFile file: pepperCmdFile, text: dataStr | 
 | 892 |     def pepperCmd = "pepper -c ${venv}/pepperrc --make-token -x ${venv}/.peppercache --json-file ${pepperCmdFile}" | 
| Oleg Grigorov | bec4558 | 2017-09-12 20:29:24 +0300 | [diff] [blame] | 893 |  | 
 | 894 |     if (venv) { | 
| Jakub Josef | e2f4ebb | 2018-01-15 16:11:51 +0100 | [diff] [blame] | 895 |         output = python.runVirtualenvCommand(venv, pepperCmd, true) | 
| Oleg Grigorov | bec4558 | 2017-09-12 20:29:24 +0300 | [diff] [blame] | 896 |     } else { | 
 | 897 |         echo("[Command]: ${pepperCmd}") | 
 | 898 |         output = sh ( | 
 | 899 |             script: pepperCmd, | 
 | 900 |             returnStdout: true | 
 | 901 |         ).trim() | 
 | 902 |     } | 
 | 903 |  | 
| Jakub Josef | 37cd497 | 2018-02-01 16:25:25 +0100 | [diff] [blame] | 904 |     def outputObj | 
 | 905 |     try { | 
 | 906 |        outputObj = new groovy.json.JsonSlurperClassic().parseText(output) | 
 | 907 |     } catch(Exception e) { | 
 | 908 |        common.errorMsg("Parsing Salt API JSON response failed! Response: " + output) | 
 | 909 |        throw e | 
 | 910 |     } | 
 | 911 |     return outputObj | 
| Oleg Grigorov | bec4558 | 2017-09-12 20:29:24 +0300 | [diff] [blame] | 912 | } |