blob: 3a4af3864b322119994c966266b0f115dc23ecbd [file] [log] [blame]
Dennis Dmitriev566db4b2017-07-18 18:13:07 +03001# Copyright 2013 - 2017 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Dennis Dmitriev806706d2017-07-29 22:31:23 +030015import copy
16
Dennis Dmitriev1110ac52017-06-22 21:07:37 +030017import reclass
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030018# from reclass.adapters import salt as reclass_salt
Dennis Dmitriev1110ac52017-06-22 21:07:37 +030019from reclass import config as reclass_config
20from reclass import core as reclass_core
Dennis Dmitriev806706d2017-07-29 22:31:23 +030021from reclass import defaults as reclass_defaults
22from reclass.utils.refvalue import RefValue
23import yaml
Dennis Dmitrievde847d92017-06-26 18:58:05 +030024
25from reclass_tools import helpers
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030026# import salt.cli.call
27# import salt.cli.caller
Dennis Dmitriev1110ac52017-06-22 21:07:37 +030028
29
Dennis Dmitriev806706d2017-07-29 22:31:23 +030030def refvalue_representer(dumper, data):
31 return dumper.represent_str(
32 data._assemble(
33 lambda s: s.join(reclass_defaults.PARAMETER_INTERPOLATION_SENTINELS)))
34yaml.add_representer(RefValue, refvalue_representer)
35
36class ReclassCore(reclass_core.Core):
37 """Track the specific key
38
39 :param key: string with dot-separated keys
40 """
41 track_key_path = None
42
43 def __init__(self, storage, class_mappings, input_data=None,
44 key=None):
45 if key:
Dennis Dmitrievdfe1fb22017-11-01 16:40:09 +020046 if ':' in key:
47 # Linux pillar notation: linux:network:interface
48 self.track_key_path = key.split(':')
49 else:
50 # Python notation: linux.network.interface
51 self.track_key_path = key.split('.')
52
53 if self.track_key_path[0] == 'parameters':
54 # Remove the first 'parameters' element because the model entities
55 # keep parameters in different object format.
56 self.track_key_path = self.track_key_path[1:]
Dennis Dmitriev806706d2017-07-29 22:31:23 +030057
58 super(ReclassCore, self).__init__(storage, class_mappings, input_data)
59
60 def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None):
61 if seen is None:
62 seen = {}
63 if '__visited' not in seen:
64 seen['__visited'] = []
65
66 orig_visited = copy.deepcopy(seen['__visited'])
67 seen['__visited'].append(entity.name)
68
69 result = super(ReclassCore, self)._recurse_entity(entity,
70 merge_base,
71 seen,
72 nodename)
73 if self.track_key_path:
74 key = helpers.get_nested_key(entity.parameters.as_dict(),
75 path=self.track_key_path)
76 if key:
77 print("# " + ' < '.join(seen['__visited']))
78 out_dict = {}
79 helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
80 print(yaml.dump(out_dict,
81 default_flow_style=False))
82
83 # Reset the data collected by child entries
84 seen['__visited'] = orig_visited
85
86 return result
87
88 def _nodeinfo(self, nodename):
89 if self.track_key_path:
90 print("\n" + nodename)
91 print("-" * len(nodename))
92
93 result = super(ReclassCore, self)._nodeinfo(nodename)
94
95 if self.track_key_path:
96 key = helpers.get_nested_key(result.parameters.as_dict(),
97 path=self.track_key_path)
98 if key:
99 print("### Final result after interpolation: ###")
100 out_dict = {}
101 helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
102 print(yaml.dump(out_dict,
103 default_flow_style=False))
104 return result
105
106
107def get_core(key=None):
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300108 """Initializes reclass Core() using /etc/reclass settings"""
109
110 defaults = reclass_config.find_and_read_configfile()
111 inventory_base_uri = defaults['inventory_base_uri']
112 storage_type = defaults['storage_type']
113
114 nodes_uri, classes_uri = reclass_config.path_mangler(inventory_base_uri,
115 None, None)
116 storage = reclass.get_storage(storage_type, nodes_uri, classes_uri,
117 default_environment='base')
118
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300119 #key = '_param.keepalived_vip_interface'
120 return ReclassCore(storage, None, None, key=key)
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300121
122
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300123# def get_minion_domain():
124# """Try to get domain from the local salt minion"""
125# client = salt.cli.call.SaltCall()
126# client.parse_args(args=['pillar.items'])
127# caller = salt.cli.caller.Caller.factory(client.config)
128# result = caller.call()
129# # Warning! There is a model-related parameter
130# # TODO(ddmitriev): move the path to the parameter to a settings/defaults
131# domain = result['return']['_param']['cluster_domain']
132# return domain
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300133
134
Dennis Dmitriev94239b12017-06-23 13:18:38 +0300135def inventory_list(domain=None):
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300136 core = get_core()
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300137 inventory = core.inventory()['nodes']
Dennis Dmitriev94239b12017-06-23 13:18:38 +0300138 if domain is not None:
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300139 inventory = {key: val for (key, val) in inventory.items()
140 if key.endswith(domain)}
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300141 return inventory
142
143
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300144def nodes_list(domain=None):
145 core = get_core()
146 nodes = core._storage.enumerate_nodes()
147 if domain is not None:
148 nodes = [node for node in nodes
149 if node.endswith(domain)]
150 return nodes
151
152
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300153def get_nodeinfo(minion_id):
154 core = get_core()
155 return core.nodeinfo(minion_id)
156
157
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300158def trace_key(key, domain=None, node=None):
159 if node:
160 nodes = [node]
161 else:
162 nodes = nodes_list(domain=domain)
163
164 core = get_core(key=key)
165 for node in nodes:
166 core.nodeinfo(node)
167
168
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300169def vcp_list(domain=None, inventory=None):
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300170 """List VCP node names
171
172 Scan all nodes for the object salt.control.cluster.internal.node.XXX.name
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300173 Return set of tuples ((nodename1, domain), (nodename2, domain), ...)
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300174 """
175
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300176 inventory = inventory or inventory_list(domain=domain)
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300177 vcp_path = 'parameters.salt.control.cluster.internal.node'.split('.')
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300178 domain_path = 'parameters._param.cluster_domain'.split('.')
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300179
180 vcp_node_names = set()
181
182 for node_name, node in inventory.items():
183 vcp_nodes = helpers.get_nested_key(node, path=vcp_path)
184 if vcp_nodes is not None:
185 for vcp_node_name, vcp_node in vcp_nodes.items():
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300186 vcp_node_names.add((
187 vcp_node['name'],
188 helpers.get_nested_key(node, path=domain_path)))
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300189 return vcp_node_names
190
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300191
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300192def reclass_storage(domain=None, inventory=None):
193 """List VCP node names
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300194
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300195 Scan all nodes for the object salt.control.cluster.internal.node.XXX.name
196 """
197
198 inventory = inventory or inventory_list(domain=domain)
199 storage_path = 'parameters.reclass.storage.node'.split('.')
200
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300201 res = dict()
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300202 for node_name, node in inventory.items():
203 storage_nodes = helpers.get_nested_key(node, path=storage_path)
204 if storage_nodes is not None:
205 for storage_node_name, storage_node in storage_nodes.items():
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300206 if storage_node['domain'] not in res:
207 res[storage_node['domain']] = dict()
208 res[storage_node['domain']][storage_node_name] = storage_node
209 return res