Coverage for drivers/XFSSR.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/python3
2#
3# Original work copyright (C) Citrix Systems Inc.
4# Modified work copyright (C) Vates SAS and XCP-ng community
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU Lesser General Public License as published
8# by the Free Software Foundation; version 2.1 only.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18#
19# XFSSR: Based on local-file storage repository, mounts xfs partition
21import SR
22from SR import deviceCheck
23import SRCommand
24import FileSR
25import util
26import lvutil
27import scsiutil
29import os
30import xs_errors
31import vhdutil
32from lock import Lock
33from constants import EXT_PREFIX
35CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_SUPPORTS_LOCAL_CACHING", \
36 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", \
37 "VDI_UPDATE", "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "VDI_MIRROR", \
38 "VDI_GENERATE_CONFIG", \
39 "VDI_RESET_ON_BOOT/2", "ATOMIC_PAUSE", "VDI_CONFIG_CBT",
40 "VDI_ACTIVATE", "VDI_DEACTIVATE", "THIN_PROVISIONING", "VDI_READ_CACHING"]
42CONFIGURATION = [['device', 'local device path (required) (e.g. /dev/sda3)']]
44DRIVER_INFO = {
45 'name': 'Local XFS VHD',
46 'description': 'SR plugin which represents disks as VHD files stored on a local XFS filesystem, created inside an LVM volume',
47 'vendor': 'Vates SAS',
48 'copyright': '(C) 2019 Vates SAS',
49 'driver_version': '1.0',
50 'required_api_version': '1.0',
51 'capabilities': CAPABILITIES,
52 'configuration': CONFIGURATION
53 }
55DRIVER_CONFIG = {"ATTACH_FROM_CONFIG_WITH_TAPDISK": True}
58class XFSSR(FileSR.FileSR):
59 """XFS Local file storage repository"""
61 DRIVER_TYPE = 'xfs'
63 def handles(srtype):
64 return srtype == XFSSR.DRIVER_TYPE
65 handles = staticmethod(handles)
67 def load(self, sr_uuid):
68 if not self._is_xfs_available():
69 raise xs_errors.XenError(
70 'SRUnavailable',
71 opterr='xfsprogs is not installed'
72 )
74 self.ops_exclusive = FileSR.OPS_EXCLUSIVE
75 self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid)
76 self.sr_vditype = SR.DEFAULT_TAP
78 self.path = os.path.join(SR.MOUNT_BASE, sr_uuid)
79 self.vgname = EXT_PREFIX + sr_uuid
80 self.remotepath = os.path.join("/dev", self.vgname, sr_uuid)
81 self.attached = self._checkmount()
82 self.driver_config = DRIVER_CONFIG
84 def delete(self, sr_uuid):
85 super(XFSSR, self).delete(sr_uuid)
87 # Check PVs match VG
88 try:
89 for dev in self.dconf['device'].split(','):
90 cmd = ["pvs", dev]
91 txt = util.pread2(cmd)
92 if txt.find(self.vgname) == -1:
93 raise xs_errors.XenError('VolNotFound', \
94 opterr='volume is %s' % self.vgname)
95 except util.CommandException as inst:
96 raise xs_errors.XenError('PVSfailed', \
97 opterr='error is %d' % inst.code)
99 # Remove LV, VG and pv
100 try:
101 cmd = ["lvremove", "-f", self.remotepath]
102 util.pread2(cmd)
104 cmd = ["vgremove", self.vgname]
105 util.pread2(cmd)
107 for dev in self.dconf['device'].split(','):
108 cmd = ["pvremove", dev]
109 util.pread2(cmd)
110 except util.CommandException as inst:
111 raise xs_errors.XenError('LVMDelete', \
112 opterr='errno is %d' % inst.code)
114 def attach(self, sr_uuid):
115 if not self._checkmount():
116 try:
117 #Activate LV
118 cmd = ['lvchange', '-ay', self.remotepath]
119 util.pread2(cmd)
121 # make a mountpoint:
122 if not os.path.isdir(self.path):
123 os.makedirs(self.path)
124 except util.CommandException as inst:
125 raise xs_errors.XenError('LVMMount', \
126 opterr='Unable to activate LV. Errno is %d' % inst.code)
128 try:
129 util.pread(["fsck", "-a", self.remotepath])
130 except util.CommandException as inst:
131 if inst.code == 1:
132 util.SMlog("FSCK detected and corrected FS errors. Not fatal.")
133 else:
134 raise xs_errors.XenError('LVMMount', \
135 opterr='FSCK failed on %s. Errno is %d' % (self.remotepath, inst.code))
137 try:
138 util.pread(["mount", self.remotepath, self.path])
139 except util.CommandException as inst:
140 raise xs_errors.XenError('LVMMount', \
141 opterr='Failed to mount FS. Errno is %d' % inst.code)
143 self.attached = True
145 #Update SCSIid string
146 scsiutil.add_serial_record(self.session, self.sr_ref, \
147 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
149 # Set the block scheduler
150 for dev in self.dconf['device'].split(','):
151 self.block_setscheduler(dev)
153 def detach(self, sr_uuid):
154 super(XFSSR, self).detach(sr_uuid)
155 try:
156 # deactivate SR
157 cmd = ["lvchange", "-an", self.remotepath]
158 util.pread2(cmd)
159 except util.CommandException as inst:
160 raise xs_errors.XenError('LVMUnMount', \
161 opterr='lvm -an failed errno is %d' % inst.code)
163 @deviceCheck
164 def probe(self):
165 return lvutil.srlist_toxml(lvutil.scan_srlist(EXT_PREFIX, self.dconf['device']),
166 EXT_PREFIX)
168 @deviceCheck
169 def create(self, sr_uuid, size):
170 if self._checkmount():
171 raise xs_errors.XenError('SRExists')
173 # Check none of the devices already in use by other PBDs
174 if util.test_hostPBD_devs(self.session, sr_uuid, self.dconf['device']):
175 raise xs_errors.XenError('SRInUse')
177 # Check serial number entry in SR records
178 for dev in self.dconf['device'].split(','):
179 if util.test_scsiserial(self.session, dev):
180 raise xs_errors.XenError('SRInUse')
182 if not lvutil._checkVG(self.vgname):
183 lvutil.createVG(self.dconf['device'], self.vgname)
185 if lvutil._checkLV(self.remotepath):
186 raise xs_errors.XenError('SRExists')
188 try:
189 numdevs = len(self.dconf['device'].split(','))
190 cmd = ["lvcreate", "-n", sr_uuid]
191 if numdevs > 1:
192 lowest = -1
193 for dev in self.dconf['device'].split(','):
194 stats = lvutil._getPVstats(dev)
195 if lowest < 0 or stats['freespace'] < lowest:
196 lowest = stats['freespace']
197 size_mb = (lowest // (1024 * 1024)) * numdevs
199 # Add stripe parameter to command
200 cmd += ["-i", str(numdevs), "-I", "2048"]
201 else:
202 stats = lvutil._getVGstats(self.vgname)
203 size_mb = stats['freespace'] // (1024 * 1024)
204 assert(size_mb > 0)
205 cmd += ["-L", str(size_mb), self.vgname]
206 text = util.pread(cmd)
208 cmd = ["lvchange", "-ay", self.remotepath]
209 text = util.pread(cmd)
210 except util.CommandException as inst:
211 raise xs_errors.XenError('LVMCreate', \
212 opterr='lv operation, error %d' % inst.code)
213 except AssertionError:
214 raise xs_errors.XenError('SRNoSpace', \
215 opterr='Insufficient space in VG %s' % self.vgname)
217 try:
218 util.pread2(["mkfs.xfs", self.remotepath])
219 except util.CommandException as inst:
220 raise xs_errors.XenError('LVMFilesystem', \
221 opterr='mkfs failed error %d' % inst.code)
223 #Update serial number string
224 scsiutil.add_serial_record(self.session, self.sr_ref, \
225 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
227 def vdi(self, uuid, loadLocked = False):
228 return XFSFileVDI(self, uuid)
230 @staticmethod
231 def _is_xfs_available():
232 import distutils.spawn
233 return distutils.spawn.find_executable('mkfs.xfs')
236class XFSFileVDI(FileSR.FileVDI):
237 def attach(self, sr_uuid, vdi_uuid):
238 if not hasattr(self, 'xenstore_data'):
239 self.xenstore_data = {}
241 self.xenstore_data['storage-type'] = XFSSR.DRIVER_TYPE
243 return super(XFSFileVDI, self).attach(sr_uuid, vdi_uuid)
246if __name__ == '__main__': 246 ↛ 247line 246 didn't jump to line 247, because the condition on line 246 was never true
247 SRCommand.run(XFSSR, DRIVER_INFO)
248else:
249 SR.registerSR(XFSSR)