from heat.common import exception
from heat.common import wsgi
-from heat.engine import capelistener
+from heat.engine import parser
from heat.engine import simpledb
logger = logging.getLogger('heat.engine.api.v1.events')
def __init__(self, conf):
self.conf = conf
- self.event_db = {}
- self.listener = capelistener.CapeEventListener()
def index(self, req, stack_id):
return simpledb.events_get(stack_id)
from heat.common import exception
from heat.common import wsgi
-from heat.engine import capelistener
-from heat.engine import json2capexml
-from heat.engine import systemctl
+from heat.engine import parser
logger = logging.getLogger('heat.engine.api.v1.stacks')
def __init__(self, conf):
self.conf = conf
- self.listener = capelistener.CapeEventListener()
-
def index(self, req, format='json'):
logger.info('format is %s' % format)
msg = _("Stack already exists with that name.")
return webob.exc.HTTPConflict(msg)
- stack = body
- stack['StackId'] = body['StackName']
- stack['StackStatus'] = 'CREATE_COMPLETE'
- # TODO self._apply_user_parameters(req, stack)
- stack_db[body['StackName']] = stack
-
- cape_transformer = json2capexml.Json2CapeXml(stack, body['StackName'])
- cape_transformer.convert_and_write()
-
- systemctl.systemctl('start', 'pcloud-cape-sshd', body['StackName'])
+ stack_db[body['StackName']] = parser.Stack(body['StackName'], body)
+ stack_db[body['StackName']].start()
return {'stack': {'id': body['StackName']}}
return webob.exc.HTTPNotFound('No stack by that name')
logger.info('deleting stack %s' % id)
- systemctl.systemctl('stop', 'pcloud-cape-sshd', id)
del stack_db[id]
return None
+++ /dev/null
-# 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 errno
-import eventlet
-from eventlet.green import socket
-import fcntl
-import libxml2
-import logging
-import os
-import stat
-from heat.engine import simpledb
-
-
-logger = logging.getLogger('heat.engine.capelistener')
-
-class CapeEventListener(object):
-
- def __init__(self):
- self.backlog = 50
- self.file = 'pacemaker-cloud-cped'
-
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- flags = fcntl.fcntl(sock, fcntl.F_GETFD)
- fcntl.fcntl(sock, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- try:
- st = os.stat(self.file)
- except OSError, err:
- if err.errno != errno.ENOENT:
- raise
- else:
- if stat.S_ISSOCK(st.st_mode):
- os.remove(self.file)
- else:
- raise ValueError("File %s exists and is not a socket",
- self.file)
- sock.bind(self.file)
- sock.listen(self.backlog)
- os.chmod(self.file, 0600)
-
- eventlet.spawn_n(self.cape_event_listner, sock)
-
- def cape_event_listner(self, sock):
- eventlet.serve(sock, self.cape_event_handle)
-
- def store(self, xml_event):
-
- try:
- doc = libxml2.parseDoc(xml_event)
- except:
- return
-
- event = {'EventId': ''}
- root = doc.getRootElement()
- child = root.children
- while child is not None:
- if child.type != "element":
- child = child.next
- elif child.name == 'event':
- child = child.children
- elif child.name == 'application':
- event['StackId'] = child.prop('name')
- event['StackName'] = child.prop('name')
- child = child.children
- elif child.name == 'node':
- event['ResourceType'] = 'AWS::EC2::Instance'
- event['LogicalResourceId'] = child.prop('name')
- child = child.children
- elif child.name == 'resource':
- event['ResourceType'] = 'ORG::HA::Service'
- event['LogicalResourceId'] = child.prop('name')
- child = child.children
- elif child.name == 'state':
- event['ResourceStatus'] = child.content
- child = child.next
- elif child.name == 'reason':
- event['ResourceStatusReason'] = child.content
- child = child.next
- else:
- child = child.next
-
- simpledb.event_append(event)
- doc.freeDoc()
-
- def cape_event_handle(self, sock, client_addr):
- while True:
- x = sock.recv(4096)
- self.store(x.strip('\n'))
- if not x: break
-
+++ /dev/null
-# 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 json
-import libxml2
-import logging
-
-from heat.common import utils
-
-logger = logging.getLogger('heat.engine.json2capexml')
-
-class Json2CapeXml:
- def __init__(self, template, stack_name):
-
- self.t = template
- self.parms = self.t['Parameters']
- self.maps = self.t['Mappings']
- self.res = {}
- self.doc = None
- self.name = stack_name
-
- self.parms['AWS::Region'] = {"Description" : "AWS Regions", "Type" : "String", "Default" : "ap-southeast-1",
- "AllowedValues" : ["us-east-1","us-west-1","us-west-2","sa-east-1","eu-west-1","ap-southeast-1","ap-northeast-1"],
- "ConstraintDescription" : "must be a valid EC2 instance type." }
-
- # expected user parameters
- self.parms['AWS::StackName'] = {'Default': stack_name}
- self.parms['KeyName'] = {'Default': 'harry-45-5-34-5'}
-
- for r in self.t['Resources']:
- # fake resource instance references
- self.parms[r] = {'Default': utils.generate_uuid()}
-
- self.resolve_static_refs(self.t['Resources'])
- self.resolve_find_in_map(self.t['Resources'])
- #self.resolve_attributes(self.t['Resources'])
- self.resolve_joins(self.t['Resources'])
- self.resolve_base64(self.t['Resources'])
- #print json.dumps(self.t['Resources'], indent=2)
-
-
- def convert(self):
-
- self.doc = libxml2.newDoc("1.0")
- dep = self.doc.newChild(None, "deployable", None)
- dep.setProp("name", self.name)
- dep.setProp("uuid", 'bogus')
- dep.setProp("username", 'nobody-yet')
- n_asses = dep.newChild(None, "assemblies", None)
-
- for r in self.t['Resources']:
- type = self.t['Resources'][r]['Type']
- if type != 'AWS::EC2::Instance':
- print 'ignoring Resource %s (%s)' % (r, type)
- continue
-
- n_ass = n_asses.newChild(None, 'assembly', None)
- n_ass.setProp("name", r)
- n_ass.setProp("uuid", self.parms[r]['Default'])
- props = self.t['Resources'][r]['Properties']
- for p in props:
- if p == 'ImageId':
- n_ass.setProp("image_name", props[p])
- elif p == 'UserData':
- new_script = []
- script_lines = props[p].split('\n')
- for l in script_lines:
- if '#!/' in l:
- new_script.append(l)
- self.insert_package_and_services(self.t['Resources'][r], new_script)
- else:
- new_script.append(l)
-
- startup = n_ass.newChild(None, 'startup', '\n'.join(new_script))
-
-
- try:
- con = self.t['Resources'][r]['Metadata']["AWS::CloudFormation::Init"]['config']
- n_services = n_ass.newChild(None, 'services', None)
- for st in con['services']:
- for s in con['services'][st]:
- n_service = n_services.newChild(None, 'service', None)
- n_service.setProp("name", '%s_%s' % (r, s))
- n_service.setProp("type", s)
- n_service.setProp("provider", 'pacemaker')
- n_service.setProp("class", 'lsb')
- n_service.setProp("monitor_interval", '30s')
- n_service.setProp("escalation_period", '1000')
- n_service.setProp("escalation_failures", '3')
- except KeyError as e:
- # if there is no config then no services.
- pass
-
- def get_xml(self):
- str = self.doc.serialize(None, 1)
- self.doc.freeDoc()
- self.doc = None
- return str
-
- def convert_and_write(self):
- self.convert()
- try:
- filename = '/var/run/%s.xml' % self.name
- open(filename, 'w').write(self.doc.serialize(None, 1))
- self.doc.freeDoc()
- self.doc = None
- except IOError as e:
- logger.error('couldn\'t write to /var/run/ error %s' % e)
-
- def insert_package_and_services(self, r, new_script):
-
- try:
- con = r['Metadata']["AWS::CloudFormation::Init"]['config']
- except KeyError as e:
- return
-
- for pt in con['packages']:
- if pt == 'yum':
- for p in con['packages']['yum']:
- new_script.append('yum install -y %s' % p)
- for st in con['services']:
- if st == 'systemd':
- for s in con['services']['systemd']:
- v = con['services']['systemd'][s]
- if v['enabled'] == 'true':
- new_script.append('systemctl enable %s.service' % s)
- if v['ensureRunning'] == 'true':
- new_script.append('systemctl start %s.service' % s)
- elif st == 'sysvinit':
- for s in con['services']['sysvinit']:
- v = con['services']['systemd'][s]
- if v['enabled'] == 'true':
- new_script.append('chkconfig %s on' % s)
- if v['ensureRunning'] == 'true':
- new_script.append('/etc/init.d/start %s' % s)
-
- def resolve_static_refs(self, s):
- '''
- looking for { "Ref": "str" }
- '''
- if isinstance(s, dict):
- for i in s:
- if i == 'Ref' and isinstance(s[i], (basestring, unicode)) and \
- self.parms.has_key(s[i]):
- if self.parms[s[i]] == None:
- print 'None Ref: %s' % str(s[i])
- elif self.parms[s[i]].has_key('Default'):
- # note the "ref: values" are in a dict of
- # size one, so return is fine.
- #print 'Ref: %s == %s' % (s[i], self.parms[s[i]]['Default'])
- return self.parms[s[i]]['Default']
- else:
- print 'missing Ref: %s' % str(s[i])
- else:
- s[i] = self.resolve_static_refs(s[i])
- elif isinstance(s, list):
- for index, item in enumerate(s):
- #print 'resolve_static_refs %d %s' % (index, item)
- s[index] = self.resolve_static_refs(item)
- return s
-
- def resolve_find_in_map(self, s):
- '''
- looking for { "Ref": "str" }
- '''
- if isinstance(s, dict):
- for i in s:
- if i == 'Fn::FindInMap':
- obj = self.maps
- if isinstance(s[i], list):
- #print 'map list: %s' % s[i]
- for index, item in enumerate(s[i]):
- if isinstance(item, dict):
- item = self.resolve_find_in_map(item)
- #print 'map item dict: %s' % (item)
- else:
- pass
- #print 'map item str: %s' % (item)
- obj = obj[item]
- else:
- obj = obj[s[i]]
- return obj
- else:
- s[i] = self.resolve_find_in_map(s[i])
- elif isinstance(s, list):
- for index, item in enumerate(s):
- s[index] = self.resolve_find_in_map(item)
- return s
-
-
- def resolve_joins(self, s):
- '''
- looking for { "Fn::join": [] }
- '''
- if isinstance(s, dict):
- for i in s:
- if i == 'Fn::Join':
- return s[i][0].join(s[i][1])
- else:
- s[i] = self.resolve_joins(s[i])
- elif isinstance(s, list):
- for index, item in enumerate(s):
- s[index] = self.resolve_joins(item)
- return s
-
-
- def resolve_base64(self, s):
- '''
- looking for { "Fn::join": [] }
- '''
- if isinstance(s, dict):
- for i in s:
- if i == 'Fn::Base64':
- return s[i]
- else:
- s[i] = self.resolve_base64(s[i])
- elif isinstance(s, list):
- for index, item in enumerate(s):
- s[index] = self.resolve_base64(item)
- return s
-
-
import json
import logging
-logger = logging.getLogger('heat.engine.parser')
-
-parse_debug = False
-#parse_debug = True
-
-
-class Resource(object):
- CREATE_IN_PROGRESS = 'CREATE_IN_PROGRESS'
- CREATE_FAILED = 'CREATE_FAILED'
- CREATE_COMPLETE = 'CREATE_COMPLETE'
- DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
- DELETE_FAILED = 'DELETE_FAILED'
- DELETE_COMPLETE = 'DELETE_COMPLETE'
- UPDATE_IN_PROGRESS = 'UPDATE_IN_PROGRESS'
- UPDATE_FAILED = 'UPDATE_FAILED'
- UPDATE_COMPLETE = 'UPDATE_COMPLETE'
-
- def __init__(self, name, json_snippet, stack):
- self.t = json_snippet
- self.depends_on = []
- self.references = []
- self.references_resolved = False
- self.state = None
- self.stack = stack
- self.name = name
- self.instance_id = None
-
-
-
- stack.resolve_static_refs(self.t)
- stack.resolve_find_in_map(self.t)
-
- def start(self):
- for c in self.depends_on:
- #print '%s->%s.start()' % (self.name, self.stack.resources[c].name)
- self.stack.resources[c].start()
+from heat.engine import resources
- self.stack.resolve_attributes(self.t)
- self.stack.resolve_joins(self.t)
- self.stack.resolve_base64(self.t)
-
-
- def stop(self):
- pass
-
- def reload(self):
- pass
-
- def FnGetRefId(self):
- '''
-http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
- '''
- if self.instance_id != None:
- return unicode(self.instance_id)
- else:
- return unicode(self.name)
-
- def FnGetAtt(self, key):
- '''
-http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
- '''
- print '%s.GetAtt(%s)' % (self.name, key)
- return unicode('not-this-surely')
-
-class GenericResource(Resource):
- def __init__(self, name, json_snippet, stack):
- super(GenericResource, self).__init__(name, json_snippet, stack)
-
- def start(self):
- if self.state != None:
- return
- self.state = self.CREATE_IN_PROGRESS
- super(GenericResource, self).start()
- print 'Starting GenericResource %s' % self.name
-
-
-class ElasticIp(Resource):
- def __init__(self, name, json_snippet, stack):
- super(ElasticIp, self).__init__(name, json_snippet, stack)
- self.instance_id = ''
-
- if self.t.has_key('Properties') and self.t['Properties'].has_key('Domain'):
- print '*** can\'t support Domain %s yet' % (self.t['Properties']['Domain'])
-
- def start(self):
- if self.state != None:
- return
- self.state = Resource.CREATE_IN_PROGRESS
- super(ElasticIp, self).start()
- self.instance_id = 'eip-000003'
-
- def FnGetRefId(self):
- return unicode('0.0.0.0')
-
- def FnGetAtt(self, key):
- return unicode(self.instance_id)
-
-class ElasticIpAssociation(Resource):
- def __init__(self, name, json_snippet, stack):
- super(ElasticIpAssociation, self).__init__(name, json_snippet, stack)
-
- # note we only support already assigned ipaddress
- #
- # Done with:
- # nova-manage floating create 172.31.0.224/28
- # euca-allocate-address
- #
-
- if not self.t['Properties'].has_key('EIP'):
- print '*** can\'t support this yet'
- if self.t['Properties'].has_key('AllocationId'):
- print '*** can\'t support AllocationId %s yet' % (self.t['Properties']['AllocationId'])
-
- def FnGetRefId(self):
- if not self.t['Properties'].has_key('EIP'):
- return unicode('0.0.0.0')
- else:
- return unicode(self.t['Properties']['EIP'])
-
- def start(self):
-
- if self.state != None:
- return
- self.state = Resource.CREATE_IN_PROGRESS
- super(ElasticIpAssociation, self).start()
- print '$ euca-associate-address -i %s %s' % (self.t['Properties']['InstanceId'],
- self.t['Properties']['EIP'])
-
-class Volume(Resource):
- def __init__(self, name, json_snippet, stack):
- super(Volume, self).__init__(name, json_snippet, stack)
-
- def start(self):
-
- if self.state != None:
- return
- self.state = Resource.CREATE_IN_PROGRESS
- super(Volume, self).start()
- # TODO start the volume here
- # of size -> self.t['Properties']['Size']
- # and set self.instance_id to the volume id
- print '$ euca-create-volume -s %s -z nova' % self.t['Properties']['Size']
- self.instance_id = 'vol-4509854'
-
-class VolumeAttachment(Resource):
- def __init__(self, name, json_snippet, stack):
- super(VolumeAttachment, self).__init__(name, json_snippet, stack)
-
- def start(self):
-
- if self.state != None:
- return
- self.state = Resource.CREATE_IN_PROGRESS
- super(VolumeAttachment, self).start()
- # TODO attach the volume with an id of:
- # self.t['Properties']['VolumeId']
- # to the vm of instance:
- # self.t['Properties']['InstanceId']
- # and make sure that the mountpoint is:
- # self.t['Properties']['Device']
- print '$ euca-attach-volume %s -i %s -d %s' % (self.t['Properties']['VolumeId'],
- self.t['Properties']['InstanceId'],
- self.t['Properties']['Device'])
-
-class Instance(Resource):
-
- def __init__(self, name, json_snippet, stack):
- super(Instance, self).__init__(name, json_snippet, stack)
-
- if not self.t['Properties'].has_key('AvailabilityZone'):
- self.t['Properties']['AvailabilityZone'] = 'nova'
- self.itype_oflavor = {'t1.micro': 'm1.tiny',
- 'm1.small': 'm1.small',
- 'm1.medium': 'm1.medium',
- 'm1.large': 'm1.large',
- 'm2.xlarge': 'm1.large',
- '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):
- print '%s.GetAtt(%s)' % (self.name, key)
-
- if key == 'AvailabilityZone':
- return unicode(self.t['Properties']['AvailabilityZone'])
- else:
- # TODO PrivateDnsName, PublicDnsName, PrivateIp, PublicIp
- return unicode('not-this-surely')
-
-
- def start(self):
-
- if self.state != None:
- return
- self.state = Resource.CREATE_IN_PROGRESS
- Resource.start(self)
-
- props = self.t['Properties']
- if not props.has_key('KeyName'):
- props['KeyName'] = 'default-key-name'
- if not props.has_key('InstanceType'):
- props['InstanceType'] = 's1.large'
- if not props.has_key('ImageId'):
- props['ImageId'] = 'F16-x86_64'
-
- for p in props:
- if p == 'UserData':
- new_script = []
- script_lines = props[p].split('\n')
-
- for l in script_lines:
- if '#!/' in l:
- new_script.append(l)
- self.insert_package_and_services(self.t, new_script)
- else:
- new_script.append(l)
-
- if parse_debug:
- print '----------------------'
- try:
- print '\n'.join(new_script)
- except:
- print str(new_script)
- raise
- print '----------------------'
-
- try:
- con = self.t['Metadata']["AWS::CloudFormation::Init"]['config']
- for st in con['services']:
- for s in con['services'][st]:
- print 'service start %s_%s' % (self.name, s)
- except KeyError as e:
- # if there is no config then no services.
- pass
-
-
- # TODO start the instance here.
- # and set self.instance_id
- print '$ euca-run-instances -k %s -t %s %s' % (self.t['Properties']['KeyName'],
- self.t['Properties']['InstanceType'],
- self.t['Properties']['ImageId'])
-
- # Convert AWS instance type to OpenStack flavor
- # TODO(sdake)
- # heat API should take care of these conversions and feed them into
- # heat engine in an openstack specific json format
- flavor = self.itype_oflavor[self.t['Properties']['InstanceType']]
- self.instance_id = 'i-734509008'
-
- def insert_package_and_services(self, r, new_script):
-
- try:
- con = r['Metadata']["AWS::CloudFormation::Init"]['config']
- except KeyError as e:
- return
-
- if con.has_key('packages'):
- for pt in con['packages']:
- if pt == 'yum':
- for p in con['packages']['yum']:
- new_script.append('yum install -y %s' % p)
+logger = logging.getLogger('heat.engine.parser')
- if con.has_key('services'):
- for st in con['services']:
- if st == 'systemd':
- for s in con['services']['systemd']:
- v = con['services']['systemd'][s]
- if v['enabled'] == 'true':
- new_script.append('systemctl enable %s.service' % s)
- if v['ensureRunning'] == 'true':
- new_script.append('systemctl start %s.service' % s)
- elif st == 'sysvinit':
- for s in con['services']['sysvinit']:
- v = con['services']['sysvinit'][s]
- if v['enabled'] == 'true':
- new_script.append('chkconfig %s on' % s)
- if v['ensureRunning'] == 'true':
- new_script.append('/etc/init.d/start %s' % s)
class Stack:
- def __init__(self, template, stack_name):
+ def __init__(self, stack_name, template):
self.t = template
if self.t.has_key('Parameters'):
"AllowedValues" : ["us-east-1","us-west-1","us-west-2","sa-east-1","eu-west-1","ap-southeast-1","ap-northeast-1"],
"ConstraintDescription" : "must be a valid EC2 instance type." }
+
+######
+# stack['StackId'] = body['StackName']
+# stack['StackStatus'] = 'CREATE_COMPLETE'
+# # TODO self._apply_user_parameters(req, stack)
+# stack_db[body['StackName']] = stack
+######
+
self.resources = {}
for r in self.t['Resources']:
type = self.t['Resources'][r]['Type']
if type == 'AWS::EC2::Instance':
- self.resources[r] = Instance(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.Instance(r, self.t['Resources'][r], self)
elif type == 'AWS::EC2::Volume':
- self.resources[r] = Volume(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.Volume(r, self.t['Resources'][r], self)
elif type == 'AWS::EC2::VolumeAttachment':
- self.resources[r] = VolumeAttachment(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.VolumeAttachment(r, self.t['Resources'][r], self)
elif type == 'AWS::EC2::EIP':
- self.resources[r] = ElasticIp(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.ElasticIp(r, self.t['Resources'][r], self)
elif type == 'AWS::EC2::EIPAssociation':
- self.resources[r] = ElasticIpAssociation(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.ElasticIpAssociation(r, self.t['Resources'][r], self)
else:
- self.resources[r] = GenericResource(r, self.t['Resources'][r], self)
+ self.resources[r] = resources.GenericResource(r, self.t['Resources'][r], self)
self.calulate_dependancies(self.t['Resources'][r], self.resources[r])
- #print json.dumps(self.t['Resources'], indent=2)
- if parse_debug:
- for r in self.t['Resources']:
- print '%s -> %s' % (r, self.resources[r].depends_on)
def start(self):
# start Volumes first.
--- /dev/null
+# 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 logging
+
+from heat.engine import simpledb
+
+logger = logging.getLogger('heat.engine.resources')
+
+class Resource(object):
+ CREATE_IN_PROGRESS = 'CREATE_IN_PROGRESS'
+ CREATE_FAILED = 'CREATE_FAILED'
+ CREATE_COMPLETE = 'CREATE_COMPLETE'
+ DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
+ DELETE_FAILED = 'DELETE_FAILED'
+ DELETE_COMPLETE = 'DELETE_COMPLETE'
+ UPDATE_IN_PROGRESS = 'UPDATE_IN_PROGRESS'
+ UPDATE_FAILED = 'UPDATE_FAILED'
+ UPDATE_COMPLETE = 'UPDATE_COMPLETE'
+
+ def __init__(self, name, json_snippet, stack):
+ self.t = json_snippet
+ self.depends_on = []
+ self.references = []
+ self.references_resolved = False
+ self.state = None
+ self.stack = stack
+ self.name = name
+ self.instance_id = None
+
+ stack.resolve_static_refs(self.t)
+ stack.resolve_find_in_map(self.t)
+
+ def start(self):
+ for c in self.depends_on:
+ #print '%s->%s.start()' % (self.name, self.stack.resources[c].name)
+ self.stack.resources[c].start()
+
+ self.stack.resolve_attributes(self.t)
+ self.stack.resolve_joins(self.t)
+ self.stack.resolve_base64(self.t)
+
+
+ def state_set(self, new_state, reason="state changed"):
+ if new_state != self.state:
+ ev = {}
+ ev['LogicalResourceId'] = self.name
+ ev['PhysicalResourceId'] = self.name
+ ev['StackId'] = self.stack.name
+ ev['StackName'] = self.stack.name
+ ev['ResourceStatus'] = new_state
+ ev['ResourceStatusReason'] = reason
+ ev['ResourceType'] = self.t['Type']
+ ev['ResourceProperties'] = self.t['Properties']
+
+ simpledb.event_append(ev)
+ self.state = new_state
+
+ def stop(self):
+ pass
+
+ def reload(self):
+ pass
+
+ def FnGetRefId(self):
+ '''
+http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
+ '''
+ if self.instance_id != None:
+ return unicode(self.instance_id)
+ else:
+ return unicode(self.name)
+
+ def FnGetAtt(self, key):
+ '''
+http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
+ '''
+ print '%s.GetAtt(%s)' % (self.name, key)
+ return unicode('not-this-surely')
+
+class GenericResource(Resource):
+ def __init__(self, name, json_snippet, stack):
+ super(GenericResource, self).__init__(name, json_snippet, stack)
+
+ def start(self):
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ super(GenericResource, self).start()
+ print 'Starting GenericResource %s' % self.name
+
+
+class ElasticIp(Resource):
+ def __init__(self, name, json_snippet, stack):
+ super(ElasticIp, self).__init__(name, json_snippet, stack)
+ self.instance_id = ''
+
+ if self.t.has_key('Properties') and self.t['Properties'].has_key('Domain'):
+ logger.warn('*** can\'t support Domain %s yet' % (self.t['Properties']['Domain']))
+
+ def start(self):
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ super(ElasticIp, self).start()
+ self.instance_id = 'eip-000003'
+
+ def FnGetRefId(self):
+ return unicode('0.0.0.0')
+
+ def FnGetAtt(self, key):
+ return unicode(self.instance_id)
+
+class ElasticIpAssociation(Resource):
+ def __init__(self, name, json_snippet, stack):
+ super(ElasticIpAssociation, self).__init__(name, json_snippet, stack)
+
+ # note we only support already assigned ipaddress
+ #
+ # Done with:
+ # nova-manage floating create 172.31.0.224/28
+ # euca-allocate-address
+ #
+
+ if not self.t['Properties'].has_key('EIP'):
+ logger.warn('*** can\'t support this yet')
+ if self.t['Properties'].has_key('AllocationId'):
+ logger.warn('*** can\'t support AllocationId %s yet' % (self.t['Properties']['AllocationId']))
+
+ def FnGetRefId(self):
+ if not self.t['Properties'].has_key('EIP'):
+ return unicode('0.0.0.0')
+ else:
+ return unicode(self.t['Properties']['EIP'])
+
+ def start(self):
+
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ super(ElasticIpAssociation, self).start()
+ logger.info('$ euca-associate-address -i %s %s' % (self.t['Properties']['InstanceId'],
+ self.t['Properties']['EIP']))
+
+class Volume(Resource):
+ def __init__(self, name, json_snippet, stack):
+ super(Volume, self).__init__(name, json_snippet, stack)
+
+ def start(self):
+
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ super(Volume, self).start()
+ # TODO start the volume here
+ # of size -> self.t['Properties']['Size']
+ # and set self.instance_id to the volume id
+ logger.info('$ euca-create-volume -s %s -z nova' % self.t['Properties']['Size'])
+ self.instance_id = 'vol-4509854'
+
+class VolumeAttachment(Resource):
+ def __init__(self, name, json_snippet, stack):
+ super(VolumeAttachment, self).__init__(name, json_snippet, stack)
+
+ def start(self):
+
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ super(VolumeAttachment, self).start()
+ # TODO attach the volume with an id of:
+ # self.t['Properties']['VolumeId']
+ # to the vm of instance:
+ # self.t['Properties']['InstanceId']
+ # and make sure that the mountpoint is:
+ # self.t['Properties']['Device']
+ logger.info('$ euca-attach-volume %s -i %s -d %s' % (self.t['Properties']['VolumeId'],
+ self.t['Properties']['InstanceId'],
+ self.t['Properties']['Device']))
+
+class Instance(Resource):
+
+ def __init__(self, name, json_snippet, stack):
+ super(Instance, self).__init__(name, json_snippet, stack)
+
+ if not self.t['Properties'].has_key('AvailabilityZone'):
+ self.t['Properties']['AvailabilityZone'] = 'nova'
+ self.itype_oflavor = {'t1.micro': 'm1.tiny',
+ 'm1.small': 'm1.small',
+ 'm1.medium': 'm1.medium',
+ 'm1.large': 'm1.large',
+ 'm2.xlarge': 'm1.large',
+ '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):
+ print '%s.GetAtt(%s)' % (self.name, key)
+
+ if key == 'AvailabilityZone':
+ return unicode(self.t['Properties']['AvailabilityZone'])
+ else:
+ # TODO PrivateDnsName, PublicDnsName, PrivateIp, PublicIp
+ return unicode('not-this-surely')
+
+
+ def start(self):
+
+ if self.state != None:
+ return
+ self.state_set(self.CREATE_IN_PROGRESS)
+ Resource.start(self)
+
+ props = self.t['Properties']
+ if not props.has_key('KeyName'):
+ props['KeyName'] = 'default-key-name'
+ if not props.has_key('InstanceType'):
+ props['InstanceType'] = 's1.large'
+ if not props.has_key('ImageId'):
+ props['ImageId'] = 'F16-x86_64'
+
+ for p in props:
+ if p == 'UserData':
+ new_script = []
+ script_lines = props[p].split('\n')
+
+ for l in script_lines:
+ if '#!/' in l:
+ new_script.append(l)
+ self.insert_package_and_services(self.t, new_script)
+ else:
+ new_script.append(l)
+
+ print '----------------------'
+ try:
+ print '\n'.join(new_script)
+ except:
+ print str(new_script)
+ raise
+ print '----------------------'
+
+ try:
+ con = self.t['Metadata']["AWS::CloudFormation::Init"]['config']
+ for st in con['services']:
+ for s in con['services'][st]:
+ print 'service start %s_%s' % (self.name, s)
+ except KeyError as e:
+ # if there is no config then no services.
+ pass
+
+
+ # TODO start the instance here.
+ # and set self.instance_id
+ logger.info('$ euca-run-instances -k %s -t %s %s' % (self.t['Properties']['KeyName'],
+ self.t['Properties']['InstanceType'],
+ self.t['Properties']['ImageId']))
+
+ # Convert AWS instance type to OpenStack flavor
+ # TODO(sdake)
+ # heat API should take care of these conversions and feed them into
+ # heat engine in an openstack specific json format
+ flavor = self.itype_oflavor[self.t['Properties']['InstanceType']]
+ self.instance_id = 'i-734509008'
+
+ def insert_package_and_services(self, r, new_script):
+
+ try:
+ con = r['Metadata']["AWS::CloudFormation::Init"]['config']
+ except KeyError as e:
+ return
+
+ if con.has_key('packages'):
+ for pt in con['packages']:
+ if pt == 'yum':
+ for p in con['packages']['yum']:
+ new_script.append('yum install -y %s' % p)
+
+ if con.has_key('services'):
+ for st in con['services']:
+ if st == 'systemd':
+ for s in con['services']['systemd']:
+ v = con['services']['systemd'][s]
+ if v['enabled'] == 'true':
+ new_script.append('systemctl enable %s.service' % s)
+ if v['ensureRunning'] == 'true':
+ new_script.append('systemctl start %s.service' % s)
+ elif st == 'sysvinit':
+ for s in con['services']['sysvinit']:
+ v = con['services']['sysvinit'][s]
+ if v['enabled'] == 'true':
+ new_script.append('chkconfig %s on' % s)
+ if v['ensureRunning'] == 'true':
+ new_script.append('/etc/init.d/start %s' % s)
import json
def event_append(event):
+ '''
+ EventId The unique ID of this event.
+ Timestamp Time the status was updated.
+ '''
name = event['StackName']
d = anydbm.open('/var/lib/heat/%s.events.db' % name, 'c')
if d.has_key('lastid'):
+++ /dev/null
-# 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.
-
-"""
-Start and Stop systemd services
-"""
-import dbus
-import logging
-
-logger = logging.getLogger('heat.engine.systemctl')
-
-def systemctl(method, name, instance=None):
-
- bus = dbus.SystemBus()
-
- sysd = bus.get_object('org.freedesktop.systemd1',
- '/org/freedesktop/systemd1')
-
- actual_method = ''
- if method == 'start':
- actual_method = 'StartUnit'
- elif method == 'stop':
- actual_method = 'StopUnit'
- else:
- raise
-
- m = sysd.get_dbus_method(actual_method, 'org.freedesktop.systemd1.Manager')
-
- if instance == None:
- service = '%s.service' % (name)
- else:
- service = '%s@%s.service' % (name, instance)
-
- try:
- result = m(service, 'replace')
- except dbus.DBusException as e:
- logger.error('couldn\'t %s %s error: %s' % (method, name, e))
- return None
- return result
-