Merge "Update the metadata if an alarm action makes changes"
This commit is contained in:
commit
2e7e3242a5
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue