Add SALT API client
diff --git a/tcp_tests/managers/saltmanager.py b/tcp_tests/managers/saltmanager.py
index 1a89616..b65c74f 100644
--- a/tcp_tests/managers/saltmanager.py
+++ b/tcp_tests/managers/saltmanager.py
@@ -11,26 +11,165 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+# import time
 
-class SaltManager(object):
+from collections import defaultdict
+
+from datetime import datetime
+from pepper.libpepper import Pepper
+from tcp_tests import settings
+from tcp_tests import logger
+from tcp_tests.managers.execute_commands import ExecuteCommandsMixin
+
+LOG = logger.logger
+
+
+class SaltManager(ExecuteCommandsMixin):
     """docstring for SaltManager"""
 
-    __config = None
-    __underlay = None
+    _config = None
+    _underlay = None
+    _map = {
+        'enforceState': 'enforce_state',
+        'enforceStates': 'enforce_states',
+        'runState': 'run_state',
+        'runStates': 'run_states',
+    }
 
-    def __init__(self, config, underlay):
-        self.__config = config
-        self.__underlay = underlay
-
+    def __init__(self, config, underlay, host=None, port='6969'):
+        self._config = config
+        self._underlay = underlay
+        self._port = port
+        self._host = host
+        self._api = None
+        self._user = settings.SALT_USER
+        self._password = settings.SALT_PASSWORD
+        self._salt = self
 
         super(SaltManager, self).__init__()
 
     def install(self, commands):
-        if self.__config.salt.salt_master_host == '0.0.0.0':
-            # Temporary workaround. Underlay should be extended with roles
-            salt_nodes = self.__underlay.node_names()
-            self.__config.salt.salt_master_host = \
-                self.__underlay.host_by_node_name(salt_nodes[0])
+        if commands[0].get('do'):
+            self.install2(commands)
+        else:
+            self.install1(commands)
 
-        self.__underlay.execute_commands(commands=commands,
-                                         label="Install and configure salt")
+    def install1(self, commands):
+        if self._config.salt.salt_master_host == '0.0.0.0':
+            # Temporary workaround. Underlay should be extended with roles
+            salt_nodes = self._underlay.node_names()
+            self._config.salt.salt_master_host = \
+                self._underlay.host_by_node_name(salt_nodes[0])
+
+        # self._underlay.execute_commands(commands=commands,
+        #                                  label="Install and configure salt")
+        self.execute_commands(commands=commands,
+                              label="Install and configure salt")
+
+    def install2(self, commands):
+        if self._config.salt.salt_master_host == '0.0.0.0':
+            # Temporary workaround. Underlay should be extended with roles
+            salt_nodes = self._underlay.node_names()
+            self._config.salt.salt_master_host = \
+                self._underlay.host_by_node_name(salt_nodes[0])
+
+        # self.run_commands(commands=commands,
+        #                   label="Install and configure salt")
+        self.execute_commands(commands=commands,
+                              label="Install and configure salt")
+
+    @property
+    def port(self):
+        return self._port
+
+    @property
+    def host(self):
+        if self._host:
+            return self._host
+        elif self._config.salt.salt_master_host == '0.0.0.0':
+            # Temporary workaround. Underlay should be extended with roles
+            salt_nodes = self._underlay.node_names()
+            self._config.salt.salt_master_host = \
+                self._underlay.host_by_node_name(salt_nodes[0])
+
+        return self._config.salt.salt_master_host
+
+    @property
+    def api(self):
+        def login():
+            LOG.info("Authentication in Salt API")
+            self._api.login(
+                username=self._user,
+                password=self._password,
+                eauth='pam')
+            return datetime.now()
+
+        if self._api:
+            if (datetime.now() - self.__session_start).seconds < 5 * 60:
+                return self._api
+            else:
+                # FIXXME: Change to debug
+                LOG.info("Session's expired")
+                self.__session_start = login()
+                return self._api
+
+        LOG.info("Connect to Salt API")
+        url = "http://{host}:{port}".format(
+            host=self.host, port=self.port)
+        self._api = Pepper(url)
+        self.__session_start = login()
+        return self._api
+
+    def local(self, tgt, fun, args=None, kwargs=None):
+        return self.api.local(tgt, fun, args, kwargs, expr_form='compound')
+
+    def local_async(self, tgt, fun, args=None, kwargs=None):
+        return self.api.local_async(tgt, fun, args, kwargs)
+
+    def lookup_result(self, jid):
+        return self.api.lookup_jid(jid)
+
+    def check_result(self, r):
+        if len(r.get('return', [])) == 0:
+            raise LookupError("Result is empty or absent")
+
+        result = r['return'][0]
+        LOG.info("Job has result for %s nodes", result.keys())
+        fails = defaultdict(list)
+        for h in result:
+            host_result = result[h]
+            LOG.info("On %s executed:", h)
+            if isinstance(host_result, list):
+                fails[h].append(host_result)
+                continue
+            for t in host_result:
+                task = host_result[t]
+                if task['result'] is False:
+                    fails[h].append(task)
+                    LOG.error("%s - %s", t, task['result'])
+                else:
+                    LOG.info("%s - %s", t, task['result'])
+
+        return fails if fails else None
+
+    def enforce_state(self, tgt, state, args=None, kwargs=None):
+        r = self.local(tgt=tgt, fun='state.sls', args=state)
+        f = self.check_result(r)
+        return r, f
+
+    def enforce_states(self, tgt, state, args=None, kwargs=None):
+        rets = []
+        for s in state:
+            r = self.enforce_state(tgt=tgt, state=s)
+            rets.append(r)
+        return rets
+
+    def run_state(self, tgt, state, args=None, kwargs=None):
+        return self.local(tgt=tgt, fun=state, args=args, kwargs=kwargs), None
+
+    def run_states(self, tgt, state, args=None, kwargs=None):
+        rets = []
+        for s in state:
+            r = self.run_state(tgt=tgt, state=s, args=args, kwargs=kwargs)
+            rets.append(r)
+        return rets