#!/usr/bin/env python # -*- coding: utf-8 -*- import argparse import logging from pylibftdi import BitBangDevice from pylibftdi._base import FtdiError from pylibftdi.device import Driver import six __author__ = "Nate Bohman" __credits__ = ["Nate Bohman"] __license__ = "LGPL" __maintainer__ = "Nate Bohman" __email__ = "natrinicle@gmail.com" __status__ = "Development" logger = logging.getLogger(__name__) # pylint: disable=invalid-name FAR_LED = {"ON": 0, "MID": 1, "OFF": 21} NEAR_LED = {"ON": 0, "MID": 2, "OFF": 42} def set_led(serial_num=None, far_led=None, near_led=None): """Set the LEDs on an FTDI BitBanged device. :param serial_num: (optional) Serial number of the FTDI device or None to use the first available device. :param far_led: (optional) Set the LED furthest from the USB connector to ON, MID, or OFF. :param near_led: (optional) Set the LED closest to the USB connector to ON, MID, or OFF. :type serial_num: :class:`str` or None :type far_led: :class:`str` :type near_led: :class:`str` """ if serial_num is not None: assert isinstance(serial_num, (six.string_types)) if far_led is not None: far_led = FAR_LED.get(far_led.upper()) assert isinstance(far_led, (int)) if near_led is not None: near_led = NEAR_LED.get(near_led.upper()) assert isinstance(near_led, (int)) try: with BitBangDevice(serial_num) as bb: if far_led is None: # Binary bitwise and the current state against the # bits that are significant to the far_led. far_led = bb.read_pins() & 21 if near_led is None: # Binary bitwise and the current state against the # bits that are significant to the near_led. near_led = bb.read_pins() & 42 serial = Driver().list_devices()[bb.device_index][2].decode() log_msg = "Setting {} far led to {}, near_led to {}".format( serial, [key for key in FAR_LED if FAR_LED[key] == far_led][0], [key for key in NEAR_LED if NEAR_LED[key] == near_led][0], ) logger.info(log_msg) bb.port = far_led | near_led except FtdiError as e: if "usb_open() failed" in str(e): err_msg = "".join( [ "Could not open any FTDI devices. Check permissions and ", "potentially add the following to udev rules " '\'SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ', 'ATTRS{idProduct}=="6001", MODE="0666"\' ', "or ensure the LED dongle is attached to the system.", ] ) raise EnvironmentError((-4, err_msg)) def configure_logging(logger, verbosity): """Take an int and configures logger levels accordingly where 0 = ERROR and 3 = DEBUG. :param logger: Logger object to configure. :param verbosity: Integer coresponding to logging level from 0 being ERROR and 3 being DEBUG. :type logger: :class:`logging.Logger` :type verbosity: :class:`int` """ assert isinstance(logger, (logging.Logger)) assert isinstance(verbosity, (int)) # The messages that display are controlled by the -v or -vv arguments in parse_arguments with # defaults being error or worse. A single v includes warning messages, a double v includes info # messages, and a triple v includes debug messages. Triple v also changes the log_format to # include the module, function name, and line number where logger was called from. # # Verbosity # parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0,) log_levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG] log_level = log_levels[ min(len(log_levels) - 1, verbosity) ] # capped to number of levels if log_level > logging.DEBUG: log_format = "%(asctime)s %(levelname)s %(message)s" else: log_format = ( "%(asctime)s %(levelname)s %(module)s %(funcName)s:%(lineno)s %(message)s" ) logging.basicConfig(format=log_format) logger.setLevel(level=log_level) def parse_args(): """Parse Arguments for set_led.""" parser = argparse.ArgumentParser( description="Set PureStorage Front Panel LED module states." ) # Base information (usernames, passwords, etc...) parser.add_argument( "-s", "--serial-num", dest="serial_num", action="store", required=False, help="Serial Number of LED module if more than one installed.", ) parser.add_argument( "-f", "--far-led", dest="far_led", action="store", required=False, type=str, help="State to set the LED furthest from the USB connector (ON, MID, OFF).", ) parser.add_argument( "-n", "--near-led", dest="near_led", action="store", required=False, type=str, help="State to set the LED closest to the USB connector (ON, MID, OFF).", ) # Verbosity parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0) # Positional arguments args = parser.parse_args() # Configure logging configure_logging(logger=logger, verbosity=args.verbose) return args def main(): """Main function that is called when script directly called.""" args = parse_args() set_led(serial_num=args.serial_num, far_led=args.far_led, near_led=args.near_led) if __name__ == "__main__": main()