Commit 5a0fc9bf authored by Sebastien Gougeaud's avatar Sebastien Gougeaud Committed by Thomas Leibovici
Browse files

dss: lock the device/medium when updating its admin status



To avoid concurency between multiple update commands (device and medium
lock/unlock), the CLI requests a lock on the object before updating it.
If other clients try to request the lock, they fail.

Also fix the media tag update, where the modified data structure is
retrieved before acquiring the lock. It now retrieves it again, for
update, after acquiring the lock.

Change-Id: I3fa9a17475d445e8ecffd848092a91a53f4f0980
Signed-off-by: default avatarSebastien Gougeaud <sebastien.gougeaud@cea.fr>
Reviewed-on: https://gerrit.ccc.ocre.cea.fr/gerrit/c/phobos/+/7089

Tested-by: default avatarJenkins s8open_nr <s8open_nr@ccc.ocre.cea.fr>
Reviewed-by: default avatarQuentin Bouget <quentin.bouget@cea.fr>
Reviewed-by: default avatarThomas Leibovici <thomas.leibovici@cea.fr>
parent 7ddae2dd
......@@ -634,15 +634,17 @@ class DeviceOptHandler(BaseResourceOptHandler):
self.logger.error("Device %s not found", serial)
sys.exit(os.EX_DATAERR)
assert len(device) == 1
if device[0].lock.lock != "":
if self.params.get('force'):
self.logger.warn("Device %s is in use. Administrative "
"locking will not be effective "
"immediately", serial)
else:
self.logger.error("Device %s is in use by %s.",
serial, device[0].lock.lock)
continue
try:
self.client.devices.lock([device[0]])
except EnvironmentError as err:
self.logger.error("Failed to lock device '%s': %s", serial,
env_error_format(err))
continue
device = self.filter(serial)
assert len(device) == 1
device[0].rsc.adm_status = PHO_RSC_ADM_ST_LOCKED
devices.append(device[0])
......@@ -656,6 +658,8 @@ class DeviceOptHandler(BaseResourceOptHandler):
self.logger.error("Failed to lock device(s): %s",
env_error_format(err))
sys.exit(os.EX_DATAERR)
finally:
self.client.devices.unlock(devices)
self.logger.info("%d device(s) locked", len(devices))
......@@ -669,10 +673,18 @@ class DeviceOptHandler(BaseResourceOptHandler):
if not device:
self.logger.error("No such device: %s", serial)
sys.exit(os.EX_DATAERR)
if device[0].lock.lock != "" and not self.params.get('force'):
self.logger.error("Device %s is in use by %s.",
serial, device[0].lock.lock)
assert len(device) == 1
try:
self.client.devices.lock([device[0]])
except EnvironmentError as err:
self.logger.error("Failed to lock device '%s': %s", serial,
env_error_format(err))
continue
device = self.filter(serial)
assert len(device) == 1
if device[0].rsc.adm_status == PHO_RSC_ADM_ST_UNLOCKED:
self.logger.warn("Device %s is already unlocked", serial)
device[0].rsc.adm_status = PHO_RSC_ADM_ST_UNLOCKED
......@@ -696,6 +708,8 @@ class DeviceOptHandler(BaseResourceOptHandler):
except EnvironmentError as err:
self.logger.error("Failed to notify: %s", env_error_format(err))
sys.exit(os.EX_DATAERR)
finally:
self.client.devices.unlock(devices)
self.logger.info("%d device(s) unlocked", len(devices))
......@@ -764,6 +778,10 @@ class MediaOptHandler(BaseResourceOptHandler):
failed.append(uid)
continue
media = self.client.media.get(family=self.family, id=uid)
assert len(media) == 1
media = media[0]
# Update tags
try:
media.tags = tags
......@@ -834,15 +852,18 @@ class MediaOptHandler(BaseResourceOptHandler):
uids = NodeSet.fromlist(self.params.get('res'))
for uid in uids:
media = self.client.media.get(id=uid)
if media[0].lock.lock != "":
if self.params.get('force'):
self.logger.warn("Media %s is in use. Administrative "
"locking will not be effective "
"immediately", uid)
else:
self.logger.error("Media %s is in use by %s.",
uid, media[0].lock.lock)
continue
assert len(media) == 1
# Attempt to lock the media to avoid concurrent modifications
try:
self.client.media.lock([media[0]])
except EnvironmentError as err:
self.logger.error("Failed to lock media '%s': %s",
uid, env_error_format(err))
continue
media = self.client.media.get(id=uid)
assert len(media) == 1
media[0].rsc.adm_status = PHO_RSC_ADM_ST_LOCKED
results.append(media[0])
......@@ -857,6 +878,8 @@ class MediaOptHandler(BaseResourceOptHandler):
self.logger.error("Failed to lock one or more media(s): %s",
env_error_format(err))
sys.exit(os.EX_DATAERR)
finally:
self.client.media.unlock(results)
self.logger.info("%d media(s) locked" % len(results))
......@@ -866,11 +889,19 @@ class MediaOptHandler(BaseResourceOptHandler):
uids = NodeSet.fromlist(self.params.get('res'))
for uid in uids:
media = self.client.media.get(id=uid)
if media[0].lock.lock != "" and not self.params.get('force'):
self.logger.error("Media %s is in use by %s",
uid, media[0].lock.lock)
assert len(media) == 1
# Attempt to lock the media to avoid concurrent modifications
try:
self.client.media.lock([media[0]])
except EnvironmentError as err:
self.logger.error("Failed to lock media '%s': %s",
uid, env_error_format(err))
continue
media = self.client.media.get(id=uid)
assert len(media) == 1
if media[0].rsc.adm_status == PHO_RSC_ADM_ST_UNLOCKED:
self.logger.warn("Media %s is already unlocked", uid)
......@@ -887,6 +918,8 @@ class MediaOptHandler(BaseResourceOptHandler):
self.logger.error("Failed to unlock one or more media(s): %s",
env_error_format(err))
sys.exit(os.EX_DATAERR)
finally:
self.client.media.unlock(results)
self.logger.info("%d media(s) unlocked" % len(results))
......
......@@ -253,6 +253,14 @@ class DeviceManager(BaseObjectManager):
"""Proxy to manipulate devices."""
wrapped_class = DevInfo
wrapped_ident = 'device'
lock_owner_count = 0
def __init__(self, *args, **kwargs):
super(DeviceManager, self).__init__(*args, **kwargs)
self.lock_owner = "py-%.210s:%.8x:%.16x:%.16x" % (
gethostname(), os.getpid(), int(time.time()), self.lock_owner_count
)
self.lock_owner_count += 1
def add(self, device_type, device_path, locked=True):
"""
......@@ -283,6 +291,23 @@ class DeviceManager(BaseObjectManager):
"""Invoke device-specific DSS set method."""
return LIBPHOBOS.dss_device_set(hdl, obj, obj_cnt, opcode)
def lock(self, objects):
"""Lock all the devices associated with a given list of DeviceInfo"""
self._generic_lock_unlock(
objects, LIBPHOBOS.dss_device_lock, "Device locking failed",
self.lock_owner,
)
def unlock(self, objects, force=False):
"""Unlock all the devices associated with a given list of DeviceInfo.
If `force` is True, unlock the objects even if they were not locked
by this instance.
"""
self._generic_lock_unlock(
objects, LIBPHOBOS.dss_device_unlock, "Device unlocking failed",
None if force else self.lock_owner,
)
class MediaManager(BaseObjectManager):
"""Proxy to manipulate media."""
wrapped_class = MediaInfo
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment