Merge "Add heat service support for composable upgrades"
[apex-tripleo-heat-templates.git] / tools / yaml-validate.py
index d75aeb4..fd1f47d 100755 (executable)
@@ -17,11 +17,52 @@ import traceback
 import yaml
 
 
+required_params = ['EndpointMap', 'ServiceNetMap', 'DefaultPasswords']
+
 def exit_usage():
     print('Usage %s <yaml file or directory>' % sys.argv[0])
     sys.exit(1)
 
 
+def validate_mysql_connection(settings):
+    no_op = lambda *args: False
+    error_status = [0]
+
+    def mysql_protocol(items):
+        return items == ['EndpointMap', 'MysqlInternal', 'protocol']
+
+    def client_bind_address(item):
+        return 'bind_address' in item
+
+    def validate_mysql_uri(key, items):
+        # Only consider a connection if it targets mysql
+        if key.endswith('connection') and \
+           search(items, mysql_protocol, no_op):
+            # Assume the "bind_address" option is one of
+            # the token that made up the uri
+            if not search(items, client_bind_address, no_op):
+                error_status[0] = 1
+        return False
+
+    def search(item, check_item, check_key):
+        if check_item(item):
+            return True
+        elif isinstance(item, list):
+            for i in item:
+                if search(i, check_item, check_key):
+                    return True
+        elif isinstance(item, dict):
+            for k in item.keys():
+                if check_key(k, item[k]):
+                    return True
+                elif search(item[k], check_item, check_key):
+                    return True
+        return False
+
+    search(settings, no_op, validate_mysql_uri)
+    return error_status[0]
+
+
 def validate_service(filename, tpl):
     if 'outputs' in tpl and 'role_data' in tpl['outputs']:
         if 'value' not in tpl['outputs']['role_data']:
@@ -39,8 +80,13 @@ def validate_service(filename, tpl):
             print('ERROR: service_name should match file name for service: %s.'
                   % filename)
             return 1
+        # if service connects to mysql, the uri should use option
+        # bind_address to avoid issues with VIP failover
+        if 'config_settings' in role_data and \
+           validate_mysql_connection(role_data['config_settings']):
+            print('ERROR: mysql connection uri should use option bind_address')
+            return 1
     if 'parameters' in tpl:
-        required_params = ['EndpointMap', 'ServiceNetMap', 'DefaultPasswords']
         for param in required_params:
             if param not in tpl['parameters']:
                 print('ERROR: parameter %s is required for %s.'
@@ -63,13 +109,16 @@ def validate(filename):
         print(traceback.format_exc())
         return 1
     # yaml is OK, now walk the parameters and output a warning for unused ones
-    for p in tpl.get('parameters', {}):
-        str_p = '\'%s\'' % p
-        in_resources = str_p in str(tpl.get('resources', {}))
-        in_outputs = str_p in str(tpl.get('outputs', {}))
-        if not in_resources and not in_outputs:
-            print('Warning: parameter %s in template %s appears to be unused'
-                  % (p, filename))
+    if 'heat_template_version' in tpl:
+        for p in tpl.get('parameters', {}):
+            if p in required_params:
+                continue
+            str_p = '\'%s\'' % p
+            in_resources = str_p in str(tpl.get('resources', {}))
+            in_outputs = str_p in str(tpl.get('outputs', {}))
+            if not in_resources and not in_outputs:
+                print('Warning: parameter %s in template %s '
+                      'appears to be unused' % (p, filename))
 
     return retval
 
@@ -84,7 +133,7 @@ for base_path in path_args:
     if os.path.isdir(base_path):
         for subdir, dirs, files in os.walk(base_path):
             for f in files:
-                if f.endswith('.yaml'):
+                if f.endswith('.yaml') and not f.endswith('.j2.yaml'):
                     file_path = os.path.join(subdir, f)
                     failed = validate(file_path)
                     if failed: