blob: 7a14f9401b6ffb000798ff316f6a4073afe7c48c [file] [log] [blame]
import re
import socket
import logging
from urlparse import urlparse
import sshtunnel
from paramiko import AuthenticationException
from wally.fuel_rest_api import (KeystoneAuth, get_cluster_id,
reflect_cluster, FuelInfo)
from wally.utils import (parse_creds, check_input_param, StopTestError,
clean_resource, get_ip_for_target)
from wally.ssh_utils import (run_over_ssh, connect, set_key_for_node,
read_from_remote)
from .node import Node
logger = logging.getLogger("wally.discover")
BASE_PF_PORT = 44006
def discover_fuel_nodes(fuel_data, var_dir, discover_nodes=True):
username, tenant_name, password = parse_creds(fuel_data['creds'])
creds = {"username": username,
"tenant_name": tenant_name,
"password": password}
conn = KeystoneAuth(fuel_data['url'], creds, headers=None)
msg = "openstack_env should be provided in fuel config"
check_input_param('openstack_env' in fuel_data, msg)
cluster_id = get_cluster_id(conn, fuel_data['openstack_env'])
cluster = reflect_cluster(conn, cluster_id)
if not discover_nodes:
logger.warning("Skip fuel cluster discovery")
return ([], None, cluster.get_openrc())
version = FuelInfo(conn).get_version()
fuel_nodes = list(cluster.get_nodes())
logger.debug("Found FUEL {0}".format(".".join(map(str, version))))
network = 'fuelweb_admin' if version >= [6, 0] else 'admin'
ssh_creds = fuel_data['ssh_creds']
fuel_host = urlparse(fuel_data['url']).hostname
fuel_ip = socket.gethostbyname(fuel_host)
try:
ssh_conn = connect("{0}@{1}".format(ssh_creds, fuel_host))
except AuthenticationException:
raise StopTestError("Wrong fuel credentials")
except Exception:
logger.exception("While connection to FUEL")
raise StopTestError("Failed to connect to FUEL")
fuel_ext_iface = get_external_interface(ssh_conn, fuel_ip)
logger.debug("Downloading fuel master key")
fuel_key = download_master_key(ssh_conn)
nodes = []
ips_ports = []
logger.info("Forwarding ssh ports from FUEL nodes localhost")
fuel_usr, fuel_passwd = ssh_creds.split(":", 1)
ips = [str(fuel_node.get_ip(network)) for fuel_node in fuel_nodes]
port_fw = forward_ssh_ports(fuel_host, fuel_usr, fuel_passwd, ips)
listen_ip = get_ip_for_target(fuel_host)
for port, fuel_node, ip in zip(port_fw, fuel_nodes, ips):
logger.debug(
"SSH port forwarding {0} => localhost:{1}".format(ip, port))
conn_url = "ssh://root@127.0.0.1:{0}".format(port)
set_key_for_node(('127.0.0.1', port), fuel_key)
node = Node(conn_url, fuel_node['roles'])
node.monitor_ip = listen_ip
nodes.append(node)
ips_ports.append((ip, port))
logger.debug("Found %s fuel nodes for env %r" %
(len(nodes), fuel_data['openstack_env']))
return (nodes,
(ssh_conn, fuel_ext_iface, ips_ports),
cluster.get_openrc())
def download_master_key(conn):
# download master key
with conn.open_sftp() as sftp:
return read_from_remote(sftp, '/root/.ssh/id_rsa')
def get_external_interface(conn, ip):
data = run_over_ssh(conn, "ip a", node='fuel-master', nolog=True)
curr_iface = None
for line in data.split("\n"):
match1 = re.match(r"\d+:\s+(?P<name>.*?):\s\<", line)
if match1 is not None:
curr_iface = match1.group('name')
match2 = re.match(r"\s+inet\s+(?P<ip>[0-9.]+)/", line)
if match2 is not None:
if match2.group('ip') == ip:
assert curr_iface is not None
return curr_iface
raise KeyError("Can't found interface for ip {0}".format(ip))
def forward_ssh_ports(proxy_ip, proxy_user, proxy_passwd, ips):
for ip in ips:
tunnel = sshtunnel.open(
(proxy_ip, 22),
ssh_username=proxy_user,
ssh_password=proxy_passwd,
threaded=True,
remote_bind_address=(ip, 22))
tunnel.__enter__()
clean_resource(tunnel.__exit__, None, None, None)
yield tunnel.local_bind_port