Skip to content

Commit b488561

Browse files
committed
fix(LVMSR): set QCOW2 chain RW from the master on vdi_attach (#155)
Changing LVs to RW on coalesce on a remote host can clash with the master doing LVM commands. It might even lead to LVM metadata corruption. With this change, the `vdi_attach` will check if the chain is coalesceable on remote, if so it will ask the master to change the status before continuing. It's copying the behavior of changing LV sizes in `_prepareThin`. Signed-off-by: Damien Thenot <damien.thenot@vates.tech>
1 parent 602b7ac commit b488561

2 files changed

Lines changed: 56 additions & 20 deletions

File tree

drivers/LVMSR.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,10 @@ def attach(self, sr_uuid, vdi_uuid) -> str:
15341534
util.logException("attach")
15351535
raise xs_errors.XenError('LVMProvisionAttach')
15361536

1537+
if self.cowutil.isCoalesceableOnRemote():
1538+
# We make the chain RW before it's used so it can be coalesced online
1539+
self._make_chain_rw()
1540+
15371541
try:
15381542
return self._attach()
15391543
finally:
@@ -2259,6 +2263,33 @@ def _prepareThin(self, attach, vdiType):
22592263
self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref,
22602264
str(sr_utilisation))
22612265

2266+
def _make_chain_rw(self):
2267+
"""
2268+
In the case of QCOW2, we need the chain to be RW for the coalesce to be done by tapdisk.
2269+
Setting the chain to be RW early avoids having to stop the tapdisk so LVM can acknowledge the chain RW on the slave.
2270+
And while making the change to RW from the slave works without the refresh, it can cause LVM metadata corruption.
2271+
"""
2272+
# TODO(XCPNG-3689): We could check if any VDI on the chain is RO before doing the call to master to change this
2273+
if self.sr.isMaster:
2274+
lvmcache = self.sr.lvmCache
2275+
for uuid, lvName in self.cowutil.getParentChain(self.lvname, LvmCowUtil.extractUuid, self.sr.vgname).items():
2276+
lvmcache.setReadonly(lvName, False)
2277+
else:
2278+
master = util.get_master_ref(self.session)
2279+
response = self.session.xenapi.host.call_plugin(
2280+
master,
2281+
self.sr.PLUGIN_ON_SLAVE,
2282+
"make_chain_rw",
2283+
{
2284+
"vgName": self.sr.vgname,
2285+
"lvName": self.lvname,
2286+
"vdiType": self.vdi_type
2287+
}
2288+
)
2289+
util.SMlog(f"call-plugin returned: {response}")
2290+
if not response:
2291+
raise Exception(f"plugin {self.sr.PLUGIN_ON_SLAVE} failed")
2292+
22622293
@override
22632294
def update(self, sr_uuid, vdi_uuid) -> None:
22642295
if self.sr.legacyMode:

drivers/on_slave.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,6 @@ def refresh_lun_size_by_SCSIid(session, args):
162162
util.SMlog("on-slave.refresh_lun_size_by_SCSIid with %s failed" % args)
163163
return "False"
164164

165-
def _make_chain_RW(cowutil, leaf_path, write: bool):
166-
def set_RW(path):
167-
try:
168-
util.pread2(["lvchange", "-p", "rw", path])
169-
except:
170-
pass
171-
172-
# if path.startswith("/dev/"):
173-
# set_RW(leaf_path) # Not needed since it's a leaf
174-
175-
parent = cowutil.getParentNoCheck(leaf_path)
176-
while parent:
177-
if parent.startswith("/dev/"):
178-
set_RW(parent)
179-
parent = cowutil.getParentNoCheck(parent)
180-
181165
def commit_tapdisk(session, args):
182166
path: str = args["path"]
183167
vdi_type = args["vdi_type"]
@@ -186,12 +170,9 @@ def commit_tapdisk(session, args):
186170
from cowutil import getCowUtil
187171
cowutil = getCowUtil(vdi_type)
188172
try:
189-
if path.startswith("/dev/"):
190-
# We need to make children RW or tapdisk will crash trying to do it
191-
_make_chain_RW(cowutil, leaf_path, True)
192173
return str(cowutil.coalesceOnline(path))
193174
except:
194-
util.logException("Couldn't coalesce online")
175+
util.logException(f"Couldn't coalesce online: `{path}`")
195176
raise
196177

197178
def commit_cancel(session, args):
@@ -249,6 +230,29 @@ def is_openers(session, args):
249230
openers_pid= util.get_openers_pid(path)
250231
return str(bool(openers_pid))
251232

233+
def make_chain_rw(session, args):
234+
from cowutil import getCowUtil
235+
from lvmcowutil import LvmCowUtil
236+
from lvutil import MASTER_LVM_CONF
237+
238+
if util.is_master(session):
239+
os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
240+
241+
vgName = args["vgName"]
242+
lvName = args["lvName"]
243+
vdi_type = args["vdiType"]
244+
245+
cowutil = getCowUtil(vdi_type)
246+
lvmcache = LVMCache(vgName)
247+
248+
for uuid, lvName in cowutil.getParentChain(lvName, LvmCowUtil.extractUuid, vgName).items():
249+
try:
250+
lvmcache.setReadonly(lvName, False)
251+
except util.CommandException as e:
252+
util.SMlog(f"on-slave:make_chain_rw: {e}")
253+
254+
return "True"
255+
252256
if __name__ == "__main__":
253257
import XenAPIPlugin
254258
XenAPIPlugin.dispatch({
@@ -259,4 +263,5 @@ def is_openers(session, args):
259263
"commit_tapdisk": commit_tapdisk,
260264
"commit_cancel": commit_cancel,
261265
"cancel_coalesce_master": cancel_coalesce_master,
266+
"make_chain_rw": make_chain_rw,
262267
})

0 commit comments

Comments
 (0)