Tidy up Resource creation and deletion
Most of this code is common between resources, so put it in the parent Resource class and have subclasses provide handle_create()/handle_delete() methods for all their extra needs. Change-Id: I14c6afa9fdd1ecc065036fa93bde2a693b6c3eb2 Signed-off-by: Zane Bitter <zbitter@redhat.com>
This commit is contained in:
parent
067e037064
commit
3b91d100a6
|
@ -54,22 +54,13 @@ class CloudWatchAlarm(Resource):
|
||||||
|
|
||||||
strict_dependency = False
|
strict_dependency = False
|
||||||
|
|
||||||
def __init__(self, name, json_snippet, stack):
|
|
||||||
super(CloudWatchAlarm, self).__init__(name, json_snippet, stack)
|
|
||||||
self.instance_id = ''
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
'''
|
'''
|
||||||
Validate the Properties
|
Validate the Properties
|
||||||
'''
|
'''
|
||||||
return Resource.validate(self)
|
return Resource.validate(self)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
Resource.create(self)
|
|
||||||
|
|
||||||
wr_values = {
|
wr_values = {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'rule': self.parsed_template()['Properties'],
|
'rule': self.parsed_template()['Properties'],
|
||||||
|
@ -80,21 +71,11 @@ class CloudWatchAlarm(Resource):
|
||||||
wr = db_api.watch_rule_create(self.stack.context, wr_values)
|
wr = db_api.watch_rule_create(self.stack.context, wr_values)
|
||||||
self.instance_id = wr.id
|
self.instance_id = wr.id
|
||||||
|
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
def handle_delete(self):
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db_api.watch_rule_delete(self.stack.context, self.name)
|
db_api.watch_rule_delete(self.stack.context, self.name)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def FnGetRefId(self):
|
def FnGetRefId(self):
|
||||||
return unicode(self.name)
|
return unicode(self.name)
|
||||||
|
|
|
@ -41,18 +41,12 @@ class ElasticIp(Resource):
|
||||||
self.ipaddress = ips.ip
|
self.ipaddress = ips.ip
|
||||||
return self.ipaddress or ''
|
return self.ipaddress or ''
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
"""Allocate a floating IP for the current tenant."""
|
"""Allocate a floating IP for the current tenant."""
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(ElasticIp, self).create()
|
|
||||||
|
|
||||||
ips = self.nova().floating_ips.create()
|
ips = self.nova().floating_ips.create()
|
||||||
logger.info('ElasticIp create %s' % str(ips))
|
logger.info('ElasticIp create %s' % str(ips))
|
||||||
self.ipaddress = ips.ip
|
self.ipaddress = ips.ip
|
||||||
self.instance_id_set(ips.id)
|
self.instance_id_set(ips.id)
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
'''
|
'''
|
||||||
|
@ -60,19 +54,11 @@ class ElasticIp(Resource):
|
||||||
'''
|
'''
|
||||||
return Resource.validate(self)
|
return Resource.validate(self)
|
||||||
|
|
||||||
def delete(self):
|
def handle_delete(self):
|
||||||
"""De-allocate a floating IP."""
|
"""De-allocate a floating IP."""
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
if self.instance_id is not None:
|
if self.instance_id is not None:
|
||||||
self.nova().floating_ips.delete(self.instance_id)
|
self.nova().floating_ips.delete(self.instance_id)
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def FnGetRefId(self):
|
def FnGetRefId(self):
|
||||||
return unicode(self._ipaddress())
|
return unicode(self._ipaddress())
|
||||||
|
|
||||||
|
@ -106,14 +92,8 @@ class ElasticIpAssociation(Resource):
|
||||||
'''
|
'''
|
||||||
return Resource.validate(self)
|
return Resource.validate(self)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
"""Add a floating IP address to a server."""
|
"""Add a floating IP address to a server."""
|
||||||
|
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(ElasticIpAssociation, self).create()
|
|
||||||
|
|
||||||
logger.debug('ElasticIpAssociation %s.add_floating_ip(%s)' %
|
logger.debug('ElasticIpAssociation %s.add_floating_ip(%s)' %
|
||||||
(self.properties['InstanceId'],
|
(self.properties['InstanceId'],
|
||||||
self.properties['EIP']))
|
self.properties['EIP']))
|
||||||
|
@ -121,17 +101,8 @@ class ElasticIpAssociation(Resource):
|
||||||
server = self.nova().servers.get(self.properties['InstanceId'])
|
server = self.nova().servers.get(self.properties['InstanceId'])
|
||||||
server.add_floating_ip(self.properties['EIP'])
|
server.add_floating_ip(self.properties['EIP'])
|
||||||
self.instance_id_set(self.properties['EIP'])
|
self.instance_id_set(self.properties['EIP'])
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
|
|
||||||
def delete(self):
|
def handle_delete(self):
|
||||||
"""Remove a floating IP address from a server."""
|
"""Remove a floating IP address from a server."""
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
server = self.nova().servers.get(self.properties['InstanceId'])
|
server = self.nova().servers.get(self.properties['InstanceId'])
|
||||||
server.remove_floating_ip(self.properties['EIP'])
|
server.remove_floating_ip(self.properties['EIP'])
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
|
@ -45,30 +45,19 @@ class Restarter(Resource):
|
||||||
properties_schema = {'InstanceId': {'Type': 'String',
|
properties_schema = {'InstanceId': {'Type': 'String',
|
||||||
'Required': True}}
|
'Required': True}}
|
||||||
|
|
||||||
def __init__(self, name, json_snippet, stack):
|
def _find_resource(self, instance_id):
|
||||||
super(Restarter, self).__init__(name, json_snippet, stack)
|
'''
|
||||||
|
Return the resource with the specified instance ID, or None if it
|
||||||
def create(self):
|
cannot be found.
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
'''
|
||||||
return
|
for resource in self.stack:
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
if resource.instance_id == instance_id:
|
||||||
Resource.create(self)
|
return resource
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
return None
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def alarm(self):
|
def alarm(self):
|
||||||
self.calculate_properties()
|
self.calculate_properties()
|
||||||
victim = None
|
victim = self._find_resource(self.properties['InstanceId'])
|
||||||
for rname, r in self.stack.resources.items():
|
|
||||||
if r.instance_id == self.properties['InstanceId']:
|
|
||||||
victim = r
|
|
||||||
break
|
|
||||||
|
|
||||||
if victim is None:
|
if victim is None:
|
||||||
logger.info('%s Alarm, can not find instance %s' %
|
logger.info('%s Alarm, can not find instance %s' %
|
||||||
|
@ -137,6 +126,15 @@ class Instance(Resource):
|
||||||
'cc2.8xlarge': 'm1.large',
|
'cc2.8xlarge': 'm1.large',
|
||||||
'cg1.4xlarge': 'm1.large'}
|
'cg1.4xlarge': 'm1.large'}
|
||||||
|
|
||||||
|
def _set_ipaddress(self, networks):
|
||||||
|
'''
|
||||||
|
Read the server's IP address from a list of networks provided by Nova
|
||||||
|
'''
|
||||||
|
# Just record the first ipaddress
|
||||||
|
for n in networks:
|
||||||
|
self.ipaddress = networks[n][0]
|
||||||
|
break
|
||||||
|
|
||||||
def _ipaddress(self):
|
def _ipaddress(self):
|
||||||
'''
|
'''
|
||||||
Return the server's IP address, fetching it from Nova if necessary
|
Return the server's IP address, fetching it from Nova if necessary
|
||||||
|
@ -147,9 +145,7 @@ class Instance(Resource):
|
||||||
except NotFound as ex:
|
except NotFound as ex:
|
||||||
logger.warn('Instance IP address not found (%s)' % str(ex))
|
logger.warn('Instance IP address not found (%s)' % str(ex))
|
||||||
else:
|
else:
|
||||||
for n in server.networks:
|
self._set_ipaddress(server.networks)
|
||||||
self.ipaddress = server.networks[n][0]
|
|
||||||
break
|
|
||||||
|
|
||||||
return self.ipaddress or '0.0.0.0'
|
return self.ipaddress or '0.0.0.0'
|
||||||
|
|
||||||
|
@ -211,18 +207,7 @@ class Instance(Resource):
|
||||||
|
|
||||||
return self.mime_string
|
return self.mime_string
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
def _null_callback(p, n, out):
|
|
||||||
"""
|
|
||||||
Method to silence the default M2Crypto.RSA.gen_key output.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
Resource.create(self)
|
|
||||||
|
|
||||||
security_groups = self.properties.get('SecurityGroups')
|
security_groups = self.properties.get('SecurityGroups')
|
||||||
userdata = self.properties['UserData']
|
userdata = self.properties['UserData']
|
||||||
flavor = self.itype_oflavor[self.properties['InstanceType']]
|
flavor = self.itype_oflavor[self.properties['InstanceType']]
|
||||||
|
@ -259,11 +244,7 @@ class Instance(Resource):
|
||||||
eventlet.sleep(1)
|
eventlet.sleep(1)
|
||||||
if server.status == 'ACTIVE':
|
if server.status == 'ACTIVE':
|
||||||
self.instance_id_set(server.id)
|
self.instance_id_set(server.id)
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
self._set_ipaddress(server.networks)
|
||||||
# just record the first ipaddress
|
|
||||||
for n in server.networks:
|
|
||||||
self.ipaddress = server.networks[n][0]
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
raise exception.Error('%s instance[%s] status[%s]' %
|
raise exception.Error('%s instance[%s] status[%s]' %
|
||||||
('nova reported unexpected',
|
('nova reported unexpected',
|
||||||
|
@ -288,11 +269,7 @@ class Instance(Resource):
|
||||||
'Provided KeyName is not registered with nova'}
|
'Provided KeyName is not registered with nova'}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def delete(self):
|
def handle_delete(self):
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
try:
|
try:
|
||||||
server = self.nova().servers.get(self.instance_id)
|
server = self.nova().servers.get(self.instance_id)
|
||||||
except NotFound:
|
except NotFound:
|
||||||
|
@ -300,4 +277,3 @@ class Instance(Resource):
|
||||||
else:
|
else:
|
||||||
server.delete()
|
server.delete()
|
||||||
self.instance_id = None
|
self.instance_id = None
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import webob
|
||||||
import json
|
import json
|
||||||
import urlparse
|
import urlparse
|
||||||
import httplib
|
import httplib
|
||||||
|
import eventlet
|
||||||
|
|
||||||
from heat import manager
|
from heat import manager
|
||||||
from heat.db import api as db_api
|
from heat.db import api as db_api
|
||||||
|
@ -36,6 +37,7 @@ from novaclient.exceptions import NotFound
|
||||||
from novaclient.exceptions import AuthorizationFailure
|
from novaclient.exceptions import AuthorizationFailure
|
||||||
|
|
||||||
logger = logging.getLogger('heat.engine.manager')
|
logger = logging.getLogger('heat.engine.manager')
|
||||||
|
greenpool = eventlet.GreenPool()
|
||||||
|
|
||||||
|
|
||||||
class EngineManager(manager.Manager):
|
class EngineManager(manager.Manager):
|
||||||
|
@ -228,7 +230,7 @@ class EngineManager(manager.Manager):
|
||||||
new_pt = db_api.parsed_template_create(None, pt)
|
new_pt = db_api.parsed_template_create(None, pt)
|
||||||
|
|
||||||
stack.parsed_template_id = new_pt.id
|
stack.parsed_template_id = new_pt.id
|
||||||
stack.create()
|
greenpool.spawn_n(stack.create)
|
||||||
|
|
||||||
return {'stack': {'id': new_s.id, 'name': new_s.name,
|
return {'stack': {'id': new_s.id, 'name': new_s.name,
|
||||||
'CreationTime': str(new_s.created_at)}}
|
'CreationTime': str(new_s.created_at)}}
|
||||||
|
@ -297,7 +299,7 @@ class EngineManager(manager.Manager):
|
||||||
ps = parser.Stack(context, st.name,
|
ps = parser.Stack(context, st.name,
|
||||||
st.raw_template.parsed_template.template,
|
st.raw_template.parsed_template.template,
|
||||||
st.id, params)
|
st.id, params)
|
||||||
ps.delete()
|
greenpool.spawn_n(ps.delete)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Helper for list_events. It's here so we can use it in tests.
|
# Helper for list_events. It's here so we can use it in tests.
|
||||||
|
@ -464,7 +466,7 @@ class EngineManager(manager.Manager):
|
||||||
s.raw_template.parsed_template.template,
|
s.raw_template.parsed_template.template,
|
||||||
s.id)
|
s.id)
|
||||||
for a in wr.rule[action_map[new_state]]:
|
for a in wr.rule[action_map[new_state]]:
|
||||||
ps[a].alarm()
|
greenpool.spawn_n(ps[a].alarm)
|
||||||
|
|
||||||
wr.last_evaluated = now
|
wr.last_evaluated = now
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,6 @@ class Stack(object):
|
||||||
self.t = template
|
self.t = template
|
||||||
self.maps = self.t.get('Mappings', {})
|
self.maps = self.t.get('Mappings', {})
|
||||||
self.outputs = self.t.get('Outputs', {})
|
self.outputs = self.t.get('Outputs', {})
|
||||||
self.timeout = self.t.get('Timeout', None)
|
|
||||||
self.res = {}
|
self.res = {}
|
||||||
self.doc = None
|
self.doc = None
|
||||||
self.name = stack_name
|
self.name = stack_name
|
||||||
|
@ -170,99 +169,81 @@ class Stack(object):
|
||||||
logger.warn('Cant find parsed template to update %d' %
|
logger.warn('Cant find parsed template to update %d' %
|
||||||
self.parsed_template_id)
|
self.parsed_template_id)
|
||||||
|
|
||||||
def status_set(self, new_status, reason='change in resource state'):
|
def state_set(self, new_status, reason='change in resource state'):
|
||||||
|
|
||||||
self.t['stack_status'] = new_status
|
self.t['stack_status'] = new_status
|
||||||
self.t['stack_status_reason'] = reason
|
self.t['stack_status_reason'] = reason
|
||||||
self.update_parsed_template()
|
self.update_parsed_template()
|
||||||
|
|
||||||
def create_blocking(self):
|
def _timeout(self):
|
||||||
|
'''Return the stack creation timeout in seconds'''
|
||||||
|
if 'Timeout' in self.t:
|
||||||
|
try:
|
||||||
|
# Timeout is in minutes
|
||||||
|
return int(self.t['Timeout']) * 60
|
||||||
|
except ValueError:
|
||||||
|
logger.exception('create timeout conversion')
|
||||||
|
|
||||||
|
# Default to 1 hour
|
||||||
|
return 60 * 60
|
||||||
|
|
||||||
|
def create(self):
|
||||||
'''
|
'''
|
||||||
Create the stack and all of the resources.
|
Create the stack and all of the resources.
|
||||||
'''
|
'''
|
||||||
self.status_set(self.IN_PROGRESS, 'Stack creation started')
|
self.state_set(self.IN_PROGRESS, 'Stack creation started')
|
||||||
|
|
||||||
stack_status = self.CREATE_COMPLETE
|
stack_status = self.CREATE_COMPLETE
|
||||||
reason = 'Stack successfully created'
|
reason = 'Stack successfully created'
|
||||||
|
|
||||||
# Timeout is in minutes (default to 1 hour)
|
with eventlet.Timeout(self._timeout()) as tmo:
|
||||||
secs_tmo = 60 * 60
|
|
||||||
if self.timeout:
|
|
||||||
try:
|
try:
|
||||||
secs_tmo = int(self.timeout) * 60
|
for res in self:
|
||||||
except ValueError as ve:
|
if stack_status != self.CREATE_FAILED:
|
||||||
logger.exception('create timeout conversion')
|
result = res.create()
|
||||||
tmo = eventlet.Timeout(secs_tmo)
|
if result:
|
||||||
try:
|
stack_status = self.CREATE_FAILED
|
||||||
for res in self:
|
reason = 'Resource %s failed with: %s' % (str(res),
|
||||||
if stack_status != self.CREATE_FAILED:
|
result)
|
||||||
try:
|
|
||||||
res.create()
|
|
||||||
except Exception as ex:
|
|
||||||
logger.exception('create')
|
|
||||||
stack_status = self.CREATE_FAILED
|
|
||||||
reason = 'resource %s failed with: %s' % (res.name,
|
|
||||||
str(ex))
|
|
||||||
res.state_set(res.CREATE_FAILED, str(ex))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.update_parsed_template()
|
self.update_parsed_template()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.exception('update_parsed_template')
|
logger.exception('update_parsed_template')
|
||||||
|
|
||||||
|
else:
|
||||||
|
res.state_set(res.CREATE_FAILED,
|
||||||
|
'Stack creation aborted')
|
||||||
|
|
||||||
|
except eventlet.Timeout, t:
|
||||||
|
if t is tmo:
|
||||||
|
stack_status = self.CREATE_FAILED
|
||||||
|
reason = 'Timed out waiting for %s' % (res.name)
|
||||||
else:
|
else:
|
||||||
res.state_set(res.CREATE_FAILED)
|
# not my timeout
|
||||||
|
raise
|
||||||
|
|
||||||
except eventlet.Timeout, t:
|
self.state_set(stack_status, reason)
|
||||||
if t is not tmo:
|
|
||||||
# not my timeout
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
stack_status = self.CREATE_FAILED
|
|
||||||
reason = 'Timed out waiting for %s' % (res.name)
|
|
||||||
finally:
|
|
||||||
tmo.cancel()
|
|
||||||
|
|
||||||
self.status_set(stack_status, reason)
|
def delete(self):
|
||||||
|
|
||||||
def create(self):
|
|
||||||
|
|
||||||
pool = eventlet.GreenPool()
|
|
||||||
pool.spawn_n(self.create_blocking)
|
|
||||||
|
|
||||||
def delete_blocking(self):
|
|
||||||
'''
|
'''
|
||||||
Delete all of the resources, and then the stack itself.
|
Delete all of the resources, and then the stack itself.
|
||||||
'''
|
'''
|
||||||
failed = False
|
self.state_set(self.DELETE_IN_PROGRESS)
|
||||||
self.status_set(self.DELETE_IN_PROGRESS)
|
|
||||||
|
failures = []
|
||||||
|
|
||||||
for res in reversed(self):
|
for res in reversed(self):
|
||||||
try:
|
result = res.delete()
|
||||||
res.delete()
|
if result:
|
||||||
except Exception as ex:
|
failures.append(str(res))
|
||||||
failed = True
|
|
||||||
res.state_set(res.DELETE_FAILED)
|
|
||||||
logger.error('delete: %s' % str(ex))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
db_api.resource_get(self.context, res.id).delete()
|
|
||||||
except Exception as ex:
|
|
||||||
# don't fail the delete if the db entry has
|
|
||||||
# not been created yet.
|
|
||||||
if 'not found' not in str(ex):
|
|
||||||
failed = True
|
|
||||||
res.state_set(res.DELETE_FAILED)
|
|
||||||
logger.error('delete: %s' % str(ex))
|
|
||||||
|
|
||||||
self.status_set(failed and self.DELETE_FAILED or self.DELETE_COMPLETE)
|
if failures:
|
||||||
if not failed:
|
self.state_set(self.DELETE_FAILED,
|
||||||
|
'Failed to delete ' + ', '.join(failures))
|
||||||
|
else:
|
||||||
|
self.state_set(self.DELETE_COMPLETE, 'Deleted successfully')
|
||||||
db_api.stack_delete(self.context, self.name)
|
db_api.stack_delete(self.context, self.name)
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
pool = eventlet.GreenPool()
|
|
||||||
pool.spawn_n(self.delete_blocking)
|
|
||||||
|
|
||||||
def get_outputs(self):
|
def get_outputs(self):
|
||||||
outputs = self.resolve_runtime_data(self.outputs)
|
outputs = self.resolve_runtime_data(self.outputs)
|
||||||
|
|
||||||
|
@ -274,7 +255,7 @@ class Stack(object):
|
||||||
|
|
||||||
return [output_dict(key) for key in outputs]
|
return [output_dict(key) for key in outputs]
|
||||||
|
|
||||||
def restart_resource_blocking(self, resource_name):
|
def restart_resource(self, resource_name):
|
||||||
'''
|
'''
|
||||||
stop resource_name and all that depend on it
|
stop resource_name and all that depend on it
|
||||||
start resource_name and all that depend on it
|
start resource_name and all that depend on it
|
||||||
|
@ -295,7 +276,6 @@ class Stack(object):
|
||||||
re.delete()
|
re.delete()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
failed = True
|
failed = True
|
||||||
res.state_set(res.DELETE_FAILED)
|
|
||||||
logger.error('delete: %s' % str(ex))
|
logger.error('delete: %s' % str(ex))
|
||||||
|
|
||||||
for res in deps:
|
for res in deps:
|
||||||
|
@ -305,22 +285,16 @@ class Stack(object):
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.exception('create')
|
logger.exception('create')
|
||||||
failed = True
|
failed = True
|
||||||
res.state_set(res.CREATE_FAILED, str(ex))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.update_parsed_template()
|
self.update_parsed_template()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.exception('update_parsed_template')
|
logger.exception('update_parsed_template')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
res.state_set(res.CREATE_FAILED)
|
res.state_set(res.CREATE_FAILED)
|
||||||
# TODO(asalkeld) if any of this fails we Should
|
# TODO(asalkeld) if any of this fails we Should
|
||||||
# restart the whole stack
|
# restart the whole stack
|
||||||
|
|
||||||
def restart_resource(self, resource_name):
|
|
||||||
pool = eventlet.GreenPool()
|
|
||||||
pool.spawn_n(self.restart_resource_blocking, resource_name)
|
|
||||||
|
|
||||||
def _apply_user_parameters(self, parms):
|
def _apply_user_parameters(self, parms):
|
||||||
for p in parms:
|
for p in parms:
|
||||||
if 'Parameters.member.' in p and 'ParameterKey' in p:
|
if 'Parameters.member.' in p and 'ParameterKey' in p:
|
||||||
|
|
|
@ -32,7 +32,7 @@ logger = logging.getLogger('heat.engine.resources')
|
||||||
|
|
||||||
|
|
||||||
class Resource(object):
|
class Resource(object):
|
||||||
CREATE_IN_PROGRESS = 'CREATE_IN_PROGRESS'
|
CREATE_IN_PROGRESS = 'IN_PROGRESS'
|
||||||
CREATE_FAILED = 'CREATE_FAILED'
|
CREATE_FAILED = 'CREATE_FAILED'
|
||||||
CREATE_COMPLETE = 'CREATE_COMPLETE'
|
CREATE_COMPLETE = 'CREATE_COMPLETE'
|
||||||
DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
|
DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
|
||||||
|
@ -140,12 +140,31 @@ class Resource(object):
|
||||||
self.properties[p] = v
|
self.properties[p] = v
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
logger.info('creating %s name:%s' % (self.t['Type'], self.name))
|
'''
|
||||||
self.calculate_properties()
|
Create the resource. Subclasses should provide a handle_create() method
|
||||||
self.properties.validate()
|
to customise creation.
|
||||||
|
'''
|
||||||
|
if self.state in (self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE):
|
||||||
|
return 'Resource creation already requested'
|
||||||
|
|
||||||
|
logger.info('creating %s' % str(self))
|
||||||
|
|
||||||
|
self.state_set(self.CREATE_IN_PROGRESS)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.calculate_properties()
|
||||||
|
self.properties.validate()
|
||||||
|
if callable(getattr(self, 'handle_create', None)):
|
||||||
|
self.handle_create()
|
||||||
|
except Exception as ex:
|
||||||
|
logger.exception('create %s', str(self))
|
||||||
|
self.state_set(self.CREATE_FAILED, str(ex))
|
||||||
|
return str(ex)
|
||||||
|
else:
|
||||||
|
self.state_set(self.CREATE_COMPLETE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
logger.info('validating %s name:%s' % (self.t['Type'], self.name))
|
logger.info('Validating %s' % str(self))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.calculate_properties()
|
self.calculate_properties()
|
||||||
|
@ -153,59 +172,98 @@ class Resource(object):
|
||||||
return str(ex)
|
return str(ex)
|
||||||
return self.properties.validate()
|
return self.properties.validate()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
'''
|
||||||
|
Delete the resource. Subclasses should provide a handle_delete() method
|
||||||
|
to customise deletion.
|
||||||
|
'''
|
||||||
|
if self.state == self.DELETE_COMPLETE:
|
||||||
|
return
|
||||||
|
if self.state == self.DELETE_IN_PROGRESS:
|
||||||
|
return 'Resource deletion already in progress'
|
||||||
|
|
||||||
|
logger.info('deleting %s (inst:%s db_id:%s)' %
|
||||||
|
(str(self), self.instance_id, str(self.id)))
|
||||||
|
self.state_set(self.DELETE_IN_PROGRESS)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if callable(getattr(self, 'handle_delete', None)):
|
||||||
|
self.handle_delete()
|
||||||
|
except Exception as ex:
|
||||||
|
logger.exception('Delete %s', str(self))
|
||||||
|
self.state_set(self.DELETE_FAILED, str(ex))
|
||||||
|
return str(ex)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
db_api.resource_get(self.stack.context, self.id).delete()
|
||||||
|
except Exception as ex:
|
||||||
|
# Don't fail on delete if the db entry has
|
||||||
|
# not been created yet.
|
||||||
|
if 'not found' not in str(ex):
|
||||||
|
self.state_set(self.DELETE_FAILED)
|
||||||
|
logger.exception('Delete %s from DB' % str(self))
|
||||||
|
return str(ex)
|
||||||
|
|
||||||
|
self.state_set(self.DELETE_COMPLETE)
|
||||||
|
|
||||||
def instance_id_set(self, inst):
|
def instance_id_set(self, inst):
|
||||||
self.instance_id = inst
|
self.instance_id = inst
|
||||||
|
|
||||||
|
def _create_db(self):
|
||||||
|
'''Create the resource in the database'''
|
||||||
|
try:
|
||||||
|
rs = {'state': self.state,
|
||||||
|
'stack_id': self.stack.id,
|
||||||
|
'parsed_template_id': self.stack.parsed_template_id,
|
||||||
|
'nova_instance': self.instance_id,
|
||||||
|
'name': self.name,
|
||||||
|
'stack_name': self.stack.name}
|
||||||
|
|
||||||
|
new_rs = db_api.resource_create(self.stack.context, rs)
|
||||||
|
self.id = new_rs.id
|
||||||
|
|
||||||
|
if new_rs.stack:
|
||||||
|
new_rs.stack.update_and_save({'updated_at': datetime.utcnow()})
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('DB error %s' % str(ex))
|
||||||
|
|
||||||
|
def _add_event(self, new_state, reason):
|
||||||
|
'''Add a state change event to the database'''
|
||||||
|
self.calculate_properties()
|
||||||
|
ev = {'logical_resource_id': self.name,
|
||||||
|
'physical_resource_id': self.instance_id,
|
||||||
|
'stack_id': self.stack.id,
|
||||||
|
'stack_name': self.stack.name,
|
||||||
|
'resource_status': new_state,
|
||||||
|
'name': new_state,
|
||||||
|
'resource_status_reason': reason,
|
||||||
|
'resource_type': self.t['Type'],
|
||||||
|
'resource_properties': dict(self.properties)}
|
||||||
|
try:
|
||||||
|
db_api.event_create(self.stack.context, ev)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('DB error %s' % str(ex))
|
||||||
|
|
||||||
def state_set(self, new_state, reason="state changed"):
|
def state_set(self, new_state, reason="state changed"):
|
||||||
|
self.state, old_state = new_state, self.state
|
||||||
|
|
||||||
if self.id is not None:
|
if self.id is not None:
|
||||||
try:
|
try:
|
||||||
rs = db_api.resource_get(self.stack.context, self.id)
|
rs = db_api.resource_get(self.stack.context, self.id)
|
||||||
rs.update_and_save({'state': new_state,
|
rs.update_and_save({'state': self.state,
|
||||||
'nova_instance': self.instance_id})
|
'nova_instance': self.instance_id})
|
||||||
|
|
||||||
if rs.stack:
|
if rs.stack:
|
||||||
rs.stack.update_and_save({'updated_at': datetime.utcnow()})
|
rs.stack.update_and_save({'updated_at': datetime.utcnow()})
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warn('db error %s' % str(ex))
|
logger.error('DB error %s' % str(ex))
|
||||||
elif new_state in [self.CREATE_COMPLETE, self.CREATE_FAILED]:
|
|
||||||
try:
|
|
||||||
rs = {}
|
|
||||||
rs['state'] = new_state
|
|
||||||
rs['stack_id'] = self.stack.id
|
|
||||||
rs['parsed_template_id'] = self.stack.parsed_template_id
|
|
||||||
rs['nova_instance'] = self.instance_id
|
|
||||||
rs['name'] = self.name
|
|
||||||
rs['stack_name'] = self.stack.name
|
|
||||||
new_rs = db_api.resource_create(self.stack.context, rs)
|
|
||||||
self.id = new_rs.id
|
|
||||||
if new_rs.stack:
|
|
||||||
new_rs.stack.update_and_save({'updated_at':
|
|
||||||
datetime.utcnow()})
|
|
||||||
|
|
||||||
except Exception as ex:
|
elif new_state in (self.CREATE_COMPLETE, self.CREATE_FAILED):
|
||||||
logger.warn('db error %s' % str(ex))
|
self._create_db()
|
||||||
|
|
||||||
if new_state != self.state:
|
if new_state != old_state:
|
||||||
ev = {}
|
self._add_event(new_state, reason)
|
||||||
ev['logical_resource_id'] = self.name
|
|
||||||
ev['physical_resource_id'] = self.instance_id
|
|
||||||
ev['stack_id'] = self.stack.id
|
|
||||||
ev['stack_name'] = self.stack.name
|
|
||||||
ev['resource_status'] = new_state
|
|
||||||
ev['name'] = new_state
|
|
||||||
ev['resource_status_reason'] = reason
|
|
||||||
ev['resource_type'] = self.t['Type']
|
|
||||||
self.calculate_properties()
|
|
||||||
ev['resource_properties'] = dict(self.properties)
|
|
||||||
try:
|
|
||||||
db_api.event_create(self.stack.context, ev)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.warn('db error %s' % str(ex))
|
|
||||||
self.state = new_state
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
logger.info('deleting %s name:%s inst:%s db_id:%s' %
|
|
||||||
(self.t['Type'], self.name,
|
|
||||||
self.instance_id, str(self.id)))
|
|
||||||
|
|
||||||
def FnGetRefId(self):
|
def FnGetRefId(self):
|
||||||
'''
|
'''
|
||||||
|
@ -235,13 +293,6 @@ class Resource(object):
|
||||||
class GenericResource(Resource):
|
class GenericResource(Resource):
|
||||||
properties_schema = {}
|
properties_schema = {}
|
||||||
|
|
||||||
def __init__(self, name, json_snippet, stack):
|
def handle_create(self):
|
||||||
super(GenericResource, self).__init__(name, json_snippet, stack)
|
logger.warning('Creating generic resource (Type "%s")' %
|
||||||
|
self.t['Type'])
|
||||||
def create(self):
|
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(GenericResource, self).create()
|
|
||||||
logger.info('creating GenericResource %s' % self.name)
|
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
|
|
|
@ -34,11 +34,7 @@ class SecurityGroup(Resource):
|
||||||
def __init__(self, name, json_snippet, stack):
|
def __init__(self, name, json_snippet, stack):
|
||||||
super(SecurityGroup, self).__init__(name, json_snippet, stack)
|
super(SecurityGroup, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
Resource.create(self)
|
|
||||||
sec = None
|
sec = None
|
||||||
|
|
||||||
groups = self.nova().security_groups.list()
|
groups = self.nova().security_groups.list()
|
||||||
|
@ -69,21 +65,7 @@ class SecurityGroup(Resource):
|
||||||
# unexpected error
|
# unexpected error
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
def handle_delete(self):
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
'''
|
|
||||||
Validate the security group
|
|
||||||
'''
|
|
||||||
return Resource.validate(self)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
if self.instance_id is not None:
|
if self.instance_id is not None:
|
||||||
try:
|
try:
|
||||||
sec = self.nova().security_groups.get(self.instance_id)
|
sec = self.nova().security_groups.get(self.instance_id)
|
||||||
|
@ -99,7 +81,5 @@ class SecurityGroup(Resource):
|
||||||
self.nova().security_groups.delete(sec)
|
self.nova().security_groups.delete(sec)
|
||||||
self.instance_id = None
|
self.instance_id = None
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def FnGetRefId(self):
|
def FnGetRefId(self):
|
||||||
return unicode(self.name)
|
return unicode(self.name)
|
||||||
|
|
|
@ -44,12 +44,7 @@ class User(Resource):
|
||||||
def __init__(self, name, json_snippet, stack):
|
def __init__(self, name, json_snippet, stack):
|
||||||
super(User, self).__init__(name, json_snippet, stack)
|
super(User, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(User, self).create()
|
|
||||||
|
|
||||||
passwd = ''
|
passwd = ''
|
||||||
if 'LoginProfile' in self.properties:
|
if 'LoginProfile' in self.properties:
|
||||||
if self.properties['LoginProfile'] and \
|
if self.properties['LoginProfile'] and \
|
||||||
|
@ -62,14 +57,8 @@ class User(Resource):
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
enabled=True)
|
enabled=True)
|
||||||
self.instance_id_set(user.id)
|
self.instance_id_set(user.id)
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
super(User, self).delete()
|
|
||||||
|
|
||||||
|
def handle_delete(self):
|
||||||
try:
|
try:
|
||||||
user = self.keystone().users.get(DummyId(self.instance_id))
|
user = self.keystone().users.get(DummyId(self.instance_id))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -78,8 +67,6 @@ class User(Resource):
|
||||||
else:
|
else:
|
||||||
user.delete()
|
user.delete()
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def FnGetRefId(self):
|
def FnGetRefId(self):
|
||||||
return unicode(self.name)
|
return unicode(self.name)
|
||||||
|
|
||||||
|
@ -116,12 +103,7 @@ class AccessKey(Resource):
|
||||||
return u
|
return u
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(AccessKey, self).create()
|
|
||||||
|
|
||||||
user = self._user_from_name(self.properties['UserName'])
|
user = self._user_from_name(self.properties['UserName'])
|
||||||
if user is None:
|
if user is None:
|
||||||
raise exception.NotFound('could not find user %s' %
|
raise exception.NotFound('could not find user %s' %
|
||||||
|
@ -132,20 +114,11 @@ class AccessKey(Resource):
|
||||||
self.instance_id_set(cred.access)
|
self.instance_id_set(cred.access)
|
||||||
self._secret = cred.secret
|
self._secret = cred.secret
|
||||||
|
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
def handle_delete(self):
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
super(AccessKey, self).delete()
|
|
||||||
|
|
||||||
user = self._user_from_name(self.properties['UserName'])
|
user = self._user_from_name(self.properties['UserName'])
|
||||||
if user and self.instance_id:
|
if user and self.instance_id:
|
||||||
self.keystone().ec2.delete(user.id, self.instance_id)
|
self.keystone().ec2.delete(user.id, self.instance_id)
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def _secret_accesskey(self):
|
def _secret_accesskey(self):
|
||||||
'''
|
'''
|
||||||
Return the user's access key, fetching it from keystone if necessary
|
Return the user's access key, fetching it from keystone if necessary
|
||||||
|
|
|
@ -33,12 +33,7 @@ class Volume(Resource):
|
||||||
def __init__(self, name, json_snippet, stack):
|
def __init__(self, name, json_snippet, stack):
|
||||||
super(Volume, self).__init__(name, json_snippet, stack)
|
super(Volume, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(Volume, self).create()
|
|
||||||
|
|
||||||
vol = self.nova('volume').volumes.create(self.properties['Size'],
|
vol = self.nova('volume').volumes.create(self.properties['Size'],
|
||||||
display_name=self.name,
|
display_name=self.name,
|
||||||
display_description=self.name)
|
display_description=self.name)
|
||||||
|
@ -48,32 +43,17 @@ class Volume(Resource):
|
||||||
vol.get()
|
vol.get()
|
||||||
if vol.status == 'available':
|
if vol.status == 'available':
|
||||||
self.instance_id_set(vol.id)
|
self.instance_id_set(vol.id)
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
else:
|
else:
|
||||||
raise exception.Error(vol.status)
|
raise exception.Error(vol.status)
|
||||||
|
|
||||||
def validate(self):
|
def handle_delete(self):
|
||||||
'''
|
|
||||||
Validate the volume
|
|
||||||
'''
|
|
||||||
return Resource.validate(self)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.instance_id is not None:
|
if self.instance_id is not None:
|
||||||
vol = self.nova('volume').volumes.get(self.instance_id)
|
vol = self.nova('volume').volumes.get(self.instance_id)
|
||||||
if vol.status == 'in-use':
|
if vol.status == 'in-use':
|
||||||
logger.warn('cant delete volume when in-use')
|
logger.warn('cant delete volume when in-use')
|
||||||
raise exception.Error("Volume in use")
|
raise exception.Error("Volume in use")
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
if self.instance_id is not None:
|
|
||||||
self.nova('volume').volumes.delete(self.instance_id)
|
self.nova('volume').volumes.delete(self.instance_id)
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachment(Resource):
|
class VolumeAttachment(Resource):
|
||||||
|
@ -88,12 +68,7 @@ class VolumeAttachment(Resource):
|
||||||
def __init__(self, name, json_snippet, stack):
|
def __init__(self, name, json_snippet, stack):
|
||||||
super(VolumeAttachment, self).__init__(name, json_snippet, stack)
|
super(VolumeAttachment, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
super(VolumeAttachment, self).create()
|
|
||||||
|
|
||||||
server_id = self.properties['InstanceId']
|
server_id = self.properties['InstanceId']
|
||||||
volume_id = self.properties['VolumeId']
|
volume_id = self.properties['VolumeId']
|
||||||
logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
|
logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
|
||||||
|
@ -109,22 +84,10 @@ class VolumeAttachment(Resource):
|
||||||
vol.get()
|
vol.get()
|
||||||
if vol.status == 'in-use':
|
if vol.status == 'in-use':
|
||||||
self.instance_id_set(va.id)
|
self.instance_id_set(va.id)
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
else:
|
else:
|
||||||
raise exception.Error(vol.status)
|
raise exception.Error(vol.status)
|
||||||
|
|
||||||
def validate(self):
|
def handle_delete(self):
|
||||||
'''
|
|
||||||
Validate the mountpoint device
|
|
||||||
'''
|
|
||||||
return Resource.validate(self)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
|
|
||||||
server_id = self.properties['InstanceId']
|
server_id = self.properties['InstanceId']
|
||||||
volume_id = self.properties['VolumeId']
|
volume_id = self.properties['VolumeId']
|
||||||
logger.info('VolumeAttachment un-attaching %s %s' %
|
logger.info('VolumeAttachment un-attaching %s %s' %
|
||||||
|
@ -146,5 +109,3 @@ class VolumeAttachment(Resource):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
vol.get()
|
vol.get()
|
||||||
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
|
@ -37,27 +37,12 @@ class WaitConditionHandle(Resource):
|
||||||
def __init__(self, name, json_snippet, stack):
|
def __init__(self, name, json_snippet, stack):
|
||||||
super(WaitConditionHandle, self).__init__(name, json_snippet, stack)
|
super(WaitConditionHandle, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
Resource.create(self)
|
|
||||||
|
|
||||||
self.instance_id = '%s/stacks/%s/resources/%s' % \
|
self.instance_id = '%s/stacks/%s/resources/%s' % \
|
||||||
(self.stack.metadata_server,
|
(self.stack.metadata_server,
|
||||||
self.stack.name,
|
self.stack.name,
|
||||||
self.name)
|
self.name)
|
||||||
|
|
||||||
self.state_set(self.CREATE_COMPLETE)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
|
|
||||||
class WaitCondition(Resource):
|
class WaitCondition(Resource):
|
||||||
properties_schema = {'Handle': {'Type': 'String',
|
properties_schema = {'Handle': {'Type': 'String',
|
||||||
|
@ -82,12 +67,7 @@ class WaitCondition(Resource):
|
||||||
self.resource_id = handle_url.split('/')[-1]
|
self.resource_id = handle_url.split('/')[-1]
|
||||||
return self.resource_id
|
return self.resource_id
|
||||||
|
|
||||||
def create(self):
|
def handle_create(self):
|
||||||
if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
|
|
||||||
return
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
Resource.create(self)
|
|
||||||
|
|
||||||
self._get_handle_resource_id()
|
self._get_handle_resource_id()
|
||||||
|
|
||||||
# keep polling our Metadata to see if the cfn-signal has written
|
# keep polling our Metadata to see if the cfn-signal has written
|
||||||
|
@ -128,23 +108,9 @@ class WaitCondition(Resource):
|
||||||
finally:
|
finally:
|
||||||
tmo.cancel()
|
tmo.cancel()
|
||||||
|
|
||||||
if status == 'SUCCESS':
|
if status != 'SUCCESS':
|
||||||
self.state_set(self.CREATE_COMPLETE,
|
|
||||||
'%s: %s' % (self.name, reason))
|
|
||||||
elif status == 'DELETED':
|
|
||||||
# don't try write to the db as it's gone.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise exception.Error(reason)
|
raise exception.Error(reason)
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.state_set(self.DELETE_IN_PROGRESS)
|
|
||||||
Resource.delete(self)
|
|
||||||
self.state_set(self.DELETE_COMPLETE)
|
|
||||||
|
|
||||||
def FnGetAtt(self, key):
|
def FnGetAtt(self, key):
|
||||||
res = None
|
res = None
|
||||||
self._get_handle_resource_id()
|
self._get_handle_resource_id()
|
||||||
|
|
|
@ -59,7 +59,7 @@ class stacksTest(unittest.TestCase):
|
||||||
def test_wordpress_single_instance_stack_create(self):
|
def test_wordpress_single_instance_stack_create(self):
|
||||||
stack = self.start_wordpress_stack('test_stack')
|
stack = self.start_wordpress_stack('test_stack')
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
stack.create_blocking()
|
stack.create()
|
||||||
assert(stack.resources['WebServer'] is not None)
|
assert(stack.resources['WebServer'] is not None)
|
||||||
assert(stack.resources['WebServer'].instance_id > 0)
|
assert(stack.resources['WebServer'].instance_id > 0)
|
||||||
assert(stack.resources['WebServer'].ipaddress != '0.0.0.0')
|
assert(stack.resources['WebServer'].ipaddress != '0.0.0.0')
|
||||||
|
@ -85,10 +85,10 @@ class stacksTest(unittest.TestCase):
|
||||||
pt['template'] = stack.t
|
pt['template'] = stack.t
|
||||||
pt['raw_template_id'] = new_rt.id
|
pt['raw_template_id'] = new_rt.id
|
||||||
new_pt = db_api.parsed_template_create(None, pt)
|
new_pt = db_api.parsed_template_create(None, pt)
|
||||||
stack.create_blocking()
|
stack.create()
|
||||||
assert(stack.resources['WebServer'] is not None)
|
assert(stack.resources['WebServer'] is not None)
|
||||||
assert(stack.resources['WebServer'].instance_id > 0)
|
assert(stack.resources['WebServer'].instance_id > 0)
|
||||||
stack.delete_blocking()
|
stack.delete()
|
||||||
assert(stack.resources['WebServer'].state == 'DELETE_COMPLETE')
|
assert(stack.resources['WebServer'].state == 'DELETE_COMPLETE')
|
||||||
assert(stack.t['stack_status'] == 'DELETE_COMPLETE')
|
assert(stack.t['stack_status'] == 'DELETE_COMPLETE')
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class stacksTest(unittest.TestCase):
|
||||||
pt['template'] = stack.t
|
pt['template'] = stack.t
|
||||||
pt['raw_template_id'] = new_rt.id
|
pt['raw_template_id'] = new_rt.id
|
||||||
new_pt = db_api.parsed_template_create(None, pt)
|
new_pt = db_api.parsed_template_create(None, pt)
|
||||||
stack.create_blocking()
|
stack.create()
|
||||||
assert(stack.resources['WebServer'] is not None)
|
assert(stack.resources['WebServer'] is not None)
|
||||||
assert(stack.resources['WebServer'].instance_id > 0)
|
assert(stack.resources['WebServer'].instance_id > 0)
|
||||||
|
|
||||||
|
@ -123,9 +123,8 @@ class stacksTest(unittest.TestCase):
|
||||||
result = m.parse_event(ev)
|
result = m.parse_event(ev)
|
||||||
assert(result['EventId'] > 0)
|
assert(result['EventId'] > 0)
|
||||||
assert(result['StackName'] == "test_event_list_stack")
|
assert(result['StackName'] == "test_event_list_stack")
|
||||||
# This is one of CREATE_COMPLETE or CREATE_IN_PROGRESS,
|
assert(result['ResourceStatus'] in ('IN_PROGRESS',
|
||||||
# just did this to make it easy.
|
'CREATE_COMPLETE'))
|
||||||
assert(result['ResourceStatus'].find('CREATE') != -1)
|
|
||||||
assert(result['ResourceType'] == 'AWS::EC2::Instance')
|
assert(result['ResourceType'] == 'AWS::EC2::Instance')
|
||||||
assert(result['ResourceStatusReason'] == 'state changed')
|
assert(result['ResourceStatusReason'] == 'state changed')
|
||||||
assert(result['LogicalResourceId'] == 'WebServer')
|
assert(result['LogicalResourceId'] == 'WebServer')
|
||||||
|
@ -166,7 +165,7 @@ class stacksTest(unittest.TestCase):
|
||||||
new_pt = db_api.parsed_template_create(ctx, pt)
|
new_pt = db_api.parsed_template_create(ctx, pt)
|
||||||
instances.Instance.nova().AndReturn(self.fc)
|
instances.Instance.nova().AndReturn(self.fc)
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
stack.create_blocking()
|
stack.create()
|
||||||
|
|
||||||
f = open("%s/WordPress_Single_Instance_gold.template" % self.path)
|
f = open("%s/WordPress_Single_Instance_gold.template" % self.path)
|
||||||
t = json.loads(f.read())
|
t = json.loads(f.read())
|
||||||
|
|
Loading…
Reference in New Issue