]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add a very rough-and-ready conversion from json to xml for cape
authorAngus Salkeld <asalkeld@redhat.com>
Thu, 15 Mar 2012 10:34:43 +0000 (21:34 +1100)
committerAngus Salkeld <asalkeld@redhat.com>
Thu, 15 Mar 2012 10:34:43 +0000 (21:34 +1100)
This needs lots of improvements. And is only tested
with templates/WordPress_Single_Instance.template

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
heat/api/v1/stacks.py

index 3841a8300dd73529080494f8b4f80b9243f3f993..a4746fd96365016e494c76395be6b9b5f264faa6 100644 (file)
@@ -19,6 +19,7 @@
 
 import httplib
 import json
+import libxml2
 import logging
 import sys
 import urlparse
@@ -29,12 +30,209 @@ from webob.exc import (HTTPNotFound,
                        HTTPBadRequest)
 
 from heat.common import exception
+from heat.common import utils
 from heat.common import wsgi
 
 logger = logging.getLogger('heat.api.v1.stacks')
 
 stack_db = {}
 
+
+class Json2CapeXml:
+    def __init__(self, template, stack_name):
+
+        self.t = template
+        self.parms = self.t['Parameters']
+        self.maps = self.t['Mappings']
+        self.res = {}
+
+        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_and_write(self):
+
+        name = self.parms['AWS::StackName']['Default']
+
+        doc = libxml2.newDoc("1.0")
+        dep = doc.newChild(None, "deployable", None)
+        dep.setProp("name", name)
+        dep.setProp("uuid", 'bogus')
+        dep.setProp("monitor", 'active')
+        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))
+
+
+            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')
+
+        filename = '/var/run/%s.xml' % name
+        open(filename, 'w').write(doc.serialize(None, 1))
+        doc.freeDoc()
+
+    def insert_package_and_services(self, r, new_script):
+
+        con = r['Metadata']["AWS::CloudFormation::Init"]['config']
+
+        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
+
+
+
 class StackController(object):
 
     """
@@ -141,6 +339,9 @@ class StackController(object):
         self._apply_user_parameters(req, stack)
         stack_db[req.params['StackName']] = stack
 
+        cape_transformer = Json2CapeXml(stack, req.params['StackName'])
+        cape_transformer.convert_and_write()
+
         return {'CreateStackResult': {'StackId': my_id}}
 
     def update(self, req):