Example #1
0
    def test_wordpress_single_instance_stack_delete(self):
        ctx = create_context(self.m, tenant='test_delete_tenant')
        stack = get_wordpress_stack('test_stack', ctx)
        fc = setup_mocks(self.m, stack)
        self.m.ReplayAll()
        stack_id = stack.store()
        stack.create()

        db_s = db_api.stack_get(ctx, stack_id)
        self.assertNotEqual(db_s, None)

        self.assertNotEqual(stack.resources['WebServer'], None)
        self.assertTrue(stack.resources['WebServer'].resource_id > 0)

        self.m.StubOutWithMock(fc.client, 'get_servers_9999')
        get = fc.client.get_servers_9999
        get().AndRaise(service.clients.novaclient.exceptions.NotFound(404))
        mox.Replay(get)
        stack.delete()

        rsrc = stack.resources['WebServer']
        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
        self.assertEqual((stack.DELETE, stack.COMPLETE), rsrc.state)
        self.assertEqual(db_api.stack_get(ctx, stack_id), None)
        self.assertEqual(db_s.action, 'DELETE')
        self.assertEqual(db_s.status, 'COMPLETE')
Example #2
0
    def test_delete_badaction(self):
        self.stack = parser.Stack(self.ctx, 'delete_badaction_test',
                                  parser.Template({}))
        stack_id = self.stack.store()

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertNotEqual(db_s, None)

        self.stack.delete(action="wibble")

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertNotEqual(db_s, None)
        self.assertEqual(self.stack.state, self.stack.DELETE_FAILED)
Example #3
0
    def test_delete_rollback(self):
        self.stack = parser.Stack(self.ctx, 'delete_rollback_test',
                                  parser.Template({}), disable_rollback=False)
        stack_id = self.stack.store()

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertNotEqual(db_s, None)

        self.stack.delete(action=self.stack.ROLLBACK)

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertEqual(db_s, None)
        self.assertEqual(self.stack.state, self.stack.ROLLBACK_COMPLETE)
Example #4
0
    def test_delete(self):
        self.stack = parser.Stack(self.ctx, 'delete_test',
                                  parser.Template({}))
        stack_id = self.stack.store()

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertNotEqual(db_s, None)

        self.stack.delete()

        db_s = db_api.stack_get(self.ctx, stack_id)
        self.assertEqual(db_s, None)
        self.assertEqual(self.stack.state, self.stack.DELETE_COMPLETE)
Example #5
0
    def _periodic_watcher_task(self, sid):
        """
        Periodic task, created for each stack, triggers watch-rule
        evaluation for all rules defined for the stack
        sid = stack ID
        """
        # Retrieve the stored credentials & create context
        # Require admin=True to the stack_get to defeat tenant
        # scoping otherwise we fail to retrieve the stack
        logger.debug("Periodic watcher task for stack %s" % sid)
        admin_context = context.get_admin_context()
        stack = db_api.stack_get(admin_context, sid, admin=True)
        if not stack:
            logger.error("Unable to retrieve stack %s for periodic task" %
                         sid)
            return
        user_creds = db_api.user_creds_get(stack.user_creds_id)
        stack_context = context.RequestContext.from_dict(user_creds)

        # Get all watchrules for this stack and evaluate them
        try:
            wrs = db_api.watch_rule_get_all_by_stack(stack_context, sid)
        except Exception as ex:
            logger.warn('periodic_task db error (%s) %s' %
                        ('watch rule removed?', str(ex)))
            return
        for wr in wrs:
            rule = watchrule.WatchRule.load(stack_context, watch=wr)
            actions = rule.evaluate()
            for action in actions:
                self._start_in_thread(sid, action)
Example #6
0
    def load(cls, context, stack_id=None, stack=None, resolve_data=True,
             parent_resource=None, show_deleted=True):
        '''Retrieve a Stack from the database.'''
        if stack is None:
            stack = db_api.stack_get(context, stack_id,
                                     show_deleted=show_deleted,
                                     eager_load=True)
        if stack is None:
            message = _('No stack exists with id "%s"') % str(stack_id)
            raise exception.NotFound(message)

        template = Template.load(
            context, stack.raw_template_id, stack.raw_template)
        env = environment.Environment(stack.parameters)
        stack = cls(context, stack.name, template, env,
                    stack.id, stack.action, stack.status, stack.status_reason,
                    stack.timeout, resolve_data, stack.disable_rollback,
                    parent_resource, owner_id=stack.owner_id,
                    stack_user_project_id=stack.stack_user_project_id,
                    created_time=stack.created_at,
                    updated_time=stack.updated_at,
                    user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
                    validate_parameters=False)

        return stack
Example #7
0
    def load(cls, context, stack_id=None, stack=None, resolve_data=True, parent_resource=None, show_deleted=True):
        """Retrieve a Stack from the database."""
        if stack is None:
            stack = db_api.stack_get(context, stack_id, show_deleted=show_deleted)
        if stack is None:
            message = _('No stack exists with id "%s"') % str(stack_id)
            raise exception.NotFound(message)

        template = Template.load(context, stack.raw_template_id)
        env = environment.Environment(stack.parameters)
        stack = cls(
            context,
            stack.name,
            template,
            env,
            stack.id,
            stack.action,
            stack.status,
            stack.status_reason,
            stack.timeout,
            resolve_data,
            stack.disable_rollback,
            parent_resource,
            owner_id=stack.owner_id,
        )

        return stack
Example #8
0
    def test_stack_describe(self):
        self.m.StubOutWithMock(service.EngineService, '_get_stack')
        s = db_api.stack_get(self.ctx, self.stack.id)
        service.EngineService._get_stack(self.ctx,
                                         self.stack.identifier()).AndReturn(s)
        self.m.ReplayAll()

        sl = self.eng.show_stack(self.ctx, self.stack.identifier())

        self.assertEqual(len(sl), 1)

        s = sl[0]
        self.assertTrue('creation_time' in s)
        self.assertTrue('updated_time' in s)
        self.assertTrue('stack_identity' in s)
        self.assertNotEqual(s['stack_identity'], None)
        self.assertTrue('stack_name' in s)
        self.assertEqual(s['stack_name'], self.stack.name)
        self.assertTrue('stack_status' in s)
        self.assertTrue('stack_status_reason' in s)
        self.assertTrue('description' in s)
        self.assertNotEqual(s['description'].find('WordPress'), -1)
        self.assertTrue('parameters' in s)

        self.m.VerifyAll()
Example #9
0
    def show_stack(self, context, stack_name, params):
        """
        The show_stack method returns the attributes of one stack.
        arg1 -> RPC context.
        arg2 -> Name of the stack you want to see.
        arg3 -> Dict of http request parameters passed in from API side.
        """
        res = {"stacks": []}
        s = db_api.stack_get(None, stack_name)
        if s:
            ps = parser.Stack(s.name, s.raw_template.parsed_template.template, s.id, params)
            mem = {}
            mem["stack_id"] = s.id
            mem["stack_name"] = s.name
            mem["creation_at"] = str(s.created_at)
            mem["updated_at"] = str(s.updated_at)
            mem["NotificationARNs"] = "TODO"
            mem["Parameters"] = ps.t["Parameters"]
            mem["StackStatusReason"] = "TODO"
            mem["TimeoutInMinutes"] = "TODO"
            mem["TemplateDescription"] = ps.t.get("Description", "No description")
            mem["StackStatus"] = ps.t.get("stack_status", "unknown")

            # only show the outputs on a completely created stack
            if ps.t["stack_status"] == ps.CREATE_COMPLETE:
                mem["Outputs"] = ps.get_outputs()

            res["stacks"].append(mem)

        return res
Example #10
0
    def test_nested_stack_update(self):
        urlfetch.get("https://localhost/the.template").MultipleTimes().AndReturn(self.nested_template)
        urlfetch.get("https://localhost/new.template").MultipleTimes().AndReturn(self.update_template)

        self.m.ReplayAll()

        stack = self.create_stack(self.test_template)
        rsrc = stack["the_nested"]

        original_nested_id = rsrc.resource_id
        t = template_format.parse(self.test_template)
        new_res = copy.deepcopy(t["Resources"]["the_nested"])
        new_res["Properties"]["TemplateURL"] = "https://localhost/new.template"
        prop_diff = {"TemplateURL": "https://localhost/new.template"}
        rsrc.handle_update(new_res, {}, prop_diff)

        # Expect the physical resource name staying the same after update,
        # so that the nested was actually updated instead of replaced.
        self.assertEqual(original_nested_id, rsrc.resource_id)
        db_nested = db_api.stack_get(stack.context, rsrc.resource_id)
        # Owner_id should be preserved during the update process.
        self.assertEqual(stack.id, db_nested.owner_id)

        self.assertEqual("foo", rsrc.FnGetAtt("Outputs.Bar"))
        self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Foo")
        self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Outputs.Foo")
        self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Bar")

        rsrc.delete()

        self.m.VerifyAll()
Example #11
0
    def state_set(self, action, status, reason):
        '''Update the stack state in the database.'''
        if action not in self.ACTIONS:
            raise ValueError(_("Invalid action %s") % action)

        if status not in self.STATUSES:
            raise ValueError(_("Invalid status %s") % status)

        self.action = action
        self.status = status
        self.status_reason = reason

        if self.id is None:
            return

        stack = db_api.stack_get(self.context, self.id)
        if stack is not None:
            stack.update_and_save({'action': action,
                                   'status': status,
                                   'status_reason': reason})
            msg = _('Stack %(action)s %(status)s (%(name)s): %(reason)s')
            LOG.info(msg % {'action': action,
                            'status': status,
                            'name': self.name,
                            'reason': reason})
            notification.send(self)
Example #12
0
    def delete(self, action=DELETE, backup=False):
        '''
        Delete all of the resources, and then the stack itself.
        The action parameter is used to differentiate between a user
        initiated delete and an automatic stack rollback after a failed
        create, which amount to the same thing, but the states are recorded
        differently.
        '''
        if action not in (self.DELETE, self.ROLLBACK):
            logger.error(_("Unexpected action %s passed to delete!") % action)
            self.state_set(self.DELETE, self.FAILED,
                           "Invalid action %s" % action)
            return

        stack_status = self.COMPLETE
        reason = 'Stack %s completed successfully' % action.lower()
        self.state_set(action, self.IN_PROGRESS, 'Stack %s started' % action)

        backup_stack = self._backup_stack(False)
        if backup_stack is not None:
            backup_stack.delete(backup=True)
            if backup_stack.status != backup_stack.COMPLETE:
                errs = backup_stack.status_reason
                failure = 'Error deleting backup resources: %s' % errs
                self.state_set(action, self.FAILED,
                               'Failed to %s : %s' % (action, failure))
                return

        action_task = scheduler.DependencyTaskGroup(self.dependencies,
                                                    resource.Resource.destroy,
                                                    reverse=True)
        try:
            scheduler.TaskRunner(action_task)(timeout=self.timeout_secs())
        except exception.ResourceFailure as ex:
            stack_status = self.FAILED
            reason = 'Resource %s failed: %s' % (action.lower(), str(ex))
        except scheduler.Timeout:
            stack_status = self.FAILED
            reason = '%s timed out' % action.title()

        if stack_status != self.FAILED and not backup:
            # If we created a trust, delete it
            stack = db_api.stack_get(self.context, self.id)
            user_creds = db_api.user_creds_get(stack.user_creds_id)
            trust_id = user_creds.get('trust_id')
            if trust_id:
                try:
                    self.clients.keystone().delete_trust(trust_id)
                except Exception as ex:
                    logger.exception(ex)
                    stack_status = self.FAILED
                    reason = "Error deleting trust: %s" % str(ex)

        self.state_set(action, stack_status, reason)

        if stack_status != self.FAILED:
            # delete the stack
            db_api.stack_delete(self.context, self.id)
            self.id = None
Example #13
0
 def metadata_list_resources(self, context, stack_name):
     """
     Return the resource IDs of the given stack.
     """
     stack = db_api.stack_get(None, stack_name)
     if stack:
         return [r.name for r in stack.resources]
     else:
         return None
Example #14
0
File: stack.py Project: noako/heat
 def refresh(self):
     db_stack = db_api.stack_get(
         self._context, self.id, show_deleted=True)
     db_stack.refresh()
     return self.__class__._from_db_object(
         self._context,
         self,
         db_stack
     )
Example #15
0
    def state_set(self, new_status, reason):
        """Update the stack state in the database"""
        self.state = new_status
        self.state_description = reason

        if self.id is None:
            return

        stack = db_api.stack_get(self.context, self.id)
        stack.update_and_save({"status": new_status, "status_reason": reason})
Example #16
0
    def load(
        cls, context, stack_id=None, stack=None, parent_resource=None, show_deleted=True, use_stored_context=False
    ):
        """Retrieve a Stack from the database."""
        if stack is None:
            stack = db_api.stack_get(context, stack_id, show_deleted=show_deleted, eager_load=True)
        if stack is None:
            message = _('No stack exists with id "%s"') % str(stack_id)
            raise exception.NotFound(message)

        return cls._from_db(context, stack, parent_resource=parent_resource, use_stored_context=use_stored_context)
Example #17
0
 def refresh(self):
     db_stack = db_api.stack_get(
         self._context, self.id, show_deleted=True)
     if db_stack is None:
         message = _('No stack exists with id "%s"') % str(self.id)
         raise exception.NotFound(message)
     return self.__class__._from_db_object(
         self._context,
         self,
         db_stack
     )
Example #18
0
    def state_set(self, new_status, reason):
        '''Update the stack state in the database.'''
        self.state = new_status
        self.state_description = reason

        if self.id is None:
            return

        stack = db_api.stack_get(self.context, self.id)
        stack.update_and_save({'status': new_status,
                               'status_reason': reason})
Example #19
0
    def load(cls, context, stack_id):
        """Retrieve a Stack from the database"""
        s = db_api.stack_get(context, stack_id)
        if s is None:
            message = 'No stack exists with id "%s"' % str(stack_id)
            raise exception.NotFound(message)

        template = Template.load(context, s.raw_template_id)
        params = Parameters(s.name, template, s.parameters)
        stack = cls(context, s.name, template, params, stack_id, s.status, s.status_reason, s.timeout)

        return stack
Example #20
0
    def metadata_get_resource(self, context, stack_name, resource_id):
        """
        Get the metadata for the given resource.
        """
        s = db_api.stack_get(None, stack_name)
        if not s:
            return ["stack", None]

        template = s.raw_template.parsed_template.template
        if not resource_id in template.get("Resources", {}):
            return ["resource", None]

        metadata = template["Resources"][resource_id].get("Metadata", {})
        return [None, metadata]
Example #21
0
    def _get_stack(self, cnxt, stack_identity):
        identity = identifier.HeatIdentifier(**stack_identity)

        if identity.tenant != cnxt.tenant_id:
            raise exception.InvalidTenant(target=identity.tenant, actual=cnxt.tenant_id)

        s = db_api.stack_get(cnxt, identity.stack_id)

        if s is None:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        if identity.path or s.name != identity.stack_name:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        return s
Example #22
0
    def _get_stack(self, context, stack_identity):
        identity = identifier.HeatIdentifier(**stack_identity)

        if identity.tenant != context.tenant:
            raise AttributeError('Invalid tenant')

        s = db_api.stack_get(context, identity.stack_id)

        if s is None:
            raise AttributeError('Stack not found')

        if identity.path or s.name != identity.stack_name:
            raise AttributeError('Invalid stack ID')

        return s
Example #23
0
    def test_stack_delete(self):
        stack_name = 'service_delete_test_stack'
        stack = get_wordpress_stack(stack_name, self.ctx)
        sid = stack.store()

        s = db_api.stack_get(self.ctx, sid)
        self.m.StubOutWithMock(parser.Stack, 'load')

        parser.Stack.load(self.ctx, stack=s).AndReturn(stack)

        self.m.ReplayAll()

        self.assertEqual(self.man.delete_stack(self.ctx, stack.identifier()),
                         None)
        self.m.VerifyAll()
Example #24
0
    def load(cls, context, stack_id=None, stack=None, resolve_data=True):
        '''Retrieve a Stack from the database.'''
        if stack is None:
            stack = db_api.stack_get(context, stack_id)
        if stack is None:
            message = 'No stack exists with id "%s"' % str(stack_id)
            raise exception.NotFound(message)

        template = Template.load(context, stack.raw_template_id)
        params = Parameters(stack.name, template, stack.parameters)
        stack = cls(context, stack.name, template, params,
                    stack.id, stack.status, stack.status_reason, stack.timeout,
                    resolve_data, stack.disable_rollback)

        return stack
Example #25
0
    def test_stack_event_list(self):
        self.m.StubOutWithMock(service.EngineService, '_get_stack')
        s = db_api.stack_get(self.ctx, self.stack.id)
        service.EngineService._get_stack(self.ctx,
                                         self.stack.identifier()).AndReturn(s)
        self.m.ReplayAll()

        events = self.eng.list_events(self.ctx, self.stack.identifier())

        self.assertEqual(len(events), 2)
        for ev in events:
            self.assertTrue('event_identity' in ev)
            self.assertEqual(type(ev['event_identity']), dict)
            self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1])

            self.assertTrue('logical_resource_id' in ev)
            self.assertEqual(ev['logical_resource_id'], 'WebServer')

            self.assertTrue('physical_resource_id' in ev)

            self.assertTrue('resource_properties' in ev)
            # Big long user data field.. it mentions 'wordpress'
            # a few times so this should work.
            user_data = ev['resource_properties']['UserData']
            self.assertNotEqual(user_data.find('wordpress'), -1)
            self.assertEqual(ev['resource_properties']['ImageId'],
                             'F17-x86_64-gold')
            self.assertEqual(ev['resource_properties']['InstanceType'],
                             'm1.large')

            self.assertTrue('resource_action' in ev)
            self.assertTrue(ev['resource_status'] in ('IN_PROGRESS',
                                                      'CREATE_COMPLETE'))

            self.assertTrue('resource_status_reason' in ev)
            self.assertEqual(ev['resource_status_reason'], 'state changed')

            self.assertTrue('resource_type' in ev)
            self.assertEqual(ev['resource_type'], 'AWS::EC2::Instance')

            self.assertTrue('stack_identity' in ev)

            self.assertTrue('stack_name' in ev)
            self.assertEqual(ev['stack_name'], self.stack.name)

            self.assertTrue('event_time' in ev)

        self.m.VerifyAll()
Example #26
0
    def delete_stack(self, context, stack_name, params):
        """
        The delete_stack method deletes a given stack.
        arg1 -> RPC context.
        arg2 -> Name of the stack you want to delete.
        arg3 -> Params passed from API.
        """
        st = db_api.stack_get(None, stack_name)
        if not st:
            return {"Error": "No stack by that name"}

        logger.info("deleting stack %s" % stack_name)

        ps = parser.Stack(st.name, st.raw_template.parsed_template.template, st.id, params)
        ps.delete()
        return None
Example #27
0
    def list_events(self, context, stack_name):
        """
        The list_events method lists all events associated with a given stack.
        arg1 -> RPC context.
        arg2 -> Name of the stack you want to get events for.
        """
        if stack_name is not None:
            st = db_api.stack_get(None, stack_name)
            if not st:
                return {"Error": "No stack by that name"}

            events = db_api.event_get_all_by_stack(None, st.id)
        else:
            events = db_api.event_get_all(None)

        return {"events": [self.parse_event(e) for e in events]}
Example #28
0
 def identify_stack(self, cnxt, stack_name):
     """
     The identify_stack method returns the full stack identifier for a
     single, live stack given the stack name.
     arg1 -> RPC context.
     arg2 -> Name or UUID of the stack to look up.
     """
     if uuidutils.is_uuid_like(stack_name):
         s = db_api.stack_get(cnxt, stack_name, show_deleted=True)
     else:
         s = db_api.stack_get_by_name(cnxt, stack_name)
     if s:
         stack = parser.Stack.load(cnxt, stack=s)
         return dict(stack.identifier())
     else:
         raise exception.StackNotFound(stack_name=stack_name)
Example #29
0
 def identify_stack(self, context, stack_name):
     """
     The identify_stack method returns the full stack identifier for a
     single, live stack given the stack name.
     arg1 -> RPC context.
     arg2 -> Name or UUID of the stack to look up.
     """
     if uuidutils.is_uuid_like(stack_name):
         s = db_api.stack_get(context, stack_name)
     else:
         s = db_api.stack_get_by_name(context, stack_name)
     if s:
         stack = parser.Stack.load(context, stack=s)
         return dict(stack.identifier())
     else:
         raise AttributeError("Unknown stack name")
Example #30
0
    def create_stack(self, context, stack_name, template, params):
        """
        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.
        arg1 -> RPC context.
        arg2 -> Name of the stack you want to create.
        arg3 -> Template of stack you want to create.
        arg4 -> Params passed from API.
        """
        logger.info("template is %s" % template)
        if db_api.stack_get(None, stack_name):
            return {"Error": "Stack already exists with that name."}

        metadata_server = config.FLAGS.heat_metadata_server_url
        # We don't want to reset the stack template, so we are making
        # an instance just for validation.
        template_copy = deepcopy(template)
        stack_validator = parser.Stack(stack_name, template_copy, 0, params, metadata_server=metadata_server)
        response = stack_validator.validate()
        stack_validator = None
        template_copy = None
        if "Malformed Query Response" in response["ValidateTemplateResult"]["Description"]:
            return response["ValidateTemplateResult"]["Description"]

        stack = parser.Stack(stack_name, template, 0, params, metadata_server=metadata_server)
        rt = {}
        rt["template"] = template
        rt["stack_name"] = stack_name
        new_rt = db_api.raw_template_create(None, rt)

        s = {}
        s["name"] = stack_name
        s["raw_template_id"] = new_rt.id
        new_s = db_api.stack_create(None, s)
        stack.id = new_s.id

        pt = {}
        pt["template"] = stack.t
        pt["raw_template_id"] = new_rt.id
        new_pt = db_api.parsed_template_create(None, pt)

        stack.parsed_template_id = new_pt.id
        stack.create()

        return {"stack": {"id": new_s.id, "name": new_s.name, "created_at": str(new_s.created_at)}}
Example #31
0
    def load(cls, context, stack_id=None, stack=None, resolve_data=True,
             parent_resource=None, show_deleted=True):
        '''Retrieve a Stack from the database.'''
        if stack is None:
            stack = db_api.stack_get(context, stack_id,
                                     show_deleted=show_deleted)
        if stack is None:
            message = _('No stack exists with id "%s"') % str(stack_id)
            raise exception.NotFound(message)

        template = Template.load(context, stack.raw_template_id)
        env = environment.Environment(stack.parameters)
        stack = cls(context, stack.name, template, env,
                    stack.id, stack.action, stack.status, stack.status_reason,
                    stack.timeout, resolve_data, stack.disable_rollback,
                    parent_resource, owner_id=stack.owner_id)

        return stack
Example #32
0
    def _get_stack(self, cnxt, stack_identity, show_deleted=False):
        identity = identifier.HeatIdentifier(**stack_identity)

        if identity.tenant != cnxt.tenant_id:
            raise exception.InvalidTenant(target=identity.tenant,
                                          actual=cnxt.tenant_id)

        s = db_api.stack_get(cnxt,
                             identity.stack_id,
                             show_deleted=show_deleted)

        if s is None:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        if identity.path or s.name != identity.stack_name:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        return s
Example #33
0
 def rule_actions(self, new_state):
     logger.info('WATCH: stack:%s, watch_name:%s %s',
                 self.stack_id, self.name, new_state)
     actions = []
     if self.ACTION_MAP[new_state] not in self.rule:
         logger.info('no action for new state %s',
                     new_state)
     else:
         s = db_api.stack_get(self.context, self.stack_id)
         stack = parser.Stack.load(self.context, stack=s)
         if (stack.action != stack.DELETE
                 and stack.status == stack.COMPLETE):
             for refid in self.rule[self.ACTION_MAP[new_state]]:
                 actions.append(stack.resource_by_refid(refid).signal)
         else:
             logger.warning("Could not process watch state %s for stack" %
                            new_state)
     return actions
Example #34
0
    def check_stack_watches(self, sid):
        # Retrieve the stored credentials & create context
        # Require tenant_safe=False to the stack_get to defeat tenant
        # scoping otherwise we fail to retrieve the stack
        LOG.debug("Periodic watcher task for stack %s" % sid)
        admin_context = context.get_admin_context()
        db_stack = db_api.stack_get(admin_context,
                                    sid,
                                    tenant_safe=False,
                                    eager_load=True)
        if not db_stack:
            LOG.error(_LE("Unable to retrieve stack %s for periodic task"),
                      sid)
            return
        stk = stack.Stack.load(admin_context,
                               stack=db_stack,
                               use_stored_context=True)

        # recurse into any nested stacks.
        children = db_api.stack_get_all_by_owner_id(admin_context, sid)
        for child in children:
            self.check_stack_watches(child.id)

        # Get all watchrules for this stack and evaluate them
        try:
            wrs = db_api.watch_rule_get_all_by_stack(admin_context, sid)
        except Exception as ex:
            LOG.warn(_LW('periodic_task db error watch rule removed? %(ex)s'),
                     ex)
            return

        def run_alarm_action(stk, actions, details):
            for action in actions:
                action(details=details)
            for res in stk.itervalues():
                res.metadata_update()

        for wr in wrs:
            rule = watchrule.WatchRule.load(stk.context, watch=wr)
            actions = rule.evaluate()
            if actions:
                self.thread_group_mgr.start(sid, run_alarm_action, stk,
                                            actions, rule.get_details())
Example #35
0
    def metadata_update(self, context, stack_id, resource_name, metadata):
        """
        Update the metadata for the given resource.
        """
        s = db_api.stack_get(None, stack_id)
        if s is None:
            logger.warn("Stack %s not found" % stack_id)
            return ['stack', None]

        stack = parser.Stack.load(None, stack=s)
        if resource_name not in stack:
            logger.warn("Resource not found %s:%s." %
                        (stack_id, resource_name))
            return ['resource', None]

        resource = stack[resource_name]
        resource.metadata = metadata

        return [None, resource.metadata]
Example #36
0
    def _get_stack(self, cnxt, stack_identity, show_deleted=False):
        identity = identifier.HeatIdentifier(**stack_identity)

        s = db_api.stack_get(cnxt,
                             identity.stack_id,
                             show_deleted=show_deleted)

        if s is None:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        if cnxt.tenant_id not in (identity.tenant, s.stack_user_project_id):
            # The DB API should not allow this, but sanity-check anyway..
            raise exception.InvalidTenant(target=identity.tenant,
                                          actual=cnxt.tenant_id)

        if identity.path or s.name != identity.stack_name:
            raise exception.StackNotFound(stack_name=identity.stack_name)

        return s
Example #37
0
    def check_stack_watches(self, sid):
        # Retrieve the stored credentials & create context
        # Require tenant_safe=False to the stack_get to defeat tenant
        # scoping otherwise we fail to retrieve the stack
        logger.debug(_("Periodic watcher task for stack %s") % sid)
        admin_context = context.get_admin_context()
        stack = db_api.stack_get(admin_context, sid, tenant_safe=False)
        if not stack:
            logger.error(
                _("Unable to retrieve stack %s for periodic task") % sid)
            return
        stack_context = EngineService.load_user_creds(stack.user_creds_id)

        # recurse into any nested stacks.
        children = db_api.stack_get_all_by_owner_id(admin_context, sid)
        for child in children:
            self.check_stack_watches(child.id)

        # Get all watchrules for this stack and evaluate them
        try:
            wrs = db_api.watch_rule_get_all_by_stack(stack_context, sid)
        except Exception as ex:
            logger.warn(
                _('periodic_task db error (%(msg)s) %(ex)s') % {
                    'msg': 'watch rule removed?',
                    'ex': str(ex)
                })
            return

        def run_alarm_action(actions, details):
            for action in actions:
                action(details=details)

            stk = parser.Stack.load(stack_context, stack=stack)
            for res in stk.itervalues():
                res.metadata_update()

        for wr in wrs:
            rule = watchrule.WatchRule.load(stack_context, watch=wr)
            actions = rule.evaluate()
            if actions:
                self.thread_group_mgr.start(sid, run_alarm_action, actions,
                                            rule.get_details())
Example #38
0
    def test_metadata(self):
        test_metadata = {'foo': 'bar', 'baz': 'quux', 'blarg': 'wibble'}
        pre_update_meta = self.stack['WebServer'].metadata

        self.m.StubOutWithMock(service.EngineService, '_get_stack')
        s = db_api.stack_get(self.ctx, self.stack.id)
        service.EngineService._get_stack(self.ctx,
                                         self.stack.identifier()).AndReturn(s)
        self.m.StubOutWithMock(instances.Instance, 'metadata_update')
        instances.Instance.metadata_update(new_metadata=test_metadata)
        self.m.ReplayAll()

        result = self.eng.metadata_update(self.ctx,
                                          dict(self.stack.identifier()),
                                          'WebServer', test_metadata)
        # metadata_update is a no-op for all resources except
        # WaitConditionHandle so we don't expect this to have changed
        self.assertEqual(result, pre_update_meta)

        self.m.VerifyAll()
Example #39
0
    def state_set(self, action, status, reason):
        '''Update the stack state in the database.'''
        if action not in self.ACTIONS:
            raise ValueError(_("Invalid action %s") % action)

        if status not in self.STATUSES:
            raise ValueError(_("Invalid status %s") % status)

        self.action = action
        self.status = status
        self.status_reason = reason

        if self.id is None:
            return

        stack = db_api.stack_get(self.context, self.id)
        stack.update_and_save({'action': action,
                               'status': status,
                               'status_reason': reason})
        notification.send(self)
Example #40
0
    def rule_action(self, new_state):
        logger.warn('WATCH: stack:%s, watch_name:%s %s', self.stack_id,
                    self.name, new_state)

        actioned = False
        if not self.ACTION_MAP[new_state] in self.rule:
            logger.info('no action for new state %s', new_state)
            actioned = True
        else:
            s = db_api.stack_get(self.context, self.stack_id)
            if s and s.status in (parser.Stack.CREATE_COMPLETE,
                                  parser.Stack.UPDATE_COMPLETE):
                stack = parser.Stack.load(self.context, stack=s)
                for a in self.rule[self.ACTION_MAP[new_state]]:
                    greenpool.spawn_n(stack[a].alarm)
                actioned = True
            else:
                logger.warning("Could not process watch state %s for stack" %
                               new_state)
        return actioned
Example #41
0
    def load(cls,
             context,
             stack_id=None,
             stack=None,
             parent_resource=None,
             show_deleted=True,
             use_stored_context=False):
        '''Retrieve a Stack from the database.'''
        if stack is None:
            stack = db_api.stack_get(context,
                                     stack_id,
                                     show_deleted=show_deleted,
                                     eager_load=True)
        if stack is None:
            message = _('No stack exists with id "%s"') % str(stack_id)
            raise exception.NotFound(message)

        return cls._from_db(context,
                            stack,
                            parent_resource=parent_resource,
                            use_stored_context=use_stored_context)
Example #42
0
 def rule_actions(self, new_state):
     LOG.info(
         _LI('WATCH: stack:%(stack)s, watch_name:%(watch_name)s, '
             'new_state:%(new_state)s'), {
                 'stack': self.stack_id,
                 'watch_name': self.name,
                 'new_state': new_state
             })
     actions = []
     if self.ACTION_MAP[new_state] not in self.rule:
         LOG.info(_LI('no action for new state %s'), new_state)
     else:
         s = db_api.stack_get(self.context, self.stack_id, eager_load=True)
         stk = stack.Stack.load(self.context, stack=s)
         if (stk.action != stk.DELETE and stk.status == stk.COMPLETE):
             for refid in self.rule[self.ACTION_MAP[new_state]]:
                 actions.append(stk.resource_by_refid(refid).signal)
         else:
             LOG.warn(_LW("Could not process watch state %s for stack"),
                      new_state)
     return actions
Example #43
0
    def identify_stack(self, cnxt, stack_name):
        """
        The identify_stack method returns the full stack identifier for a
        single, live stack given the stack name.

        :param cnxt: RPC context.
        :param stack_name: Name or UUID of the stack to look up.
        """
        if uuidutils.is_uuid_like(stack_name):
            s = db_api.stack_get(cnxt, stack_name, show_deleted=True)
            # may be the name is in uuid format, so if get by id returns None,
            # we should get the info by name again
            if not s:
                s = db_api.stack_get_by_name(cnxt, stack_name)
        else:
            s = db_api.stack_get_by_name(cnxt, stack_name)
        if s:
            stack = parser.Stack.load(cnxt, stack=s)
            return dict(stack.identifier())
        else:
            raise exception.StackNotFound(stack_name=stack_name)
Example #44
0
    def test_nested_stack_update(self):
        urlfetch.get('https://server.test/the.template').MultipleTimes().\
            AndReturn(self.nested_template)
        urlfetch.get('https://server.test/new.template').MultipleTimes().\
            AndReturn(self.update_template)

        self.m.ReplayAll()

        stack = self.create_stack(self.test_template)
        rsrc = stack['the_nested']

        original_nested_id = rsrc.resource_id
        t = template_format.parse(self.test_template)
        new_res = copy.deepcopy(t['Resources']['the_nested'])
        new_res['Properties']['TemplateURL'] = (
            'https://server.test/new.template')
        prop_diff = {'TemplateURL': 'https://server.test/new.template'}
        updater = rsrc.handle_update(new_res, {}, prop_diff)
        updater.run_to_completion()
        self.assertIs(True, rsrc.check_update_complete(updater))

        # Expect the physical resource name staying the same after update,
        # so that the nested was actually updated instead of replaced.
        self.assertEqual(original_nested_id, rsrc.resource_id)
        db_nested = db_api.stack_get(stack.context,
                                     rsrc.resource_id)
        # Owner_id should be preserved during the update process.
        self.assertEqual(stack.id, db_nested.owner_id)

        self.assertEqual('foo', rsrc.FnGetAtt('Outputs.Bar'))
        self.assertRaises(
            exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Foo')
        self.assertRaises(
            exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Outputs.Foo')
        self.assertRaises(
            exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Bar')

        rsrc.delete()

        self.m.VerifyAll()
Example #45
0
    def _periodic_watcher_task(self, sid):
        """
        Periodic task, created for each stack, triggers watch-rule
        evaluation for all rules defined for the stack
        sid = stack ID
        """
        # Retrieve the stored credentials & create context
        # Require admin=True to the stack_get to defeat tenant
        # scoping otherwise we fail to retrieve the stack
        logger.debug("Periodic watcher task for stack %s" % sid)
        admin_context = context.get_admin_context()
        stack = db_api.stack_get(admin_context, sid, admin=True)
        if not stack:
            logger.error("Unable to retrieve stack %s for periodic task" % sid)
            return
        user_creds = db_api.user_creds_get(stack.user_creds_id)
        stack_context = context.RequestContext.from_dict(user_creds)

        # Get all watchrules for this stack and evaluate them
        try:
            wrs = db_api.watch_rule_get_all_by_stack(stack_context, sid)
        except Exception as ex:
            logger.warn('periodic_task db error (%s) %s' %
                        ('watch rule removed?', str(ex)))
            return

        def run_alarm_action(actions, details):
            for action in actions:
                action(details=details)

            stk = parser.Stack.load(stack_context, stack=stack)
            for res in stk:
                res.metadata_update()

        for wr in wrs:
            rule = watchrule.WatchRule.load(stack_context, watch=wr)
            actions = rule.evaluate()
            if actions:
                self._start_in_thread(sid, run_alarm_action, actions,
                                      rule.get_details())
Example #46
0
    def _check_stack_watches(self, sid):
        # Retrieve the stored credentials & create context
        # Require admin=True to the stack_get to defeat tenant
        # scoping otherwise we fail to retrieve the stack
        logger.debug("Periodic watcher task for stack %s" % sid)
        admin_context = context.get_admin_context()
        stack = db_api.stack_get(admin_context, sid, admin=True)
        if not stack:
            logger.error("Unable to retrieve stack %s for periodic task" % sid)
            return
        stack_context = self._load_user_creds(stack.user_creds_id)

        # recurse into any nested stacks.
        children = db_api.stack_get_all_by_owner_id(admin_context, sid)
        for child in children:
            self._check_stack_watches(child.id)

        # Get all watchrules for this stack and evaluate them
        try:
            wrs = db_api.watch_rule_get_all_by_stack(stack_context, sid)
        except Exception as ex:
            logger.warn('periodic_task db error (%s) %s' %
                        ('watch rule removed?', str(ex)))
            return

        def run_alarm_action(actions, details):
            for action in actions:
                action(details=details)

            stk = parser.Stack.load(stack_context, stack=stack)
            for res in stk:
                res.metadata_update()

        for wr in wrs:
            rule = watchrule.WatchRule.load(stack_context, watch=wr)
            actions = rule.evaluate()
            if actions:
                self._start_in_thread(sid, run_alarm_action, actions,
                                      rule.get_details())
Example #47
0
    def test_stack_update(self):
        stack_name = 'service_update_test_stack'
        params = {'foo': 'bar'}
        template = '{ "Template": "data" }'

        old_stack = get_wordpress_stack(stack_name, self.ctx)
        sid = old_stack.store()
        s = db_api.stack_get(self.ctx, sid)

        stack = get_wordpress_stack(stack_name, self.ctx)

        self.m.StubOutWithMock(parser, 'Stack')
        self.m.StubOutWithMock(parser.Stack, 'load')
        parser.Stack.load(self.ctx, stack=s).AndReturn(old_stack)

        self.m.StubOutWithMock(parser, 'Template')
        self.m.StubOutWithMock(parser, 'Parameters')

        parser.Template(template).AndReturn(stack.t)
        parser.Parameters(stack_name, stack.t,
                          params).AndReturn(stack.parameters)
        parser.Stack(self.ctx, stack.name, stack.t,
                     stack.parameters).AndReturn(stack)

        self.m.StubOutWithMock(stack, 'validate')
        stack.validate().AndReturn(None)

        self.m.StubOutWithMock(threadgroup, 'ThreadGroup')
        threadgroup.ThreadGroup().AndReturn(DummyThreadGroup())

        self.m.ReplayAll()

        result = self.man.update_stack(self.ctx, old_stack.identifier(),
                                       template, params, {})
        self.assertEqual(result, old_stack.identifier())
        self.assertTrue(isinstance(result, dict))
        self.assertTrue(result['stack_id'])
        self.m.VerifyAll()
Example #48
0
 def get_by_id(cls, context, stack_id, **kwargs):
     db_stack = db_api.stack_get(context, stack_id, **kwargs)
     if not db_stack:
         return None
     stack = cls._from_db_object(context, cls(context), db_stack)
     return stack
Example #49
0
 def refresh(self):
     db_stack = db_api.stack_get(self._context, self.id, show_deleted=True)
     if db_stack is None:
         message = _('No stack exists with id "%s"') % str(self.id)
         raise exception.NotFound(message)
     return self.__class__._from_db_object(self._context, self, db_stack)
Example #50
0
 def refresh(self):
     db_stack = db_api.stack_get(self._context, self.id, show_deleted=True)
     db_stack.refresh()
     return self.__class__._from_db_object(self._context, self, db_stack)
Example #51
0
    def delete(self, action=DELETE, backup=False):
        '''
        Delete all of the resources, and then the stack itself.
        The action parameter is used to differentiate between a user
        initiated delete and an automatic stack rollback after a failed
        create, which amount to the same thing, but the states are recorded
        differently.
        '''
        if action not in (self.DELETE, self.ROLLBACK):
            logger.error(_("Unexpected action %s passed to delete!") % action)
            self.state_set(self.DELETE, self.FAILED,
                           "Invalid action %s" % action)
            return

        stack_status = self.COMPLETE
        reason = 'Stack %s completed successfully' % action
        self.state_set(action, self.IN_PROGRESS, 'Stack %s started' % action)

        backup_stack = self._backup_stack(False)
        if backup_stack is not None:
            backup_stack.delete(backup=True)
            if backup_stack.status != backup_stack.COMPLETE:
                errs = backup_stack.status_reason
                failure = 'Error deleting backup resources: %s' % errs
                self.state_set(action, self.FAILED,
                               'Failed to %s : %s' % (action, failure))
                return

        action_task = scheduler.DependencyTaskGroup(self.dependencies,
                                                    resource.Resource.destroy,
                                                    reverse=True)
        try:
            scheduler.TaskRunner(action_task)(timeout=self.timeout_secs())
        except exception.ResourceFailure as ex:
            stack_status = self.FAILED
            reason = 'Resource %s failed: %s' % (action, str(ex))
        except scheduler.Timeout:
            stack_status = self.FAILED
            reason = '%s timed out' % action.title()

        if stack_status != self.FAILED and not backup:
            # If we created a trust, delete it
            stack = db_api.stack_get(self.context, self.id)
            user_creds = db_api.user_creds_get(stack.user_creds_id)
            trust_id = user_creds.get('trust_id')
            if trust_id:
                try:
                    self.clients.keystone().delete_trust(trust_id)
                except Exception as ex:
                    logger.exception(ex)
                    stack_status = self.FAILED
                    reason = "Error deleting trust: %s" % str(ex)

            # If the stack has a domain project, delete it
            if self.stack_user_project_id:
                try:
                    self.clients.keystone().delete_stack_domain_project(
                        project_id=self.stack_user_project_id)
                except Exception as ex:
                    logger.exception(ex)
                    stack_status = self.FAILED
                    reason = "Error deleting project: %s" % str(ex)

        self.state_set(action, stack_status, reason)

        if stack_status != self.FAILED:
            # delete the stack
            db_api.stack_delete(self.context, self.id)
            self.id = None