blob: a787786939897ac1765ffcb2bad7ab33cd0d0ce5 [file] [log] [blame]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03001import os
2import re
3import sys
4import socket
5import logging
6from urlparse import urlparse
7
8import yaml
9from wally.fuel_rest_api import (KeystoneAuth, get_cluster_id,
10 reflect_cluster, FuelInfo)
11from wally.utils import parse_creds
12from wally.ssh_utils import run_over_ssh, connect
13
14from .node import Node
15
16
17logger = logging.getLogger("wally.discover")
koder aka kdanilov168f6092015-04-19 02:33:38 +030018BASE_PF_PORT = 44006
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030019
20
koder aka kdanilove87ae652015-04-20 02:14:35 +030021def discover_fuel_nodes(fuel_data, var_dir, discover_nodes=True):
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030022 username, tenant_name, password = parse_creds(fuel_data['creds'])
23 creds = {"username": username,
24 "tenant_name": tenant_name,
25 "password": password}
26
27 conn = KeystoneAuth(fuel_data['url'], creds, headers=None)
28
29 cluster_id = get_cluster_id(conn, fuel_data['openstack_env'])
30 cluster = reflect_cluster(conn, cluster_id)
koder aka kdanilove87ae652015-04-20 02:14:35 +030031
32 if not discover_nodes:
33 logger.warning("Skip fuel cluster discovery")
34 return ([], None, cluster.get_openrc())
35
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030036 version = FuelInfo(conn).get_version()
37
38 fuel_nodes = list(cluster.get_nodes())
39
koder aka kdanilov168f6092015-04-19 02:33:38 +030040 logger.debug("Found FUEL {0}".format(".".join(map(str, version))))
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030041
42 network = 'fuelweb_admin' if version >= [6, 0] else 'admin'
43
44 ssh_creds = fuel_data['ssh_creds']
45
46 fuel_host = urlparse(fuel_data['url']).hostname
47 fuel_ip = socket.gethostbyname(fuel_host)
48 ssh_conn = connect("{0}@@{1}".format(ssh_creds, fuel_host))
49
50 fuel_ext_iface = get_external_interface(ssh_conn, fuel_ip)
51
52 # TODO: keep ssh key in memory
53 # http://stackoverflow.com/questions/11994139/how-to-include-the-private-key-in-paramiko-after-fetching-from-string
54 fuel_key_file = os.path.join(var_dir, "fuel_master_node_id_rsa")
55 download_master_key(ssh_conn, fuel_key_file)
56
57 nodes = []
58 ports = range(BASE_PF_PORT, BASE_PF_PORT + len(fuel_nodes))
59 ips_ports = []
60
61 for fuel_node, port in zip(fuel_nodes, ports):
62 ip = fuel_node.get_ip(network)
63 forward_ssh_port(ssh_conn, fuel_ext_iface, port, ip)
64
65 conn_url = "ssh://root@{0}:{1}:{2}".format(fuel_host,
66 port,
67 fuel_key_file)
koder aka kdanilov168f6092015-04-19 02:33:38 +030068 node = Node(conn_url, fuel_node['roles'])
69 node.monitor_url = None
70 nodes.append(node)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030071 ips_ports.append((ip, port))
72
73 logger.debug("Found %s fuel nodes for env %r" %
74 (len(nodes), fuel_data['openstack_env']))
75
koder aka kdanilov168f6092015-04-19 02:33:38 +030076 # return ([],
77 # (ssh_conn, fuel_ext_iface, ips_ports),
78 # cluster.get_openrc())
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030079
80 return (nodes,
81 (ssh_conn, fuel_ext_iface, ips_ports),
82 cluster.get_openrc())
83
84
85def download_master_key(conn, dest):
86 # download master key
87 sftp = conn.open_sftp()
88 sftp.get('/root/.ssh/id_rsa', dest)
89 os.chmod(dest, 0o400)
90 sftp.close()
91
92 logger.debug("Fuel master key stored in {0}".format(dest))
93
94
95def get_external_interface(conn, ip):
96 data = run_over_ssh(conn, "ip a", node='fuel-master')
97 curr_iface = None
98 for line in data.split("\n"):
99
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 raise KeyError("Can't found interface for ip {0}".format(ip))
110
111
112def forward_ssh_port(conn, iface, new_port, ip, clean=False):
113 mode = "-D" if clean is True else "-A"
114 cmd = "iptables -t nat {mode} PREROUTING -p tcp " + \
115 "-i {iface} --dport {port} -j DNAT --to {ip}:22"
116 run_over_ssh(conn,
117 cmd.format(iface=iface, port=new_port, ip=ip, mode=mode),
118 node='fuel-master')
119
120
121def clean_fuel_port_forwarding(clean_data):
koder aka kdanilove87ae652015-04-20 02:14:35 +0300122 if clean_data is None:
123 return
124
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300125 conn, iface, ips_ports = clean_data
126 for ip, port in ips_ports:
127 forward_ssh_port(conn, iface, port, ip, clean=True)
128
129
130def main(argv):
131 fuel_data = yaml.load(open(sys.argv[1]).read())['clouds']['fuel']
132 nodes, to_clean, openrc = discover_fuel_nodes(fuel_data, '/tmp')
133
134 print nodes
135 print openrc
136 print "Ready to test"
137
138 sys.stdin.readline()
139
140 clean_fuel_port_forwarding(to_clean)
141
142 return 0
143
144
145if __name__ == "__main__":
146 main(sys.argv[1:])