from ceph_volume import sys_info, process
from ceph_volume.api import lvm
from ceph_volume.util import disk
+from ceph_volume.util.lsmdisk import LSMDisk
from ceph_volume.util.constants import ceph_disk_guids
report_template = """
'path',
'sys_api',
'device_id',
+ 'lsm_data',
]
pretty_report_sys_fields = [
'human_readable_size',
self._exists = None
self._is_lvm_member = None
self._parse()
+ self.lsm_data = self.fetch_lsm()
self.available_lvm, self.rejected_reasons_lvm = self._check_lvm_reject_reasons()
self.available_raw, self.rejected_reasons_raw = self._check_raw_reject_reasons()
self.device_id = self._get_device_id()
+ def fetch_lsm(self):
+ '''
+ Attempt to fetch libstoragemgmt (LSM) metadata, and return to the caller
+ as a dict. An empty dict is passed back to the caller if the target path
+ is not a block device, or lsm is unavailable on the host. Otherwise the
+ json returned will provide LSM attributes, and any associated errors that
+ lsm encountered when probing the device.
+ '''
+ devName = self.path.split('/')[-1]
+ if not os.path.exists('/sys/block/{}'.format(devName)):
+ return dict()
+
+ lsm_disk = LSMDisk(self.path)
+ if not lsm_disk.lsm_available:
+ return dict()
+
+ lsm_json = lsm_disk.json_report()
+
+ return lsm_json
+
def __lt__(self, other):
'''
Implementing this method and __eq__ allows the @total_ordering
--- /dev/null
+"""
+This module handles the interaction with libstoragemgmt for local disk
+devices. Interaction may fail with LSM for a number of issues, but the
+intent here is to make this a soft fail, since LSM related data is not
+a critical component of ceph-volume.
+"""
+import logging
+
+try:
+ import lsm
+ from lsm._local_disk import LocalDisk, LsmError
+except ImportError:
+ lsm_available = False
+else:
+ lsm_available = True
+
+logger = logging.getLogger(__name__)
+
+
+class LSMDisk:
+ def __init__(self, dev_path):
+ self.dev_path = dev_path
+ self.error_list = set()
+
+ if lsm_available:
+ self.lsm_available = True
+ self.disk = LocalDisk()
+ else:
+ self.lsm_available = False
+ self.error_list.add("libstoragemgmt (lsm module) is unavailable")
+ logger.info("LSM information is unavailable: libstoragemgmt is not installed")
+ self.disk = None
+
+ self.led_bits = None
+
+ @property
+ def errors(self):
+ """show any errors that the LSM interaction has encountered (str)"""
+ return ",".join(self.error_list)
+
+ def _query_lsm(self, func, path):
+ """Common method used to call the LSM functions, returning the function's result or None"""
+
+ # if disk is None, lsm is unavailable so all calls should return None
+ if self.disk is None:
+ return None
+
+ method = getattr(self.disk, func)
+ try:
+ output = method(path)
+ except LsmError as err:
+ # logger.exception("LSM Error: {}".format(err._msg))
+ self.error_list.add(err._msg)
+ return None
+ else:
+ return output
+
+ @property
+ def led_status(self):
+ """Fetch LED status, store in the LSMDisk object and return current status (int)"""
+ if self.led_bits is None:
+ bitfield = self._query_lsm('led_status_get', self.dev_path) or 1
+ self.led_bits = bitfield
+ return bitfield
+ else:
+ return self.led_bits
+
+ @property
+ def led_ident_state(self):
+ """Query a disks IDENT LED state to discover when it is On, Off or Unknown (str)"""
+ led_state = self.led_status
+ if led_state == 1:
+ return "Unsupported"
+ if led_state & lsm.Disk.LED_STATUS_IDENT_ON == lsm.Disk.LED_STATUS_IDENT_ON:
+ return "On"
+ elif led_state & lsm.Disk.LED_STATUS_IDENT_OFF == lsm.Disk.LED_STATUS_IDENT_OFF:
+ return "Off"
+ elif led_state & lsm.Disk.LED_STATUS_IDENT_UNKNOWN == lsm.Disk.LED_STATUS_IDENT_UNKNOWN:
+ return "Unknown"
+
+ return "Unsupported"
+
+ @property
+ def led_fault_state(self):
+ """Query a disks FAULT LED state to discover when it is On, Off or Unknown (str)"""
+ led_state = self.led_status
+ if led_state == 1:
+ return "Unsupported"
+ if led_state & lsm.Disk.LED_STATUS_FAULT_ON == lsm.Disk.LED_STATUS_FAULT_ON:
+ return "On"
+ elif led_state & lsm.Disk.LED_STATUS_FAULT_OFF == lsm.Disk.LED_STATUS_FAULT_OFF:
+ return "Off"
+ elif led_state & lsm.Disk.LED_STATUS_FAULT_UNKNOWN == lsm.Disk.LED_STATUS_FAULT_UNKNOWN:
+ return "Unknown"
+
+ return "Unsupported"
+
+ @property
+ def led_ident_support(self):
+ """Query the LED state to determine IDENT support: Unknown, Supported, Unsupported (str)"""
+ led_state = self.led_status
+ if led_state == 1:
+ return "Unknown"
+
+ ident_states = (
+ lsm.Disk.LED_STATUS_IDENT_ON +
+ lsm.Disk.LED_STATUS_IDENT_OFF +
+ lsm.Disk.LED_STATUS_IDENT_UNKNOWN
+ )
+
+ if (led_state & ident_states) == 0:
+ return "Unsupported"
+
+ return "Supported"
+
+ @property
+ def led_fault_support(self):
+ """Query the LED state to determine FAULT support: Unknown, Supported, Unsupported (str)"""
+ led_state = self.led_status
+ if led_state == 1:
+ return "Unknown"
+
+ fail_states = (
+ lsm.Disk.LED_STATUS_FAULT_ON +
+ lsm.Disk.LED_STATUS_FAULT_OFF +
+ lsm.Disk.LED_STATUS_FAULT_UNKNOWN
+ )
+
+ if led_state & fail_states == 0:
+ return "Unsupported"
+
+ return "Supported"
+
+ @property
+ def health(self):
+ """Determine the health of the disk from LSM : Unknown, Fail, Warn or Good (str)"""
+ health_map = {
+ -1: "Unknown",
+ 0: "Fail",
+ 1: "Warn",
+ 2: "Good",
+ }
+ _health_int = self._query_lsm('health_status_get', self.dev_path)
+ if _health_int is None:
+ _health_int = -1
+ return health_map.get(_health_int, "Unknown")
+
+ @property
+ def transport(self):
+ """Translate a disks link type to a human readable format (str)"""
+ _link_type = self._query_lsm('link_type_get', self.dev_path)
+
+ if _link_type is not None:
+ _transport_map = {
+ lsm.Disk.LINK_TYPE_UNKNOWN: "Unavailable",
+ lsm.Disk.LINK_TYPE_FC: "Fibre Channel",
+ lsm.Disk.LINK_TYPE_SSA: "IBM SSA",
+ lsm.Disk.LINK_TYPE_SBP: "Serial Bus",
+ lsm.Disk.LINK_TYPE_SRP: "SCSI RDMA",
+ lsm.Disk.LINK_TYPE_ISCSI: "iSCSI",
+ lsm.Disk.LINK_TYPE_SAS: "SAS",
+ lsm.Disk.LINK_TYPE_ADT: "ADT (Tape)",
+ lsm.Disk.LINK_TYPE_ATA: "ATA/SATA",
+ lsm.Disk.LINK_TYPE_USB: "USB",
+ lsm.Disk.LINK_TYPE_SOP: "SCSI over PCI-E",
+ lsm.Disk.LINK_TYPE_PCIE: "PCI-E",
+ }
+ return _transport_map.get(_link_type, "Unknown")
+ else:
+ return "Unknown"
+
+ @property
+ def media_type(self):
+ """Use the rpm value to determine the type of disk media: Flash or HDD (str)"""
+ _rpm = self._query_lsm('rpm_get', self.dev_path)
+ if _rpm is not None:
+ if _rpm == 0:
+ return "Flash"
+ elif _rpm > 1:
+ return "HDD"
+
+ return "Unknown"
+
+ def json_report(self):
+ """Return the LSM related metadata for the current local disk (dict)"""
+ if self.lsm_available:
+ return {
+ "serialNum": self._query_lsm('serial_num_get', self.dev_path) or "Unknown",
+ "transport": self.transport,
+ "mediaType": self.media_type,
+ "rpm": self._query_lsm('rpm_get', self.dev_path) or "Unknown",
+ "linkSpeed": self._query_lsm('link_speed_get', self.dev_path) or "Unknown",
+ "health": self.health,
+ "ledSupport": {
+ "IDENTsupport": self.led_ident_support,
+ "IDENTstatus": self.led_ident_state,
+ "FAILsupport": self.led_fault_support,
+ "FAILstatus": self.led_fault_state,
+ },
+ "errors": list(self.error_list)
+ }
+ else:
+ return dict()