Coverage for libs/sm/drivers/EXTSR.py: 21%
126 statements
« prev ^ index » next coverage.py v7.3.2, created at 2026-07-10 07:59 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2026-07-10 07:59 +0000
1#!/usr/bin/python3
2#
3# Copyright (C) Citrix Systems Inc.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License as published
7# by the Free Software Foundation; version 2.1 only.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with this program; if not, write to the Free Software Foundation, Inc.,
16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17#
18# EXTSR: Based on local-file storage repository, mounts ext3 partition
19# matches with drivers/EXTSR
21import os
23from sm import SR
24from sm import lvutil
25from sm import vhdutil
26from sm.constants import EXT_PREFIX
27from sm.drivers import FileSR
28from sm.SR import deviceCheck
29from sm.core import util
30from sm.core import scsiutil
31from sm.core import xs_errors
32from sm.core.lock import Lock
34CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_SUPPORTS_LOCAL_CACHING",
35 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH",
36 "VDI_UPDATE", "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "VDI_MIRROR",
37 "VDI_GENERATE_CONFIG",
38 "VDI_RESET_ON_BOOT/2", "ATOMIC_PAUSE", "VDI_CONFIG_CBT",
39 "VDI_ACTIVATE", "VDI_DEACTIVATE", "THIN_PROVISIONING", "VDI_READ_CACHING"]
41CONFIGURATION = [['device', 'local device path (required) (e.g. /dev/sda3)']]
43DRIVER_INFO = {
44 'name': 'Local EXT3 VHD',
45 'description': 'SR plugin which represents disks as VHD files stored on a local EXT3 filesystem, created inside an LVM volume',
46 'vendor': 'Citrix Systems Inc',
47 'copyright': '(C) 2008 Citrix Systems Inc',
48 'driver_version': '1.0',
49 'required_api_version': '1.0',
50 'capabilities': CAPABILITIES,
51 'configuration': CONFIGURATION
52 }
54DRIVER_CONFIG = {"ATTACH_FROM_CONFIG_WITH_TAPDISK": True}
57class EXTSR(FileSR.FileSR):
58 """EXT3 Local file storage repository"""
60 def handles(srtype):
61 return srtype == 'ext'
62 handles = staticmethod(handles)
64 def load(self, sr_uuid):
65 self.ops_exclusive = FileSR.OPS_EXCLUSIVE
66 self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid)
67 self.sr_vditype = SR.DEFAULT_TAP
69 self.path = os.path.join(SR.MOUNT_BASE, sr_uuid)
70 self.vgname = EXT_PREFIX + sr_uuid
71 self.remotepath = os.path.join("/dev", self.vgname, sr_uuid)
72 self.attached = self._checkmount()
73 self.driver_config = DRIVER_CONFIG
75 def delete(self, sr_uuid):
76 super(EXTSR, self).delete(sr_uuid)
78 # Check PVs match VG
79 try:
80 for dev in self.dconf['device'].split(','):
81 cmd = ["pvs", dev]
82 txt = util.pread2(cmd)
83 if txt.find(self.vgname) == -1:
84 raise xs_errors.XenError('VolNotFound',
85 opterr='volume is %s' % self.vgname)
86 except util.CommandException as inst:
87 raise xs_errors.XenError('PVSfailed',
88 opterr='error is %d' % inst.code)
90 # Remove LV, VG and pv
91 try:
92 cmd = ["lvremove", "-f", self.remotepath]
93 util.pread2(cmd)
95 cmd = ["vgremove", self.vgname]
96 util.pread2(cmd)
98 for dev in self.dconf['device'].split(','):
99 cmd = ["pvremove", dev]
100 util.pread2(cmd)
101 except util.CommandException as inst:
102 raise xs_errors.XenError('LVMDelete',
103 opterr='errno is %d' % inst.code)
105 def attach(self, sr_uuid):
106 if not self._checkmount():
107 try:
108 #Activate LV
109 cmd = ['lvchange', '-ay', self.remotepath]
110 util.pread2(cmd)
111 except util.CommandException as inst:
112 raise xs_errors.XenError(
113 'LVMMount',
114 opterr='Unable to activate LV. Errno is %d' % inst.code)
116 try:
117 util.pread(["fsck", "-a", self.remotepath])
118 except util.CommandException as inst:
119 if inst.code == 1:
120 util.SMlog("FSCK detected and corrected FS errors. Not fatal.")
121 else:
122 raise xs_errors.XenError(
123 'LVMMount',
124 opterr='FSCK failed on %s. Errno is %d' % (self.remotepath, inst.code))
126 super(EXTSR, self).attach(sr_uuid, bind=False)
128 self.attached = True
130 #Update SCSIid string
131 scsiutil.add_serial_record(
132 self.session, self.sr_ref,
133 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
135 # Set the block scheduler
136 for dev in self.dconf['device'].split(','):
137 self.block_setscheduler(dev)
139 def detach(self, sr_uuid):
140 super(EXTSR, self).detach(sr_uuid)
141 try:
142 # deactivate SR
143 cmd = ["lvchange", "-an", self.remotepath]
144 util.pread2(cmd)
145 except util.CommandException as inst:
146 raise xs_errors.XenError(
147 'LVMUnMount',
148 opterr='lvm -an failed errno is %d' % inst.code)
150 @deviceCheck
151 def probe(self):
152 return lvutil.srlist_toxml(lvutil.scan_srlist(EXT_PREFIX, self.dconf['device']),
153 EXT_PREFIX)
155 @deviceCheck
156 def create(self, sr_uuid, size):
157 if self._checkmount():
158 raise xs_errors.XenError('SRExists')
160 # Check none of the devices already in use by other PBDs
161 if util.test_hostPBD_devs(self.session, sr_uuid, self.dconf['device']):
162 raise xs_errors.XenError('SRInUse')
164 # Check serial number entry in SR records
165 for dev in self.dconf['device'].split(','):
166 if util.test_scsiserial(self.session, dev):
167 raise xs_errors.XenError('SRInUse')
169 if not lvutil._checkVG(self.vgname):
170 lvutil.createVG(self.dconf['device'], self.vgname)
172 if lvutil._checkLV(self.remotepath):
173 raise xs_errors.XenError('SRExists')
175 try:
176 numdevs = len(self.dconf['device'].split(','))
177 cmd = ["lvcreate", "-n", sr_uuid]
178 if numdevs > 1:
179 lowest = -1
180 for dev in self.dconf['device'].split(','):
181 stats = lvutil._getPVstats(dev)
182 if lowest < 0 or stats['freespace'] < lowest:
183 lowest = stats['freespace']
184 size_mb = (lowest // (1024 * 1024)) * numdevs
186 # Add stripe parameter to command
187 cmd += ["-i", str(numdevs), "-I", "2048"]
188 else:
189 stats = lvutil._getVGstats(self.vgname)
190 size_mb = stats['freespace'] // (1024 * 1024)
191 assert(size_mb > 0)
192 cmd += ["-L", str(size_mb), self.vgname]
193 cmd += ["-W", "y", "--yes"]
194 text = util.pread(cmd)
196 cmd = ["lvchange", "-ay", self.remotepath]
197 text = util.pread(cmd)
198 except util.CommandException as inst:
199 raise xs_errors.XenError(
200 'LVMCreate',
201 opterr='lv operation, error %d' % inst.code)
202 except AssertionError:
203 raise xs_errors.XenError(
204 'SRNoSpace',
205 opterr='Insufficient space in VG %s' % self.vgname)
207 try:
208 util.pread2(["mkfs.ext4", "-F", self.remotepath])
209 except util.CommandException as inst:
210 raise xs_errors.XenError('LVMFilesystem',
211 opterr='mkfs failed error %d' % inst.code)
213 #Update serial number string
214 scsiutil.add_serial_record(
215 self.session, self.sr_ref,
216 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
218 def vdi(self, uuid):
219 return EXTFileVDI(self, uuid)
222class EXTFileVDI(FileSR.FileVDI):
223 def attach(self, sr_uuid, vdi_uuid):
224 if not hasattr(self, 'xenstore_data'):
225 self.xenstore_data = {}
227 self.xenstore_data["storage-type"] = "ext"
229 return super(EXTFileVDI, self).attach(sr_uuid, vdi_uuid)
232# SR registration at import
233SR.registerSR(EXTSR)