Example #1
0
 def __init__(self):
     super(ServiceMsgHandler, self).__init__(self.MODULE_NAME,
                                             self.PRIORITY)
     self._service_actuator = None
     self._query_utility = None
     self._dbus_service = DbusServiceHandler()
     # Flag to indicate suspension of module
     self._suspended = False
    def install_files(self, PRODUCT):
        """Configure required log files."""

        dbus_service = DbusServiceHandler()
        # Copy rsyslog configuration
        if not os.path.exists(self.RSYSLOG_CONF):
            shutil.copyfile(
                "%s/low-level/files/%s" % (SSPL_BASE_DIR, self.RSYSLOG_CONF),
                self.RSYSLOG_CONF)

        if not os.path.exists(self.RSYSLOG_SSPL_CONF):
            shutil.copyfile(
                "%s/low-level/files/%s" %
                (SSPL_BASE_DIR, self.RSYSLOG_SSPL_CONF),
                self.RSYSLOG_SSPL_CONF)

        # Create soft link for SINGLE product name service to existing LDR_R1, LR2 service
        # Instead of keeping separate service file for SINGLE product with same content.
        currentProduct = "%s/conf/sspl-ll.service.%s" % (SSPL_BASE_DIR,
                                                         PRODUCT)
        if (PRODUCT == "SINGLE" and not os.path.exists(currentProduct)) or \
                (PRODUCT == "DUAL" and not os.path.exists(currentProduct)):
            os.symlink("%s/conf/sspl-ll.service.%s" % (SSPL_BASE_DIR, PRODUCT),
                       currentProduct)

        if PRODUCT == "CLUSTER" and not os.path.exists(currentProduct):
            os.symlink("%s/conf/sspl-ll.service.LR2" % (SSPL_BASE_DIR),
                       currentProduct)

        # Copy sspl-ll.service file and enable service
        shutil.copyfile(currentProduct, "/etc/systemd/system/sspl-ll.service")
        dbus_service.enable('sspl-ll.service')
        daemon_reload_cmd = "systemctl daemon-reload"
        output, error, returncode = SimpleProcess(daemon_reload_cmd).run()
        if returncode != 0:
            raise SetupError(returncode, error, daemon_reload_cmd)

        # Copy IEC mapping files
        os.makedirs("%s/iem/iec_mapping" % (PRODUCT_BASE_DIR), exist_ok=True)
        distutils.dir_util.copy_tree(
            "%s/low-level/files/iec_mapping/" % (SSPL_BASE_DIR),
            "%s/iem/iec_mapping" % (PRODUCT_BASE_DIR))

        # Skip this step if sspl is being configured for node replacement scenario as sspl configurations are
        # already available in consul on the healthy node
        # Running script for fetching the config using salt and feeding values to consul
        # Onward LR2, consul will be abstracted out and it won't exit as hard dependeny of SSPL
        if PRODUCT == "LDR_R1":
            if not os.path.exists(REPLACEMENT_NODE_ENV_VAR_FILE):
                config_from_salt = "%s/sspl_fetch_config_from_salt.py %s %s" % (
                    self._script_dir, self.ENVIRONMENT, PRODUCT)
                output, error, returncode = SimpleProcess(
                    config_from_salt).run()
                if returncode != 0:
                    raise SetupError(returncode, error, config_from_salt)
 def __init__(self, args: list):
     self.args = args
     self.name = "sspl_test"
     self.plan = "self"
     self.avoid_rmq = False
     self.dbus_service = DbusServiceHandler()
     if args.config and args.config[0]:
         self.sspl_test_gc_url = args.config[0]
     else:
         self.sspl_test_gc_url = global_config_path
     self.sspl_test_gc_copy_file = "/etc/sspl_test_gc_url.yaml"
Example #4
0
 def __init__(self, args: list):
     self.args = args
     self.name = "sspl_test"
     self.plan = "self_primary"
     self.avoid_rmq = False
     self.dbus_service = DbusServiceHandler()
     # Load global, sspl and test configs
     Conf.load(SSPL_CONFIG_INDEX, sspl_config_path)
     global_config_url = Conf.get(
         SSPL_CONFIG_INDEX, "SYSTEM_INFORMATION>global_config_copy_url")
     Conf.load(GLOBAL_CONFIG_INDEX, global_config_url)
     Conf.load(SSPL_TEST_CONFIG_INDEX, sspl_test_config_path)
Example #5
0
def test_service_inactive_alert(args):
    check_sspl_ll_is_running()
    check_service_is_running(service_name)
    # Simulate Fault alert by stopping the service.
    DbusServiceHandler().stop(service_name)
    time.sleep(WAIT_TIME)
    sensor_response = read_ingress_queue()
    assert_on_mismatch(sensor_response, "fault")
    # Simulate Fault resolved alert.
    DbusServiceHandler().start(service_name)
    time.sleep(5)
    sensor_response = read_ingress_queue()
    assert_on_mismatch(sensor_response, "fault_resolved")
    def __init__(self, args: list):
        self.args = args
        self.name = "sspl_test"
        self.plan = "sanity"
        self.coverage_enabled = self.args.coverage
        self.test_dir = f"/opt/seagate/{PRODUCT_FAMILY}/sspl/sspl_test"
        if self.args.v2:
            self.test_dir = f"/opt/seagate/{PRODUCT_FAMILY}/sspl/sspl_test/functional_tests/v2"
            sspl_constants.sspl_test_file_path = sspl_testv2_file_path
            sspl_constants.sspl_test_config_path = sspl_testv2_config_path

        self.dbus_service = DbusServiceHandler()
        if args.config and args.config[0]:
            self.sspl_test_gc_url = args.config[0]
        else:
            self.sspl_test_gc_url = global_config_path
        self.sspl_test_gc_copy_file = "/etc/sspl_test_gc_url.yaml"
Example #7
0
    def process(self):
        # stop sspl service
        dbus_service = DbusServiceHandler()
        dbus_service.stop('sspl-ll.service')

        # Remove sspl_conf
        self.del_file(file_store_config_path)

        # Remove sspl-configured file
        self.del_file(SSPL_CONFIGURED)

        # Remove sspl data
        shutil.rmtree(DATA_PATH, ignore_errors=True)

        # Remove sspl-ll user if preset
        CMD = "id -u sspl-ll"
        output, error, returncode = SimpleProcess(CMD).run()
        if returncode != 0:
            raise SetupError(returncode, "ERROR: %s - CMD %s", error, CMD)
        else:
            self.user_present = True

        if self.user_present:
            CMD = "/usr/sbin/userdel sspl-ll"
            output, error, returncode = SimpleProcess(CMD).run()
            if returncode != 0:
                raise SetupError(returncode, "ERROR: %s - CMD %s", error, CMD)

        # Remove log directories
        shutil.rmtree(f"/var/log/{PRODUCT_FAMILY}/sspl", ignore_errors=True)
        shutil.rmtree(f"/var/log/{PRODUCT_FAMILY}/iem", ignore_errors=True)

        # Remove rsyslog config files
        self.del_file("/etc/rsyslog.d/0-iemfwd.conf")
        self.del_file("/etc/rsyslog.d/1-ssplfwd.conf")

        # Remove logrotate config files
        self.del_file("/etc/logrotate.d/iem_messages")
        self.del_file("/etc/logrotate.d/sspl_logs")

        # Remove SSPL configuration files
        shutil.rmtree("/etc/sspl-ll", ignore_errors=True)
        self.del_file("/etc/sspl.conf.bak")
def test_service_restart_case(args):
    check_sspl_ll_is_running()
    check_service_is_running(service_name)
    DbusServiceHandler().restart(service_name)
    time.sleep(WAIT_TIME)
    sensor_response = read_ingress_queue()
    check_service_is_running(service_name)
    if sensor_response:
        print(sensor_response)
        assert (sensor_response['info']['alert_type'] != "fault")
Example #9
0
    def __init__(self):
        super(SystemdService, self).__init__()

        # Use d-bus to communicate with systemd
        #  Described at: http://www.freedesktop.org/wiki/Software/systemd/dbus/

        # Obtain an instance of d-bus to communicate with systemd
        self._bus = SystemBus()

        # Obtain a manager interface to d-bus for communications with systemd
        systemd = self._bus.get_object('org.freedesktop.systemd1',
                                       '/org/freedesktop/systemd1')
        self._manager = Interface(
            systemd, dbus_interface='org.freedesktop.systemd1.Manager')

        # Subscribe to signal changes
        self._manager.Subscribe()

        # create service cls obj.
        self._service = DbusServiceHandler()
Example #10
0
    def process(self):
        dbus_service = DbusServiceHandler()
        # Stop SSPL service if state is active
        service_state = dbus_service.get_state('sspl-ll.service')
        if service_state._state == 'active':
            logger.warning ("SSPL service should have been stopped,"
                            f"before {self.name} interface is invoked")
            logger.warning("Stopping SSPL service now...")
            dbus_service.stop('sspl-ll.service')

        # Remove sspl data
        shutil.rmtree(DATA_PATH, ignore_errors=True)

        # Remove log files
        Reset.del_files_from_dir('.log', f"/var/log/{PRODUCT_FAMILY}/sspl/")
        Reset.del_files_from_dir('.log.gz',
                                 f"/var/log/{PRODUCT_FAMILY}/sspl/")
        Reset.del_files_from_dir('.log', f"/var/log/{PRODUCT_FAMILY}/iem/")
        Reset.del_files_from_dir('.log.gz',
                                 f"/var/log/{PRODUCT_FAMILY}/iem/")
    def process(self):
        dbus_service = DbusServiceHandler()
        # Stop SSPL service if state is active
        service_state = dbus_service.get_state('sspl-ll.service')
        if service_state._state == 'active':
            logger.warning ("SSPL service should have been stopped,"
                            f"before {self.name} interface is invoked")
            logger.warning("Stopping SSPL service now...")
            dbus_service.stop('sspl-ll.service')

        # Remove sspl data
        shutil.rmtree(DATA_PATH, ignore_errors=True)

        # Clear log data from log files and delete 'log.gz' files
        FileStore().truncate(f"/var/log/{PRODUCT_FAMILY}/sspl/", '.log')
        FileStore().truncate(f"/var/log/{PRODUCT_FAMILY}/iem/", '.log')
        FileStore().delete(
            f"/var/log/{PRODUCT_FAMILY}/sspl/", '.log.gz')
        FileStore().delete(
            f"/var/log/{PRODUCT_FAMILY}/iem/", '.log.gz')
Example #12
0
    def process(self):
        """Create SSPL user and required config files."""
        # dbus module import is implicit in cortx utils. Keeping this
        # after dependency validation will enrich the use of
        # validate_dependencies() method.
        from cortx.utils.service import DbusServiceHandler
        self.dbus_service = DbusServiceHandler()

        # Create and load sspl config
        self.create_sspl_conf()
        Conf.load(consts.SSPL_CONFIG_INDEX, consts.sspl_config_path)

        # Update sspl.conf with provisioner supplied input config copy
        Conf.set(consts.SSPL_CONFIG_INDEX,
                 "SYSTEM_INFORMATION>global_config_copy_url",
                 consts.global_config_path)
        Conf.save(consts.SSPL_CONFIG_INDEX)

        self.create_user()
        self.create_directories_and_ownership()
        self.install_sspl_service_files()
        self.enable_sspl_service()
Example #13
0
class ServiceMsgHandler(ScheduledModuleThread, InternalMsgQ):
    """Message Handler for service request messages"""

    MODULE_NAME = "ServiceMsgHandler"
    PRIORITY = 2

    RESOURCE_TYPE = "node:sw:os:service"
    ALERT_TYPE = "UPDATE"
    # Dependency list
    DEPENDENCIES = {"plugins": ["EgressProcessor"], "rpms": []}

    @staticmethod
    def dependencies():
        """Returns a list of plugins and RPMs this module requires
           to function.
        """
        return ServiceMsgHandler.DEPENDENCIES

    @staticmethod
    def name():
        """ @return: name of the module."""
        return ServiceMsgHandler.MODULE_NAME

    def __init__(self):
        super(ServiceMsgHandler, self).__init__(self.MODULE_NAME,
                                                self.PRIORITY)
        self._service_actuator = None
        self._query_utility = None
        self._dbus_service = DbusServiceHandler()
        # Flag to indicate suspension of module
        self._suspended = False

    def initialize(self, conf_reader, msgQlist, product):
        """initialize configuration reader and internal msg queues"""
        # Initialize ScheduledMonitorThread
        super(ServiceMsgHandler, self).initialize(conf_reader)

        # Initialize internal message queues for this module
        super(ServiceMsgHandler, self).initialize_msgQ(msgQlist)

        self._import_products(product)

        self.host_id = socket.getfqdn()
        self.site_id = Conf.get(GLOBAL_CONF, SITE_ID_KEY, "DC01")
        self.rack_id = Conf.get(GLOBAL_CONF, RACK_ID_KEY, "RC01")
        self.node_id = Conf.get(GLOBAL_CONF, NODE_ID_KEY, "SN01")
        self.cluster_id = Conf.get(GLOBAL_CONF, CLUSTER_ID_KEY, "CC01")
        self.storage_set_id = Conf.get(GLOBAL_CONF, STORAGE_SET_ID_KEY, "ST01")
        self.monitored_services = Conf.get(
            SSPL_CONF, f'{SERVICEMONITOR}>{MONITORED_SERVICES}')

    def _import_products(self, product):
        """Import classes based on which product is being used"""
        if product.lower() in [x.lower() for x in enabled_products]:
            from zope.component import queryUtility
            self._query_utility = queryUtility

    def run(self):
        """Run the module periodically on its own thread."""
        logger.debug("Start accepting requests")

        # Do not proceed if module is suspended
        if self._suspended == True:
            self._scheduler.enter(1, self._priority, self.run, ())
            return

        # self._set_debug(True)
        # self._set_debug_persist(True)

        try:
            # Block on message queue until it contains an entry
            json_msg, _ = self._read_my_msgQ()
            if json_msg is not None:
                self._process_msg(json_msg)

            # Keep processing until the message queue is empty
            while not self._is_my_msgQ_empty():
                json_msg, _ = self._read_my_msgQ()
                if json_msg is not None:
                    self._process_msg(json_msg)

        except Exception as ae:
            # Log it and restart the whole process when a failure occurs
            logger.exception(f"ServiceMsgHandler restarting: {ae}")

        self._scheduler.enter(1, self._priority, self.run, ())
        logger.debug("Finished processing successfully")

    def _process_msg(self, jsonMsg):
        """Parses the incoming message and hands off to the appropriate logger
        """
        logger.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") is not None and \
           jsonMsg.get("sspl_ll_msg_header").get("uuid") is not None:
            uuid = jsonMsg.get("sspl_ll_msg_header").get("uuid")
            logger.debug(f"_processMsg, uuid: {uuid}")

        # Handle service start, stop, restart, status requests
        if "actuator_request_type" in jsonMsg and \
           "service_controller" in jsonMsg["actuator_request_type"]:

            logger.debug("_processMsg, msg_type: service_controller")

            service_name = jsonMsg.get("actuator_request_type") \
                .get("service_controller").get("service_name")
            service_request = jsonMsg.get("actuator_request_type") \
                .get("service_controller").get("service_request")
            request = f"{service_request}:{service_name}"

            if service_name not in self.monitored_services:
                logger.error(f"{service_name} - service not monitored")
                msg = ("Check if supplied service name is valid, %s is not "
                       "monitored or managed." % service_name)
                self.send_error_response(service_request, service_name, msg,
                                         errno.EINVAL)
                return
            elif service_request not in ["disable", "enable"]:
                status = self._dbus_service.is_enabled(service_name)
                if status == "disabled":
                    logger.error(f"{service_name} - service is disabled")
                    msg = ("%s is disabled, enable request needed before "
                           "current - %s request can be processed." %
                           (service_name, service_request))
                    self.send_error_response(service_request, service_name,
                                             msg, errno.EPERM)
                    return
            # If the state is INITIALIZED, We can assume that actuator is
            # ready to perform operation.
            if actuator_state_manager.is_initialized("Service"):
                logger.debug(f"_process_msg, service_actuator name: \
                                        {self._service_actuator.name()}")
                self._execute_request(self._service_actuator, jsonMsg, uuid)

            # If the state is INITIALIZING, need to send message
            elif actuator_state_manager.is_initializing("Service"):
                # This state will not be reached. Kept here for consistency.
                logger.info("Service actuator is initializing")
                self.send_error_response(service_request, service_name, \
                        "BUSY - Service actuator is initializing.", errno.EBUSY)

            elif actuator_state_manager.is_imported("Service"):
                # This case will be for first request only. Subsequent
                # requests will go to INITIALIZED state case.
                logger.info("Service actuator is imported and initializing")
                from actuators.IService import IService
                actuator_state_manager.set_state(
                    "Service", actuator_state_manager.INITIALIZING)
                service_actuator_class = self._query_utility(IService)
                if service_actuator_class:
                    # NOTE: Instantiation part should not time consuming
                    # otherwise ServiceMsgHandler will get block and will
                    # not be able serve any subsequent requests. This applies
                    # to instantiation of evey actuator.
                    self._service_actuator = service_actuator_class()
                    logger.info(f"_process_msg, service_actuator name: \
                                            {self._service_actuator.name()}")
                    self._execute_request(self._service_actuator, jsonMsg,
                                          uuid)
                    actuator_state_manager.set_state(
                        "Service", actuator_state_manager.INITIALIZED)
                else:
                    logger.info("Service 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("Service actuator is not loaded or not supported")

        # Handle events generated by the service monitor
        elif "sensor_request_type" in jsonMsg and \
            "service_status_alert" in jsonMsg["sensor_request_type"]:
            logger.debug(f"Received alert from ServiceMonitor : {jsonMsg}")
            jsonMsg1 = ServiceMonitorMsg(
                jsonMsg["sensor_request_type"]).getJson()
            self._write_internal_msgQ("EgressProcessor", jsonMsg1)

        # ... handle other service message types

    def _execute_request(self, actuator_instance, json_msg, uuid):
        """Calls perform_request method of an actuator and sends response to
           output channel.
        """
        service_name = json_msg.get("actuator_request_type").\
                            get("service_controller").get("service_name")
        service_request = json_msg.get("actuator_request_type").\
                            get("service_controller").get("service_request")
        service_info = {}
        service_name, result, error = actuator_instance.\
                                                perform_request(json_msg)
        if error:
            self.send_error_response(service_request, service_name, result)
            return

        logger.debug(f"_processMsg, service_name: {service_name}, \
                                                result: {result}")

        service_info["service_name"] = service_name
        service_info.update(result)

        # Create an actuator response and send it out
        response = self._create_actuator_response(service_info)
        service_controller_msg = ServiceControllerMsg(response)
        if uuid is not None:
            service_controller_msg.set_uuid(uuid)
        json_msg = service_controller_msg.getJson()
        self._write_internal_msgQ("EgressProcessor", json_msg)

    def send_error_response(self, request, service_name, err_msg, err_no=None):
        """Send error in response."""
        error_info = {}
        error_response = True
        error_info["service_name"] = service_name
        error_info["request"] = request
        error_info["error_msg"] = err_msg
        if err_no is not None:
            str_err = errno_to_str_mapping(err_no)
            error_info["error_no"] = f"{err_no} - {str_err}"
        response = self._create_actuator_response(error_info, error_response)
        service_controller_msg = ServiceControllerMsg(response).getJson()
        self._write_internal_msgQ("EgressProcessor", service_controller_msg)

    def _create_actuator_response(self, service_info, is_error=False):
        """Create JSON msg."""
        if is_error:
            severity = "warning"
        else:
            severity = "informational"
        resource_id = service_info.get("service_name")
        epoch_time = str(int(time.time()))
        specific_info = []
        info = {
            "cluster_id": self.cluster_id,
            "site_id": self.site_id,
            "rack_id": self.rack_id,
            "storage_set_id": self.storage_set_id,
            "node_id": self.node_id,
            "resource_id": resource_id,
            "resource_type": self.RESOURCE_TYPE,
            "event_time": epoch_time
        }

        specific_info.append(service_info)

        response = {
            "alert_type": self.ALERT_TYPE,
            "severity": severity,
            "host_id": self.host_id,
            "instance_id": resource_id,
            "info": info,
            "specific_info": specific_info
        }

        return response

    def suspend(self):
        """Suspends the module thread. It should be non-blocking"""
        super(ServiceMsgHandler, self).suspend()
        self._suspended = True

    def resume(self):
        """Resumes the module thread. It should be non-blocking"""
        super(ServiceMsgHandler, self).resume()
        self._suspended = False

    def shutdown(self):
        """Clean up scheduler queue and gracefully shutdown thread"""
        super(ServiceMsgHandler, self).shutdown()
Example #14
0
class SSPLTestCmd:
    """Starts test based on plan (sanity|alerts|self_primary|self_secondary)."""
    def __init__(self, args: list):
        self.args = args
        self.name = "sspl_test"
        self.plan = "self"
        self.avoid_rmq = False
        self.dbus_service = DbusServiceHandler()
        if args.config and args.config[0]:
            self.sspl_test_gc_url = args.config[0]
        else:
            self.sspl_test_gc_url = global_config_path
        self.sspl_test_gc_copy_file = "/etc/sspl_test_gc_url.yaml"

    def validate(self):
        """Check for required packages are installed."""
        # RPM dependency
        rpm_deps = {"cortx-sspl-test": None}
        # python 3rd party package dependency
        pip3_3ps_packages_test = {"Flask": "1.1.1"}
        pkg_validator = PkgV()
        pkg_validator.validate_pip3_pkgs(host=socket.getfqdn(),
                                         pkgs=pip3_3ps_packages_test,
                                         skip_version_check=False)
        pkg_validator.validate_rpm_pkgs(host=socket.getfqdn(),
                                        pkgs=rpm_deps,
                                        skip_version_check=True)
        # Load global, sspl and test configs
        Conf.load(SSPL_CONFIG_INDEX, sspl_config_path)
        Conf.load(SSPL_TEST_CONFIG_INDEX, sspl_test_config_path)
        # Take copy of supplied config passed to sspl_test and load it
        with open(self.sspl_test_gc_copy_file, "w") as f:
            f.write("")
        self.sspl_test_gc_copy_url = "yaml://%s" % self.sspl_test_gc_copy_file
        Conf.load(SSPL_TEST_GLOBAL_CONFIG, self.sspl_test_gc_copy_url)
        Conf.load("global_config", self.sspl_test_gc_url)
        Conf.copy("global_config", SSPL_TEST_GLOBAL_CONFIG)
        # Validate input configs
        machine_id = Utility.get_machine_id()
        self.node_type = Conf.get(SSPL_TEST_GLOBAL_CONFIG,
                                  "server_node>%s>type" % machine_id)
        enclosure_id = Conf.get(
            SSPL_TEST_GLOBAL_CONFIG,
            "server_node>%s>storage>enclosure_id" % machine_id)
        self.enclosure_type = Conf.get(
            SSPL_TEST_GLOBAL_CONFIG,
            "storage_enclosure>%s>type" % enclosure_id)

    def process(self):
        """Run test using user requested test plan."""
        self.plan = self.args.plan[0]
        self.avoid_rmq = self.args.avoid_rmq
        # if self.plan is other than "self"
        # then only config change and service restart is required.
        if self.plan not in ["self", "self_primary", "self_secondary"]:
            # Take back up of sspl test config
            sspl_test_backup = '/etc/sspl_tests.conf.back'
            shutil.copyfile(sspl_test_file_path, sspl_test_backup)

            # Add global config in sspl_test config and revert the changes once test completes.
            # Global config path in sspl_tests.conf will be referred by sspl_tests later
            sspl_global_config_url = Conf.get(
                SSPL_CONFIG_INDEX, "SYSTEM_INFORMATION>global_config_copy_url")
            Conf.set(SSPL_CONFIG_INDEX,
                     "SYSTEM_INFORMATION>global_config_copy_url",
                     self.sspl_test_gc_copy_url)
            Conf.save(SSPL_CONFIG_INDEX)

            # Enable & disable sensors based on environment
            update_sensor_info(SSPL_TEST_CONFIG_INDEX, self.node_type,
                               self.enclosure_type)

            # TODO: Move lines 99-131 & 152-159 to RunQATest class
            # Create dummy service and add service name in /etc/sspl.conf
            service_name = "dummy_service.service"
            service_file_path_src = f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.service"
            service_executable_code_src = f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.py"
            service_file_path_des = "/etc/systemd/system"
            service_executable_code_des = "/var/cortx/sspl/test"

            os.makedirs(service_executable_code_des, 0o777, exist_ok=True)

            shutil.copy(service_executable_code_src,
                        f'{service_executable_code_des}/dummy_service.py')
            # Make service file executable.
            cmd = f"chmod +x {service_executable_code_des}/dummy_service.py"
            _, error, returncode = SimpleProcess(cmd).run()
            if returncode != 0:
                print("%s error occurred while executing cmd: %s" %
                      (error, cmd))
                print("failed to assign execute permission for dummy_service.py."\
                        " dummy_service will fail.")

            # Copy service file to /etc/systemd/system/ path.
            shutil.copyfile(service_file_path_src,
                            f'{service_file_path_des}/dummy_service.service')
            cmd = "systemctl daemon-reload"
            _, error, returncode = SimpleProcess(cmd).run()
            if returncode != 0:
                print(f"failed to execute '{cmd}', systemctl will be unable"\
                    f" to manage the dummy_service.service \nError: {error}")

            self.dbus_service.enable(service_name)
            self.dbus_service.start(service_name)

            service_list = Conf.get(SSPL_CONFIG_INDEX,
                                    "SERVICEMONITOR>monitored_services")
            service_list.append(service_name)
            Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                     service_list)

            threshold_inactive_time_original = Conf.get(
                SSPL_CONFIG_INDEX, "SERVICEMONITOR>threshold_inactive_time")
            threshold_inactive_time_new = 30
            Conf.set(SSPL_CONFIG_INDEX,
                     "SERVICEMONITOR>threshold_inactive_time",
                     threshold_inactive_time_new)
            Conf.save(SSPL_CONFIG_INDEX)

            # TODO: Convert shell script to python
            # from cortx.sspl.sspl_test.run_qa_test import RunQATest
            # RunQATest(self.plan, self.avoid_rmq).run()
            CMD = "%s/run_qa_test.sh %s %s" % (TEST_DIR, self.plan,
                                               self.avoid_rmq)
            try:
                output, error, rc = SimpleProcess(CMD).run(
                    realtime_output=True)
            except KeyboardInterrupt:
                rc = 1
                error = "KeyboardInterrupt occurred while executing sspl test."
            # Restore the original path/file & service, then throw exception
            # if execution is failed.
            service_list.remove(service_name)
            Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                     service_list)
            Conf.set(SSPL_CONFIG_INDEX,
                     "SERVICEMONITOR>threshold_inactive_time",
                     threshold_inactive_time_original)
            Conf.set(SSPL_CONFIG_INDEX,
                     "SYSTEM_INFORMATION>global_config_copy_url",
                     sspl_global_config_url)
            Conf.save(SSPL_CONFIG_INDEX)
            shutil.copyfile(sspl_test_backup, sspl_test_file_path)
            if rc != 0:
                raise TestException("%s - ERROR: %s - CMD %s" %
                                    (self.name, error, CMD))
        else:
            # TODO: Convert shell script to python
            # from cortx.sspl.sspl_test.run_qa_test import RunQATest
            # RunQATest(self.plan, self.avoid_rmq).run()
            try:
                CMD = "%s/run_qa_test.sh %s %s" % (TEST_DIR, self.plan,
                                                   self.avoid_rmq)
                output, error, returncode = SimpleProcess(CMD).run(
                    realtime_output=True)
            except KeyboardInterrupt:
                raise TestException(
                    "KeyboardInterrupt occurred while executing sspl test.")
            except Exception as error:
                raise TestException(
                    "Error occurred while executing self test: %s" % error)
Example #15
0
class SystemdService(Debug):
    """Handles service request messages to systemd"""

    ACTUATOR_NAME = "SystemdService"

    @staticmethod
    def name():
        """ @return: name of the module."""
        return SystemdService.ACTUATOR_NAME

    def __init__(self):
        super(SystemdService, self).__init__()

        # Use d-bus to communicate with systemd
        #  Described at: http://www.freedesktop.org/wiki/Software/systemd/dbus/

        # Obtain an instance of d-bus to communicate with systemd
        self._bus = SystemBus()

        # Obtain a manager interface to d-bus for communications with systemd
        systemd = self._bus.get_object('org.freedesktop.systemd1',
                                       '/org/freedesktop/systemd1')
        self._manager = Interface(
            systemd, dbus_interface='org.freedesktop.systemd1.Manager')

        # Subscribe to signal changes
        self._manager.Subscribe()

        # create service cls obj.
        self._service = DbusServiceHandler()

    def perform_request(self, jsonMsg):
        """Performs the service request"""
        self._check_debug(jsonMsg)

        # Parse out the service name and request to perform on it
        if jsonMsg.get("actuator_request_type").get("service_controller") \
                                                                is not None:
            self._service_name = jsonMsg.get("actuator_request_type").\
                                get("service_controller").get("service_name")
            self._service_request = jsonMsg.get("actuator_request_type").\
                        get("service_controller").get("service_request")
        else:
            self._service_name = jsonMsg.get("actuator_request_type").\
                        get("service_watchdog_controller").get("service_name")
            self._service_request = jsonMsg.get("actuator_request_type").\
                        get("service_watchdog_controller").get("service_request")

        logger.debug("perform_request, service_name: %s, service_request: %s" % \
                        (self._service_name, self._service_request))

        try:
            # Load the systemd unit for the service
            systemd_unit = self._manager.LoadUnit(self._service_name)

            # Get a proxy to systemd for accessing properties of units
            self._proxy = self._bus.get_object("org.freedesktop.systemd1", \
                                                            str(systemd_unit))

            # The returned result of the desired action
            result = {}
            is_err_response = False
            if self._service_request in ['restart', 'start']:
                # Before restart/start the service, check service state.
                # If it is not active or activating then only process
                # restart/start request.
                service_state = self._service.get_state(self._service_name)
                state = service_state.state
                if state not in ['active', 'activating']:
                    if self._service_request == "restart":
                        self._service.restart(self._service_name)
                    elif self._service_request == "start":
                        self._service.start(self._service_name)
                    # Ensure we get an "active" state and not "activating"
                    service_state = self._service.get_state(self._service_name)
                    state = service_state.state
                    max_wait = 0
                    while state != "active":
                        logger.debug(
                            "%s status is activating, needs 'active' "
                            "state after %s request has been processed, retrying"
                            % (self._service_name, self._service_request))
                        time.sleep(1)
                        max_wait += 1
                        if max_wait > 20:
                            logger.debug("maximum wait - %s seconds, for "
                                         "service restart reached." % max_wait)
                            break
                        service_state = self._service.get_state(
                            self._service_name)
                        state = service_state.state

                else:
                    is_err_response = True
                    err_msg = (
                        "Can not process %s request, for %s, as service "
                        "is already in %s state." %
                        (self._service_request, self._service_name, state))
                    logger.error(err_msg)
                    return (self._service_name, err_msg, is_err_response)

            elif self._service_request == "stop":
                self._service.stop(self._service_name)

            elif self._service_request == "status":
                # Return the status below
                service_status = self._service.get_state(self._service_name)

            # TODO: Use cortx.utils Service class methods for
            # enable/disable services.
            elif self._service_request == "enable":
                service_list = []
                service_list.append(self._service_name)

                # EnableUnitFiles() function takes second argument as boolean.
                # 'True' will enable a service for runtime only(creates symlink
                #  in /run/.. directory) 'False' will enable a service
                #  persistently (creates symlink in /etc/.. directory)
                _, dbus_result = self._manager.EnableUnitFiles(
                    service_list, False, True)
                res = parse_enable_disable_dbus_result(dbus_result)
                result.update(res)
                logger.debug("perform_request, result for enable request: "
                             "result: %s" % (result))

            elif self._service_request == "disable":
                service_list = []
                service_list.append(self._service_name)

                # DisableUnitFiles() function takes second argument as boolean.
                # 'True' will disable a service for runtime only(removes symlink
                # from /run/.. directory) 'False' will disable a service
                # persistently(removes symlink from /etc/.. directory)
                dbus_result = self._manager.DisableUnitFiles(
                    service_list, False)
                res = parse_enable_disable_dbus_result(dbus_result)
                result.update(res)
                logger.debug(
                    "perform_request, result for disable request: %s" % result)
            else:
                logger.error("perform_request, Unknown service request - %s "
                             "for service - %s" %
                             (self._service_request, self._service_name))
                is_err_response = True
                return (self._service_name, "Unknown service request",
                        is_err_response)

        except debus_exceptions.DBusException as error:
            is_err_response = True
            logger.exception("DBus Exception: %r" % error)
            return (self._service_name, str(error), is_err_response)

        except Exception as ae:
            logger.exception("SystemD Exception: %r" % ae)
            is_err_response = True
            return (self._service_name, str(ae), is_err_response)

        # Give the unit some time to finish starting/stopping to get final status
        time.sleep(5)

        # Get the current status of the process and return it back:
        service_status = self._service.get_state(self._service_name)
        pid = service_status.pid
        state = service_status.state
        substate = service_status.substate
        status = self._service.is_enabled(self._service_name)
        uptime = get_service_uptime(self._service_name)
        # Parse dbus output to fetch command line path with args.
        command_line = service_status.command_line_path
        command_line_path_with_args = []
        for field in list(command_line[0][1]):
            command_line_path_with_args.append(str(field))
        result["pid"] = pid
        result["state"] = state
        result["substate"] = substate
        result["status"] = status
        result["uptime"] = uptime
        result["command_line_path"] = command_line_path_with_args

        logger.debug("perform_request, state: %s, substate: %s" %
                     (str(state), str(substate)))
        return (self._service_name, result, is_err_response)
Example #16
0
class SSPLPostInstall:
    """Prepare environment for SSPL service."""

    name = "sspl_post_install"

    def __init__(self):
        """Initialize varibales for post install."""
        consts.SSPL_LOG_PATH = "/var/log/%s/sspl/" % consts.PRODUCT_FAMILY
        consts.SSPL_BUNDLE_PATH = "/var/%s/sspl/bundle/" % consts.PRODUCT_FAMILY
        self.state_file = "%s/state.txt" % consts.DATA_PATH

    def validate(self):
        """Check below requirements are met in setup.
        1. Check if given product is supported by SSPL
        2. Check if given setup is supported by SSPL
        3. Check if required pre-requisites softwares are installed.
        4. Validate BMC connectivity
        5. Validate storage controller connectivity
        """
        machine_id = Utility.get_machine_id()

        # Validate input/provisioner configs
        self.product = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
                                                "cortx>release>product")
        self.setup = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
                                              "cortx>release>setup")
        node_type = Utility.get_config_value(
            consts.PRVSNR_CONFIG_INDEX, "server_node>%s>type" % machine_id)
        if node_type.lower() not in ["vm", "virtual"]:
            bmc_ip = Utility.get_config_value(
                consts.PRVSNR_CONFIG_INDEX,
                "server_node>%s>bmc>ip" % machine_id)
        enclosure_id = Utility.get_config_value(
            consts.PRVSNR_CONFIG_INDEX,
            "server_node>%s>storage>enclosure_id" % machine_id)
        Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
                                 "storage_enclosure>%s>type" % enclosure_id)
        primary_ip = Utility.get_config_value(
            consts.PRVSNR_CONFIG_INDEX,
            "storage_enclosure>%s>controller>primary>ip" % enclosure_id)
        secondary_ip = Utility.get_config_value(
            consts.PRVSNR_CONFIG_INDEX,
            "storage_enclosure>%s>controller>secondary>ip" % enclosure_id)

        # Validate product support
        if self.product not in consts.enabled_products:
            msg = "Product '%s' is not in sspl supported product list: %s" % (
                self.product, consts.enabled_products)
            logger.error(msg)
            raise SetupError(errno.EINVAL, msg)

        # Validate setup support
        if self.setup not in consts.setups:
            msg = "Setup '%s' is not in sspl supported setup list: %s" % (
                self.setup, consts.setups)
            logger.error(msg)
            raise SetupError(errno.EINVAL, msg)

        # Validate required pip3s and rpms are installed
        self.validate_dependencies(self.setup)

        # Validate BMC & Storage controller IP reachability
        if node_type.lower() not in ["vm", "virtual"]:
            # cluster_id required for decrypting the secret is only available from
            # the prepare stage. However accessibility validation will be done in
            # prepare stage. So at this time, validating ip reachability is fine.
            max_retry = 3
            for i in range(max_retry):
                try:
                    NetworkV().validate("connectivity",
                                        [bmc_ip, primary_ip, secondary_ip])
                    break
                except VError:
                    if i == (max_retry - 1):
                        raise
                    time.sleep(1)

    @staticmethod
    def validate_dependencies(setup):
        """Validate pre-requisites software packages."""
        pip3_3ps_packages_main = {
            "cryptography": "2.8",
            "jsonschema": "3.2.0",
            "pika": "1.1.0",
            "pyinotify": "0.9.6",
            "python-daemon": "2.2.4",
            "requests": "2.25.1",
            "zope.component": "4.6.2",
            "zope.event": "4.5.0",
            "zope.interface": "5.2.0"
        }
        rpm_3ps_packages = {
            "hdparm": "9.43",
            "ipmitool": "1.8.18",
            "lshw": "B.02.18",
            "python3": "3.6.8",
            "python36-dbus": "1.2.4",
            "python36-gobject": "3.22.0",
            "python36-paramiko": "2.1.1",
            "python36-psutil": "5.6.7",
            "shadow-utils": "4.6",
            "smartmontools": "7.0",
            "systemd-python36": "1.0.0",
            "udisks2": "2.8.4"
        }
        ssu_dependency_rpms = [
            "sg3_utils", "gemhpi", "pull_sea_logs", "python-hpi",
            "zabbix-agent-lib", "zabbix-api-gescheit", "zabbix-xrtx-lib",
            "python-openhpi-baselib", "zabbix-collector"
        ]
        ssu_required_process = ["openhpid", "dcs-collectord"]
        vm_dependency_rpms = []

        pkg_validator = PkgV()
        pkg_validator.validate_pip3_pkgs(host=socket.getfqdn(),
                                         pkgs=pip3_3ps_packages_main,
                                         skip_version_check=False)
        pkg_validator.validate_rpm_pkgs(host=socket.getfqdn(),
                                        pkgs=rpm_3ps_packages,
                                        skip_version_check=False)

        # Check for sspl required processes and misc dependencies if
        # setup/role is other than cortx
        if setup == "ssu":
            pkg_validator.validate("rpms", ssu_dependency_rpms)
            ServiceV().validate("isrunning", ssu_required_process)
        elif setup == "vm" or setup == "gw" or setup == "cmu":
            # No dependency currently. Keeping this section as it
            # may be needed in future.
            pkg_validator.validate("rpms", vm_dependency_rpms)
            # No processes to check in VM environment

    def process(self):
        """Create SSPL user and required config files."""
        # dbus module import is implicit in cortx utils. Keeping this
        # after dependency validation will enrich the use of
        # validate_dependencies() method.
        from cortx.utils.service import DbusServiceHandler
        self.dbus_service = DbusServiceHandler()

        # Create and load sspl config
        self.create_sspl_conf()
        Conf.load(consts.SSPL_CONFIG_INDEX, consts.sspl_config_path)

        # Update sspl.conf with provisioner supplied input config copy
        Conf.set(consts.SSPL_CONFIG_INDEX,
                 "SYSTEM_INFORMATION>global_config_copy_url",
                 consts.global_config_path)
        Conf.save(consts.SSPL_CONFIG_INDEX)

        self.create_user()
        self.create_directories_and_ownership()
        self.install_sspl_service_files()
        self.enable_sspl_service()

    def create_sspl_conf(self):
        """Install product specific sspl config."""
        # Copy and load product specific sspl config
        if not os.path.exists(consts.file_store_config_path):
            shutil.copyfile(
                "%s/conf/sspl.conf.%s.yaml" %
                (consts.SSPL_BASE_DIR, self.product),
                consts.file_store_config_path)

    def validate_user_existence(self):
        max_retry = 3
        for i in range(max_retry):
            sspl_uid = Utility.get_uid(consts.USER)
            if sspl_uid != -1:
                break
            else:
                if i == (max_retry - 1):
                    msg = "No user found with name : %s" % (consts.USER)
                    logger.error(msg)
                    raise SetupError(errno.EINVAL, msg)
                time.sleep(1)

    def validate_group_existence(self):
        max_retry = 3
        for i in range(max_retry):
            sspl_gid = Utility.get_gid(consts.USER)
            if sspl_gid != -1:
                break
            else:
                if i == (max_retry - 1):
                    msg = "No group found with name : %s" % (consts.USER)
                    logger.error(msg)
                    raise SetupError(errno.EINVAL, msg)
                time.sleep(1)

    def create_user(self):
        """Add sspl-ll user and validate user creation."""
        command = f"/usr/sbin/useradd -r {consts.USER} -s /sbin/nologin \
                  -c 'User account to run the {consts.USER} service'"

        _, error, rc = SimpleProcess(command).run()
        # rc 9 means user already exists
        if rc != 0 and rc != 9:
            logger.error(f"Failed to create sspl user. ERROR: {error}")
            raise SetupError(rc, error, command)

        self.validate_user_existence()
        self.validate_group_existence()
        # Add sspl-ll user to required groups and sudoers file etc.
        sspl_reinit = "%s/low-level/framework/sspl_reinit" % consts.SSPL_BASE_DIR
        _, error, rc = SimpleProcess(sspl_reinit).run()
        if rc:
            msg = "%s failed for with error : %e" % (sspl_reinit, error)
            logger.error(msg)
            raise SetupError(rc, msg)

    def create_directories_and_ownership(self):
        """Create ras persistent cache directory and state file.

        Assign ownership recursively on the configured directory.
        The created state file will be used later by SSPL resourse agent(HA).
        """
        # Extract the data path
        sspldp = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
                                          "SYSTEM_INFORMATION>data_path")
        if not sspldp:
            raise SetupError(errno.EINVAL, "Data path not set in sspl.conf")
        sspl_uid = Utility.get_uid(consts.USER)
        sspl_gid = Utility.get_gid(consts.USER)
        # Create sspl data directory if not exists
        os.makedirs(sspldp, exist_ok=True)
        # Create state file under sspl data directory
        if not os.path.exists(self.state_file):
            file = open(self.state_file, "w")
            file.close()

        # Create SSPL log and bundle directories
        os.makedirs(consts.SSPL_LOG_PATH, exist_ok=True)
        os.makedirs(consts.SSPL_BUNDLE_PATH, exist_ok=True)
        # set ownership for SSPL dirs
        list_root_dir = [consts.SSPL_CONFIGURED_DIR, consts.SSPL_LOG_PATH]
        Utility.set_ownership_recursively(list_root_dir, sspl_uid, sspl_gid)
        # Create /tmp/dcs/hpi if required. Not required for '<product>' role
        if self.setup != "cortx":
            os.makedirs(consts.HPI_PATH, mode=0o777, exist_ok=True)
            zabbix_uid = Utility.get_uid("zabbix")
            if zabbix_uid != -1:
                os.chown(consts.HPI_PATH, zabbix_uid, -1)
        # Create mdadm.conf to set ACL on it.
        with open(consts.MDADM_PATH, 'a'):
            os.utime(consts.MDADM_PATH)
        os.chmod(consts.MDADM_PATH, mode=0o666)
        os.chown(consts.MDADM_PATH, sspl_uid, -1)

    def install_sspl_service_files(self):
        """Copy service file to systemd location based on product."""
        # Create soft link for SINGLE product name service to existing LDR_R1, LR2 service
        # Instead of keeping separate service file for SINGLE product with same content.
        currentProduct = "%s/conf/sspl-ll.service.%s" % (consts.SSPL_BASE_DIR,
                                                         self.product)
        if (self.product == "SINGLE" and not os.path.exists(currentProduct)) or \
                (self.product == "DUAL" and not os.path.exists(currentProduct)):
            os.symlink(
                "%s/conf/sspl-ll.service.%s" %
                (consts.SSPL_BASE_DIR, self.product), currentProduct)
        if self.product == "CLUSTER" and not os.path.exists(currentProduct):
            os.symlink("%s/conf/sspl-ll.service.LR2" % (consts.SSPL_BASE_DIR),
                       currentProduct)
        shutil.copyfile(currentProduct, "/etc/systemd/system/sspl-ll.service")

    def enable_sspl_service(self):
        """Enable sspl-ll service."""
        self.dbus_service.enable("sspl-ll.service")
        daemon_reload_cmd = "systemctl daemon-reload"
        output, error, rc = SimpleProcess(daemon_reload_cmd).run()
        if rc != 0:
            logger.error(f"Failed in enable sspl service. ERROR: {error}")
            raise SetupError(rc, error, daemon_reload_cmd)
Example #17
0
def check_service_is_running(service_name):
    state = DbusServiceHandler().get_state(service_name).state
    assert (state == "active")
    return state
Example #18
0
class SSPLTestCmd:
    """Starts test based on plan (sanity|alerts|dev_sanity|full|performance|scalability|regression)."""
    def __init__(self, args: list):
        self.args = args
        self.name = "sspl_test"
        self.plan = "sanity"
        self.coverage_enabled = self.args.coverage

        self.dbus_service = DbusServiceHandler()
        if args.config and args.config[0]:
            self.sspl_test_gc_url = args.config[0]
        else:
            self.sspl_test_gc_url = global_config_path
        self.sspl_test_gc_copy_file = "/etc/sspl_test_gc_url.yaml"

    def validate(self):
        """Check for required packages are installed."""
        # RPM dependency
        rpm_deps = {"cortx-sspl-test": None}
        # python 3rd party package dependency
        pip3_packages_dep = {"Flask": "1.1.1", "coverage": "5.5"}
        if not self.coverage_enabled:
            pip3_packages_dep.pop("coverage")

        # Validate pip3 python pkg with required version.
        for pkg, version in pip3_packages_dep.items():
            installed_pkg = None
            uninstalled_pkg = False
            try:
                pkg_req = Requirement.parse(f"{pkg}=={version}")
                installed_pkg = working_set.find(pkg_req)
            except VersionConflict:
                cmd = f'pip3 uninstall -y {pkg}'
                _, err, ret = SimpleProcess(cmd).run()
                if ret:
                    raise TestException(
                        "Failed to uninstall the pip3 pkg: %s(v%s), "
                        "due to an Error: %s" % (pkg, version, err))
                uninstalled_pkg = True
            except Exception as err:
                raise TestException("Failed at verification of pip3 pkg: %s, "
                                    "due to an Error: %s" % (pkg, err))

            if not installed_pkg or uninstalled_pkg:
                cmd = f'pip3 install {pkg}=={version}'
                _, err, ret = SimpleProcess(cmd).run()
                if ret:
                    raise TestException(
                        "Failed to install the pip3 pkg: %s(v%s), "
                        "due to an Error: %s" % (pkg, version, err))
            logger.info(f"Ensured Package Dependency: {pkg}(v{version}).")

        # Validate rpm dependencies
        pkg_validator = PkgV()
        pkg_validator.validate_rpm_pkgs(host=socket.getfqdn(),
                                        pkgs=rpm_deps,
                                        skip_version_check=True)
        # Load global, sspl and test configs
        Conf.load(SSPL_CONFIG_INDEX, sspl_config_path)
        Conf.load(SSPL_TEST_CONFIG_INDEX, sspl_test_config_path)
        # Take copy of supplied config passed to sspl_test and load it
        with open(self.sspl_test_gc_copy_file, "w") as f:
            f.write("")
        self.sspl_test_gc_copy_url = "yaml://%s" % self.sspl_test_gc_copy_file
        Conf.load(SSPL_TEST_GLOBAL_CONFIG, self.sspl_test_gc_copy_url)
        Conf.load("global_config", self.sspl_test_gc_url)
        Conf.copy("global_config", SSPL_TEST_GLOBAL_CONFIG)
        # Validate input configs
        machine_id = Utility.get_machine_id()
        self.node_type = Conf.get(SSPL_TEST_GLOBAL_CONFIG,
                                  "server_node>%s>type" % machine_id)
        enclosure_id = Conf.get(
            SSPL_TEST_GLOBAL_CONFIG,
            "server_node>%s>storage>enclosure_id" % machine_id)
        self.enclosure_type = Conf.get(
            SSPL_TEST_GLOBAL_CONFIG,
            "storage_enclosure>%s>type" % enclosure_id)

    def process(self):
        """Run test using user requested test plan."""
        self.plan = self.args.plan[0]

        # if self.plan is other than "self"
        # then only config change and service restart is required.
        if self.plan not in IVT_TEST_PLANS:
            # Take back up of sspl test config
            sspl_test_backup = '/etc/sspl_tests.conf.back'
            shutil.copyfile(sspl_test_file_path, sspl_test_backup)

            # Add global config in sspl_test config and revert the changes once
            # test completes. Global config path in sspl_tests.conf will be
            # referred by sspl_tests later
            sspl_global_config_url = Conf.get(
                SSPL_CONFIG_INDEX, "SYSTEM_INFORMATION>global_config_copy_url")
            Conf.set(SSPL_CONFIG_INDEX,
                     "SYSTEM_INFORMATION>global_config_copy_url",
                     self.sspl_test_gc_copy_url)
            Conf.save(SSPL_CONFIG_INDEX)

            # Enable & disable sensors based on environment
            update_sensor_info(SSPL_TEST_CONFIG_INDEX, self.node_type,
                               self.enclosure_type)

            # TODO: Move lines 99-131 & 152-159 to RunQATest class
            # Create dummy service and add service name in /etc/sspl.conf
            service_name = "dummy_service.service"
            service_file_path_src = \
                f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.service"
            service_executable_code_src = \
                f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.py"
            service_file_path_des = "/etc/systemd/system"
            service_executable_code_des = "/var/cortx/sspl/test"

            os.makedirs(service_executable_code_des, 0o777, exist_ok=True)

            shutil.copy(service_executable_code_src,
                        f'{service_executable_code_des}/dummy_service.py')
            # Make service file executable.
            cmd = f"chmod +x {service_executable_code_des}/dummy_service.py"
            _, error, returncode = SimpleProcess(cmd).run()
            if returncode != 0:
                logger.error("%s error occurred while executing cmd: %s" %
                             (error, cmd))
                logger.error("failed to assign execute permission for"
                             "dummy_service.py. dummy_service will fail.")

            # Copy service file to /etc/systemd/system/ path.
            shutil.copyfile(service_file_path_src,
                            f'{service_file_path_des}/dummy_service.service')
            cmd = "systemctl daemon-reload"
            _, error, returncode = SimpleProcess(cmd).run()
            if returncode != 0:
                logger.error(f"failed to execute '{cmd}',"
                             "systemctl will be unable to manage the"
                             f"dummy_service.service \n Error: {error}")

            self.dbus_service.enable(service_name)
            self.dbus_service.start(service_name)

            service_list = Conf.get(SSPL_CONFIG_INDEX,
                                    "SERVICEMONITOR>monitored_services")
            service_list.append(service_name)
            Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                     service_list)

            threshold_inactive_time_original = Conf.get(
                SSPL_CONFIG_INDEX, "SERVICEMONITOR>threshold_inactive_time")
            threshold_inactive_time_new = 30
            Conf.set(SSPL_CONFIG_INDEX,
                     "SERVICEMONITOR>threshold_inactive_time",
                     threshold_inactive_time_new)
            Conf.save(SSPL_CONFIG_INDEX)

            cpu_usage_alert_wait = Conf.get(
                SSPL_CONFIG_INDEX,
                "NODEDATAMSGHANDLER>high_cpu_usage_wait_threshold")
            memory_usage_alert_wait = Conf.get(
                SSPL_CONFIG_INDEX,
                "NODEDATAMSGHANDLER>high_memory_usage_wait_threshold")

            cpu_usage_alert_wait_new = 10
            memory_usage_alert_wait_new = 20

            Conf.set(SSPL_CONFIG_INDEX,
                     "NODEDATAMSGHANDLER>high_cpu_usage_wait_threshold",
                     cpu_usage_alert_wait_new)
            Conf.set(SSPL_CONFIG_INDEX,
                     "NODEDATAMSGHANDLER>high_memory_usage_wait_threshold",
                     memory_usage_alert_wait_new)
            Conf.save(SSPL_CONFIG_INDEX)

            # TODO: Convert shell script to python
            # from cortx.sspl.sspl_test.run_qa_test import RunQATest
            # RunQATest(self.plan, self.coverage_enabled).run()
            CMD = "%s/run_qa_test.sh --plan %s --coverage %s"\
                   %(TEST_DIR, self.plan, self.coverage_enabled)
            try:
                _, error, rc = SimpleProcess(CMD).run(realtime_output=True)
            except KeyboardInterrupt:
                rc = 1
                error = "KeyboardInterrupt occurred while executing sspl test."
                logger.error("%s - ERROR: %s - CMD %s" %
                             (self.name, error, CMD))
            # Restore the original path/file & service, then throw exception
            # if execution is failed.
            service_list.remove(service_name)
            Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                     service_list)
            Conf.set(SSPL_CONFIG_INDEX,
                     "SERVICEMONITOR>threshold_inactive_time",
                     threshold_inactive_time_original)
            Conf.set(SSPL_CONFIG_INDEX,
                     "SYSTEM_INFORMATION>global_config_copy_url",
                     sspl_global_config_url)
            Conf.set(SSPL_CONFIG_INDEX,
                     "NODEDATAMSGHANDLER>high_cpu_usage_wait_threshold",
                     cpu_usage_alert_wait)
            Conf.set(SSPL_CONFIG_INDEX,
                     "NODEDATAMSGHANDLER>high_memory_usage_wait_threshold",
                     memory_usage_alert_wait)
            Conf.save(SSPL_CONFIG_INDEX)
            shutil.copyfile(sspl_test_backup, sspl_test_file_path)
            if rc != 0:
                raise TestException("%s - ERROR: %s - CMD %s" %
                                    (self.name, error, CMD))

            print('Restarting the SSPL service..')
            CMD = "systemctl restart sspl-ll"
            try:
                SimpleProcess(CMD).run(realtime_output=True)
            except Exception as error:
                raise TestException(
                    "Error occurred while executing sspl tests: %s" % error)
        elif self.plan in NOT_IMPLEMENTED_TEST_PLANS:
            print("Tests skipped, test plan %s not applicable for SSPL." %
                  (self.plan))
            return 0
        else:
            # TODO: Convert shell script to python
            # from cortx.sspl.sspl_test.run_qa_test import RunQATest
            # RunQATest(self.plan).run()
            try:
                CMD = "%s/run_qa_test.sh --plan %s" % (TEST_DIR, self.plan)
                _, error, returncode = SimpleProcess(CMD).run(
                    realtime_output=True)
            except KeyboardInterrupt:
                msg = "KeyboardInterrupt occurred while executing sspl test."
                logger.error(msg)
                raise TestException(msg)
            except Exception as error:
                msg = "Error occurred while executing self test: %s" % error
                logger.error(msg)
                raise TestException(msg)
class SSPLPostInstall:
    """Prepare environment for SSPL service."""

    name = "sspl_post_install"

    def __init__(self):
        """Initialize varibales for post install."""
        consts.SSPL_LOG_PATH = "/var/log/%s/sspl/" % consts.PRODUCT_FAMILY
        consts.SSPL_BUNDLE_PATH = "/var/%s/sspl/bundle/" % consts.PRODUCT_FAMILY
        self.state_file = "%s/state.txt" % consts.DATA_PATH
        self.dbus_service = DbusServiceHandler()

    def validate(self):
        """Check below requirements are met in setup.

        1. Check if given product is supported by SSPL
        2. Check if given setup is supported by SSPL
        3. Check if required pre-requisites softwares are installed.
        4. Validate BMC connectivity
        5. Validate storage controller connectivity
        """
        machine_id = Utility.get_machine_id()

        # Validate input/provisioner configs
        self.product = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
            "cortx>release>product")
        self.setup = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
            "cortx>release>setup")
        node_type = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
             "server_node>%s>type" % machine_id)
        if node_type.lower() not in ["vm", "virtual"]:
            bmc_ip = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
                "server_node>%s>bmc>ip" % machine_id)
        enclosure_id = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
            "server_node>%s>storage>enclosure_id" % machine_id)
        primary_ip = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
            "storage_enclosure>%s>controller>primary>ip" % enclosure_id)
        secondary_ip = Utility.get_config_value(consts.PRVSNR_CONFIG_INDEX,
            "storage_enclosure>%s>controller>secondary>ip" % enclosure_id)

        # Validate product support
        if self.product not in consts.enabled_products:
            raise SetupError(errno.EINVAL,
                "Product '%s' is not in sspl supported product list: %s",
                self.product, consts.enabled_products)

        # Validate setup support
        if self.setup not in consts.setups:
            raise SetupError(errno.EINVAL,
                "Setup '%s' is not in sspl supported setup list: %s",
                self.setup, consts.setups)

        # Validate required pip3s and rpms are installed
        self.validate_dependencies(self.setup)

        # Validate BMC & Storage controller IP reachability
        if node_type.lower() not in ["vm", "virtual"]:
            # cluster_id required for decrypting the secret is only available from
            # the prepare stage. However accessibility validation will be done in
            # prepare stage. So at this time, validating ip reachability is fine.
            NetworkV().validate("connectivity", [bmc_ip, primary_ip, secondary_ip])

    @staticmethod
    def validate_dependencies(setup):
        """Validate pre-requisites software packages."""
        pip3_3ps_packages_main = {
            "cryptography": "2.8",
            "jsonschema": "3.2.0",
            "pika": "1.1.0",
            "pyinotify": "0.9.6",
            "python-daemon": "2.2.4",
            "requests": "2.25.1",
            "zope.component": "4.6.2",
            "zope.event": "4.5.0",
            "zope.interface": "5.2.0"
            }
        rpm_3ps_packages = {
            "hdparm": "9.43",
            "ipmitool": "1.8.18",
            "lshw": "B.02.18",
            "python3": "3.6.8",
            "python36-dbus": "1.2.4",
            "python36-gobject": "3.22.0",
            "python36-paramiko": "2.1.1",
            "python36-psutil": "5.6.7",
            "shadow-utils": "4.6",
            "smartmontools": "7.0",
            "systemd-python36": "1.0.0",
            "udisks2": "2.8.4"
            }
        ssu_dependency_rpms = [
            "sg3_utils",
            "gemhpi",
            "pull_sea_logs",
            "python-hpi",
            "zabbix-agent-lib",
            "zabbix-api-gescheit",
            "zabbix-xrtx-lib",
            "python-openhpi-baselib",
            "zabbix-collector"
            ]
        ssu_required_process = [
            "openhpid",
            "dcs-collectord"
            ]
        vm_dependency_rpms = []

        pkg_validator = PkgV()
        pkg_validator.validate_pip3_pkgs(host=socket.getfqdn(),
            pkgs=pip3_3ps_packages_main, skip_version_check=False)
        pkg_validator.validate_rpm_pkgs(host=socket.getfqdn(),
            pkgs=rpm_3ps_packages, skip_version_check=False)

        # Check for sspl required processes and misc dependencies if
        # setup/role is other than cortx
        if setup == "ssu":
            pkg_validator.validate("rpms", ssu_dependency_rpms)
            ServiceV().validate("isrunning", ssu_required_process)
        elif setup == "vm" or setup == "gw" or setup == "cmu":
            # No dependency currently. Keeping this section as it
            # may be needed in future.
            pkg_validator.validate("rpms", vm_dependency_rpms)
            # No processes to check in VM environment

    def process(self):
        """Create SSPL user and required config files."""
        self.create_sspl_conf()
        # Load sspl config
        Conf.load(consts.SSPL_CONFIG_INDEX, consts.sspl_config_path)
        self.create_user()
        self.create_directories_and_ownership()
        self.configure_sspl_syslog()
        self.install_sspl_service_files()
        self.enable_sspl_service()

    def create_sspl_conf(self):
        """Install product specific sspl config."""
        # Copy and load product specific sspl config
        if not os.path.exists(consts.file_store_config_path):
            shutil.copyfile("%s/conf/sspl.conf.%s.yaml" % (consts.SSPL_BASE_DIR,
                self.product), consts.file_store_config_path)

    def create_user(self):
        """Add sspl-ll user and validate user creation."""
        os.system("/usr/sbin/useradd -r %s -s /sbin/nologin \
            -c 'User account to run the %s service'" % (consts.USER, consts.USER))
        usernames = [x[0] for x in pwd.getpwall()]
        if consts.USER not in usernames:
            raise SetupError(errno.EINVAL,
                "User %s doesn't exit. Please add user." % consts.USER)
        # Add sspl-ll user to required groups and sudoers file etc.
        sspl_reinit = "%s/low-level/framework/sspl_reinit" % consts.SSPL_BASE_DIR
        _ , error, rc = SimpleProcess(sspl_reinit).run()
        if rc:
            raise SetupError(rc, "%s failed for with error : %e", sspl_reinit, error)

    def create_directories_and_ownership(self):
        """Create ras persistent cache directory and state file.
        Assign ownership recursively on the configured directory.
        The created state file will be used later by SSPL resourse agent(HA).
        """
        # Extract the data path
        sspldp = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
            "SYSTEM_INFORMATION>data_path")
        if not sspldp:
            raise SetupError(errno.EINVAL, "Data path not set in sspl.conf")
        sspl_uid = Utility.get_uid(consts.USER)
        sspl_gid = Utility.get_gid(consts.USER)
        if sspl_uid == -1 or sspl_gid == -1:
            raise SetupError(errno.EINVAL,
                "No user found with name : %s", consts.USER)
        # Create sspl data directory if not exists
        os.makedirs(sspldp, exist_ok=True)
        # Create state file under sspl data directory
        if not os.path.exists(self.state_file):
            file = open(self.state_file, "w")
            file.close()
        Utility.set_ownership_recursively(consts.SSPL_CONFIGURED_DIR,sspl_uid,sspl_gid)

        # Create SSPL log and bundle directories
        os.makedirs(consts.SSPL_LOG_PATH, exist_ok=True)
        os.makedirs(consts.SSPL_BUNDLE_PATH, exist_ok=True)
        # Create /tmp/dcs/hpi if required. Not required for '<product>' role
        if self.setup != "cortx":
            os.makedirs(consts.HPI_PATH, mode=0o777, exist_ok=True)
            zabbix_uid = Utility.get_uid("zabbix")
            if zabbix_uid != -1:
                os.chown(consts.HPI_PATH, zabbix_uid, -1)
        # Create mdadm.conf to set ACL on it.
        with open(consts.MDADM_PATH, 'a'):
            os.utime(consts.MDADM_PATH)
        os.chmod(consts.MDADM_PATH, mode=0o666)
        os.chown(consts.MDADM_PATH, sspl_uid, -1)

    def configure_sspl_syslog(self):
        """Configure log file path in rsyslog and update logrotate config file."""
        system_files_root = "%s/low-level/files" % consts.SSPL_BASE_DIR
        sspl_log_file_path = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
            "SYSTEM_INFORMATION>sspl_log_file_path")
        sspl_sb_log_file_path = sspl_log_file_path.replace("/sspl.log","/sspl_support_bundle.log")
        iem_log_file_path = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
            "IEMSENSOR>log_file_path")
        manifest_log_file_path = sspl_log_file_path.replace("/sspl.log","/manifest.log")

        # IEM configuration
        os.makedirs("%s/iem/iec_mapping" % consts.PRODUCT_BASE_DIR, exist_ok=True)
        distutils.dir_util.copy_tree("%s/iec_mapping/" % system_files_root,
            "%s/iem/iec_mapping" % consts.PRODUCT_BASE_DIR)
        if not os.path.exists(consts.RSYSLOG_IEM_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_IEM_CONF),
                consts.RSYSLOG_IEM_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_IEM_CONF,
            'File.*[=,"]', 'File="%s"' % iem_log_file_path)

        # SSPL rsys log configuration
        if not os.path.exists(consts.RSYSLOG_SSPL_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_SSPL_CONF),
                consts.RSYSLOG_SSPL_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_SSPL_CONF, 'File.*[=,"]',
            'File="%s"' % sspl_log_file_path)

        # Manifest Bundle log configuration
        if not os.path.exists(consts.RSYSLOG_MSB_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_MSB_CONF),
                consts.RSYSLOG_MSB_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_MSB_CONF, 'File.*[=,"]',
            'File="%s"' % manifest_log_file_path)

        # Support Bundle log configuration
        if not os.path.exists(consts.RSYSLOG_SB_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_SB_CONF),
                consts.RSYSLOG_SB_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_SB_CONF, 'File.*[=,"]',
            'File="%s"' % sspl_sb_log_file_path)

        # Configure logrotate
        # Create logrotate dir in case it's not present
        os.makedirs(consts.LOGROTATE_DIR, exist_ok=True)
        Utility.replace_expr("%s/etc/logrotate.d/iem_messages" % system_files_root,
            0, iem_log_file_path)
        Utility.replace_expr("%s/etc/logrotate.d/sspl_logs" % system_files_root,
            0, sspl_log_file_path)
        Utility.replace_expr("%s/etc/logrotate.d/sspl_sb_logs" % system_files_root,
            0, sspl_sb_log_file_path)
        shutil.copy2("%s/etc/logrotate.d/iem_messages" % system_files_root,
            consts.IEM_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/sspl_logs" % system_files_root,
            consts.SSPL_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/manifest_logs" % system_files_root,
            consts.MSB_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/sspl_sb_logs" % system_files_root,
            consts.SB_LOGROTATE_CONF)

        # This rsyslog restart will happen after successful updation of rsyslog
        # conf file and before sspl starts. If at all this will be removed from
        # here, there will be a chance that SSPL intial logs will not be present in
        # "/var/log/<product>/sspl/sspl.log" file. So, initial logs needs to be collected from
        # "/var/log/messages"
        service = DbusServiceHandler()
        service.restart('rsyslog.service')

    def install_sspl_service_files(self):
        """Copy service file to systemd location based on product."""
        # Create soft link for SINGLE product name service to existing LDR_R1, LR2 service
        # Instead of keeping separate service file for SINGLE product with same content.
        currentProduct = "%s/conf/sspl-ll.service.%s" % (consts.SSPL_BASE_DIR,
                                                         self.product)
        if (self.product == "SINGLE" and not os.path.exists(currentProduct)) or \
                (self.product == "DUAL" and not os.path.exists(currentProduct)):
            os.symlink("%s/conf/sspl-ll.service.%s" % (consts.SSPL_BASE_DIR, self.product),
                       currentProduct)
        if self.product == "CLUSTER" and not os.path.exists(currentProduct):
            os.symlink("%s/conf/sspl-ll.service.LR2" % (consts.SSPL_BASE_DIR),
                       currentProduct)
        shutil.copyfile(currentProduct, "/etc/systemd/system/sspl-ll.service")

    def enable_sspl_service(self):
        """Enable sspl-ll service."""
        self.dbus_service.enable("sspl-ll.service")
        daemon_reload_cmd = "systemctl daemon-reload"
        output, error, rc = SimpleProcess(daemon_reload_cmd).run()
        if rc != 0:
            raise SetupError(rc, error, daemon_reload_cmd)
Example #20
0
class SSPLTestCmd:
    """Starts test based on plan (sanity|alerts|self_primary|self_secondary)."""
    def __init__(self, args: list):
        self.args = args
        self.name = "sspl_test"
        self.plan = "self_primary"
        self.avoid_rmq = False
        self.dbus_service = DbusServiceHandler()
        # Load global, sspl and test configs
        Conf.load(SSPL_CONFIG_INDEX, sspl_config_path)
        global_config_url = Conf.get(
            SSPL_CONFIG_INDEX, "SYSTEM_INFORMATION>global_config_copy_url")
        Conf.load(GLOBAL_CONFIG_INDEX, global_config_url)
        Conf.load(SSPL_TEST_CONFIG_INDEX, sspl_test_config_path)

    @staticmethod
    def validate():
        """Check for required packages are installed."""
        # python 3rd party package dependency
        pip3_3ps_packages_test = {"Flask": "1.1.1"}
        pkg_validator = PkgV()
        pkg_validator.validate_pip3_pkgs(host=None,
                                         pkgs=pip3_3ps_packages_test,
                                         skip_version_check=False)

    def process(self):
        self.plan = self.args.plan[0]
        self.avoid_rmq = self.args.avoid_rmq

        # Take back up of sspl test config
        sspl_test_backup = '/etc/sspl_tests.conf.back'
        shutil.copyfile(sspl_test_file_path, sspl_test_backup)

        # Add global config in sspl_test config and revert the changes once test completes.
        # Global config path in sspl_tests.conf will be referred by sspl_tests later
        global_config_copy_url = Conf.get(
            SSPL_CONFIG_INDEX, "SYSTEM_INFORMATION>global_config_copy_url")
        Conf.copy(GLOBAL_CONFIG_INDEX, SSPL_TEST_CONFIG_INDEX)
        Conf.set(SSPL_CONFIG_INDEX,
                 "SYSTEM_INFORMATION>global_config_copy_url",
                 sspl_test_config_path)
        Conf.save(SSPL_CONFIG_INDEX)

        # Enable & disable sensors based on environment
        update_sensor_info(SSPL_TEST_CONFIG_INDEX)

        # Get rabbitmq values from sspl.conf and update sspl_tests.conf
        rmq_passwd = Conf.get(SSPL_CONFIG_INDEX,
                              "RABBITMQEGRESSPROCESSOR>password")
        Conf.set(SSPL_TEST_CONFIG_INDEX, "RABBITMQEGRESSPROCESSOR>password",
                 rmq_passwd)
        Conf.save(SSPL_TEST_CONFIG_INDEX)

        # TODO: Move lines 90-116 & 125-127 to RunQATest class
        # Create dummy service and add service name in /etc/sspl.conf
        service_name = "dummy_service.service"
        service_file_path_src = f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.service"
        service_executable_code_src = f"{TEST_DIR}/alerts/os/dummy_service_files/dummy_service.py"
        service_file_path_des = "/etc/systemd/system"
        service_executable_code_des = "/var/cortx/sspl/test"

        os.makedirs(service_executable_code_des, 0o777, exist_ok=True)

        shutil.copy(service_executable_code_src,
                    f'{service_executable_code_des}/dummy_service.py')
        # Make service file executable.
        cmd = f"chmod +x {service_executable_code_des}/dummy_service.py"
        _, error, returncode = SimpleProcess(cmd).run()
        if returncode != 0:
            print("%s error occurred while executing cmd: %s" % (error, cmd))
            print("failed to assign execute permission for dummy_service.py."\
                    " dummy_service will fail.")

        # Copy service file to /etc/systemd/system/ path.
        shutil.copyfile(service_file_path_src,
                        f'{service_file_path_des}/dummy_service.service')
        cmd = "systemctl daemon-reload"
        _, error, returncode = SimpleProcess(cmd).run()
        if returncode != 0:
            print(f"failed to execute '{cmd}', systemctl will be unable"\
                f" to manage the dummy_service.service \nError: {error}")

        self.dbus_service.enable(service_name)
        self.dbus_service.start(service_name)

        service_list = Conf.get(SSPL_CONFIG_INDEX,
                                "SERVICEMONITOR>monitored_services")
        service_list.append(service_name)
        Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                 service_list)

        threshold_inactive_time_original = Conf.get(
            SSPL_CONFIG_INDEX, "SERVICEMONITOR>threshold_inactive_time")
        threshold_inactive_time_new = 30
        Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>threshold_inactive_time",
                 threshold_inactive_time_new)
        Conf.save(SSPL_CONFIG_INDEX)

        # TODO: Convert shell script to python
        # from cortx.sspl.sspl_test.run_qa_test import RunQATest
        # RunQATest(self.plan, self.avoid_rmq).run()
        CMD = "%s/run_qa_test.sh %s %s" % (TEST_DIR, self.plan, self.avoid_rmq)
        output, error, returncode = SimpleProcess(CMD).run(
            realtime_output=True)
        # Restore the original path/file & service, then throw exception
        # if execution is failed.
        service_list.remove(service_name)
        Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>monitored_services",
                 service_list)
        Conf.set(SSPL_CONFIG_INDEX, "SERVICEMONITOR>threshold_inactive_time",
                 threshold_inactive_time_original)
        Conf.set(SSPL_CONFIG_INDEX,
                 "SYSTEM_INFORMATION>global_config_copy_url",
                 global_config_copy_url)
        Conf.save(SSPL_CONFIG_INDEX)
        shutil.copyfile(sspl_test_backup, sspl_test_file_path)
        self.dbus_service.restart('sspl-ll.service')
        if returncode != 0:
            raise SetupError(returncode, "%s - ERROR: %s - CMD %s", self.name,
                             error, CMD)
    def configure_sspl_syslog(self):
        """Configure log file path in rsyslog and update logrotate config file."""
        system_files_root = "%s/low-level/files" % consts.SSPL_BASE_DIR
        sspl_log_file_path = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
            "SYSTEM_INFORMATION>sspl_log_file_path")
        sspl_sb_log_file_path = sspl_log_file_path.replace("/sspl.log","/sspl_support_bundle.log")
        iem_log_file_path = Utility.get_config_value(consts.SSPL_CONFIG_INDEX,
            "IEMSENSOR>log_file_path")
        manifest_log_file_path = sspl_log_file_path.replace("/sspl.log","/manifest.log")

        # IEM configuration
        os.makedirs("%s/iem/iec_mapping" % consts.PRODUCT_BASE_DIR, exist_ok=True)
        distutils.dir_util.copy_tree("%s/iec_mapping/" % system_files_root,
            "%s/iem/iec_mapping" % consts.PRODUCT_BASE_DIR)
        if not os.path.exists(consts.RSYSLOG_IEM_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_IEM_CONF),
                consts.RSYSLOG_IEM_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_IEM_CONF,
            'File.*[=,"]', 'File="%s"' % iem_log_file_path)

        # SSPL rsys log configuration
        if not os.path.exists(consts.RSYSLOG_SSPL_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_SSPL_CONF),
                consts.RSYSLOG_SSPL_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_SSPL_CONF, 'File.*[=,"]',
            'File="%s"' % sspl_log_file_path)

        # Manifest Bundle log configuration
        if not os.path.exists(consts.RSYSLOG_MSB_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_MSB_CONF),
                consts.RSYSLOG_MSB_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_MSB_CONF, 'File.*[=,"]',
            'File="%s"' % manifest_log_file_path)

        # Support Bundle log configuration
        if not os.path.exists(consts.RSYSLOG_SB_CONF):
            shutil.copyfile("%s/%s" % (system_files_root, consts.RSYSLOG_SB_CONF),
                consts.RSYSLOG_SB_CONF)
        # Update log location as per sspl.conf
        Utility.replace_expr(consts.RSYSLOG_SB_CONF, 'File.*[=,"]',
            'File="%s"' % sspl_sb_log_file_path)

        # Configure logrotate
        # Create logrotate dir in case it's not present
        os.makedirs(consts.LOGROTATE_DIR, exist_ok=True)
        Utility.replace_expr("%s/etc/logrotate.d/iem_messages" % system_files_root,
            0, iem_log_file_path)
        Utility.replace_expr("%s/etc/logrotate.d/sspl_logs" % system_files_root,
            0, sspl_log_file_path)
        Utility.replace_expr("%s/etc/logrotate.d/sspl_sb_logs" % system_files_root,
            0, sspl_sb_log_file_path)
        shutil.copy2("%s/etc/logrotate.d/iem_messages" % system_files_root,
            consts.IEM_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/sspl_logs" % system_files_root,
            consts.SSPL_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/manifest_logs" % system_files_root,
            consts.MSB_LOGROTATE_CONF)
        shutil.copy2("%s/etc/logrotate.d/sspl_sb_logs" % system_files_root,
            consts.SB_LOGROTATE_CONF)

        # This rsyslog restart will happen after successful updation of rsyslog
        # conf file and before sspl starts. If at all this will be removed from
        # here, there will be a chance that SSPL intial logs will not be present in
        # "/var/log/<product>/sspl/sspl.log" file. So, initial logs needs to be collected from
        # "/var/log/messages"
        service = DbusServiceHandler()
        service.restart('rsyslog.service')
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# For any questions about this software or licensing, please email
# [email protected] or [email protected].

import os
import shutil
import time
from cortx.utils.process import SimpleProcess
from cortx.utils.service import DbusServiceHandler

path = os.path.dirname(os.path.abspath(__file__))
service_name = "dummy_service.service"
service_file_path = f"{path}/{service_name}"
service_executable_code_src = f"{path}/dummy_service.py"
service_executable_code_des = "/var/cortx/sspl/test"
dbus_service = DbusServiceHandler()


def simulate_fault_alert():
    """Simulate fault for dummy service by deleting executable service file."""
    os.remove(f"{service_executable_code_des}/dummy_service.py")
    dbus_service.restart(service_name)
    time.sleep(5)


def restore_service_file():
    """Simulate fault resolved for dummy service by creating executable
        service file."""
    shutil.copy(service_executable_code_src, service_executable_code_des)
    # Make service file executable.
    cmd = f"chmod +x {service_executable_code_des}/dummy_service.py"
 def __init__(self):
     """Initialize varibales for post install."""
     consts.SSPL_LOG_PATH = "/var/log/%s/sspl/" % consts.PRODUCT_FAMILY
     consts.SSPL_BUNDLE_PATH = "/var/%s/sspl/bundle/" % consts.PRODUCT_FAMILY
     self.state_file = "%s/state.txt" % consts.DATA_PATH
     self.dbus_service = DbusServiceHandler()
Example #24
0
    def config_sspl(self):
        if (os.geteuid() != 0):
            raise SetupError(errno.EINVAL,
                             "Run this command with root privileges!!")

        if not os.path.isfile(consts.file_store_config_path):
            raise SetupError(errno.EINVAL,
                             "Missing configuration!! Create and rerun.",
                             consts.file_store_config_path)

        # Put minion id, consul_host and consul_port in conf file
        # Onward LDR_R2, salt will be abstracted out and it won't
        # exist as a hard dependeny of SSPL
        if consts.PRODUCT_NAME == "LDR_R1":
            from framework.utils.salt_util import SaltInterface
            salt_util = SaltInterface()
            salt_util.update_config_file(consts.file_store_config_path)

        if os.path.isfile(consts.SSPL_CONFIGURED):
            os.remove(consts.SSPL_CONFIGURED)

        # Add sspl-ll user to required groups and sudoers file etc.
        sspl_reinit = [
            f"{consts.SSPL_BASE_DIR}/low-level/framework/sspl_reinit",
            self.product
        ]
        _, error, returncode = SimpleProcess(sspl_reinit).run()
        if returncode:
            raise SetupError(
                returncode, "%s/low-level/framework/sspl_reinit failed for"
                "product %s with error : %e", consts.SSPL_BASE_DIR,
                self.product, error)

        os.makedirs(consts.SSPL_CONFIGURED_DIR, exist_ok=True)
        with open(consts.SSPL_CONFIGURED, 'a'):
            os.utime(consts.SSPL_CONFIGURED)

        # SSPL Log file configuration
        # SSPL_LOG_FILE_PATH = self.getval_from_ssplconf('sspl_log_file_path')
        SSPL_LOG_FILE_PATH = Conf.get(consts.SSPL_CONFIG_INDEX,
                                      'SYSTEM_INFORMATION>sspl_log_file_path')
        IEM_LOG_FILE_PATH = Conf.get(consts.SSPL_CONFIG_INDEX,
                                     'IEMSENSOR>log_file_path')
        if SSPL_LOG_FILE_PATH:
            self.replace_expr(consts.RSYSLOG_SSPL_CONF, 'File.*[=,"]',
                              'File="%s"' % SSPL_LOG_FILE_PATH)
            self.replace_expr(
                f"{consts.SSPL_BASE_DIR}/low-level/files/etc/logrotate.d/sspl_logs",
                0, SSPL_LOG_FILE_PATH)

        # IEM configuration
        # Configure log file path in Rsyslog and logrotate configuration file
        IEM_LOG_FILE_PATH = Conf.get(consts.SSPL_CONFIG_INDEX,
                                     'IEMSENSOR>log_file_path')

        if IEM_LOG_FILE_PATH:
            self.replace_expr(consts.RSYSLOG_IEM_CONF, 'File.*[=,"]',
                              'File="%s"' % IEM_LOG_FILE_PATH)
            self.replace_expr(
                f'{consts.SSPL_BASE_DIR}/low-level/files/etc/logrotate.d/iem_messages',
                0, IEM_LOG_FILE_PATH)
        else:
            self.replace_expr(
                consts.RSYSLOG_IEM_CONF, 'File.*[=,"]',
                'File="/var/log/%s/iem/iem_messages"' % consts.PRODUCT_FAMILY)

        # Create logrotate dir in case it's not present for dev environment
        if not os.path.exists(consts.LOGROTATE_DIR):
            os.makedirs(consts.LOGROTATE_DIR)

        shutil.copy2(
            '%s/low-level/files/etc/logrotate.d/iem_messages' %
            consts.SSPL_BASE_DIR, consts.IEM_LOGROTATE_CONF)

        shutil.copy2(
            '%s/low-level/files/etc/logrotate.d/sspl_logs' %
            consts.SSPL_BASE_DIR, consts.SSPL_LOGROTATE_CONF)

        # This rsyslog restart will happen after successful updation of rsyslog
        # conf file and before sspl starts. If at all this will be removed from
        # here, there will be a chance that SSPL intial logs will not be present in
        # "/var/log/<product>/sspl/sspl.log" file. So, initial logs needs to be collected from
        # "/var/log/messages"
        service = DbusServiceHandler()
        service.restart('rsyslog.service')

        # For node replacement scenario consul will not be running on the new node. But,
        # there will be two instance of consul running on healthy node. When new node is configured
        # consul will be brought back on it. We are using VIP to connect to consul. So, if consul
        # is not running on new node, we dont need to error out.
        # If consul is not running, exit
        # Onward LDR_R2, consul will be abstracted out and it won't
        # exit as hard dependeny of SSPL

        if consts.PRODUCT_NAME == 'LDR_R1':
            if not os.path.exists(consts.REPLACEMENT_NODE_ENV_VAR_FILE):
                ServiceV().validate('isrunning', ['consul'], is_process=True)

        # Get the types of server and storage we are currently running on and
        # enable/disable sensor groups in the conf file accordingly.
        update_sensor_info(consts.SSPL_CONFIG_INDEX)

        self.create_message_types()