Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 1 | /** |
| 2 | * Verify and restore Galera cluster |
| 3 | * |
| 4 | * Expected parameters: |
| 5 | * SALT_MASTER_CREDENTIALS Credentials to the Salt API. |
| 6 | * SALT_MASTER_URL Full Salt API address [http://10.10.10.1:8000]. |
Sergey | 6579de6 | 2019-01-15 17:27:59 +0400 | [diff] [blame] | 7 | * ASK_CONFIRMATION Ask confirmation for restore |
Martin Polreich | 721b725 | 2019-01-21 14:42:48 +0100 | [diff] [blame] | 8 | * CHECK_TIME_SYNC Set to true to check time synchronization accross selected nodes. |
Martin Polreich | f7889b5 | 2019-02-01 14:46:10 +0100 | [diff] [blame] | 9 | * VERIFICATION_RETRIES Number of restries to verify the restoration. |
Martin Polreich | ddfdb61 | 2019-03-21 15:12:15 +0100 | [diff] [blame] | 10 | * RESTORE_TYPE Sets restoration method |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 11 | * |
| 12 | **/ |
| 13 | |
| 14 | def common = new com.mirantis.mk.Common() |
| 15 | def salt = new com.mirantis.mk.Salt() |
Martin Polreich | 208c487 | 2019-02-15 10:09:10 +0100 | [diff] [blame] | 16 | def galera = new com.mirantis.mk.Galera() |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 17 | def python = new com.mirantis.mk.Python() |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 18 | def pepperEnv = "pepperEnv" |
| 19 | def resultCode = 99 |
Martin Polreich | ddfdb61 | 2019-03-21 15:12:15 +0100 | [diff] [blame] | 20 | def restoreType = env.RESTORE_TYPE |
| 21 | def runRestoreDb = false |
| 22 | def runBackupDb = false |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 23 | def restartCluster = false |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 24 | |
Sergey | 6579de6 | 2019-01-15 17:27:59 +0400 | [diff] [blame] | 25 | askConfirmation = (env.getProperty('ASK_CONFIRMATION') ?: true).toBoolean() |
Martin Polreich | 721b725 | 2019-01-21 14:42:48 +0100 | [diff] [blame] | 26 | checkTimeSync = (env.getProperty('CHECK_TIME_SYNC') ?: true).toBoolean() |
Martin Polreich | 49f16c0 | 2019-02-04 13:14:01 +0100 | [diff] [blame] | 27 | if (common.validInputParam('VERIFICATION_RETRIES') && VERIFICATION_RETRIES.isInteger()) { |
Martin Polreich | f7889b5 | 2019-02-01 14:46:10 +0100 | [diff] [blame] | 28 | verificationRetries = VERIFICATION_RETRIES.toInteger() |
| 29 | } else { |
| 30 | verificationRetries = 5 |
| 31 | } |
Martin Polreich | ddfdb61 | 2019-03-21 15:12:15 +0100 | [diff] [blame] | 32 | if (restoreType.equals("BACKUP_AND_RESTORE") || restoreType.equals("ONLY_RESTORE")) { |
| 33 | runRestoreDb = true |
| 34 | } |
| 35 | if (restoreType.equals("BACKUP_AND_RESTORE")) { |
| 36 | runBackupDb = true |
| 37 | } |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 38 | if (restoreType.equals("RESTART_CLUSTER")) { |
| 39 | restartCluster = true |
| 40 | } |
Sergey | 6579de6 | 2019-01-15 17:27:59 +0400 | [diff] [blame] | 41 | |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 42 | timeout(time: 12, unit: 'HOURS') { |
| 43 | node() { |
| 44 | stage('Setup virtualenv for Pepper') { |
| 45 | python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS) |
| 46 | } |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 47 | |
| 48 | def galeraStatus = [:] |
Martin Polreich | ddfdb61 | 2019-03-21 15:12:15 +0100 | [diff] [blame] | 49 | stage('Verify status') { |
Ivan Berezovskiy | 844da96 | 2019-07-24 15:30:54 +0400 | [diff] [blame] | 50 | def sysstatTargets = 'I@xtrabackup:client or I@xtrabackup:server' |
| 51 | def sysstatTargetsNodes = salt.getMinions(pepperEnv, sysstatTargets) |
| 52 | try { |
| 53 | if (!salt.isPackageInstalled(['saltId': pepperEnv, 'target': sysstatTargets, 'packageName': 'sysstat', 'output': false])) { |
| 54 | if (askConfirmation) { |
| 55 | input message: "Do you want to install 'sysstat' package on targeted nodes: ${sysstatTargetsNodes}? Click to confirm" |
| 56 | } |
| 57 | salt.runSaltProcessStep(pepperEnv, sysstatTargets, 'pkg.install', ['sysstat']) |
| 58 | } |
| 59 | } catch (Exception e) { |
| 60 | common.errorMsg("Unable to determine status of sysstat package on target nodes: ${sysstatTargetsNodes}.") |
Martin Polreich | 42767c3 | 2019-08-07 18:07:57 +0200 | [diff] [blame] | 61 | common.errorMsg(e.getMessage()) |
Ivan Berezovskiy | 844da96 | 2019-07-24 15:30:54 +0400 | [diff] [blame] | 62 | if (askConfirmation) { |
| 63 | input message: "Do you want to continue? Click to confirm" |
| 64 | } |
| 65 | } |
Denis Egorenko | b33c76a | 2019-10-22 18:10:22 +0400 | [diff] [blame] | 66 | try { |
| 67 | common.infoMsg('Checking required xtrabackup pillars...') |
| 68 | def xtrabackupRestoreFrom = salt.getPillar(pepperEnv, 'I@galera:master or I@galera:slave', 'xtrabackup:client:restore_from') |
| 69 | def xtrabackupRestoreLatest = salt.getPillar(pepperEnv, 'I@galera:master or I@galera:slave', 'xtrabackup:client:restore_full_latest') |
| 70 | if ('' in xtrabackupRestoreFrom['return'][0].values() || '' in xtrabackupRestoreLatest['return'][0].values()) { |
| 71 | throw new Exception('Pillars xtrabackup:client:restore_from or xtrabackup:client:restore_full_latest are missed for \'I@galera:master or I@galera:slave\' nodes.') |
| 72 | } |
| 73 | } catch (Exception e) { |
| 74 | common.errorMsg(e.getMessage()) |
| 75 | common.errorMsg('Please fix your pillar data. For more information check docs: https://docs.mirantis.com/mcp/latest/mcp-operations-guide/backup-restore/openstack/database/xtrabackup-restore-database.html') |
| 76 | return |
| 77 | } |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 78 | galeraStatus = galera.verifyGaleraStatus(pepperEnv, checkTimeSync) |
| 79 | |
| 80 | switch (galeraStatus.error) { |
| 81 | case 128: |
| 82 | common.errorMsg("Unable to obtain Galera members minions list. Without fixing this issue, pipeline cannot continue in verification, backup and restoration. This may be caused by wrong Galera configuration or corrupted pillar data.") |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 83 | currentBuild.result = "FAILURE" |
Martin Polreich | f7889b5 | 2019-02-01 14:46:10 +0100 | [diff] [blame] | 84 | return |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 85 | case 130: |
Martin Polreich | 42767c3 | 2019-08-07 18:07:57 +0200 | [diff] [blame] | 86 | common.errorMsg("Neither master or slaves are reachable. Without fixing this issue, pipeline cannot continue in verification, backup and restoration. Is at least one member of the Galera cluster up and running?") |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 87 | currentBuild.result = "FAILURE" |
Martin Polreich | f7889b5 | 2019-02-01 14:46:10 +0100 | [diff] [blame] | 88 | return |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 89 | case 131: |
| 90 | common.errorMsg("Time desynced - Please fix this issue and rerun the pipeline.") |
| 91 | currentBuild.result = "FAILURE" |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 92 | return |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 93 | case 140..141: |
| 94 | common.errorMsg("Disk utilization check failed - Please fix this issue and rerun the pipeline.") |
| 95 | currentBuild.result = "FAILURE" |
| 96 | return |
| 97 | case 1: |
| 98 | if (askConfirmation) { |
| 99 | input message: "There was a problem with parsing the status output or with determining it. Do you want to run a next action: ${restoreType}?" |
| 100 | } else { |
| 101 | common.warningMsg("There was a problem with parsing the status output or with determining it. Trying to perform action: ${restoreType}.") |
| 102 | } |
| 103 | break |
| 104 | case 0: |
| 105 | if (askConfirmation) { |
| 106 | input message: "There seems to be everything alright with the cluster, do you still want to continue with next action: ${restoreType}?" |
| 107 | break |
| 108 | } else { |
| 109 | common.warningMsg("There seems to be everything alright with the cluster, no backup and no restoration will be done.") |
| 110 | currentBuild.result = "SUCCESS" |
| 111 | return |
| 112 | } |
| 113 | default: |
| 114 | if (askConfirmation) { |
| 115 | input message: "There's something wrong with the cluster, do you want to continue with action: ${restoreType}?" |
| 116 | } else { |
| 117 | common.warningMsg("There's something wrong with the cluster, trying to perform action: ${restoreType}") |
| 118 | } |
| 119 | break |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 120 | } |
Martin Polreich | 0f3b85d | 2019-04-02 14:22:11 +0200 | [diff] [blame] | 121 | } |
| 122 | if (runBackupDb) { |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 123 | if (askConfirmation) { |
| 124 | input message: "Are you sure you want to run a backup? Click to confirm" |
| 125 | } |
Martin Polreich | 0f3b85d | 2019-04-02 14:22:11 +0200 | [diff] [blame] | 126 | stage('Backup') { |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 127 | deployBuild = build(job: 'galera_backup_database', parameters: [ |
| 128 | [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: SALT_MASTER_URL], |
| 129 | [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: SALT_MASTER_CREDENTIALS], |
| 130 | [$class: 'StringParameterValue', name: 'OVERRIDE_BACKUP_NODE', value: "none"], |
| 131 | ] |
Martin Polreich | 0f3b85d | 2019-04-02 14:22:11 +0200 | [diff] [blame] | 132 | ) |
| 133 | } |
| 134 | } |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 135 | if (runRestoreDb || restartCluster) { |
| 136 | if (runRestoreDb) { |
| 137 | stage('Restore') { |
| 138 | if (askConfirmation) { |
| 139 | input message: "Are you sure you want to run a restore? Click to confirm" |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 140 | } |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 141 | try { |
| 142 | if ((!askConfirmation && resultCode > 0) || askConfirmation) { |
| 143 | galera.restoreGaleraCluster(pepperEnv, galeraStatus) |
| 144 | } |
| 145 | } catch (Exception e) { |
| 146 | common.errorMsg("Restoration process has failed.") |
| 147 | common.errorMsg(e.getMessage()) |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | if (restartCluster) { |
| 152 | stage('Restart cluster') { |
| 153 | if (askConfirmation) { |
| 154 | input message: "Are you sure you want to run a restart? Click to confirm" |
| 155 | } |
| 156 | try { |
| 157 | if ((!askConfirmation && resultCode > 0) || askConfirmation) { |
| 158 | galera.restoreGaleraCluster(pepperEnv, galeraStatus, false) |
| 159 | } |
| 160 | } catch (Exception e) { |
| 161 | common.errorMsg("Restart process has failed.") |
| 162 | common.errorMsg(e.getMessage()) |
| 163 | } |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 164 | } |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 165 | } |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 166 | stage('Verify restoration result') { |
| 167 | common.retry(verificationRetries, 15) { |
Denis Egorenko | afc4344 | 2019-08-12 18:17:02 +0400 | [diff] [blame] | 168 | def status = galera.verifyGaleraStatus(pepperEnv, false) |
| 169 | if (status.error >= 1) { |
Ivan Berezovskiy | 6482411 | 2019-07-26 15:55:24 +0400 | [diff] [blame] | 170 | error("Verification attempt finished with an error. This may be caused by cluster not having enough time to come up or to sync. Next verification attempt in 5 seconds.") |
| 171 | } else { |
| 172 | common.infoMsg("Restoration procedure seems to be successful. See verification report to be sure.") |
| 173 | currentBuild.result = "SUCCESS" |
| 174 | } |
Martin Polreich | f7889b5 | 2019-02-01 14:46:10 +0100 | [diff] [blame] | 175 | } |
Martin Polreich | 7bc654c | 2019-01-18 14:17:52 +0100 | [diff] [blame] | 176 | } |
| 177 | } |
Martin Polreich | f7a1bb0 | 2018-12-05 11:12:23 +0100 | [diff] [blame] | 178 | } |
| 179 | } |