blob: 459bfc4458d950da1544ebf9b012e3899d97c0e2 [file] [log] [blame]
Martin Polreichaae1b9d2018-12-05 11:12:23 +01001/**
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].
Martin Polreich0d538262019-02-01 14:46:10 +01007 * ASK_CONFIRMATION Ask confirmation for restore
Martin Polreich0d538262019-02-01 14:46:10 +01008 * VERIFICATION_RETRIES Number of restries to verify the restoration.
Martin Polreich2aa74402019-01-21 14:42:48 +01009 * CHECK_TIME_SYNC Set to true to check time synchronization accross selected nodes.
Martin Polreich7ba33592019-03-21 15:12:15 +010010 * RESTORE_TYPE Sets restoration method
Martin Polreichaae1b9d2018-12-05 11:12:23 +010011 *
12**/
13
14def common = new com.mirantis.mk.Common()
15def salt = new com.mirantis.mk.Salt()
Martin Polreich71a08db2019-02-15 10:09:10 +010016def galera = new com.mirantis.mk.Galera()
Martin Polreichaae1b9d2018-12-05 11:12:23 +010017def python = new com.mirantis.mk.Python()
Martin Polreichaae1b9d2018-12-05 11:12:23 +010018def pepperEnv = "pepperEnv"
19def resultCode = 99
Martin Polreich7ba33592019-03-21 15:12:15 +010020def restoreType = env.RESTORE_TYPE
21def runRestoreDb = false
22def runBackupDb = false
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040023def restartCluster = false
Martin Polreichaae1b9d2018-12-05 11:12:23 +010024
Martin Polreich0d538262019-02-01 14:46:10 +010025askConfirmation = (env.getProperty('ASK_CONFIRMATION') ?: true).toBoolean()
Martin Polreich2aa74402019-01-21 14:42:48 +010026checkTimeSync = (env.getProperty('CHECK_TIME_SYNC') ?: true).toBoolean()
Sergeyc8a8a792019-01-15 17:27:59 +040027
Martin Polreich0d538262019-02-01 14:46:10 +010028if (common.validInputParam(VERIFICATION_RETRIES) && VERIFICATION_RETRIES.isInteger()) {
29 verificationRetries = VERIFICATION_RETRIES.toInteger()
30} else {
31 verificationRetries = 5
32}
Martin Polreich7ba33592019-03-21 15:12:15 +010033if (restoreType.equals("BACKUP_AND_RESTORE") || restoreType.equals("ONLY_RESTORE")) {
34 runRestoreDb = true
35}
36if (restoreType.equals("BACKUP_AND_RESTORE")) {
37 runBackupDb = true
38}
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040039if (restoreType.equals("RESTART_CLUSTER")) {
40 restartCluster = true
41}
Martin Polreich0d538262019-02-01 14:46:10 +010042
Martin Polreichaae1b9d2018-12-05 11:12:23 +010043timeout(time: 12, unit: 'HOURS') {
44 node() {
45 stage('Setup virtualenv for Pepper') {
46 python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
47 }
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040048
49 def galeraStatus = [:]
Martin Polreich7ba33592019-03-21 15:12:15 +010050 stage('Verify status') {
Ivan Berezovskiyb6d18d52019-07-24 15:30:54 +040051 def sysstatTargets = 'I@xtrabackup:client or I@xtrabackup:server'
52 def sysstatTargetsNodes = salt.getMinions(pepperEnv, sysstatTargets)
53 try {
54 if (!salt.isPackageInstalled(['saltId': pepperEnv, 'target': sysstatTargets, 'packageName': 'sysstat', 'output': false])) {
55 if (askConfirmation) {
56 input message: "Do you want to install 'sysstat' package on targeted nodes: ${sysstatTargetsNodes}? Click to confirm"
57 }
58 salt.runSaltProcessStep(pepperEnv, sysstatTargets, 'pkg.install', ['sysstat'])
59 }
60 } catch (Exception e) {
61 common.errorMsg("Unable to determine status of sysstat package on target nodes: ${sysstatTargetsNodes}.")
Martin Polreich63b40fc2019-08-07 18:07:57 +020062 common.errorMsg(e.getMessage())
Ivan Berezovskiyb6d18d52019-07-24 15:30:54 +040063 if (askConfirmation) {
64 input message: "Do you want to continue? Click to confirm"
65 }
66 }
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040067 galeraStatus = galera.verifyGaleraStatus(pepperEnv, checkTimeSync)
68
69 switch (galeraStatus.error) {
70 case 128:
71 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 Polreichaae1b9d2018-12-05 11:12:23 +010072 currentBuild.result = "FAILURE"
Martin Polreich0d538262019-02-01 14:46:10 +010073 return
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040074 case 130:
Martin Polreich63b40fc2019-08-07 18:07:57 +020075 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 Polreichaae1b9d2018-12-05 11:12:23 +010076 currentBuild.result = "FAILURE"
Martin Polreich0d538262019-02-01 14:46:10 +010077 return
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040078 case 131:
79 common.errorMsg("Time desynced - Please fix this issue and rerun the pipeline.")
80 currentBuild.result = "FAILURE"
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +040081 return
Denis Egorenkoc4da1a12019-08-12 18:17:02 +040082 case 140..141:
83 common.errorMsg("Disk utilization check failed - Please fix this issue and rerun the pipeline.")
84 currentBuild.result = "FAILURE"
85 return
86 case 1:
87 if (askConfirmation) {
88 input message: "There was a problem with parsing the status output or with determining it. Do you want to run a next action: ${restoreType}?"
89 } else {
90 common.warningMsg("There was a problem with parsing the status output or with determining it. Trying to perform action: ${restoreType}.")
91 }
92 break
93 case 0:
94 if (askConfirmation) {
95 input message: "There seems to be everything alright with the cluster, do you still want to continue with next action: ${restoreType}?"
96 break
97 } else {
98 common.warningMsg("There seems to be everything alright with the cluster, no backup and no restoration will be done.")
99 currentBuild.result = "SUCCESS"
100 return
101 }
102 default:
103 if (askConfirmation) {
104 input message: "There's something wrong with the cluster, do you want to continue with action: ${restoreType}?"
105 } else {
106 common.warningMsg("There's something wrong with the cluster, trying to perform action: ${restoreType}")
107 }
108 break
Martin Polreichaae1b9d2018-12-05 11:12:23 +0100109 }
Martin Polreich7ba33592019-03-21 15:12:15 +0100110 }
111 if (runBackupDb) {
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400112 if (askConfirmation) {
113 input message: "Are you sure you want to run a backup? Click to confirm"
114 }
Martin Polreich7ba33592019-03-21 15:12:15 +0100115 stage('Backup') {
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400116 deployBuild = build(job: 'galera_backup_database', parameters: [
117 [$class: 'StringParameterValue', name: 'SALT_MASTER_URL', value: SALT_MASTER_URL],
118 [$class: 'StringParameterValue', name: 'SALT_MASTER_CREDENTIALS', value: SALT_MASTER_CREDENTIALS],
119 [$class: 'StringParameterValue', name: 'OVERRIDE_BACKUP_NODE', value: "none"],
120 ]
Martin Polreich7ba33592019-03-21 15:12:15 +0100121 )
122 }
123 }
Denis Egorenkoc4da1a12019-08-12 18:17:02 +0400124 if (runRestoreDb || restartCluster) {
125 if (runRestoreDb) {
126 stage('Restore') {
127 if (askConfirmation) {
128 input message: "Are you sure you want to run a restore? Click to confirm"
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400129 }
Denis Egorenkoc4da1a12019-08-12 18:17:02 +0400130 try {
131 if ((!askConfirmation && resultCode > 0) || askConfirmation) {
132 galera.restoreGaleraCluster(pepperEnv, galeraStatus)
133 }
134 } catch (Exception e) {
135 common.errorMsg("Restoration process has failed.")
136 common.errorMsg(e.getMessage())
137 }
138 }
139 }
140 if (restartCluster) {
141 stage('Restart cluster') {
142 if (askConfirmation) {
143 input message: "Are you sure you want to run a restart? Click to confirm"
144 }
145 try {
146 if ((!askConfirmation && resultCode > 0) || askConfirmation) {
147 galera.restoreGaleraCluster(pepperEnv, galeraStatus, false)
148 }
149 } catch (Exception e) {
150 common.errorMsg("Restart process has failed.")
151 common.errorMsg(e.getMessage())
152 }
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400153 }
Martin Polreichaae1b9d2018-12-05 11:12:23 +0100154 }
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400155 stage('Verify restoration result') {
156 common.retry(verificationRetries, 15) {
Denis Egorenkoc4da1a12019-08-12 18:17:02 +0400157 def status = galera.verifyGaleraStatus(pepperEnv, false)
158 if (status.error >= 1) {
Ivan Berezovskiy6ef32f02019-07-26 15:55:24 +0400159 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.")
160 } else {
161 common.infoMsg("Restoration procedure seems to be successful. See verification report to be sure.")
162 currentBuild.result = "SUCCESS"
163 }
Martin Polreich0d538262019-02-01 14:46:10 +0100164 }
Martin Polreichc9466c72019-01-18 14:17:52 +0100165 }
166 }
Martin Polreichaae1b9d2018-12-05 11:12:23 +0100167 }
168}