blob: a787786939897ac1765ffcb2bad7ab33cd0d0ce5 [file] [log] [blame]
import os
import re
import sys
import socket
import logging
from urlparse import urlparse
import yaml
from wally.fuel_rest_api import (KeystoneAuth, get_cluster_id,
reflect_cluster, FuelInfo)
from wally.utils import parse_creds
from wally.ssh_utils import run_over_ssh, connect
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)
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)
ssh_conn = connect("{0}@@{1}".format(ssh_creds, fuel_host))
fuel_ext_iface = get_external_interface(ssh_conn, fuel_ip)
# TODO: keep ssh key in memory
# http://stackoverflow.com/questions/11994139/how-to-include-the-private-key-in-paramiko-after-fetching-from-string
fuel_key_file = os.path.join(var_dir, "fuel_master_node_id_rsa")
download_master_key(ssh_conn, fuel_key_file)
nodes = []
ports = range(BASE_PF_PORT, BASE_PF_PORT + len(fuel_nodes))
ips_ports = []
for fuel_node, port in zip(fuel_nodes, ports):
ip = fuel_node.get_ip(network)
forward_ssh_port(ssh_conn, fuel_ext_iface, port, ip)
conn_url = "ssh://root@{0}:{1}:{2}".format(fuel_host,
port,
fuel_key_file)
node = Node(conn_url, fuel_node['roles'])
node.monitor_url = None
nodes.append(node)
ips_ports.append((ip, port))
logger.debug("Found %s fuel nodes for env %r" %
(len(nodes), fuel_data['openstack_env']))
# return ([],
# (ssh_conn, fuel_ext_iface, ips_ports),
# cluster.get_openrc())
return (nodes,
(ssh_conn, fuel_ext_iface, ips_ports),
cluster.get_openrc())
def download_master_key(conn, dest):
# download master key
sftp = conn.open_sftp()
sftp.get('/root/.ssh/id_rsa', dest)
os.chmod(dest, 0o400)
sftp.close()
logger.debug("Fuel master key stored in {0}".format(dest))
def get_external_interface(conn, ip):
data = run_over_ssh(conn, "ip a", node='fuel-master')
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_port(conn, iface, new_port, ip, clean=False):
mode = "-D" if clean is True else "-A"
cmd = "iptables -t nat {mode} PREROUTING -p tcp " + \
"-i {iface} --dport {port} -j DNAT --to {ip}:22"
run_over_ssh(conn,
cmd.format(iface=iface, port=new_port, ip=ip, mode=mode),
node='fuel-master')
def clean_fuel_port_forwarding(clean_data):
if clean_data is None:
return
conn, iface, ips_ports = clean_data
for ip, port in ips_ports:
forward_ssh_port(conn, iface, port, ip, clean=True)
def main(argv):
fuel_data = yaml.load(open(sys.argv[1]).read())['clouds']['fuel']
nodes, to_clean, openrc = discover_fuel_nodes(fuel_data, '/tmp')
print nodes
print openrc
print "Ready to test"
sys.stdin.readline()
clean_fuel_port_forwarding(to_clean)
return 0
if __name__ == "__main__":
main(sys.argv[1:])