blob: 34adc076465a35121a3e7f981cbc88fb127f9736 [file] [log] [blame]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03001import re
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03002import socket
3import logging
4from urlparse import urlparse
5
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03006import sshtunnel
7from paramiko import AuthenticationException
8
9
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030010from wally.fuel_rest_api import (KeystoneAuth, get_cluster_id,
11 reflect_cluster, FuelInfo)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030012from wally.utils import (parse_creds, check_input_param, StopTestError,
13 clean_resource, get_ip_for_target)
14from wally.ssh_utils import (run_over_ssh, connect, set_key_for_node,
15 read_from_remote)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030016
17from .node import Node
18
19
20logger = logging.getLogger("wally.discover")
koder aka kdanilov168f6092015-04-19 02:33:38 +030021BASE_PF_PORT = 44006
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030022
23
koder aka kdanilove87ae652015-04-20 02:14:35 +030024def discover_fuel_nodes(fuel_data, var_dir, discover_nodes=True):
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030025 username, tenant_name, password = parse_creds(fuel_data['creds'])
26 creds = {"username": username,
27 "tenant_name": tenant_name,
28 "password": password}
29
30 conn = KeystoneAuth(fuel_data['url'], creds, headers=None)
31
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030032 msg = "openstack_env should be provided in fuel config"
33 check_input_param('openstack_env' in fuel_data, msg)
34
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030035 cluster_id = get_cluster_id(conn, fuel_data['openstack_env'])
36 cluster = reflect_cluster(conn, cluster_id)
koder aka kdanilove87ae652015-04-20 02:14:35 +030037
38 if not discover_nodes:
39 logger.warning("Skip fuel cluster discovery")
40 return ([], None, cluster.get_openrc())
41
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030042 version = FuelInfo(conn).get_version()
43
44 fuel_nodes = list(cluster.get_nodes())
45
koder aka kdanilov416b87a2015-05-12 00:26:04 +030046 logger.info("Found FUEL {0}".format(".".join(map(str, version))))
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030047
48 network = 'fuelweb_admin' if version >= [6, 0] else 'admin'
49
50 ssh_creds = fuel_data['ssh_creds']
51
52 fuel_host = urlparse(fuel_data['url']).hostname
53 fuel_ip = socket.gethostbyname(fuel_host)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030054
55 try:
56 ssh_conn = connect("{0}@{1}".format(ssh_creds, fuel_host))
57 except AuthenticationException:
58 raise StopTestError("Wrong fuel credentials")
59 except Exception:
60 logger.exception("While connection to FUEL")
61 raise StopTestError("Failed to connect to FUEL")
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030062
63 fuel_ext_iface = get_external_interface(ssh_conn, fuel_ip)
64
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030065 logger.debug("Downloading fuel master key")
66 fuel_key = download_master_key(ssh_conn)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030067
68 nodes = []
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030069 ips_ports = []
70
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030071 logger.info("Forwarding ssh ports from FUEL nodes to localhost")
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030072 fuel_usr, fuel_passwd = ssh_creds.split(":", 1)
73 ips = [str(fuel_node.get_ip(network)) for fuel_node in fuel_nodes]
74 port_fw = forward_ssh_ports(fuel_host, fuel_usr, fuel_passwd, ips)
75 listen_ip = get_ip_for_target(fuel_host)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030076
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030077 for port, fuel_node, ip in zip(port_fw, fuel_nodes, ips):
78 logger.debug(
79 "SSH port forwarding {0} => localhost:{1}".format(ip, port))
80
81 conn_url = "ssh://root@127.0.0.1:{0}".format(port)
82 set_key_for_node(('127.0.0.1', port), fuel_key)
83
koder aka kdanilov168f6092015-04-19 02:33:38 +030084 node = Node(conn_url, fuel_node['roles'])
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030085 node.monitor_ip = listen_ip
koder aka kdanilov168f6092015-04-19 02:33:38 +030086 nodes.append(node)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030087 ips_ports.append((ip, port))
88
89 logger.debug("Found %s fuel nodes for env %r" %
90 (len(nodes), fuel_data['openstack_env']))
91
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030092 return (nodes,
93 (ssh_conn, fuel_ext_iface, ips_ports),
koder aka kdanilovc0c97e22015-07-21 00:08:33 +030094 cluster.get_openrc())
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030095
96
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030097def download_master_key(conn):
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030098 # download master key
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030099 with conn.open_sftp() as sftp:
100 return read_from_remote(sftp, '/root/.ssh/id_rsa')
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300101
102
103def get_external_interface(conn, ip):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300104 data = run_over_ssh(conn, "ip a", node='fuel-master', nolog=True)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300105 curr_iface = None
106 for line in data.split("\n"):
107
108 match1 = re.match(r"\d+:\s+(?P<name>.*?):\s\<", line)
109 if match1 is not None:
110 curr_iface = match1.group('name')
111
112 match2 = re.match(r"\s+inet\s+(?P<ip>[0-9.]+)/", line)
113 if match2 is not None:
114 if match2.group('ip') == ip:
115 assert curr_iface is not None
116 return curr_iface
117 raise KeyError("Can't found interface for ip {0}".format(ip))
118
119
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300120def forward_ssh_ports(proxy_ip, proxy_user, proxy_passwd, ips):
121 for ip in ips:
122 tunnel = sshtunnel.open(
123 (proxy_ip, 22),
124 ssh_username=proxy_user,
125 ssh_password=proxy_passwd,
126 threaded=True,
127 remote_bind_address=(ip, 22))
128 tunnel.__enter__()
129 clean_resource(tunnel.__exit__, None, None, None)
130 yield tunnel.local_bind_port