X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=tools%2Fyaml-validate.py;h=63e3ce51237300709644458ea495cf7ce939e69c;hb=7efa88bbec6294e59d7334e60dffab02825f8418;hp=d75aeb4f6be8e11b92f251834fd3e06fa25d7a1f;hpb=e95304e58ac5bee7cf6f71e4525fbea3f1839828;p=apex-tripleo-heat-templates.git diff --git a/tools/yaml-validate.py b/tools/yaml-validate.py index d75aeb4f..63e3ce51 100755 --- a/tools/yaml-validate.py +++ b/tools/yaml-validate.py @@ -17,12 +17,87 @@ import traceback import yaml +required_params = ['EndpointMap', 'ServiceNetMap', 'DefaultPasswords'] + +envs_containing_endpoint_map = ['tls-endpoints-public-dns.yaml', + 'tls-endpoints-public-ip.yaml', + 'tls-everywhere-endpoints-dns.yaml'] +ENDPOINT_MAP_FILE = 'endpoint_map.yaml' + def exit_usage(): print('Usage %s ' % sys.argv[0]) sys.exit(1) +def get_base_endpoint_map(filename): + try: + tpl = yaml.load(open(filename).read()) + return tpl['parameters']['EndpointMap']['default'] + except Exception: + print(traceback.format_exc()) + return None + + +def get_endpoint_map_from_env(filename): + try: + tpl = yaml.load(open(filename).read()) + return { + 'file': filename, + 'map': tpl['parameter_defaults']['EndpointMap'] + } + except Exception: + print(traceback.format_exc()) + return None + + +def validate_endpoint_map(base_map, env_map): + return sorted(base_map.keys()) == sorted(env_map.keys()) + + +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 'heat_template_version' in tpl and not str(tpl['heat_template_version']).isalpha(): + print('ERROR: heat_template_version needs to be the release alias not a date: %s' + % filename) + return 1 if 'outputs' in tpl and 'role_data' in tpl['outputs']: if 'value' not in tpl['outputs']['role_data']: print('ERROR: invalid role_data for filename: %s' @@ -39,8 +114,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 +143,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 @@ -79,17 +162,25 @@ if len(sys.argv) < 2: path_args = sys.argv[1:] exit_val = 0 failed_files = [] +base_endpoint_map = None +env_endpoint_maps = list() 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: failed_files.append(file_path) exit_val |= failed + if f == ENDPOINT_MAP_FILE: + base_endpoint_map = get_base_endpoint_map(file_path) + if f in envs_containing_endpoint_map: + env_endpoint_map = get_endpoint_map_from_env(file_path) + if env_endpoint_map: + env_endpoint_maps.append(env_endpoint_map) elif os.path.isfile(base_path) and base_path.endswith('.yaml'): failed = validate(base_path) if failed: @@ -99,6 +190,30 @@ for base_path in path_args: print('Unexpected argument %s' % base_path) exit_usage() +if base_endpoint_map and \ + len(env_endpoint_maps) == len(envs_containing_endpoint_map): + for env_endpoint_map in env_endpoint_maps: + matches = validate_endpoint_map(base_endpoint_map, + env_endpoint_map['map']) + if not matches: + print("ERROR: %s doesn't match base endpoint map" % + env_endpoint_map['file']) + failed_files.append(env_endpoint_map['file']) + exit_val |= 1 + else: + print("%s matches base endpoint map" % env_endpoint_map['file']) +else: + print("ERROR: Can't validate endpoint maps since a file is missing. " + "If you meant to delete one of these files you should update this " + "tool as well.") + if not base_endpoint_map: + failed_files.append(ENDPOINT_MAP_FILE) + if len(env_endpoint_maps) != len(envs_containing_endpoint_map): + matched_files = set(os.path.basename(matched_env_file['file']) + for matched_env_file in env_endpoint_maps) + failed_files.extend(set(envs_containing_endpoint_map) - matched_files) + exit_val |= 1 + if failed_files: print('Validation failed on:') for f in failed_files: