Split the resourses up into seperate files.
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
parent
9de4d852dc
commit
20fc3c76a2
|
@ -0,0 +1,126 @@
|
|||
# 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 eventlet
|
||||
import logging
|
||||
import os
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.resources import Resource
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class ElasticIp(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(ElasticIp, self).__init__(name, json_snippet, stack)
|
||||
self.ipaddress = ''
|
||||
|
||||
if 'Domain' in self.t['Properties']:
|
||||
logger.warn('*** can\'t support Domain %s yet' % \
|
||||
(self.t['Properties']['Domain']))
|
||||
|
||||
def create(self):
|
||||
"""Allocate a floating IP for the current tenant."""
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(ElasticIp, self).create()
|
||||
|
||||
ips = self.nova().floating_ips.create()
|
||||
logger.info('ElasticIp create %s' % str(ips))
|
||||
self.ipaddress = ips.ip
|
||||
self.instance_id_set(ips.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def reload(self):
|
||||
'''
|
||||
get the ipaddress here
|
||||
'''
|
||||
if self.instance_id != None:
|
||||
try:
|
||||
ips = self.nova().floating_ips.get(self.instance_id)
|
||||
self.ipaddress = ips.ip
|
||||
except Exception as ex:
|
||||
logger.warn("Error getting floating IPs: %s" % str(ex))
|
||||
|
||||
Resource.reload(self)
|
||||
|
||||
def delete(self):
|
||||
"""De-allocate a floating IP."""
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
self.nova().floating_ips.delete(self.instance_id)
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
def FnGetRefId(self):
|
||||
return unicode(self.ipaddress)
|
||||
|
||||
def FnGetAtt(self, key):
|
||||
if key == 'AllocationId':
|
||||
return unicode(self.instance_id)
|
||||
else:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
|
||||
class ElasticIpAssociation(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(ElasticIpAssociation, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def FnGetRefId(self):
|
||||
if not 'EIP' in self.t['Properties']:
|
||||
return unicode('0.0.0.0')
|
||||
else:
|
||||
return unicode(self.t['Properties']['EIP'])
|
||||
|
||||
def create(self):
|
||||
"""Add a floating IP address to a server."""
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(ElasticIpAssociation, self).create()
|
||||
logger.debug('ElasticIpAssociation %s.add_floating_ip(%s)' % \
|
||||
(self.t['Properties']['InstanceId'],
|
||||
self.t['Properties']['EIP']))
|
||||
|
||||
server = self.nova().servers.get(self.t['Properties']['InstanceId'])
|
||||
server.add_floating_ip(self.t['Properties']['EIP'])
|
||||
self.instance_id_set(self.t['Properties']['EIP'])
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def delete(self):
|
||||
"""Remove a floating IP address from a server."""
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
server = self.nova().servers.get(self.t['Properties']['InstanceId'])
|
||||
server.remove_floating_ip(self.t['Properties']['EIP'])
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
# 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 base64
|
||||
import eventlet
|
||||
import logging
|
||||
import os
|
||||
import string
|
||||
import json
|
||||
import sys
|
||||
from email import encoders
|
||||
from email.message import Message
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from novaclient.exceptions import NotFound
|
||||
|
||||
from heat.engine.resources import Resource
|
||||
from heat.common import exception
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
# If ../heat/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
cloudinit_path = '%s/heat/%s/' % (possible_topdir, "cloudinit")
|
||||
else:
|
||||
for p in sys.path:
|
||||
if 'heat' in p:
|
||||
cloudinit_path = '%s/heat/%s/' % (p, "cloudinit")
|
||||
break
|
||||
|
||||
|
||||
|
||||
class Instance(Resource):
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(Instance, self).__init__(name, json_snippet, stack)
|
||||
self.ipaddress = '0.0.0.0'
|
||||
|
||||
if not 'AvailabilityZone' in self.t['Properties']:
|
||||
self.t['Properties']['AvailabilityZone'] = 'nova'
|
||||
self.itype_oflavor = {'t1.micro': 'm1.tiny',
|
||||
'm1.small': 'm1.small',
|
||||
'm1.medium': 'm1.medium',
|
||||
'm1.large': 'm1.large',
|
||||
'm1.xlarge': 'm1.tiny', # TODO(sdake)
|
||||
'm2.xlarge': 'm1.xlarge',
|
||||
'm2.2xlarge': 'm1.large',
|
||||
'm2.4xlarge': 'm1.large',
|
||||
'c1.medium': 'm1.medium',
|
||||
'c1.4xlarge': 'm1.large',
|
||||
'cc2.8xlarge': 'm1.large',
|
||||
'cg1.4xlarge': 'm1.large'}
|
||||
|
||||
def FnGetAtt(self, key):
|
||||
|
||||
res = None
|
||||
if key == 'AvailabilityZone':
|
||||
res = self.t['Properties']['AvailabilityZone']
|
||||
elif key == 'PublicIp':
|
||||
res = self.ipaddress
|
||||
else:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
# TODO(asalkeld) PrivateDnsName, PublicDnsName & PrivateIp
|
||||
|
||||
logger.info('%s.GetAtt(%s) == %s' % (self.name, key, res))
|
||||
return unicode(res)
|
||||
|
||||
def _build_userdata(self, userdata):
|
||||
# Build mime multipart data blob for cloudinit userdata
|
||||
mime_blob = MIMEMultipart()
|
||||
fp = open('%s/%s' % (cloudinit_path, 'config'), 'r')
|
||||
msg = MIMEText(fp.read(), _subtype='cloud-config')
|
||||
fp.close()
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='cloud-config')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
fp = open('%s/%s' % (cloudinit_path, 'part-handler.py'), 'r')
|
||||
msg = MIMEText(fp.read(), _subtype='part-handler')
|
||||
fp.close()
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='part-handler.py')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
msg = MIMEText(json.dumps(self.t['Metadata']),
|
||||
_subtype='x-cfninitdata')
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='cfn-init-data')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
msg = MIMEText(userdata, _subtype='x-shellscript')
|
||||
msg.add_header('Content-Disposition', 'attachment', filename='startup')
|
||||
mime_blob.attach(msg)
|
||||
return mime_blob.as_string()
|
||||
|
||||
def create(self):
|
||||
def _null_callback(p, n, out):
|
||||
"""
|
||||
Method to silence the default M2Crypto.RSA.gen_key output.
|
||||
"""
|
||||
pass
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
Resource.create(self)
|
||||
props = self.t['Properties']
|
||||
if not 'KeyName' in props:
|
||||
raise exception.UserParameterMissing(key='KeyName')
|
||||
if not 'InstanceType' in props:
|
||||
raise exception.UserParameterMissing(key='InstanceType')
|
||||
if not 'ImageId' in props:
|
||||
raise exception.UserParameterMissing(key='ImageId')
|
||||
|
||||
security_groups = props.get('SecurityGroups')
|
||||
|
||||
userdata = self.t['Properties']['UserData']
|
||||
|
||||
flavor = self.itype_oflavor[self.t['Properties']['InstanceType']]
|
||||
key_name = self.t['Properties']['KeyName']
|
||||
|
||||
keypairs = self.nova().keypairs.list()
|
||||
key_exists = False
|
||||
for k in keypairs:
|
||||
if k.name == key_name:
|
||||
# cool it exists
|
||||
key_exists = True
|
||||
break
|
||||
if not key_exists:
|
||||
raise exception.UserKeyPairMissing(key_name=key_name)
|
||||
|
||||
image_name = self.t['Properties']['ImageId']
|
||||
image_id = None
|
||||
image_list = self.nova().images.list()
|
||||
for o in image_list:
|
||||
if o.name == image_name:
|
||||
image_id = o.id
|
||||
|
||||
if image_id is None:
|
||||
logger.info("Image %s was not found in glance" % image_name)
|
||||
raise exception.ImageNotFound(image_name=image_name)
|
||||
|
||||
flavor_list = self.nova().flavors.list()
|
||||
for o in flavor_list:
|
||||
if o.name == flavor:
|
||||
flavor_id = o.id
|
||||
|
||||
server_userdata = self._build_userdata(userdata)
|
||||
server = self.nova().servers.create(name=self.name, image=image_id,
|
||||
flavor=flavor_id,
|
||||
key_name=key_name,
|
||||
security_groups=security_groups,
|
||||
userdata=server_userdata)
|
||||
while server.status == 'BUILD':
|
||||
server.get()
|
||||
eventlet.sleep(1)
|
||||
if server.status == 'ACTIVE':
|
||||
self.instance_id_set(server.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
# just record the first ipaddress
|
||||
for n in server.networks:
|
||||
self.ipaddress = server.networks[n][0]
|
||||
break
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def reload(self):
|
||||
'''
|
||||
re-read the server's ipaddress so FnGetAtt works.
|
||||
'''
|
||||
try:
|
||||
server = self.nova().servers.get(self.instance_id)
|
||||
for n in server.networks:
|
||||
self.ipaddress = server.networks[n][0]
|
||||
except NotFound:
|
||||
self.ipaddress = '0.0.0.0'
|
||||
|
||||
Resource.reload(self)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
server = self.nova().servers.get(self.instance_id)
|
||||
server.delete()
|
||||
self.instance_id = None
|
||||
self.state_set(self.DELETE_COMPLETE)
|
|
@ -19,9 +19,15 @@ import logging
|
|||
|
||||
from heat.common import exception
|
||||
from heat.engine import resources
|
||||
from heat.engine import instance
|
||||
from heat.engine import volume
|
||||
from heat.engine import eip
|
||||
from heat.engine import security_group
|
||||
from heat.engine import wait_condition
|
||||
|
||||
from heat.db import api as db_api
|
||||
|
||||
logger = logging.getLogger('heat.engine.parser')
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class Stack(object):
|
||||
|
@ -42,6 +48,7 @@ class Stack(object):
|
|||
self.doc = None
|
||||
self.name = stack_name
|
||||
self.parsed_template_id = 0
|
||||
self.metadata_server = 'http://10.0.0.1'
|
||||
|
||||
self.parms['AWS::StackName'] = {"Description": "AWS StackName",
|
||||
"Type": "String",
|
||||
|
@ -66,22 +73,23 @@ class Stack(object):
|
|||
for r in self.t['Resources']:
|
||||
type = self.t['Resources'][r]['Type']
|
||||
if type == 'AWS::EC2::Instance':
|
||||
self.resources[r] = resources.Instance(r,
|
||||
self.resources[r] = instance.Instance(r,
|
||||
self.t['Resources'][r], self)
|
||||
elif type == 'AWS::EC2::Volume':
|
||||
self.resources[r] = resources.Volume(r,
|
||||
self.resources[r] = volume.Volume(r,
|
||||
self.t['Resources'][r], self)
|
||||
elif type == 'AWS::EC2::VolumeAttachment':
|
||||
self.resources[r] = resources.VolumeAttachment(r,
|
||||
self.resources[r] = volume.VolumeAttachment(r,
|
||||
self.t['Resources'][r], self)
|
||||
elif type == 'AWS::EC2::EIP':
|
||||
self.resources[r] = resources.ElasticIp(r,
|
||||
self.resources[r] = eip.ElasticIp(r,
|
||||
self.t['Resources'][r], self)
|
||||
elif type == 'AWS::EC2::EIPAssociation':
|
||||
self.resources[r] = resources.ElasticIpAssociation(r,
|
||||
self.resources[r] = eip.ElasticIpAssociation(r,
|
||||
self.t['Resources'][r], self)
|
||||
elif type == 'AWS::EC2::SecurityGroup':
|
||||
self.resources[r] = resources.SecurityGroup(r,
|
||||
self.resources[r] = security_group.SecurityGroup(r,
|
||||
self.t['Resources'][r], self)
|
||||
self.t['Resources'][r], self)
|
||||
else:
|
||||
self.resources[r] = resources.GenericResource(r,
|
||||
|
|
|
@ -20,11 +20,6 @@ import os
|
|||
import string
|
||||
import json
|
||||
import sys
|
||||
from email import encoders
|
||||
from email.message import Message
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
from novaclient.v1_1 import client
|
||||
from novaclient.exceptions import BadRequest
|
||||
|
@ -34,7 +29,7 @@ from heat.common import exception
|
|||
from heat.db import api as db_api
|
||||
from heat.common.config import HeatEngineConfigOpts
|
||||
|
||||
logger = logging.getLogger('heat.engine.resources')
|
||||
logger = logging.getLogger(__file__)
|
||||
# If ../heat/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
|
@ -203,442 +198,3 @@ class GenericResource(Resource):
|
|||
super(GenericResource, self).create()
|
||||
logger.info('creating GenericResource %s' % self.name)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
|
||||
class SecurityGroup(Resource):
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(SecurityGroup, self).__init__(name, json_snippet, stack)
|
||||
self.instance_id = ''
|
||||
|
||||
if 'GroupDescription' in self.t['Properties']:
|
||||
self.description = self.t['Properties']['GroupDescription']
|
||||
else:
|
||||
self.description = ''
|
||||
|
||||
def create(self):
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
Resource.create(self)
|
||||
sec = None
|
||||
|
||||
groups = self.nova().security_groups.list()
|
||||
for group in groups:
|
||||
if group.name == self.name:
|
||||
sec = group
|
||||
break
|
||||
|
||||
if not sec:
|
||||
sec = self.nova().security_groups.create(self.name,
|
||||
self.description)
|
||||
|
||||
self.instance_id_set(sec.id)
|
||||
|
||||
if 'SecurityGroupIngress' in self.t['Properties']:
|
||||
rules_client = self.nova().security_group_rules
|
||||
for i in self.t['Properties']['SecurityGroupIngress']:
|
||||
try:
|
||||
rule = rules_client.create(sec.id,
|
||||
i['IpProtocol'],
|
||||
i['FromPort'],
|
||||
i['ToPort'],
|
||||
i['CidrIp'])
|
||||
except BadRequest as ex:
|
||||
if ex.message.find('already exists') >= 0:
|
||||
# no worries, the rule is already there
|
||||
pass
|
||||
else:
|
||||
# unexpected error
|
||||
raise
|
||||
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
sec = self.nova().security_groups.get(self.instance_id)
|
||||
|
||||
for rule in sec.rules:
|
||||
self.nova().security_group_rules.delete(rule['id'])
|
||||
|
||||
self.nova().security_groups.delete(sec)
|
||||
self.instance_id = None
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
def FnGetRefId(self):
|
||||
return unicode(self.name)
|
||||
|
||||
|
||||
class ElasticIp(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(ElasticIp, self).__init__(name, json_snippet, stack)
|
||||
self.ipaddress = ''
|
||||
|
||||
if 'Domain' in self.t['Properties']:
|
||||
logger.warn('*** can\'t support Domain %s yet' % \
|
||||
(self.t['Properties']['Domain']))
|
||||
|
||||
def create(self):
|
||||
"""Allocate a floating IP for the current tenant."""
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(ElasticIp, self).create()
|
||||
|
||||
ips = self.nova().floating_ips.create()
|
||||
logger.info('ElasticIp create %s' % str(ips))
|
||||
self.ipaddress = ips.ip
|
||||
self.instance_id_set(ips.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def reload(self):
|
||||
'''
|
||||
get the ipaddress here
|
||||
'''
|
||||
if self.instance_id != None:
|
||||
try:
|
||||
ips = self.nova().floating_ips.get(self.instance_id)
|
||||
self.ipaddress = ips.ip
|
||||
except Exception as ex:
|
||||
logger.warn("Error getting floating IPs: %s" % str(ex))
|
||||
|
||||
Resource.reload(self)
|
||||
|
||||
def delete(self):
|
||||
"""De-allocate a floating IP."""
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
self.nova().floating_ips.delete(self.instance_id)
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
def FnGetRefId(self):
|
||||
return unicode(self.ipaddress)
|
||||
|
||||
def FnGetAtt(self, key):
|
||||
if key == 'AllocationId':
|
||||
return unicode(self.instance_id)
|
||||
else:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
|
||||
class ElasticIpAssociation(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(ElasticIpAssociation, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def FnGetRefId(self):
|
||||
if not 'EIP' in self.t['Properties']:
|
||||
return unicode('0.0.0.0')
|
||||
else:
|
||||
return unicode(self.t['Properties']['EIP'])
|
||||
|
||||
def create(self):
|
||||
"""Add a floating IP address to a server."""
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(ElasticIpAssociation, self).create()
|
||||
logger.debug('ElasticIpAssociation %s.add_floating_ip(%s)' % \
|
||||
(self.t['Properties']['InstanceId'],
|
||||
self.t['Properties']['EIP']))
|
||||
|
||||
server = self.nova().servers.get(self.t['Properties']['InstanceId'])
|
||||
server.add_floating_ip(self.t['Properties']['EIP'])
|
||||
self.instance_id_set(self.t['Properties']['EIP'])
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def delete(self):
|
||||
"""Remove a floating IP address from a server."""
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
server = self.nova().servers.get(self.t['Properties']['InstanceId'])
|
||||
server.remove_floating_ip(self.t['Properties']['EIP'])
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
|
||||
class Volume(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(Volume, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def create(self):
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(Volume, self).create()
|
||||
|
||||
vol = self.nova('volume').volumes.create(self.t['Properties']['Size'],
|
||||
display_name=self.name,
|
||||
display_description=self.name)
|
||||
|
||||
while vol.status == 'creating':
|
||||
eventlet.sleep(1)
|
||||
vol.get()
|
||||
if vol.status == 'available':
|
||||
self.instance_id_set(vol.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
if self.instance_id != None:
|
||||
vol = self.nova('volume').volumes.get(self.instance_id)
|
||||
if vol.status == 'in-use':
|
||||
logger.warn('cant delete volume when in-use')
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
self.nova('volume').volumes.delete(self.instance_id)
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
|
||||
class VolumeAttachment(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(VolumeAttachment, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def create(self):
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(VolumeAttachment, self).create()
|
||||
|
||||
server_id = self.t['Properties']['InstanceId']
|
||||
volume_id = self.t['Properties']['VolumeId']
|
||||
logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
|
||||
(server_id, volume_id, self.t['Properties']['Device']))
|
||||
volapi = self.nova().volumes
|
||||
va = volapi.create_server_volume(server_id=server_id,
|
||||
volume_id=volume_id,
|
||||
device=self.t['Properties']['Device'])
|
||||
|
||||
vol = self.nova('volume').volumes.get(va.id)
|
||||
while vol.status == 'available' or vol.status == 'attaching':
|
||||
eventlet.sleep(1)
|
||||
vol.get()
|
||||
if vol.status == 'in-use':
|
||||
self.instance_id_set(va.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
server_id = self.t['Properties']['InstanceId']
|
||||
volume_id = self.t['Properties']['VolumeId']
|
||||
logger.info('VolumeAttachment un-attaching %s %s' % \
|
||||
(server_id, volume_id))
|
||||
|
||||
volapi = self.nova().volumes
|
||||
volapi.delete_server_volume(server_id,
|
||||
volume_id)
|
||||
|
||||
vol = self.nova('volume').volumes.get(volume_id)
|
||||
logger.info('un-attaching %s, status %s' % (volume_id, vol.status))
|
||||
while vol.status == 'in-use':
|
||||
logger.info('trying to un-attach %s, but still %s' %
|
||||
(volume_id, vol.status))
|
||||
eventlet.sleep(1)
|
||||
try:
|
||||
volapi.delete_server_volume(server_id,
|
||||
volume_id)
|
||||
except Exception:
|
||||
pass
|
||||
vol.get()
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
|
||||
class Instance(Resource):
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(Instance, self).__init__(name, json_snippet, stack)
|
||||
self.ipaddress = '0.0.0.0'
|
||||
|
||||
if not 'AvailabilityZone' in self.t['Properties']:
|
||||
self.t['Properties']['AvailabilityZone'] = 'nova'
|
||||
self.itype_oflavor = {'t1.micro': 'm1.tiny',
|
||||
'm1.small': 'm1.small',
|
||||
'm1.medium': 'm1.medium',
|
||||
'm1.large': 'm1.large',
|
||||
'm1.xlarge': 'm1.tiny', # TODO(sdake)
|
||||
'm2.xlarge': 'm1.xlarge',
|
||||
'm2.2xlarge': 'm1.large',
|
||||
'm2.4xlarge': 'm1.large',
|
||||
'c1.medium': 'm1.medium',
|
||||
'c1.4xlarge': 'm1.large',
|
||||
'cc2.8xlarge': 'm1.large',
|
||||
'cg1.4xlarge': 'm1.large'}
|
||||
|
||||
def FnGetAtt(self, key):
|
||||
|
||||
res = None
|
||||
if key == 'AvailabilityZone':
|
||||
res = self.t['Properties']['AvailabilityZone']
|
||||
elif key == 'PublicIp':
|
||||
res = self.ipaddress
|
||||
else:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
# TODO(asalkeld) PrivateDnsName, PublicDnsName & PrivateIp
|
||||
|
||||
logger.info('%s.GetAtt(%s) == %s' % (self.name, key, res))
|
||||
return unicode(res)
|
||||
|
||||
def _build_userdata(self, userdata):
|
||||
# Build mime multipart data blob for cloudinit userdata
|
||||
mime_blob = MIMEMultipart()
|
||||
fp = open('%s/%s' % (cloudinit_path, 'config'), 'r')
|
||||
msg = MIMEText(fp.read(), _subtype='cloud-config')
|
||||
fp.close()
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='cloud-config')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
fp = open('%s/%s' % (cloudinit_path, 'part-handler.py'), 'r')
|
||||
msg = MIMEText(fp.read(), _subtype='part-handler')
|
||||
fp.close()
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='part-handler.py')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
msg = MIMEText(json.dumps(self.t['Metadata']),
|
||||
_subtype='x-cfninitdata')
|
||||
msg.add_header('Content-Disposition', 'attachment',
|
||||
filename='cfn-init-data')
|
||||
mime_blob.attach(msg)
|
||||
|
||||
msg = MIMEText(userdata, _subtype='x-shellscript')
|
||||
msg.add_header('Content-Disposition', 'attachment', filename='startup')
|
||||
mime_blob.attach(msg)
|
||||
return mime_blob.as_string()
|
||||
|
||||
def create(self):
|
||||
def _null_callback(p, n, out):
|
||||
"""
|
||||
Method to silence the default M2Crypto.RSA.gen_key output.
|
||||
"""
|
||||
pass
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
Resource.create(self)
|
||||
props = self.t['Properties']
|
||||
if not 'KeyName' in props:
|
||||
raise exception.UserParameterMissing(key='KeyName')
|
||||
if not 'InstanceType' in props:
|
||||
raise exception.UserParameterMissing(key='InstanceType')
|
||||
if not 'ImageId' in props:
|
||||
raise exception.UserParameterMissing(key='ImageId')
|
||||
|
||||
security_groups = props.get('SecurityGroups')
|
||||
|
||||
userdata = self.t['Properties']['UserData']
|
||||
|
||||
flavor = self.itype_oflavor[self.t['Properties']['InstanceType']]
|
||||
distro_name = self.stack.parameter_get('LinuxDistribution')
|
||||
key_name = self.t['Properties']['KeyName']
|
||||
|
||||
keypairs = self.nova().keypairs.list()
|
||||
key_exists = False
|
||||
for k in keypairs:
|
||||
if k.name == key_name:
|
||||
# cool it exists
|
||||
key_exists = True
|
||||
break
|
||||
if not key_exists:
|
||||
raise exception.UserKeyPairMissing(key_name=key_name)
|
||||
|
||||
image_name = self.t['Properties']['ImageId']
|
||||
image_id = None
|
||||
image_list = self.nova().images.list()
|
||||
for o in image_list:
|
||||
if o.name == image_name:
|
||||
image_id = o.id
|
||||
|
||||
if image_id is None:
|
||||
logger.info("Image %s was not found in glance" % image_name)
|
||||
raise exception.ImageNotFound(image_name=image_name)
|
||||
|
||||
flavor_list = self.nova().flavors.list()
|
||||
for o in flavor_list:
|
||||
if o.name == flavor:
|
||||
flavor_id = o.id
|
||||
|
||||
server_userdata = self._build_userdata(userdata)
|
||||
server = self.nova().servers.create(name=self.name, image=image_id,
|
||||
flavor=flavor_id,
|
||||
key_name=key_name,
|
||||
security_groups=security_groups,
|
||||
userdata=server_userdata)
|
||||
while server.status == 'BUILD':
|
||||
server.get()
|
||||
eventlet.sleep(1)
|
||||
if server.status == 'ACTIVE':
|
||||
self.instance_id_set(server.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
# just record the first ipaddress
|
||||
for n in server.networks:
|
||||
self.ipaddress = server.networks[n][0]
|
||||
break
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def reload(self):
|
||||
'''
|
||||
re-read the server's ipaddress so FnGetAtt works.
|
||||
'''
|
||||
try:
|
||||
server = self.nova().servers.get(self.instance_id)
|
||||
for n in server.networks:
|
||||
self.ipaddress = server.networks[n][0]
|
||||
except NotFound:
|
||||
self.ipaddress = '0.0.0.0'
|
||||
|
||||
Resource.reload(self)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
server = self.nova().servers.get(self.instance_id)
|
||||
server.delete()
|
||||
self.instance_id = None
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# 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 eventlet
|
||||
import logging
|
||||
import os
|
||||
|
||||
from novaclient.exceptions import BadRequest
|
||||
from heat.common import exception
|
||||
from heat.engine.resources import Resource
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class SecurityGroup(Resource):
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(SecurityGroup, self).__init__(name, json_snippet, stack)
|
||||
self.instance_id = ''
|
||||
|
||||
if 'GroupDescription' in self.t['Properties']:
|
||||
self.description = self.t['Properties']['GroupDescription']
|
||||
else:
|
||||
self.description = ''
|
||||
|
||||
def create(self):
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
Resource.create(self)
|
||||
sec = None
|
||||
|
||||
groups = self.nova().security_groups.list()
|
||||
for group in groups:
|
||||
if group.name == self.name:
|
||||
sec = group
|
||||
break
|
||||
|
||||
if not sec:
|
||||
sec = self.nova().security_groups.create(self.name,
|
||||
self.description)
|
||||
|
||||
self.instance_id_set(sec.id)
|
||||
|
||||
if 'SecurityGroupIngress' in self.t['Properties']:
|
||||
rules_client = self.nova().security_group_rules
|
||||
for i in self.t['Properties']['SecurityGroupIngress']:
|
||||
try:
|
||||
rule = rules_client.create(sec.id,
|
||||
i['IpProtocol'],
|
||||
i['FromPort'],
|
||||
i['ToPort'],
|
||||
i['CidrIp'])
|
||||
except BadRequest as ex:
|
||||
if ex.message.find('already exists') >= 0:
|
||||
# no worries, the rule is already there
|
||||
pass
|
||||
else:
|
||||
# unexpected error
|
||||
raise
|
||||
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
sec = self.nova().security_groups.get(self.instance_id)
|
||||
|
||||
for rule in sec.rules:
|
||||
self.nova().security_group_rules.delete(rule['id'])
|
||||
|
||||
self.nova().security_groups.delete(sec)
|
||||
self.instance_id = None
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
def FnGetRefId(self):
|
||||
return unicode(self.name)
|
||||
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# 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 eventlet
|
||||
import logging
|
||||
import os
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.resources import Resource
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class Volume(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(Volume, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def create(self):
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(Volume, self).create()
|
||||
|
||||
vol = self.nova('volume').volumes.create(self.t['Properties']['Size'],
|
||||
display_name=self.name,
|
||||
display_description=self.name)
|
||||
|
||||
while vol.status == 'creating':
|
||||
eventlet.sleep(1)
|
||||
vol.get()
|
||||
if vol.status == 'available':
|
||||
self.instance_id_set(vol.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
|
||||
if self.instance_id != None:
|
||||
vol = self.nova('volume').volumes.get(self.instance_id)
|
||||
if vol.status == 'in-use':
|
||||
logger.warn('cant delete volume when in-use')
|
||||
return
|
||||
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
if self.instance_id != None:
|
||||
self.nova('volume').volumes.delete(self.instance_id)
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
||||
|
||||
class VolumeAttachment(Resource):
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(VolumeAttachment, self).__init__(name, json_snippet, stack)
|
||||
|
||||
def create(self):
|
||||
|
||||
if self.state != None:
|
||||
return
|
||||
self.state_set(self.CREATE_IN_PROGRESS)
|
||||
super(VolumeAttachment, self).create()
|
||||
|
||||
server_id = self.t['Properties']['InstanceId']
|
||||
volume_id = self.t['Properties']['VolumeId']
|
||||
logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
|
||||
(server_id, volume_id, self.t['Properties']['Device']))
|
||||
volapi = self.nova().volumes
|
||||
va = volapi.create_server_volume(server_id=server_id,
|
||||
volume_id=volume_id,
|
||||
device=self.t['Properties']['Device'])
|
||||
|
||||
vol = self.nova('volume').volumes.get(va.id)
|
||||
while vol.status == 'available' or vol.status == 'attaching':
|
||||
eventlet.sleep(1)
|
||||
vol.get()
|
||||
if vol.status == 'in-use':
|
||||
self.instance_id_set(va.id)
|
||||
self.state_set(self.CREATE_COMPLETE)
|
||||
else:
|
||||
self.state_set(self.CREATE_FAILED)
|
||||
|
||||
def delete(self):
|
||||
if self.state == self.DELETE_IN_PROGRESS or \
|
||||
self.state == self.DELETE_COMPLETE:
|
||||
return
|
||||
self.state_set(self.DELETE_IN_PROGRESS)
|
||||
Resource.delete(self)
|
||||
|
||||
server_id = self.t['Properties']['InstanceId']
|
||||
volume_id = self.t['Properties']['VolumeId']
|
||||
logger.info('VolumeAttachment un-attaching %s %s' % \
|
||||
(server_id, volume_id))
|
||||
|
||||
volapi = self.nova().volumes
|
||||
volapi.delete_server_volume(server_id,
|
||||
volume_id)
|
||||
|
||||
vol = self.nova('volume').volumes.get(volume_id)
|
||||
logger.info('un-attaching %s, status %s' % (volume_id, vol.status))
|
||||
while vol.status == 'in-use':
|
||||
logger.info('trying to un-attach %s, but still %s' %
|
||||
(volume_id, vol.status))
|
||||
eventlet.sleep(1)
|
||||
try:
|
||||
volapi.delete_server_volume(server_id,
|
||||
volume_id)
|
||||
except Exception:
|
||||
pass
|
||||
vol.get()
|
||||
|
||||
self.state_set(self.DELETE_COMPLETE)
|
||||
|
|
@ -12,6 +12,7 @@ from nose import with_setup
|
|||
|
||||
from heat.tests.v1_1 import fakes
|
||||
from heat.engine import resources
|
||||
from heat.engine import instance
|
||||
import heat.db as db_api
|
||||
from heat.engine import parser
|
||||
|
||||
|
@ -42,11 +43,11 @@ class ResourcesTest(unittest.TestCase):
|
|||
db_api.resource_get_by_name_and_stack(None, 'test_resource_name',\
|
||||
stack).AndReturn(None)
|
||||
|
||||
self.m.StubOutWithMock(resources.Instance, 'nova')
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(instance.Instance, 'nova')
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
|
||||
#Need to find an easier way
|
||||
userdata = t['Resources']['WebServer']['Properties']['UserData']
|
||||
|
@ -56,10 +57,10 @@ class ResourcesTest(unittest.TestCase):
|
|||
t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
|
||||
t['Resources']['WebServer']['Properties']['InstanceType'] = \
|
||||
'256 MB Server'
|
||||
instance = resources.Instance('test_resource_name',\
|
||||
t['Resources']['WebServer'], stack)
|
||||
inst = instance.Instance('test_resource_name',\
|
||||
t['Resources']['WebServer'], stack)
|
||||
|
||||
server_userdata = instance._build_userdata(json.dumps(userdata))
|
||||
server_userdata = inst._build_userdata(json.dumps(userdata))
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
self.fc.servers.create(image=1, flavor=1, key_name='test',\
|
||||
name='test_resource_name', security_groups=None,\
|
||||
|
@ -67,16 +68,16 @@ class ResourcesTest(unittest.TestCase):
|
|||
AndReturn(self.fc.servers.list()[1])
|
||||
self.m.ReplayAll()
|
||||
|
||||
instance.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
instance.create()
|
||||
inst.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
inst.create()
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
instance.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
instance.create()
|
||||
inst.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
inst.create()
|
||||
|
||||
# this makes sure the auto increment worked on instance creation
|
||||
assert(instance.id > 0)
|
||||
assert(inst.id > 0)
|
||||
|
||||
def test_initialize_instance_from_template_and_delete(self):
|
||||
f = open('../../templates/WordPress_Single_Instance_gold.template')
|
||||
|
@ -93,11 +94,11 @@ class ResourcesTest(unittest.TestCase):
|
|||
db_api.resource_get_by_name_and_stack(None, 'test_resource_name',\
|
||||
stack).AndReturn(None)
|
||||
|
||||
self.m.StubOutWithMock(resources.Instance, 'nova')
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
resources.Instance.nova().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(instance.Instance, 'nova')
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
instance.Instance.nova().AndReturn(self.fc)
|
||||
|
||||
#Need to find an easier way
|
||||
userdata = t['Resources']['WebServer']['Properties']['UserData']
|
||||
|
@ -107,10 +108,10 @@ class ResourcesTest(unittest.TestCase):
|
|||
t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
|
||||
t['Resources']['WebServer']['Properties']['InstanceType'] = \
|
||||
'256 MB Server'
|
||||
instance = resources.Instance('test_resource_name',\
|
||||
t['Resources']['WebServer'], stack)
|
||||
inst = instance.Instance('test_resource_name',\
|
||||
t['Resources']['WebServer'], stack)
|
||||
|
||||
server_userdata = instance._build_userdata(json.dumps(userdata))
|
||||
server_userdata = inst._build_userdata(json.dumps(userdata))
|
||||
self.m.StubOutWithMock(self.fc.servers, 'create')
|
||||
self.fc.servers.create(image=1, flavor=1, key_name='test',\
|
||||
name='test_resource_name', security_groups=None,\
|
||||
|
@ -118,18 +119,18 @@ class ResourcesTest(unittest.TestCase):
|
|||
AndReturn(self.fc.servers.list()[1])
|
||||
self.m.ReplayAll()
|
||||
|
||||
instance.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
instance.create()
|
||||
inst.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
inst.create()
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
instance.instance_id = 1234
|
||||
instance.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
instance.create()
|
||||
inst.instance_id = 1234
|
||||
inst.itype_oflavor['256 MB Server'] = '256 MB Server'
|
||||
inst.create()
|
||||
|
||||
instance.delete()
|
||||
assert(instance.instance_id == None)
|
||||
assert(instance.state == instance.DELETE_COMPLETE)
|
||||
inst.delete()
|
||||
assert(inst.instance_id == None)
|
||||
assert(inst.state == inst.DELETE_COMPLETE)
|
||||
|
||||
# allows testing of the test directly, shown below
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue