blob: 62ccae78df09ce8b408b3d3a58228c1895abf773 [file] [log] [blame]
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +00001import os
2import yaml
3import requests
4import re
Oleksii Zhurbae592ed12018-06-21 18:01:09 -05005import sys, traceback
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -05006import time
Oleksii Zhurba3dbed242017-10-31 19:58:53 +00007
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +00008
Mikhail Chernikc492a682018-08-27 22:41:17 +02009class AuthenticationError(Exception):
10 pass
11
12
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +000013class salt_remote:
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050014 def __init__(self):
15 self.config = get_configuration()
16 self.skipped_nodes = self.config.get('skipped_nodes') or []
17 self.url = self.config['SALT_URL'].strip()
18 if not re.match("^(http|https)://", self.url):
Valentyn Khalin81dd23d2018-09-20 15:39:07 +030019 raise AuthenticationError("Salt URL should start \
20 with http or https, given - {}".format(url))
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050021 self.login_payload = {'username': self.config['SALT_USERNAME'],
22 'password': self.config['SALT_PASSWORD'], 'eauth': 'pam'}
23 # TODO: proxies
24 self.proxies = {"http": None, "https": None}
25 self.expires = ''
26 self.cookies = []
27 self.headers = {'Accept': 'application/json'}
28 self._login()
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +000029
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050030 def _login (self):
Oleksii Zhurbae592ed12018-06-21 18:01:09 -050031 try:
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050032 login_request = requests.post(os.path.join(self.url, 'login'),
33 headers={'Accept': 'application/json'},
34 data=self.login_payload,
35 proxies=self.proxies)
Mikhail Chernikc492a682018-08-27 22:41:17 +020036 if not login_request.ok:
37 raise AuthenticationError("Authentication to SaltMaster failed")
Mikhail Chernikc492a682018-08-27 22:41:17 +020038 except Exception as e:
39 print ("\033[91m\nConnection to SaltMaster "
40 "was not established.\n"
41 "Please make sure that you "
42 "provided correct credentials.\n"
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050043 "Error message: {}\033[0m\n".format(e.message or e))
Oleksii Zhurbae592ed12018-06-21 18:01:09 -050044 traceback.print_exc(file=sys.stdout)
45 sys.exit()
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -050046 self.expire = login_request.json()['return'][0]['expire']
47 self.cookies = login_request.cookies
48 self.headers['X-Auth-Token'] = login_request.json()['return'][0]['token']
49
50 def cmd(self, tgt, fun='cmd.run', param=None, expr_form=None, tgt_type=None, check_status=False, retries=3):
51 if self.expire < time.time() + 300:
52 self.headers['X-Auth-Token'] = self._login()
53 accept_key_payload = {'fun': fun, 'tgt': tgt, 'client': 'local',
54 'expr_form': expr_form, 'tgt_type': tgt_type,
55 'timeout': self.config['salt_timeout']}
56 if param:
57 accept_key_payload['arg'] = param
58
59 for i in range(retries):
60 request = requests.post(self.url, headers=self.headers,
61 data=accept_key_payload,
62 cookies=self.cookies,
63 proxies=self.proxies)
64 if not request.ok or not isinstance(request.json()['return'][0], dict):
65 print("Salt master is not responding or response is incorrect. Output: {}".format(request))
66 continue
67 response = request.json()['return'][0]
68 result = {key: response[key] for key in response if key not in self.skipped_nodes}
69 if check_status:
70 if False in result.values():
71 print(
72 "One or several nodes are not responding. Output {}".format(json.dumps(result, indent=4)))
73 continue
74 break
75 else:
76 raise Exception("Error with Salt Master response")
77 return result
78
79 def test_ping(self, tgt, expr_form='pillar'):
80 return self.cmd(tgt=tgt, fun='test.ping', param=None, expr_form=expr_form)
81
82 def cmd_any(self, tgt, param=None, expr_form='pillar'):
83 """
84 This method returns first non-empty result on node or nodes.
85 If all nodes returns nothing, then exception is thrown.
86 """
87 response = self.cmd(tgt=tgt, param=param, expr_form=expr_form)
88 for node in response.keys():
89 if response[node] or response[node] == '':
90 return response[node]
91 else:
92 raise Exception("All minions are down")
93
94 def pillar_get(self, tgt='salt:master', param=None, expr_form='pillar', fail_if_empty=False):
95 """
96 This method is for fetching pillars only.
97 Returns value for pillar, False (if no such pillar) or if fail_if_empty=True - exception
98 """
99 response = self.cmd(tgt=tgt, fun='pillar.get', param=param, expr_form=expr_form)
100 for node in response.keys():
101 if response[node] or response[node] != '':
102 return response[node]
103 else:
104 if fail_if_empty:
105 raise Exception("No pillar found or it is empty.")
106 else:
107 return False
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000108
109
110def init_salt_client():
111 local = salt_remote()
112 return local
113
114
Mikhail Chernik714596e2018-08-10 21:19:07 +0200115def list_to_target_string(node_list, separator, add_spaces=True):
116 if add_spaces:
117 separator = ' ' + separator.strip() + ' '
118 return separator.join(node_list)
Oleksii Zhurbae0668ae2017-10-27 23:58:18 +0000119
120
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000121def calculate_groups():
Oleksii Zhurbae0668ae2017-10-27 23:58:18 +0000122 config = get_configuration()
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000123 local_salt_client = init_salt_client()
Oleksii Zhurba30122e12018-03-29 14:01:50 -0500124 node_groups = {}
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000125 nodes_names = set ()
126 expr_form = ''
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500127 all_nodes = set(local_salt_client.test_ping(tgt='*',expr_form=None))
128 print all_nodes
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500129 if 'groups' in config.keys() and 'PB_GROUPS' in os.environ.keys() and \
130 os.environ['PB_GROUPS'].lower() != 'false':
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000131 nodes_names.update(config['groups'].keys())
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500132 expr_form = 'compound'
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000133 else:
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500134 for node in all_nodes:
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000135 index = re.search('[0-9]{1,3}$', node.split('.')[0])
136 if index:
137 nodes_names.add(node.split('.')[0][:-len(index.group(0))])
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000138 else:
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000139 nodes_names.add(node)
140 expr_form = 'pcre'
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000141
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500142 gluster_nodes = local_salt_client.test_ping(tgt='I@salt:control and '
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500143 'I@glusterfs:server',
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500144 expr_form='compound')
145 kvm_nodes = local_salt_client.test_ping(tgt='I@salt:control and not '
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500146 'I@glusterfs:server',
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500147 expr_form='compound')
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500148
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000149 for node_name in nodes_names:
150 skipped_groups = config.get('skipped_groups') or []
151 if node_name in skipped_groups:
152 continue
153 if expr_form == 'pcre':
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500154 nodes = local_salt_client.test_ping(tgt='{}[0-9]{{1,3}}'.format(node_name),
155 expr_form=expr_form)
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000156 else:
Oleksii Zhurba4bfd2ee2019-04-10 21:56:58 -0500157 nodes = local_salt_client.test_ping(tgt=config['groups'][node_name],
158 expr_form=expr_form)
Oleksii Zhurbae592ed12018-06-21 18:01:09 -0500159 if nodes == {}:
160 continue
161
162 node_groups[node_name]=[x for x in nodes
163 if x not in config['skipped_nodes']
164 if x not in gluster_nodes.keys()
165 if x not in kvm_nodes.keys()]
166 all_nodes = set(all_nodes - set(node_groups[node_name]))
167 if node_groups[node_name] == []:
168 del node_groups[node_name]
169 if kvm_nodes:
170 node_groups['kvm'] = kvm_nodes.keys()
171 node_groups['kvm_gluster'] = gluster_nodes.keys()
172 all_nodes = set(all_nodes - set(kvm_nodes.keys()))
173 all_nodes = set(all_nodes - set(gluster_nodes.keys()))
174 if all_nodes:
175 print ("These nodes were not collected {0}. Check config (groups section)".format(all_nodes))
Oleksii Zhurbad0ae87f2018-03-26 13:36:25 -0500176 return node_groups
Oleksii Zhurbae0dedb52018-01-16 00:55:25 +0000177
178
Oleksii Zhurbae0668ae2017-10-27 23:58:18 +0000179def get_configuration():
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000180 """function returns configuration for environment
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000181 and for test if it's specified"""
182 global_config_file = os.path.join(
183 os.path.dirname(os.path.abspath(__file__)), "../global_config.yaml")
184 with open(global_config_file, 'r') as file:
185 global_config = yaml.load(file)
Oleksii Zhurbae0668ae2017-10-27 23:58:18 +0000186 for param in global_config.keys():
187 if param in os.environ.keys():
188 if ',' in os.environ[param]:
Oleksii Zhurba3dbed242017-10-31 19:58:53 +0000189 global_config[param] = []
Oleksii Zhurbae0668ae2017-10-27 23:58:18 +0000190 for item in os.environ[param].split(','):
191 global_config[param].append(item)
192 else:
Oleksii Zhurba3dbed242017-10-31 19:58:53 +0000193 global_config[param] = os.environ[param]
Oleksii Zhurbaa10927b2017-09-27 22:09:23 +0000194
195 return global_config