Source code for openlmi.storage.DeviceProvider

# Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#
# Authors: Jan Safranek <jsafrane@redhat.com>
# -*- coding: utf-8 -*-
""""
Module for DeviceProvider class.

DeviceProvider
--------------

.. autoclass:: DeviceProvider
    :members:

"""

from openlmi.storage.BaseProvider import BaseProvider
import pywbem
import openlmi.common.cmpi_logging as cmpi_logging

[docs]class DeviceProvider(BaseProvider): """ CIM Provider which provides CIM StorageExtent or CIM_StoragePool of a Anaconda device. In addition to CIM provider methods, this class and its subclasses can convert CIM InstanceName to Anaconda's StorageDevice instance and a vice versa. """ @cmpi_logging.trace_method
[docs] def __init__(self, *args, **kwargs): """ Initialize the provider. Store reference to blivet.Blivet. Store reference to StorageConfiguration. Register at given ProviderManager. """ super(DeviceProvider, self).__init__(*args, **kwargs) self.provider_manager.add_device_provider(self)
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def provides_name(self, object_name): """ Returns True, if this class is provider for given CIM InstanceName. """ return False
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def provides_device(self, device): """ Returns True, if this class is provider for given Anaconda StorageDevice class. """ return False
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def get_device_for_name(self, object_name): """ Returns Anaconda StorageDevice for given CIM InstanceName or None if no device is found. """ return None
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def get_name_for_device(self, device): """ Returns CIM InstanceName for given Anaconda StorageDevice. None if no device is found. """ return None
@cmpi_logging.trace_method
[docs] def get_status(self, device): """ Returns OperationalStatus for given Anaconda StorageDevice. It combines statuses of all parent devices. Subclasses should override this method to provide additional statuses. """ status = set() parents = self.get_base_devices(device) if len(parents) > 0: for parent in parents: parent_provider = self.provider_manager.get_provider_for_device( parent) parent_status = parent_provider.get_status(parent) status.update(parent_status) else: status.add(self.Values.OperationalStatus.OK) return list(status)
@cmpi_logging.trace_method
[docs] def get_base_devices(self, device): """ Return iterable with base devices for given StorageDevice. Base devices are StorageDevices, that the given StorageDevice depend on, e.g. RAID members of a RAID, physical volumes of a Volume Group and Volume Group of Logical Volume. """ if device.parents: return device.parents return []
@cmpi_logging.trace_method def _find_redundancy(self, device): """ Discover redundancy of given StorageDevice. It uses ProviderManager to do so. """ provider = self.provider_manager.get_provider_for_device(device) if not provider: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Cannot find provider for device " + device.path) return provider.get_redundancy(device) @cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def do_delete_instance(self, device): """ Really delete given Anaconda StorageDevice. Subclasses must override this method to allow DeleteInstance intrinsic method. """ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "Cannot delete this device.")
@cmpi_logging.trace_method
[docs] def delete_instance(self, env, instance_name): """Delete an instance intrinsic method""" # just check the instance validity device = self.get_device_for_name(instance_name) if not device: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find the device.") if self.storage.deviceDeps(device): deps = self.storage.deviceDeps(device) depnames = [device.path for device in deps] cmpi_logging.logger.info("Cannot remove %s, it's used by %s" % (device.path, str(depnames))) raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "The device is in use by %s" % str(depnames)) self.do_delete_instance(device)
@cmpi_logging.trace_method
[docs] def get_redundancy(self, device): """ Returns redundancy characteristics for given Anaconda StorageDevice. """ parents = self.get_base_devices(device) if len(parents) > 0: # find all parents and get their redundancy redundancies = [self._find_redundancy(device) for device in parents] # iteratively call self.get_common_redundancy(r1, r2), ... final_redundancy = self.Redundancy.get_common_redundancy_list( redundancies) else: # this device has no parents, assume it is simple disk final_redundancy = self.Redundancy( no_single_point_of_failure=False, data_redundancy=1, package_redundancy=0, stripe_length=1) return final_redundancy
[docs] class Redundancy(object): """ Class representing redundancy characteristics of a StorageExtent device, i.e. both StorageExtent and StoragePool """ # constants for RAID levels RAID0 = 0 RAID1 = 1 RAID4 = 4 RAID5 = 5 RAID6 = 6 RAID10 = 10 LINEAR = 4096 # ParityLayout PARITY_ROTATED = 2 PARITY_NON_ROTATED = 1 @cmpi_logging.trace_method
[docs] def __init__(self, no_single_point_of_failure=False, data_redundancy=1, package_redundancy=0, stripe_length=1, parity_layout=None): self.no_single_point_of_failure = no_single_point_of_failure self.data_redundancy = data_redundancy self.package_redundancy = package_redundancy self.stripe_length = stripe_length self.parity_layout = parity_layout
@cmpi_logging.trace_method
[docs] def get_redundancy_raid0(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID0. """ # data is spread on all devices -> DataRedundancy is sum of base # DataRedundancies # PackageRedundancy is the minimum of PackageRedundancies data_redundancy = min(self.data_redundancy, second.data_redundancy) package_redundancy = min(self.package_redundancy, second.package_redundancy) no_single_point_of_failure = (self.no_single_point_of_failure and second.no_single_point_of_failure) stripe_length = self.stripe_length + second.stripe_length return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length)
@cmpi_logging.trace_method
[docs] def get_redundancy_raid1(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID1. """ data_redundancy = self.data_redundancy + second.data_redundancy package_redundancy = (self.package_redundancy + second.package_redundancy) no_single_point_of_failure = True stripe_length = min(self.stripe_length, second.stripe_length) return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length)
@cmpi_logging.trace_method
[docs] def get_redundancy_raid5(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID5. """ data_redundancy = min(self.data_redundancy, second.data_redundancy) package_redundancy = min(self.package_redundancy, second.package_redundancy) no_single_point_of_failure = True stripe_length = self.stripe_length + second.stripe_length return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length, parity_layout=self.PARITY_ROTATED)
@cmpi_logging.trace_method
[docs] def get_redundancy_raid4(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID4. """ redundancy = self.get_redundancy_raid5(second) redundancy.parity_layout = self.PARITY_NON_ROTATED return redundancy
@cmpi_logging.trace_method
[docs] def get_redundancy_raid6(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID6. """ data_redundancy = min(self.data_redundancy, second.data_redundancy) package_redundancy = min(self.package_redundancy, second.package_redundancy) no_single_point_of_failure = True stripe_length = self.stripe_length + second.stripe_length return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length, parity_layout=self.PARITY_ROTATED)
@cmpi_logging.trace_method
[docs] def get_redundancy_raid10(self, second): """ Return the combined data redundancy characteristics for two devices combined in RAID10. """ data_redundancy = self.data_redundancy + second.data_redundancy package_redundancy = min(self.package_redundancy, second.package_redundancy) no_single_point_of_failure = True stripe_length = self.stripe_length + second.stripe_length return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length)
@cmpi_logging.trace_method
[docs] def get_redundancy_linear(self, second): """ Return the combined data redundancy characteristics for two devices. Linear device is assumed, i.e. the data are either on self or on B. """ # assume linear device, i.e. a data is either on A or on B # hence data_redundancy is the minimum of both data_redundancy = min(self.data_redundancy, second.data_redundancy) # assume the worst package_redundancy = min(self.package_redundancy, second.package_redundancy) # both NoSinglePointOfFailure must be true to be the result true no_single_point_of_failure = (self.no_single_point_of_failure and second.no_single_point_of_failure) # we don't know if the data are on A or B, so assume the worst stripe_length = min(self.stripe_length, second.stripe_length) return DeviceProvider.Redundancy( no_single_point_of_failure=no_single_point_of_failure, data_redundancy=data_redundancy, package_redundancy=package_redundancy, stripe_length=stripe_length)
@staticmethod @cmpi_logging.trace_function
[docs] def get_common_redundancy_list(redundancy_list, raid_level=LINEAR): """ Return common redundancy characteristics for list of devices. Linear device is assumed, i.e. the data are either on self or on B. raid_level: LINEAR = Linear, 0,1,5,6 - raidX """ if raid_level == DeviceProvider.Redundancy.LINEAR: redundancy = reduce( lambda a, b: a.get_redundancy_linear(b), redundancy_list) elif raid_level == DeviceProvider.Redundancy.RAID0: redundancy = reduce( lambda a, b: a.get_redundancy_raid0(b), redundancy_list) elif raid_level == DeviceProvider.Redundancy.RAID1: redundancy = reduce( lambda a, b: a.get_redundancy_raid1(b), redundancy_list) redundancy.package_redundancy = \ redundancy.package_redundancy + len(redundancy_list) - 1 elif raid_level == DeviceProvider.Redundancy.RAID4: redundancy = reduce( lambda a, b: a.get_redundancy_raid4(b), redundancy_list) redundancy.package_redundancy = \ redundancy.package_redundancy + 1 elif raid_level == DeviceProvider.Redundancy.RAID5: redundancy = reduce( lambda a, b: a.get_redundancy_raid5(b), redundancy_list) redundancy.package_redundancy = \ redundancy.package_redundancy + 1 elif raid_level == DeviceProvider.Redundancy.RAID6: redundancy = reduce( lambda a, b: a.get_redundancy_raid6(b), redundancy_list) redundancy.package_redundancy = \ redundancy.package_redundancy + 2 elif raid_level == DeviceProvider.Redundancy.RAID10: redundancy = reduce( lambda a, b: a.get_redundancy_raid10(b), redundancy_list) # stripe-length needs to be calculated separately # nr. of stripes for N=2: 1, N=3: 2, N=4: 2, N=5: 3, ... stripes = len(redundancy_list) // 2 + len(redundancy_list) % 2 stripe_lengths = [x.stripe_length for x in redundancy_list] stripe_lengths.sort() # final stripe_length = sum of the 'stripes' lowest stripe # lengths stripe_lengths = stripe_lengths[:stripes] redundancy.stripe_length = reduce( lambda a, b: a + b, stripe_lengths) # data redundancy needs to be calculated separately # it's always 2 (at least for now), # i.e. sum of two lowest data redundancies data_redundancies = [x.data_redundancy for x in redundancy_list] data_redundancies.sort() redundancy.data_redundancy = (data_redundancies[0] + data_redundancies[1]) redundancy.package_redundancy = \ redundancy.package_redundancy + 1 else: cmpi_logging.logger.trace_warn( "Unknown raid_level: " + str(raid_level)) return None return redundancy
class Values(object): class OperationalStatus(object): Unknown = pywbem.Uint16(0) Other = pywbem.Uint16(1) OK = pywbem.Uint16(2) Degraded = pywbem.Uint16(3) Stressed = pywbem.Uint16(4) Predictive_Failure = pywbem.Uint16(5) Error = pywbem.Uint16(6) Non_Recoverable_Error = pywbem.Uint16(7) Starting = pywbem.Uint16(8) Stopping = pywbem.Uint16(9) Stopped = pywbem.Uint16(10) In_Service = pywbem.Uint16(11) No_Contact = pywbem.Uint16(12) Lost_Communication = pywbem.Uint16(13) Aborted = pywbem.Uint16(14) Dormant = pywbem.Uint16(15) Supporting_Entity_in_Error = pywbem.Uint16(16) Completed = pywbem.Uint16(17) Power_Mode = pywbem.Uint16(18) Relocating = pywbem.Uint16(19) # DMTF_Reserved = .. # Vendor_Reserved = 0x8000..