Hide keyboard shortcuts

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 

20 

21from sm_typing import override 

22 

23import SR 

24from SR import deviceCheck 

25import SRCommand 

26import VDI 

27import FileSR 

28import util 

29import lvutil 

30import scsiutil 

31 

32import os 

33import xs_errors 

34import vhdutil 

35from lock import Lock 

36from constants import EXT_PREFIX 

37 

38CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_SUPPORTS_LOCAL_CACHING", \ 

39 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", \ 

40 "VDI_UPDATE", "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "VDI_MIRROR", \ 

41 "VDI_GENERATE_CONFIG", \ 

42 "VDI_RESET_ON_BOOT/2", "ATOMIC_PAUSE", "VDI_CONFIG_CBT", 

43 "VDI_ACTIVATE", "VDI_DEACTIVATE", "THIN_PROVISIONING", "VDI_READ_CACHING"] 

44 

45CONFIGURATION = [['device', 'local device path (required) (e.g. /dev/sda3)']] 

46 

47DRIVER_INFO = { 

48 'name': 'Local XFS VHD', 

49 'description': 'SR plugin which represents disks as VHD files stored on a local XFS filesystem, created inside an LVM volume', 

50 'vendor': 'Vates SAS', 

51 'copyright': '(C) 2019 Vates SAS', 

52 'driver_version': '1.0', 

53 'required_api_version': '1.0', 

54 'capabilities': CAPABILITIES, 

55 'configuration': CONFIGURATION 

56 } 

57 

58DRIVER_CONFIG = {"ATTACH_FROM_CONFIG_WITH_TAPDISK": True} 

59 

60 

61class XFSSR(FileSR.FileSR): 

62 """XFS Local file storage repository""" 

63 

64 DRIVER_TYPE = 'xfs' 

65 

66 @override 

67 @staticmethod 

68 def handles(srtype) -> bool: 

69 return srtype == XFSSR.DRIVER_TYPE 

70 

71 @override 

72 def load(self, sr_uuid) -> None: 

73 if not self._is_xfs_available(): 

74 raise xs_errors.XenError( 

75 'SRUnavailable', 

76 opterr='xfsprogs is not installed' 

77 ) 

78 

79 self.ops_exclusive = FileSR.OPS_EXCLUSIVE 

80 self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid) 

81 self.sr_vditype = SR.DEFAULT_TAP 

82 

83 self.path = os.path.join(SR.MOUNT_BASE, sr_uuid) 

84 self.vgname = EXT_PREFIX + sr_uuid 

85 self.remotepath = os.path.join("/dev", self.vgname, sr_uuid) 

86 self.attached = self._checkmount() 

87 self.driver_config = DRIVER_CONFIG 

88 

89 @override 

90 def delete(self, sr_uuid) -> None: 

91 super(XFSSR, self).delete(sr_uuid) 

92 

93 # Check PVs match VG 

94 try: 

95 for dev in self.dconf['device'].split(','): 

96 cmd = ["pvs", dev] 

97 txt = util.pread2(cmd) 

98 if txt.find(self.vgname) == -1: 

99 raise xs_errors.XenError('VolNotFound', \ 

100 opterr='volume is %s' % self.vgname) 

101 except util.CommandException as inst: 

102 raise xs_errors.XenError('PVSfailed', \ 

103 opterr='error is %d' % inst.code) 

104 

105 # Remove LV, VG and pv 

106 try: 

107 cmd = ["lvremove", "-f", self.remotepath] 

108 util.pread2(cmd) 

109 

110 cmd = ["vgremove", self.vgname] 

111 util.pread2(cmd) 

112 

113 for dev in self.dconf['device'].split(','): 

114 cmd = ["pvremove", dev] 

115 util.pread2(cmd) 

116 except util.CommandException as inst: 

117 raise xs_errors.XenError('LVMDelete', \ 

118 opterr='errno is %d' % inst.code) 

119 

120 @override 

121 def attach(self, sr_uuid) -> None: 

122 if not self._checkmount(): 

123 try: 

124 #Activate LV 

125 cmd = ['lvchange', '-ay', self.remotepath] 

126 util.pread2(cmd) 

127 

128 # make a mountpoint: 

129 if not os.path.isdir(self.path): 

130 os.makedirs(self.path) 

131 except util.CommandException as inst: 

132 raise xs_errors.XenError('LVMMount', \ 

133 opterr='Unable to activate LV. Errno is %d' % inst.code) 

134 

135 try: 

136 util.pread(["fsck", "-a", self.remotepath]) 

137 except util.CommandException as inst: 

138 if inst.code == 1: 

139 util.SMlog("FSCK detected and corrected FS errors. Not fatal.") 

140 else: 

141 raise xs_errors.XenError('LVMMount', \ 

142 opterr='FSCK failed on %s. Errno is %d' % (self.remotepath, inst.code)) 

143 

144 try: 

145 util.pread(["mount", self.remotepath, self.path]) 

146 except util.CommandException as inst: 

147 raise xs_errors.XenError('LVMMount', \ 

148 opterr='Failed to mount FS. Errno is %d' % inst.code) 

149 

150 self.attached = True 

151 

152 #Update SCSIid string 

153 scsiutil.add_serial_record(self.session, self.sr_ref, \ 

154 scsiutil.devlist_to_serialstring(self.dconf['device'].split(','))) 

155 

156 # Set the block scheduler 

157 for dev in self.dconf['device'].split(','): 

158 self.block_setscheduler(dev) 

159 

160 @override 

161 def detach(self, sr_uuid) -> None: 

162 super(XFSSR, self).detach(sr_uuid) 

163 try: 

164 # deactivate SR 

165 cmd = ["lvchange", "-an", self.remotepath] 

166 util.pread2(cmd) 

167 except util.CommandException as inst: 

168 raise xs_errors.XenError('LVMUnMount', \ 

169 opterr='lvm -an failed errno is %d' % inst.code) 

170 

171 @override 

172 @deviceCheck 

173 def probe(self) -> str: 

174 return lvutil.srlist_toxml(lvutil.scan_srlist(EXT_PREFIX, self.dconf['device']), 

175 EXT_PREFIX) 

176 

177 @override 

178 @deviceCheck 

179 def create(self, sr_uuid, size) -> None: 

180 if self._checkmount(): 

181 raise xs_errors.XenError('SRExists') 

182 

183 # Check none of the devices already in use by other PBDs 

184 if util.test_hostPBD_devs(self.session, sr_uuid, self.dconf['device']): 

185 raise xs_errors.XenError('SRInUse') 

186 

187 # Check serial number entry in SR records 

188 for dev in self.dconf['device'].split(','): 

189 if util.test_scsiserial(self.session, dev): 

190 raise xs_errors.XenError('SRInUse') 

191 

192 if not lvutil._checkVG(self.vgname): 

193 lvutil.createVG(self.dconf['device'], self.vgname) 

194 

195 if lvutil._checkLV(self.remotepath): 

196 raise xs_errors.XenError('SRExists') 

197 

198 try: 

199 numdevs = len(self.dconf['device'].split(',')) 

200 cmd = ["lvcreate", "-n", sr_uuid] 

201 if numdevs > 1: 

202 lowest = -1 

203 for dev in self.dconf['device'].split(','): 

204 stats = lvutil._getPVstats(dev) 

205 if lowest < 0 or stats['freespace'] < lowest: 

206 lowest = stats['freespace'] 

207 size_mb = (lowest // (1024 * 1024)) * numdevs 

208 

209 # Add stripe parameter to command 

210 cmd += ["-i", str(numdevs), "-I", "2048"] 

211 else: 

212 stats = lvutil._getVGstats(self.vgname) 

213 size_mb = stats['freespace'] // (1024 * 1024) 

214 assert(size_mb > 0) 

215 cmd += ["-L", str(size_mb), self.vgname] 

216 text = util.pread(cmd) 

217 

218 cmd = ["lvchange", "-ay", self.remotepath] 

219 text = util.pread(cmd) 

220 except util.CommandException as inst: 

221 raise xs_errors.XenError('LVMCreate', \ 

222 opterr='lv operation, error %d' % inst.code) 

223 except AssertionError: 

224 raise xs_errors.XenError('SRNoSpace', \ 

225 opterr='Insufficient space in VG %s' % self.vgname) 

226 

227 try: 

228 util.pread2(["mkfs.xfs", self.remotepath]) 

229 except util.CommandException as inst: 

230 raise xs_errors.XenError('LVMFilesystem', \ 

231 opterr='mkfs failed error %d' % inst.code) 

232 

233 #Update serial number string 

234 scsiutil.add_serial_record(self.session, self.sr_ref, \ 

235 scsiutil.devlist_to_serialstring(self.dconf['device'].split(','))) 

236 

237 @override 

238 def vdi(self, uuid, loadLocked = False) -> VDI.VDI: 

239 return XFSFileVDI(self, uuid) 

240 

241 @staticmethod 

242 def _is_xfs_available(): 

243 return util.find_executable('mkfs.xfs') 

244 

245 

246class XFSFileVDI(FileSR.FileVDI): 

247 @override 

248 def attach(self, sr_uuid, vdi_uuid) -> str: 

249 if not hasattr(self, 'xenstore_data'): 

250 self.xenstore_data = {} 

251 

252 self.xenstore_data['storage-type'] = XFSSR.DRIVER_TYPE 

253 

254 return super(XFSFileVDI, self).attach(sr_uuid, vdi_uuid) 

255 

256 

257if __name__ == '__main__': 257 ↛ 258line 257 didn't jump to line 258, because the condition on line 257 was never true

258 SRCommand.run(XFSSR, DRIVER_INFO) 

259else: 

260 SR.registerSR(XFSSR)