Skip to content

Commit

Permalink
LINSTOR COW in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Wescoeur committed Feb 4, 2025
1 parent 38b6b4e commit f6d9f60
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 186 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ SM_LIBS += scsiutil
SM_LIBS += scsi_host_rescan
SM_LIBS += cowutil
SM_LIBS += vhdutil
SM_LIBS += linstorcowutil
SM_LIBS += linstorjournaler
SM_LIBS += linstorvhdutil
SM_LIBS += linstorvolumemanager
SM_LIBS += lvmcowutil
SM_LIBS += cifutils
Expand Down
205 changes: 96 additions & 109 deletions drivers/LinstorSR.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion drivers/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
from vditype import VdiType, VdiTypeExtension, VDI_COW_TYPES, VDI_TYPE_TO_EXTENSION

try:
from linstorcowutil import LinstorCowUtil
from linstorjournaler import LinstorJournaler
from linstorvhdutil import LinstorVhdUtil
from linstorvolumemanager import get_controller_uri
from linstorvolumemanager import LinstorVolumeManager
from linstorvolumemanager import LinstorVolumeManagerError
Expand Down
66 changes: 30 additions & 36 deletions drivers/linstor-manager
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ import XenAPI
import XenAPIPlugin

from json import JSONEncoder
from cowutil import getCowUtil
from linstorcowutil import LinstorCowUtil, check_ex
from linstorjournaler import LinstorJournaler
from linstorvhdutil import LinstorVhdUtil, check_ex
from linstorvolumemanager import get_controller_uri, get_local_volume_openers, LinstorVolumeManager
import json
import LinstorSR
import lock
import re
import util
import vhdutil

BACKING_DISK_RE = re.compile('^/dev/([^/]+)/(?:[^/]+)$')
LVM_PLUGIN = 'lvm.py'
Expand Down Expand Up @@ -281,6 +281,12 @@ def get_ip_addr_of_pif(session, pif_uuid):
raise XenAPIPlugin.Failure('-1', ['PIF has no IP'])
return ip_addr


def extract_uuid(device_path):
return linstor.get_volume_uuid_from_device_path(
device_path.rstrip('\n')
)

# ------------------------------------------------------------------------------


Expand Down Expand Up @@ -399,8 +405,9 @@ def check(session, args):
raise


def get_vhd_info(session, args): # TODO: rename
def get_info(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
group_name = args['groupName']
include_parent = util.strtobool(args['includeParent'])
Expand All @@ -411,28 +418,19 @@ def get_vhd_info(session, args): # TODO: rename
logger=util.SMlog
)

# TODO
cowutil = None

def extract_uuid(device_path):
# TODO: Remove new line in the vhdutil module. Not here.
return linstor.get_volume_uuid_from_device_path(
device_path.rstrip('\n')
)

image_info = cowutil.getInfo(
device_path, extract_uuid, include_parent, False
)
return json.dumps(image_info.__dict__)
except Exception as e:
util.SMlog('linstor-manager:get_vhd_info error: {}'.format(e))
util.SMlog('linstor-manager:get_info error: {}'.format(e))
raise


def has_parent(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return str(cowutil.hasParent(device_path))
except Exception as e:
util.SMlog('linstor-manager:has_parent error: {}'.format(e))
Expand All @@ -441,6 +439,7 @@ def has_parent(session, args):

def get_parent(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
group_name = args['groupName']

Expand All @@ -450,14 +449,6 @@ def get_parent(session, args):
logger=util.SMlog
)

cowutil = None # TODO

def extract_uuid(device_path):
# TODO: Remove new line in the vhdutil module. Not here.
return linstor.get_volume_uuid_from_device_path(
device_path.rstrip('\n')
)

return cowutil.getParent(device_path, extract_uuid)
except Exception as e:
util.SMlog('linstor-manager:get_parent error: {}'.format(e))
Expand All @@ -466,8 +457,8 @@ def get_parent(session, args):

def get_size_virt(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None # TODO
return str(cowutil.getSizeVirt(device_path))
except Exception as e:
util.SMlog('linstor-manager:get_size_virt error: {}'.format(e))
Expand All @@ -476,17 +467,18 @@ def get_size_virt(session, args):

def get_size_phys(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
return str(vhdutil.getSizePhys(device_path))
return str(cowutil.getSizePhys(device_path))
except Exception as e:
util.SMlog('linstor-manager:get_size_phys error: {}'.format(e))
raise


def get_allocated_size(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return str(cowutil.getAllocatedSize(device_path))
except Exception as e:
util.SMlog('linstor-manager:get_allocated_size error: {}'.format(e))
Expand All @@ -495,8 +487,8 @@ def get_allocated_size(session, args):

def get_depth(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return str(cowutil.getDepth(device_path))
except Exception as e:
util.SMlog('linstor-manager:get_depth error: {}'.format(e))
Expand All @@ -505,8 +497,8 @@ def get_depth(session, args):

def get_key_hash(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return cowutil.getKeyHash(device_path) or ''
except Exception as e:
util.SMlog('linstor-manager:get_key_hash error: {}'.format(e))
Expand All @@ -515,8 +507,8 @@ def get_key_hash(session, args):

def get_block_bitmap(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return base64.b64encode(cowutil.getBlockBitmap(device_path)).decode('ascii')
except Exception as e:
util.SMlog('linstor-manager:get_block_bitmap error: {}'.format(e))
Expand All @@ -537,9 +529,9 @@ def get_drbd_size(session, args):

def set_parent(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
parent_path = args['parentPath']
cowutil = None # TODO
cowutil.setParent(device_path, parent_path, False)
return ''
except Exception as e:
Expand All @@ -549,8 +541,8 @@ def set_parent(session, args):

def coalesce(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
cowutil = None
return str(cowutil.coalesce(device_path))
except Exception as e:
util.SMlog('linstor-manager:coalesce error: {}'.format(e))
Expand All @@ -559,8 +551,9 @@ def coalesce(session, args):

def repair(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
vhdutil.repair(device_path)
cowutil.repair(device_path)
return ''
except Exception as e:
util.SMlog('linstor-manager:repair error: {}'.format(e))
Expand All @@ -569,6 +562,7 @@ def repair(session, args):

def deflate(session, args):
try:
cowutil = getCowUtil(args['vdiType'])
device_path = args['devicePath']
new_size = int(args['newSize'])
old_size = int(args['oldSize'])
Expand All @@ -580,7 +574,7 @@ def deflate(session, args):
group_name,
logger=util.SMlog
)
LinstorVhdUtil(session, linstor).deflate(device_path, new_size, old_size, zeroize)
LinstorCowUtil(session, linstor, cowutil).deflate(device_path, new_size, old_size, zeroize)
return ''
except Exception as e:
util.SMlog('linstor-manager:deflate error: {}'.format(e))
Expand Down Expand Up @@ -1213,14 +1207,14 @@ if __name__ == '__main__':
'detach': detach,
'destroy': destroy,

# vhdutil wrappers called by linstorvhdutil.
# Note: When a VHD is open in RO mode (so for all vhdutil getters),
# cowutil wrappers called by LinstorCowUtil.
# Note: When a COW image is open in RO mode (so for all cowutil getters),
# the LVM layer is used directly to bypass DRBD verifications.
# In this case there can't be EROFS errors.
# Note 2: We assume linstorvhdutil executes remote calls on diskful
# Note 2: We assume LinstorCowUtil executes remote calls on diskful
# DRBDs, otherwise we still have EROFS errors...
'check': check,
'getVHDInfo': get_vhd_info,
'getInfo': get_info,
'hasParent': has_parent,
'getParent': get_parent,
'getSizeVirt': get_size_virt,
Expand Down
39 changes: 19 additions & 20 deletions drivers/linstorvhdutil.py → drivers/linstorcowutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def wrapper(*args, **kwargs):
# B. Execute the plugin on master or slave.
remote_args = {
'devicePath': device_path,
'groupName': self._linstor.group_name
'groupName': self._linstor.group_name,
'vdiType': self._vdi_type
}
remote_args.update(**kwargs)
remote_args = {str(key): str(value) for key, value in remote_args.items()}
Expand Down Expand Up @@ -153,16 +154,15 @@ def wrapper(*args, **kwargs):
return decorated


class LinstorVhdUtil:
MAX_SIZE = 2 * 1024 * 1024 * 1024 * 1024 # Max VHD size.

class LinstorCowUtil:
def __init__(self, session, linstor, vdi_type: str):
self._cowutil = getCowUtil(vdi_type)
self._session = session
self._linstor = linstor
self._cowutil = getCowUtil(vdi_type)
self._vdi_type = vdi_type

def create_chain_paths(self, vdi_uuid, readonly=False):
# OPTIMIZE: Add a limit_to_first_allocated_block param to limit vhdutil calls.
# OPTIMIZE: Add a limit_to_first_allocated_block param to limit cowutil calls.
# Useful for the snapshot code algorithm.

leaf_vdi_path = self._linstor.get_device_path(vdi_uuid)
Expand Down Expand Up @@ -219,17 +219,17 @@ def check(self, vdi_uuid, ignore_missing_footer=False, fast=False):
def _check(self, vdi_uuid, response):
return util.strtobool(response)

def get_vhd_info(self, vdi_uuid, include_parent=True):
def get_info(self, vdi_uuid, include_parent=True):
kwargs = {
'includeParent': include_parent,
'resolveParent': False
}
# TODO: Replace pylint comment with this feature when possible:
# https://github.com/PyCQA/pylint/pull/2926
return self._get_vhd_info(vdi_uuid, self._extract_uuid, **kwargs) # pylint: disable = E1123
return self._get_info(vdi_uuid, self._extract_uuid, **kwargs) # pylint: disable = E1123

@linstorhostcall(CowUtil.getInfo, 'getInfo')
def _get_vhd_info(self, vdi_uuid, response):
def _get_info(self, vdi_uuid, response):
obj = json.loads(response)

image_info = CowImageInfo(vdi_uuid)
Expand Down Expand Up @@ -410,12 +410,11 @@ def _force_deflate(self, cowutil_inst, path, newSize, oldSize, zeroize):
# Helpers.
# --------------------------------------------------------------------------

@classmethod
def compute_volume_size(cls, virtual_size, vdi_type: str):
def compute_volume_size(cls, virtual_size: int) -> int:
if VdiType.isCowImage(vdi_type):
# All LINSTOR VDIs have the metadata area preallocated for
# the maximum possible virtual size (for fast online VDI.resize).
meta_overhead = self._cowutil.calcOverheadEmpty(self.MAX_SIZE)
meta_overhead = self._cowutil.calcOverheadEmpty(self.MAX_SIZE) # TODO: getDefaultPreallocaqatio... + max
bitmap_overhead = self._cowutil.calcOverheadBitmap(virtual_size)
virtual_size += meta_overhead + bitmap_overhead
else:
Expand All @@ -431,9 +430,9 @@ def _extract_uuid(self, device_path):

def _get_readonly_host(self, vdi_uuid, device_path, node_names):
"""
When vhd-util is called to fetch VDI info we must find a
When CowUtil is called to fetch VDI info we must find a
diskful DRBD disk to read the data. It's the goal of this function.
Why? Because when a VHD is open in RO mode, the LVM layer is used
Why? Because when a COW image is open in RO mode, the LVM layer is used
directly to bypass DRBD verifications (we can have only one process
that reads/writes to disk with DRBD devices).
"""
Expand Down Expand Up @@ -497,7 +496,7 @@ def local_call():
# Retry only locally if it's not an EROFS exception.
return util.retry(local_call, 5, 2, exceptions=[util.CommandException])
except util.CommandException as e:
util.SMlog('failed to execute locally vhd-util (sys {})'.format(e.code))
util.SMlog('failed to execute locally CowUtil (sys {})'.format(e.code))
raise e

def _call_local_method_or_fail(self, local_method, device_path, *args, **kwargs):
Expand All @@ -508,7 +507,7 @@ def _call_local_method_or_fail(self, local_method, device_path, *args, **kwargs)
self._raise_openers_exception(device_path, e.cmd_err)

def _call_method(self, local_method, remote_method, device_path, use_parent, *args, **kwargs):
# Note: `use_parent` exists to know if the VHD parent is used by the local/remote method.
# Note: `use_parent` exists to know if the COW image parent is used by the local/remote method.
# Normally in case of failure, if the parent is unused we try to execute the method on
# another host using the DRBD opener list. In the other case, if the parent is required,
# we must check where this last one is open instead of the child.
Expand All @@ -531,7 +530,7 @@ def _call_method(self, local_method, remote_method, device_path, use_parent, *ar
except Exception as e:
raise xs_errors.XenError(
'VDIUnavailable',
opterr='Unable to get host list to run vhd-util command `{}` (path={}): {}'
opterr='Unable to get host list to run CowUtil command `{}` (path={}): {}'
.format(remote_method, device_path, e)
)

Expand Down Expand Up @@ -559,7 +558,7 @@ def remote_call():
except Exception as e:
raise xs_errors.XenError(
'VDIUnavailable',
opterr='Unable to get DRBD openers to run vhd-util command `{}` (path={}): {}'
opterr='Unable to get DRBD openers to run CowUtil command `{}` (path={}): {}'
.format(remote_method, device_path, e)
)

Expand Down Expand Up @@ -587,7 +586,7 @@ def remote_call():

raise xs_errors.XenError(
'VDIUnavailable',
opterr='No valid host found to run vhd-util command `{}` (path=`{}`, openers=`{}`)'
opterr='No valid host found to run CowUtil command `{}` (path=`{}`, openers=`{}`)'
.format(remote_method, device_path, openers)
)
return util.retry(remote_call, 5, 2)
Expand All @@ -596,5 +595,5 @@ def _zeroize(path, size):
if not util.zeroOut(path, size, self._cowutil.getFooterSize()):
raise xs_errors.XenError(
'EIO',
opterr='Failed to zero out VHD footer {}'.format(path)
opterr='Failed to zero out COW image footer {}'.format(path)
)
Loading

0 comments on commit f6d9f60

Please sign in to comment.