CiscoUcs/imcsdk

View on GitHub
imcsdk/apis/v2/storage/vd.py

Summary

Maintainability
F
1 wk
Test Coverage
# Copyright 2017 Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""
This module provides APIs for storage configuration like virtual drives and
disk groups.
"""

import math
import logging

from imcsdk.imcexception import ImcOperationError, ImcException, ImcOperationErrorDetail
from imcsdk.apis.v2.storage.controller import _get_controller_dn
from imcsdk.apis.v2.storage.pd import pd_get
from imcsdk.mometa.storage.StorageVirtualDriveCreatorUsingUnusedPhysicalDrive \
    import StorageVirtualDriveCreatorUsingUnusedPhysicalDrive as vd_creator
from imcsdk.mometa.storage.StorageVirtualDriveCreatorUsingVirtualDriveGroup \
    import StorageVirtualDriveCreatorUsingVirtualDriveGroup as vd_carve

log = logging.getLogger('imc')


def _list_to_string(drive_list):
    # convert to format imc expects
    # list to string
    # [[1]] => '[1]'
    # [[1,2],[3,4]] => '[1,2][3,4]'
    # imc fails to parse '[1, 2]'
    # it needs to be '[1,2]'
    # and hence the replace(' ', '')
    dg_str = ""
    for each in drive_list:
        dg_str += str(each)
    return dg_str.replace(' ', '')


def _flatten_list(drive_list):
    # convert to format imc expects
    # [[1]] => [1]
    # [[1,2],[3,4]] => [1, 2, 3, 4]
    if not (isinstance(drive_list, list) and isinstance(drive_list[0], list)):
        raise "drive_list needs a list of list(s). i.e [[1,2],[3,4]]"
    dg_list = []
    for each in drive_list:
        for sub_each in each:
            dg_list.append(sub_each)
    return dg_list


def _flatten_to_string(drive_list):
    # convert to format imc expects
    # [[1]] => '1'
    # [[1,2],[3,4]] => '1234'
    return ''.join([''.join(str(x)) for x in _flatten_list(drive_list)])


def vd_name_derive(raid_level, drive_list):
    return "RAID" + str(raid_level) + "_" + _flatten_to_string(drive_list)


def _human_to_bytes(size_str):
    """
    returns the size in bytes
        supported formats KB, MB, GB, TB, PB, EB, ZB, YB

        KB to bytes = (* 1024) == << 10
        MB to bytes = (* 1024 * 1024) == << 20
        GB to bytes = (* 1024 * 1024 * 1024) == << 30
    """
    convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4,
               'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8}
    s = size_str.split()
    if s[1] not in convert:
        raise "unknown size format" + size_str
    return int(s[0]) << (10 * convert[s[1]])


def _bytes_to_human(size, output_format=None):
    """
    converts bytes to human readable format.
        The return is in output_format.
    """
    convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4,
               'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8}
    unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    if output_format is None:
        output_format = unit[int(math.floor(math.log(size, 2))/10)]
    if output_format not in convert:
        raise "unknown output format" + output_format
    return str(size >> (10 * convert[output_format])) + ' ' + output_format


def _pd_sizes_get(handle,
                  controller_type,
                  controller_slot,
                  drive_list,
                  server_id=1):
    sizes = []
    for each in drive_list:
        for drive in each:
            dmo = pd_get(handle=handle,
                         controller_type=controller_type,
                         controller_slot=controller_slot,
                         drive_slot=drive,
                         server_id=server_id)
            sizes.append(_human_to_bytes(dmo.coerced_size))
    return sizes


def _pd_min_size_get(sizes):
    return min(sizes)


def _pd_total_size_get(sizes):
    return sum(sizes)


def _pd_unused_get(handle):
    return handle.query_classid(class_id='StorageUnusedLocalDisk')


def _vd_span_depth_get(drive_list):
    return len(drive_list)


def _raid_max_size_get(raid_level, total_size, min_size, span_depth):
    size = {0: total_size,
            1: total_size/2,
            5: total_size - (span_depth * 1 * min_size),
            6: total_size - (span_depth * 2 * min_size),
            10: total_size/2,
            50: total_size - (span_depth * 1 * min_size),
            60: total_size - (span_depth * 2 * min_size)}

    if raid_level not in size:
        raise ImcOperationError("Create Virtual Drive",
                                "Unsupported Raid level <%s>" % raid_level)
    return size[raid_level]


def _vd_max_size_get(handle,
                     controller_type,
                     controller_slot,
                     drive_list,
                     raid_level,
                     server_id=1):
    """
    Returns the usable disk size for the specified virtual_drive
        drive_list:
            [[1]]
            [[1,2],[3,4]]

        min_size = smallest size of all the drives from this disk group
        available_size = accumulated size of all the drives together
        span_depth = number of sub groups [[1,2,3],[4,5,6]] span_depth = 2
    """
    sizes = _pd_sizes_get(handle=handle,
                          controller_type=controller_type,
                          controller_slot=controller_slot,
                          drive_list=drive_list,
                          server_id=server_id)
    min_size = _pd_min_size_get(sizes)
    total_size = _pd_total_size_get(sizes)
    span_depth = _vd_span_depth_get(drive_list)

    max_size = _raid_max_size_get(raid_level, total_size, min_size, span_depth)
    return _bytes_to_human(max_size, output_format="MB")


def _existing_vd_maxsize_get(handle, vd_carve, id):
    dn = vd_carve.dn + "/vd-" + str(id)

    mo = handle.query_dn(dn)
    if mo is None:
        raise ImcOperationError("Create Virtual drive using existing Virtual drives",
                                "Existing Drive: %s does not exist OR " \
                                "No space is available to create another Virtual " \
                                "drive." % id)
    return mo.max_available_space


def vd_create_using_unused_pds(handle,
                               drive_group,
                               controller_type,
                               controller_slot,
                               raid_level=0,
                               virtual_drive_name=None,
                               access_policy="read-write",
                               read_policy="no-read-ahead",
                               cache_policy="direct-io",
                               disk_cache_policy="unchanged",
                               write_policy="Write Through",
                               strip_size="64k",
                               size=None,
                               self_encrypt=False,
                               server_id=1):
    """
    Creates virtual drive from unused physical drives

    Args:
        handle (ImcHandle)
        drive_group (list of lists): list of drives
                        [[1]]
                        [[1,2]]
                        [[1,2],[3,4]]
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        raid_level (int): raid level
                        0, 1, 5, 6, 10, 50, 60
                    Raid 0 Simple striping.
                    Raid 1 Simple mirroring.
                    Raid 5 Striping with parity.
                    Raid 6 Striping with two parity drives.
                    Raid 10 Spanned mirroring.
                    Raid 50 Spanned striping with parity.
                    Raid 60 Spanned striping with two parity drives.
        virtual_drive_name (str): Name of the virtual drive
        access_policy (str): Access-policy for the virtual drive
                ['read-write', 'read-only', 'hidden', 'default', 'blocked']
        read_policy (str): Read Policy for the virtual drive.
                ["always-read-ahead", "no-read-ahead"]
        cache_policy (str): Cache policy used for buffering reads.
                ["cached-io", "direct-io"]
        disk_cache_policy: Disk cache policy.
                ["disabled", "enabled", "unchanged"]
        write_policy: Write policy
                ["always-write-back", "write-back-good-bbu", "write-through"]
        strip_size: Size of each strip
                ["1024k", "128k", "16k", "256k", "32k", "512k", "64k", "8k"]
        size: The size of the virtual drive you want to create.
                Enter a value and select one of the following units - MB,GB,TB
                e.g. '100 MB' or '20 GB' or '1 TB'
        self_encrypt (bool): Encrypt the virtual drive if the underlying
                             controller and physical drive support it
        server_id (int): Specify server id for UCS C3260 modular servers

    Returns:
        None

    Examples:
        vd_create_using_unused_pds(handle=imc, drive_group=[[2]],
                                   controller_type = 'SAS',
                                   controller_slot='MEZZ')
    """
    slot_dn = _get_controller_dn(handle, controller_type,
                                 controller_slot, server_id)

    dg_str = _list_to_string(drive_group)
    vdn = virtual_drive_name

    params = {}
    params["parent_mo_or_dn"] = slot_dn
    params["drive_group"] = dg_str
    params["raid_level"] = str(raid_level)
    params["access_policy"] = access_policy
    params["read_policy"] = read_policy
    params["cache_policy"] = cache_policy
    params["disk_cache_policy"] = disk_cache_policy
    params["write_policy"] = write_policy
    params["strip_size"] = strip_size

    if self_encrypt:
        params["admin_action"] = "enable-self-encrypt"

    params["virtual_drive_name"] = \
        (vd_name_derive(raid_level, drive_group), vdn)[vdn is not None]

    params["size"] = (_vd_max_size_get(handle=handle,
                                       controller_type=controller_type,
                                       controller_slot=controller_slot,
                                       drive_list=drive_group,
                                       raid_level=raid_level,
                                       server_id=server_id),
                      size)[size is not None]

    mo = vd_creator(**params)
    mo.admin_state = "trigger"
    handle.add_mo(mo)


def vd_create_using_existing_vd(handle,
                                controller_type,
                                controller_slot,
                                shared_virtual_drive_id,
                                virtual_drive_name,
                                access_policy="read-write",
                                read_policy="no-read-ahead",
                                cache_policy="direct-io",
                                disk_cache_policy="unchanged",
                                write_policy="Write Through",
                                strip_size="64k",
                                size=None,
                                server_id=1):
    """
    Creates virtual drive from the existing virtua drives.

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        shared_virtual_drive_id (str): Id of the existing virtual drive
        virtual_drive_name (str): Name of the virtual drive
        access_policy (str): Access-policy for the virtual drive
                ['read-write', 'read-only', 'hidden', 'default', 'blocked']
        read_policy (str): Read Policy for the virtual drive.
                ["always-read-ahead", "no-read-ahead"]
        cache_policy (str): Cache policy used for buffering reads.
                ["cached-io", "direct-io"]
        disk_cache_policy: Disk cache policy.
                ["disabled", "enabled", "unchanged"]
        write_policy: Write policy
                ["always-write-back", "write-back-good-bbu", "write-through"]
        strip_size: Size of each strip
                ["1024k", "128k", "16k", "256k", "32k", "512k", "64k", "8k"]
        size: The size of the virtual drive you want to create.
                Enter a value and select one of the following units - MB,GB,TB
                e.g. '100 MB' or '20 GB' or '1 TB'
        server_id (int): Specify server id for UCS C3260 modular servers

    Returns:
        None

    Examples:
        vd_create_using_existing_vd(handle=imc,
                                    controller_type='SAS',
                                    controller_slot='MEZZ',
                                    shared_virtual_drive_id=1,
                                    virtual_drive_name='newvd')
    """
    slot_dn = _get_controller_dn(handle, controller_type,
                                 controller_slot, server_id)

    mo = vd_carve(parent_mo_or_dn=slot_dn)

    params = {}
    params["shared_virtual_drive_id"] = str(shared_virtual_drive_id)
    params["virtual_drive_name"] = virtual_drive_name
    params["access_policy"] = access_policy
    params["read_policy"] = read_policy
    params["cache_policy"] = cache_policy
    params["disk_cache_policy"] = disk_cache_policy
    params["write_policy"] = write_policy
    params["strip_size"] = strip_size

    if size and size.strip():
        params["size"] = size
    else:
        params["size"] = _existing_vd_maxsize_get(handle=handle,
            vd_carve=mo, id=shared_virtual_drive_id)

    mo.set_prop_multiple(**params)
    mo.admin_state = "trigger"
    handle.add_mo(mo)


def vd_get(handle, controller_type, controller_slot, id, server_id=1):
    slot_dn = _get_controller_dn(handle, controller_type, controller_slot,
                                 server_id)
    dn = slot_dn + "/vd-" + str(id)
    mo = handle.query_dn(dn)
    if mo is None:
        raise ImcOperationError("Get Virtual drive: %s" % id,
                                "Not found")
    return mo

def vd_update(handle,
            controller_type,
            controller_slot,
            id,
            server_id=1,
            access_policy=None,
            read_policy=None,
            cache_policy=None,
            disk_cache_policy=None,
            write_policy=None):
    mo = vd_get(handle, controller_type, controller_slot, id, server_id)
    if mo is None:
        raise ImcOperationError("Get Virtual drive",
                                "Managed Object not found")

    if access_policy is not None:
        mo.access_policy = access_policy

    if read_policy is not None:
        mo.read_policy = read_policy

    if cache_policy is not None:
        mo.cache_policy = cache_policy

    if disk_cache_policy is not None:
        mo.disk_cache_policy = disk_cache_policy

    if write_policy is not None:
        mo.requested_write_cache_policy = write_policy

    handle.set_mo(mo)
    return mo


def _vd_set_action(handle, controller_type, controller_slot, id,
                   action,
                   server_id=1, **kwargs):
    mo = vd_get(handle, controller_type, controller_slot, id, server_id)
    if mo is None:
        raise ImcOperationError("Configure Virtual drive: %s" % id,
                                "Not found")
    mo.admin_action = action
    mo.set_prop_multiple(**kwargs)
    handle.set_mo(mo)
    return mo


def vd_exists(handle, controller_type, controller_slot, id, server_id=1, **kwargs):
    """
    Checks if a virtual drive exists.

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): Id of the virtual drive

    Returns:
        exists(bool), error(str)

    Examples:
        vd_exists(handle=imc, controller_type='SAS', controller_slot='MEZZ',
                  id=1)
    """
    try:
        mo = vd_get(handle, controller_type, controller_slot, id, server_id)
    except:
        return False, None

    return True, mo


def vd_delete(handle, controller_type, controller_slot, id, server_id=1,
              delete_boot_drive=False):
    """
    Deletes the specified virtual drive

    Args:
        handle (ImcHandle)
        controller_slot (str): controller slot name/number
                               "MEZZ","0"-"9"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Examples:
        vd_delete(handle=imc, controller_type='SAS', controller_slot='MEZZ',
                  id=1)
    """
    from imcsdk.apis.v2.storage.controller import controller_clear_boot_drive

    mo = vd_get(handle=handle,
                controller_type=controller_type,
                controller_slot=controller_slot,
                id=id,
                server_id=server_id)

    if delete_boot_drive and mo.boot_drive.lower() in ('yes', 'true'):
        controller_clear_boot_drive(handle, controller_type,
                                    controller_slot, server_id)

    handle.remove_mo(mo)


def vd_delete_all(handle, controller_type, controller_slot, server_id=1,
                  delete_boot_drive=False):
    """
    Delete all the specified virtual drive

    Args:
        handle (ImcHandle)
        controller_slot (str): controller slot name/number
                               "MEZZ","0"-"9"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Examples:
        vd_delete_all(handle=imc, controller_type='SAS',
                      controller_slot='MEZZ', id=1)
    """
    from imcsdk.apis.v2.storage.controller import controller_clear_boot_drive

    controller_dn = _get_controller_dn(handle, controller_type,
                                       controller_slot, server_id)
    mos = handle.query_children(in_dn=controller_dn,
                                class_id="storageVirtualDrive")
    error_msg = ""
    for mo in mos:
        if delete_boot_drive and mo.boot_drive.lower() in ('yes', 'true'):
            controller_clear_boot_drive(handle, controller_type,
                                        controller_slot, server_id)
        try:
            handle.remove_mo(mo)
        except ImcException as e:
            error_msg += str(e)

    if error_msg:
        raise ImcOperationError("Delete all Virtual drives on controller: %s" % controller_slot,
                                error_msg)


def vd_exists_any(handle, controller_type, controller_slot, server_id=1,
                  **kwargs):
    """
    Checks if any virtual drive exists

    Args:
        handle (ImcHandle)
        controller_slot (str): controller slot name/number
                               "MEZZ","0"-"9"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        bool

    Examples:
        vd_exists_any(handle=imc, controller_type='SAS',
                      controller_slot='MEZZ', id=1)
    """
    controller_dn = _get_controller_dn(handle, controller_type,
                                       controller_slot, server_id)
    mos = handle.query_children(in_dn=controller_dn,
                                class_id="storageVirtualDrive")
    if mos and len(mos) > 0:
        return True
    return False


def vd_boot_drive_enable(handle, controller_type, controller_slot, id,
                         server_id=1, **kwargs):
    """
    Set a virtual drive as boot drive.

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        StorageVirtualDrive object

    Examples:
        vd_set_boot_drive(handle, 'SAS', 'HBA', 'test_vd')
    """
    from imcsdk.mometa.storage.StorageVirtualDrive import \
        StorageVirtualDriveConsts

    action = StorageVirtualDriveConsts.ADMIN_ACTION_SET_BOOT_DRIVE
    return _vd_set_action(
            handle,
            controller_type=controller_type,
            controller_slot=controller_slot,
            id=id,
            action=action,
            server_id=server_id
        )


def vd_boot_drive_exists(handle, controller_type, controller_slot, id,
                         server_id=1):
    """
    Checkes if virtual drive is set as boot drive.

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        StorageVirtualDrive object

    Examples:
        vd_boot_drive_exists(handle, 'SAS', 'HBA', 1)
    """
    try:
        mo = vd_get(handle=handle,
                    controller_type=controller_type,
                    controller_slot=controller_slot,
                    id=id,
                    server_id=server_id)
    except:
        False, None

    if mo.boot_drive.lower() not in ('yes', 'true'):
        return False, mo
    return True, mo


def vd_boot_drive_disable(handle, controller_type, controller_slot, id,
                          server_id=1):
    """
    Set a virtual drive as boot drive.

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        StorageVirtualDrive object

    Examples:
        vd_set_boot_drive(handle, 'SAS', 'HBA', 'test_vd')
    """
    from imcsdk.apis.v2.storage.controller import controller_clear_boot_drive

    vd = vd_get(handle, controller_type, controller_slot, id, server_id)
    if vd.boot_drive.lower() not in ('true', 'yes'):
        return vd

    controller_clear_boot_drive(handle, controller_type, controller_slot,
                                server_id)

    return handle.query_dn(vd.dn)


def vd_encryption_enable(handle, controller_type, controller_slot, id,
                         server_id=1):
    """
    Enables encryption on the virtual drive if it is supported by the
    controller and the underlying physical drive

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        StorageVirtualDrive object

    Examples:
        vd_encryption_enable(handle, 'SAS', 'HBA', 1)
    """
    from imcsdk.mometa.storage.StorageVirtualDrive import \
        StorageVirtualDriveConsts

    action = StorageVirtualDriveConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT
    return _vd_set_action(
            handle,
            controller_type=controller_type,
            controller_slot=controller_slot,
            id=id,
            action=action,
            server_id=server_id
        )


def vd_encryption_exists(handle, controller_type, controller_slot, id,
                         server_id=1):
    """
    Checks if encryption is enabled on the virtual drive

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        id (int): id of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        boot

    Examples:
        vd_encryption_exists(handle, 'SAS', 'HBA', 0)
    """
    mo = vd_get(handle, controller_type, controller_slot, id, server_id)
    if mo is None:
        return False, None

    if mo.fde_enabled.lower() not in ['yes', 'true']:
        return False, mo

    return True, mo


def vd_get_by_name(handle, controller_type, controller_slot, name,
                   server_id=1):
    """
    Gets a virtual drive by name

    Args:
        handle (ImcHandle)
        controller_type (str): Controller type
                               'SAS'
        controller_slot (str): Controller slot name/number
                                "MEZZ","0"-"9", "HBA"
        name (str): name of the virtual drive
        server_id (int): server_id for UCS 3260 platform

    Returns:
        StorageVirtualDrive object

    Examples:
        vd_get_by_name(handle, 'SAS', 'HBA', 'test_vd')
    """
    slot_dn = _get_controller_dn(handle, controller_type, controller_slot,
                                 server_id)

    mos = handle.query_children(in_dn=slot_dn, class_id="storageVirtualDrive")
    for mo in mos:
        if mo.name == name:
            return mo
    return None