blob: bc07bb34b99c5b1e9fd42c7a8ce389d9ede8b044 [file] [log] [blame]
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02001import abc
koder aka kdanilov7f59d562016-12-26 01:34:23 +02002from typing import Any, Set, Dict, NamedTuple, Optional
koder aka kdanilov73084622016-11-16 21:51:08 +02003from .ssh_utils import ConnCreds
4from .common_types import IPAddr
koder aka kdanilov7f59d562016-12-26 01:34:23 +02005from .result_classes import IStorable
koder aka kdanilov73084622016-11-16 21:51:08 +02006
7
8RPCCreds = NamedTuple("RPCCreds", [("addr", IPAddr), ("key_file", str), ("cert_file", str)])
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02009
10
koder aka kdanilov7f59d562016-12-26 01:34:23 +020011class NodeInfo(IStorable):
koder aka kdanilov73084622016-11-16 21:51:08 +020012 """Node information object, result of discovery process or config parsing"""
koder aka kdanilov7f59d562016-12-26 01:34:23 +020013 def __init__(self, ssh_creds: ConnCreds, roles: Set[str], params: Dict[str, Any] = None) -> None:
koder aka kdanilov73084622016-11-16 21:51:08 +020014 # ssh credentials
15 self.ssh_creds = ssh_creds
koder aka kdanilov7f59d562016-12-26 01:34:23 +020016
koder aka kdanilov73084622016-11-16 21:51:08 +020017 # credentials for RPC connection
18 self.rpc_creds = None # type: Optional[RPCCreds]
koder aka kdanilov7f59d562016-12-26 01:34:23 +020019
koder aka kdanilov73084622016-11-16 21:51:08 +020020 self.roles = roles
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020021 self.os_vm_id = None # type: Optional[int]
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020022 self.params = {} # type: Dict[str, Any]
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020023 if params is not None:
24 self.params = params
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020025
koder aka kdanilov73084622016-11-16 21:51:08 +020026 def node_id(self) -> str:
27 return "{0.host}:{0.port}".format(self.ssh_creds.addr)
28
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020029 def __str__(self) -> str:
30 return self.node_id()
31
32 def __repr__(self) -> str:
33 return str(self)
34
koder aka kdanilov7f59d562016-12-26 01:34:23 +020035 def raw(self) -> Dict[str, Any]:
36 dct = self.__dict__.copy()
37
38 if self.rpc_creds is not None:
39 dct['rpc_creds'] = list(self.rpc_creds)
40
41 dct['ssh_creds'] = self.ssh_creds.raw()
42 dct['roles'] = list(self.roles)
43 return dct
44
45 @classmethod
46 def fromraw(cls, data: Dict[str, Any]) -> 'NodeInfo':
47 data = data.copy()
48 if data['rpc_creds'] is not None:
49 data['rpc_creds'] = RPCCreds(*data['rpc_creds'])
50
51 data['ssh_creds'] = ConnCreds.fromraw(data['ssh_creds'])
52 data['roles'] = set(data['roles'])
53 obj = cls.__new__(cls)
54 obj.__dict__.update(data)
55 return obj
56
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020057
58class ISSHHost(metaclass=abc.ABCMeta):
59 """Minimal interface, required to setup RPC connection"""
60 info = None # type: NodeInfo
61
62 @abc.abstractmethod
63 def run(self, cmd: str, timeout: int = 60, nolog: bool = False) -> str:
64 pass
65
66 @abc.abstractmethod
koder aka kdanilov73084622016-11-16 21:51:08 +020067 def __str__(self) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020068 pass
69
70 @abc.abstractmethod
koder aka kdanilov73084622016-11-16 21:51:08 +020071 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020072 pass
73
74 @abc.abstractmethod
koder aka kdanilove7e1a4d2016-12-17 20:29:52 +020075 def put_to_file(self, path: Optional[str], content: bytes) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020076 pass
77
koder aka kdanilov73084622016-11-16 21:51:08 +020078 def __enter__(self) -> 'ISSHHost':
79 return self
80
81 def __exit__(self, x, y, z) -> bool:
82 self.disconnect()
83 return False
84
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020085
86class IRPCNode(metaclass=abc.ABCMeta):
87 """Remote filesystem interface"""
88 info = None # type: NodeInfo
koder aka kdanilov70227062016-11-26 23:23:21 +020089 conn = None # type: Any
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020090 rpc_log_file = None # type: str
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020091
92 @abc.abstractmethod
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020093 def __str__(self) -> str:
94 pass
95
96 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +020097 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 +020098 pass
99
100 @abc.abstractmethod
101 def copy_file(self, local_path: str, remote_path: str = None) -> str:
102 pass
103
104 @abc.abstractmethod
105 def get_file_content(self, path: str) -> bytes:
106 pass
107
108 @abc.abstractmethod
koder aka kdanilove7e1a4d2016-12-17 20:29:52 +0200109 def put_to_file(self, path: Optional[str], content: bytes) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200110 pass
111
112 @abc.abstractmethod
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200113 def stat_file(self, path:str) -> Any:
114 pass
115
116 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200117 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200118 pass
119
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200120 @abc.abstractmethod
121 def upload_plugin(self, name: str, code: bytes, version: str = None) -> None:
122 pass
123
koder aka kdanilov73084622016-11-16 21:51:08 +0200124 def __enter__(self) -> 'IRPCNode':
125 return self
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200126
koder aka kdanilov73084622016-11-16 21:51:08 +0200127 def __exit__(self, x, y, z) -> bool:
128 self.disconnect()
129 return False
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200130