Пример #1
0
    def create_stack(self, cnxt, stack_name, template, params, files, args):
        """
        The create_stack method creates a new stack using the template
        provided.
        Note that at this stage the template has already been fetched from the
        heat-api process if using a template-url.

        :param cnxt: RPC context.
        :param stack_name: Name of the stack you want to create.
        :param template: Template of stack you want to create.
        :param params: Stack Input Params
        :param files: Files referenced from the template
        :param args: Request parameters/args passed from API
        """
        logger.info(_('template is %s') % template)

        def _stack_create(stack):
            # Create the stack, and create the periodic task if successful
            stack.create()
            if stack.action == stack.CREATE and stack.status == stack.COMPLETE:
                # Schedule a periodic watcher task for this stack
                self._start_watch_task(stack.id, cnxt)
            else:
                logger.warning(
                    _("Stack create failed, status %s") % stack.status)

        if db_api.stack_get_by_name(cnxt, stack_name):
            raise exception.StackExists(stack_name=stack_name)
        tenant_limit = cfg.CONF.max_stacks_per_tenant
        if db_api.stack_count_all_by_tenant(cnxt) >= tenant_limit:
            message = _("You have reached the maximum stacks per tenant, %d."
                        " Please delete some stacks.") % tenant_limit
            raise exception.RequestLimitExceeded(message=message)

        tmpl = parser.Template(template, files=files)

        if len(tmpl[tpl.RESOURCES]) > cfg.CONF.max_resources_per_stack:
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)

        # Extract the common query parameters
        common_params = api.extract_args(args)
        env = environment.Environment(params)
        stack = parser.Stack(cnxt, stack_name, tmpl, env, **common_params)

        self._validate_deferred_auth_context(cnxt, stack)

        stack.validate()

        stack_id = stack.store()

        self._start_in_thread(stack_id, _stack_create, stack)

        return dict(stack.identifier())
Пример #2
0
    def _validate_new_stack(self, cnxt, stack_name, parsed_template):
        if db_api.stack_get_by_name(cnxt, stack_name):
            raise exception.StackExists(stack_name=stack_name)

        tenant_limit = cfg.CONF.max_stacks_per_tenant
        if db_api.stack_count_all(cnxt) >= tenant_limit:
            message = _("You have reached the maximum stacks per tenant, %d."
                        " Please delete some stacks.") % tenant_limit
            raise exception.RequestLimitExceeded(message=message)

        num_resources = len(parsed_template[parsed_template.RESOURCES])
        if num_resources > cfg.CONF.max_resources_per_stack:
            message = exception.StackResourceLimitExceeded.msg_fmt
            raise exception.RequestLimitExceeded(message=message)
Пример #3
0
def parse(tmpl_str):
    '''
    Takes a string and returns a dict containing the parsed structure.
    This includes determination of whether the string is using the
    JSON or YAML format.
    '''
    if len(tmpl_str) > cfg.CONF.max_template_size:
        msg = _('Template exceeds maximum allowed size.')
        raise exception.RequestLimitExceeded(message=msg)
    try:
        tpl = json.loads(tmpl_str)
    except ValueError:
        try:
            tpl = yaml.load(tmpl_str, Loader=yaml_loader)
        except yaml.YAMLError as yea:
            raise ValueError(yea)
        else:
            if tpl is None:
                tpl = {}
    if not isinstance(tpl, dict):
        raise ValueError(
            _('The template is not a JSON object '
              'or YAML mapping.'))
    # Looking for supported version keys in the loaded template
    if not ('HeatTemplateFormatVersion' in tpl or 'heat_template_version'
            in tpl or 'AWSTemplateFormatVersion' in tpl):
        raise ValueError(_("Template format version not found."))
    return tpl
Пример #4
0
    def create_with_template(self,
                             child_template,
                             user_params,
                             timeout_mins=None,
                             adopt_data=None):
        '''
        Handle the creation of the nested stack from a given JSON template.
        '''
        if self.recursion_depth >= cfg.CONF.max_nested_stack_depth:
            msg = _("Recursion depth exceeds %d.") % \
                cfg.CONF.max_nested_stack_depth
            raise exception.RequestLimitExceeded(message=msg)
        template = parser.Template(child_template)
        if ((len(template[template.RESOURCES]) +
             self.stack.root_stack.total_resources() >
             cfg.CONF.max_resources_per_stack)):
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)
        self._outputs_to_attribs(child_template)

        # Note we disable rollback for nested stacks, since they
        # should be rolled back by the parent stack on failure
        nested = parser.Stack(self.context,
                              self.physical_resource_name(),
                              template,
                              environment.Environment(user_params),
                              timeout_mins=timeout_mins,
                              disable_rollback=True,
                              parent_resource=self,
                              owner_id=self.stack.id,
                              adopt_stack_data=adopt_data)
        nested.validate()
        self._nested = nested
        nested_id = self._nested.store()
        self.resource_id_set(nested_id)

        action = self._nested.CREATE
        if adopt_data:
            action = self._nested.ADOPT

        stack_creator = scheduler.TaskRunner(self._nested.stack_task,
                                             action=action)
        stack_creator.start(timeout=self._nested.timeout_secs())
        return stack_creator
Пример #5
0
    def update_stack(self, cnxt, stack_identity, template, params, files,
                     args):
        """
        The update_stack method updates an existing stack based on the
        provided template and parameters.
        Note that at this stage the template has already been fetched from the
        heat-api process if using a template-url.

        :param cnxt: RPC context.
        :param stack_identity: Name of the stack you want to create.
        :param template: Template of stack you want to create.
        :param params: Stack Input Params
        :param files: Files referenced from the template
        :param args: Request parameters/args passed from API
        """
        LOG.info(_('template is %s') % template)

        # Get the database representation of the existing stack
        db_stack = self._get_stack(cnxt, stack_identity)

        current_stack = parser.Stack.load(cnxt, stack=db_stack)

        if current_stack.action == current_stack.SUSPEND:
            msg = _('Updating a stack when it is suspended')
            raise exception.NotSupported(feature=msg)

        if current_stack.status == current_stack.IN_PROGRESS:
            msg = _('Updating a stack when another action is in progress')
            raise exception.NotSupported(feature=msg)

        # Now parse the template and any parameters for the updated
        # stack definition.
        tmpl = parser.Template(template, files=files)
        if len(tmpl[tmpl.RESOURCES]) > cfg.CONF.max_resources_per_stack:
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)
        stack_name = current_stack.name
        common_params = api.extract_args(args)
        common_params.setdefault(rpc_api.PARAM_TIMEOUT,
                                 current_stack.timeout_mins)
        env = environment.Environment(params)
        updated_stack = parser.Stack(cnxt, stack_name, tmpl, env,
                                     **common_params)
        updated_stack.parameters.set_stack_id(current_stack.identifier())

        self._validate_deferred_auth_context(cnxt, updated_stack)
        updated_stack.validate()

        self.thread_group_mgr.start_with_lock(cnxt, current_stack,
                                              self.engine_id,
                                              current_stack.update,
                                              updated_stack)

        return dict(current_stack.identifier())
Пример #6
0
    def _validate_nested_resources(self, templ):
        total_resources = (len(templ[templ.RESOURCES]) +
                           self.stack.root_stack.total_resources())

        if self.nested():
            # It's an update and these resources will be deleted
            total_resources -= len(self.nested().resources)

        if (total_resources > cfg.CONF.max_resources_per_stack):
            message = exception.StackResourceLimitExceeded.msg_fmt
            raise exception.RequestLimitExceeded(message=message)
Пример #7
0
 def from_json(self, datastring):
     try:
         if len(datastring) > cfg.CONF.max_json_body_size:
             msg = _('JSON body size (%(len)s bytes) exceeds maximum '
                     'allowed size (%(limit)s bytes).'
                     ) % {'len': len(datastring),
                          'limit': cfg.CONF.max_json_body_size}
             raise exception.RequestLimitExceeded(message=msg)
         return jsonutils.loads(datastring)
     except ValueError as ex:
         raise webob.exc.HTTPBadRequest(str(ex))
Пример #8
0
    def test_preview_validates_nested_resources(self):
        stack_resource = MyImplementedStackResource('test', ws_res_snippet,
                                                    self.parent_stack)
        stack_resource.child_template = mock.Mock(return_value={})
        stack_resource.child_params = mock.Mock()
        exc = exception.RequestLimitExceeded(message='Validation Failed')
        validation_mock = mock.Mock(side_effect=exc)
        stack_resource._validate_nested_resources = validation_mock

        self.assertRaises(exception.RequestLimitExceeded,
                          stack_resource.preview)
Пример #9
0
def validate_template_limit(contain_str):
    """Validate limit for the template.

    Check if the contain exceeds allowed size range.
    """

    if len(contain_str) > cfg.CONF.max_template_size:
        msg = _("Template size (%(actual_len)s bytes) exceeds maximum "
                "allowed size (%(limit)s bytes)."
                ) % {'actual_len': len(contain_str),
                     'limit': cfg.CONF.max_template_size}
        raise exception.RequestLimitExceeded(message=msg)
Пример #10
0
    def test_preview_dict_validates_nested_resources(self):
        parent_t = self.parent_stack.t
        resource_defns = parent_t.resource_definitions(self.parent_stack)
        stk_resource = MyImplementedStackResource(
            'test', resource_defns[self.ws_resname], self.parent_stack)
        stk_resource.child_params = mock.Mock(return_value={})
        stk_resource.child_template = mock.Mock(
            return_value=self.simple_template)
        exc = exception.RequestLimitExceeded(message='Validation Failed')
        validation_mock = mock.Mock(side_effect=exc)
        stk_resource._validate_nested_resources = validation_mock

        self.assertRaises(exception.RequestLimitExceeded, stk_resource.preview)
Пример #11
0
def parse(tmpl_str):
    """Takes a string and returns a dict containing the parsed structure.

    This includes determination of whether the string is using the
    JSON or YAML format.
    """
    if len(tmpl_str) > cfg.CONF.max_template_size:
        msg = (_('Template exceeds maximum allowed size (%s bytes)') %
               cfg.CONF.max_template_size)
        raise exception.RequestLimitExceeded(message=msg)
    tpl = simple_parse(tmpl_str)
    # Looking for supported version keys in the loaded template
    if not ('HeatTemplateFormatVersion' in tpl or 'heat_template_version'
            in tpl or 'AWSTemplateFormatVersion' in tpl):
        raise ValueError(_("Template format version not found."))
    return tpl
Пример #12
0
    def update_stack(self, cnxt, stack_identity, template, params, files,
                     args):
        """
        The update_stack method updates an existing stack based on the
        provided template and parameters.
        Note that at this stage the template has already been fetched from the
        heat-api process if using a template-url.
        arg1 -> RPC context.
        arg2 -> Name of the stack you want to create.
        arg3 -> Template of stack you want to create.
        arg4 -> Stack Input Params
        arg4 -> Request parameters/args passed from API
        """
        logger.info('template is %s' % template)

        # Get the database representation of the existing stack
        db_stack = self._get_stack(cnxt, stack_identity)

        current_stack = parser.Stack.load(cnxt, stack=db_stack)

        if current_stack.action == current_stack.SUSPEND:
            msg = _('Updating a stack when it is suspended')
            raise exception.NotSupported(feature=msg)

        if current_stack.status == current_stack.IN_PROGRESS:
            msg = _('Updating a stack when another action is in progress')
            raise exception.NotSupported(feature=msg)

        # Now parse the template and any parameters for the updated
        # stack definition.
        tmpl = parser.Template(template, files=files)
        if len(tmpl[tpl.RESOURCES]) > cfg.CONF.max_resources_per_stack:
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)
        stack_name = current_stack.name
        common_params = api.extract_args(args)
        env = environment.Environment(params)
        updated_stack = parser.Stack(cnxt, stack_name, tmpl, env,
                                     **common_params)

        self._validate_deferred_auth_context(cnxt, updated_stack)
        updated_stack.validate()

        self._start_in_thread(db_stack.id, current_stack.update, updated_stack)

        return dict(current_stack.identifier())
Пример #13
0
    def _parse_nested_stack(self,
                            stack_name,
                            child_template,
                            child_params=None,
                            timeout_mins=None,
                            adopt_data=None):
        if self.stack.nested_depth >= cfg.CONF.max_nested_stack_depth:
            msg = _("Recursion depth exceeds %d."
                    ) % cfg.CONF.max_nested_stack_depth
            raise exception.RequestLimitExceeded(message=msg)

        parsed_template = self._parse_child_template(child_template)
        self._validate_nested_resources(parsed_template)

        # Don't overwrite the attributes_schema for subclasses that
        # define their own attributes_schema.
        if not hasattr(type(self), 'attributes_schema'):
            self.attributes = None
            self._outputs_to_attribs(parsed_template)

        if timeout_mins is None:
            timeout_mins = self.stack.timeout_mins

        stack_user_project_id = self.stack.stack_user_project_id
        new_nested_depth = self.stack.nested_depth + 1

        if child_params is None:
            child_params = self.child_params()
        child_env = environment.get_child_environment(self.stack.env,
                                                      child_params)

        # Note we disable rollback for nested stacks, since they
        # should be rolled back by the parent stack on failure
        nested = parser.Stack(self.context,
                              stack_name,
                              parsed_template,
                              env=child_env,
                              timeout_mins=timeout_mins,
                              disable_rollback=True,
                              parent_resource=self,
                              owner_id=self.stack.id,
                              user_creds_id=self.stack.user_creds_id,
                              stack_user_project_id=stack_user_project_id,
                              adopt_stack_data=adopt_data,
                              nested_depth=new_nested_depth)
        return nested
Пример #14
0
    def _validate_nested_resources(self, templ):
        if cfg.CONF.max_resources_per_stack == -1:
            return

        total_resources = (len(templ[templ.RESOURCES]) +
                           self.stack.total_resources(self.root_stack_id))

        identity = self.nested_identifier()
        if identity is not None:
            existing = self.rpc_client().list_stack_resources(self.context,
                                                              identity)
            # Don't double-count existing resources during an update
            total_resources -= len(existing)

        if (total_resources > cfg.CONF.max_resources_per_stack):
            message = exception.StackResourceLimitExceeded.msg_fmt
            raise exception.RequestLimitExceeded(message=message)
Пример #15
0
    def update_with_template(self,
                             child_template,
                             user_params,
                             timeout_mins=None):
        """Update the nested stack with the new template."""
        template = parser.Template(child_template, files=self.stack.t.files)
        # Note that there is no call to self._outputs_to_attribs here.
        # If we have a use case for updating attributes of the resource based
        # on updated templates we should make sure it's optional because not
        # all subclasses want that behavior, since they may offer custom
        # attributes.
        nested_stack = self.nested()
        if nested_stack is None:
            raise exception.Error(
                _('Cannot update %s, stack not created') % self.name)
        res_diff = (len(template[template.RESOURCES]) -
                    len(nested_stack.resources))
        new_size = nested_stack.root_stack.total_resources() + res_diff
        if new_size > cfg.CONF.max_resources_per_stack:
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)

        if timeout_mins is None:
            timeout_mins = self.stack.timeout_mins

        # Note we disable rollback for nested stacks, since they
        # should be rolled back by the parent stack on failure
        stack = parser.Stack(self.context,
                             self.physical_resource_name(),
                             template,
                             self._nested_environment(user_params),
                             timeout_mins=timeout_mins,
                             disable_rollback=True,
                             parent_resource=self,
                             owner_id=self.stack.id)
        stack.parameters.set_stack_id(nested_stack.identifier())
        stack.validate()

        if not hasattr(type(self), 'attributes_schema'):
            self.attributes = None
            self._outputs_to_attribs(child_template)

        updater = scheduler.TaskRunner(nested_stack.update_task, stack)
        updater.start()
        return updater
Пример #16
0
    def update_with_template(self, child_template, user_params,
                             timeout_mins=None):
        """Update the nested stack with the new template."""
        if isinstance(child_template, parser.Template):
            template = child_template
            template.files = self.stack.t.files
        else:
            template = parser.Template(child_template,
                                       files=self.stack.t.files)
        nested_stack = self.nested()
        if nested_stack is None:
            raise exception.Error(_('Cannot update %s, stack not created')
                                  % self.name)
        res_diff = (
            len(template[template.RESOURCES]) - len(nested_stack.resources))
        new_size = nested_stack.root_stack.total_resources() + res_diff
        if new_size > cfg.CONF.max_resources_per_stack:
            raise exception.RequestLimitExceeded(
                message=exception.StackResourceLimitExceeded.msg_fmt)

        if timeout_mins is None:
            timeout_mins = self.stack.timeout_mins

        # Note we disable rollback for nested stacks, since they
        # should be rolled back by the parent stack on failure
        stack = parser.Stack(self.context,
                             self.physical_resource_name(),
                             template,
                             self._nested_environment(user_params),
                             timeout_mins=timeout_mins,
                             disable_rollback=True,
                             parent_resource=self,
                             owner_id=self.stack.id)
        stack.parameters.set_stack_id(nested_stack.identifier())
        stack.validate()

        # Don't overwrite the attributes_schema on update for subclasses that
        # define their own attributes_schema.
        if not hasattr(type(self), 'attributes_schema'):
            self.attributes = None
            self._outputs_to_attribs(template)

        updater = scheduler.TaskRunner(nested_stack.update_task, stack)
        updater.start()
        return updater
Пример #17
0
def parse(tmpl_str):
    '''
    Takes a string and returns a dict containing the parsed structure.
    This includes determination of whether the string is using the
    JSON or YAML format.
    '''
    if len(tmpl_str) > cfg.CONF.max_template_size:
        msg = _('Template exceeds maximum allowed size.')
        raise exception.RequestLimitExceeded(message=msg)
    if tmpl_str.startswith('{'):
        tpl = json.loads(tmpl_str)
    else:
        try:
            tpl = yaml.load(tmpl_str, Loader=yaml_loader)
        except yaml.YAMLError as yea:
            raise ValueError(yea)
        else:
            if tpl is None:
                tpl = {}
            if u'heat_template_version' not in tpl:
                default_for_missing(tpl, u'HeatTemplateFormatVersion',
                                    HEAT_VERSIONS)
    return tpl
Пример #18
0
 def _child_nested_depth(self):
     if self.stack.nested_depth >= cfg.CONF.max_nested_stack_depth:
         msg = _("Recursion depth exceeds %d."
                 ) % cfg.CONF.max_nested_stack_depth
         raise exception.RequestLimitExceeded(message=msg)
     return self.stack.nested_depth + 1
Пример #19
0
 def _validate_nested_resources(self, template):
     total_resources = (len(template[template.RESOURCES]) +
                        self.stack.root_stack.total_resources())
     if (total_resources > cfg.CONF.max_resources_per_stack):
         message = exception.StackResourceLimitExceeded.msg_fmt
         raise exception.RequestLimitExceeded(message=message)
Пример #20
0
 def test_map_remote_error_request_limit_exceeded(self):
     ex = common_exception.RequestLimitExceeded(message="testing")
     expected = aws_exception.HeatRequestLimitExceeded
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)