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

lun: try to recovery the config from the LIO #254

Open
wants to merge 3 commits into
base: main
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
117 changes: 88 additions & 29 deletions ceph_iscsi_config/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@
import json
import traceback

from ceph_iscsi_config.backstore import USER_RBD
from ceph_iscsi_config.backstore import USER_RBD, lookup_storage_object_by_disk
import ceph_iscsi_config.settings as settings
from ceph_iscsi_config.utils import encryption_available, get_time
from ceph_iscsi_config.utils import encryption_available, get_time, get_rbd_size, this_host


class ConfigTransaction(object):

def __init__(self, cfg_type, element_name, txn_action='add', initial_value=None):
def __init__(self, cfg_type, element_name, item_name, txn_action='add', initial_value=None):

self.type = cfg_type
self.action = txn_action
self.item_name = element_name
self.element_name = element_name
self.item_name = item_name

init_state = {} if initial_value is None else initial_value
self.item_content = init_state
self.content = init_state

def __repr__(self):
return str(self.__dict__)
Expand Down Expand Up @@ -188,8 +189,53 @@ def needs_hostname_update(self):

return True

# if the 'recovery' are set, that means the image size
# have changed and we need to update the local LIO
# device size.
def try_to_update_lio_dev_size(self):
if self.config['version'] < 11:
return

now = get_time()
local_gw = this_host()
for disk_key, disk in self.config['disks'].items():
if disk.get('recovery', []) == []:
continue

if local_gw not in disk['recovery']:
continue

so = lookup_storage_object_by_disk(self, disk_key)
if not so:
continue

try:
size = get_rbd_size(disk['pool'], disk['image'])
except Exception as err:
self.logger.warn("Failed to get image size, "
"{}".format(self.config_key))
continue

# most likely
if so.size == size:
continue
elif so.size < size:
stg_object.set_attribute("dev_size", self.size_bytes)
else:
self.logger.warn("Image size({}) is smaller than LIO device size"
"({})".format(size, so.size))

# update the other items
disk['recovery'].remove(local_gw)
self.update_sub_item('disks', self.config_key,
'recovery', disk['recovery'])
self.update_sub_item('disks', self.config_key,
'updated', now)
self.commit("retain")

def _upgrade_config(self):
update_hostname = self.needs_hostname_update()
self.try_to_update_lio_dev_size()

if self.config['version'] >= Config.seed_config['version'] and not update_hostname:
return
Expand Down Expand Up @@ -527,7 +573,7 @@ def add_item(self, cfg_type, element_name=None, initial_value=None):
if isinstance(init_state, str) and 'created' not in self.config[cfg_type]:
self.config[cfg_type]['created'] = now
# add a separate transaction to capture the creation date to the section
txn = ConfigTransaction(cfg_type, 'created', initial_value=now)
txn = ConfigTransaction(cfg_type, 'created', None, initial_value=now)
self.txn_list.append(txn)

else:
Expand All @@ -540,7 +586,7 @@ def add_item(self, cfg_type, element_name=None, initial_value=None):
self.logger.debug("(Config.add_item) config updated to {}".format(self.config))
self.changed = True

txn = ConfigTransaction(cfg_type, element_name, initial_value=init_state)
txn = ConfigTransaction(cfg_type, element_name, None, initial_value=init_state)
self.txn_list.append(txn)

def del_item(self, cfg_type, element_name):
Expand All @@ -551,44 +597,48 @@ def del_item(self, cfg_type, element_name):
del self.config[cfg_type]
self.logger.debug("(Config.del_item) config updated to {}".format(self.config))

txn = ConfigTransaction(cfg_type, element_name, 'delete')
txn = ConfigTransaction(cfg_type, element_name, None, 'delete')
self.txn_list.append(txn)

def update_item(self, cfg_type, element_name, element_value):
def update_sub_item(self, cfg_type, element_name, item_name, value):
now = get_time()

if element_name:
current_values = self.config[cfg_type][element_name]
if item_name:
current_values = self.config[cfg_type][element_name][item_name]
self.config[cfg_type][element_name][item_name] = value
else:
current_values = self.config[cfg_type][element_name]
if isinstance(value, dict):
merged = current_values.copy()
new_dict = value
new_dict['updated'] = now
merged.update(new_dict)
value = merged.copy()
self.config[cfg_type][element_name] = value
self.logger.debug("prior to update, item contains {}".format(current_values))
if isinstance(element_value, dict):
merged = current_values.copy()
new_dict = element_value
new_dict['updated'] = now
merged.update(new_dict)
element_value = merged.copy()

self.config[cfg_type][element_name] = element_value
else:
# update to a root level config element, like version
self.config[cfg_type] = element_value
self.config[cfg_type] = value

self.logger.debug("(Config.update_item) config is {}".format(self.config))
self.changed = True
self.logger.debug("update_item: type={}, item={}, update={}".format(
cfg_type, element_name, element_value))
cfg_type, element_name, value))

txn = ConfigTransaction(cfg_type, element_name, 'add')
txn.item_content = element_value
txn = ConfigTransaction(cfg_type, element_name, item_name, 'add', value)
self.txn_list.append(txn)

def update_item(self, cfg_type, element_name, value):
self.update_sub_item(cfg_type, element_name, None, value)

def set_item(self, cfg_type, element_name, element_value):
self.logger.debug("(Config.update_item) config is {}".format(self.config))
self.changed = True
self.logger.debug("update_item: type={}, item={}, update={}".format(
cfg_type, element_name, element_value))

txn = ConfigTransaction(cfg_type, element_name, 'add')
txn.item_content = element_value
txn = ConfigTransaction(cfg_type, element_name, None, 'add', element_value)
self.txn_list.append(txn)

def _commit_rbd(self, post_action):
Expand All @@ -607,14 +657,23 @@ def _commit_rbd(self, post_action):

self.logger.debug("_commit_rbd transaction shows {}".format(txn))
if txn.action == 'add': # add's and updates
if txn.item_name:
current_config[txn.type][txn.item_name] = txn.item_content
if txn.element_name:
if txn.item_name:
# for the 'update' item it's monotone increasing
if txn.item_name == "updated":
cur_time = current_config[txn.type][txn.element_name][txn.item_name]
if cur_time < txn.content:
current_config[txn.type][txn.element_name][txn.item_name] = txn.content
else:
current_config[txn.type][txn.element_name][txn.item_name] = txn.content
else:
current_config[txn.type][txn.element_name] = txn.content
else:
current_config[txn.type] = txn.item_content
current_config[txn.type] = txn.content

elif txn.action == 'delete':
if txn.item_name:
del current_config[txn.type][txn.item_name]
if txn.element_name:
del current_config[txn.type][txn.element_name]
else:
del current_config[txn.type]
else:
Expand Down
68 changes: 42 additions & 26 deletions ceph_iscsi_config/lun.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,38 @@ def activate(self):
if client_err:
raise CephiSCSIError(client_err)

def add_disk_item(self, wwn, pool_id, recovery=False):
# rbd image is OK to use, so ensure it's in the config
# object
if self.config_key not in self.config.config['disks']:
self.config.add_item('disks', self.config_key)

if recovery:
gateways = self.config.config['gateways'].keys()
recovery_count = [gw for gw in gateways if gw != this_host()]
else:
recovery_count = []

# update the other items
disk_attr = {"wwn": wwn,
"image": self.image,
"pool": self.pool,
"allocating_host": self.allocating_host,
"pool_id": pool_id,
"controls": self.controls,
"backstore": self.backstore,
"backstore_object_name": self.backstore_object_name,
"recovery": recovery_count}

self.config.update_item('disks',
self.config_key,
disk_attr)

self.logger.debug("(LUN.allocate) registered '{}' with "
"wwn '{}' with the config "
"object".format(self.image,
wwn))

def allocate(self, keep_dev_in_lio=True, in_wwn=None):
"""
Create image and add to LIO and config.
Expand Down Expand Up @@ -604,7 +636,6 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None):
rbd_image.create()

if not rbd_image.error:
self.config.add_item('disks', self.config_key)
self.logger.info("(LUN.allocate) created {}/{} "
"successfully".format(self.pool,
self.image))
Expand All @@ -630,13 +661,7 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None):
else:
# requested image is already defined to ceph

if rbd_image.valid:
# rbd image is OK to use, so ensure it's in the config
# object
if self.config_key not in self.config.config['disks']:
self.config.add_item('disks', self.config_key)

else:
if not rbd_image.valid:
# rbd image is not valid for export, so abort
self.error = True
features = ','.join(RBDDev.unsupported_features_list[self.backstore])
Expand Down Expand Up @@ -700,23 +725,7 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None):
if self.error:
return None

disk_attr = {"wwn": wwn,
"image": self.image,
"pool": self.pool,
"allocating_host": self.allocating_host,
"pool_id": rbd_image.pool_id,
"controls": self.controls,
"backstore": self.backstore,
"backstore_object_name": self.backstore_object_name}

self.config.update_item('disks',
self.config_key,
disk_attr)

self.logger.debug("(LUN.allocate) registered '{}' with "
"wwn '{}' with the config "
"object".format(self.image,
wwn))
self.add_disk_item(wwn, rbd_image.pool_id)
self.logger.info("(LUN.allocate) added '{}/{}' to LIO and"
" config object".format(self.pool,
self.image))
Expand Down Expand Up @@ -780,8 +789,15 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None):
self.logger.critical(self.error_msg)
return None

# try to recovery the config from LIO
if local_gw == self.allocating_host:
# lun is now in LIO, time for some housekeeping :P
wwn = so._get_wwn()
self.add_disk_item(wwn, rbd_image.pool_id, True)
self.num_changes += 1

self.logger.debug("config meta data for this disk is "
"{}".format(self.config.config['disks'][self.config_key]))
"{}".format(self.config.config['disks'].get(self.config_key)))

# the owning host for an image is the only host that commits to the
# config
Expand Down