blob: 935ca41a9963f8735c32c1a305fd7d3261e7c573 [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'])
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
koder aka kdanilov108ac362017-01-19 20:17:16 +020095 @property
96 def node_id(self) -> str:
97 return self.info.node_id
98
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020099 @abc.abstractmethod
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200100 def __str__(self) -> str:
101 pass
102
103 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200104 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 +0200105 pass
106
107 @abc.abstractmethod
108 def copy_file(self, local_path: str, remote_path: str = None) -> str:
109 pass
110
111 @abc.abstractmethod
112 def get_file_content(self, path: str) -> bytes:
113 pass
114
115 @abc.abstractmethod
koder aka kdanilove7e1a4d2016-12-17 20:29:52 +0200116 def put_to_file(self, path: Optional[str], content: bytes) -> str:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200117 pass
118
119 @abc.abstractmethod
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200120 def stat_file(self, path:str) -> Any:
121 pass
122
123 @abc.abstractmethod
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200124 def disconnect(self) -> None:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200125 pass
126
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200127 @abc.abstractmethod
128 def upload_plugin(self, name: str, code: bytes, version: str = None) -> None:
129 pass
130
koder aka kdanilov73084622016-11-16 21:51:08 +0200131 def __enter__(self) -> 'IRPCNode':
132 return self
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200133
koder aka kdanilov73084622016-11-16 21:51:08 +0200134 def __exit__(self, x, y, z) -> bool:
135 self.disconnect()
136 return False
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200137