Esempio n. 1
0
 def summary(self):
     total_bytes = 0
     total_disks = 0
     for pool in self.children:
         total_disks += len(pool.children)
         for disk in pool.children:
             total_bytes += disk.size
     return '{}, Disks: {}'.format(human_size(total_bytes),
                                   total_disks), None
Esempio n. 2
0
    def __init__(self,
                 parent,
                 image_id,
                 image_config,
                 size=None,
                 features=None,
                 snapshots=None):
        """
        Create a disk entry under the Disks subtree
        :param parent: parent object (instance of the Disks class)
        :param image_id: key used in the config object for this rbd image
               (pool.image_name) - str
        :param image_config: meta data for this image
        :return:
        """
        self.pool, self.rbd_image = image_id.split('.', 1)

        UINode.__init__(self, image_id, parent)

        self.image_id = image_id
        self.size = 0
        self.size_h = ''
        self.features = 0
        self.feature_list = []
        self.snapshots = []
        self.backstore = image_config['backstore']
        self.controls = {}
        self.control_values = {}
        self.ceph_cluster = self.parent.parent.ceph.local_ceph.name

        disk_map = self.parent.disk_info
        if image_id not in disk_map:
            disk_map[image_id] = {}

        if image_id not in self.parent.disk_lookup:
            self.parent.disk_lookup[image_id] = self

        self._apply_config(image_config)

        if not size:
            # Size/features are not stored in the config, since it can be changed
            # outside of this tool-chain, so we get them dynamically
            self._refresh_config()
        else:
            # size and features have been passed in from the Disks.refresh
            # method
            self.exists = True
            self.size = size
            self.size_h = human_size(self.size)
            self.features = features
            self.feature_list = self._get_features()
            self._parse_snapshots(snapshots)

        # update the parent's disk info map
        disk_map = self.parent.disk_info
        disk_map[self.image_id]['size'] = self.size
        disk_map[self.image_id]['size_h'] = self.size_h
Esempio n. 3
0
 def _get_meta_data_tcmu(self):
     """
     query the rbd to get the features and size of the rbd
     :return:
     """
     self.logger.debug("Refreshing image metadata")
     with rados.Rados(conffile=settings.config.cephconf) as cluster:
         with cluster.open_ioctx(self.pool) as ioctx:
             with rbd.Image(ioctx, self.image) as rbd_image:
                 self.size = rbd_image.size()
                 self.size_h = human_size(self.size)
                 self.features = rbd_image.features()
                 self.feature_list = self._get_features()
                 self._parse_snapshots(list(rbd_image.list_snaps()))
Esempio n. 4
0
    def ui_command_info(self):

        color = {
            "HEALTH_OK": "green",
            "HEALTH_WARN": "yellow",
            "HEALTH_ERR": "red"
        }

        status = self.ceph_status
        # backward compatibility (< octopus)
        if 'osdmap' in status['osdmap']:
            osdmap = status['osdmap']['osdmap']
        else:
            osdmap = status['osdmap']
        output = "Cluster name: {}\n".format(self.cluster_name)
        output += "Ceph version: {}\n".format(self.version)
        output += "Health      : {}\n".format(self.health_status)
        if self.health_status != 'HEALTH_OK':
            output += " - {}\n".format(','.join(self.health_list))

        # backward compatibility (< octopus)
        if 'mons' in status['monmap']:
            num_mons = len(status['monmap']['mons'])
        else:
            num_mons = status['monmap']['num_mons']
        num_quorum = len(status['quorum_names'])
        num_out_of_quorum = num_mons - num_quorum
        q_str = "quorum {}, out of quorum: {}".format(num_quorum,
                                                      num_out_of_quorum)
        output += "\nMONs : {:>4} ({})\n".format(self.topology.num_mons, q_str)
        output += "OSDs : {:>4} ({} up, {} in)\n".format(
            self.topology.num_osds, osdmap['num_up_osds'],
            osdmap['num_in_osds'])
        output += "Pools: {:>4}\n".format(self.num_pools)
        raw = status['pgmap']['bytes_total']
        output += "Raw capacity: {}\n".format(human_size(raw))
        output += "\nConfig : {}\n".format(self.conf)
        output += "Client Name: {}\n".format(self.client_name)

        console_message(output, color=color[self.health_status])
Esempio n. 5
0
 def summary(self):
     total_bytes = 0
     for disk in self.children:
         total_bytes += disk.size
     return '{} ({})'.format(self.name, human_size(total_bytes)), None
Esempio n. 6
0
    def valid_disk(ceph_iscsi_config, logger, **kwargs):
        """
        determine whether the given image info is valid for a disk operation

        :param ceph_iscsi_config: Config object
        :param logger: logger object
        :param image_id: (str) <pool>.<image> format
        :return: (str) either 'ok' or an error description
        """

        # create can also pass optional controls dict
        mode_vars = {
            "create": ['pool', 'image', 'size', 'count'],
            "resize": ['pool', 'image', 'size'],
            "reconfigure": ['pool', 'image', 'controls'],
            "delete": ['pool', 'image']
        }

        if 'mode' in kwargs.keys():
            mode = kwargs['mode']
        else:
            mode = None

        backstore = kwargs['backstore']
        if backstore not in LUN.BACKSTORES:
            return "Invalid '{}' backstore - Supported backstores: " \
                   "{}".format(backstore, ','.join(LUN.BACKSTORES))

        if mode in mode_vars:
            if not all(x in kwargs for x in mode_vars[mode]):
                return ("{} request must contain the following "
                        "variables: ".format(mode, ','.join(mode_vars[mode])))
        else:
            return "disk operation mode '{}' is invalid".format(mode)

        config = ceph_iscsi_config.config

        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 mode == 'create':
            if kwargs['size'] and not valid_size(kwargs['size']):
                return "Size is invalid"

            if len(config['disks']) >= 256:
                return "Disk limit of 256 reached."

            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 not disk_regex.search(kwargs['image']):
                return "Invalid image name (use alphanumeric, '_', or '-' characters)"

            if kwargs['count'].isdigit():
                if not 1 <= int(kwargs['count']) <= 10:
                    return "invalid count specified, must be an integer (1-10)"
            else:
                return "invalid count specified, must be an integer (1-10)"

            if kwargs['count'] == '1':
                new_disks = {disk_key}
            else:
                limit = int(kwargs['count']) + 1
                new_disks = set(
                    ['{}{}'.format(disk_key, ctr) for ctr in range(1, limit)])

            if any(new_disk in config['disks'] for new_disk in new_disks):
                return ("at least one rbd image(s) with that name/prefix is "
                        "already defined")

        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']))

        if mode == 'resize':

            if not valid_size(kwargs['size']):
                return "Size is invalid"

            size = kwargs['size'].upper()
            current_size = get_rbd_size(kwargs['pool'], kwargs['image'])
            if convert_2_bytes(size) <= current_size:
                return ("resize value must be larger than the "
                        "current size ({}/{})".format(human_size(current_size),
                                                      current_size))

        if mode in ['create', 'reconfigure']:

            try:
                settings.Settings.normalize_controls(kwargs['controls'],
                                                     LUN.SETTINGS[backstore])
            except ValueError as err:
                return (err)

        if mode == 'delete':

            # disk must *not* be allocated to a client in the config
            mapped_list = []
            allocation_list = []
            for target_iqn, target in config['targets'].items():
                if disk_key in target['disks']:
                    mapped_list.append(target_iqn)
                for client_iqn in target['clients']:
                    client_metadata = target['clients'][client_iqn]
                    if disk_key in client_metadata['luns']:
                        allocation_list.append(client_iqn)

            if allocation_list:
                return ("Unable to delete {}. Allocated "
                        "to: {}".format(disk_key, ','.join(allocation_list)))

            if mapped_list:
                return ("Unable to delete {}. Mapped "
                        "to: {}".format(disk_key, ','.join(mapped_list)))

        return 'ok'
Esempio n. 7
0
def define_luns(gateway):
    """
    define the disks in the config to LIO
    :param gateway: (object) gateway object - used for mapping
    :return: None
    """

    local_gw = this_host()

    # sort the disks dict keys, so the disks are registered in a specific
    # sequence
    disks = config.config['disks']
    srtd_disks = sorted(disks)
    pools = {disks[disk_key]['pool'] for disk_key in srtd_disks}

    if pools:
        with rados.Rados(conffile=settings.config.cephconf) as cluster:

            for pool in pools:

                logger.debug("Processing rbd's in '{}' pool".format(pool))

                with cluster.open_ioctx(pool) as ioctx:

                    pool_disks = [disk_key for disk_key in srtd_disks
                                  if disk_key.startswith(pool)]
                    for disk_key in pool_disks:

                        pool, image_name = disk_key.split('.')

                        try:
                            with rbd.Image(ioctx, image_name) as rbd_image:
                                image_bytes = rbd_image.size()
                                image_size_h = human_size(image_bytes)

                                lun = LUN(logger, pool, image_name,
                                          image_size_h, local_gw)
                                if lun.error:
                                    halt("Error defining rbd image "
                                         "{}".format(disk_key))

                                lun.allocate()
                                if lun.error:
                                    halt("Error unable to register {} with "
                                         "LIO - {}".format(disk_key,
                                                           lun.error_msg))

                        except rbd.ImageNotFound:
                            halt("Disk '{}' defined to the config, but image "
                                 "'{}' can not be found in "
                                 "'{}' pool".format(disk_key,
                                                    image_name,
                                                    pool))

        # Gateway Mapping : Map the LUN's registered to all tpg's within the
        # LIO target
        gateway.manage('map')
        if gateway.error:
            halt("Error mapping the LUNs to the tpg's within the iscsi Target")

    else:
        logger.info("No LUNs to export")
Esempio n. 8
0
    def valid_disk(ceph_iscsi_config, logger, **kwargs):
        """
        determine whether the given image info is valid for a disk operation

        :param ceph_iscsi_config: Config object
        :param logger: logger object
        :param image_id: (str) <pool>.<image> format
        :return: (str) either 'ok' or an error description
        """

        # create can also pass optional controls dict
        mode_vars = {
            "create": ['pool', 'image', 'size', 'count'],
            "resize": ['pool', 'image', 'size'],
            "reconfigure": ['pool', 'image', 'controls'],
            "delete": ['pool', 'image']
        }

        if 'mode' in kwargs.keys():
            mode = kwargs['mode']
        else:
            mode = None

        if mode in mode_vars:
            if not all(x in kwargs for x in mode_vars[mode]):
                return ("{} request must contain the following "
                        "variables: ".format(mode, ','.join(mode_vars[mode])))
        else:
            return "disk operation mode '{}' is invalid".format(mode)

        config = ceph_iscsi_config.config

        disk_key = "{}.{}".format(kwargs['pool'], kwargs['image'])

        if mode in ['create', 'resize']:

            if not valid_size(kwargs['size']):
                return "Size is invalid"

            elif kwargs['pool'] not in get_pools():
                return "pool name is invalid"

        if mode == 'create':
            if len(config['disks']) >= 256:
                return "Disk limit of 256 reached."

            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 not disk_regex.search(kwargs['image']):
                return "Invalid image name (use alphanumeric, '_', or '-' characters)"

            if kwargs['count'].isdigit():
                if not 1 <= int(kwargs['count']) <= 10:
                    return "invalid count specified, must be an integer (1-10)"
            else:
                return "invalid count specified, must be an integer (1-10)"

            if kwargs['count'] == '1':
                new_disks = {disk_key}
            else:
                limit = int(kwargs['count']) + 1
                new_disks = set(
                    ['{}{}'.format(disk_key, ctr) for ctr in range(1, limit)])

            if any(new_disk in config['disks'] for new_disk in new_disks):
                return ("at least one rbd image(s) with that name/prefix is "
                        "already defined")

            gateways_defined = len([
                key for key in config['gateways']
                if isinstance(config['gateways'][key], dict)
            ])
            if gateways_defined < settings.config.minimum_gateways:
                return ("disks can not be added until at least {} gateways "
                        "are defined".format(settings.config.minimum_gateways))

        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']))

        if mode == 'resize':

            size = kwargs['size'].upper()
            current_size = get_rbd_size(kwargs['pool'], kwargs['image'])
            if convert_2_bytes(size) <= current_size:
                return ("resize value must be larger than the "
                        "current size ({}/{})".format(human_size(current_size),
                                                      current_size))

        if mode in ['create', 'reconfigure']:

            try:
                settings.Settings.normalize_controls(kwargs['controls'],
                                                     LUN.SETTINGS)
            except ValueError as err:
                return (err)

        if mode == 'delete':

            # disk must *not* be allocated to a client in the config
            allocation_list = []
            for client_iqn in config['clients']:
                client_metadata = config['clients'][client_iqn]
                if disk_key in client_metadata['luns']:
                    allocation_list.append(client_iqn)

            if allocation_list:
                return ("Unable to delete {}. Allocated "
                        "to: {}".format(disk_key, ','.join(allocation_list)))

            try:
                with rados.Rados(conffile=settings.config.cephconf) as cluster:
                    with cluster.open_ioctx(kwargs['pool']) as ioctx:
                        with rbd.Image(ioctx, kwargs['image']) as rbd_image:
                            if list(rbd_image.list_snaps()):
                                return ("Unable to delete {}. Snapshots must "
                                        "be removed first.".format(disk_key))
            except rbd.ImageNotFound:
                pass
            except Exception as e:
                return "Unable to query {}: {}".format(disk_key, e)

        return 'ok'
Esempio n. 9
0
 def summary(self):
     total_bytes = 0
     for disk in self.children:
         total_bytes += disk.size
     return '{}, Disks: {}'.format(human_size(total_bytes),
                                   len(self.children)), None
Esempio n. 10
0
 def _parse_snapshots(self, snapshots):
     self.snapshots = ["{name} ({size})".format(name=s['name'],
                                                size=human_size(s['size']))
                       for s in snapshots]
     self.snapshot_names = [s['name'] for s in snapshots]