Merge "Update the metadata if an alarm action makes changes"

This commit is contained in:
Jenkins 2013-02-26 04:25:41 +00:00 committed by Gerrit Code Review
commit 2e7e3242a5
7 changed files with 199 additions and 28 deletions

View File

@ -493,12 +493,13 @@ class Resource(object):
raise NotImplementedError("Update not implemented for Resource %s"
% type(self))
def metadata_update(self, metadata):
def metadata_update(self, new_metadata=None):
'''
No-op for resources which don't explicitly override this method
'''
logger.warning("Resource %s does not implement metadata update" %
self.name)
if new_metadata:
logger.warning("Resource %s does not implement metadata update" %
self.name)
class GenericResource(Resource):

View File

@ -338,6 +338,13 @@ class Instance(resource.Resource):
return status
def metadata_update(self, new_metadata=None):
'''
Refresh the metadata if new_metadata is None
'''
if new_metadata is None:
self.metadata = self.parsed_template('Metadata')
def validate(self):
'''
Validate any of the provided params

View File

@ -130,21 +130,24 @@ class WaitConditionHandle(resource.Resource):
if sorted(metadata.keys()) == expected_keys:
return metadata['Status'] in (SUCCESS, FAILURE)
def metadata_update(self, metadata):
def metadata_update(self, new_metadata=None):
'''
Validate and update the resource metadata
'''
if self._metadata_format_ok(metadata):
if new_metadata is None:
return
if self._metadata_format_ok(new_metadata):
rsrc_metadata = self.metadata
if metadata['UniqueId'] in rsrc_metadata:
if new_metadata['UniqueId'] in rsrc_metadata:
logger.warning("Overwriting Metadata item for UniqueId %s!" %
metadata['UniqueId'])
new_metadata = {}
new_metadata['UniqueId'])
safe_metadata = {}
for k in ('Data', 'Reason', 'Status'):
new_metadata[k] = metadata[k]
safe_metadata[k] = new_metadata[k]
# Note we can't update self.metadata directly, as it
# is a Metadata descriptor object which only supports get/set
rsrc_metadata.update({metadata['UniqueId']: new_metadata})
rsrc_metadata.update({new_metadata['UniqueId']: safe_metadata})
self.metadata = rsrc_metadata
else:
logger.error("Metadata failed validation for %s" % self.name)

View File

@ -482,7 +482,7 @@ class EngineService(service.Service):
stack_name=stack.name)
resource = stack[resource_name]
resource.metadata_update(metadata)
resource.metadata_update(new_metadata=metadata)
return resource.metadata
@ -517,6 +517,10 @@ class EngineService(service.Service):
for action in actions:
action()
stk = parser.Stack.load(admin_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()

View File

@ -103,7 +103,7 @@ class LoadBalancerTest(unittest.TestCase):
self.fc.servers.list()[1])
#stack.Stack.create_with_template(mox.IgnoreArg()).AndReturn(None)
Metadata.__set__(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(None)
mox.IgnoreArg()).MultipleTimes().AndReturn(None)
lb.LoadBalancer.nova().MultipleTimes().AndReturn(self.fc)
self.m.ReplayAll()

View File

@ -0,0 +1,148 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mox
import uuid
import time
import datetime
import json
import eventlet
import unittest
from nose.plugins.attrib import attr
from oslo.config import cfg
from heat.tests import fakes
from heat.tests.utils import stack_delete_after
import heat.db as db_api
from heat.common import template_format
from heat.common import identifier
from heat.engine import parser
from heat.engine.resources import instance
from heat.common import context
test_template_metadata = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "",
"Parameters" : {
"KeyName" : {"Type" : "String", "Default": "mine" },
},
"Resources" : {
"S1": {
"Type": "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"files" : {
"/tmp/random_file" : {
"content" : { "Fn::Join" : ["", [
"s2-ip=", {"Fn::GetAtt": ["S2", "PublicIp"]}
]]},
"mode" : "000400",
"owner" : "root",
"group" : "root"
}
}
}
}
},
"Properties": {
"ImageId" : "a",
"InstanceType" : "m1.large",
"KeyName" : { "Ref" : "KeyName" },
"UserData" : "#!/bin/bash -v\n"
}
},
"S2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId" : "a",
"InstanceType" : "m1.large",
"KeyName" : { "Ref" : "KeyName" },
"UserData" : "#!/bin/bash -v\n"
}
}
}
}
'''
@attr(tag=['unit', 'resource', 'Metadata'])
@attr(speed='slow')
class MetadataRefreshTest(unittest.TestCase):
'''
The point of the test is to confirm that metadata gets updated
when FnGetAtt() returns something different.
gets called.
'''
def setUp(self):
self.m = mox.Mox()
self.m.StubOutWithMock(eventlet, 'sleep')
self.fc = fakes.FakeKeystoneClient()
def tearDown(self):
self.m.UnsetStubs()
# Note tests creating a stack should be decorated with @stack_delete_after
# to ensure the stack is properly cleaned up
def create_stack(self, stack_name='test_stack',
template=test_template_metadata, params={},
stub=True):
temp = template_format.parse(template)
template = parser.Template(temp)
parameters = parser.Parameters(stack_name, template, params)
ctx = context.get_admin_context()
ctx.tenant_id = 'test_tenant'
stack = parser.Stack(ctx, stack_name, template, parameters,
disable_rollback=True)
self.stack_id = stack.store()
if stub:
self.m.StubOutWithMock(instance.Instance, 'handle_create')
instance.Instance.handle_create().MultipleTimes().AndReturn(None)
self.m.StubOutWithMock(instance.Instance, 'FnGetAtt')
return stack
@stack_delete_after
def test_FnGetAtt(self):
self.stack = self.create_stack()
instance.Instance.FnGetAtt('PublicIp').AndReturn('1.2.3.5')
# called by metadata_update()
instance.Instance.FnGetAtt('PublicIp').AndReturn('10.0.0.5')
self.m.ReplayAll()
self.stack.create()
s1 = self.stack.resources['S1']
s2 = self.stack.resources['S2']
files = s1.metadata['AWS::CloudFormation::Init']['config']['files']
cont = files['/tmp/random_file']['content']
self.assertEqual(cont, 's2-ip=1.2.3.5')
s1.metadata_update()
s2.metadata_update()
files = s1.metadata['AWS::CloudFormation::Init']['config']['files']
cont = files['/tmp/random_file']['content']
self.assertEqual(cont, 's2-ip=10.0.0.5')
self.m.VerifyAll()

View File

@ -258,13 +258,13 @@ class WaitConditionTest(unittest.TestCase):
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
handle.metadata_update(test_metadata)
handle.metadata_update(new_metadata=test_metadata)
wc_att = resource.FnGetAtt('Data')
self.assertEqual(wc_att, '{"123": "foo"}')
test_metadata = {'Data': 'dog', 'Reason': 'cat',
'Status': 'SUCCESS', 'UniqueId': '456'}
handle.metadata_update(test_metadata)
handle.metadata_update(new_metadata=test_metadata)
wc_att = resource.FnGetAtt('Data')
self.assertEqual(wc_att, u'{"123": "foo", "456": "dog"}')
self.m.VerifyAll()
@ -457,7 +457,7 @@ class WaitConditionHandleTest(unittest.TestCase):
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
handle_metadata = {u'123': {u'Data': u'foo',
u'Reason': u'bar',
u'Status': u'SUCCESS'}}
@ -472,31 +472,39 @@ class WaitConditionHandleTest(unittest.TestCase):
# metadata_update should raise a ValueError if the metadata
# is missing any of the expected keys
err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'data': 'foo', 'reason': 'bar',
'status': 'SUCCESS', 'uniqueid': '1234'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
# Also any Status other than SUCCESS or FAILURE should be rejected
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'UCCESS', 'UniqueId': '123'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'wibble', 'UniqueId': '123'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'success', 'UniqueId': '123'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'FAIL', 'UniqueId': '123'}
self.assertRaises(ValueError, resource.metadata_update, err_metadata)
self.assertRaises(ValueError, resource.metadata_update,
new_metadata=err_metadata)
self.m.VerifyAll()
@stack_delete_after
@ -512,12 +520,12 @@ class WaitConditionHandleTest(unittest.TestCase):
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status(), ['SUCCESS'])
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '456'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status(), ['SUCCESS', 'SUCCESS'])
# re-stub keystone() with fake client or stack delete fails
@ -532,16 +540,16 @@ class WaitConditionHandleTest(unittest.TestCase):
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar')
test_metadata = {'Data': 'dog', 'Reason': 'cat',
'Status': 'SUCCESS', 'UniqueId': '456'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar;cat')
test_metadata = {'Data': 'boo', 'Reason': 'hoo',
'Status': 'FAILURE', 'UniqueId': '789'}
resource.metadata_update(test_metadata)
resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('FAILURE'), 'hoo')
self.m.VerifyAll()