Exemplo n.º 1
0
    def __init__(self, id, properties, parent):
        """Constructor for a LogicalDrive object."""
        # Strip off 'Logical Drive' before storing it in id
        self.id = id[15:]
        self.parent = parent
        self.properties = properties

        # 'string_to_bytes' takes care of converting any returned
        # (like 500MB, 25GB) unit of storage space to bytes (Integer value).
        # It requires space to be stripped.
        try:
            size = self.properties['Size'].replace(' ', '')
            # TODO(rameshg87): Reduce the disk size by 1 to make sure Ironic
            # has enough space to write a config drive. Remove this when
            # Ironic doesn't need it.
            self.size_gb = int(
                strutils.string_to_bytes(size, return_int=True) /
                (1024 * 1024 * 1024)) - 1
        except KeyError:
            msg = ("Can't get 'Size' parameter from ssacli output for logical "
                   "disk '%(logical_disk)s' of RAID array '%(array)s' in "
                   "controller '%(controller)s'." % {
                       'logical_disk': self.id,
                       'array': self.parent.id,
                       'controller': self.parent.parent.id
                   })
            raise exception.HPSSAOperationError(reason=msg)
        except ValueError:
            msg = ("ssacli returned unknown size '%(size)s' for logical "
                   "disk '%(logical_disk)s' of RAID array '%(array)s' in "
                   "controller '%(controller)s'." % {
                       'size': size,
                       'logical_disk': self.id,
                       'array': self.parent.id,
                       'controller': self.parent.parent.id
                   })
            raise exception.HPSSAOperationError(reason=msg)

        self.raid_level = self.properties.get('Fault Tolerance')
        # For RAID levels (like 5+0 and 6+0), HPSSA names them differently.
        # Check if we have mapping stored, otherwise use the same.
        raid_level_mapping = constants.RAID_LEVEL_HPSSA_TO_INPUT_MAPPING
        self.raid_level = raid_level_mapping.get(self.raid_level,
                                                 self.raid_level)

        self.volume_name = self.properties.get('Logical Drive Label')

        # Trim down the WWN to 16 digits (8 bytes) so that it matches
        # lsblk output in Linux.
        wwn = self.properties.get('Unique Identifier')
        if wwn:
            wwn = '0x' + wwn[:16].lower()
            self.wwn = wwn
Exemplo n.º 2
0
    def __init__(self, id, properties, parent):
        """Constructor for a PhysicalDrive object."""
        self.parent = parent
        self.properties = properties

        # Strip off physicaldrive before storing it in id
        self.id = id[14:]

        # 'string_to_bytes' takes care of converting any returned
        # (like 500MB, 25GB) unit of storage space to bytes (Integer value).
        # It requires space to be stripped.
        try:
            size = self.properties['Size'].replace(' ', '')
            self.size_gb = int(
                strutils.string_to_bytes(size, return_int=True) /
                (1024 * 1024 * 1024))
        except KeyError:
            msg = (
                "Can't get 'Size' parameter from ssacli output for physical "
                "disk '%(physical_disk)s' of controller '%(controller)s'." % {
                    'physical_disk': self.id,
                    'controller': self.parent.parent.id
                })
            raise exception.HPSSAOperationError(reason=msg)
        except ValueError:
            msg = ("ssacli returned unknown size '%(size)s' for physical "
                   "disk '%(physical_disk)s' of controller "
                   "'%(controller)s'." % {
                       'size': size,
                       'physical_disk': self.id,
                       'controller': self.parent.id
                   })
            raise exception.HPSSAOperationError(reason=msg)

        try:
            ssa_interface = self.properties['Interface Type']
        except KeyError:
            msg = ("Can't get 'Interface Type' parameter from ssacli output "
                   "for physical disk '%(physical_disk)s' of controller "
                   "'%(controller)s'." % {
                       'physical_disk': self.id,
                       'controller': self.parent.parent.id
                   })
            raise exception.HPSSAOperationError(reason=msg)

        self.interface_type = constants.get_interface_type(ssa_interface)
        self.disk_type = constants.get_disk_type(ssa_interface)
        self.model = self.properties.get('Model')
        self.firmware = self.properties.get('Firmware Revision')
        self.erase_status = self.properties.get('Status')
Exemplo n.º 3
0
def _select_controllers_by(server, select_condition, msg):
    """Filters out the hpssa controllers based on the condition.

    This method updates the server with only the controller which satisfies
    the condition. The controllers which doesn't satisfies the selection
    condition will be removed from the list.

    :param server: The object containing all the supported hpssa controllers
        details.
    :param select_condition: A lambda function to select the controllers based
        on requirement.
    :param msg: A String which describes the controller selection.
    :raises exception.HPSSAOperationError, if all the controller are in HBA
        mode.
    """
    all_controllers = server.controllers
    supported_controllers = [c for c in all_controllers if select_condition(c)]

    if not supported_controllers:
        reason = ("None of the available SSA controllers %(controllers)s "
                  "have %(msg)s" % {
                      'controllers': ', '.join([c.id
                                                for c in all_controllers]),
                      'msg': msg
                  })
        raise exception.HPSSAOperationError(reason=reason)

    server.controllers = supported_controllers
Exemplo n.º 4
0
def _hpssacli(*args, **kwargs):
    """Wrapper function for executing hpssacli command.

    :param args: args to be provided to hpssacli command
    :param kwargs: kwargs to be sent to processutils except the
        following:
        - dont_transform_to_hpssa_exception - Set to True if this
          method shouldn't transform other exceptions to hpssa
          exceptions. This is useful when the return code from hpssacli
          is useful for analysis.
    :returns: a tuple containing the stdout and stderr after running
        the process.
    :raises: HPSSAOperationError, if some error was encountered and
        dont_dont_transform_to_hpssa_exception was set to False.
    :raises: OSError or processutils.ProcessExecutionError if execution
        failed and dont_dont_transform_to_hpssa_exception was set to True.
    """

    dont_transform_to_hpssa_exception = kwargs.get(
        'dont_transform_to_hpssa_exception', False)
    kwargs.pop('dont_transform_to_hpssa_exception', None)

    try:
        stdout, stderr = processutils.execute("hpssacli", *args, **kwargs)
    except (OSError, processutils.ProcessExecutionError) as e:
        if not dont_transform_to_hpssa_exception:
            raise exception.HPSSAOperationError(reason=e)
        else:
            raise

    return stdout, stderr
Exemplo n.º 5
0
def _hpssacli(*args):
    """Wrapper function for executing hpssacli command."""
    try:
        stdout, stderr = processutils.execute("hpssacli", *args)
    except (OSError, processutils.ProcessExecutionError) as e:
        raise exception.HPSSAOperationError(reason=e)

    return stdout, stderr
Exemplo n.º 6
0
    def can_accomodate(self, logical_disk):
        """Check if this RAID array can accomodate the logical disk.

        This method uses hpssacli/ssacli command's option to check if the
        logical disk with desired size and RAID level can be created
        on this RAID array.

        :param logical_disk: Dictionary of logical disk to be created.
        :returns: True, if logical disk can be created on the RAID array
                  False, otherwise.
        """
        raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get(
            logical_disk['raid_level'], logical_disk['raid_level'])
        args = ("array", self.id, "create", "type=logicaldrive",
                "raid=%s" % raid_level, "size=?")

        if logical_disk['size_gb'] != "MAX":
            desired_disk_size = logical_disk['size_gb']
        else:
            desired_disk_size = constants.MINIMUM_DISK_SIZE

        try:
            stdout, stderr = self.parent.execute_cmd(
                *args, dont_transform_to_hpssa_exception=True)
        except processutils.ProcessExecutionError as ex:
            # hpssacli/ssacli returns error code 1 when RAID level of the
            # logical disk is not supported on the array.
            # If that's the case, just return saying the logical disk
            # cannot be accomodated in the array.
            # If exist_code is not 1, then it's some other error that we
            # don't expect to appear and hence raise it back.
            if ex.exit_code == 1:
                return False
            else:
                raise exception.HPSSAOperationError(reason=ex)
        except Exception as ex:
            raise exception.HPSSAOperationError(reason=ex)

        # TODO(rameshg87): This always returns in MB, but confirm with
        # HPSSA folks.
        match = re.search('Max: (\d+)', stdout)
        if not match:
            return False

        max_size_gb = int(match.group(1)) / 1024
        return desired_disk_size <= max_size_gb
Exemplo n.º 7
0
def _ssacli(*args, **kwargs):
    """Wrapper function for executing hpssacli/ssacli command.

    This function executes ssacli command if it exists, else it
    falls back to hpssacli.
    :param args: args to be provided to hpssacli/ssacli command
    :param kwargs: kwargs to be sent to processutils except the
        following:
        - dont_transform_to_hpssa_exception - Set to True if this
          method shouldn't transform other exceptions to hpssa
          exceptions only when hpssa controller is available. This is
          useful when the return code from hpssacli/ssacli is useful for
          analysis.
    :returns: a tuple containing the stdout and stderr after running
        the process.
    :raises: HPSSAOperationError, if some error was encountered and
        dont_dont_transform_to_hpssa_exception was set to False.
    :raises: OSError or processutils.ProcessExecutionError if execution
        failed and dont_transform_to_hpssa_exception was set to True.
    """

    dont_transform_to_hpssa_exception = kwargs.get(
        'dont_transform_to_hpssa_exception', False)
    kwargs.pop('dont_transform_to_hpssa_exception', None)

    try:
        if os.path.exists("/usr/sbin/ssacli"):
            stdout, stderr = processutils.execute("ssacli", *args, **kwargs)
        else:
            stdout, stderr = processutils.execute("hpssacli", *args, **kwargs)
    except (OSError, processutils.ProcessExecutionError) as e:
        if 'No controllers detected' in str(e):
            msg = ("SSA controller not found. Enable ssa controller"
                   " to continue with the desired operation")
            raise exception.HPSSAOperationError(reason=msg)
        elif not dont_transform_to_hpssa_exception:
            raise exception.HPSSAOperationError(reason=e)
        else:
            raise

    return stdout, stderr
    def test_erase_devices_not_supported(self, erase_mock, generic_erase_mock):
        node = {}
        port = {}
        value = ("Sanitize erase not supported in the "
                 "available controllers")
        e = exception.HPSSAOperationError(reason=value)
        erase_mock.side_effect = e

        exc = self.assertRaises(exception.HPSSAOperationError,
                                self.hardware_manager.erase_devices, node,
                                port)

        self.assertIn(value, str(exc))
Exemplo n.º 9
0
    def test_erase_devices_exception(self, execute_mock, get_all_details_mock):
        get_all_details_mock.return_value = raid_constants.SSA_ERASE_DRIVE
        server = objects.Server()
        d = [x for x in server.controllers[0].unassigned_physical_drives]
        controller = server.controllers[0]
        value = 'Some Exception'
        execute_mock.side_effect = [
            exception.HPSSAOperationError(reason=value), None
        ]

        ex = self.assertRaises(exception.HPSSAOperationError,
                               controller.erase_devices, d)

        self.assertIn(value, str(ex))
Exemplo n.º 10
0
    def test_erase_devices_not_supported(self, erase_mock, generic_erase_mock):
        node = {}
        port = {}
        value = ("Sanitize erase not supported in the "
                 "available controllers")
        e = exception.HPSSAOperationError(value)
        erase_mock.side_effect = e
        generic_erase_mock.return_value = {'foo': 'bar'}
        expt_return = {'Sanitize Erase': erase_mock.side_effect}
        expt_return.update(generic_erase_mock.return_value)

        self.hardware_manager.erase_devices(node, port)

        generic_erase_mock.assert_called_once_with(node, port)
Exemplo n.º 11
0
def create_configuration(raid_config):
    """Create a RAID configuration on this server.

    This method creates the given RAID configuration on the
    server based on the input passed.
    :param raid_config: The dictionary containing the requested
        RAID configuration. This data structure should be as follows:
        raid_config = {'logical_disks': [{'raid_level': 1, 'size_gb': 100},
                                         <info-for-logical-disk-2>
                                        ]}
    :returns: the current raid configuration. This is same as raid_config
        with some extra properties like root_device_hint, volume_name,
        controller, physical_disks, etc filled for each logical disk
        after its creation.
    :raises exception.InvalidInputError, if input is invalid.
    :raises exception.HPSSAOperationError, if all the controllers are in HBA
        mode.
    """
    server = objects.Server()

    select_controllers = lambda x: not x.properties.get(
        'HBA Mode Enabled', False)
    _select_controllers_by(server, select_controllers, 'RAID enabled')

    validate(raid_config)

    # Make sure we create the large disks first.  This is avoid the
    # situation that we avoid giving large disks to smaller requests.
    # For example, consider this:
    #   - two logical disks - LD1(50), LD(100)
    #   - have 4 physical disks - PD1(50), PD2(50), PD3(100), PD4(100)
    #
    # In this case, for RAID1 configuration, if we were to consider
    # LD1 first and allocate PD3 and PD4 for it, then allocation would
    # fail. So follow a particular order for allocation.
    #
    # Also make sure we create the MAX logical_disks the last to make sure
    # we allot only the remaining space available.
    logical_disks_sorted = (
        sorted(
            (x for x in raid_config['logical_disks'] if x['size_gb'] != "MAX"),
            reverse=True,
            key=lambda x: x['size_gb']) +
        [x for x in raid_config['logical_disks'] if x['size_gb'] == "MAX"])

    if any(logical_disk['share_physical_disks']
           for logical_disk in logical_disks_sorted
           if 'share_physical_disks' in logical_disk):
        logical_disks_sorted = _sort_shared_logical_disks(logical_disks_sorted)

    # We figure out the new disk created by recording the wwns
    # before and after the create, and then figuring out the
    # newly found wwn from it.
    wwns_before_create = set([x.wwn for x in server.get_logical_drives()])

    for logical_disk in logical_disks_sorted:

        if 'physical_disks' not in logical_disk:
            disk_allocator.allocate_disks(logical_disk, server, raid_config)

        controller_id = logical_disk['controller']

        controller = server.get_controller_by_id(controller_id)
        if not controller:
            msg = (
                "Unable to find controller named '%(controller)s'."
                " The available controllers are '%(ctrl_list)s'." % {
                    'controller': controller_id,
                    'ctrl_list': ', '.join([c.id for c in server.controllers])
                })
            raise exception.InvalidInputError(reason=msg)

        if 'physical_disks' in logical_disk:
            for physical_disk in logical_disk['physical_disks']:
                disk_obj = controller.get_physical_drive_by_id(physical_disk)
                if not disk_obj:
                    msg = ("Unable to find physical disk '%(physical_disk)s' "
                           "on '%(controller)s'" % {
                               'physical_disk': physical_disk,
                               'controller': controller_id
                           })
                    raise exception.InvalidInputError(msg)

        controller.create_logical_drive(logical_disk)

        # Now find the new logical drive created.
        server.refresh()
        wwns_after_create = set([x.wwn for x in server.get_logical_drives()])

        new_wwn = wwns_after_create - wwns_before_create

        if not new_wwn:
            reason = ("Newly created logical disk with raid_level "
                      "'%(raid_level)s' and size %(size_gb)s GB not "
                      "found." % {
                          'raid_level': logical_disk['raid_level'],
                          'size_gb': logical_disk['size_gb']
                      })
            raise exception.HPSSAOperationError(reason=reason)

        new_logical_disk = server.get_logical_drive_by_wwn(new_wwn.pop())
        new_log_drive_properties = new_logical_disk.get_logical_drive_dict()
        logical_disk.update(new_log_drive_properties)

        wwns_before_create = wwns_after_create.copy()

    _update_physical_disk_details(raid_config, server)
    return raid_config