Example #1
0
    def generate(bundle_id: str, target_path: str, cluster_conf_url: str,
                 **filters):
        """Generate a tar file."""
        # duration = filters.get('duration', 'P5D')
        # size_limit = filters.get('size_limit', '500MB')
        # binlogs = filters.get('binlogs', False)
        # coredumps = filters.get('coredumps', False)
        # stacktrace = filters.get('stacktrace', False)
        # TODO process duration, size_limit, binlogs, coredumps and stacktrace
        # Find log dirs
        cluster_conf = MappedConf(cluster_conf_url)
        log_base = cluster_conf.get(CLUSTER_CONF_LOG_KEY)
        local_base = cluster_conf.get(
            f'cortx{_DELIM}common{_DELIM}storage{_DELIM}local')
        machine_id = Conf.machine_id

        if os.path.exists(_sb_tmp_src):
            HASupportBundle.__clear_tmp_files()
        else:
            os.makedirs(_sb_tmp_src)
        # Copy log files
        shutil.copytree(os.path.join(log_base, f'ha/{machine_id}'),\
            os.path.join(_sb_tmp_src, 'logs'))

        # Copy configuration files
        shutil.copytree(os.path.join(local_base, 'ha'),\
            os.path.join(_sb_tmp_src, 'conf'))
        HASupportBundle.__generate_tar(bundle_id, target_path)
        HASupportBundle.__clear_tmp_files()
def main(argv: dict):

    try:
        desc = "CORTX Kafka Setup command"
        command = Cmd.get_command(desc, argv[1:])
        # Get kafka server list from template file
        kafka_config = 'kafka_config'
        Conf.load(kafka_config, command.url)
        kafka_servers = Conf.get(kafka_config, 'cortx>software>kafka>servers')
        # Get log path and initialise Log
        cluster_conf = MappedConf(command.cluster_conf)
        log_dir = cluster_conf.get(CLUSTER_CONF_LOG_KEY)
        log_path = os.path.join(log_dir, f'utils/{Conf.machine_id}')
        log_level = cluster_conf.get('utils>log_level', 'INFO')
        Log.init('kafka_setup', log_path, level=log_level, backup_count=5,\
            file_size_in_mb=5)

        rc = command.process(kafka_servers)
        if rc != 0:
            raise ValueError(f"Failed to run {argv[1]}")

    except KafkaSetupError as e:
        sys.stderr.write("%s\n" % str(e))
        Cmd.usage(argv[0])
        return e.rc()

    except Exception as e:
        sys.stderr.write("error: %s\n\n" % str(e))
        sys.stderr.write("%s\n" % traceback.format_exc())
        Cmd.usage(argv[0])
        return errno.EINVAL
    def _provision_components(cortx_conf: MappedConf, interfaces: Enum,
                              apply_phase: str):
        """Invoke Mini Provisioners of cluster components."""
        node_id, node_name = CortxProvisioner._get_node_info(cortx_conf)
        num_components = int(cortx_conf.get(f'node>{node_id}>num_components'))
        for interface in interfaces:
            for comp_idx in range(0, num_components):
                key_prefix = f'node>{node_id}>components[{comp_idx}]'
                component_name = cortx_conf.get(f'{key_prefix}>name')
                # Get services.
                service_idx = 0
                services = []
                while (cortx_conf.get(f'{key_prefix}>services[{service_idx}]')
                       is not None):
                    services.append(
                        cortx_conf.get(
                            f'{key_prefix}>services[{service_idx}]'))
                    service_idx = service_idx + 1
                service = 'all' if service_idx == 0 else ','.join(services)
                if apply_phase == ProvisionerStages.UPGRADE.value:
                    version = cortx_conf.get(f'{key_prefix}>version')
                    # Skip update for component if it is already updated.
                    is_updated = CortxProvisioner._is_component_updated(
                        component_name, version)
                    if is_updated is True:
                        Log.info(
                            f'{component_name} is already updated with {version} version.'
                        )
                        continue
                CortxProvisioner._update_provisioning_status(
                    cortx_conf, node_id, apply_phase,
                    ProvisionerStatus.PROGRESS.value)
                cmd = (
                    f"/opt/seagate/cortx/{component_name}/bin/{component_name}_setup {interface.value}"
                    f" --config {cortx_conf._conf_url} --services {service}")
                Log.info(f"{cmd}")
                cmd_proc = SimpleProcess(cmd)
                _, err, rc = cmd_proc.run()
                if rc != 0:
                    CortxProvisioner._update_provisioning_status(
                        cortx_conf, node_id, apply_phase,
                        ProvisionerStatus.ERROR.value)
                    raise CortxProvisionerError(rc,
                                                "%s phase of %s, failed. %s",
                                                interface.value,
                                                component_name, err)

                # Update version for each component if Provisioning successful.
                component_version = CortxProvisioner.cortx_release.get_component_version(
                    component_name)
                cortx_conf.set(f'{key_prefix}>version', component_version)
Example #4
0
    def _get_node_list(version_conf: MappedConf):
        """
        Get list of node Id.

        Parameters:
        version_conf - ConfStore instance of Gconf.
        """
        node_list = []
        num_storage_set = int(version_conf.get(const.NUM_STORAGESET_KEY))
        for storage_set_idx in range(0, num_storage_set):
            num_nodes = int(
                version_conf.get(const.NUM_NODES_KEY % storage_set_idx))
            for node_idx in range(0, num_nodes):
                # Add node id to node list. ex: cluster>storage_set[storage_set_idx]>nodes[node_idx].
                # node list: [ '0cdf725015124cbbbc578114dbc51982' ].
                node_list.append(
                    version_conf.get(const.NODE_ID_KEY %
                                     (storage_set_idx, node_idx)))
        return node_list
Example #5
0
def main():
    from cortx.utils.log import Log
    from cortx.utils.conf_store import Conf

    # Setup Parser
    parser = argparse.ArgumentParser(description='Cortx Support Bundle Interface', \
        formatter_class=RawTextHelpFormatter)
    sub_parser = parser.add_subparsers(title='command', \
        help='represents the action from: generate, get_status\n\n', \
        dest='command')

    # Add Command Parsers
    members = inspect.getmembers(sys.modules[__name__])
    for name, cls in members:
        if name != "Cmd" and name.endswith("Cmd"):
            cls.add_args(sub_parser)

    # Parse and Process Arguments
    try:
        args = parser.parse_args()
        cluster_conf_path = args.cluster_conf_path[0]
        cluster_conf = MappedConf(cluster_conf_path)
        log_path = os.path.join(cluster_conf.get(CLUSTER_CONF_LOG_KEY), \
            f'utils/{Conf.machine_id}/support')
        log_level = cluster_conf.get('utils>log_level', 'INFO')
        Log.init('support_bundle', log_path, level=log_level, backup_count=5, \
            file_size_in_mb=5)
        out = args.func(args)
        if out is not None and len(out) > 0:
            print(out)
        return 0

    except Exception as e:
        sys.stderr.write("%s\n\n" % str(e))
        sys.stderr.write("%s\n" % traceback.format_exc())
        return errno.EINVAL
    def _get_node_info(cortx_conf: MappedConf):
        """To get the node information."""
        node_id = Conf.machine_id
        if node_id is None:
            raise CortxProvisionerError(errno.EINVAL, "Invalid node_id: %s", \
                node_id)

        # Reinitialize logging with configured log path
        log_path = os.path.join(cortx_conf.get('cortx>common>storage>log'),
                                const.APP_NAME, node_id)
        log_level = os.getenv('CORTX_PROVISIONER_DEBUG_LEVEL',
                              const.DEFAULT_LOG_LEVEL)
        CortxProvisionerLog.reinitialize(const.SERVICE_NAME,
                                         log_path,
                                         level=log_level)

        if cortx_conf.get(f'node>{node_id}>name') is None:
            raise CortxProvisionerError(
                errno.EINVAL,
                f'Node name not found in cortx config for node {node_id}.')

        node_name = cortx_conf.get(f'node>{node_id}>name')

        return node_id, node_name
Example #7
0
    def get_installed_version(resource_id: str, conf_url: str):
        """
        Get current deployed versions on the node.

        Parameters:
        resource_id - Name of the node (as in gconf).
        conf_url - Global Configuration URL.
        """
        node_id = None
        version_info = {}
        version_conf = MappedConf(conf_url)

        # Get list of all the nodes in the cluster.
        node_list = Release._get_node_list(version_conf)
        for node in node_list:
            if resource_id == version_conf.get(const.NODE_NAME_KEY % node):
                node_id = node
        if node_id is None:
            raise SetupError(errno.EINVAL,
                             "Invalid Resource Id %s." % resource_id)

        # Get version details of all the components of a node
        num_components = int(
            version_conf.get(const.NUM_COMPONENTS_KEY % node_id))
        for component in range(0, num_components):
            _name = version_conf.get(const.COMPONENT_NAME_KEY %
                                     (node_id, component))
            _version = version_conf.get(const.COMPONENT_VERSION_KEY %
                                        (node_id, component))
            if _version is not None:
                version_info[_name] = _version
            else:
                raise SetupError(
                    INTERNAL_ERROR,
                    "No installed version found for component %s" % _name)

        # get cluster release version
        release_name = version_conf.get(const.RELEASE_NAME_KEY)
        release_version = version_conf.get(const.RELEASE_VERSION_KEY)
        version_info[release_name] = release_version

        return version_info if len(version_info) > 1 else 0
Example #8
0
            web.post('/AuditLog/webhook/', \
            AuditLogRequestHandler.send_webhook_info)
            ])
        super().run_app(web, message_server_port)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Utils server CLI',
        formatter_class=RawTextHelpFormatter)
    parser.add_argument('-c', '--config', dest='cluster_conf',\
        help="Cluster config file path for Support Bundle")
    args=parser.parse_args()
    cluster_conf_url = args.cluster_conf
    cluster_conf = MappedConf(cluster_conf_url)
    # Get the log path
    log_dir = cluster_conf.get(CLUSTER_CONF_LOG_KEY)
    if not log_dir:
        raise UtilsServerError(errno.EINVAL, "Fail to initialize logger."+\
            " Unable to find log_dir path entry")
    utils_log_path = os.path.join(log_dir, f'utils/{Conf.machine_id}/utils_server')
    # Get the log level
    log_level = cluster_conf.get('utils>log_level', 'INFO')
    Log.init('utils_server', utils_log_path, level=log_level, backup_count=5, \
        file_size_in_mb=5)
    message_bus_backend = cluster_conf.get('cortx>utils>message_bus_backend')
    message_server_endpoints = cluster_conf.get(
        f'cortx>external>{message_bus_backend}>endpoints')
    message_server_port = cluster_conf.get('cortx>utils>message_server_port', 28300)
    cluster_id = cluster_conf.get('cluster>id')
    MessageServer(message_server_endpoints, message_server_port, cluster_id)
Example #9
0
import re
import time
import errno
import psutil
from datetime import datetime

from cortx.utils import const
from cortx.utils.conf_store import MappedConf
from cortx.utils.kv_store import KvStoreFactory
from cortx.utils.discovery.error import DiscoveryError
from cortx.utils.discovery.resource import Resource, ResourceFactory

# Load cortx common config
store_type = "json"
cluster_conf = MappedConf(const.CLUSTER_CONF)
local_storage_path = cluster_conf.get('cortx>common>storage>local')
config_url = "%s://%s" % (
    store_type, os.path.join(local_storage_path, 'utils/conf/cortx.conf'))
common_config = KvStoreFactory.get_instance(config_url)
common_config.load()

# Load Discovery request status tracker
try:
    os.makedirs(common_config.get(["discovery>resource_map>location"])[0],
                exist_ok=True)
except PermissionError as err:
    raise DiscoveryError(errno.EACCES,
                         "Failed to create default store directory. %s" % err)
requests_url = "%s://%s" % (store_type,
                            os.path.join(
                                common_config.get([
Example #10
0
    def config_apply(solution_config_url: str, cortx_conf_url: str = None,
        force_override: bool = False):
        """
        Description:

        Parses input config and store in CORTX config location
        Parameters:
        [IN]  Solution Config URL
        [OUT] CORTX Config URL
        """
        if Log.logger is None:
            CortxProvisionerLog.initialize(const.SERVICE_NAME, const.TMP_LOG_PATH)

        if cortx_conf_url is None:
            cortx_conf_url = CortxProvisioner._cortx_conf_url
        cortx_conf = MappedConf(CortxProvisioner._tmp_cortx_conf_url)

        # Load same config again if force_override is True
        try:
            cs_option = {"fail_reload": False} if force_override else {"skip_reload": True}
            Log.info('Applying config %s' % solution_config_url)
            Conf.load(CortxProvisioner._solution_index, solution_config_url,
                **cs_option)
        except ConfError as e:
            Log.error(f'Unable to load {solution_config_url} url, Error:{e}')

        # Secrets path from config file
        if cortx_conf.get('cortx>common>storage>local'):
            CortxProvisioner._secrets_path = cortx_conf.get('cortx>common>storage>local')+CortxProvisioner._rel_secret_path

        # source code for encrypting and storing secret key
        if Conf.get(CortxProvisioner._solution_index, 'cluster') is not None:
            CortxProvisioner.apply_cluster_config(cortx_conf, CortxProvisioner.cortx_release)

        if Conf.get(CortxProvisioner._solution_index, 'cortx') is not None:
            # generating cipher key
            cipher_key = None
            cluster_id = Conf.get(CortxProvisioner._solution_index, 'cluster>id')
            if cluster_id is None:
                cluster_id = cortx_conf.get('cluster>id')
                if cluster_id is None:
                    raise CortxProvisionerError(errno.EINVAL, 'Cluster ID not specified')
            cipher_key = Cipher.gen_key(cluster_id, 'cortx')
            if cipher_key is None:
                raise CortxProvisionerError(errno.EINVAL, 'Cipher key not specified')
            for key in Conf.get_keys(CortxProvisioner._solution_index):
                # using path /etc/cortx/solution/secret to confirm secret
                if key.endswith('secret'):
                    secret_val = Conf.get(CortxProvisioner._solution_index, key)
                    val = None
                    with open(os.path.join(CortxProvisioner._secrets_path, secret_val), 'rb') as secret:
                        val = secret.read()
                    if val is None:
                        raise CortxProvisionerError(errno.EINVAL,
                            f'Could not find the Secret in  {CortxProvisioner._secrets_path}')
                    val = Cipher.encrypt(cipher_key, val)
                    # decoding the byte string in val variable
                    Conf.set(CortxProvisioner._solution_index, key, val.decode('utf-8'))
            CortxProvisioner.apply_cortx_config(cortx_conf, CortxProvisioner.cortx_release)
            # Adding array count key in conf
            cortx_conf.add_num_keys()
            Conf.save(cortx_conf._conf_idx)
Example #11
0
    async def init(bundle_obj, node_id, config_url, **kwargs):
        """
        Initializes the Process of Support Bundle Generation for Every Component.

        command:        cli Command Object :type: command
        return:         None
        """
        cluster_conf = MappedConf(config_url)
        log_path = os.path.join(cluster_conf.get(CLUSTER_CONF_LOG_KEY), \
            f'utils/{Conf.machine_id}/support')
        log_level = cluster_conf.get('utils>log_level', 'INFO')
        Log.init('support_bundle_node', log_path, level=log_level, \
            backup_count=5, file_size_in_mb=5)
        bundle_id = bundle_obj.bundle_id
        node_name = bundle_obj.node_name
        comment = bundle_obj.comment
        components_list = bundle_obj.components
        services_dict = bundle_obj.services
        Log.info(f"components:{components_list}")
        bundle_path = bundle_obj.bundle_path
        duration = kwargs.get('duration')
        size_limit = kwargs.get('size_limit')
        binlogs = kwargs.get('binlogs')
        coredumps = kwargs.get('coredumps')
        stacktrace = kwargs.get('stacktrace')

        Log.debug((f"{const.SB_BUNDLE_ID}: {bundle_id}, {const.SB_NODE_NAME}: "
            f"{node_name}, {const.SB_COMMENT}: {comment}, "
            f"{const.SB_COMPONENTS}: {components_list}, {const.SOS_COMP}"))
        # Read support_bundle.Yaml and Check's If It Exists.
        cmd_setup_file = os.path.join(
            cluster_conf.get('install_path', DEFAULT_INSTALL_PATH),
            const.SUPPORT_YAML)
        try:
            support_bundle_config = Yaml(cmd_setup_file).load()
        except Exception as e:
            Log.error(f"Internal error while parsing YAML file {cmd_setup_file}{e}")
        if not support_bundle_config:
            Log.error(f"No such file {cmd_setup_file}. ERROR:{ERROR}")

        # Start Execution for each Component Command.
        command_files_info = support_bundle_config.get('COMPONENTS')
        # OS Logs are specifically generated hence here Even
        # When All is Selected O.S. Logs Will Be Skipped.
        for each_component in components_list:
            services = services_dict[each_component]
            components_commands = []
            components_files = command_files_info[each_component]
            for file_path in components_files:
                if not os.path.exists(file_path):
                    Log.error(f"'{file_path}' file does not exist!")
                    continue
                try:
                    file_data = Yaml(file_path).load()
                except Exception as e:
                    Log.error(f"Internal error while parsing YAML file {file_path}{e}")
                    file_data = None
                    break
                if file_data:
                    components_commands = file_data.get(
                        const.SUPPORT_BUNDLE.lower(), [])
                else:
                    Log.error(f"Support.yaml file is empty: {file_path}")
                    break
                if components_commands:
                    component, return_code = await(\
                        ComponentsBundle._exc_components_cmd(\
                        components_commands, f'{bundle_id}_{node_id}_{each_component}',
                            f'{bundle_path}{os.sep}', each_component,
                            node_name, comment, config_url, services,
                            binlogs, coredumps, stacktrace, duration,
                            size_limit))
                    if return_code != 0:
                        Log.error(
                            f"Bundle generation failed for component - '{component}'")
                    else:
                        Log.info(
                            f"Bundle generation started for component - '{component}'")
        tar_file_name = os.path.join(bundle_path, \
            f'{bundle_id}_{node_id}.tar.gz')

        ComponentsBundle._create_summary_file(bundle_id, node_name, \
            comment, bundle_path)
        try:
            Log.debug(f"Generating tar.gz file on path {tar_file_name} "
                f"from {bundle_path}")
            Tar(tar_file_name).dump([bundle_path])
            bundle_status = f"Successfully generated SB at path:{bundle_path}"
        except Exception as e:
            bundle_status = f"Failed to generate tar file. ERROR:{e}"
            Log.error(f"Could not generate tar file {e}")
        finally:
            if os.path.exists(bundle_path):
                for each_dir in os.listdir(bundle_path):
                    comp_dir = os.path.join(bundle_path, each_dir)
                    if os.path.isdir(comp_dir):
                        shutil.rmtree(comp_dir)
                if os.path.exists(os.path.join(bundle_path, 'summary.yaml')):
                    os.remove(os.path.join(bundle_path, 'summary.yaml'))

        Log.info("Support bundle generation completed.")
        # Update Status in ConfStor
        Conf.load(const.SB_INDEX, 'json://' + const.FILESTORE_PATH, fail_reload=False)
        Conf.set(const.SB_INDEX, f'{node_id}>{bundle_id}>status', bundle_status)
        Conf.save(const.SB_INDEX)
    def _validate_provisioning_status(cortx_conf: MappedConf, node_id: str,
                                      apply_phase: str):
        """Validate provisioning."""
        ret_code = 0
        recent_phase = cortx_conf.get(f'node>{node_id}>provisioning>phase')
        recent_status = cortx_conf.get(f'node>{node_id}>provisioning>status')
        msg = f'Recent phase for this node is {recent_phase} and ' + \
                f'recent status is {recent_status}. '
        # {apply_phase: {recent_phase: {recent_status: [boolean_result,rc]}}}
        validations_checks = {
            ProvisionerStages.DEPLOYMENT.value: {
                ProvisionerStages.DEPLOYMENT.value: {
                    ProvisionerStatus.DEFAULT.value: [True, 0],
                    ProvisionerStatus.ERROR.value: [True, 0],
                    ProvisionerStatus.PROGRESS.value: [True, 0],
                    ProvisionerStatus.SUCCESS.value: [False, 0]
                },
                ProvisionerStages.UPGRADE.value: {
                    ProvisionerStatus.DEFAULT.value: [True, 0],
                    ProvisionerStatus.ERROR.value: [True, 0],
                    ProvisionerStatus.PROGRESS.value: [True, 0],
                    ProvisionerStatus.SUCCESS.value: [True, 0]
                }
            },
            ProvisionerStages.UPGRADE.value: {
                ProvisionerStages.DEPLOYMENT.value: {
                    ProvisionerStatus.DEFAULT.value: [False, 1],
                    ProvisionerStatus.ERROR.value: [False, 1],
                    ProvisionerStatus.PROGRESS.value: [False, 1],
                    ProvisionerStatus.SUCCESS.value: [True, 0]
                },
                ProvisionerStages.UPGRADE.value: {
                    ProvisionerStatus.DEFAULT.value: [True, 0],
                    ProvisionerStatus.ERROR.value: [True, 0],
                    ProvisionerStatus.PROGRESS.value: [True, 0],
                    ProvisionerStatus.SUCCESS.value: [True, 0]
                }
            }
        }
        if recent_phase is None and recent_status is None:
            Log.info(msg + f'Performing {apply_phase} on this node.')
            return True, ret_code

        if (not validations_checks.get(apply_phase)
                or not validations_checks.get(apply_phase).get(recent_phase)
                or not validations_checks.get(apply_phase).get(
                    recent_phase).get(recent_status)):
            Log.error('Invalid phase or status.')
            ret_code = 1
            return False, ret_code

        validate_result = validations_checks.get(apply_phase).get(
            recent_phase).get(recent_status)
        if validate_result[1] != 0:
            Log.error(msg + f'{apply_phase} is not possible on this node.')
            if apply_phase == ProvisionerStages.UPGRADE.value:
                # Reset status.
                recent_status = cortx_conf.set(
                    f'node>{node_id}>provisioning>status',
                    ProvisionerStatus.DEFAULT.value)
        else:
            Log.info(msg)
        return validate_result[0], validate_result[1]