Hello Ayal Baron, Bala.FA, Saggi Mizrahi, Dan Kenigsberg,
I'd like you to do a code review. Please visit
http://gerrit.ovirt.org/18303
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(a)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'}
--
To view, visit
http://gerrit.ovirt.org/18303
To unsubscribe, visit
http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9c43f0950bbaa215cfe22ba18cc02e5c5851c347
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Timothy Asir <tjeyasin(a)redhat.com>
Gerrit-Reviewer: Ayal Baron <abaron(a)redhat.com>
Gerrit-Reviewer: Bala.FA <barumuga(a)redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken(a)redhat.com>
Gerrit-Reviewer: Saggi Mizrahi <smizrahi(a)redhat.com>