From 0feaf3e1c26ad9eb3223cd468bbbb07077c31872 Mon Sep 17 00:00:00 2001 From: Peter Razumovsky Date: Mon, 23 Jan 2017 15:52:27 +0400 Subject: [PATCH] Handle nodes configs and merge them with globals Get nodes configs map, check for existing configs for current node, and if it is, merge it with globals config - variables for further using in jinja and so on. Change-Id: I379f4840e6093b7910b5d7dd612de62b1fe0ac2e Depends-on: I4de6a0fad94d5f83ca486c952d80d1c87c880c0e Related-bug: #1653077 --- fuel_ccp_entrypoint/start_script.py | 35 ++++++++++ .../tests/test_fuel_ccp_entrypoint.py | 69 ++++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/fuel_ccp_entrypoint/start_script.py b/fuel_ccp_entrypoint/start_script.py index 92a74e0..9c9f233 100644 --- a/fuel_ccp_entrypoint/start_script.py +++ b/fuel_ccp_entrypoint/start_script.py @@ -6,6 +6,7 @@ import functools import logging import os import pwd +import re import signal import socket import subprocess @@ -23,6 +24,7 @@ import six VARIABLES = {} GLOBALS_PATH = '/etc/ccp/globals/globals.json' +NODES_CONFIG_PATH = '/etc/ccp/nodes-config/nodes-config.json' META_FILE = "/etc/ccp/meta/meta.json" WORKFLOW_PATH_TEMPLATE = '/etc/ccp/role/%s.json' FILES_DIR = '/etc/ccp/files' @@ -446,10 +448,43 @@ def get_workflow(role_name): return workflow +def find_node_config_keys(nodes_config): + current_node = os.environ['CCP_NODE_NAME'] + config_keys = [] + for node in sorted(nodes_config): + if re.match(node, current_node): + config_keys.append(node) + return config_keys + + +def merge_nodes_configs(variables, node_config): + for k, v in node_config.items(): + if k not in variables: + variables[k] = v + continue + if isinstance(v, dict) and isinstance(variables[k], dict): + merge_nodes_configs(variables[k], v) + else: + variables[k] = v + + def get_variables(role_name): LOG.info("Getting global variables from %s", GLOBALS_PATH) with open(GLOBALS_PATH) as f: variables = json.load(f) + LOG.info("Getting nodes variables from %s", NODES_CONFIG_PATH) + with open(NODES_CONFIG_PATH) as f: + nodes_config = json.load(f) + config_keys = find_node_config_keys(nodes_config) + if config_keys: + # merge configs for all keys and get final node_configs for this node. + # Note that if there several override configs, variables will be + # override with order of list this configs. + node_config = nodes_config[config_keys.pop(0)] + for key in config_keys: + merge_nodes_configs(node_config, nodes_config[key]) + # and then merge variables with final node_config. + merge_nodes_configs(variables, node_config) if os.path.exists(META_FILE): LOG.info("Getting meta information from %s", META_FILE) with open(META_FILE) as f: diff --git a/fuel_ccp_entrypoint/tests/test_fuel_ccp_entrypoint.py b/fuel_ccp_entrypoint/tests/test_fuel_ccp_entrypoint.py index 51d8f63..f77b25e 100644 --- a/fuel_ccp_entrypoint/tests/test_fuel_ccp_entrypoint.py +++ b/fuel_ccp_entrypoint/tests/test_fuel_ccp_entrypoint.py @@ -85,9 +85,7 @@ class TestGetVariables(base.TestCase): @mock.patch('json.load') @mock.patch('fuel_ccp_entrypoint.start_script.create_network_topology') def test_get_variables(self, m_create_network_topology, m_json_load): - def side_effect(file_name): - return {'glob': 'glob_val'} - m_json_load.return_value = {'glob': 'glob_val'} + m_json_load.side_effect = [{'glob': 'glob_val'}, {}] m_create_network_topology.return_value = 'network_topology' r_value = start_script.get_variables('role') e_value = { @@ -99,6 +97,71 @@ class TestGetVariables(base.TestCase): } self.assertEqual(r_value, e_value) + @mock.patch('six.moves.builtins.open', mock.mock_open()) + @mock.patch('json.load') + @mock.patch('fuel_ccp_entrypoint.start_script.create_network_topology') + def test_get_variables_with_node_config(self, m_create_network_topology, + m_json_load): + m_json_load.side_effect = [ + # globals + { + 'a': { + 'b': { + 'c': ['d', 'e', 'f'], + 'g': 'h', + }, + 'i': ['j', 'k'], + 'l': 'm' + }, + 'n': ['o', 'p', 'q'], + 'r': 's' + }, + # nodes configs + { + 'node[1-3]': { + 'a': { + 'b': { + 'c': ['e', 'f', 't'], + 'u': 'v' + }, + 'w': { + 'x': 'y' + } + }, + 'n': ['o', 'p'], + 'z': 'NaN' + }, + 'node[1-2]': { + 'aa': {'ab': 'ac'}, + 'r': {'ad': ['ae', 'af', 'ag']} + + } + } + ] + m_create_network_topology.return_value = 'network_topology' + actual = start_script.get_variables('fake_role') + expected = { + 'role_name': 'fake_role', + 'network_topology': 'network_topology', + 'node_name': 'node1', + 'pod_name': 'pod1', + 'a': { + 'b': { + 'c': ['e', 'f', 't'], + 'g': 'h', + 'u': 'v' + }, + 'w': {'x': 'y'}, + 'i': ['j', 'k'], + 'l': 'm' + }, + 'n': ['o', 'p'], + 'r': {'ad': ['ae', 'af', 'ag']}, + 'z': 'NaN', + 'aa': {'ab': 'ac'}, + } + self.assertEqual(expected, actual) + class TestRetry(base.TestCase): def setUp(self):