Example #1
0
    def save(self, cortx_conf, cortx_solution_config):
        """Save cortx-config into confstore"""

        try:
            cortx_solution_config_keys = filter(
                lambda x: x.startswith('cortx'),
                Conf.get_keys(cortx_solution_config))
            cortx_conf.copy(cortx_solution_config, cortx_solution_config_keys)

            # Change environment_type to setup_type
            # TODO: remove this code once setup_type key is deleted.
            cortx_conf.set('cortx>common>setup_type',
                           cortx_conf.get('cortx>common>environment_type'))
            cortx_conf.delete('cortx>common>environment_type')
            # Check for release key.
            release_spec = self._cortx_solution_config.get('common').get(
                'release')
            is_valid, release_info = self._cortx_release.validate(release_spec)
            if is_valid is False:
                for key in release_info.keys():
                    release_key_path = f'cortx>common>release>{key}'
                    Log.warn(
                        f'Release key {release_key_path} is missing or has '
                        'incorrect value.')
                    Log.info(f'Adding key "{release_key_path}" '
                             f'and value "{release_info[key]}" in confstore.')
                    cortx_conf.set(release_key_path, release_info[key])

        except KeyError as e:
            raise CortxProvisionerError(
                errno.EINVAL,
                f'Error occurred while adding CORTX config information into confstore {e}'
            )
Example #2
0
    def _provision_components(cortx_conf_url: str, _conf_idx: str, interfaces: Enum, apply_phase: str):
        """Invoke Mini Provisioners of cluster components."""
        node_id, _ = CortxProvisioner._get_node_info(_conf_idx)
        num_components = int(Conf.get(_conf_idx, 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 = Conf.get(_conf_idx, f'{key_prefix}>name')
                # Check if RPM exists for the component, if it does exist get the build version
                component_version = CortxProvisioner.cortx_release.get_component_version(
                    component_name)
                # Get services.
                service_idx = 0
                services = []
                while (Conf.get(_conf_idx, f'{key_prefix}>services[{service_idx}]') is not None):
                    services.append(Conf.get(_conf_idx, 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 = Conf.get(_conf_idx, 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(
                        _conf_idx, node_id, apply_phase, ProvisionerStatus.PROGRESS.value)
                if interface.value == 'upgrade':
                    # TODO: add --changeset parameter once all components support config upgrade
                    cmd = (
                        f"/opt/seagate/cortx/{component_name}/bin/{component_name}_setup {interface.value}"
                        f" --config {cortx_conf_url} --services {service}")
                else:
                    cmd = (
                        f"/opt/seagate/cortx/{component_name}/bin/{component_name}_setup {interface.value}"
                        f" --config {cortx_conf_url} --services {service}")
                Log.info(f"{cmd}")
                cmd_proc = SimpleProcess(cmd)
                _, err, rc = cmd_proc.run()
                if rc != 0:
                    CortxProvisioner._update_provisioning_status(
                        _conf_idx, 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.
                Conf.set(_conf_idx, f'{key_prefix}>version', component_version)

                # TODO: Remove the following code when gconf is completely moved to consul.
                CortxProvisioner._load_consul_conf(CortxProvisioner._cortx_gconf_consul_index)
                Conf.set(CortxProvisioner._cortx_gconf_consul_index,
                        f'{key_prefix}>version', component_version)
                Conf.save(CortxProvisioner._cortx_gconf_consul_index)
    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 cluster_deploy(cortx_conf_url: str, force_override: bool = False):
     """
     Description:
     Configures Cluster Components
     1. Reads Cortx Config and obtain cluster components
     2. Invoke Mini Provisioners of cluster components
     Paramaters:
     [IN] CORTX Config URL
     """
     apply_phase = ProvisionerStages.DEPLOYMENT.value
     node_id, node_name = CortxProvisioner._get_node_info(CortxProvisioner._conf_index)
     is_valid, ret_code = CortxProvisioner._validate_provisioning_status(
         CortxProvisioner._conf_index, node_id, apply_phase)
     if is_valid is False:
         if force_override is False:
             Log.warn('Validation check failed, Aborting cluster bootstarp'
                 f' with return code {ret_code}')
             return ret_code
         else:
             Log.info('Validation check failed, Forcefully overriding deployment.')
     Log.info(f"Starting cluster bootstrap on {node_id}:{node_name}")
     CortxProvisioner._update_provisioning_status(
         CortxProvisioner._conf_index, node_id, apply_phase)
     CortxProvisioner._provision_components(cortx_conf_url, CortxProvisioner._conf_index, DeploymentInterfaces, apply_phase)
     CortxProvisioner._add_version_info(CortxProvisioner._conf_index, node_id)
     CortxProvisioner._update_provisioning_status(
         CortxProvisioner._conf_index, node_id, apply_phase, ProvisionerStatus.SUCCESS.value)
     Log.info(f"Finished cluster bootstrap on {node_id}:{node_name}")
    def cluster_upgrade(cortx_conf_url: str, force_override: bool = False):
        """
        Description:
        Upgrades Cluster Components
        1. Reads Cortx Config and obtain cluster components
        2. Invoke upgrade phase of cluster components
        Paramaters:
        [IN] CORTX Config URL
        """
        cortx_conf = MappedConf(cortx_conf_url)
        apply_phase = ProvisionerStages.UPGRADE.value
        node_id, node_name = CortxProvisioner._get_node_info(cortx_conf)
        is_valid, ret_code = CortxProvisioner._validate_provisioning_status(
            cortx_conf, node_id, apply_phase)
        if is_valid is False:
            if force_override is False:
                Log.warn('Validation check failed, Aborting upgrade with '
                         f'return code {ret_code}.')
                return ret_code
            else:
                Log.info(
                    'Validation check failed, Forcefully overriding upgrade.')

        Log.info(f"Starting cluster upgrade on {node_id}:{node_name}")
        CortxProvisioner._update_provisioning_status(cortx_conf, node_id,
                                                     apply_phase)

        CortxProvisioner._provision_components(cortx_conf, UpgradeInterfaces,
                                               apply_phase)
        # Update CORTX version, once the upgrade is successful
        CortxProvisioner._add_version_info(cortx_conf, node_id)
        CortxProvisioner._update_provisioning_status(
            cortx_conf, node_id, apply_phase, ProvisionerStatus.SUCCESS.value)
        Log.info(f"Finished cluster upgrade on {node_id}:{node_name}")
Example #6
0
    def _validate_provisioning_status(_conf_idx: str, node_id: str, apply_phase: str):
        """Validate provisioning."""
        ret_code = 0
        recent_phase = Conf.get(_conf_idx, f'node>{node_id}>provisioning>phase')
        recent_status = Conf.get(_conf_idx, 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 = Conf.set(_conf_idx, f'node>{node_id}>provisioning>status',
                    ProvisionerStatus.DEFAULT.value)
        else:
            Log.info(msg)
        return validate_result[0], validate_result[1]
Example #7
0
    def _validate(self, s_set: dict):
        """
        validates a give storage_sets to have required properties
        Raises exception if there is any entry missing
        """
        s_set_name = s_set.get('name')
        if s_set_name is None:
            raise CortxProvisionerError(
                errno.EINVAL, 'Missing name for the storage_set entry')

        Log.debug("Validating storage set '%s' properties" % s_set_name)

        required_keys_for_storage_set = ['durability', 'nodes']
        for k in required_keys_for_storage_set:
            if s_set.get(k) is None:
                raise VError(
                    errno.EINVAL,
                    f"'{k}' property is unspecified for storage_set {s_set_name}."
                )
Example #8
0
    def _check_storage_sets(self):
        """Validate storage_sets present in cortx_conf."""
        solution_config_storage_sets = self._get_config(
            ConfigValidator._key_storage_set_sc)

        storage_set_counter = 0
        while self.cortx_conf.get(
                f'cluster>storage_set[{storage_set_counter}]>name'
        ) is not None:
            storage_set_counter = storage_set_counter + 1
        if len(solution_config_storage_sets) != storage_set_counter:
            Log.debug(
                f'Number of storage_sets define in {self.solution_conf_url} is '
                f'{len(solution_config_storage_sets)} and in {self.cortx_conf_url} '
                f'is {storage_set_counter}')
            raise CortxProvisionerError(
                errno.EINVAL,
                f'Number of storage_sets define in {self.cortx_conf_url} '
                f'and {self.solution_conf_url} is not equal.')
        return 0
Example #9
0
 def _check_number_of_nodes(self):
     """Validate number of nodes specified in cortx_conf."""
     solution_config_storage_sets = self._get_config(
         ConfigValidator._key_storage_set_sc)
     for storage_set in solution_config_storage_sets:
         storage_set_name = storage_set['name']
         solution_config_nodes = storage_set['nodes']
         # Get number of nodes from confstore which has same storage_set_name.
         conf_store_nodes = self.cortx_conf.search('node', 'storage_set',
                                                   storage_set_name)
         if len(solution_config_nodes) != len(conf_store_nodes):
             Log.debug(
                 f'Number of nodes define in {self.solution_conf_url} is '
                 f'{len(solution_config_nodes)} and {self.cortx_conf_url} is '
                 f'{len(conf_store_nodes)}')
             raise CortxProvisionerError(
                 errno.EINVAL,
                 f'Number of nodes define in {self.cortx_conf_url} and '
                 f'{self.solution_conf_url} is not equal.')
     return 0
Example #10
0
    def _validate(self, node: dict):
        """
        validates a give node to have required properties
        Raises exception if there is any entry missing
        """

        node_name = node.get('name')
        if node_name is None:
            raise CortxProvisionerError(errno.EINVAL,
                                        'Missing name for the node entry')

        Log.debug("Validating node '%s' properties" % node_name)

        required_keys_for_node = [
            'id', 'components', 'storage_set', 'hostname'
        ]
        for k in required_keys_for_node:
            if node.get(k) is None:
                raise CortxProvisionerError(
                    errno.EINVAL,
                    f"'{k}' property is unspecified for node {node_name}")
Example #11
0
 def _validate_services(self, service_list, component_name):
     """Verify services defined in cluster.yaml is supported in constant file."""
     for service in service_list:
         # check if service name is define in utils const.py file.
         if service not in Const._value2member_map_:
             raise CortxProvisionerError(errno.EINVAL,
                                         f'{service} is not supported.')
         # Verify service_name is define for the specific component.
         # Get all keys from constant file which has same 'service_name'.
         constant_service_keys = [
             key for key, enum_ele in Const.__members__.items()
             if enum_ele.value.lower() == service.lower()
         ]
         if not any(component_name.upper() in key
                    for key in constant_service_keys):
             Log.debug(
                 f'"{service}" service defined in "{self.solution_conf_url}" for '
                 f'"{component_name}", is not supported.')
             raise CortxProvisionerError(
                 errno.EINVAL,
                 f'Component "{component_name}" does not support service "{service}".'
             )
     return 0
Example #12
0
    def cluster_upgrade(cortx_conf_url: str, force_override: bool = False):
        """
        Description:
        Upgrades Cluster Components
        1. Reads Cortx Config and obtain cluster components
        2. Invoke upgrade phase of cluster components
        Paramaters:
        [IN] CORTX Config URL
        """
        # query to get cluster health
        upgrade_mode = os.getenv(const.UPGRADE_MODE_KEY, const.UPGRADE_MODE_VAL).upper()
        if upgrade_mode != "COLD" and not CortxProvisioner.is_cluster_healthy():
            Log.error('Cluster is unhealthy, Aborting upgrade with return code 1')
            return 1
        apply_phase = ProvisionerStages.UPGRADE.value
        node_id, node_name = CortxProvisioner._get_node_info(CortxProvisioner._conf_index)
        is_valid, ret_code = CortxProvisioner._validate_provisioning_status(
            CortxProvisioner._conf_index, node_id, apply_phase)
        if is_valid is False:
            if force_override is False:
                Log.warn('Validation check failed, Aborting upgrade with '
                    f'return code {ret_code}.')
                return ret_code
            else:
                Log.info('Validation check failed, Forcefully overriding upgrade.')

        Log.info(f"Starting cluster upgrade on {node_id}:{node_name}")
        CortxProvisioner._update_provisioning_status(
            CortxProvisioner._conf_index, node_id, apply_phase)

        CortxProvisioner._provision_components(cortx_conf_url, CortxProvisioner._conf_index, UpgradeInterfaces, apply_phase)
        # Update CORTX version, once the upgrade is successful
        CortxProvisioner._add_version_info(CortxProvisioner._conf_index, node_id)
        CortxProvisioner._update_provisioning_status(
            CortxProvisioner._conf_index, node_id, apply_phase, ProvisionerStatus.SUCCESS.value)
        Log.info(f"Finished cluster upgrade on {node_id}:{node_name}")
Example #13
0
def main():
    CortxProvisionerLog.initialize(const.SERVICE_NAME, const.TMP_LOG_PATH)
    try:
        # Parse and Process Arguments
        command = Cmd.get_command(sys.modules[__name__], 'cortx_setup', \
            sys.argv[1:])
        rc = command.process()

    except CortxProvisionerError as e:
        Log.error('%s' % str(e))
        Log.error('%s' % traceback.format_exc())
        rc = e.rc

    except Exception as e:
        Log.error('%s' % str(e))
        Log.error('%s' % traceback.format_exc())
        rc = errno.EINVAL

    return rc
Example #14
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)