blob: b10f83d86b6282c938f364cf29143e217196a159 [file] [log] [blame]
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02001import abc
koder aka kdanilov108ac362017-01-19 20:17:16 +02002import logging
3from typing import Any, Set, Dict, Optional, NamedTuple
4
koder aka kdanilov73084622016-11-16 21:51:08 +02005from .ssh_utils import ConnCreds
koder aka kdanilov108ac362017-01-19 20:17:16 +02006from .common_types import IPAddr, IStorable
koder aka kdanilov73084622016-11-16 21:51:08 +02007
8
9RPCCreds = NamedTuple("RPCCreds", [("addr", IPAddr), ("key_file", str), ("cert_file", str)])
koder aka kdanilov108ac362017-01-19 20:17:16 +020010logger = logging.getLogger("wally")
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020011
12
koder aka kdanilov7f59d562016-12-26 01:34:23 +020013class NodeInfo(IStorable):
koder aka kdanilov73084622016-11-16 21:51:08 +020014 """Node information object, result of discovery process or config parsing"""
koder aka kdanilov7f59d562016-12-26 01:34:23 +020015 def __init__(self, ssh_creds: ConnCreds, roles: Set[str], params: Dict[str, Any] = None) -> None:
koder aka kdanilov73084622016-11-16 21:51:08 +020016 # ssh credentials
17 self.ssh_creds = ssh_creds
koder aka kdanilov7f59d562016-12-26 01:34:23 +020018
koder aka kdanilov73084622016-11-16 21:51:08 +020019 # credentials for RPC connection
20 self.rpc_creds = None # type: Optional[RPCCreds]
koder aka kdanilov7f59d562016-12-26 01:34:23 +020021
koder aka kdanilov73084622016-11-16 21:51:08 +020022 self.roles = roles
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020023 self.os_vm_id = None # type: Optional[int]
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020024 self.params = {} # type: Dict[str, Any]
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020025 if params is not None:
26 self.params = params
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020027
koder aka kdanilov108ac362017-01-19 20:17:16 +020028 @property
koder aka kdanilov73084622016-11-16 21:51:08 +020029 def node_id(self) -> str:
30 return "{0.host}:{0.port}".format(self.ssh_creds.addr)
31
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020032 def __str__(self) -> str:
koder aka kdanilov108ac362017-01-19 20:17:16 +020033 return self.node_id
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020034
35 def __repr__(self) -> str:
36 return str(self)
37
koder aka kdanilov7f59d562016-12-26 01:34:23 +020038 def raw(self) -> Dict[str, Any]:
39 dct = self.__dict__.copy()
40
41 if self.rpc_creds is not None:
42 dct['rpc_creds'] = list(self.rpc_creds)
43
44 dct['ssh_creds'] = self.ssh_creds.raw()
45 dct['roles'] = list(self.roles)
46 return dct
47
48 @classmethod
49 def fromraw(cls, data: Dict[str, Any]) -> 'NodeInfo':
50 data = data.copy()
51 if data['rpc_creds'] is not None:
52 data['rpc_creds'] = RPCCreds(*data['rpc_creds'])
53
54 data['ssh_creds'] = ConnCreds.fromraw(data['ssh_creds'])
55 data['roles'] = set(data['roles'])
koder aka kdanilova732a602017-02-01 20:29:56 +020056 obj = cls.__new__(cls) # type: ignore
koder aka kdanilov7f59d562016-12-26 01:34:23 +020057 obj.__dict__.update(data)
58 return obj
59
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020060
61class ISSHHost(metaclass=abc.ABCMeta):
62 """Minimal interface, required to setup RPC connection"""
63 info = None # type: NodeInfo
64
65 @abc.abstractmethod
66 def run(self, cmd: str, timeout: int = 60, nolog: bool = False) -> str:
67 pass
68
69 @abc.abstractmethod
koder aka kdanilov73084622016-11-16 21:51:08 +020070 def __str__(self) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020071 pass
72
73 @abc.abstractmethod
koder aka kdanilov73084622016-11-16 21:51:08 +020074 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020075 pass
76
77 @abc.abstractmethod
koder aka kdanilove7e1a4d2016-12-17 20:29:52 +020078 def put_to_file(self, path: Optional[str], content: bytes) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020079 pass
80
koder aka kdanilov73084622016-11-16 21:51:08 +020081 def __enter__(self) -> 'ISSHHost':
82 return self
83
84 def __exit__(self, x, y, z) -> bool:
85 self.disconnect()
86 return False
87
koder aka kdanilova732a602017-02-01 20:29:56 +020088 @property
89 def node_id(self) -> str:
90 return self.info.node_id
91
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020092
93class IRPCNode(metaclass=abc.ABCMeta):
94 """Remote filesystem interface"""
95 info = None # type: NodeInfo
koder aka kdanilov70227062016-11-26 23:23:21 +020096 conn = None # type: Any
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020097 rpc_log_file = None # type: str
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020098
koder aka kdanilov108ac362017-01-19 20:17:16 +020099 @property
100 def node_id(self) -> str:
101 return self.info.node_id
102
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200103 @abc.abstractmethod
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200104 def __str__(self) -> str:
105 pass
106
107 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200108 def run(self, cmd: str, timeout: int = 60, nolog: bool = False, check_timeout: float = 0.01) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200109 pass
110
111 @abc.abstractmethod
kdanylov aka kodercdfcdaf2017-04-29 10:03:39 +0300112 def copy_file(self, local_path: str, remote_path: str = None,
113 expanduser: bool = False, compress: bool = False) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200114 pass
115
116 @abc.abstractmethod
kdanylov aka kodercdfcdaf2017-04-29 10:03:39 +0300117 def get_file_content(self, path: str, expanduser: bool = False, compress: bool = False) -> bytes:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200118 pass
119
120 @abc.abstractmethod
kdanylov aka kodercdfcdaf2017-04-29 10:03:39 +0300121 def put_to_file(self, path: Optional[str], content: bytes, expanduser: bool = False, compress: bool = False) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200122 pass
123
124 @abc.abstractmethod
kdanylov aka kodercdfcdaf2017-04-29 10:03:39 +0300125 def stat_file(self, path:str, expanduser: bool = False) -> Any:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200126 pass
127
128 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200129 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200130 pass
131
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200132 @abc.abstractmethod
133 def upload_plugin(self, name: str, code: bytes, version: str = None) -> None:
134 pass
135
koder aka kdanilov73084622016-11-16 21:51:08 +0200136 def __enter__(self) -> 'IRPCNode':
137 return self
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200138
koder aka kdanilov73084622016-11-16 21:51:08 +0200139 def __exit__(self, x, y, z) -> bool:
140 self.disconnect()
141 return False
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200142