blob: 798a8e57ca48f850d50c2eb7dc326966cccd046a [file] [log] [blame]
Alex Savatieiev9b2f6512019-02-20 18:05:00 -06001import json
2import os
3import sys
4
5from copy import deepcopy
6
7from cfg_checker.common import utils, const
8from cfg_checker.common import config, logger, logger_cli, pkg_dir
9from cfg_checker.common import salt_utils
10
11node_tmpl = {
12 'role': '',
13 'node_group': '',
14 'status': const.NODE_DOWN,
15 'pillars': {},
16 'grains': {}
17}
18
19
20class SaltNodes(object):
21 def __init__(self):
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060022 logger_cli.info("# Collecting nodes")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060023 # simple salt rest client
24 self.salt = salt_utils.SaltRemote()
25
26 # Keys for all nodes
27 # this is not working in scope of 2016.8.3, will overide with list
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060028 logger_cli.debug("...collecting node names existing in the cloud")
Alex Savatieiev9df93a92019-02-27 17:40:16 -060029 try:
30 _keys = self.salt.list_keys()
31 _str = []
32 for _k, _v in _keys.iteritems():
33 _str.append("{}: {}".format(_k, len(_v)))
34 logger_cli.info("-> keys collected: {}".format(", ".join(_str)))
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060035
Alex Savatieiev9df93a92019-02-27 17:40:16 -060036 self.node_keys = {
37 'minions': _keys['minions']
38 }
39 except Exception as e:
40 _keys = None
41 self.node_keys = None
42
43 # List of minions with grains
44 _minions = self.salt.list_minions()
45 if _minions:
46 logger_cli.info("-> api reported {} active minions".format(len(_minions)))
47 elif not self.node_keys:
48 # this is the last resort
49 _minions = config.load_nodes_list()
50 logger_cli.info("-> {} nodes loaded from list file".format(len(_minions)))
51 else:
52 _minions = self.node_keys['minions']
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060053
Alex Savatieiev9df93a92019-02-27 17:40:16 -060054 # in case API not listed minions, we need all that answer ping
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060055 _active = self.salt.get_active_nodes()
Alex Savatieiev9df93a92019-02-27 17:40:16 -060056 logger_cli.info("-> nodes responded: {}".format(len(_active)))
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060057 # just inventory for faster interaction
58 # iterate through all accepted nodes and create a dict for it
59 self.nodes = {}
Alex Savatieiev9df93a92019-02-27 17:40:16 -060060 for _name in _minions:
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060061 _nc = utils.get_node_code(_name)
62 _rmap = const.all_roles_map
63 _role = _rmap[_nc] if _nc in _rmap else 'unknown'
64 _status = const.NODE_UP if _name in _active else const.NODE_DOWN
65
66 self.nodes[_name] = deepcopy(node_tmpl)
67 self.nodes[_name]['node_group'] = _nc
68 self.nodes[_name]['role'] = _role
69 self.nodes[_name]['status'] = _status
70
71 logger_cli.info("-> {} nodes collected".format(len(self.nodes)))
72
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060073 # form an all nodes compound string to use in salt
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060074 self.active_nodes_compound = self.salt.compound_string_from_list(
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060075 filter(
76 lambda nd: self.nodes[nd]['status'] == const.NODE_UP,
77 self.nodes
78 )
79 )
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060080
81 def get_nodes(self):
82 return self.nodes
83
84 def get_specific_pillar_for_nodes(self, pillar_path):
85 """Function gets pillars on given path for all nodes
86
87 :return: no return value, data pulished internally
88 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060089 logger_cli.debug("...collecting node pillars for '{}'".format(pillar_path))
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060090 _result = self.salt.pillar_get(self.active_nodes_compound, pillar_path)
91 for node, data in self.nodes.iteritems():
92 _pillar_keys = pillar_path.split(':')
93 _data = data['pillars']
94 # pre-create nested dict
95 for idx in range(0, len(_pillar_keys)-1):
96 _key = _pillar_keys[idx]
97 if _key not in _data:
98 _data[_key] = {}
99 _data = _data[_key]
100 _data[_pillar_keys[-1]] = _result[node]
101
102 def execute_script_on_active_nodes(self, script_filename, args=[]):
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600103 # Prepare script
104 _p = os.path.join(pkg_dir, 'scripts', script_filename)
105 with open(_p, 'rt') as fd:
106 _script = fd.read().splitlines()
107 _storage_path = os.path.join(
108 config.salt_file_root, config.salt_scripts_folder
109 )
110 logger_cli.debug(
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600111 "...Uploading script {} to master's file cache folder: '{}'".format(
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600112 script_filename,
113 _storage_path
114 )
115 )
116 _result = self.salt.mkdir("cfg01*", _storage_path)
117 # Form cache, source and target path
118 _cache_path = os.path.join(_storage_path, script_filename)
119 _source_path = os.path.join(
120 'salt://',
121 config.salt_scripts_folder,
122 script_filename
123 )
124 _target_path = os.path.join(
125 '/root',
126 config.salt_scripts_folder,
127 script_filename
128 )
129
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600130 logger_cli.debug("...creating file in cache '{}'".format(_cache_path))
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600131 _result = self.salt.f_touch_master(_cache_path)
132 _result = self.salt.f_append_master(_cache_path, _script)
133 # command salt to copy file to minions
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600134 logger_cli.debug("...creating script target folder '{}'".format(_cache_path))
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600135 _result = self.salt.mkdir(
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600136 self.active_nodes_compound,
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600137 os.path.join(
138 '/root',
139 config.salt_scripts_folder
140 ),
141 tgt_type="compound"
142 )
143 logger_cli.info("-> Running script to all active nodes")
144 _result = self.salt.get_file(
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600145 self.active_nodes_compound,
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600146 _source_path,
147 _target_path,
148 tgt_type="compound"
149 )
150 # execute pkg collecting script
151 logger.debug("Running script to all nodes")
152 # handle results for each node
153 _script_arguments = " ".join(args) if args else ""
154 _result = self.salt.cmd(
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600155 self.active_nodes_compound,
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600156 'cmd.run',
157 param='python {} {}'.format(_target_path, _script_arguments),
158 expr_form="compound"
159 )
160
161 # TODO: Handle error result
162
163 return _result