def read_data(self):
        """Send a dict of drive status to the DiskMsgHandler"""

        for drive_data in self._drive_data:
            logger.info(f"HPIMonitor, read_data: {str(drive_data)}")
            # Send it to the disk message handler to be processed and transmitted
            self._write_internal_msgQ(DiskMsgHandler.name(), drive_data)
Esempio n. 2
0
    def _notify_DiskMsgHandler(self, status_file: str, serial_num_file):
        """Send the event to the disk message handler for generating JSON message"""

        if not os.path.isfile(status_file):
            logger.warn(
                f"status_file: {status_file} does not exist, ignoring.")
            return

        if not os.path.isfile(serial_num_file):
            logger.warn(
                f"serial_num_file: {serial_num_file} does not exist, ignoring."
            )
            return

        # Read in status and see if it has changed
        with open(status_file, "r") as datafile:
            status = datafile.read().replace('\n', '')

        # See if there's a reason file
        reason_file = os.path.join(os.path.dirname(status_file), "reason")
        if os.path.isfile(reason_file):
            with open(reason_file, "r") as datafile:
                reason = datafile.read().replace('\n', '')
                status = f"{status}_{reason}"

        # Do nothing if the drive status has not changed
        if self._drive_status[os.path.dirname(status_file)] == status:
            return

        # Update the status for this drive
        self._log_debug(
            f"Status change, status_file: {status_file}, status: {status}")
        self._drive_status[os.path.dirname(status_file)] = status

        # Read in the serial number
        with open(serial_num_file, "r") as datafile:
            serial_number = datafile.read().replace('\n', '')

        # Remove base dcs dir since it contains no relevant data
        data_str = status_file[len(self._drive_mngr_base_dir) + 1:]

        # Send a message to the disk manager handler to create and transmit json msg
        internal_json_msg = json.dumps({
            "sensor_response_type": "disk_status_drivemanager",
            "event_path": data_str,
            "status": status,
            "serial_number": serial_number
        })

        # Send the event to disk message handler to generate json message
        self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)

        # Reset debug mode if persistence is not enabled
        self._disable_debug_if_persist_false()
    def _process_msg(self, jsonMsg):
        """Parses the incoming message and handles appropriately"""
        self._log_debug(f"_process_msg, jsonMsg: {jsonMsg}")

        if isinstance(jsonMsg, dict) is False:
            jsonMsg = json.loads(jsonMsg)

        # Parse out the uuid so that it can be sent back in Ack message
        uuid = None
        if jsonMsg.get("sspl_ll_msg_header").get("uuid") is not None:
            uuid = jsonMsg.get("sspl_ll_msg_header").get("uuid")
            self._log_debug(f"_processMsg, uuid: {uuid}")

        if jsonMsg.get("actuator_request_type").get("node_controller").get("node_request") is not None:
            node_request = jsonMsg.get("actuator_request_type").get("node_controller").get("node_request")
            self._log_debug(f"_processMsg, node_request: {node_request}")

            # Parse out the component field in the node_request
            component = node_request[0:4]

            # Handle generic command line requests
            if component == 'SSPL':
                # Query the Zope GlobalSiteManager for an object implementing the MOTR actuator
                if self._command_line_actuator is None:
                    from actuators.Icommand_line import ICommandLine

                    command_line_actuator_class = self._queryUtility(ICommandLine)
                    # Instantiate CommandLine Actuator only if class is loaded
                    if command_line_actuator_class:
                        self._command_line_actuator = command_line_actuator_class(self._conf_reader)
                    else:
                        logger.warn("CommandLine Actuator not loaded")
                        json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                # Perform the request and get the response
                command_line_response = self._command_line_actuator.perform_request(jsonMsg).strip()
                self._log_debug(f"_process_msg, command line response: {command_line_response}")

                json_msg = AckResponseMsg(node_request, command_line_response, uuid).getJson()
                self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            # Handle LED effects using the HPI actuator
            elif component == "LED:":
                # HPI related operations are not supported in VM environment.
                if self._is_env_vm():
                    logger.warn("HPI operations are not supported in current environment")
                    return
                # Query the Zope GlobalSiteManager for an object implementing the IHPI actuator
                if self._HPI_actuator is None:
                    from actuators.Ihpi import IHPI
                    # Load HPIActuator class
                    HPI_actuator_class = self._queryUtility(IHPI)
                    # Instantiate HPIActuator only if class is loaded
                    if HPI_actuator_class:
                        self._HPI_actuator = HPI_actuator_class(self._conf_reader)
                    else:
                        logger.warn("HPIActuator not loaded")
                        if self._product.lower() in [x.lower() for x in enabled_products]:
                            json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                            self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                    self._log_debug(f"_process_msg, _HPI_actuator name: {self._HPI_actuator.name()}")

                    # Perform the request using HPI and get the response
                    hpi_response = self._HPI_actuator.perform_request(jsonMsg).strip()
                    self._log_debug(f"_process_msg, hpi_response: {hpi_response}")

                    json_msg = AckResponseMsg(node_request, hpi_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            # Set the Bezel LED color using the GEM interface
            elif component == "BEZE":
                # Query the Zope GlobalSiteManager for an object implementing the IGEM actuator
                if self._GEM_actuator is None:
                    self._GEM_actuator = self._queryUtility(IGEM)(self._conf_reader)
                    self._log_debug(f"_process_msg, _GEM_actuator name: {self._GEM_actuator.name()}")

                # Perform the request using GEM and get the response
                gem_response = self._GEM_actuator.perform_request(jsonMsg).strip()
                self._log_debug(f"_process_msg, gem_response: {gem_response}")

                json_msg = AckResponseMsg(node_request, gem_response, uuid).getJson()
                self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            elif component == "PDU:":
                # Query the Zope GlobalSiteManager for an object implementing the IPDU actuator
                if self._PDU_actuator is None:
                    from actuators.Ipdu import IPDU

                    PDU_actuator_class = self._queryUtility(IPDU)
                    # Instantiate RaritanPDU Actuator only if class is loaded
                    if PDU_actuator_class:
                        self._PDU_actuator = PDU_actuator_class(self._conf_reader)
                    else:
                        logger.warn("RaritanPDU Actuator not loaded")
                        json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                # Perform the request on the PDU and get the response
                pdu_response = self._PDU_actuator.perform_request(jsonMsg).strip()
                self._log_debug(f"_process_msg, pdu_response: {pdu_response}")

                json_msg = AckResponseMsg(node_request, pdu_response, uuid).getJson()
                self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            elif component == "RAID":
                # If the state is INITIALIZED, We can assume that actuator is
                # ready to perform operation.
                if actuator_state_manager.is_initialized("RAIDactuator"):
                    self._log_debug(f"_process_msg, _RAID_actuator name: {self._RAID_actuator.name()}")
                    self._execute_raid_request(
                        node_request, self._RAID_actuator, jsonMsg, uuid)

                # If the state is INITIALIZING, need to send message
                elif actuator_state_manager.is_initializing("RAIDactuator"):
                    # This state will not be reached. Kept here for consistency.
                    logger.info("RAID actuator is initializing")
                    busy_json_msg = AckResponseMsg(
                        node_request, "BUSY", uuid, error_no=errno.EBUSY).getJson()
                    self._write_internal_msgQ(
                        "RabbitMQegressProcessor", busy_json_msg)

                elif actuator_state_manager.is_imported("RAIDactuator"):
                    # This case will be for first request only. Subsequent
                    # requests will go to INITIALIZED state case.
                    logger.info("RAID actuator is imported and initializing")

                    from actuators.Iraid import IRAIDactuator
                    actuator_state_manager.set_state(
                            "RAIDactuator", actuator_state_manager.INITIALIZING)
                    # Query the Zope GlobalSiteManager for an object implementing the IRAIDactuator
                    raid_actuator_class = self._queryUtility(IRAIDactuator)
                    if raid_actuator_class:
                        # NOTE: Instantiation part should not time consuming
                        # otherwise NodeControllerMsgHandler will get block
                        # and will not be able serve any subsequent requests.
                        # This applies to instantiation of evey actuator.
                        self._RAID_actuator = raid_actuator_class()
                        logger.info(f"_process_msg, _RAID_actuator name: {self._RAID_actuator.name()}")
                        self._execute_raid_request(
                            node_request, self._RAID_actuator, jsonMsg, uuid)
                        actuator_state_manager.set_state(
                            "RAIDactuator", actuator_state_manager.INITIALIZED)
                    else:
                        logger.warn("RAID actuator is not instantiated")

                # If there is no entry for actuator in table, We can assume
                # that it is not loaded for some reason.
                else:
                    logger.warn("RAID actuator is not loaded or not supported")

            elif component == "IPMI":
                # Query the Zope GlobalSiteManager for an object implementing the IPMI actuator
                if self._IPMI_actuator is None:
                    from actuators.Iipmi import Iipmi

                    IPMI_actuator_class = self._queryUtility(Iipmi)
                    # Instantiate IPMI Actuator only if class is loaded
                    if IPMI_actuator_class:
                        self._IPMI_actuator = IPMI_actuator_class(self._conf_reader)
                    else:
                        logger.warn("IPMI Actuator not loaded")
                        json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                # Perform the IPMI request on the node and get the response
                ipmi_response = self._IPMI_actuator.perform_request(jsonMsg).strip()
                self._log_debug(f"_process_msg, ipmi_response: {ipmi_response}")

                json_msg = AckResponseMsg(node_request, ipmi_response, uuid).getJson()
                self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            elif component == "STOP":
                # HPI related operations are not supported in VM environment.
                if self._is_env_vm():
                    logger.warn("HPI operations are not supported in current environment")
                    return
                # Query the Zope GlobalSiteManager for an object implementing the IHPI actuator
                if self._HPI_actuator is None:
                    from actuators.Ihpi import IHPI
                    # Load HPIActuator class
                    HPI_actuator_class = self._queryUtility(IHPI)
                    # Instantiate HPIActuator only if class is loaded
                    if HPI_actuator_class:
                        self._HPI_actuator = HPI_actuator_class(self._conf_reader)
                    else:
                        logger.warn("HPIActuator not loaded")
                        if self._product.lower() in [x.lower() for x in enabled_products]:
                            json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                            self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                    self._log_debug(f"_process_msg, _HPI_actuator name: {self._HPI_actuator.name()}")

                    # Parse out the drive to stop
                    drive_request = node_request[12:].strip()
                    self._log_debug(f"perform_request, drive to stop: {drive_request}")

                    # Append POWER_OFF to notify HPI actuator of desired state
                    jsonMsg["actuator_request_type"]["node_controller"]["node_request"] = \
                            f"DISK: set {drive_request} POWER_OFF"
                    self._log_debug(f"_process_msg, jsonMsg: {jsonMsg}")

                    # Perform the request using HPI and get the response
                    hpi_response = self._HPI_actuator.perform_request(jsonMsg).strip()
                    self._log_debug(f"_process_msg, hpi_response: {hpi_response}")

                    # Simplify success message as external apps don't care about details
                    if "Success" in hpi_response:
                        hpi_response = "Successful"

                    json_msg = AckResponseMsg(node_request, hpi_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            elif component == "STAR":
                # HPI related operations are not supported in VM environment.
                if self._is_env_vm():
                    logger.warn("HPI operations are not supported in current environment")
                    return
                # Query the Zope GlobalSiteManager for an object implementing the IHPI actuator
                if self._HPI_actuator is None:
                    from actuators.Ihpi import IHPI
                    # Load HPIActuator class
                    HPI_actuator_class = self._queryUtility(IHPI)
                    # Instantiate HPIActuator only if class is loaded
                    if HPI_actuator_class:
                        self._HPI_actuator = HPI_actuator_class(self._conf_reader)
                    else:
                        logger.warn("HPIActuator not loaded")
                        if self._product.lower() in [x.lower() for x in enabled_products]:
                            json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                            self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                    self._log_debug(f"_process_msg, _HPI_actuator name: {self._HPI_actuator.name()}")

                    # Parse out the drive to start
                    drive_request = node_request[13:].strip()
                    self._log_debug(f"perform_request, drive to start: {drive_request}")

                    # Append POWER_ON to notify HPI actuator of desired state
                    jsonMsg["actuator_request_type"]["node_controller"]["node_request"] = \
                            f"DISK: set {drive_request} POWER_ON"
                    self._log_debug(f"_process_msg, jsonMsg: {jsonMsg}")

                    # Perform the request using HPI and get the response
                    hpi_response = self._HPI_actuator.perform_request(jsonMsg).strip()
                    self._log_debug(f"_process_msg, hpi_response: {hpi_response}")

                    # Simplify success message as external apps don't care about details
                    if "Success" in hpi_response:
                        hpi_response = "Successful"

                    json_msg = AckResponseMsg(node_request, hpi_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)


            elif component == "RESE":
                # HPI related operations are not supported in VM environment.
                if self._is_env_vm():
                    logger.warn("HPI operations are not supported in current environment")
                    return
                # Query the Zope GlobalSiteManager for an object implementing the IHPI actuator
                if self._HPI_actuator is None:
                    from actuators.Ihpi import IHPI
                    # Load HPIActuator class
                    HPI_actuator_class = self._queryUtility(IHPI)
                    # Instantiate HPIActuator only if class is loaded
                    if HPI_actuator_class:
                        self._HPI_actuator = HPI_actuator_class(self._conf_reader)
                    else:
                        logger.warn("HPIActuator not loaded")
                        if self._product.lower() in [x.lower() for x in enabled_products]:
                            json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                            self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return

                    self._log_debug(f"_process_msg, _HPI_actuator name: {self._HPI_actuator.name()}")

                    # Parse out the drive to power cycle
                    drive_request = node_request[13:].strip()
                    self._log_debug(f"perform_request, drive to power cycle: {drive_request}")

                    # Append POWER_OFF and then POWER_ON to notify HPI actuator of desired state
                    jsonMsg["actuator_request_type"]["node_controller"]["node_request"] = \
                            f"DISK: set {drive_request} POWER_OFF"
                    self._log_debug(f"_process_msg, jsonMsg: {jsonMsg}")

                    # Perform the request using HPI and get the response
                    hpi_response = self._HPI_actuator.perform_request(jsonMsg).strip()
                    self._log_debug(f"_process_msg, hpi_response: {hpi_response}")

                    # Check for success and power the disk back on
                    if "Success" in hpi_response:
                        # Append POWER_ON to notify HPI actuator of desired state
                        jsonMsg["actuator_request_type"]["node_controller"]["node_request"] = \
                                   f"DISK: set {drive_request} POWER_ON"
                        self._log_debug(f"_process_msg, jsonMsg: {jsonMsg}")

                        # Perform the request using HPI and get the response
                        hpi_response = self._HPI_actuator.perform_request(jsonMsg).strip()
                        self._log_debug(f"_process_msg, hpi_response: {hpi_response}")

                            # Simplify success message as external apps don't care about details
                        if "Success" in hpi_response:
                            hpi_response = "Successful"

                    json_msg = AckResponseMsg(node_request, hpi_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

            elif component == "HDPA":
                # If the state is INITIALIZED, We can assume that actuator is
                # ready to perform operation.
                if actuator_state_manager.is_initialized("Hdparm"):
                    logger.info(f"_process_msg, Hdparm_actuator name: {self._hdparm_actuator.name()}")
                    # Perform the hdparm request on the node and get the response
                    hdparm_response = self._hdparm_actuator.perform_request(jsonMsg).strip()
                    self._log_debug(f"_process_msg, hdparm_response: {hdparm_response}")

                    json_msg = AckResponseMsg(node_request, hdparm_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)

                # If the state is INITIALIZING, need to send message
                elif actuator_state_manager.is_initializing("Hdparm"):
                    # This state will not be reached. Kept here for consistency.
                    logger.info("Hdparm actuator is initializing")
                    busy_json_msg = AckResponseMsg(
                        node_request, "BUSY", uuid, error_no=errno.EBUSY).getJson()
                    self._write_internal_msgQ(
                        "RabbitMQegressProcessor", busy_json_msg)

                elif actuator_state_manager.is_imported("Hdparm"):
                    # This case will be for first request only. Subsequent
                    # requests will go to INITIALIZED state case.
                    logger.info("Hdparm actuator is imported and initializing")
                    # Query the Zope GlobalSiteManager for an object
                    # implementing the hdparm actuator.
                    from actuators.Ihdparm import IHdparm
                    actuator_state_manager.set_state(
                            "Hdparm", actuator_state_manager.INITIALIZING)
                    hdparm_actuator_class = self._queryUtility(IHdparm)
                    if hdparm_actuator_class:
                        # NOTE: Instantiation part should not time consuming
                        # otherwise NodeControllerMsgHandler will get block and will
                        # not be able serve any subsequent requests. This applies
                        # to instantiation of evey actuator.
                        self._hdparm_actuator = hdparm_actuator_class()
                        self._log_debug(f"_process_msg, _hdparm_actuator name: {self._hdparm_actuator.name()}")
                        # Perform the hdparm request on the node and get the response
                        hdparm_response = self._hdparm_actuator.perform_request(jsonMsg).strip()
                        self._log_debug(f"_process_msg, hdparm_response: {hdparm_response}")

                        json_msg = AckResponseMsg(node_request, hdparm_response, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        actuator_state_manager.set_state(
                            "Hdparm", actuator_state_manager.INITIALIZED)
                    else:
                        logger.info("Hdparm actuator is not instantiated")

                # If there is no entry for actuator in table, We can assume
                # that it is not loaded for some reason.
                else:
                    logger.info("Hdparm actuator is not loaded or not supported")

            elif component == "SMAR":
                # Parse out the drive request field in json msg
                node_request = jsonMsg.get("actuator_request_type").get("node_controller").get("node_request")
                drive_request = node_request[12:].strip()
                self._log_debug(f"perform_request, drive: {drive_request}")

                # If the drive field is an asterisk then send all the smart results for all drives available
                if drive_request == "*":
                    # Send the event to SystemdWatchdog to schedule SMART test
                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "disk_smart_test",
                         "serial_number" : "*",
                         "node_request" : self.host_id,
                         "uuid" : uuid
                         })

                    self._write_internal_msgQ("SystemdWatchdog", internal_json_msg)
                    return

                # Put together a message to get the serial number of the drive using hdparm tool
                if drive_request.startswith("/"):
                    serial_number, error = self._retrieve_serial_number(drive_request)

                    # Send error response back on ack channel
                    if error != "":
                        json_msg = AckResponseMsg(node_request, error, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return
                else:
                    if self._smartctl_actuator is None:
                        from actuators.Ismartctl import ISmartctl
                        smartctl_actuator_class = self._queryUtility(ISmartctl)
                        if smartctl_actuator_class:
                            self._smartctl_actuator = self._queryUtility(ISmartctl)()
                            self._log_debug("_process_msg, _smart_actuator name: %s" % self._smartctl_actuator.name())
                        else:
                            logger.error(" No module Smartctl is present to load")
                    serial_compare = self._smartctl_actuator._check_serial_number(drive_request)
                    if not serial_compare:
                        json_msg = AckResponseMsg(node_request, "Drive Not Found", uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return
                    else:
                        serial_number = drive_request

                    # Send the event to SystemdWatchdog to schedule SMART test
                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "disk_smart_test",
                            "serial_number" : serial_number,
                            "node_request" : node_request,
                            "uuid" : uuid
                        })

                    self._write_internal_msgQ("SystemdWatchdog", internal_json_msg)

            elif component == "DRVM":
                # Requesting the current status from drivemanager
                # Parse out the drive request field in json msg
                node_request = jsonMsg.get("actuator_request_type").get("node_controller").get("node_request")
                drive_request = node_request[15:].strip()
                self._log_debug(f"perform_request, drive: {drive_request}")

                # If the drive field is an asterisk then send all the drivemanager results for all drives available
                if drive_request == "*":
                    # Send a message to the disk message handler to lookup the drivemanager status and send it out
                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "drvmngr_status",
                         "serial_number" : "*",
                         "node_request" : self.host_id,
                         "uuid" : uuid
                         })

                    # Send the event to disk message handler to generate json message
                    self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)
                    return

                # Put together a message to get the serial number of the drive using hdparm tool
                if drive_request.startswith("/"):
                    serial_number, error = self._retrieve_serial_number(drive_request)

                    # Send error response back on ack channel
                    if error != "":
                        json_msg = AckResponseMsg(node_request, error, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return
                else:
                    serial_number = drive_request

                # Send a message to the disk message handler to lookup the smart status and send it out
                internal_json_msg = json.dumps(
                    {"sensor_request_type" : "drvmngr_status",
                     "serial_number" : serial_number,
                     "node_request" : node_request,
                     "uuid" : uuid
                    })

                # Send the event to disk message handler to generate json message
                self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)

            elif component == "HPI_":
                # Requesting the current status from HPI data
                # Parse out the drive request field in json msg
                if self._is_env_vm():
                    logger.warn("HPI operations are not supported in current environment")
                    return

                if self.setup == 'cortx':
                    logger.warn("HPIMonitor not loaded")
                    json_msg = AckResponseMsg(node_request, NodeControllerMsgHandler.UNSUPPORTED_REQUEST, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                    return

                node_request = jsonMsg.get("actuator_request_type").get("node_controller").get("node_request")
                drive_request = node_request[11:].strip()
                self._log_debug(f"perform_request, drive: {drive_request}")

                # If the drive field is an asterisk then send all the hpi results for all drives available
                if drive_request == "*":
                    # Send a message to the disk message handler to lookup the hpi status and send it out
                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "hpi_status",
                         "serial_number" : "*",
                         "node_request" : self.host_id,
                         "uuid" : uuid
                         })

                    # Send the event to disk message handler to generate json message
                    self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)
                    return

                # Put together a message to get the serial number of the drive using hdparm tool
                if drive_request.startswith("/"):
                    serial_number, error = self._retrieve_serial_number(drive_request)

                    # Send error response back on ack channel
                    if error != "":
                        json_msg = AckResponseMsg(node_request, error, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return
                else:
                    serial_number = drive_request

                # Send a message to the disk message handler to lookup the smart status and send it out
                internal_json_msg = json.dumps(
                    {"sensor_request_type" : "hpi_status",
                     "serial_number" : serial_number,
                     "node_request" : node_request,
                     "uuid" : uuid
                    })

                # Send the event to disk message handler to generate json message
                self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)

            elif component == "SIMU":
                # Requesting to simulate an event
                # Parse out the simulated request field
                node_request = jsonMsg.get("actuator_request_type").get("node_controller").get("node_request")
                sim_request = node_request[9:].strip().split(" ")
                self._log_debug(f"perform_request, sim_request: {str(sim_request)}")

                # Put together a message to get the serial number of the drive using hdparm tool
                if sim_request[1].startswith("/"):
                    serial_number, error = self._retrieve_serial_number(sim_request[1])

                    # Send error response back on ack channel
                    if error != "":
                        json_msg = AckResponseMsg(node_request, error, uuid).getJson()
                        self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                        return
                else:
                    serial_number = sim_request[1]

                # SMART simulation requests are sent to SystemdWatchdog
                if sim_request[0] == "SMART_FAILURE":
                    logger.info(f"NodeControllerMsgHandler, simulating SMART_FAILURE on drive: {serial_number}")

                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "simulate_failure",
                         "serial_number" : serial_number,
                         "node_request" : sim_request[0],
                         "uuid" : uuid
                         })

                    # Send the event to SystemdWatchdog to handle it from here
                    self._write_internal_msgQ("SystemdWatchdog", internal_json_msg)

                else:
                    # Send a message to the disk message handler to handle simulation request
                    internal_json_msg = json.dumps(
                        {"sensor_request_type" : "sim_event",
                         "serial_number" : serial_number,
                         "node_request" : sim_request[0],
                         "uuid" : uuid
                         })

                    # Send the event to disk message handler to generate json message
                    self._write_internal_msgQ(DiskMsgHandler.name(), internal_json_msg)

            elif component == "NDHW":
                # NDHW Stands for Node HW.
                try:
                    # Load and Instantiate the Actuator for the first request
                    if self._NodeHW_actuator is None:
                        from actuators.impl.generic.node_hw import NodeHWactuator
                        from framework.utils.ipmi_client import IpmiFactory
                        self.ipmi_client_name = self._conf_reader._get_value_with_default(
                            self.NODE_HW_ACTUATOR, self.IPMI_IMPLEMENTOR,
                            "ipmitool")
                        ipmi_factory = IpmiFactory()
                        ipmi_client = \
                           ipmi_factory.get_implementor(self.ipmi_client_name)
                        # Instantiate NodeHWactuator only if class is loaded
                        if ipmi_client is not None:
                            self._NodeHW_actuator = NodeHWactuator(ipmi_client, self._conf_reader)
                            self._NodeHW_actuator.initialize()
                        else:
                            logger.error(f"IPMI client: '{self.ipmi_client_name}' doesn't exist")
                            return
                    node_request = jsonMsg.get("actuator_request_type")
                    # Perform the NodeHW request on the node and get the response
                    #TODO: Send message to Ack as well as Sensor in their respective channel.
                    node_hw_response = self._NodeHW_actuator.perform_request(node_request)
                    self._log_debug(f"_process_msg, node_hw_response: {node_hw_response}")
                    json_msg = NodeHwAckResponseMsg(node_request, node_hw_response, uuid).getJson()
                    self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
                except ImportError as e:
                    logger.error(f"Modules could not be loaded: {e}")
                    return
                except Exception as e:
                    logger.error(f"NodeControllerMsgHandler, _process_msg, Exception in request handling: {e}")
                    return

            else:
                response = f"NodeControllerMsgHandler, _process_msg, unknown node controller msg: {node_request}"
                self._log_debug(response)

                json_msg = AckResponseMsg(node_request, response, uuid).getJson()
                self._write_internal_msgQ(RabbitMQegressProcessor.name(), json_msg)
Esempio n. 4
0
    def _init_drive_status(self):
        # Allow time for the drivemanager to come up and populate the directory
        time.sleep(5)

        # Ensure there are enclosures present
        self._validate_drive_manager_dir()

        enclosures = os.listdir(self._drive_mngr_base_dir)
        # Remove the 'discovery' file
        enclosures = [enclosure for enclosure in enclosures \
                 if not os.path.isdir(os.path.join(self._drive_mngr_base_dir, enclosure))]

        for enclosure in enclosures:
            disk_dir = os.path.join(self._drive_mngr_base_dir, enclosure,
                                    "disk")
            logger.info(f"DriveManager initializing: {disk_dir}")

            disks = os.listdir(disk_dir)
            for disk in disks:
                # Read in the status file for each disk and fill into dict
                pathname = os.path.join(disk_dir, disk)
                # Ignore the discovery file
                if not os.path.isdir(pathname):
                    continue

                # Read in the serial number for the disk
                serial_num_file = os.path.join(pathname, "serial_number")
                if not os.path.isfile(serial_num_file):
                    logger.error(
                        f"DriveManager error no serial_number file for disk: {disk}"
                    )
                    continue
                try:
                    with open(serial_num_file, "r") as datafile:
                        serial_number = datafile.read().replace('\n', '')
                except Exception as e:
                    logger.info(
                        f"DriveManager, _init_drive_status, exception: {e}")

                # Read in the status for the disk
                status_file = os.path.join(pathname, "status")
                if not os.path.isfile(status_file):
                    logger.error(
                        f"DriveManager error no status file for disk: {disk}")
                    continue
                try:
                    with open(status_file, "r") as datafile:
                        status = datafile.read().replace('\n', '')

                    # Read in the reason file if it's present
                    reason_file = os.path.join(pathname, "reason")

                    if os.path.isfile(reason_file):
                        with open(reason_file, "r") as datafile:
                            reason = datafile.read().replace('\n', '')

                        # Append the reason to the status file
                        self._drive_status[pathname] = f"{status}_{reason}"
                    else:
                        self._drive_status[pathname] = status

                    logger.info(
                        f"DriveManager, pathname: {pathname}, status: {self._drive_status[pathname]}"
                    )

                    # Remove base dcs dir since it contains no relevant data
                    data_str = status_file[len(self._drive_mngr_base_dir) + 1:]

                    # Send a message to the disk manager handler to create and transmit json msg
                    internal_json_msg = json.dumps({
                        "sensor_response_type":
                        "disk_status_drivemanager",
                        "event_path":
                        data_str,
                        "status":
                        self._drive_status[pathname],
                        "serial_number":
                        serial_number
                    })

                    # Send the event to disk message handler to generate json message
                    self._write_internal_msgQ(DiskMsgHandler.name(),
                                              internal_json_msg)

                except Exception as e:
                    logger.info(
                        f"DriveManager, _init_drive_status, exception: {e}")

            logger.info("DriveManager, initialization completed")
    def _notify_DiskMsgHandler(self, updated_file):
        """Send the event to the disk message handler for generating JSON message"""
        # Parse out the drive location without the ending filename that changed
        driveloc = os.path.dirname(updated_file)

        # Check to see if the drive is present
        serial_number = self._gather_data(f"{driveloc}/serial_number")

        # Update the status to status_reason used throughout
        if self._gather_data(f"{driveloc}/status") == "available":
            status = "OK_None"
        else:
            status = "EMPTY_None"

        disk_installed: bool
        if self._gather_data(f"{driveloc}/disk_installed") == "1":
            disk_installed = True
        else:
            disk_installed = False

        disk_powered: bool
        if self._gather_data(f"{driveloc}/disk_powered") == "1":
            disk_powered = True
        else:
            disk_powered = False

        # See if we need to use the previously saved serial number when disk is uninstalled
        if "disk_installed" in updated_file:
            # If field changed to disk being uninstalled then check for a valid serial number
            if disk_installed is False and \
                serial_number == "ZBX_NOTPRESENT":
                if self._drive_data.get(driveloc) is not None:
                    serial_number = self._drive_data.get(driveloc).get(
                        "serial_number")
                    logger.info(
                        f"Disk was removed, s/n=ZBX_NOTPRESENT, replacing with s/n: {serial_number}"
                    )

        # Send a message to the disk message handler to transmit
        json_data = {
            "sensor_response_type": "disk_status_hpi",
            "event_path": driveloc[len(self._hpi_mntr_base_dir) + 1:],
            "status": status,
            "drawer": self._gather_data(f"{driveloc}/drawer"),
            "location": self._gather_data(f"{driveloc}/location"),
            "manufacturer": self._gather_data(f"{driveloc}/manufacturer"),
            "productName": self._gather_data(f"{driveloc}/product_name"),
            "productVersion": self._gather_data(f"{driveloc}/product_version"),
            "serial_number": serial_number,
            "wwn": self._gather_data(f"{driveloc}/wwn"),
            "disk_installed": disk_installed,
            "disk_powered": disk_powered
        }

        # Do nothing if the overall state has not changed anywhere
        if self._drive_data.get(driveloc) is not None and \
            self._drive_data.get(driveloc) == json_data:
            return

        # Store the JSON data into the dict for global access
        if serial_number != "ZBX_NOTPRESENT":
            self._drive_data[driveloc] = json_data

        # Send the event to disk message handler to generate json message
        self._write_internal_msgQ(DiskMsgHandler.name(), json_data)

        # Restart openhpid to update HPI data for newly installed drives
        if serial_number == "ZBX_NOTPRESENT" and \
           disk_installed is True and \
           disk_powered is True:
            logger.info(
                "HPImonitor, _notify_DiskMsgHandler, Restarting openhpid")
            time.sleep(20)
            command = "/usr/bin/systemctl restart openhpid"
            response, error = self._run_command(command)
            if len(error) > 0:
                logger.info(f"Error restarting openhpid: {error}")
            else:
                logger.info("Restarted openhpid succesfully")

        # Reset debug mode if persistence is not enabled
        self._disable_debug_if_persist_false()
    def _init_drive_data(self):
        """Initialize the dict containing HPI data for each disk"""

        # Wait for the dcs-collector to populate the /tmp/dcs/hpi directory
        while not os.path.isdir(self._hpi_mntr_base_dir):
            logger.info(
                f"HPIMonitor, dir not found: {self._hpi_mntr_base_dir} ")
            logger.info(f"HPIMonitor, rechecking in {self._start_delay} secs")
            time.sleep(int(self._start_delay))

        enclosures = os.listdir(self._hpi_mntr_base_dir)

        # Remove the 'discovery' file and any others, only care about enclosure dirs
        enclosures = [enclosure for enclosure in enclosures \
                 if not os.path.isdir(os.path.join(self._hpi_mntr_base_dir, enclosure))]

        for enclosure in enclosures:
            disk_dir = os.path.join(self._hpi_mntr_base_dir, enclosure, "disk")
            self._log_debug(f"initializing: {disk_dir}")

            disks = os.listdir(disk_dir)
            for disk in disks:
                # Create the drive location path
                driveloc = os.path.join(disk_dir, disk)

                # Ignore the discovery file
                if not os.path.isdir(driveloc):
                    continue

                # Check to see if the drive is present
                serial_number = self._gather_data(driveloc + "/serial_number")

                # Update the status to status_reason used throughout
                if self._gather_data(driveloc + "/status") == "available":
                    status = "OK_None"
                else:
                    status = "EMPTY_None"

                disk_installed: bool
                if self._gather_data(driveloc + "/disk_installed") == "1":
                    disk_installed = True
                else:
                    disk_installed = False

                disk_powered: bool
                if self._gather_data(driveloc + "/disk_powered") == "1":
                    disk_powered = True
                else:
                    disk_powered = False

                # Read in the date for each disk and fill into dict
                json_data = {
                    "sensor_response_type":
                    "disk_status_hpi",
                    "event_path":
                    driveloc[len(self._hpi_mntr_base_dir) + 1:],
                    "status":
                    status,
                    "drawer":
                    self._gather_data(f"{driveloc}/drawer"),
                    "location":
                    self._gather_data(f"{driveloc}/location"),
                    "manufacturer":
                    self._gather_data(f"{driveloc}/manufacturer"),
                    "productName":
                    self._gather_data(f"{driveloc}/product_name"),
                    "productVersion":
                    self._gather_data(f"{driveloc}/product_version"),
                    "serial_number":
                    serial_number,
                    "wwn":
                    self._gather_data(f"{driveloc}/wwn"),
                    "disk_installed":
                    disk_installed,
                    "disk_powered":
                    disk_powered
                }

                logger.info(f"HPIMonitor: {str(json_data)}")
                # Store the JSON data into the dict for global access
                self._drive_data[driveloc] = json_data

                # Send it out to initialize anyone listening
                self._write_internal_msgQ(DiskMsgHandler.name(), json_data)

        logger.info("HPIMonitor, initialization completed")