blob: f8a3fd85bc923fb283613e4516c0ba2f6b7ca013 [file] [log] [blame]
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +02001# Copyright 2016 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
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030015from collections import defaultdict
16
17from datetime import datetime
18from pepper.libpepper import Pepper
19from tcp_tests import settings
20from tcp_tests import logger
21from tcp_tests.managers.execute_commands import ExecuteCommandsMixin
22
23LOG = logger.logger
24
25
26class SaltManager(ExecuteCommandsMixin):
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +020027 """docstring for SaltManager"""
28
Dmitry Tyzhnenkobc0f8262017-04-28 15:39:26 +030029 __config = None
30 __underlay = None
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030031 _map = {
32 'enforceState': 'enforce_state',
33 'enforceStates': 'enforce_states',
34 'runState': 'run_state',
35 'runStates': 'run_states',
36 }
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +020037
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030038 def __init__(self, config, underlay, host=None, port='6969'):
Dmitry Tyzhnenkobc0f8262017-04-28 15:39:26 +030039 self.__config = config
40 self.__underlay = underlay
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030041 self.__port = port
42 self.__host = host
43 self.__api = None
44 self.__user = settings.SALT_USER
45 self.__password = settings.SALT_PASSWORD
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +020046
Dmitry Tyzhnenkobc0f8262017-04-28 15:39:26 +030047 super(SaltManager, self).__init__(config=config, underlay=underlay)
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +020048
49 def install(self, commands):
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030050 #if self.__config.salt.salt_master_host == '0.0.0.0':
51 # # Temporary workaround. Underlay should be extended with roles
52 # salt_nodes = self.__underlay.node_names()
53 # self.__config.salt.salt_master_host = \
54 # self.__underlay.host_by_node_name(salt_nodes[0])
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +020055
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030056 self.execute_commands(commands=commands,
57 label="Install and configure salt")
58
59 @property
60 def port(self):
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030061 return self.__port
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030062
63 @property
64 def host(self):
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030065 if self.__host:
66 return self.__host
67 else:
68 #TODO(ddmitriev): consider to add a check and raise
69 # exception if 'salt_master_host' is not initialized.
70 return self.__config.salt.salt_master_host
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030071
72 @property
73 def api(self):
74 def login():
75 LOG.info("Authentication in Salt API")
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030076 self.__api.login(
77 username=self.__user,
78 password=self.__password,
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030079 eauth='pam')
80 return datetime.now()
81
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030082 if self.__api:
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030083 if (datetime.now() - self.__session_start).seconds < 5 * 60:
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030084 return self.__api
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030085 else:
86 # FIXXME: Change to debug
87 LOG.info("Session's expired")
88 self.__session_start = login()
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030089 return self.__api
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030090
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030091 url = "http://{host}:{port}".format(
92 host=self.host, port=self.port)
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030093 LOG.info("Connecting to Salt API {0}".format(url))
94 self.__api = Pepper(url)
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030095 self.__session_start = login()
Dennis Dmitriev2d60c8e2017-05-12 18:34:01 +030096 return self.__api
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +030097
98 def local(self, tgt, fun, args=None, kwargs=None):
99 return self.api.local(tgt, fun, args, kwargs, expr_form='compound')
100
101 def local_async(self, tgt, fun, args=None, kwargs=None):
102 return self.api.local_async(tgt, fun, args, kwargs)
103
104 def lookup_result(self, jid):
105 return self.api.lookup_jid(jid)
106
107 def check_result(self, r):
108 if len(r.get('return', [])) == 0:
109 raise LookupError("Result is empty or absent")
110
111 result = r['return'][0]
Dmitry Tyzhnenkobc0f8262017-04-28 15:39:26 +0300112 if len(result) == 0:
113 raise LookupError("Result is empty or absent")
Dmitry Tyzhnenko2b730a02017-04-07 19:31:32 +0300114 LOG.info("Job has result for %s nodes", result.keys())
115 fails = defaultdict(list)
116 for h in result:
117 host_result = result[h]
118 LOG.info("On %s executed:", h)
119 if isinstance(host_result, list):
120 fails[h].append(host_result)
121 continue
122 for t in host_result:
123 task = host_result[t]
124 if task['result'] is False:
125 fails[h].append(task)
126 LOG.error("%s - %s", t, task['result'])
127 else:
128 LOG.info("%s - %s", t, task['result'])
129
130 return fails if fails else None
131
132 def enforce_state(self, tgt, state, args=None, kwargs=None):
133 r = self.local(tgt=tgt, fun='state.sls', args=state)
134 f = self.check_result(r)
135 return r, f
136
137 def enforce_states(self, tgt, state, args=None, kwargs=None):
138 rets = []
139 for s in state:
140 r = self.enforce_state(tgt=tgt, state=s)
141 rets.append(r)
142 return rets
143
144 def run_state(self, tgt, state, args=None, kwargs=None):
145 return self.local(tgt=tgt, fun=state, args=args, kwargs=kwargs), None
146
147 def run_states(self, tgt, state, args=None, kwargs=None):
148 rets = []
149 for s in state:
150 r = self.run_state(tgt=tgt, state=s, args=args, kwargs=kwargs)
151 rets.append(r)
152 return rets
Artem Panchenko0594cd72017-06-12 13:25:26 +0300153
154 def get_pillar(self, tgt, pillar):
155 result = self.local(tgt=tgt, fun='pillar.get', args=pillar)
156 return result['return']