refactoring is on the way
diff --git a/wally/fuel_rest_api.py b/wally/fuel_rest_api.py
index 03beb37..2addaf4 100644
--- a/wally/fuel_rest_api.py
+++ b/wally/fuel_rest_api.py
@@ -1,29 +1,38 @@
 import re
+import abc
 import json
-import time
 import logging
 import urllib.request
 import urllib.parse
-from typing import Dict, Any, Iterator, Match
-from functools import partial, wraps
+from typing import Dict, Any, Iterator, Match, List, Callable
+from functools import partial
 
 import netaddr
-
-from keystoneclient.v2_0 import Client as keystoneclient
 from keystoneclient import exceptions
+from keystoneclient.v2_0 import Client as keystoneclient
 
 
 logger = logging.getLogger("wally.fuel_api")
 
 
-class Urllib2HTTP:
+class Connection(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def do(self, method: str, path: str, params: Dict = None) -> Dict:
+        pass
+
+    @abc.abstractmethod
+    def get(self, path: str, params: Dict = None) -> Dict:
+        pass
+
+
+class Urllib2HTTP(Connection):
     """
     class for making HTTP requests
     """
 
     allowed_methods = ('get', 'put', 'post', 'delete', 'patch', 'head')
 
-    def __init__(self, root_url: str, headers: Dict[str, str]=None):
+    def __init__(self, root_url: str, headers: Dict[str, str] = None) -> None:
         """
         """
         if root_url.endswith('/'):
@@ -31,12 +40,14 @@
         else:
             self.root_url = root_url
 
-        self.headers = headers if headers is not None else {}
+        self.host = urllib.parse.urlparse(self.root_url).hostname
 
-    def host(self) -> str:
-        return self.root_url.split('/')[2]
+        if headers is None:
+            self.headers = {}  # type: Dict[str, str]
+        else:
+            self.headers  = headers
 
-    def do(self, method: str, path: str, params: Dict[Any, Any]=None) -> Dict[str, Any]:
+    def do(self, method: str, path: str, params: Dict = None) -> Dict:
         if path.startswith('/'):
             url = self.root_url + path
         else:
@@ -120,11 +131,11 @@
     name = None
     id = None
 
-    def __init__(self, conn, **kwargs):
+    def __init__(self, conn, **kwargs) -> None:
         self.__dict__.update(kwargs)
         self.__connection__ = conn
 
-    def __str__(self):
+    def __str__(self) -> str:
         res = ["{0}({1}):".format(self.__class__.__name__, self.name)]
         for k, v in sorted(self.__dict__.items()):
             if k.startswith('__') or k.endswith('__'):
@@ -133,12 +144,12 @@
                 res.append("    {0}={1!r}".format(k, v))
         return "\n".join(res)
 
-    def __getitem__(self, item):
+    def __getitem__(self, item: str) -> Any:
         return getattr(self, item)
 
 
-def make_call(method: str, url: str):
-    def closure(obj, entire_obj=None, **data):
+def make_call(method: str, url: str) -> Callable[[Any, Any], Dict]:
+    def closure(obj: Any, entire_obj: Any = None, **data) -> Dict:
         inline_params_vals = {}
         for name in get_inline_param_list(url):
             if name in data:
@@ -160,30 +171,10 @@
 GET = partial(make_call, 'get')
 DELETE = partial(make_call, 'delete')
 
-
-def with_timeout(tout, message):
-    def closure(func):
-        @wraps(func)
-        def closure2(*dt, **mp):
-            ctime = time.time()
-            etime = ctime + tout
-
-            while ctime < etime:
-                if func(*dt, **mp):
-                    return
-                sleep_time = ctime + 1 - time.time()
-                if sleep_time > 0:
-                    time.sleep(sleep_time)
-                ctime = time.time()
-            raise RuntimeError("Timeout during " + message)
-        return closure2
-    return closure
-
-
 # -------------------------------  ORM ----------------------------------------
 
 
-def get_fuel_info(url):
+def get_fuel_info(url: str) -> 'FuelInfo':
     conn = Urllib2HTTP(url)
     return FuelInfo(conn)
 
@@ -198,27 +189,27 @@
     get_info = GET('api/releases')
 
     @property
-    def nodes(self):
+    def nodes(self) -> 'NodeList':
         """Get all fuel nodes"""
         return NodeList([Node(self.__connection__, **node) for node
                          in self.get_nodes()])
 
     @property
-    def free_nodes(self):
+    def free_nodes(self) -> 'NodeList':
         """Get unallocated nodes"""
         return NodeList([Node(self.__connection__, **node) for node in
                          self.get_nodes() if not node['cluster']])
 
     @property
-    def clusters(self):
+    def clusters(self) -> List['Cluster']:
         """List clusters in fuel"""
         return [Cluster(self.__connection__, **cluster) for cluster
                 in self.get_clusters()]
 
-    def get_version(self):
+    def get_version(self) -> List[int]:
         for info in self.get_info():
             vers = info['version'].split("-")[1].split('.')
-            return map(int, vers)
+            return list(map(int, vers))
         raise ValueError("No version found")
 
 
@@ -228,23 +219,18 @@
     get_info = GET('/api/nodes/{id}')
     get_interfaces = GET('/api/nodes/{id}/interfaces')
 
-    def get_network_data(self):
+    def get_network_data(self) -> Dict:
         """Returns node network data"""
-        node_info = self.get_info()
-        return node_info.get('network_data')
+        return self.get_info().get('network_data')
 
-    def get_roles(self, pending=False):
+    def get_roles(self) -> List[str]:
         """Get node roles
 
         Returns: (roles, pending_roles)
         """
-        node_info = self.get_info()
-        if pending:
-            return node_info.get('roles'), node_info.get('pending_roles')
-        else:
-            return node_info.get('roles')
+        return self.get_info().get('roles')
 
-    def get_ip(self, network='public'):
+    def get_ip(self, network='public') -> netaddr.IPAddress:
         """Get node ip
 
         :param network: network to pick
@@ -267,7 +253,7 @@
     allowed_roles = ['controller', 'compute', 'cinder', 'ceph-osd', 'mongo',
                      'zabbix-server']
 
-    def __getattr__(self, name):
+    def __getattr__(self, name: str) -> List[Node]:
         if name in self.allowed_roles:
             return [node for node in self if name in node.roles]
 
@@ -280,13 +266,13 @@
     get_attributes = GET('api/clusters/{id}/attributes')
     _get_nodes = GET('api/nodes?cluster_id={id}')
 
-    def __init__(self, *dt, **mp):
+    def __init__(self, *dt, **mp) -> None:
         super(Cluster, self).__init__(*dt, **mp)
         self.nodes = NodeList([Node(self.__connection__, **node) for node in
                                self._get_nodes()])
         self.network_roles = {}
 
-    def check_exists(self):
+    def check_exists(self) -> bool:
         """Check if cluster exists"""
         try:
             self.get_status()
@@ -296,7 +282,7 @@
                 return False
             raise
 
-    def get_openrc(self):
+    def get_openrc(self) -> Dict[str, str]:
         access = self.get_attributes()['editable']['access']
         creds = {'username': access['user']['value'],
                  'password': access['password']['value'],
@@ -313,31 +299,31 @@
                 self.get_networks()['public_vip'])
         return creds
 
-    def get_nodes(self):
+    def get_nodes(self) -> Iterator[Node]:
         for node_descr in self._get_nodes():
             yield Node(self.__connection__, **node_descr)
 
 
-def reflect_cluster(conn, cluster_id):
+def reflect_cluster(conn: Connection, cluster_id: int) -> Cluster:
     """Returns cluster object by id"""
     c = Cluster(conn, id=cluster_id)
     c.nodes = NodeList(list(c.get_nodes()))
     return c
 
 
-def get_all_nodes(conn):
+def get_all_nodes(conn: Connection) -> Iterator[Node]:
     """Get all nodes from Fuel"""
     for node_desc in conn.get('api/nodes'):
         yield Node(conn, **node_desc)
 
 
-def get_all_clusters(conn):
+def get_all_clusters(conn: Connection) -> Iterator[Cluster]:
     """Get all clusters"""
     for cluster_desc in conn.get('api/clusters'):
         yield Cluster(conn, **cluster_desc)
 
 
-def get_cluster_id(conn, name):
+def get_cluster_id(conn: Connection, name: str) -> int:
     """Get cluster id by name"""
     for cluster in get_all_clusters(conn):
         if cluster.name == name: