diff --git a/README b/README index fe32bd2..50696d7 100644 --- a/README +++ b/README @@ -17,38 +17,48 @@ o- / ........................................................................... o- cluster .................................................................. [Clusters: 1] | o- ceph ..................................................................... [HEALTH_OK] | o- pools ................................................................... [Pools: 3] - | | o- ec ........................................ [(2+1), Commit: 0b/40G (0%), Used: 0b] + | | o- ec ....................................... [(2+2), Commit: 0b/40G (0%), Used: 24K] | | o- iscsi ..................................... [(x3), Commit: 0b/20G (0%), Used: 18b] - | | o- rbd ....................................... [(x3), Commit: 8G/20G (40%), Used: 5K] + | | o- rep ....................................... [(x3), Commit: 8G/20G (40%), Used: 5K] | o- topology ......................................................... [OSDs: 3,MONs: 3] o- disks ................................................................... [8G, Disks: 5] - | o- rbd ....................................................................... [rbd (8G)] - | o- disk_1 ............................................................... [disk_1 (1G)] - | o- disk_2 ............................................................... [disk_2 (2G)] - | o- disk_3 ............................................................... [disk_3 (2G)] - | o- disk_4 ............................................................... [disk_4 (1G)] - | o- disk_5 ............................................................... [disk_5 (2G)] + | o- rep ....................................................................... [rep (8G)] + | o- disk_1 ........................................................... [rep/disk_1 (1G)] + | o- disk_2 ........................................................... [rep/disk_2 (2G)] + | o- disk_3 ........................................................... [rep/disk_3 (2G)] + | o- disk_4 ........................................................ [rep+ec/disk_4 (1G)] + | o- disk_5 ........................................................ [rep+ec/disk_5 (2G)] o- iscsi-targets ............................................................. [Targets: 1] o- iqn.2003-01.com.redhat.iscsi-gw:ceph-gw1 ................... [Auth: CHAP, Gateways: 2] | o- disks ................................................................... [Disks: 1] - | | o- rbd/disk_1 .............................................. [Owner: rh7-gw2, Lun: 0] + | | o- rep/disk_1 .............................................. [Owner: rh7-gw2, Lun: 0] | o- gateways ..................................................... [Up: 2/2, Portals: 2] | | o- rh7-gw1 .................................................... [192.168.122.69 (UP)] | | o- rh7-gw2 .................................................... [192.168.122.14 (UP)] o- host-groups ........................................................... [Groups : 0] o- hosts ................................................ [Auth: ACL_ENABLED, Hosts: 1] | o- iqn.1994-05.com.redhat:rh7-client .......... [LOGGED-IN, Auth: CHAP, Disks: 1(2G)] - | o- lun 0 ......................................... [rbd.disk_1(2G), Owner: rh7-gw2] + | o- lun 0 ......................................... [rep/disk_1(2G), Owner: rh7-gw2] o- iqn.2003-01.com.redhat.iscsi-gw:ceph-gw2 ................... [Auth: None, Gateways: 2] o- disks ................................................................... [Disks: 1] - | o- rbd/disk_2 .............................................. [Owner: rh7-gw1, Lun: 0] + | o- rep/disk_2 .............................................. [Owner: rh7-gw1, Lun: 0] o- gateways ..................................................... [Up: 2/2, Portals: 2] | o- rh7-gw1 ................................................... [2006:ac81::1103 (UP)] | o- rh7-gw2 ................................................... [2006:ac81::1104 (UP)] o- host-groups ........................................................... [Groups : 0] o- hosts ................................................ [Auth: ACL_ENABLED, Hosts: 1] o- iqn.1994-05.com.redhat:rh7-client .......... [LOGGED-IN, Auth: None, Disks: 1(2G)] - o- lun 0 ......................................... [rbd.disk_2(2G), Owner: rh7-gw1] + o- lun 0 ......................................... [rep/disk_2(2G), Owner: rh7-gw1] + o- iqn.2003-01.com.redhat.iscsi-gw:ceph-gw3 ................... [Auth: None, Gateways: 2] + o- disks ................................................................... [Disks: 1] + | o- rep/disk_4 ........................................... [Owner: rh7-gw2, Lun: 0] + o- gateways ..................................................... [Up: 2/2, Portals: 2] + | o- rh7-gw1 ................................................... [2006:ac81::1103 (UP)] + | o- rh7-gw2 ................................................... [2006:ac81::1104 (UP)] + o- host-groups ........................................................... [Groups : 0] + o- hosts ................................................ [Auth: ACL_ENABLED, Hosts: 1] + o- iqn.1994-05.com.redhat:rh7-client .......... [LOGGED-IN, Auth: None, Disks: 1(1G)] + o- lun 0 ...................................... [rep/disk_4(1G), Owner: rh7-gw1] @@ -95,6 +105,23 @@ curl --user admin:admin -d ip_address=2006:ac81::1104 \ NOTE: please make sure both the IPv4 and IPv6 addresses are in the trusted ip list in iscsi-gateway.cfg. +Erasure Pool Support: +For the erasure pool, you need to specify the "datapool=" parameter to store the +data when creating a disk, and the "pool=" will contiue to be a replicated pool, which will +store the metadata only. + +curl --user admin:admin -d mode=create -d size=1g -d pool=rbd -d datapool=ec -d count=5 + -X PUT http://192.168.122.69:5000/api/disk/rbd/new0_ +curl --user admin:admin -d mode=create -d size=1g -d pool=rbd -d datapool=ec -d create_image=false + -X PUT http://192.168.122.69:5000/api/disk/rbd/new1 +curl --user admin:admin -X GET http://192.168.122.69:5000/api/disk/rbd/new2 +curl --user admin:admin -X DELETE http://192.168.122.69:5000/api/disk/rbd/new3 + +curl --user admin:admin -d mode=create + -X PUT http://192.168.122.69:5000/api/disksnap/rbd/image/new1 +curl --user admin:admin + -X DELETE http://192.168.122.69:5000/api/disksnap/rbd/image/new1 + ## Installation ### Via RPM diff --git a/ceph_iscsi_config/lun.py b/ceph_iscsi_config/lun.py index c169c3d..4626ce6 100644 --- a/ceph_iscsi_config/lun.py +++ b/ceph_iscsi_config/lun.py @@ -1,3 +1,4 @@ +import json import rados import rbd import re @@ -13,8 +14,8 @@ from ceph_iscsi_config.backstore import USER_RBD from ceph_iscsi_config.utils import (convert_2_bytes, gen_control_string, valid_size, get_pool_id, ip_addresses, - get_pools, get_rbd_size, this_host, - human_size, CephiSCSIError) + get_pools, get_rbd_size, run_shell_cmd, + human_size, CephiSCSIError, this_host) from ceph_iscsi_config.gateway_object import GWObject from ceph_iscsi_config.target import GWTarget from ceph_iscsi_config.client import GWClient, CHAP @@ -46,13 +47,14 @@ class RBDDev(object): ] } - def __init__(self, image, size, backstore, pool=None): + def __init__(self, image, size, backstore, pool=None, datapool=None): self.image = image self.size_bytes = convert_2_bytes(size) self.backstore = backstore if pool is None: pool = settings.config.pool self.pool = pool + self.datapool = datapool self.pool_id = get_pool_id(pool_name=self.pool) self.error = False self.error_msg = '' @@ -74,14 +76,14 @@ def create(self): self.image, self.size_bytes, features=RBDDev.default_features(self.backstore), - old_format=False) + old_format=False, + data_pool=self.datapool) except (rbd.ImageExists, rbd.InvalidArgument) as err: self.error = True - self.error_msg = ("Failed to create rbd image {} in " - "pool {} : {}".format(self.image, - self.pool, - err)) + self.error_msg = ("Failed to create rbd image {} in pool {}, " + "datapool {} : {}".format(self.image, self.pool, + self.datapool, err)) def delete(self): """ @@ -289,11 +291,12 @@ class LUN(GWObject): USER_RBD: TCMU_SETTINGS } - def __init__(self, logger, pool, image, size, allocating_host, + def __init__(self, logger, pool, datapool, image, size, allocating_host, backstore, backstore_object_name): self.logger = logger self.image = image self.pool = pool + self.datapool = datapool self.pool_id = 0 self.size_bytes = convert_2_bytes(size) self.config_key = '{}/{}'.format(self.pool, self.image) @@ -351,7 +354,7 @@ def remove_lun(self, preserve_image): if self.error: return - rbd_image = RBDDev(self.image, '0G', self.backstore, self.pool) + rbd_image = RBDDev(self.image, '0G', self.backstore, self.pool, self.datapool) if local_gw == self.allocating_host: # by using the allocating host we ensure the delete is not @@ -574,6 +577,44 @@ def activate(self): if client_err: raise CephiSCSIError(client_err) + def _erasure_pool_check(self): + # skip it and if no ecpool specified + if not self.datapool: + return True + + data, err = run_shell_cmd( + "ceph -n {name} --conf {conf} osd metadata --format=json". + format(name=settings.config.cluster_client_name, + conf=settings.config.cephconf)) + if err: + self.logger.error("Cannot get the objectstore type") + return False + bluestore = False + for _osd in json.loads(data): + store_type = _osd['osd_objectstore'] + self.logger.debug(f"objectstore type is ({store_type})") + if store_type == 'bluestore': + bluestore = True + break + + if not bluestore: + self.logger.error("Only bluestore is effective for datapool") + return False + + data, err = run_shell_cmd( + "ceph -n {name} --conf {conf} osd pool get {pool} allow_ec_overwrites -f json". + format(name=settings.config.cluster_client_name, + conf=settings.config.cephconf, pool=self.datapool)) + if err: + self.logger.error(f"Cannot get allow_ec_overwrites from pool ({self.pool})") + return False + result = json.loads(data) + if result['allow_ec_overwrites']: + self.logger.debug(f"erasure pool ({self.pool}) allow_ec_overwrites is enabled") + return True + self.logger.debug(f"erasure pool ({self.pool}) allow_ec_overwrites is disabled") + return False + def allocate(self, keep_dev_in_lio=True, in_wwn=None): """ Create image and add to LIO and config. @@ -583,6 +624,9 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None): :return: LIO storage object if successful and keep_dev_in_lio=True else None. """ + if not self._erasure_pool_check(): + return None + self.logger.debug("LUN.allocate starting, listing rbd devices") disk_list = RBDDev.rbd_list(pool=self.pool) self.logger.debug("rados pool '{}' contains the following - " @@ -593,7 +637,8 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None): "allocations is {}".format(local_gw, self.allocating_host)) - rbd_image = RBDDev(self.image, self.size_bytes, self.backstore, self.pool) + rbd_image = RBDDev(self.image, self.size_bytes, self.backstore, self.pool, + self.datapool) self.pool_id = rbd_image.pool_id # if the image required isn't defined, create it! @@ -703,6 +748,7 @@ def allocate(self, keep_dev_in_lio=True, in_wwn=None): disk_attr = {"wwn": wwn, "image": self.image, "pool": self.pool, + "datapool": self.datapool, "allocating_host": self.allocating_host, "pool_id": rbd_image.pool_id, "controls": self.controls, @@ -963,7 +1009,10 @@ def valid_disk(ceph_iscsi_config, logger, **kwargs): :param ceph_iscsi_config: Config object :param logger: logger object - :param image_id: (str) . format + :param pool: (str) pool name + :param datapool: (str) datapool name + :param image: (str) image name + :param size: (str) size :return: (str) either 'ok' or an error description """ @@ -993,12 +1042,15 @@ def valid_disk(ceph_iscsi_config, logger, **kwargs): config = ceph_iscsi_config.config + datapool = kwargs.get('datapool', None) disk_key = "{}/{}".format(kwargs['pool'], kwargs['image']) if mode in ['create', 'resize']: if kwargs['pool'] not in get_pools(): return "pool name is invalid" + if datapool and datapool not in get_pools(): + return "datapool name is invalid" if mode == 'create': if kwargs['size'] and not valid_size(kwargs['size']): @@ -1010,6 +1062,8 @@ def valid_disk(ceph_iscsi_config, logger, **kwargs): disk_regex = re.compile(r"^[a-zA-Z0-9\-_\.]+$") if not disk_regex.search(kwargs['pool']): return "Invalid pool name (use alphanumeric, '_', '.', or '-' characters)" + if datapool and not disk_regex.search(datapool): + return "Invalid datapool name (use alphanumeric, '_', '.', or '-' characters)" if not disk_regex.search(kwargs['image']): return "Invalid image name (use alphanumeric, '_', '.', or '-' characters)" @@ -1040,9 +1094,7 @@ def valid_disk(ceph_iscsi_config, logger, **kwargs): if mode in ["resize", "delete", "reconfigure"]: # disk must exist in the config if disk_key not in config['disks']: - return ("rbd {}/{} is not defined to the " - "configuration".format(kwargs['pool'], - kwargs['image'])) + return ("rbd {} is not defined to the configuration".format(disk_key)) if mode == 'resize': @@ -1231,13 +1283,15 @@ def define_luns(logger, config, target): for disk_key in pool_disks: pool, image_name = disk_key.split('/') + with rbd.Image(ioctx, image_name) as rbd_image: disk_config = config.config['disks'][disk_key] + datapool = disk_config.get('datapool', None) backstore = disk_config['backstore'] backstore_object_name = disk_config['backstore_object_name'] - lun = LUN(logger, pool, image_name, + lun = LUN(logger, pool, datapool, image_name, rbd_image.size(), local_gw, backstore, backstore_object_name) diff --git a/gwcli/client.py b/gwcli/client.py index 040bbe3..5ee6bb2 100644 --- a/gwcli/client.py +++ b/gwcli/client.py @@ -472,14 +472,14 @@ def get_srtd_names(lun_list): return [rbd_name for rbd_name, lun_id in srtd_luns] - def ui_command_disk(self, action='add', disk=None, size=None): + def ui_command_disk(self, action='add', disk=None, size=None, datapool=None): """ Disks can be added or removed from the client one at a time using the 'disk' sub-command. Note that if the disk does not currently exist in the configuration, the cli will attempt to create it for you. e.g. - disk add + disk add [datapool] disk remove Adding a disk will result in the disk occupying the client's next @@ -545,7 +545,7 @@ def ui_command_disk(self, action='add', disk=None, size=None): self.logger.error("Invalid format. Use pool_name/disk_name") return - rc = ui_disks.create_disk(pool=pool, image=image, size=size) + rc = ui_disks.create_disk(pool=pool, image=image, size=size, datapool=datapool) if rc == 0: self.logger.debug("disk auto-define successful") else: diff --git a/gwcli/storage.py b/gwcli/storage.py index 1488856..5776fd1 100644 --- a/gwcli/storage.py +++ b/gwcli/storage.py @@ -53,6 +53,14 @@ def __init__(self, parent): self.scan_queue = None self.scan_mutex = None + def _get_pool_type(self, pool): + root = self.get_ui_root() + pools = root.ceph.cluster.pools + pool_object = pools.pool_lookup.get(pool, None) + if pool_object: + return pool_object.type + return None + def _get_disk_meta(self, cluster_ioctx, disk_meta): """ Use the provided cluster context to take an rbd image name from the @@ -153,18 +161,18 @@ def reset(self): for child in children: self.remove_child(child) - def ui_command_attach(self, pool=None, image=None, backstore=None, wwn=None): + def ui_command_attach(self, pool=None, image=None, backstore=None, wwn=None, datapool=None): """ Assign a previously created RBD image to the gateway(s) The attach command supports two request formats; - Long format : attach pool= image= + Long format : attach pool= image= datapool= Short format : attach pool/image e.g. attach pool=rbd image=testimage - attach rbd.testimage + attach rbd/testimage The syntax of each parameter is as follows; pool : Pool and image name may contain a-z, A-Z, 0-9, '_', or '-' @@ -187,10 +195,11 @@ def ui_command_attach(self, pool=None, image=None, backstore=None, wwn=None): self.logger.debug("CMD: /disks/ attach pool={} " "image={}".format(pool, image)) - self.create_disk(pool=pool, image=image, create_image=False, backstore=backstore, wwn=wwn) + self.create_disk(pool=pool, datapool=datapool, image=image, create_image=False, + backstore=backstore, wwn=wwn) - def ui_command_create(self, pool=None, image=None, size=None, backstore=None, wwn=None, - count=1): + def ui_command_create(self, pool=None, datapool=None, image=None, size=None, backstore=None, + wwn=None, count=1): """ Create a RBD image and assign to the gateway(s). @@ -205,7 +214,8 @@ def ui_command_create(self, pool=None, image=None, size=None, backstore=None, ww The syntax of each parameter is as follows; pool : Pool and image name may contain a-z, A-Z, 0-9, '_', or '-' - image characters. + datapool: Data pool name for erasure code pool may contain a-z, A-Z, 0-9, '_', or '-' + image : characters. size : integer, suffixed by the allocation unit - either m/M, g/G or t/T representing the MB/GB/TB [1] backstore : lio backstore @@ -260,8 +270,8 @@ def ui_command_create(self, pool=None, image=None, size=None, backstore=None, ww self.logger.debug("CMD: /disks/ create pool={} " "image={} size={} " "count={} ".format(pool, image, size, count)) - self.create_disk(pool=pool, image=image, size=size, count=count, backstore=backstore, - wwn=wwn) + self.create_disk(pool=pool, datapool=datapool, image=image, size=size, count=count, + backstore=backstore, wwn=wwn) def _valid_pool(self, pool=None): """ @@ -277,15 +287,15 @@ def _valid_pool(self, pool=None): pools = root.ceph.cluster.pools pool_object = pools.pool_lookup.get(pool, None) if pool_object: - if pool_object.type == 'replicated': - self.logger.debug("pool '{}' is ok to use".format(pool)) + if pool_object.type in ['replicated', 'erasure']: + self.logger.debug(f"pool '{pool}' is ok to use") return True - self.logger.error("Invalid pool ({}). Must already exist and " - "be replicated".format(pool)) + self.logger.error(f"Invalid pool ({pool}), the type is ({pool_object.type})." + " Must already exist and be erasure or replicated") return False - def create_disk(self, pool=None, image=None, size=None, count=1, + def create_disk(self, pool=None, datapool=None, image=None, size=None, count=1, parent=None, create_image=True, backstore=None, wwn=None): rc = 0 @@ -300,14 +310,15 @@ def create_disk(self, pool=None, image=None, size=None, count=1, if not self._valid_pool(pool): return - self.logger.debug("Creating/mapping disk {}/{}".format(pool, - image)) + self.logger.debug("Creating/mapping disk {}, datapool {}".format(disk_key, + datapool)) # make call to local api server's disk endpoint - disk_api = '{}://localhost:{}/api/disk/{}'.format(self.http_mode, - settings.config.api_port, - disk_key) - api_vars = {'pool': pool, 'owner': local_gw, + disk_api = ('{}://localhost:{}/api/disk/' + '{}'.format(self.http_mode, + settings.config.api_port, + disk_key)) + api_vars = {'pool': pool, 'datapool': datapool, 'owner': local_gw, 'count': count, 'mode': 'create', 'create_image': 'true' if create_image else 'false', 'backstore': backstore, 'wwn': wwn} @@ -333,12 +344,13 @@ def create_disk(self, pool=None, image=None, size=None, count=1, else: disk_key = "{}/{}".format(pool, image) + api_vars = {'datapool': datapool} disk_api = ('{}://localhost:{}/api/disk/' '{}'.format(self.http_mode, settings.config.api_port, disk_key)) - api = APIRequest(disk_api) + api = APIRequest(disk_api, data=api_vars) api.get() if api.response.status_code == 200: @@ -603,6 +615,14 @@ def __init__(self, parent, pool, pool_disks_config, disks_meta=None): self.disks_meta = disks_meta self.refresh() + def _get_pool_type(self, pool): + root = self.get_ui_root() + pools = root.ceph.cluster.pools + pool_object = pools.pool_lookup.get(pool, None) + if pool_object: + return pool_object.type + return None + def refresh(self): for pool_disk_config in self.pool_disks_config: disk_id = '{}/{}'.format(pool_disk_config['pool'], pool_disk_config['image']) @@ -645,6 +665,8 @@ def __init__(self, parent, image_id, image_config, size=None, UINode.__init__(self, self.rbd_image, parent) + self.datapool = image_config.get('datapool', None) + self.is_erasure = True if self.datapool is not None else False self.image_id = image_id self.size = 0 self.size_h = '' @@ -749,7 +771,11 @@ def summary(self): if state != "Online": status = False - msg = [self.image_id, "({}, {})".format(state, self.size_h)] + if self.datapool: + _image_id = "{}+{}/{}".format(self.pool, self.datapool, self.rbd_image) + else: + _image_id = self.image_id + msg = [_image_id, "({}, {})".format(state, self.size_h)] return " ".join(msg), status @@ -954,11 +980,10 @@ def snapshot(self, action, name): self.logger.debug("Issuing snapshot {} request".format(action)) disk_api = ('{}://localhost:{}/api/' - 'disksnap/{}/{}/{}'.format(self.http_mode, - settings.config.api_port, - self.pool, - self.rbd_image, - name)) + 'disksnap/{}/{}'.format(self.http_mode, + settings.config.api_port, + self.image_id, + name)) if action == 'delete': api = APIRequest(disk_api) diff --git a/rbd-target-api.py b/rbd-target-api.py index 49ea5a4..7760976 100644 --- a/rbd-target-api.py +++ b/rbd-target-api.py @@ -875,6 +875,7 @@ def _target_disk(target_iqn=None): disk = request.form.get('disk') pool, image = disk.split('/', 1) disk_config = config.config['disks'][disk] + datapool = disk_config.get('datapool', None) backstore = disk_config['backstore'] backstore_object_name = disk_config['backstore_object_name'] @@ -897,6 +898,7 @@ def _target_disk(target_iqn=None): size = rbd_image.current_size lun = LUN(logger, pool, + datapool, image, size, allocating_host, @@ -925,6 +927,7 @@ def _target_disk(target_iqn=None): lun = LUN(logger, pool, + datapool, image, 0, purge_host, @@ -985,7 +988,6 @@ def disk(pool, image): :param image: (str) rbd image name :param mode: (str) 'create' or 'resize' the rbd image :param size: (str) the size of the rbd image - :param pool: (str) the pool name the rbd image will be in :param count: (str) the number of images will be created :param owner: (str) the owner of the rbd image :param controls: (JSON dict) valid control overrides @@ -997,8 +999,8 @@ def disk(pool, image): Examples: curl --user admin:admin -d mode=create -d size=1g -d pool=rbd -d count=5 -X PUT http://192.168.122.69:5000/api/disk/rbd/new0_ - curl --user admin:admin -d mode=create -d size=10g -d pool=rbd -d create_image=false - -X PUT http://192.168.122.69:5000/api/disk/rbd/new1 + curl --user admin:admin -d mode=create -d size=1g -d pool=rbd -d datapool=ec + -d create_image=false -X PUT http://192.168.122.69:5000/api/disk/rbd/new1 curl --user admin:admin -X GET http://192.168.122.69:5000/api/disk/rbd/new2 curl --user admin:admin -X DELETE http://192.168.122.69:5000/api/disk/rbd/new3 """ @@ -1007,6 +1009,7 @@ def disk(pool, image): logger.debug("this host is {}".format(local_gw)) image_id = '{}/{}'.format(pool, image) + datapool = request.form.get('datapool', None) config.refresh() @@ -1062,7 +1065,7 @@ def disk(pool, image): logger.debug("{} controls {}".format(mode, controls)) wwn = request.form.get('wwn') - disk_usable = LUN.valid_disk(config, logger, pool=pool, + disk_usable = LUN.valid_disk(config, logger, pool=pool, datapool=datapool, image=image, size=size, mode=mode, count=count, controls=controls, backstore=backstore, wwn=wwn) @@ -1091,7 +1094,7 @@ def disk(pool, image): return jsonify(message="Image {} does not exist".format(image_id)), 400 if mode == 'reconfigure': - resp_text, resp_code = lun_reconfigure(image_id, controls, backstore) + resp_text, resp_code = lun_reconfigure(image_id, controls, backstore, datapool) if resp_code == 200: return jsonify(message="lun reconfigured: {}".format(resp_text)), resp_code else: @@ -1107,6 +1110,7 @@ def disk(pool, image): sfx) api_vars = {'pool': pool, + 'datapool': datapool, 'image': image, 'size': size, 'owner': local_gw, @@ -1130,13 +1134,14 @@ def disk(pool, image): else: # this is a DELETE request - disk_usable = LUN.valid_disk(config, logger, mode='delete', - pool=pool, image=image, backstore=backstore) + disk_usable = LUN.valid_disk(config, logger, mode='delete', pool=pool, + datapool=datapool, image=image, backstore=backstore) if disk_usable != 'ok': return jsonify(message=disk_usable), 400 api_vars = { + 'datapool': datapool, 'purge_host': local_gw, 'preserve_image': request.form.get('preserve_image'), 'backstore': backstore @@ -1163,10 +1168,12 @@ def _disk(pool, image): Disks can be created and added to each gateway, or deleted through this call :param pool: (str) pool name + :param datapool: (str) datapool name for erasure :param image: (str) image name **RESTRICTED** """ + datapool = request.form.get('datapool', None) image_id = '{}/{}'.format(pool, image) config.refresh() @@ -1214,6 +1221,7 @@ def _disk(pool, image): lun = LUN(logger, str(request.form['pool']), + datapool, image, str(request.form['size']), str(request.form['owner']), @@ -1252,11 +1260,11 @@ def _disk(pool, image): return jsonify(message="LUN {} failure".format(mode)), 500 if 'owner' not in disk: - msg = "Disk {}/{} must be assigned to a target".format(disk['pool'], disk['image']) + msg = "Disk {} must be assigned to a target".format(image_id) logger.error("LUN owner not defined - {}".format(msg)) return jsonify(message="LUN {} failure - {}".format(mode, msg)), 400 - lun = LUN(logger, pool, image, size, disk['owner'], + lun = LUN(logger, pool, datapool, image, size, disk['owner'], backstore, backstore_object_name) if mode == 'deactivate': try: @@ -1283,13 +1291,13 @@ def _disk(pool, image): purge_host = request.form['purge_host'] preserve_image = request.form.get('preserve_image') == 'true' logger.debug("delete request for disk image '{}'".format(image_id)) - pool, image = image_id.split('/', 1) disk_config = config.config['disks'][image_id] backstore = disk_config['backstore'] backstore_object_name = disk_config['backstore_object_name'] lun = LUN(logger, pool, + datapool, image, 0, purge_host, @@ -1319,7 +1327,7 @@ def _disk(pool, image): return jsonify(message="LUN removed"), 200 -def lun_reconfigure(image_id, controls, backstore): +def lun_reconfigure(image_id, controls, backstore, datapool): logger.debug("lun reconfigure request") config.refresh() @@ -1345,11 +1353,10 @@ def lun_reconfigure(image_id, controls, backstore): return "failed to deactivate disk: {}".format(resp_text), resp_code pool_name, image_name = image_id.split('/', 1) - rbd_image = RBDDev(image_name, 0, backstore, pool_name) size = rbd_image.current_size - lun = LUN(logger, pool_name, image_name, size, disk['owner'], + lun = LUN(logger, pool_name, datapool, image_name, size, disk['owner'], disk['backstore'], disk['backstore_object_name']) for k, v in controls.items(): @@ -1367,7 +1374,7 @@ def lun_reconfigure(image_id, controls, backstore): api_vars['controls'] = json.dumps(controls) # activate disk - api_vars['mode'] = 'activate' + api_vars = {'mode': 'activate'} logger.debug("activating disk") activate_resp_text, activate_resp_code = call_api(gateways, '_disk', @@ -1398,15 +1405,16 @@ def disksnap(pool, image, name): each gateway. Processing is done serially: rollback is done locally first, then other gateways. Other actions are only performed locally. - :param image_id: (str) rbd image name of the format pool/image + :param pool: (str) pool name + :param image: (str) rbd image name :param name: (str) rbd snapshot name :param mode: (str) 'create' or 'rollback' the rbd snapshot **RESTRICTED** Examples: curl --user admin:admin -d mode=create - -X PUT http://192.168.122.69:5000/api/disksnap/rbd.image/new1 + -X PUT http://192.168.122.69:5000/api/disksnap/rbd/image/new1 curl --user admin:admin - -X DELETE http://192.168.122.69:5000/api/disksnap/rbd.image/new1 + -X DELETE http://192.168.122.69:5000/api/disksnap/rbd/image/new1 """ if not valid_snapshot_name(name): @@ -1424,8 +1432,7 @@ def disksnap(pool, image, name): if mode == 'create': resp_text, resp_code = _disksnap_create(pool, image, name) elif mode == 'rollback': - resp_text, resp_code = _disksnap_rollback(image_id, pool, - image, name) + resp_text, resp_code = _disksnap_rollback(pool, image, name) else: logger.debug("snapshot request rejected due to invalid mode") resp_text = "mode is invalid" @@ -1481,9 +1488,11 @@ def _disksnap_delete(pool_name, image_name, name): return resp_text, resp_code -def _disksnap_rollback(image_id, pool_name, image_name, name): +def _disksnap_rollback(pool, image, name): logger.debug("snapshot rollback request") + image_id = '{}/{}'.format(pool, image) + disk = config.config['disks'].get(image_id, None) if not disk: return "rbd image {} not found".format(image_id), 404 @@ -1509,8 +1518,8 @@ def _disksnap_rollback(image_id, pool_name, image_name, name): try: with rados.Rados(conffile=settings.config.cephconf, name=settings.config.cluster_client_name) as cluster, \ - cluster.open_ioctx(pool_name) as ioctx, \ - rbd.Image(ioctx, image_name) as image: + cluster.open_ioctx(pool) as ioctx, \ + rbd.Image(ioctx, image) as image: try: logger.debug("rolling back to snapshot")