pySMART.device_list

This module contains the definition of the DeviceList class, used to represent all physical storage devices connected to the system. Once initialized, the sole member devices will contain a list of Device objects.

This class has no public methods. All interaction should be through the Device class API.

View Source
# Copyright (C) 2014 Marc Herndon
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301, USA.
#
################################################################
"""
This module contains the definition of the `DeviceList` class, used to
represent all physical storage devices connected to the system.
Once initialized, the sole member `devices` will contain a list of `Device`
objects.

This class has no public methods.  All interaction should be through the
`Device` class API.
"""
# Python built-ins
from subprocess import Popen, PIPE
import re

# pySMART module imports
from .device import Device
from .utils import SMARTCTL_PATH


class DeviceList(object):
    """
    Represents a list of all the storage devices connected to this computer.
    """

    def __init__(self, init=True):
        """
        Instantiates and optionally initializes the `DeviceList`.

        ###Args:
        * **init (bool):** By default, `pySMART.device_list.DeviceList.devices`
        is populated with `Device` objects during instantiation. Setting init
        to False will skip initialization and create an empty
        `pySMART.device_list.DeviceList` object instead.
        """
        self.devices = []
        """
        **(list of `Device`):** Contains all storage devices detected during
        instantiation, as `Device` objects.
        """
        if init:
            self._initialize()

    def __repr__(self):
        """Define a basic representation of the class object."""
        rep = "<DeviceList contents:\n"
        for device in self.devices:
            rep += str(device) + '\n'
        return rep + '>'
        # return "<DeviceList contents:%r>" % (self.devices)

    def _cleanup(self):
        """
        Removes duplicate ATA devices that correspond to an existing CSMI
        device. Also removes any device with no capacity value, as this
        indicates removable storage, ie: CD/DVD-ROM, ZIP, etc.
        """
        # We can't operate directly on the list while we're iterating
        # over it, so we collect indeces to delete and remove them later
        to_delete = []
        # Enumerate the list to get tuples containing indeces and values
        for index, device in enumerate(self.devices):
            if device.interface == 'csmi':
                for otherindex, otherdevice in enumerate(self.devices):
                    if (otherdevice.interface == 'ata' or
                            otherdevice.interface == 'sata'):
                        if device.serial == otherdevice.serial:
                            to_delete.append(otherindex)
                            device._sd_name = otherdevice.name
            if device.capacity is None and index not in to_delete:
                to_delete.append(index)
        # Recreate the self.devices list without the marked indeces
        self.devices[:] = [v for i, v in enumerate(self.devices)
                           if i not in to_delete]

    def _initialize(self):
        """
        Scans system busses for attached devices and add them to the
        `DeviceList` as `Device` objects.
        """
        cmd = Popen([SMARTCTL_PATH, '--scan-open'], stdout=PIPE, stderr=PIPE)
        _stdout, _stderr = [i.decode('utf8') for i in cmd.communicate()]
        for line in _stdout.split('\n'):
            if not ('failed:' in line or line == ''):
                groups = re.compile(
                    '^(/dev/\S+)\s+-d\s+(\S+)').match(line).groups()
                name = groups[0]
                interface = groups[1]
                self.devices.append(Device(name, interface=interface))

        # Remove duplicates and unwanted devices (optical, etc.) from the list
        self._cleanup()
        # Sort the list alphabetically by device name
        self.devices.sort(key=lambda device: device.name)

    def __getitem__(self, index: int) -> Device:
        """Returns an element from self.devices

        Args:
            index (int): An index of self.devices

        Returns:
            Device: Returns a Device that is located on the asked index
        """
        return self.devices[index]


__all__ = ['DeviceList']
#   class DeviceList:
View Source
class DeviceList(object):
    """
    Represents a list of all the storage devices connected to this computer.
    """

    def __init__(self, init=True):
        """
        Instantiates and optionally initializes the `DeviceList`.

        ###Args:
        * **init (bool):** By default, `pySMART.device_list.DeviceList.devices`
        is populated with `Device` objects during instantiation. Setting init
        to False will skip initialization and create an empty
        `pySMART.device_list.DeviceList` object instead.
        """
        self.devices = []
        """
        **(list of `Device`):** Contains all storage devices detected during
        instantiation, as `Device` objects.
        """
        if init:
            self._initialize()

    def __repr__(self):
        """Define a basic representation of the class object."""
        rep = "<DeviceList contents:\n"
        for device in self.devices:
            rep += str(device) + '\n'
        return rep + '>'
        # return "<DeviceList contents:%r>" % (self.devices)

    def _cleanup(self):
        """
        Removes duplicate ATA devices that correspond to an existing CSMI
        device. Also removes any device with no capacity value, as this
        indicates removable storage, ie: CD/DVD-ROM, ZIP, etc.
        """
        # We can't operate directly on the list while we're iterating
        # over it, so we collect indeces to delete and remove them later
        to_delete = []
        # Enumerate the list to get tuples containing indeces and values
        for index, device in enumerate(self.devices):
            if device.interface == 'csmi':
                for otherindex, otherdevice in enumerate(self.devices):
                    if (otherdevice.interface == 'ata' or
                            otherdevice.interface == 'sata'):
                        if device.serial == otherdevice.serial:
                            to_delete.append(otherindex)
                            device._sd_name = otherdevice.name
            if device.capacity is None and index not in to_delete:
                to_delete.append(index)
        # Recreate the self.devices list without the marked indeces
        self.devices[:] = [v for i, v in enumerate(self.devices)
                           if i not in to_delete]

    def _initialize(self):
        """
        Scans system busses for attached devices and add them to the
        `DeviceList` as `Device` objects.
        """
        cmd = Popen([SMARTCTL_PATH, '--scan-open'], stdout=PIPE, stderr=PIPE)
        _stdout, _stderr = [i.decode('utf8') for i in cmd.communicate()]
        for line in _stdout.split('\n'):
            if not ('failed:' in line or line == ''):
                groups = re.compile(
                    '^(/dev/\S+)\s+-d\s+(\S+)').match(line).groups()
                name = groups[0]
                interface = groups[1]
                self.devices.append(Device(name, interface=interface))

        # Remove duplicates and unwanted devices (optical, etc.) from the list
        self._cleanup()
        # Sort the list alphabetically by device name
        self.devices.sort(key=lambda device: device.name)

    def __getitem__(self, index: int) -> Device:
        """Returns an element from self.devices

        Args:
            index (int): An index of self.devices

        Returns:
            Device: Returns a Device that is located on the asked index
        """
        return self.devices[index]

Represents a list of all the storage devices connected to this computer.

#   DeviceList(init=True)
View Source
    def __init__(self, init=True):
        """
        Instantiates and optionally initializes the `DeviceList`.

        ###Args:
        * **init (bool):** By default, `pySMART.device_list.DeviceList.devices`
        is populated with `Device` objects during instantiation. Setting init
        to False will skip initialization and create an empty
        `pySMART.device_list.DeviceList` object instead.
        """
        self.devices = []
        """
        **(list of `Device`):** Contains all storage devices detected during
        instantiation, as `Device` objects.
        """
        if init:
            self._initialize()

Instantiates and optionally initializes the DeviceList.

Args:

#   devices

(list of Device): Contains all storage devices detected during instantiation, as Device objects.