blob: b146876a9041f00e73d936e54a5a840489daebdc [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
12 def __init__(self, node_info: NodeInfo):
13 self.info = node_info
14 self.roles = node_info.roles
15 self.bind_ip = node_info.bind_ip
16
17 assert self.ssh_conn_url.startswith("ssh://")
18 self.ssh_conn_url = node_info.ssh_conn_url
19
20 self.ssh_conn = None
21 self.rpc_conn_url = None
22 self.rpc_conn = None
23 self.os_vm_id = None
24 self.hw_info = None
25
26 if self.ssh_conn_url is not None:
27 self.ssh_cred = parse_ssh_uri(self.ssh_conn_url)
28 self.node_id = "{0.host}:{0.port}".format(self.ssh_cred)
29 else:
30 self.ssh_cred = None
31 self.node_id = None
32
33 def __str__(self):
34 template = "<Node: url={conn_url!r} roles={roles}" + \
35 " connected={is_connected}>"
36 return template.format(conn_url=self.ssh_conn_url,
37 roles=", ".join(self.roles),
38 is_connected=self.ssh_conn is not None)
39
40 def __repr__(self):
41 return str(self)
42
43 def connect_ssh(self) -> None:
44 self.ssh_conn = connect(self.ssh_conn_url)
45
46 def connect_rpc(self) -> None:
47 raise NotImplementedError()
48
49 def prepare_rpc(self) -> None:
50 raise NotImplementedError()
51
52 def get_ip(self) -> str:
53 """get node connection ip address"""
54
55 if self.ssh_conn_url == 'local':
56 return '127.0.0.1'
57 return self.ssh_cred.host
58
59 def get_user(self) -> str:
60 """"get ssh connection username"""
61 if self.ssh_conn_url == 'local':
62 return getpass.getuser()
63 return self.ssh_cred.user
64
65 def run(self, cmd: str, stdin_data: str=None, timeout: int=60, nolog: bool=False) -> Tuple[int, str]:
66 """Run command on node. Will use rpc connection, if available"""
67
68 if self.rpc_conn is None:
69 return run_over_ssh(self.ssh_conn, cmd,
70 stdin_data=stdin_data, timeout=timeout,
71 nolog=nolog, node=self)
72 assert not stdin_data
73 proc_id = self.rpc_conn.cli.spawn(cmd)
74 exit_code = None
75 output = ""
76
77 while exit_code is None:
78 exit_code, stdout_data, stderr_data = self.rpc_conn.cli.get_updates(proc_id)
79 output += stdout_data + stderr_data
80
81 return exit_code, output
82
83 def discover_hardware_info(self) -> None:
84 raise NotImplementedError()
85
86 def get_file_content(self, path: str) -> str:
87 raise NotImplementedError()
88
89 def forward_port(self, ip: str, remote_port: int, local_port: int = None) -> int:
90 raise NotImplementedError()
91
92 def get_interface(self, ip: str) -> str:
93 """Get node external interface for given IP"""
94 data = self.run("ip a", nolog=True)
95 curr_iface = None
96
97 for line in data.split("\n"):
98 match1 = re.match(r"\d+:\s+(?P<name>.*?):\s\<", line)
99 if match1 is not None:
100 curr_iface = match1.group('name')
101
102 match2 = re.match(r"\s+inet\s+(?P<ip>[0-9.]+)/", line)
103 if match2 is not None:
104 if match2.group('ip') == ip:
105 assert curr_iface is not None
106 return curr_iface
107
108 raise KeyError("Can't found interface for ip {0}".format(ip))