| 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) |
| version = FuelInfo(conn).get_version() |
| |
| if not discover_nodes: |
| logger.warning("Skip fuel cluster discovery") |
| return ([], None, cluster.get_openrc(), version) |
| |
| fuel_nodes = list(cluster.get_nodes()) |
| |
| logger.info("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 to 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(), |
| version) |
| |
| |
| 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 |