blob: d0fabac9eec0de138d384cafe7755641015d407b [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 kdanilov3d2bc4f2016-11-12 18:31:18 +020013
koder aka kdanilov7f59d562016-12-26 01:34:23 +020014 yaml_tag = 'node_info'
15
16 def __init__(self, ssh_creds: ConnCreds, roles: Set[str], params: Dict[str, Any] = None) -> None:
koder aka kdanilov73084622016-11-16 21:51:08 +020017 # ssh credentials
18 self.ssh_creds = ssh_creds
koder aka kdanilov7f59d562016-12-26 01:34:23 +020019
koder aka kdanilov73084622016-11-16 21:51:08 +020020 # credentials for RPC connection
21 self.rpc_creds = None # type: Optional[RPCCreds]
koder aka kdanilov7f59d562016-12-26 01:34:23 +020022
koder aka kdanilov73084622016-11-16 21:51:08 +020023 self.roles = roles
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020024 self.os_vm_id = None # type: Optional[int]
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020025 self.params = {} # type: Dict[str, Any]
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020026 if params is not None:
27 self.params = params
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020028
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:
33 return self.node_id()
34
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'])
56 obj = cls.__new__(cls)
57 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 kdanilov3d2bc4f2016-11-12 18:31:18 +020088
89class IRPCNode(metaclass=abc.ABCMeta):
90 """Remote filesystem interface"""
91 info = None # type: NodeInfo
koder aka kdanilov70227062016-11-26 23:23:21 +020092 conn = None # type: Any
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020093 rpc_log_file = None # type: str
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020094
95 @abc.abstractmethod
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +020096 def __str__(self) -> str:
97 pass
98
99 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200100 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 +0200101 pass
102
103 @abc.abstractmethod
104 def copy_file(self, local_path: str, remote_path: str = None) -> str:
105 pass
106
107 @abc.abstractmethod
108 def get_file_content(self, path: str) -> bytes:
109 pass
110
111 @abc.abstractmethod
koder aka kdanilove7e1a4d2016-12-17 20:29:52 +0200112 def put_to_file(self, path: Optional[str], content: bytes) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200113 pass
114
115 @abc.abstractmethod
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200116 def stat_file(self, path:str) -> Any:
117 pass
118
119 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200120 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200121 pass
122
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200123 @abc.abstractmethod
124 def upload_plugin(self, name: str, code: bytes, version: str = None) -> None:
125 pass
126
koder aka kdanilov73084622016-11-16 21:51:08 +0200127 def __enter__(self) -> 'IRPCNode':
128 return self
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200129
koder aka kdanilov73084622016-11-16 21:51:08 +0200130 def __exit__(self, x, y, z) -> bool:
131 self.disconnect()
132 return False
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200133