openvstorage / framework-alba-plugin

The Framework ALBA plugin extends the OpenvStorage GUI with functionality to manage ASDs (Alternate Storage Daemon) and Seagate Kinetic drives.
Other
2 stars 3 forks source link

Backend role can be overwritten by api #275

Closed JeffreyDevloo closed 7 years ago

JeffreyDevloo commented 7 years ago

Problem description

The GUI prohibits the ability to configure a disk, that has been claimed for a backend, with any of the roles. The API however still allows the disk/partition to be configured to your liking.

Testing code

# Copyright (C) 2016 iNuron NV
#
# This file is part of Open vStorage Open Source Edition (OSE),
# as available from
#
#      http://www.openvstorage.org and
#      http://www.openvstorage.com.
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License v3 (GNU AGPLv3)
# as published by the Free Software Foundation, in version 3 as it comes
# in the LICENSE.txt file of the Open vStorage OSE distribution.
#
# Open vStorage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY of any kind.
from ci.helpers.api import OVSClient
from ovs.dal.lists.storagerouterlist import StorageRouterList

class ConfigureTest(object):

    @staticmethod
    def test():
        # Current configuration uses sda, sdb and sdc for backend
        ip = '10.100.199.151'
        roles = ['WRITE']
        diskname = 'sda'
        api = OVSClient(ip, 'admin', 'admin')
        ConfigureTest.add_disk_role(ip, diskname, roles, api)
        # Expecting an error to occur
        print 'Test failed'

    @staticmethod
    def add_disk_role(ip, diskname, roles, api, min_size=2):
        """
        Partition and adds roles to a disk

        :param ip: storagerouter ip where the disk is located
        :type ip: str
        :param diskname: shortname of a disk (e.g. sdb)
        :type diskname: str
        :param roles: list of roles you want to add to the disk
        :type roles: list
        :param api: specify a valid api connection to the setup
        :type api: ci.helpers.api.OVSClient
        :param min_size: minimum total_partition_size that is required to allocate the disk role
        :type min_size: int
        :param config: configuration file
        :type config: dict
        :return:
        """

        # Fetch information
        storagerouter = StorageRouterList.get_by_ip(ip)
        storagerouter_guid = storagerouter.guid
        disk = ConfigureTest.get_disk_by_name(storagerouter, diskname)
        # Check if there are any partitions on the disk, if so check if there is enough space
        unused_partitions = []
        if len(disk.partitions) > 0:
            total_partition_size = 0
            for partition in disk.partitions:
                total_partition_size += partition.size
                # Check if the partition is in use - could possibly write role on unused partition
                # if partition.mountpoint is None:
                    # Means no output -> partition not mounted
                    # @Todo support partitions that are not sequentional
                unused_partitions.append(partition)

            # Elect biggest unused partition as potential candidate
            biggest_unused_partition = None
            if len(unused_partitions) > 0:
                # Sort the list based on size
                unused_partitions.sort(key=lambda x: x.size, reverse=True)
                biggest_unused_partition = unused_partitions[0]
            if ((disk.size-total_partition_size)/1024**3) > min_size:
                # disk is still large enough, let the partitioning begin and apply some roles!
                ConfigureTest._configure_disk(storagerouter_guid=storagerouter_guid, disk_guid=disk.guid, offset=total_partition_size+1,
                                          size=(disk.size-total_partition_size)-1, roles=roles, api=api)
            elif biggest_unused_partition is not None and (biggest_unused_partition.size/1024**3) > min_size:
                ConfigureTest._configure_disk(storagerouter_guid=storagerouter_guid, disk_guid=disk.guid, offset=biggest_unused_partition.offset,
                                          size=biggest_unused_partition.size, roles=roles, api=api, partition_guid=biggest_unused_partition.guid)
            else:
                # disk is too small
                raise RuntimeError("Disk `{0}` on node `{1}` is too small for role(s) `{2}`, min. total_partition_size is `{3}`"
                                   .format(diskname, ip, roles, min_size))
        else:
            # there are no partitions on the disk, go nuke it!
            ConfigureTest._configure_disk(storagerouter_guid, disk.guid, 0, disk.size, roles, api)

    @staticmethod
    def _configure_disk(storagerouter_guid, disk_guid, offset, size, roles, api, partition_guid=None,
                        timeout=30):
        """
        Partition a disk and add roles to it

        :param storagerouter_guid: guid of a storagerouter
        :type storagerouter_guid: str
        :param disk_guid: guid of a disk
        :type disk_guid: str
        :param offset: start of the partition
        :type offset: int
        :param size: size of the partition
        :type size: int
        :param roles: roles to add to a partition (e.g. ['DB', 'WRITE'])
        :type roles: list
        :param api: specify a valid api connection to the setup
        :type api: ci.helpers.api.OVSClient
        :param timeout: time to wait for the task to complete
        :type timeout: int
        :param partition_guid: guid of the partition
        :type partition_guid: str
        :return: tuple that consists of disk_guid and storagerouter_guid
        :rtype: tuple
        """
        data = {
            'disk_guid': disk_guid,
            'offset': offset,
            'size': size,
            'roles': roles,
            'partition_guid': partition_guid
        }
        task_guid = api.post(
            api='/storagerouters/{0}/configure_disk/'.format(storagerouter_guid),
            data=data
        )
        task_result = api.wait_for_task(task_id=task_guid, timeout=timeout)

        if not task_result[0]:
            error_msg = "Adjusting disk `{0}` has failed on storagerouter `{1}` with error '{2}'" \
                .format(disk_guid, storagerouter_guid, task_result[1])
            print (error_msg)
            raise RuntimeError(error_msg)
        else:
            print ("Adjusting disk `{0}` should have succeeded on storagerouter `{1}`"
                                   .format(disk_guid, storagerouter_guid))
            return disk_guid, storagerouter_guid

    @staticmethod
    def get_disk_by_name(storagerouter, diskname):
        disks = storagerouter.disks
        for d in disks:
            if d.name == diskname:
                return d

if __name__ == "__main__":
    ConfigureTest.test()

Output

Adjusting disk `3984a040-7130-49eb-a80b-468b9a25b7c5` should have succeeded on storagerouter `32783fe3-b2df-471a-9db5-41419e07efa1`
Test failed

Possible solution

Return an error when the role is ['BACKEND']

openvstorage-ci commented 7 years ago

This issue was moved to openvstorage/framework#1300