Hello Ayal Baron, Bala.FA, Saggi Mizrahi, Dan Kenigsberg,
I'd like you to do a code review. Please visit
to review the following change.
Change subject: gluster: Add gluster volume geo-replication configuration feature ......................................................................
gluster: Add gluster volume geo-replication configuration feature
Configure geo-replication options between the hosts specified by MASTER and SLAVE. This provides listing, add, remove & update geo-rep configurations.
New verbs: * glusterVolumeGeoRepConfigList - return value structure: {{NAME: VALUE, writable: BOOL, 'description': DESCRIPTION}... } * glusterVolumeGeoRepConfigGet - return value structure: {NAME: VALUE, writable: BOOL, 'description': DESCRIPTION} * glusterVolumeGeoRepConfigSet * glusterVolumeGeoRepConfigDelete
Change-Id: I9c43f0950bbaa215cfe22ba18cc02e5c5851c347 Signed-off-by: Timothy Asir tjeyasin@redhat.com --- M client/vdsClientGluster.py M vdsm/gluster/api.py M vdsm/gluster/cli.py M vdsm/gluster/exception.py M vdsm/gluster/vdsmapi-gluster-schema.json 5 files changed, 471 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/03/18303/1
diff --git a/client/vdsClientGluster.py b/client/vdsClientGluster.py index 0e8ebd1..8258c26 100644 --- a/client/vdsClientGluster.py +++ b/client/vdsClientGluster.py @@ -453,6 +453,70 @@ pp.pprint(status) return status['status']['code'], status['status']['message']
+ def do_glusterVolumeGeoRepConfigList(self, args): + params = self._eqSplit(args) + masterVolName = params.get('masterVolName', '') + slaveHost = params.get('slaveHost', '') + slaveVolName = params.get('slaveVolName', '') + if not(masterVolName and slaveHost and slaveVolName): + raise ValueError + + status = self.s.glusterVolumeGeoRepConfigList(masterVolName, + slaveHost, + slaveVolName) + pp.pprint(status) + return status['status']['code'], status['status']['message'] + + def do_glusterVolumeGeoRepConfigSet(self, args): + params = self._eqSplit(args) + masterVolName = params.get('masterVolName', '') + slaveHost = params.get('slaveHost', '') + slaveVolName = params.get('slaveVolName', '') + key = params.get('key', '') + value = params.get('value', '') + if not(masterVolName and slaveHost and slaveVolName and key and value): + raise ValueError + + status = self.s.glusterVolumeGeoRepConfigSet(masterVolName, + slaveHost, + slaveVolName, + key, + value) + pp.pprint(status) + return status['status']['code'], status['status']['message'] + + def do_glusterVolumeGeoRepConfigGet(self, args): + params = self._eqSplit(args) + masterVolName = params.get('masterVolName', '') + slaveHost = params.get('slaveHost', '') + slaveVolName = params.get('slaveVolName', '') + key = params.get('key', '') + if not(masterVolName and slaveHost and slaveVolName and key): + raise ValueError + + status = self.s.glusterVolumeGeoRepConfigGet(masterVolName, + slaveHost, + slaveVolName, + key) + pp.pprint(status) + return status['status']['code'], status['status']['message'] + + def do_glusterVolumeGeoRepConfigDelete(self, args): + params = self._eqSplit(args) + masterVolName = params.get('masterVolName', '') + slaveHost = params.get('slaveHost', '') + slaveVolName = params.get('slaveVolName', '') + key = params.get('key', '') + if not(masterVolName and slaveHost and slaveVolName and key): + raise ValueError + + status = self.s.glusterVolumeGeoRepConfigDelete(masterVolName, + slaveHost, + slaveVolName, + key) + pp.pprint(status) + return status['status']['code'], status['status']['message'] +
def getGlusterCmdDict(serv): return \ @@ -754,5 +818,56 @@ '<slave_host_name>is remote slave host name or ip\n\t' '<slave_volume_name>existing volume name in the slave node', 'Delete the geo-replication session' + )), + 'glusterVolumeGeoRepConfigList': ( + serv.do_glusterVolumeGeoRepConfigList, + ('masterVolName=<master_volume_name> slaveHost=<slave_host> ' + 'slaveVolName=<slave_volume_name>\n\t' + '<master_volume_name> is an existing volume name in ' + 'the master node\n\t' + '<slave_host> is slave host name\n\t' + '<slave_volume_name> is an existing volume name ' + 'in the slave node', + 'list volume geo-replication configurations' + )), + 'glusterVolumeGeoRepConfigSet': ( + serv.do_glusterVolumeGeoRepConfigSet, + ('masterVolName=<master_volume_name> slaveHost=<slave_host> ' + 'slaveVolName=<slave_volume_name> keyName=<key> ' + 'value=<value>\n\t' + '<master_volume_name> is an existing volume name ' + 'in the master node\n\t' + '<slave_host> is slave host name\n\t' + '<slave_volume_name> is an existing volume name in ' + 'the slave node\n\t' + '<key> is the key name\n\t' + '<value> is the key value', + 'set volume geo-replication configuration' + )), + 'glusterVolumeGeoRepConfigGet': ( + serv.do_glusterVolumeGeoRepConfigGet, + ('masterVolName=<master_volume_name> slaveHost=<slave_host> ' + 'slaveVolName=<slave_volume_name> keyName=<key> ' + 'value=<value>\n\t' + '<master_volume_name> is an existing volume name in ' + 'the master node\n\t' + '<slave_host> is slave host name\n\t' + '<slave_volume_name> is an existing volume name in ' + 'the slave node\n\t' + '<key> is the key name', + 'get volume geo-replication configuration' + )), + 'glusterVolumeGeoRepConfigDelete': ( + serv.do_glusterVolumeGeoRepConfigDelete, + ('masterVolName=<master_volume_name> slaveHost=<slave_host> ' + 'slaveVolName=<slave_volume_name> keyName=<key> ' + 'value=<value>\n\t' + '<master_volume_name> is an existing volume name in ' + 'the master node\n\t' + '<slave_host> is slave host name\n\t' + '<slave_volume_name> is an existing volume name in ' + 'the slave node\n\t' + '<key> is the key name', + 'Delete volume geo-replication configuration' )) } diff --git a/vdsm/gluster/api.py b/vdsm/gluster/api.py index d65a8a2..6a58ece 100644 --- a/vdsm/gluster/api.py +++ b/vdsm/gluster/api.py @@ -304,6 +304,47 @@ slaveVolName) return {'geo-rep': status}
+ @exportAsVerb + def volumeGeoRepConfigList(self, masterVolName, slaveHost, slaveVolName, + options=None): + status = self.svdsmProxy.glusterVolumeGeoRepConfigList(masterVolName, + slaveHost, + slaveVolName) + return {'geoRepConfig': status} + + @exportAsVerb + def volumeGeoRepConfigSet(self, masterVolName, slaveHost, slaveVolName, + key, value, options=None): + status = self.svdsmProxy.glusterVolumeGeoRepConfigSet(masterVolName, + slaveHost, + slaveVolName, + key, + value) + return {'geoRepSet': status} + + @exportAsVerb + def volumeGeoRepConfigGet(self, masterVolName, slaveHost, slaveVolName, + key, options=None): + + fp = open("/tmp/file1.txt", "w") + fp.write("entering geo rep config get") + fp.close() + status = self.svdsmProxy.glusterVolumeGeoRepConfigGet(masterVolName, + slaveHost, + slaveVolName, + key) + return {'geoRepGet': status} + + @exportAsVerb + def volumeGeoRepConfigDelete(self, masterVolName, slaveHost, slaveVolName, + key, options=None): + + status = self.svdsmPorxy.glusterVolumeGeoRepConfigDelete(masterVolName, + slaveHost, + slaveVolName, + key) + return {'geoRepDelete': status} +
def getGlusterMethods(gluster): l = [] diff --git a/vdsm/gluster/cli.py b/vdsm/gluster/cli.py index 4529a44..d16d88f 100644 --- a/vdsm/gluster/cli.py +++ b/vdsm/gluster/cli.py @@ -73,6 +73,95 @@ RDMA = 'RDMA'
+class GeoRepConf: + EDITABLE = 'True' + READONLY = 'False' + + +GEOREP_CONFIG = { + 'gluster_log_file': ( + 'glusterLogFile', + 'Path to the geo-replication glusterfs log file', + GeoRepConf.EDITABLE), + 'gluster_log_level': ( + 'glusterLogLevel', + 'The log level for glusterfs processes', + GeoRepConf.EDITABLE), + 'log_file': ( + 'logFile', + 'The path to the geo-replication log file', + GeoRepConf.EDITABLE), + 'ssh_command': ( + 'sshCommand', + 'Command to connect to the remote machine (the default is ssh)', + GeoRepConf.EDITABLE), + 'rsync_command': ( + 'rsyncCommand', + 'rsync command for syncronizing (default - rsync)', + GeoRepConf.EDITABLE), + 'volume_id': ( + 'volumeID', + 'Volume ID which can be use to delete the existing master UID ' + 'for the intermediate/slave node', + GeoRepConf.EDITABLE), + 'timeout': ( + 'timeout', + 'The timeout period', GeoRepConf.EDITABLE), + 'sync_jobs': ( + 'syncJobs', + 'The number of simultaneous files/directories ' + 'that can be synchronized', + GeoRepConf.EDITABLE), + 'ignore_deletes': ( + 'ignoreDeletes', + 'delete files only in master and will not trigger a delete ' + 'operation in the slave. Hence, the slave will remain as a ' + 'superset of the master and can be used to recover the master ' + 'in case of any crash or accidental delete', + GeoRepConf.EDITABLE), + 'checkpoint': ( + 'checkPoint', + 'Sets the checkpoint with the given value (label). If the value is ' + 'set as now, then the current time will be used as value (label)', + GeoRepConf.EDITABLE), + 'gluster_command_dir': ( + 'glusterCommandDir', + 'gluster command path', + GeoRepConf.READONLY), + 'gluster_params': ( + 'glusterParams', + 'gluster parameters', + GeoRepConf.READONLY), + 'pid_file': ( + 'pidFile', + 'geo replication session pid file', + GeoRepConf.READONLY), + 'session_owner': ( + 'sessionOwner', + 'session owner uuid', + GeoRepConf.READONLY), + 'socketdir': ( + 'socketDir', + 'socket directory', + GeoRepConf.READONLY), + 'special_sync_mode': ( + 'specialSyncMode', + 'special sync mode', + GeoRepConf.READONLY), + 'state_detail_file': ( + 'stateDetailFile', + 'state detail file', + GeoRepConf.READONLY), + 'state_file': ( + 'stateFile', + 'state file', + GeoRepConf.READONLY), + 'working_dir': ( + 'workingDir', + 'working directory', + GeoRepConf.READONLY)} + + def _execGluster(cmd): return utils.execCmd(cmd)
@@ -938,3 +1027,89 @@ return True except ge.GlusterCmdFailedException as e: raise ge.GlusterGeoRepDeletionFailedException(rc=e.rc, err=e.err) + + +def _parseVolumeGeoRepConfigListXml(tree): + config = {} + + for el in tree.findall('geoRepConfig'): + try: + key = el.find('name').text + config[GEOREP_CONFIG[key]] = { + 'value': el.find('value').text, + 'description': GEOREP_CONFIG[key][1], + 'editable': GEOREP_CONFIG[key][2]} + except KeyError: + pass # omitting unwanted items + return config + + +def _parseVolumeGeoRepConfigList(out): + config = {} + + for line in out: + k, v = line.split(':') + try: + config[GEOREP_CONFIG[k][0]] = {'value': v.strip(), + 'description': GEOREP_CONFIG[k][1], + 'editable': GEOREP_CONFIG[k][2]} + except KeyError: + pass # omitting unwanted items + return config + + +@makePublic +def volumeGeoRepConfigList(masterVolName, slaveHost, slaveVolName): + """Returns + {{OPTIONNAME: VALUE, + 'writable': BOOL, + 'description': DESCRIPTION}... } + """ + command = _getGlusterVolCmd() + ["geo-replication", masterVolName, + "%s::%s" % (slaveHost, slaveVolName), + "config"] + + try: + xmltree = _execGlusterXml(command) + return _parseVolumeGeoRepConfigListXml(xmltree) + except ge.GlusterCmdFailedException as e: + rc, out, err = _execGluster(command) + if rc: + raise ge.GlusterGeoRepConfigListFailedException(rc, out, err) + return _parseVolumeGeoRepConfigList(out) + + +@makePublic +def volumeGeoRepConfigSet(masterVolName, slaveHost, slaveVolName, key, value): + command = _getGlusterVolCmd() + ["geo-replication", masterVolName, + "%s::%s" % (slaveHost, slaveVolName), + "config", key, value] + rc, out, err = _execGluster(command) + if rc: + raise ge.GlusterGeoRepConfigSetFailedException(rc, out, err) + return True + + +@makePublic +def volumeGeoRepConfigGet(masterVolName, slaveHost, slaveVolName, key): + command = _getGlusterVolCmd() + ["geo-replication", masterVolName, + "%s::%s" % (slaveHost, slaveVolName), + "config", key] + rc, out, err = _execGluster(command) + if rc: + raise ge.GlusterGeoRepConfigGetFailedException(rc, out, err) + + return {key: {'name': out.strip(), + 'description': GEOREP_CONFIG[key][1], + 'editable': GEOREP_CONFIG[key][2]}} + + +@makePublic +def volumeGeoRepConfigDelete(masterVolName, slaveHost, slaveVolName, key): + command = _getGlusterVolCmd() + ["geo-replication", masterVolName, + "%s::%s" % (slaveHost, slaveVolName), + "config", "!%s" % key] + rc, out, err = _execGluster(command) + if rc: + raise ge.GlusterGeoRepConfigDeleteFailedException(rc, out, err) + return True diff --git a/vdsm/gluster/exception.py b/vdsm/gluster/exception.py index 0e98c06..846a70e 100644 --- a/vdsm/gluster/exception.py +++ b/vdsm/gluster/exception.py @@ -511,3 +511,23 @@ class GlusterGeoRepDeletionFailedException(GlusterGeoRepException): code = 4564 message = "Geo Rep session deletion failed" + + +class GlusterGeoRepConfigListFailedException(GlusterVolumeException): + code = 4165 + message = "Get volume geo-replication config list failed" + + +class GlusterGeoRepConfigSetFailedException(GlusterVolumeException): + code = 4166 + message = "Set volume geo-replication config failed" + + +class GlusterGeoRepConfigGetFailedException(GlusterVolumeException): + code = 4167 + message = "Get volume geo-replication config failed" + + +class GlusterGeoRepConfigDeleteFailedException(GlusterVolumeException): + code = 4168 + message = "Delete volume geo-replication config failed" diff --git a/vdsm/gluster/vdsmapi-gluster-schema.json b/vdsm/gluster/vdsmapi-gluster-schema.json index e4dad64..da4177e 100644 --- a/vdsm/gluster/vdsmapi-gluster-schema.json +++ b/vdsm/gluster/vdsmapi-gluster-schema.json @@ -414,3 +414,123 @@ {'command': {'class': 'GlusterGeoRep', 'name': 'delete'}, 'data': {'masterVolName': 'str', 'slaveHost': 'str', 'slaveVolName': 'str'}, 'returns': 'bool'} + +## +# @GeoRepConfig: +# +# Geo replication config details. +# +# @optionname: Config option name +# +# @editable: Editable option or not +# +# @description: Option details +# +# Since: 4.10.3 +## +{'type': 'GeoRepConfig', + 'data': {'optionname': 'str', 'editable': 'bool', 'description': 'str'}} + +## +# @GlusterGeoRep.configList: +# +# List Geo Replication configuration +# +# @mastervolname: is an existing volume name in the master node +# +# @slavehost: is remote slave host name or ip +# +# @slavevolname: is an available existing volume name in the slave node +# +# Returns: +# List of geo replication configurations +# +# Since: 4.10.3 +## +{'command': {'class': 'GlusterGeoRep', 'name': 'geoRepConfigList'}, + 'data': {'mastervolname': 'str', 'slavehost': 'str', 'slavevolname': 'str'}, + 'returns': 'GeoRepConfig'} + +## +# @GlusterGeoRep.configSet: +# +# Set Geo Replication config option +# +# @mastervolname: is an existing volume name in the master node +# +# @slavehost: is remote slave host name or ip +# +# @slavevolname: is an available existing volume name in the slave node +# +# @key: valid configuration option name +# +# @value: value to the option +# +# Returns: +# True if it sets value to the option successfully +# +# Since: 4.10.3 +## +{'command': {'class': 'GlusterGeoRep', 'name': 'geoRepConfigSet'}, + 'data': {'mastervolname': 'str', 'slavehost': 'str', 'slavevolname': 'str', 'key': 'str', 'value': 'str'}, + 'returns': 'bool'} + +## +# @GeoRepConfig: +# +# Geo replication config details. +# +# @optionname: Config option name +# +# @editable: Editable option or not +# +# @description: Option details +# +# Since: 4.10.3 +## +{'type': 'GeoRepConfig', + 'data': {'optionname': 'str', 'editable': 'bool', 'description': 'str'}} + +## +# @GlusterVolume.geoRepConfigGet: +# +# Get value of the Geo Replication config option +# +# @mastervolname: is an existing volume name in the master node +# +# @slavehost: is remote slave host name or ip +# +# @slavevolname: is an available existing volume name in the slave node +# +# @key: valid configuration option name +# +# Returns: +# The value of the Geo Replication config option +# +# Since: 4.10.3 +## +{'command': {'class': 'GlusterGeoRep', 'name': 'geoRepConfigGet'}, + 'data': {'mastervolname': 'str', 'slavehost': 'str', 'slavevolname': 'str', 'key': 'str'}, + 'returns': 'GeoRepConfig'} + +## +# @GlusterVolume.geoRepConfigDelete: +# +# Delete Geo Replication config option +# +# @mastervolname: is an existing volume name in the master node +# +# @slavehost: is remote slave host name or ip +# +# @slavevolname: is an available existing volume name in the slave node +# +# @key: valid configuration option name +# +# Returns: +# True if it deletes the option successfully +# +# Since: 4.10.3 +## +{'command': {'class': 'GlusterGeoRep', 'name': 'geoRepConfigDelete'}, + 'data': {'mastervolname': 'str', 'slavehost': 'str', 'slavevolname': 'str', 'key': 'str'}, + 'returns': 'bool'}