mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			261 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/python
 | 
						|
# Licensed to the Apache Software Foundation (ASF) under one
 | 
						|
# or more contributor license agreements.  See the NOTICE file
 | 
						|
# distributed with this work for additional information
 | 
						|
# regarding copyright ownership.  The ASF licenses this file
 | 
						|
# to you under the Apache License, Version 2.0 (the
 | 
						|
# "License"); you may not use this file except in compliance
 | 
						|
# with the License.  You may obtain a copy of the License at
 | 
						|
# 
 | 
						|
#   http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
# 
 | 
						|
# Unless required by applicable law or agreed to in writing,
 | 
						|
# software distributed under the License is distributed on an
 | 
						|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | 
						|
# KIND, either express or implied.  See the License for the
 | 
						|
# specific language governing permissions and limitations
 | 
						|
# under the License.
 | 
						|
 | 
						|
# 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': 'The Apache Software Foundation',
 | 
						|
    'copyright': 'Copyright (c) 2012 The Apache Software Foundation',
 | 
						|
    '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
 | 
						|
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)
 |