blob: 8435cf3d00a0dad8387339072a5a71af92cd3d0c [file] [log] [blame]
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +03001import re
2import getpass
3from typing import Tuple
4from .inode import INode, NodeInfo
5
6from .ssh_utils import parse_ssh_uri, run_over_ssh, connect
7
8
9class Node(INode):
10 """Node object"""
11
koder aka kdanilov22d134e2016-11-08 11:33:19 +020012 def __init__(self, node_info: NodeInfo) -> None:
13 INode.__init__(self)
14
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030015 self.info = node_info
16 self.roles = node_info.roles
17 self.bind_ip = node_info.bind_ip
18
19 assert self.ssh_conn_url.startswith("ssh://")
20 self.ssh_conn_url = node_info.ssh_conn_url
21
22 self.ssh_conn = None
23 self.rpc_conn_url = None
24 self.rpc_conn = None
25 self.os_vm_id = None
26 self.hw_info = None
27
28 if self.ssh_conn_url is not None:
29 self.ssh_cred = parse_ssh_uri(self.ssh_conn_url)
30 self.node_id = "{0.host}:{0.port}".format(self.ssh_cred)
31 else:
32 self.ssh_cred = None
33 self.node_id = None
34
koder aka kdanilov22d134e2016-11-08 11:33:19 +020035 def __str__(self) -> str:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030036 template = "<Node: url={conn_url!r} roles={roles}" + \
37 " connected={is_connected}>"
38 return template.format(conn_url=self.ssh_conn_url,
39 roles=", ".join(self.roles),
40 is_connected=self.ssh_conn is not None)
41
koder aka kdanilov22d134e2016-11-08 11:33:19 +020042 def __repr__(self) -> str:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030043 return str(self)
44
koder aka kdanilov22d134e2016-11-08 11:33:19 +020045 def connect_ssh(self, timeout: int=None) -> None:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030046 self.ssh_conn = connect(self.ssh_conn_url)
47
48 def connect_rpc(self) -> None:
49 raise NotImplementedError()
50
51 def prepare_rpc(self) -> None:
52 raise NotImplementedError()
53
54 def get_ip(self) -> str:
55 """get node connection ip address"""
56
57 if self.ssh_conn_url == 'local':
58 return '127.0.0.1'
59 return self.ssh_cred.host
60
61 def get_user(self) -> str:
62 """"get ssh connection username"""
63 if self.ssh_conn_url == 'local':
64 return getpass.getuser()
65 return self.ssh_cred.user
66
67 def run(self, cmd: str, stdin_data: str=None, timeout: int=60, nolog: bool=False) -> Tuple[int, str]:
68 """Run command on node. Will use rpc connection, if available"""
69
70 if self.rpc_conn is None:
71 return run_over_ssh(self.ssh_conn, cmd,
72 stdin_data=stdin_data, timeout=timeout,
73 nolog=nolog, node=self)
74 assert not stdin_data
75 proc_id = self.rpc_conn.cli.spawn(cmd)
76 exit_code = None
77 output = ""
78
79 while exit_code is None:
80 exit_code, stdout_data, stderr_data = self.rpc_conn.cli.get_updates(proc_id)
81 output += stdout_data + stderr_data
82
83 return exit_code, output
84
85 def discover_hardware_info(self) -> None:
86 raise NotImplementedError()
87
88 def get_file_content(self, path: str) -> str:
89 raise NotImplementedError()
90
91 def forward_port(self, ip: str, remote_port: int, local_port: int = None) -> int:
92 raise NotImplementedError()
93
94 def get_interface(self, ip: str) -> str:
95 """Get node external interface for given IP"""
96 data = self.run("ip a", nolog=True)
97 curr_iface = None
98
99 for line in data.split("\n"):
100 match1 = re.match(r"\d+:\s+(?P<name>.*?):\s\<", line)
101 if match1 is not None:
102 curr_iface = match1.group('name')
103
104 match2 = re.match(r"\s+inet\s+(?P<ip>[0-9.]+)/", line)
105 if match2 is not None:
106 if match2.group('ip') == ip:
107 assert curr_iface is not None
108 return curr_iface
109
110 raise KeyError("Can't found interface for ip {0}".format(ip))
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200111
112 def sync_hw_info(self) -> None:
113 pass
114
115 def sync_sw_info(self) -> None:
116 pass