]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Begin the change to a python only implementation.
authorAngus Salkeld <asalkeld@redhat.com>
Mon, 26 Mar 2012 05:45:06 +0000 (16:45 +1100)
committerAngus Salkeld <asalkeld@redhat.com>
Mon, 26 Mar 2012 05:45:06 +0000 (16:45 +1100)
- Don't start pacemaker-cloud cape, instead start the
  resources (soon to be implemented).
- kill off systemctl, capelistener and json2capexml

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
heat/engine/api/v1/events.py
heat/engine/api/v1/stacks.py
heat/engine/capelistener.py [deleted file]
heat/engine/json2capexml.py [deleted file]
heat/engine/parser.py
heat/engine/resources.py [new file with mode: 0644]
heat/engine/simpledb.py
heat/engine/systemctl.py [deleted file]

index 994398e3f4e415afd7b6f3b28277f12d86a5e857..941fa744ffe8e179dcbdf278277b1fb1a5a71a13 100644 (file)
@@ -27,7 +27,7 @@ from webob.exc import (HTTPNotFound,
 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')
@@ -41,8 +41,6 @@ class EventsController(object):
 
     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)
index ba3af0c40f34ac8f9fb9c2fc93554390237d942c..698435f79083bd75b5d8075598632db1c1081024 100644 (file)
@@ -27,9 +27,7 @@ from webob.exc import (HTTPNotFound,
 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')
@@ -43,8 +41,6 @@ class StacksController(object):
 
     def __init__(self, conf):
         self.conf = conf
-        self.listener = capelistener.CapeEventListener()
-
 
     def index(self, req, format='json'):
         logger.info('format is %s' % format)
@@ -99,16 +95,8 @@ class StacksController(object):
             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']}}
 
@@ -117,7 +105,6 @@ class StacksController(object):
             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
 
diff --git a/heat/engine/capelistener.py b/heat/engine/capelistener.py
deleted file mode 100644 (file)
index 4752574..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-# 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
-
diff --git a/heat/engine/json2capexml.py b/heat/engine/json2capexml.py
deleted file mode 100644 (file)
index 96503a4..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-# 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
-
-
index 6cbc894f42fb14dd3e35a556dce35c9b3af79830..90e3176d4882e40509194029048e5ef352670bfb 100644 (file)
 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'):
@@ -318,27 +41,31 @@ class Stack:
               "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.
diff --git a/heat/engine/resources.py b/heat/engine/resources.py
new file mode 100644 (file)
index 0000000..58016ea
--- /dev/null
@@ -0,0 +1,308 @@
+# 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)
index 7921ab65ea572036c086a3d8d4c917df41f8a4fd..a3907991496c62b3a251209e0cb2a263f57ea90d 100644 (file)
@@ -17,6 +17,10 @@ import anydbm
 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'):
diff --git a/heat/engine/systemctl.py b/heat/engine/systemctl.py
deleted file mode 100644 (file)
index 693dc40..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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
-