Skip to content
Open
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
48 changes: 38 additions & 10 deletions drivers/linstorvolumemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

from sm_typing import override
from sm_typing import Any, Dict, override

import errno
import json
Expand Down Expand Up @@ -302,7 +302,8 @@ class LinstorVolumeManager(object):
'_base_group_name', '_group_name', '_ha_group_name',
'_volumes', '_storage_pools', '_storage_pools_time',
'_kv_cache', '_resource_cache', '_volume_info_cache',
'_kv_cache_dirty', '_resource_cache_dirty', '_volume_info_cache_dirty'
'_kv_cache_dirty', '_resource_cache_dirty', '_volume_info_cache_dirty',
'_resources_info_cache',
)

DEV_ROOT_PATH = DRBD_BY_RES_PATH
Expand Down Expand Up @@ -392,7 +393,7 @@ def __init__(
"""
Create a new LinstorVolumeManager object.
:param str uri: URI to communicate with the LINSTOR controller.
:param str group_name: The SR goup name to use.
:param str group_name: The SR group name to use.
:param bool repair: If true we try to remove bad volumes due to a crash
or unexpected behavior.
:param function logger: Function to log messages.
Expand Down Expand Up @@ -420,13 +421,14 @@ def __init__(
self._volumes = set()
self._storage_pools_time = 0

# To increate performance and limit request count to LINSTOR services,
# To increase performance and limit request count to LINSTOR services,
# we use caches.
self._kv_cache = self._create_kv_cache()
self._resource_cache = None
self._resource_cache_dirty = True
self._volume_info_cache = None
self._volume_info_cache_dirty = True
self._resources_info_cache = None
self._build_volumes(repair=repair)

@property
Expand Down Expand Up @@ -686,6 +688,13 @@ def destroy_volume(self, volume_uuid):
self._ensure_volume_exists(volume_uuid)
self.ensure_volume_is_not_locked(volume_uuid)

is_volume_in_use = any(node["in-use"] for node in self.get_resource_info(volume_uuid)["nodes"].values())
if is_volume_in_use:
raise LinstorVolumeManagerError(
f"Could not destroy volume `{volume_uuid}` as it is currently in use",
LinstorVolumeManagerError.ERR_VOLUME_DESTROY
)

# Mark volume as destroyed.
volume_properties = self._get_volume_properties(volume_uuid)
volume_properties[self.PROP_NOT_EXISTS] = self.STATE_NOT_EXISTS
Expand Down Expand Up @@ -1158,7 +1167,7 @@ def get_usage_states(self, volume_uuid):
"""
Check if a volume is currently used.
:param str volume_uuid: The volume uuid to check.
:return: A dictionnary that contains states.
:return: A dictionary that contains states.
:rtype: dict(str, bool or None)
"""

Expand All @@ -1176,22 +1185,22 @@ def get_volume_openers(self, volume_uuid):
"""
Get openers of a volume.
:param str volume_uuid: The volume uuid to monitor.
:return: A dictionnary that contains openers.
:return: A dictionary that contains openers.
:rtype: dict(str, obj)
"""
return get_all_volume_openers(self.get_volume_name(volume_uuid), '0')

def get_volumes_with_name(self):
"""
Give a volume dictionnary that contains names actually owned.
Give a volume dictionary that contains names actually owned.
:return: A volume/name dict.
:rtype: dict(str, str)
"""
return self._get_volumes_by_property(self.REG_VOLUME_NAME)

def get_volumes_with_info(self):
"""
Give a volume dictionnary that contains VolumeInfos.
Give a volume dictionary that contains VolumeInfos.
:return: A volume/VolumeInfo dict.
:rtype: dict(str, VolumeInfo)
"""
Expand All @@ -1215,7 +1224,7 @@ def get_volumes_with_info(self):

def get_volumes_with_metadata(self):
"""
Give a volume dictionnary that contains metadata.
Give a volume dictionary that contains metadata.
:return: A volume/metadata dict.
:rtype: dict(str, dict)
"""
Expand Down Expand Up @@ -1683,6 +1692,9 @@ def get_resources_info(self):
Give all resources of current group name.
:rtype: dict(str, list)
"""
if self._resources_info_cache and not self._resource_cache_dirty:
return self._resources_info_cache

resources = {}
resource_list = self._get_resource_cache()
volume_names = self.get_volumes_with_name()
Expand Down Expand Up @@ -1739,7 +1751,23 @@ def get_resources_info(self):
if resource:
resource['uuid'] = volume_uuid

return resources
self._resources_info_cache = resources
return self._resources_info_cache

def get_resource_info(self, volume_uuid: str) -> Dict[str, Any]:
"""
Give all resource info related to provided UUID in the current group.
:param volume_uuid str: volume uuid to search for
:rtype: dict(str, any)
"""
for volume in self.get_resources_info().values():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cost can be really important. I guess we can extract a few logic of get_resources_info to reduce this expense.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've introduced some caching mechanism to mitigate this issue :)

if volume["uuid"] == volume_uuid:
return volume

raise LinstorVolumeManagerError(
f"Could not find info about volume `{volume_uuid}`",
LinstorVolumeManagerError.ERR_VOLUME_NOT_EXISTS
)

def get_database_path(self):
"""
Expand Down