diff --git a/drivers/NFSSR.py b/drivers/NFSSR.py index dbfae8080..9649b76e9 100755 --- a/drivers/NFSSR.py +++ b/drivers/NFSSR.py @@ -112,12 +112,12 @@ def validate_remotepath(self, scan): def check_server(self): try: - if not nfs.is_nfs_4(self.nfsversion) and PROBEVERSION in self.dconf: - sv = nfs.get_supported_nfs_versions(self.remoteserver) + if PROBEVERSION in self.dconf: + sv = nfs.get_supported_nfs_versions(self.remoteserver, self.transport) if len(sv): self.nfsversion = sv[0] else: - nfs.check_server_tcp(self.remoteserver, self.nfsversion) + nfs.check_server_tcp(self.remoteserver, self.transport, self.nfsversion) except nfs.NfsException as exc: raise xs_errors.XenError('NFSVersion', opterr=exc.errstr) @@ -250,7 +250,7 @@ def vdi(self, uuid, loadLocked=False): def scan_exports(self, target): util.SMlog("scanning2 (target=%s)" % target) - dom = nfs.scan_exports(target) + dom = nfs.scan_exports(target, self.transport) print(dom.toprettyxml(), file=sys.stderr) def set_transport(self): diff --git a/drivers/nfs.py b/drivers/nfs.py index 46ebb9a9f..0cac557fa 100644 --- a/drivers/nfs.py +++ b/drivers/nfs.py @@ -41,6 +41,8 @@ RPCINFO_BIN = "/usr/sbin/rpcinfo" SHOWMOUNT_BIN = "/usr/sbin/showmount" +NFS_STAT = "/usr/sbin/nfsstat" +LS = "/usr/bin/ls" DEFAULT_NFSVERSION = '3' @@ -50,41 +52,38 @@ NFS_SERVICE_WAIT = 30 NFS_SERVICE_RETRY = 6 +NSFv4_PSEUDOFS = "/" +NFS4_TMP_MOUNTPOINT = "/tmp/mnt" class NfsException(Exception): def __init__(self, errstr): self.errstr = errstr -def is_nfs_4(nfsversion): - return validate_nfsversion(nfsversion) and nfsversion != DEFAULT_NFSVERSION - -def check_server_tcp(server, nfsversion=DEFAULT_NFSVERSION): +def check_server_tcp(server, transport, nfsversion=DEFAULT_NFSVERSION): """Make sure that NFS over TCP/IP V3 is supported on the server. Returns True if everything is OK False otherwise. """ - if is_nfs_4(nfsversion): - return True - try: - sv = get_supported_nfs_versions(server) + sv = get_supported_nfs_versions(server, transport) return (True if nfsversion in sv else False) except util.CommandException as inst: raise NfsException("rpcinfo failed or timed out: return code %d" % inst.code) -def check_server_service(server, nfsversion=DEFAULT_NFSVERSION): +def check_server_service(server, transport): """Ensure NFS service is up and available on the remote server. Returns False if fails to detect service after NFS_SERVICE_RETRY * NFS_SERVICE_WAIT """ - if is_nfs_4(nfsversion): + sv = get_supported_nfs_versions(server, transport) + if sv == ['4']: return True retries = 0 @@ -141,7 +140,7 @@ def soft_mount(mountpoint, remoteserver, remotepath, transport, useroptions='', # Wait for NFS service to be available try: - if not check_server_service(remoteserver, nfsversion): + if not check_server_service(remoteserver, transport): raise util.CommandException( code=errno.EOPNOTSUPP, reason='No NFS service on server: `%s`' % remoteserver @@ -195,38 +194,74 @@ def unmount(mountpoint, rmmountpoint): raise NfsException("rmdir failed with error '%s'" % inst.strerror) -def scan_exports(target): +def scan_exports(target, transport): """Scan target and return an XML DOM with target, path and accesslist.""" util.SMlog("scanning") - cmd = [SHOWMOUNT_BIN, "--no-headers", "-e", target] dom = xml.dom.minidom.Document() element = dom.createElement("nfs-exports") dom.appendChild(element) - for val in util.pread2(cmd).split('\n'): - if not len(val): - continue - entry = dom.createElement('Export') - element.appendChild(entry) - - subentry = dom.createElement("Target") - entry.appendChild(subentry) - textnode = dom.createTextNode(target) - subentry.appendChild(textnode) - - # Access is not always provided by showmount return - # If none is provided we need to assume "*" - array = val.split() - path = array[0] - access = array[1] if len(array) >= 2 else "*" - subentry = dom.createElement("Path") - entry.appendChild(subentry) - textnode = dom.createTextNode(path) - subentry.appendChild(textnode) + fail = True + try: + cmd = [SHOWMOUNT_BIN, "--no-headers", "-e", target] + for val in util.pread2(cmd).split('\n'): + if not len(val): + continue + entry = dom.createElement('Export') + element.appendChild(entry) + + subentry = dom.createElement("Target") + entry.appendChild(subentry) + textnode = dom.createTextNode(target) + subentry.appendChild(textnode) + + # Access is not always provided by showmount return + # If none is provided we need to assume "*" + array = val.split() + path = array[0] + access = array[1] if len(array) >= 2 else "*" + subentry = dom.createElement("Path") + entry.appendChild(subentry) + textnode = dom.createTextNode(path) + subentry.appendChild(textnode) + + subentry = dom.createElement("Accesslist") + entry.appendChild(subentry) + textnode = dom.createTextNode(access) + subentry.appendChild(textnode) + fail = False + except Exception: + util.SMlog("Unable to scan exports with rpcingo, trying NFSv4") - subentry = dom.createElement("Accesslist") - entry.appendChild(subentry) - textnode = dom.createTextNode(access) - subentry.appendChild(textnode) + try: + mountpoint = "%s/%s" % NFS4_TMP_MOUNTPOINT, target + soft_mount(mountpoint, target, NSFv4_PSEUDOFS, transport, nfsversion='4') + paths = util.pread2([LS]) + unmount(mountpoint, NSFv4_PSEUDOFS) + for path in paths.split("\n"): + entry = dom.createElement('Export') + element.appendChild(entry) + + subentry = dom.createElement("Target") + entry.appendChild(subentry) + textnode = dom.createTextNode(target) + subentry.appendChild(textnode) + subentry = dom.createElement("Path") + entry.appendChild(subentry) + textnode = dom.createTextNode(path) + subentry.appendChild(textnode) + + subentry = dom.createElement("Accesslist") + entry.appendChild(subentry) + # Assume everyone as we do not have any info about it + textnode = dom.createTextNode("*") + subentry.appendChild(textnode) + fail = False + except Exception: + util.SMlog("Unable to scan exports with NFSv4 pseudo FS mount") + + if fail: + raise NfsException('Failed to read NFS export paths from server %s' % + (target)) return dom @@ -270,10 +305,11 @@ def scan_srlist(path, dconf): return dom.toprettyxml() -def get_supported_nfs_versions(server): +def get_supported_nfs_versions(server, transport): """Return list of supported nfs versions.""" valid_versions = set(['3', '4']) cv = set() + res = None try: ns = util.pread2([RPCINFO_BIN, "-s", "%s" % server]) ns = ns.split("\n") @@ -282,11 +318,26 @@ def get_supported_nfs_versions(server): cvi = ns[i].split()[1].split(",") for j in range(len(cvi)): cv.add(cvi[j]) - return sorted(cv & valid_versions) - except: - util.SMlog("Unable to obtain list of valid nfs versions") + res = sorted(cv & valid_versions) + except Exception: + util.SMlog("Unable to obtain list of valid nfs versions with rpcinfo, trying NSFv4") + + try: + mountpoint = "%s/%s" % NFS4_TMP_MOUNTPOINT, server + soft_mount(mountpoint, server, NSFv4_PSEUDOFS, transport, nfsversion='4') + util.pread2([NFS_STAT, '-m']) + unmount(mountpoint, NSFv4_PSEUDOFS) + if res is None: + res = ['4'] + else: + res.add('4') + except Exception: + util.SMlog("Unable to obtain list of valid nfs versions with NSFv4 pseudo FS mount") + + if res is None: raise NfsException('Failed to read supported NFS version from server %s' % (server)) + return res def get_nfs_timeout(other_config):