def test_update_in_env(self): env = plan_utils.get_env(self.swift, self.container) updated_env = plan_utils.update_in_env(self.swift, env, 'template', 'updated-overcloud.yaml') self.assertEqual(updated_env['template'], 'updated-overcloud.yaml') updated_env = plan_utils.update_in_env( self.swift, env, 'parameter_defaults', {'another-key': 'another-value'}) self.assertEqual( updated_env['parameter_defaults'], { 'BlockStorageCount': 42, 'OvercloudControlFlavor': 'yummy', 'another-key': 'another-value' }) updated_env = plan_utils.update_in_env(self.swift, env, 'parameter_defaults', delete_key=True) self.assertNotIn('parameter_defaults', updated_env) self.swift.get_object.assert_called() self.swift.put_object.assert_called()
def run(self, context): # get the stack. Error if doesn't exist heat = self.get_orchestration_client(context) try: stack = heat.stacks.get(self.container) except heatexceptions.HTTPNotFound: msg = "Error retrieving stack: %s" % self.container LOG.exception(msg) return actions.Result(error=msg) swift = self.get_object_client(context) plan_env = plan_utils.get_env(swift, self.container) # Get output and check if DeployStep are None steps = ['OS::TripleO::DeploymentSteps'] for output in stack.to_dict().get('outputs', {}): if output['output_key'] == 'RoleData': for role in output['output_value']: steps.append("OS::TripleO::Tasks::%sPreConfig" % role) steps.append("OS::TripleO::Tasks::%sPostConfig" % role) # Remove noop Steps for step in steps: if step in plan_env.get('resource_registry', {}): if plan_env['resource_registry'][step] == 'OS::Heat::None': plan_env['resource_registry'].pop(step) # Push plan_env plan_utils.put_env(swift, plan_env)
def run(self, context): swift = self.get_object_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) parameter_defaults = env.get('parameter_defaults', {}) passwords = self._get_overriden_passwords(env.get('passwords', {}), parameter_defaults) next_index = self.get_next_index(passwords['KeystoneFernetKeys']) keys_map = self.rotate_keys(passwords['KeystoneFernetKeys'], next_index) max_keys = self.get_max_keys_value(parameter_defaults) keys_map = self.purge_excess_keys(max_keys, keys_map) env['passwords']['KeystoneFernetKeys'] = keys_map try: plan_utils.put_env(swift, env) except swiftexceptions.ClientException as err: err_msg = "Error uploading to container: %s" % err LOG.exception(err_msg) return actions.Result(error=err_msg) self.cache_delete(context, self.container, "tripleo.parameters.get") return keys_map
def run(self, context): heat = self.get_orchestration_client(context) swift = self.get_object_client(context) mistral = self.get_workflow_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) try: stack_env = heat.stacks.environment( stack_id=self.container) # legacy heat resource names from overcloud.yaml # We don't modify these to avoid changing defaults for pw_res in constants.LEGACY_HEAT_PASSWORD_RESOURCE_NAMES: try: res = heat.resources.get(self.container, pw_res) param_defaults = stack_env.get('parameter_defaults', {}) param_defaults[pw_res] = res.attributes['value'] except heat_exc.HTTPNotFound: LOG.debug('Heat resouce not found: %s' % pw_res) pass except heat_exc.HTTPNotFound: stack_env = None passwords = password_utils.generate_passwords(mistral, stack_env) # if passwords don't yet exist in plan environment if 'passwords' not in env: env['passwords'] = {} # ensure all generated passwords are present in plan env, # but respect any values previously generated and stored for name, password in passwords.items(): if name not in env['passwords']: env['passwords'][name] = password try: plan_utils.put_env(swift, env) except swiftexceptions.ClientException as err: err_msg = "Error uploading to container: %s" % err LOG.exception(err_msg) return actions.Result(error=err_msg) self.cache_delete(context, self.container, "tripleo.parameters.get") return env['passwords']
def run(self, context): swift = self.get_object_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) parameter_defaults = env.get('parameter_defaults', {}) passwords = env.get('passwords', {}) return self._get_overriden_passwords(passwords, parameter_defaults)
def run(self, context): cached = self.cache_get(context, self.container, "tripleo.parameters.get") if cached is not None: return cached processed_data = super(GetParametersAction, self).run(context) # If we receive a 'Result' instance it is because the parent action # had an error. if isinstance(processed_data, actions.Result): return processed_data processed_data['show_nested'] = True # respect previously user set param values swift = self.get_object_client(context) heat = self.get_orchestration_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) params = env.get('parameter_defaults') fields = { 'template': processed_data['template'], 'files': processed_data['files'], 'environment': processed_data['environment'], 'show_nested': True } result = { 'heat_resource_tree': heat.stacks.validate(**fields), 'environment_parameters': params, } self.cache_set(context, self.container, "tripleo.parameters.get", result) return result
def run(self, context): try: swift = self.get_object_client(context) plan_env = plan_utils.get_env(swift, self.container) if self.env_key in plan_env.keys(): if self.delete: try: plan_env[self.env_key].pop(self.parameter) except KeyError: pass else: plan_env[self.env_key].update({self.parameter: self.value}) else: msg = "The environment key doesn't exist: %s" % self.env_key return actions.Result(error=msg) except swiftexceptions.ClientException as err: msg = "Error attempting an operation on container: %s" % err return actions.Result(error=msg) except Exception as err: msg = "Error while updating plan: %s" % err return actions.Result(error=msg)
def run(self, context): orchestration_client = self.get_orchestration_client(context) swift = self.get_object_client(context) try: stack = orchestration_client.stacks.get(self.container) except heat_exc.HTTPNotFound: error = ( "The Heat stack {} could not be found. Make sure you have " "deployed before calling this action.").format(self.container) return actions.Result(error=error) # We need to check parameter_defaults first for a user provided # password. If that doesn't exist, we then should look in the # automatically generated passwords. # TODO(d0ugal): Abstract this operation somewhere. We shouldn't need to # know about the structure of the environment to get a password. try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % (self.container, err)) LOG.error(err_msg) return actions.Result(error=err_msg) try: parameter_defaults = env['parameter_defaults'] passwords = env['passwords'] admin_pass = parameter_defaults.get('AdminPassword') if admin_pass is None: admin_pass = passwords['AdminPassword'] except KeyError: error = ("Unable to find the AdminPassword in the plan " "environment.") return actions.Result(error=error) return overcloudrc.create_overcloudrc(stack, self.no_proxy, admin_pass)
def run(self, context): swift = self.get_object_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % (self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) for k, v in self.environments.items(): found = False if {'path': k} in env['environments']: found = True if v: if not found: env['environments'].append({'path': k}) else: if found: env['environments'].remove({'path': k}) if self.purge_missing: for e in env['environments']: if e.get('path') not in self.environments: env['environments'].remove(e) self.cache_delete(context, self.container, "tripleo.parameters.get") try: plan_utils.put_env(swift, env) except swiftexceptions.ClientException as err: err_msg = "Error uploading to container: %s" % err LOG.exception(err_msg) return actions.Result(error=err_msg) return env
def run(self, context): swift = self.get_object_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) try: plan_utils.update_in_env(swift, env, self.key, delete_key=True) except swiftexceptions.ClientException as err: err_msg = ("Error updating environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) self.cache_delete(context, self.container, "tripleo.parameters.get") return env
def test_get_env(self): env = plan_utils.get_env(self.swift, self.container) self.swift.get_object.assert_called() self.assertEqual(env['template'], 'overcloud.yaml')
def run(self, context): # check to see if the stack exists heat = self.get_orchestration_client(context) try: stack = heat.stacks.get(self.container) except heat_exc.HTTPNotFound: stack = None stack_is_new = stack is None # update StackAction, DeployIdentifier and UpdateIdentifier swift = self.get_object_client(context) parameters = dict() if not self.skip_deploy_identifier: parameters['DeployIdentifier'] = int(time.time()) parameters['UpdateIdentifier'] = '' parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE' try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % (self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) self.set_tls_parameters(parameters, env) try: plan_utils.update_in_env(swift, env, 'parameter_defaults', parameters) except swiftexceptions.ClientException as err: err_msg = ("Error updating environment for plan %s: %s" % (self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) # process all plan files and create or update a stack processed_data = super(DeployStackAction, self).run(context) # If we receive a 'Result' instance it is because the parent action # had an error. if isinstance(processed_data, actions.Result): return processed_data stack_args = processed_data.copy() stack_args['timeout_mins'] = self.timeout_mins if stack_is_new: try: swift.copy_object( "%s-swift-rings" % self.container, "swift-rings.tar.gz", "%s-swift-rings/%s-%d" % (self.container, "swift-rings.tar.gz", time.time())) swift.delete_object("%s-swift-rings" % self.container, "swift-rings.tar.gz") except swiftexceptions.ClientException: pass LOG.info("Perfoming Heat stack create") return heat.stacks.create(**stack_args) LOG.info("Performing Heat stack update") stack_args['existing'] = 'true' return heat.stacks.update(stack.id, **stack_args)
def run(self, context): swift = self.get_object_client(context) heat = self.get_orchestration_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) saved_env = copy.deepcopy(env) try: plan_utils.update_in_env(swift, env, self.key, self.parameters) except swiftexceptions.ClientException as err: err_msg = ("Error updating environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) processed_data = super(UpdateParametersAction, self).run(context) # If we receive a 'Result' instance it is because the parent action # had an error. if isinstance(processed_data, actions.Result): return processed_data processed_data['show_nested'] = True env = plan_utils.get_env(swift, self.container) params = env.get('parameter_defaults') fields = { 'template': processed_data['template'], 'files': processed_data['files'], 'environment': processed_data['environment'], 'show_nested': True } try: result = { 'heat_resource_tree': heat.stacks.validate(**fields), 'environment_parameters': params, } # Validation passes so the old cache gets replaced. self.cache_set(context, self.container, "tripleo.parameters.get", result) if result['heat_resource_tree']: flattened = {'resources': {}, 'parameters': {}} _flat_it(flattened, 'Root', result['heat_resource_tree']) result['heat_resource_tree'] = flattened except heat_exc.HTTPException as err: LOG.debug("Validation failed rebuilding saved env") # There has been an error validating we must reprocess the # templates with the saved working env plan_utils.put_env(swift, saved_env) env = saved_env processed_data = super(UpdateParametersAction, self).run(context) err_msg = ("Error validating environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) LOG.debug("Validation worked new env is saved") return result
def run(self, context): error_text = None self.context = context swift = self.get_object_client(context) try: plan_env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % ( self.container, err)) LOG.exception(err_msg) return actions.Result(error=error_text) try: # if the jinja overcloud template exists, process it and write it # back to the swift container before continuing processing. The # method called below should handle the case where the files are # not found in swift, but if they are found and an exception # occurs during processing, that exception will cause the # ProcessTemplatesAction to return an error result. self._process_custom_roles(context) except Exception as err: LOG.exception("Error occurred while processing custom roles.") return actions.Result(error=six.text_type(err)) template_name = plan_env.get('template', "") environments = plan_env.get('environments', []) env_paths = [] temp_files = [] template_object = os.path.join(swift.url, self.container, template_name) LOG.debug('Template: %s' % template_name) LOG.debug('Environments: %s' % environments) try: for env in environments: if env.get('path'): env_paths.append(os.path.join(swift.url, self.container, env['path'])) elif env.get('data'): env_temp_file = _create_temp_file(env['data']) temp_files.append(env_temp_file) env_paths.append(env_temp_file) # create a dict to hold all user set params and merge # them in the appropriate order merged_params = {} # merge generated passwords into params first passwords = plan_env.get('passwords', {}) merged_params.update(passwords) # derived parameters are merged before 'parameter defaults' # so that user-specified values can override the derived values. derived_params = plan_env.get('derived_parameters', {}) merged_params.update(derived_params) # handle user set parameter values next in case a user has set # a new value for a password parameter params = plan_env.get('parameter_defaults', {}) merged_params = template_utils.deep_update(merged_params, params) if merged_params: env_temp_file = _create_temp_file( {'parameter_defaults': merged_params}) temp_files.append(env_temp_file) env_paths.append(env_temp_file) registry = plan_env.get('resource_registry', {}) if registry: env_temp_file = _create_temp_file( {'resource_registry': registry}) temp_files.append(env_temp_file) env_paths.append(env_temp_file) def _env_path_is_object(env_path): retval = env_path.startswith(swift.url) LOG.debug('_env_path_is_object %s: %s' % (env_path, retval)) return retval def _object_request(method, url, token=context.auth_token): response = requests.request( method, url, headers={'X-Auth-Token': token}) response.raise_for_status() return response.content template_files, template = template_utils.get_template_contents( template_object=template_object, object_request=_object_request) env_files, env = ( template_utils.process_multiple_environments_and_files( env_paths=env_paths, env_path_is_object=_env_path_is_object, object_request=_object_request)) except Exception as err: error_text = six.text_type(err) LOG.exception("Error occurred while processing plan files.") finally: # cleanup any local temp files for f in temp_files: os.remove(f) if error_text: return actions.Result(error=error_text) files = dict(list(template_files.items()) + list(env_files.items())) return { 'stack_name': self.container, 'template': template, 'environment': env, 'files': files }
def run(self, context): # get the stack. Error if doesn't exist heat = self.get_orchestration_client(context) try: stack = heat.stacks.get(self.container) except heat_exc.HTTPNotFound: msg = "Error retrieving stack: %s" % self.container LOG.exception(msg) return actions.Result(error=msg) swift = self.get_object_client(context) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % (self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) update_env = {} if self.container_registry is not None: update_env.update(self.container_registry) noop_env = { 'resource_registry': { 'OS::TripleO::DeploymentSteps': 'OS::Heat::None', }, } for output in stack.to_dict().get('outputs', {}): if output['output_key'] == 'RoleData': for role in output['output_value']: role_env = { "OS::TripleO::Tasks::%sPreConfig" % role: 'OS::Heat::None', "OS::TripleO::Tasks::%sPostConfig" % role: 'OS::Heat::None', } noop_env['resource_registry'].update(role_env) update_env.update(noop_env) template_utils.deep_update(env, update_env) parameters = {} if self.container_registry is not None: parameters.update(self.container_registry['parameter_defaults']) if self.ceph_ansible_playbook: parameters.update( {'CephAnsiblePlaybook': '%s' % self.ceph_ansible_playbook}) plan_utils.update_in_env(swift, env, 'parameter_defaults', parameters) # process all plan files and create or update a stack processed_data = super(UpdateStackAction, self).run(context) # If we receive a 'Result' instance it is because the parent action # had an error. if isinstance(processed_data, actions.Result): return processed_data stack_args = processed_data.copy() LOG.info("Performing Heat stack update") LOG.info('updating stack: %s', stack.stack_name) return heat.stacks.update(stack.id, **stack_args)
def run(self, context): try: swift = self.get_object_client(context) map_file = swift.get_object(self.container, 'capabilities-map.yaml') capabilities = yaml.safe_load(map_file[1]) except Exception: err_msg = ("Error parsing capabilities-map.yaml.") LOG.exception(err_msg) return actions.Result(error=err_msg) try: container_files = swift.get_container(self.container) container_file_list = [ entry['name'] for entry in container_files[1] ] except Exception as swift_err: err_msg = ("Error retrieving plan files: %s" % swift_err) LOG.exception(err_msg) return actions.Result(error=err_msg) try: env = plan_utils.get_env(swift, self.container) except swiftexceptions.ClientException as err: err_msg = ("Error retrieving environment for plan %s: %s" % (self.container, err)) LOG.exception(err_msg) return actions.Result(error=err_msg) selected_envs = [ item['path'] for item in env['environments'] if 'path' in item ] # extract environment files plan_environments = [] for env_group in capabilities['topics']: for envs in env_group['environment_groups']: for files in envs['environments']: file = files.get('file') if file: plan_environments.append(file) # parse plan for environment files env_files = fnmatch.filter(container_file_list, '*environments/*.yaml') env_user_files = fnmatch.filter(container_file_list, '*user-environment.yaml') outstanding_envs = list( set(env_files).union(env_user_files) - set(plan_environments)) # change capabilities format data_to_return = {} for topic in capabilities['topics']: title = topic.get('title', '_title_holder') data_to_return[title] = topic for eg in topic['environment_groups']: for env in eg['environments']: if selected_envs and env.get('file') in selected_envs: env['enabled'] = True else: env['enabled'] = False # add custom environment files other_environments = [] for env in outstanding_envs: flag = selected_envs and env in selected_envs new_env = { "description": "Enable %s environment" % env, "enabled": flag, "file": env, "title": env, } other_environments.append(new_env) other_environments.sort(key=lambda x: x['file']) other_environment_groups = [] for group in other_environments: new_group = { "description": None, "environments": [group], "title": group['file'], } other_environment_groups.append(new_group) other_environments_topic_dict = { "description": None, "title": "Other", "environment_groups": other_environment_groups } other_environments_topic = {"Other": other_environments_topic_dict} data_to_return.update(other_environments_topic) return data_to_return