blob: 265c37aaec9bf63f1d6cacf863b63467b1bc1e51 [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:
46 self.track_key_path = key.split('.')
47 if 'parameters' not in self.track_key_path:
48 raise Exception("Please use the key path starting from 'parameters'.")
49 # Remove the first 'parameters' element because the model entities
50 # keep parameters in different object format.
51 self.track_key_path = self.track_key_path[1:]
52
53 super(ReclassCore, self).__init__(storage, class_mappings, input_data)
54
55 def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None):
56 if seen is None:
57 seen = {}
58 if '__visited' not in seen:
59 seen['__visited'] = []
60
61 orig_visited = copy.deepcopy(seen['__visited'])
62 seen['__visited'].append(entity.name)
63
64 result = super(ReclassCore, self)._recurse_entity(entity,
65 merge_base,
66 seen,
67 nodename)
68 if self.track_key_path:
69 key = helpers.get_nested_key(entity.parameters.as_dict(),
70 path=self.track_key_path)
71 if key:
72 print("# " + ' < '.join(seen['__visited']))
73 out_dict = {}
74 helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
75 print(yaml.dump(out_dict,
76 default_flow_style=False))
77
78 # Reset the data collected by child entries
79 seen['__visited'] = orig_visited
80
81 return result
82
83 def _nodeinfo(self, nodename):
84 if self.track_key_path:
85 print("\n" + nodename)
86 print("-" * len(nodename))
87
88 result = super(ReclassCore, self)._nodeinfo(nodename)
89
90 if self.track_key_path:
91 key = helpers.get_nested_key(result.parameters.as_dict(),
92 path=self.track_key_path)
93 if key:
94 print("### Final result after interpolation: ###")
95 out_dict = {}
96 helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
97 print(yaml.dump(out_dict,
98 default_flow_style=False))
99 return result
100
101
102def get_core(key=None):
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300103 """Initializes reclass Core() using /etc/reclass settings"""
104
105 defaults = reclass_config.find_and_read_configfile()
106 inventory_base_uri = defaults['inventory_base_uri']
107 storage_type = defaults['storage_type']
108
109 nodes_uri, classes_uri = reclass_config.path_mangler(inventory_base_uri,
110 None, None)
111 storage = reclass.get_storage(storage_type, nodes_uri, classes_uri,
112 default_environment='base')
113
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300114 #key = '_param.keepalived_vip_interface'
115 return ReclassCore(storage, None, None, key=key)
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300116
117
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300118# def get_minion_domain():
119# """Try to get domain from the local salt minion"""
120# client = salt.cli.call.SaltCall()
121# client.parse_args(args=['pillar.items'])
122# caller = salt.cli.caller.Caller.factory(client.config)
123# result = caller.call()
124# # Warning! There is a model-related parameter
125# # TODO(ddmitriev): move the path to the parameter to a settings/defaults
126# domain = result['return']['_param']['cluster_domain']
127# return domain
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300128
129
Dennis Dmitriev94239b12017-06-23 13:18:38 +0300130def inventory_list(domain=None):
Dennis Dmitriev1110ac52017-06-22 21:07:37 +0300131 core = get_core()
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300132 inventory = core.inventory()['nodes']
Dennis Dmitriev94239b12017-06-23 13:18:38 +0300133 if domain is not None:
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300134 inventory = {key: val for (key, val) in inventory.items()
135 if key.endswith(domain)}
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300136 return inventory
137
138
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300139def nodes_list(domain=None):
140 core = get_core()
141 nodes = core._storage.enumerate_nodes()
142 if domain is not None:
143 nodes = [node for node in nodes
144 if node.endswith(domain)]
145 return nodes
146
147
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300148def get_nodeinfo(minion_id):
149 core = get_core()
150 return core.nodeinfo(minion_id)
151
152
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300153def trace_key(key, domain=None, node=None):
154 if node:
155 nodes = [node]
156 else:
157 nodes = nodes_list(domain=domain)
158
159 core = get_core(key=key)
160 for node in nodes:
161 core.nodeinfo(node)
162
163
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300164def vcp_list(domain=None, inventory=None):
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300165 """List VCP node names
166
167 Scan all nodes for the object salt.control.cluster.internal.node.XXX.name
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300168 Return set of tuples ((nodename1, domain), (nodename2, domain), ...)
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300169 """
170
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300171 inventory = inventory or inventory_list(domain=domain)
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300172 vcp_path = 'parameters.salt.control.cluster.internal.node'.split('.')
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300173 domain_path = 'parameters._param.cluster_domain'.split('.')
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300174
175 vcp_node_names = set()
176
177 for node_name, node in inventory.items():
178 vcp_nodes = helpers.get_nested_key(node, path=vcp_path)
179 if vcp_nodes is not None:
180 for vcp_node_name, vcp_node in vcp_nodes.items():
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300181 vcp_node_names.add((
182 vcp_node['name'],
183 helpers.get_nested_key(node, path=domain_path)))
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300184 return vcp_node_names
185
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300186
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300187def reclass_storage(domain=None, inventory=None):
188 """List VCP node names
Dennis Dmitrievde847d92017-06-26 18:58:05 +0300189
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300190 Scan all nodes for the object salt.control.cluster.internal.node.XXX.name
191 """
192
193 inventory = inventory or inventory_list(domain=domain)
194 storage_path = 'parameters.reclass.storage.node'.split('.')
195
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300196 res = dict()
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300197 for node_name, node in inventory.items():
198 storage_nodes = helpers.get_nested_key(node, path=storage_path)
199 if storage_nodes is not None:
200 for storage_node_name, storage_node in storage_nodes.items():
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300201 if storage_node['domain'] not in res:
202 res[storage_node['domain']] = dict()
203 res[storage_node['domain']][storage_node_name] = storage_node
204 return res