blob: ac1d35204e1961f6941f4dfdb58629815e293717 [file] [log] [blame]
Martin Polreichf7a1bb02018-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].
Sergey6579de62019-01-15 17:27:59 +04007 * ASK_CONFIRMATION Ask confirmation for restore
Martin Polreich721b7252019-01-21 14:42:48 +01008 * CHECK_TIME_SYNC Set to true to check time synchronization accross selected nodes.
Martin Polreichf7889b52019-02-01 14:46:10 +01009 * VERIFICATION_RETRIES Number of restries to verify the restoration.
Martin Polreichddfdb612019-03-21 15:12:15 +010010 * RESTORE_TYPE Sets restoration method
Martin Polreichf7a1bb02018-12-05 11:12:23 +010011 *
12**/
13
14def common = new com.mirantis.mk.Common()
15def salt = new com.mirantis.mk.Salt()
Martin Polreich208c4872019-02-15 10:09:10 +010016def galera = new com.mirantis.mk.Galera()
Martin Polreichf7a1bb02018-12-05 11:12:23 +010017def python = new com.mirantis.mk.Python()
Martin Polreichf7a1bb02018-12-05 11:12:23 +010018def pepperEnv = "pepperEnv"
19def resultCode = 99
Martin Polreichddfdb612019-03-21 15:12:15 +010020def restoreType = env.RESTORE_TYPE
21def runRestoreDb = false
22def runBackupDb = false
Denis Egorenkoafc43442019-08-12 18:17:02 +040023def restartCluster = false
Martin Polreichf7a1bb02018-12-05 11:12:23 +010024
Sergey6579de62019-01-15 17:27:59 +040025askConfirmation = (env.getProperty('ASK_CONFIRMATION') ?: true).toBoolean()
Martin Polreich721b7252019-01-21 14:42:48 +010026checkTimeSync = (env.getProperty('CHECK_TIME_SYNC') ?: true).toBoolean()
Martin Polreich49f16c02019-02-04 13:14:01 +010027if (common.validInputParam('VERIFICATION_RETRIES') && VERIFICATION_RETRIES.isInteger()) {
Martin Polreichf7889b52019-02-01 14:46:10 +010028 verificationRetries = VERIFICATION_RETRIES.toInteger()
29} else {
30 verificationRetries = 5
31}
Martin Polreichddfdb612019-03-21 15:12:15 +010032if (restoreType.equals("BACKUP_AND_RESTORE") || restoreType.equals("ONLY_RESTORE")) {
33 runRestoreDb = true
34}
35if (restoreType.equals("BACKUP_AND_RESTORE")) {
36 runBackupDb = true
37}
Denis Egorenkoafc43442019-08-12 18:17:02 +040038if (restoreType.equals("RESTART_CLUSTER")) {
39 restartCluster = true
40}
Sergey6579de62019-01-15 17:27:59 +040041
Martin Polreichf7a1bb02018-12-05 11:12:23 +010042timeout(time: 12, unit: 'HOURS') {
43 node() {
44 stage('Setup virtualenv for Pepper') {
45 python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
46 }
Denis Egorenkoafc43442019-08-12 18:17:02 +040047
48 def galeraStatus = [:]
Martin Polreichddfdb612019-03-21 15:12:15 +010049 stage('Verify status') {
Ivan Berezovskiy844da962019-07-24 15:30:54 +040050 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 Polreich42767c32019-08-07 18:07:57 +020061 common.errorMsg(e.getMessage())
Ivan Berezovskiy844da962019-07-24 15:30:54 +040062 if (askConfirmation) {
63 input message: "Do you want to continue? Click to confirm"
64 }
65 }
Denis Egorenkob33c76a2019-10-22 18:10:22 +040066 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 Egorenkoafc43442019-08-12 18:17:02 +040078 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 Polreichf7a1bb02018-12-05 11:12:23 +010083 currentBuild.result = "FAILURE"
Martin Polreichf7889b52019-02-01 14:46:10 +010084 return
Denis Egorenkoafc43442019-08-12 18:17:02 +040085 case 130:
Martin Polreich42767c32019-08-07 18:07:57 +020086 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 Polreichf7a1bb02018-12-05 11:12:23 +010087 currentBuild.result = "FAILURE"
Martin Polreichf7889b52019-02-01 14:46:10 +010088 return
Denis Egorenkoafc43442019-08-12 18:17:02 +040089 case 131:
90 common.errorMsg("Time desynced - Please fix this issue and rerun the pipeline.")
91 currentBuild.result = "FAILURE"
Ivan Berezovskiy64824112019-07-26 15:55:24 +040092 return
Denis Egorenkoafc43442019-08-12 18:17:02 +040093 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 Polreichf7a1bb02018-12-05 11:12:23 +0100120 }
Martin Polreich0f3b85d2019-04-02 14:22:11 +0200121 }
122 if (runBackupDb) {
Ivan Berezovskiy64824112019-07-26 15:55:24 +0400123 if (askConfirmation) {
124 input message: "Are you sure you want to run a backup? Click to confirm"
125 }
Martin Polreich0f3b85d2019-04-02 14:22:11 +0200126 stage('Backup') {
Ivan Berezovskiy64824112019-07-26 15:55:24 +0400127 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 Polreich0f3b85d2019-04-02 14:22:11 +0200132 )
133 }
134 }
Denis Egorenkoafc43442019-08-12 18:17:02 +0400135 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 Berezovskiy64824112019-07-26 15:55:24 +0400140 }
Denis Egorenkoafc43442019-08-12 18:17:02 +0400141 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 Berezovskiy64824112019-07-26 15:55:24 +0400164 }
Martin Polreichf7a1bb02018-12-05 11:12:23 +0100165 }
Ivan Berezovskiy64824112019-07-26 15:55:24 +0400166 stage('Verify restoration result') {
167 common.retry(verificationRetries, 15) {
Denis Egorenkoafc43442019-08-12 18:17:02 +0400168 def status = galera.verifyGaleraStatus(pepperEnv, false)
169 if (status.error >= 1) {
Ivan Berezovskiy64824112019-07-26 15:55:24 +0400170 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 Polreichf7889b52019-02-01 14:46:10 +0100175 }
Martin Polreich7bc654c2019-01-18 14:17:52 +0100176 }
177 }
Martin Polreichf7a1bb02018-12-05 11:12:23 +0100178 }
179}