Esempio n. 1
0
 async def get_bundle_status(command):
     """
     Initializes the process for Displaying the Status for Support Bundle.
     :param command: Csm_cli Command Object :type: command
     :return: None
     """
     try:
         bundle_id = command.options.get(const.SB_BUNDLE_ID)
         conf = GeneralConfig(database.DATABASE)
         db = DataBaseProvider(conf)
         repo = SupportBundleRepository(db)
         all_nodes_status = await repo.retrieve_all(bundle_id)
         response = {
             "status": [
                 each_status.to_primitive()
                 for each_status in all_nodes_status
             ]
         }
         return Response(output=response, rc=OPERATION_SUCESSFUL)
     except DataAccessExternalError as e:
         Log.warn(f"Failed to connect to elasticsearch: {e}")
         return Response(
             output=("Support Bundle status is not available currently"
                     " as required services are not running."
                     " Please wait and check the /tmp/support_bundle"
                     " folder for newly generated support bundle."),
             rc=str(errno.ECONNREFUSED))
     except Exception as e:
         Log.error(f"Failed to get bundle status: {e}")
         return Response(
             output=("Support Bundle status is not available currently"
                     " as required services are not running."
                     " Failed to get status of bundle."),
             rc=str(errno.ENOENT))
    async def _get_bundle_status(command):
        """
        Initializes the process for Displaying the Status for Support Bundle.

        command:    Command Object :type: command
        return:     None
        """
        try:
            status = ''
            node_id = Conf.machine_id
            Conf.load(const.SB_INDEX,
                      'json://' + const.FILESTORE_PATH,
                      skip_reload=True)
            bundle_id = command.options.get(const.SB_BUNDLE_ID)
            if not bundle_id:
                status = Conf.get(const.SB_INDEX, f'{node_id}')
            else:
                status = Conf.get(const.SB_INDEX,
                                  f'{node_id}>{bundle_id}>status')
            if not status:
                return Response(output=(f"No status found for bundle_id: {bundle_id}" \
                    "in input config. Please check if the Bundle ID is correct"), \
                    rc=ERR_OP_FAILED)
            return Response(output=status, rc=OPERATION_SUCESSFUL)
        except Exception as e:
            Log.error(f"Failed to get bundle status: {e}")
            return Response(output=(f"Support Bundle status is not available " \
                f"Failed to get status of bundle. Related error - {e}"), \
                rc=str(errno.ENOENT))
Esempio n. 3
0
    async def generate_bundle(command) -> sys.stdout:
        """
        Initializes the process for Generating Support Bundle on Each CORTX Node.
        :param command: Csm_cli Command Object :type: command
        :return: None.
        """
        current_user = str(getpass.getuser())
        # Check if User is Root User.
        if current_user.lower() != "root":
            response_msg = "Support Bundle Command requires root privileges"
            return Response(output=response_msg, rc=errno.EACCES)
        bundle_id = SupportBundle._generate_bundle_id()
        provisioner = ProvisionerServices()
        if not provisioner:
            return Response(output="Provisioner package not found.",
                            rc=errno.ENOENT)
        # Get Arguments From Command
        comment = command.options.get(const.SB_COMMENT)
        components = command.options.get(const.SB_COMPONENTS)
        if not components:
            components = []
        if command.options.get(const.SOS_COMP, False) == "true":
            components.append("os")
        comp_list = SupportBundle._get_components(components)

        # Get HostNames and Node Names.
        node_hostname_map = await SupportBundle.get_active_nodes()
        if not isinstance(node_hostname_map, dict):
            return node_hostname_map

        # Start SB Generation on all Nodes.
        for nodename, hostname in node_hostname_map.items():
            Log.debug(f"Connect to {hostname}")
            try:
                await provisioner.begin_bundle_generation(
                    f"bundle_generate '{bundle_id}' '{comment}' "
                    f"'{hostname}' {comp_list}", nodename)
            except BundleError as be:
                Log.error(f"Bundle generation failed.{be}")
                return Response(
                    "Bundle generation failed.\nPlease "
                    "check CLI for details.",
                    rc=errno.EINVAL)
            except Exception as e:
                Log.error(f"Provisioner API call failed : {e}")
                return Response(output="Bundle Generation Failed.",
                                rc=errno.ENOENT)

        symlink_path = const.SYMLINK_PATH
        display_string_len = len(bundle_id) + 4
        response_msg = (
            f"Please use the below bundle id for checking the status of support bundle."
            f"\n{'-' * display_string_len}"
            f"\n| {bundle_id} |"
            f"\n{'-' * display_string_len}"
            f"\nPlease Find the file on -> {symlink_path} .\n")

        return Response(output=response_msg, rc=OPERATION_SUCESSFUL)
Esempio n. 4
0
    async def get_active_nodes():
        """
        This Method is for reading hostnames, node_list information.
        :return: hostnames : List of Hostname :type: List
        :return: node_list : : List of Node Name :type: List
        """
        Log.info("Reading hostnames, node_list information")
        Conf.load('cortx_cluster', 'json:///etc/cortx/cluster.conf')
        node_hostname_map = Conf.get('cortx_cluster', 'cluster')
        if not node_hostname_map:
            response_msg = "Node list and hostname not found."
            return Response(output=response_msg, rc=errno.ENODATA), None

        return node_hostname_map
    async def _get_active_nodes() -> dict:
        """This Method is for reading hostnames, node_list information

        Returns:
            dict:     hosts in cluster eg {node1:fqd1}
            Response: Response object if no host found in config file
        """
        Log.info("Reading hostnames, node_list information")
        Conf.load('cortx_cluster', 'json:///etc/cortx/cluster.conf', \
            skip_reload=True)
        node_hostname_map = Conf.get('cortx_cluster', 'cluster')
        if not node_hostname_map:
            response_msg = "Node list and hostname not found."
            return Response(output=response_msg, rc=errno.ENODATA), None
        return node_hostname_map
Esempio n. 6
0
    def test_output(self):
        self.command = CommandFactory.get_command(self.cli_command,
                                                  self.permissions,
                                                  self.directory_path)
        response = Response(output=self.cmd_response)
        op = Output(self.command, response)
        with open(self.obtained_output_file, "w") as obtained_output:
            op.dump(out=obtained_output,
                    err=sys.stderr,
                    response=response,
                    output_type=self.command.options.get("format"),
                    **self.command.options.get("output"))

        with open(self.expected_output_file) as expected_output:
            with open(self.obtained_output_file) as obtained_output:
                self.assertEqual(expected_output.readlines(),
                                 obtained_output.readlines())
    async def _generate_bundle(command):
        """
        Initializes the process for Generating Support Bundle on Each CORTX Node.

        command:    Command Object :type: command
        return:     None.
        """
        bundle_id = SupportBundle._generate_bundle_id()
        provisioner = ProvisionerServices()
        if not provisioner:
            return Response(output="Provisioner package not found.", \
                rc=errno.ENOENT)
        # Get Arguments From Command
        comment = command.options.get(const.SB_COMMENT)
        components = command.options.get(const.SB_COMPONENTS)
        if not components:
            components = []
        if command.options.get(const.SOS_COMP, False) == 'true':
            components.append('os')
        Conf.load('cortx_conf', 'json:///etc/cortx/cortx.conf', \
            skip_reload=True)
        # Get HostNames and Node Names.
        node_hostname_map = await SupportBundle._get_active_nodes()
        if not isinstance(node_hostname_map, dict):
            return node_hostname_map

        shared_path = Storage.get_path(name='support_bundle')
        path = shared_path if shared_path else Conf.get('cortx_conf',\
            'support>local_path')

        bundle_path = os.path.join(path, bundle_id)
        os.makedirs(bundle_path)

        bundle_obj = Bundle(bundle_id=bundle_id, bundle_path=path, \
            comment=comment,is_shared=True if shared_path else False)

        support_bundle_file = os.path.join(Conf.get('cortx_conf', 'install_path'),\
            'cortx/utils/conf/support_bundle.yaml')
        Conf.load('sb_yaml', f'yaml://{support_bundle_file}', skip_reload=True)
        all_components = Conf.get('sb_yaml', 'COMPONENTS')
        invalid_comps = [
            component for component in components
            if component not in all_components.keys()
        ]
        if invalid_comps:
            components = list(set(components) - set(invalid_comps))
            ComponentsBundle._publish_log(f"""Invalid components - '{", ".join(invalid_comps)}'""", \
                'error', bundle_id, '', comment)
        if invalid_comps and not components:
            return bundle_obj
        comp_list = SupportBundle._get_components(components)

        # Start SB Generation on all Nodes.
        for nodename, hostname in node_hostname_map.items():
            Log.debug(f"Connect to {hostname}")
            try:
                # TODO: pass bundle_path to bundle_generation when args implemented
                await provisioner.begin_bundle_generation(
                    f"bundle_generate '{bundle_id}' '{comment}' "
                    f"'{hostname}' {comp_list}", nodename)
            except BundleError as be:
                Log.error(f"Bundle generation failed.{be}")
                ComponentsBundle._publish_log(f"Bundle generation failed.{be}", \
                    'error', bundle_id, nodename, comment)
            except Exception as e:
                Log.error(f"Internal error, bundle generation failed {e}")
                ComponentsBundle._publish_log(
                    f"Internal error, bundle generation failed \
                    {e}", 'error', bundle_id, nodename, comment)

        # Create common tar.
        #if bundle_obj.is_shared:
        #    tar_dest_file = f"{bundle_id}.tar.gz"
        #    Log.debug(f"Merging all bundle to {bundle_path}/{tar_dest_file}")
        #    try:
        #        Tar(os.path.join(bundle_path, tar_dest_file)).dump([bundle_path])
        #    except:
        #        Log.debug("Merging of node support bundle failed")
        #        return Response(output="Bundle Generation Failed in merging",
        #        rc=errno.EINVAL)
        if command.sub_command_name == 'generate':
            display_string_len = len(bundle_obj.bundle_id) + 4
            response_msg = (
                f"Please use the below bundle id for checking the status of support bundle."
                f"\n{'-' * display_string_len}"
                f"\n| {bundle_obj.bundle_id} |"
                f"\n{'-' * display_string_len}"
                f"\nPlease Find the file on -> {bundle_obj.bundle_path} .\n")
            return Response(output=response_msg, rc=OPERATION_SUCESSFUL)
        return bundle_obj
    async def _generate_bundle(command):
        """
        Initializes the process for Generating Support Bundle at shared path.
        command:    Command Object :type: command
        return:     None.
        """
        # Get Arguments From Command
        bundle_id = command.options.get(const.SB_BUNDLE_ID)
        comment = command.options.get(const.SB_COMMENT)
        duration = command.options.get(const.SB_DURATION)
        size_limit = command.options.get(const.SB_SIZE)
        config_url = command.options.get('config_url')
        binlogs = command.options.get('binlogs')
        coredumps = command.options.get('coredumps')
        stacktrace = command.options.get('stacktrace')
        components = command.options.get('components')
        config_path = config_url.split('//')[1] if '//' in config_url else ''
        path = command.options.get('target_path')
        bundle_path = os.path.join(path, bundle_id)
        try:
            os.makedirs(bundle_path)
        except FileExistsError:
            raise BundleError(
                errno.EINVAL, "Bundle ID already exists,"
                "Please use Unique Bundle ID")

        cluster_conf = MappedConf(config_url)
        # Get Node ID
        node_id = Conf.machine_id
        if node_id is None:
            raise  BundleError(errno.EINVAL, "Invalid node_id: %s", \
                node_id)
        # Update SB status in Filestore.
        # load conf for Support Bundle
        Conf.load(const.SB_INDEX,
                  'json://' + const.FILESTORE_PATH,
                  skip_reload=True)
        data = {
            'status': 'In-Progress',
            'start_time': datetime.strftime(datetime.now(),
                                            '%Y-%m-%d %H:%M:%S')
        }
        Conf.set(const.SB_INDEX, f'{node_id}>{bundle_id}', data)
        Conf.save(const.SB_INDEX)

        node_name = cluster_conf.get(f'node>{node_id}>name')
        Log.info(f'Starting SB Generation on {node_id}:{node_name}')
        # Get required SB size per component
        components_list, service_per_comp = SupportBundle._get_component_and_services(
            cluster_conf, node_id, components)

        if not components_list:
            Log.warn(f"No component specified for {node_name} in CORTX config")
            Log.warn(f"Skipping SB generation on node:{node_name}.")
            return
        num_components = len(components_list)
        size_limit_per_comp = SupportBundle.get_component_size_limit(
            size_limit, num_components)
        bundle_obj = Bundle(bundle_id=bundle_id, bundle_path=bundle_path, \
            comment=comment,node_name=node_name, components=components_list,
            services=service_per_comp)

        # Start SB Generation on Node.
        # Adding CORTX manifest data inside support Bundle.
        try:
            # Copying config file into support bundle.
            common_locations = set()
            if config_path and os.path.exists(config_path):
                Log.info(f'For manifest data collection, taking config from \
                    {config_path} location.')
                # Remove secrets from the input config.
                conf_name = config_path.split('/')[-1]
                sb_config = config_path.replace(conf_name, 'sb_cluster.conf')
                with open(sb_config, 'w+') as sb_file:
                    with open(config_path, 'r') as f:
                        content = f.read()
                        if 'secret:' in content:
                            content = re.sub(r'secret:.+', r'secret: ****',
                                             content)
                        sb_file.write(content)
                conf_target = os.path.join(bundle_path, 'common' + config_path)
                os.makedirs(conf_target.replace(f'/{conf_name}', ''),
                            exist_ok=True)
                shutil.move(sb_config, conf_target)
                common_locations.add(config_path.split('/')[1])

            # Copying "/etc/cortx/solution" directory into support bundle
            # except for "secret" folder.
            sln_target = os.path.join(bundle_path, 'common' + const\
                .CORTX_SOLUTION_DIR)
            if os.path.exists(sln_target):
                shutil.rmtree(sln_target)
            if os.path.exists(const.CORTX_SOLUTION_DIR):
                _ = shutil.copytree(const.CORTX_SOLUTION_DIR, sln_target, \
                        ignore=shutil.ignore_patterns('secret'))
                common_locations.add(const.CORTX_SOLUTION_DIR.split('/')[1])

            # Copying RELEASE.INFO file into support bundle.
            if os.path.exists(const.CORTX_RELEASE_INFO):
                rel_target = os.path.join(bundle_path, 'common' + const\
                    .CORTX_RELEASE_INFO)
                os.makedirs(rel_target.replace('/RELEASE.INFO', ''),
                            exist_ok=True)
                shutil.copyfile(const.CORTX_RELEASE_INFO, rel_target)
                common_locations.add(const.CORTX_RELEASE_INFO.split('/')[1])
            else:
                Log.warn(f'{const.CORTX_RELEASE_INFO} file not found.')

            # Adding node resources health into the support bundle.
            health_target = os.path.join(bundle_path, 'common' + '/health')
            os.makedirs(health_target, exist_ok=True)
            with open(health_target + '/node_health.json', 'w') as fp:
                info = {}
                info["resource_usage"] = {}
                info["resource_usage"]["cpu_usage"] = SupportBundle.\
                    get_cpu_overall_usage()
                info["resource_usage"]["uptime"] = SupportBundle.\
                    get_system_uptime()
                info["resource_usage"]["disk_usage"] = SupportBundle.\
                    get_disk_overall_usage()
                info["resource_usage"]["memory_usage"] = SupportBundle.\
                    get_mem_overall_usage()
                json.dump(info, fp, indent=4)
            common_locations.add('health')

            try:
                common_path = os.path.join(bundle_path, 'common')
                common_tar = os.path.join(common_path, 'common.tar.gz')
                with tarfile.open(common_tar, "w:gz") as tar:
                    if os.path.exists(common_path):
                        tar.add(common_path, arcname='common')

                # Deleting untar directories from the common folder.
                for location in common_locations:
                    untar_location = os.path.join(common_path, location)
                    if os.path.exists(untar_location):
                        shutil.rmtree(untar_location)
            except (OSError, tarfile.TarError) as err:
                Log.error(
                    "Facing issues while adding manifest data into common "
                    "directory: {0}".format(err))

        except BundleError as be:
            Log.error(
                f"Failed to add CORTX manifest data inside Support Bundle.{be}"
            )

        try:
            await ComponentsBundle.init(bundle_obj,
                                        node_id,
                                        config_url,
                                        duration=duration,
                                        size_limit=size_limit_per_comp,
                                        binlogs=binlogs,
                                        coredumps=coredumps,
                                        stacktrace=stacktrace)
        except BundleError as be:
            Log.error(f"Bundle generation failed.{be}")
        except Exception as e:
            Log.error(f"Internal error, bundle generation failed {e}")

        if command.sub_command_name == 'generate':
            display_string_len = len(bundle_obj.bundle_id) + 4
            response_msg = (
                f"Please use the below bundle id for checking the status of support bundle."
                f"\n{'-' * display_string_len}"
                f"\n| {bundle_obj.bundle_id} |"
                f"\n{'-' * display_string_len}"
                f"\nPlease Find the file on -> {bundle_obj.bundle_path} .\n")
            return Response(output=response_msg, rc=OPERATION_SUCESSFUL)
        return bundle_obj