Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8.2 linstor: Add sync to dd command #71

Open
wants to merge 3 commits into
base: 2.30.8-8.2-linstor-fixes-staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 56 additions & 38 deletions drivers/linstorvhdutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,21 @@


def call_remote_method(session, host_ref, method, device_path, args):
host_rec = session.xenapi.host.get_record(host_ref)
host_uuid = host_rec['uuid']

try:
response = session.xenapi.host.call_plugin(
host_ref, MANAGER_PLUGIN, method, args
)
except Exception as e:
util.SMlog('call-plugin ({} with {}) exception: {}'.format(
method, args, e
util.SMlog('call-plugin on {} ({} with {}) exception: {}'.format(
host_uuid, method, args, e
))
raise util.SMException(str(e))

util.SMlog('call-plugin ({} with {}) returned: {}'.format(
method, args, response
util.SMlog('call-plugin on {} ({} with {}) returned: {}'.format(
host_uuid, method, args, response
))

return response
Expand Down Expand Up @@ -75,6 +78,17 @@ class ErofsLinstorCallException(LinstorCallException):
class NoPathLinstorCallException(LinstorCallException):
pass

def log_successful_call(target_host, device_path, vdi_uuid, remote_method, response):
util.SMlog(
'Successful access on {} for device {} ({}): `{}` => {}'.format(target_host, device_path, vdi_uuid, remote_method, str(response)),
priority=util.LOG_DEBUG
)

def log_failed_call(target_host, next_target, device_path, vdi_uuid, remote_method, e):
util.SMlog(
'Failed to call method on {} for device {} ({}): {}. Trying accessing on {}... (cause: {})'.format(target_host, device_path, vdi_uuid, remote_method, next_target, e),
priority=util.LOG_DEBUG
)

def linstorhostcall(local_method, remote_method):
def decorated(response_parser):
Expand All @@ -86,33 +100,6 @@ def wrapper(*args, **kwargs):
self._linstor.get_volume_name(vdi_uuid)
)

# A. Try a call using directly the DRBD device to avoid
# remote request.

# Try to read locally if the device is not in use or if the device
# is up to date and not diskless.
(node_names, in_use_by) = \
self._linstor.find_up_to_date_diskful_nodes(vdi_uuid)

local_e = None
try:
if not in_use_by or socket.gethostname() in node_names:
return self._call_local_method(local_method, device_path, *args[2:], **kwargs)
except ErofsLinstorCallException as e:
local_e = e.cmd_err
except Exception as e:
local_e = e

util.SMlog(
'unable to execute `{}` locally, retry using a readable host... (cause: {})'.format(
remote_method, local_e if local_e else 'local diskless + in use or not up to date'
)
)

if in_use_by:
node_names = {in_use_by}

# B. Execute the plugin on master or slave.
remote_args = {
'devicePath': device_path,
'groupName': self._linstor.group_name
Expand All @@ -121,14 +108,45 @@ def wrapper(*args, **kwargs):
remote_args = {str(key): str(value) for key, value in remote_args.iteritems()}

try:
def remote_call():
host_ref = self._get_readonly_host(vdi_uuid, device_path, node_names)
return call_remote_method(self._session, host_ref, remote_method, device_path, remote_args)
response = util.retry(remote_call, 5, 2)
except Exception as remote_e:
self._raise_openers_exception(device_path, local_e or remote_e)
host_ref_attached = util.get_hosts_attached_on(self._session, [vdi_uuid])[0]
if host_ref_attached:
response = call_remote_method(
self._session, host_ref_attached, remote_method, device_path, remote_args
)
log_successful_call('attached node', device_path, vdi_uuid, remote_method, response)
return response_parser(self, vdi_uuid, response)
except Exception as e:
log_failed_call('attached node', 'master', device_path, vdi_uuid, remote_method, e)

try:
master_ref = self._session.xenapi.pool.get_all_records().values()[0]['master']
response = call_remote_method(self._session, master_ref, remote_method, device_path, remote_args)
log_successful_call('master', device_path, vdi_uuid, remote_method, response)
return response_parser(self, vdi_uuid, response)
except Exception as e:
log_failed_call('master', 'primary', device_path, vdi_uuid, remote_method, e)


nodes, primary_hostname = self._linstor.find_up_to_date_diskful_nodes(vdi_uuid)
if primary_hostname:
try:
host_ref = self._get_readonly_host(vdi_uuid, device_path, {primary_hostname})
response = call_remote_method(self._session, host_ref, remote_method, device_path, remote_args)
log_successful_call('primary', device_path, vdi_uuid, remote_method, response)
return response_parser(self, vdi_uuid, response)
except Exception as remote_e:
self._raise_openers_exception(device_path, remote_e)
else:
log_failed_call('primary', 'another node', device_path, vdi_uuid, remote_method, e)

try:
host = self._get_readonly_host(vdi_uuid, device_path, nodes)
response = call_remote_method(self._session, host, remote_method, device_path, remote_args)
log_successful_call('another node', device_path, vdi_uuid, remote_method, response)
return response_parser(self, vdi_uuid, response)
except Exception as remote_e:
self._raise_openers_exception(device_path, remote_e)

return response_parser(self, vdi_uuid, response)
return wrapper
return decorated

Expand Down
18 changes: 18 additions & 0 deletions drivers/linstorvolumemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,24 @@ def find_up_to_date_diskful_nodes(self, volume_uuid):

return (node_names, in_use_by)

def get_primary(self, volume_uuid):
"""
Find the node that opened a volume, i.e. the primary.
:rtype: str
"""
volume_name = self.get_volume_name(volume_uuid)

resource_states = filter(
lambda resource_state: resource_state.name == volume_name,
self._get_resource_cache().resource_states
)

for resource_state in resource_states:
if resource_state.in_use:
return resource_state.node_name

return None

def invalidate_resource_cache(self):
"""
If resources are impacted by external commands like vhdutil,
Expand Down
6 changes: 3 additions & 3 deletions drivers/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ def zeroOut(path, fromByte, bytes):
bytesBefore = bytes
bytes -= bytesBefore
cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=1", \
"seek=%s" % fromByte, "count=%s" % bytesBefore]
"seek=%s" % fromByte, "count=%s" % bytesBefore, "conv=fsync"]
try:
text = pread2(cmd)
except CommandException:
Expand All @@ -586,15 +586,15 @@ def zeroOut(path, fromByte, bytes):
fromByte = (fromBlock + blocks) * blockSize
if blocks:
cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=%s" % blockSize, \
"seek=%s" % fromBlock, "count=%s" % blocks]
"seek=%s" % fromBlock, "count=%s" % blocks, "conv=fsync"]
try:
text = pread2(cmd)
except CommandException:
return False

if bytes:
cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=1", \
"seek=%s" % fromByte, "count=%s" % bytes]
"seek=%s" % fromByte, "count=%s" % bytes, "conv=fsync"]
try:
text = pread2(cmd)
except CommandException:
Expand Down