mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
257 lines
8.7 KiB
Python
Executable File
257 lines
8.7 KiB
Python
Executable File
#!/usr/bin/python
|
|
# Copyright (C) 2006-2007 XenSource Ltd.
|
|
# Copyright (C) 2008-2009 Citrix Ltd.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published
|
|
# by the Free Software Foundation; version 2.1 only.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# FileSR: local-file storage repository
|
|
|
|
import SR, VDI, SRCommand, FileSR, util
|
|
import errno
|
|
import os, re, sys
|
|
import time
|
|
import xml.dom.minidom
|
|
import xs_errors
|
|
import nfs
|
|
import vhdutil
|
|
from lock import Lock
|
|
import cleanup
|
|
|
|
CAPABILITIES = ["SR_PROBE","SR_UPDATE", "SR_CACHING", \
|
|
"VDI_CREATE","VDI_DELETE","VDI_ATTACH","VDI_DETACH", \
|
|
"VDI_UPDATE", "VDI_CLONE","VDI_SNAPSHOT","VDI_RESIZE", \
|
|
"VDI_RESIZE_ONLINE", "VDI_RESET_ON_BOOT", "ATOMIC_PAUSE"]
|
|
|
|
CONFIGURATION = [ [ 'server', 'hostname or IP address of NFS server (required)' ], \
|
|
[ 'serverpath', 'path on remote server (required)' ] ]
|
|
|
|
|
|
DRIVER_INFO = {
|
|
'name': 'NFS VHD',
|
|
'description': 'SR plugin which stores disks as VHD files on a remote NFS filesystem',
|
|
'vendor': 'Citrix Systems Inc',
|
|
'copyright': '(C) 2008 Citrix Systems Inc',
|
|
'driver_version': '1.0',
|
|
'required_api_version': '1.0',
|
|
'capabilities': CAPABILITIES,
|
|
'configuration': CONFIGURATION
|
|
}
|
|
|
|
|
|
# The mountpoint for the directory when performing an sr_probe. All probes
|
|
# are guaranteed to be serialised by xapi, so this single mountpoint is fine.
|
|
PROBE_MOUNTPOINT = "probe"
|
|
NFSPORT = 2049
|
|
DEFAULT_TRANSPORT = "tcp"
|
|
|
|
|
|
class NFSSR(FileSR.FileSR):
|
|
"""NFS file-based storage repository"""
|
|
def handles(type):
|
|
return type == 'nfs'
|
|
handles = staticmethod(handles)
|
|
|
|
|
|
def load(self, sr_uuid):
|
|
self.ops_exclusive = FileSR.OPS_EXCLUSIVE
|
|
self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid)
|
|
self.sr_vditype = SR.DEFAULT_TAP
|
|
if not self.dconf.has_key('server'):
|
|
raise xs_errors.XenError('ConfigServerMissing')
|
|
self.remoteserver = self.dconf['server']
|
|
self.path = os.path.join(SR.MOUNT_BASE, sr_uuid)
|
|
|
|
# Test for the optional 'nfsoptions' dconf attribute
|
|
self.transport = DEFAULT_TRANSPORT
|
|
if self.dconf.has_key('useUDP') and self.dconf['useUDP'] == 'true':
|
|
self.transport = "udp"
|
|
|
|
|
|
def validate_remotepath(self, scan):
|
|
if not self.dconf.has_key('serverpath'):
|
|
if scan:
|
|
try:
|
|
self.scan_exports(self.dconf['server'])
|
|
except:
|
|
pass
|
|
raise xs_errors.XenError('ConfigServerPathMissing')
|
|
if not self._isvalidpathstring(self.dconf['serverpath']):
|
|
raise xs_errors.XenError('ConfigServerPathBad', \
|
|
opterr='serverpath is %s' % self.dconf['serverpath'])
|
|
|
|
def check_server(self):
|
|
try:
|
|
nfs.check_server_tcp(self.remoteserver)
|
|
except nfs.NfsException, exc:
|
|
raise xs_errors.XenError('NFSVersion',
|
|
opterr=exc.errstr)
|
|
|
|
|
|
def mount(self, mountpoint, remotepath):
|
|
try:
|
|
nfs.soft_mount(mountpoint, self.remoteserver, remotepath, self.transport)
|
|
except nfs.NfsException, exc:
|
|
raise xs_errors.XenError('NFSMount', opterr=exc.errstr)
|
|
|
|
|
|
def attach(self, sr_uuid):
|
|
self.validate_remotepath(False)
|
|
#self.remotepath = os.path.join(self.dconf['serverpath'], sr_uuid)
|
|
self.remotepath = self.dconf['serverpath']
|
|
util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget')
|
|
self.mount_remotepath(sr_uuid)
|
|
|
|
|
|
def mount_remotepath(self, sr_uuid):
|
|
if not self._checkmount():
|
|
self.check_server()
|
|
self.mount(self.path, self.remotepath)
|
|
|
|
return super(NFSSR, self).attach(sr_uuid)
|
|
|
|
|
|
def probe(self):
|
|
# Verify NFS target and port
|
|
util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget')
|
|
|
|
self.validate_remotepath(True)
|
|
self.check_server()
|
|
|
|
temppath = os.path.join(SR.MOUNT_BASE, PROBE_MOUNTPOINT)
|
|
|
|
self.mount(temppath, self.dconf['serverpath'])
|
|
try:
|
|
return nfs.scan_srlist(temppath)
|
|
finally:
|
|
try:
|
|
nfs.unmount(temppath, True)
|
|
except:
|
|
pass
|
|
|
|
|
|
def detach(self, sr_uuid):
|
|
"""Detach the SR: Unmounts and removes the mountpoint"""
|
|
if not self._checkmount():
|
|
return
|
|
util.SMlog("Aborting GC/coalesce")
|
|
cleanup.abort(self.uuid)
|
|
|
|
# Change directory to avoid unmount conflicts
|
|
os.chdir(SR.MOUNT_BASE)
|
|
|
|
try:
|
|
nfs.unmount(self.path, True)
|
|
except nfs.NfsException, exc:
|
|
raise xs_errors.XenError('NFSUnMount', opterr=exc.errstr)
|
|
|
|
return super(NFSSR, self).detach(sr_uuid)
|
|
|
|
|
|
def create(self, sr_uuid, size):
|
|
util._testHost(self.dconf['server'], NFSPORT, 'NFSTarget')
|
|
self.validate_remotepath(True)
|
|
if self._checkmount():
|
|
raise xs_errors.XenError('NFSAttached')
|
|
|
|
# Set the target path temporarily to the base dir
|
|
# so that we can create the target SR directory
|
|
self.remotepath = self.dconf['serverpath']
|
|
try:
|
|
self.mount_remotepath(sr_uuid)
|
|
except Exception, exn:
|
|
try:
|
|
os.rmdir(self.path)
|
|
except:
|
|
pass
|
|
raise exn
|
|
|
|
#newpath = os.path.join(self.path, sr_uuid)
|
|
#if util.ioretry(lambda: util.pathexists(newpath)):
|
|
# if len(util.ioretry(lambda: util.listdir(newpath))) != 0:
|
|
# self.detach(sr_uuid)
|
|
# raise xs_errors.XenError('SRExists')
|
|
#else:
|
|
# try:
|
|
# util.ioretry(lambda: util.makedirs(newpath))
|
|
# except util.CommandException, inst:
|
|
# if inst.code != errno.EEXIST:
|
|
# self.detach(sr_uuid)
|
|
# raise xs_errors.XenError('NFSCreate',
|
|
# opterr='remote directory creation error is %d'
|
|
# % inst.code)
|
|
self.detach(sr_uuid)
|
|
|
|
def delete(self, sr_uuid):
|
|
# try to remove/delete non VDI contents first
|
|
super(NFSSR, self).delete(sr_uuid)
|
|
try:
|
|
if self._checkmount():
|
|
self.detach(sr_uuid)
|
|
|
|
# Set the target path temporarily to the base dir
|
|
# so that we can remove the target SR directory
|
|
self.remotepath = self.dconf['serverpath']
|
|
self.mount_remotepath(sr_uuid)
|
|
newpath = os.path.join(self.path, sr_uuid)
|
|
|
|
if util.ioretry(lambda: util.pathexists(newpath)):
|
|
util.ioretry(lambda: os.rmdir(newpath))
|
|
self.detach(sr_uuid)
|
|
except util.CommandException, inst:
|
|
self.detach(sr_uuid)
|
|
if inst.code != errno.ENOENT:
|
|
raise xs_errors.XenError('NFSDelete')
|
|
|
|
def vdi(self, uuid, loadLocked = False):
|
|
if not loadLocked:
|
|
return NFSFileVDI(self, uuid)
|
|
return NFSFileVDI(self, uuid)
|
|
|
|
def _checkmount(self):
|
|
return util.ioretry(lambda: util.pathexists(self.path)) \
|
|
and util.ioretry(lambda: util.ismount(self.path))
|
|
|
|
def scan_exports(self, target):
|
|
util.SMlog("scanning2 (target=%s)" % target)
|
|
dom = nfs.scan_exports(target)
|
|
print >>sys.stderr,dom.toprettyxml()
|
|
|
|
class NFSFileVDI(FileSR.FileVDI):
|
|
def attach(self, sr_uuid, vdi_uuid):
|
|
try:
|
|
vdi_ref = self.sr.srcmd.params['vdi_ref']
|
|
self.session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, \
|
|
"vdi-type")
|
|
self.session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, \
|
|
"storage-type")
|
|
self.session.xenapi.VDI.add_to_xenstore_data(vdi_ref, \
|
|
"storage-type", "nfs")
|
|
except:
|
|
util.logException("NFSSR:attach")
|
|
pass
|
|
|
|
return super(NFSFileVDI, self).attach(sr_uuid, vdi_uuid)
|
|
|
|
def clone(self, sr_uuid, vdi_uuid):
|
|
timestamp_before = int(util.get_mtime(self.sr.path))
|
|
ret = super(NFSFileVDI, self).clone(sr_uuid, vdi_uuid)
|
|
timestamp_after = int(util.get_mtime(self.sr.path))
|
|
if timestamp_after == timestamp_before:
|
|
util.SMlog("SR dir timestamp didn't change, updating")
|
|
timestamp_after += 1
|
|
os.utime(self.sr.path, (timestamp_after, timestamp_after))
|
|
return ret
|
|
|
|
|
|
if __name__ == '__main__':
|
|
SRCommand.run(NFSSR, DRIVER_INFO)
|
|
else:
|
|
SR.registerSR(NFSSR)
|