Support for Heat LoadBalancer and Stack Outputs

Change-Id: If5261fe83967d2d5fbef4f71d4fa3b250b3ce6d7
This commit is contained in:
Stan Lagun 2013-05-30 13:01:12 +04:00
parent 9feed1b9a5
commit f49dbfe907
8 changed files with 98 additions and 117 deletions

View File

@ -22,23 +22,30 @@ import time
import xml_code_engine
def update_cf_stack(engine, context, body, template,
mappings, arguments, **kwargs):
def update_cf_stack(engine, context, body, template, result=None, **kwargs):
command_dispatcher = context['/commandDispatcher']
callback = lambda result: engine.evaluate_content(
body.find('success'), context)
def callback(result_value):
if result is not None:
context[result] = result_value['Result']
success_handler = body.find('success')
if success_handler is not None:
engine.evaluate_content(success_handler, context)
command_dispatcher.execute(
name='cf', command='CreateOrUpdate', template=template,
mappings=mappings, arguments=arguments, callback=callback)
mappings=kwargs.get('mappings', {}),
arguments=kwargs.get('arguments', {}),
callback=callback)
def delete_cf_stack(engine, context, body, **kwargs):
command_dispatcher = context['/commandDispatcher']
callback = lambda result: engine.evaluate_content(
body.find('success'), context)
def callback(result_value):
success_handler = body.find('success')
if success_handler is not None:
engine.evaluate_content(success_handler, context)
command_dispatcher.execute(
name='cf', command='Delete', callback=callback)

View File

@ -59,8 +59,8 @@ class HeatExecutor(CommandBase):
if command == 'CreateOrUpdate':
return self._execute_create_update(
kwargs['template'],
kwargs['mappings'],
kwargs['arguments'],
kwargs.get('mappings', {}),
kwargs.get('arguments', {}),
callback)
elif command == 'Delete':
return self._execute_delete(callback)
@ -116,23 +116,24 @@ class HeatExecutor(CommandBase):
template=template)
log.debug(
'Waiting for the stack {0} to be update'.format(self._stack))
self._wait_state('UPDATE_COMPLETE')
outs = self._wait_state('UPDATE_COMPLETE')
log.info('Stack {0} updated'.format(self._stack))
else:
self._heat_client.stacks.create(
stack_name=self._stack,
parameters=arguments,
template=template)
log.debug('Waiting for the stack {0} to be create'.format(
self._stack))
self._wait_state('CREATE_COMPLETE')
outs = self._wait_state('CREATE_COMPLETE')
log.info('Stack {0} created'.format(self._stack))
pending_list = self._update_pending_list
self._update_pending_list = []
for item in pending_list:
item['callback'](True)
item['callback'](outs)
return True
@ -177,9 +178,11 @@ class HeatExecutor(CommandBase):
while True:
try:
status = self._heat_client.stacks.get(
stack_id=self._stack).stack_status
stack_info = self._heat_client.stacks.get(
stack_id=self._stack)
status = stack_info.stack_status
except heatclient.exc.HTTPNotFound:
stack_info = None
status = ''
if 'IN_PROGRESS' in status:
@ -187,4 +190,9 @@ class HeatExecutor(CommandBase):
continue
if status not in states:
raise EnvironmentError()
return
try:
return dict([(t['output_key'], t['output_value'])
for t in stack_info.outputs])
except Exception:
return {}

View File

@ -27,7 +27,13 @@ def transform_json(json, mappings):
transform_json(value, mappings)
return result
if isinstance(json, types.StringTypes) and json.startswith('$'):
elif isinstance(json, types.ListType):
result = []
for value in json:
result.append(transform_json(value, mappings))
return result
elif isinstance(json, types.StringTypes) and json.startswith('$'):
value = mappings.get(json[1:])
if value is not None:
return value
@ -35,19 +41,28 @@ def transform_json(json, mappings):
return json
def merge_lists(list1, list2):
return list1 + list2
def merge_dicts(dict1, dict2, max_levels=0):
result = {}
for key, value in dict1.items():
result[key] = value
if key in dict2:
other_value = dict2[key]
if max_levels == 1 or not isinstance(
if type(other_value) != type(value):
raise TypeError()
if max_levels != 1 and isinstance(
other_value, types.DictionaryType):
result[key] = other_value
else:
result[key] = merge_dicts(
value, other_value,
0 if max_levels == 0 else max_levels - 1)
elif max_levels != 1 and isinstance(
other_value, types.ListType):
result[key] = merge_lists(value, other_value)
else:
result[key] = other_value
for key, value in dict2.items():
if key not in result:
result[key] = value

View File

@ -0,0 +1,21 @@
{
"Resources" : {
"$lbName" : {
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties" : {
"Instances" : [{"Ref": "$instanceName"}],
"Listeners" : [ {
"LoadBalancerPort" : "$lbPort",
"InstancePort" : "80",
"Protocol" : "HTTP"
}]
}
}
},
"Outputs": {
"LoadBalancerIP": {
"Value": { "Fn::GetAtt": [ "$lbName", "DNSName" ] },
"Description": ""
}
}
}

View File

@ -1,11 +1,11 @@
<workflow>
<rule match="$.services.aspNetApps[?(@.domain)].units[*]">
<rule match="$.services.webServers,aspNetApps,webServerFarms,aspNetAppFarms[?(@.domain)].units[*]">
<set path="domain">
<select path="::domain"/>
</set>
</rule>
<rule match="$.services.aspNetApps[*].units[?(@.state.hostname and not @.state.instanceName)]">
<rule match="$.services.webServers,aspNetApps,webServerFarms,aspNetAppFarms[*].units[?(@.state.hostname and not @.state.instanceName)]">
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Creating instance <select path="name"/></parameter>
@ -41,7 +41,25 @@
</update-cf-stack>
</rule>
<rule match="$.services.aspNetApps[?(@.adminPassword and @.adminPassword != @.state.adminPassword)].units[?(@.state.instanceName)]">
<rule match="$.services.webServerFarms,aspNetAppFarms[*].units[?(@.state.hostname and not @.state.registeredWithLB)]">
<update-cf-stack template="LoadBalancer" result="outputs">
<parameter name="mappings">
<map>
<mapping name="instanceName"><select path="state.hostname"/></mapping>
<mapping name="lbPort"><select path="::loadBalancerPort"/></mapping>
<mapping name="lbName"><select path="::name"/></mapping>
</map>
</parameter>
<success>
<set path="state.registeredWithLB"><true/></set>
<set path="::LoadBalancerIP">
<select source="outputs" path="LoadBalancerIP"/>
</set>
</success>
</update-cf-stack>
</rule>
<rule match="$.services.webServers,aspNetApps,webServerFarms,aspNetAppFarms[?(@.adminPassword and @.adminPassword != @.state.adminPassword)].units[?(@.state.instanceName)]">
<send-command template="SetPassword">
<parameter name="unit">
<select path="id"/>
@ -64,7 +82,8 @@
</send-command>
</rule>
<rule match="$.services.aspNetApps[*].units[?(@.state.instanceName and not @.state.iisInstalled)]">
<rule match="$.services.webServers,aspNetApps,webServerFarms,aspNetAppFarms[*].units[?(@.state.instanceName and not @.state.iisInstalled)]">
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Creating IIS Web Server on unit <select path="name"/></parameter>
@ -86,7 +105,7 @@
</send-command>
</rule>
<rule match="$.services.aspNetApps[*].units[?(@.state.iisInstalled and not @.state.webAppDeployed)]">
<rule match="$.services.aspNetApps,aspNetAppFarms[*].units[?(@.state.iisInstalled and not @.state.webAppDeployed)]">
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Deploying Web App on unit <select path="name"/></parameter>

View File

@ -1,89 +0,0 @@
<workflow>
<rule match="$.services.webServers[?(@.domain)].units[*]">
<set path="domain">
<select path="::domain"/>
</set>
</rule>
<rule match="$.services.webServers[*].units[?(@.state.hostname and not @.state.instanceName)]">
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Creating instance <select path="name"/></parameter>
</report>
<update-cf-stack template="Windows">
<parameter name="mappings">
<map>
<mapping name="instanceName"><select path="state.hostname"/></mapping>
<mapping name="userData">
<prepare-user-data>
<parameter name="hostname"><select path="state.hostname"/></parameter>
<parameter name="unit"><select path="id"/></parameter>
<parameter name="service"><select path="::id"/></parameter>
</prepare-user-data>
</mapping>
</map>
</parameter>
<parameter name="arguments">
<map>
<argument name="KeyName">murano-keys</argument>
<argument name="InstanceType">m1.medium</argument>
<argument name="ImageName">ws-2012-full</argument>
</map>
</parameter>
<success>
<set path="state.instanceName"><select path="name"/></set>
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Instance <select path="name"/> created</parameter>
</report>
</success>
</update-cf-stack>
</rule>
<rule match="$.services.webServers[?(@.adminPassword and @.adminPassword != @.state.adminPassword)].units[?(@.state.instanceName)]">
<send-command template="SetPassword">
<parameter name="unit">
<select path="id"/>
</parameter>
<parameter name="service">
<select path="::id"/>
</parameter>
<parameter name="mappings">
<map>
<mapping name="adminPassword">
<select path="::adminPassword"/>
</mapping>
</map>
</parameter>
<success>
<set path="::state.adminPassword">
<select path="::adminPassword"/>
</set>
</success>
</send-command>
</rule>
<rule match="$.services.webServers[*].units[?(@.state.instanceName and not @.state.iisInstalled)]">
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">Creating IIS Web Server on unit <select path="name"/></parameter>
</report>
<send-command template="InstallIIS">
<parameter name="unit">
<select path="id"/>
</parameter>
<parameter name="service">
<select path="::id"/>
</parameter>
<success>
<set path="state.iisInstalled"><true/></set>
<report entity="unit">
<parameter name="id"><select path="id"/></parameter>
<parameter name="text">IIS <select path="name"/> has started</parameter>
</report>
</success>
</send-command>
</rule>
</workflow>

View File

@ -4,10 +4,10 @@ debug=True
verbose=True
[heat]
auth_url = http://172.18.124.101:5000/v2.0
auth_url = http://172.18.79.71:5000/v2.0
[rabbitmq]
host = 172.18.124.101
host = localhost
port = 5672
virtual_host = murano
login = murano

View File

@ -84,7 +84,7 @@ class TestHeatExecutor(unittest.TestCase):
"testKey": "testValue"
}
})
callback.assert_called_with(True)
callback.assert_called_with({})
@mock.patch('heatclient.v1.client.Client')
@mock.patch('keystoneclient.v2_0.client.Client')
@ -139,7 +139,7 @@ class TestHeatExecutor(unittest.TestCase):
"testKey": "testValue"
}
})
callback.assert_called_with(True)
callback.assert_called_with({})
@mock.patch('heatclient.v1.client.Client')
@mock.patch('keystoneclient.v2_0.client.Client')