1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# 4# Copyright 2015 Nandaja Varma <nvarma@redhat.com> 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 3 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU Library General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 19# USA. 20 21from __future__ import absolute_import, division, print_function 22__metaclass__ = type 23 24DOCUMENTATION = """ 25module: geo_rep 26short_description: Manage geo-replication sessions 27description: 28 - Create, stop, delete and configure geo-replication session 29author: Sachidananda Urs (@sac) 30options: 31 action: 32 description: 33 - Action to be performed on geo-replication session. 34 required: true 35 choices: ['create', 'start', 'stop', 'delete', 'pause', 'resume', 'config'] 36 type: str 37 mastervol: 38 description: 39 - Master volume name. 40 type: str 41 slavevol: 42 description: 43 - Slave volume name. 44 type: str 45 force: 46 description: 47 - force the system to perform the action. 48 type: str 49 georepuser: 50 description: 51 - Username to be used for the action being performed. 52 type: str 53 gluster_log_file: 54 description: 55 - The path to the geo-replication glusterfs log file. 56 type: str 57 gluster_log_level: 58 description: 59 - The log level for glusterfs processes. 60 type: str 61 log_file: 62 description: 63 - The path to the geo-replication log file. 64 type: str 65 log_level: 66 description: 67 - The log level for geo-replication. 68 type: str 69 changelog_log_level: 70 description: 71 - The log level for the changelog. 72 type: str 73 ssh_command: 74 description: 75 - The SSH command to connect to the remote machine. 76 type: str 77 rsync_command: 78 description: 79 - The command to use for setting synchronizing method for the files. 80 type: str 81 use_tarssh: 82 description: 83 - To use tar over ssh. 84 type: str 85 volume_id: 86 description: 87 - deletes the existing master UID for the intermediate/slave node. 88 type: str 89 timeout: 90 description: 91 - timeout period. 92 type: str 93 sync_jobs: 94 description: 95 - number of sync-jobs . 96 type: str 97 ignore_deletes: 98 description: 99 - file deletion on the master will not trigger a delete operation on the slave. 100 type: str 101 checkpoint: 102 description: 103 - Sets a checkpoint with the given option. 104 type: str 105 sync_acls: 106 description: 107 - Syncs acls to the Slave cluster. 108 type: str 109 sync_xattrs: 110 description: 111 - Syncs extended attributes to the Slave cluster. 112 type: str 113 log_rsync_performance: 114 description: 115 - for recording the rsync performance in log files. 116 type: str 117 rsync_options: 118 description: 119 - Additional options to rsync. 120 type: str 121 use_meta_volume: 122 description: 123 - to use meta volume in Geo-replication. 124 type: str 125 meta_volume_mnt: 126 description: 127 - The path of the meta volume mount point. 128 type: str 129""" 130 131EXAMPLES = """ 132- name: Create the geo-rep session 133 gluster.gluster.geo_rep: 134 action: create 135 mastervol: 10.70.42.122:mastervolume 136 slavevol: 10.70.43.48:slavevolume 137 force: true 138 georepuser: staff 139- name: Starts the geo-rep session 140 gluster.gluster.geo_rep: 141 action: start 142 mastervol: 10.70.42.122:mastervolume 143 slavevol: 10.70.43.48:slavevolume 144 force: true 145 georepuser: staff 146- name: Pause the geo-rep session 147 gluster.gluster.geo_rep: 148 action: pause 149 mastervol: 10.70.42.122:mastervolume 150 slavevol: 10.70.43.48:slavevolume 151 force: true 152 georepuser: staff 153- name: Resume the geo-rep session 154 gluster.gluster.geo_rep: 155 action: resume 156 mastervol: 10.70.42.122:mastervolume 157 slavevol: 10.70.43.48:slavevolume 158 force: true 159 georepuser: staff 160- name: Stop the geo-rep session 161 gluster.gluster.geo_rep: 162 action: stop 163 mastervol: 10.70.42.122:mastervolume 164 slavevol: 10.70.43.48:slavevolume 165 force: true 166 georepuser: staff 167- name: Configures the geo-rep session 168 gluster.gluster.geo_rep: 169 action: config 170 mastervol: 10.70.42.122:mastervolume 171 slavevol: 10.70.43.48:slavevolume 172 gluster_log_file: /var/log/glusterfs/geo-replication/gluster.log 173 gluster_log_level: INFO 174 log_file: /var/log/glusterfs/geo-replication/file.log 175 log_level: INFO 176 changelog_log_level: INFO 177 ssh_command: SSH 178 rsync_command: rsync 179 use_tarssh: true 180 volume_id: 6a071cfa-b150-4f0b-b1ed-96ab5d4bd671 181 timeout: 60 182 sync_jobs: 3 183 ignore_deletes: 1 184 checkpoint: now 185 sync_acls: true 186 sync_xattr: true 187 log_rsync_performance: true 188 rsync_options: --compress-level=0 189 use_meta_volume: true 190 meta_volume_mnt: /var/run/gluster/shared_storage/ 191- name: Delete the geo-rep session 192 gluster.gluster.geo_rep: 193 action: delete 194 mastervol: 10.70.42.122:mastervolume 195 slavevol: 10.70.43.48:slavevolume 196 georepuser: staff 197""" 198 199import re 200from ansible.module_utils.basic import AnsibleModule 201 202 203class GeoRep(object): 204 def __init__(self, module): 205 self.module = module 206 self.action = self._validated_params('action') 207 self.gluster_georep_ops() 208 209 def get_playbook_params(self, opt): 210 return self.module.params[opt] 211 212 def _validated_params(self, opt): 213 value = self.get_playbook_params(opt) 214 if value is None: 215 msg = "Please provide %s option in the playbook!" % opt 216 self.module.fail_json(msg=msg) 217 return value 218 219 def gluster_georep_ops(self): 220 mastervol = self._validated_params('mastervol') 221 slavevol = self._validated_params('slavevol') 222 slavevol = self.check_pool_exclusiveness(mastervol, slavevol) 223 if self.action in ['delete', 'config']: 224 force = '' 225 else: 226 force = self._validated_params('force') 227 force = 'force' if force == 'yes' else ' ' 228 options = 'no-verify' if self.action == 'create' \ 229 else self.config_georep() 230 if isinstance(options, list): 231 for opt in options: 232 rc, output, err = self.call_gluster_cmd('volume', 233 'geo-replication', 234 mastervol, slavevol, 235 self.action, opt, 236 force) 237 else: 238 rc, output, err = self.call_gluster_cmd('volume', 239 'geo-replication', 240 mastervol, slavevol, 241 self.action, options, 242 force) 243 self._get_output(rc, output, err) 244 if self.action in ['stop', 'delete'] and self.user == 'root': 245 self.user = 'geoaccount' 246 rc, output, err = self.call_gluster_cmd('volume', 'geo-replication', 247 mastervol, slavevol.replace( 248 'root', 'geoaccount'), 249 self.action, options, force) 250 self._get_output(rc, output, err) 251 252 def config_georep(self): 253 if self.action != 'config': 254 return '' 255 options = ['gluster_log_file', 'gluster_log_level', 'log_file', 256 'log_level', 'changelog_log_level', 'ssh_command', 257 'rsync_command', 'use_tarssh', 'volume_id', 'timeout', 258 'sync_jobs', 'ignore_deletes', 'checkpoint', 'sync_acls', 259 'sync_xattrs', 'log_rsync_performance', 'rsync_options', 260 'use_meta_volume', 'meta_volume_mnt'] 261 configs = [] 262 for opt in options: 263 value = self._validated_params(opt) 264 if value: 265 if value == 'reset': 266 configs.append("'!" + opt.replace('_', '-') + "'") 267 configs.append(opt.replace('_', '-') + ' ' + value) 268 if configs: 269 return configs 270 value = self._validated_params('config') 271 op = self._validated_params('op') 272 return value + ' ' + op 273 274 def check_pool_exclusiveness(self, mastervol, slavevol): 275 rc, output, err = self.module.run_command( 276 "gluster pool list") 277 peers_in_cluster = [line.split('\t')[1].strip() for 278 line in filter(None, output.split('\n')[1:])] 279 val_group = re.search("(.*):(.*)", slavevol) 280 if not val_group: 281 self.module.fail_json(msg="Slave volume in Unknown format. " 282 "Correct format: <hostname>:<volume name>") 283 if val_group.group(1) in peers_in_cluster: 284 self.module.fail_json(msg="slave volume is in the trusted " 285 "storage pool of master") 286 self.user = 'root' if self.module.params['georepuser'] is None \ 287 else self.module.params['georepuser'] 288 return self.user + '@' + val_group.group(1) + '::' + val_group.group(2) 289 290 def call_gluster_cmd(self, *args, **kwargs): 291 params = ' '.join(opt for opt in args) 292 key_value_pair = ' '.join(' %s %s ' % (key, value) 293 for key, value in kwargs) 294 return self._run_command('gluster', ' ' + params + ' ' + key_value_pair) 295 296 def _get_output(self, rc, output, err): 297 carryon = True if self.action in ['stop', 298 'delete', 'resume'] else False 299 changed = 0 if (carryon and rc) else 1 300 if self.action in ['stop', 'delete'] and ( 301 self.user == 'root' and changed == 0): 302 return 303 if not rc or carryon: 304 self.module.exit_json(stdout=output, changed=changed) 305 else: 306 self.module.fail_json(msg=err) 307 308 def _run_command(self, op, opts): 309 cmd = self.module.get_bin_path(op, True) + opts 310 return self.module.run_command(cmd) 311 312 313def main(): 314 module = AnsibleModule( 315 argument_spec=dict( 316 action=dict(required=True, choices=['create', 'start', 317 'stop', 'delete', 'pause', 'resume', 'config']), 318 mastervol=dict(), 319 slavevol=dict(), 320 force=dict(), 321 georepuser=dict(), 322 gluster_log_file=dict(), 323 gluster_log_level=dict(), 324 log_file=dict(), 325 log_level=dict(), 326 changelog_log_level=dict(), 327 ssh_command=dict(), 328 rsync_command=dict(), 329 use_tarssh=dict(), 330 volume_id=dict(), 331 timeout=dict(), 332 sync_jobs=dict(), 333 ignore_deletes=dict(), 334 checkpoint=dict(), 335 sync_acls=dict(), 336 sync_xattrs=dict(), 337 log_rsync_performance=dict(), 338 rsync_options=dict(), 339 use_meta_volume=dict(), 340 meta_volume_mnt=dict() 341 ), 342 ) 343 GeoRep(module) 344 345 346if __name__ == "__main__": 347 main() 348