Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
project-migrator.py 6.01 KiB
#!/usr/bin/env python3
"""
OpenStack project multicloud migrator
"""

import argparse
import re
import sys

import paramiko
import keystoneauth1.session
from keystoneauth1.identity import v3
from openstack import connection
from keystoneauth1 import session

def get_openrc(file_handle):
    """ parse and return OpenRC file """
    openrc_vars = {}

    for line in file_handle:
        match = re.match(r'^export (\w+)=(.+)$', line.strip())
        if match:
            openrc_vars[match.group(1)] = match.group(2).strip('"')
    return openrc_vars


def get_ostack_session_connection(openrc_vars):
    """ """
    auth_args = {
        'auth_url': openrc_vars.get('OS_AUTH_URL'),
        'username': openrc_vars.get('OS_USERNAME'),
        'password': openrc_vars.get('OS_PASSWORD'),
        'project_name': openrc_vars.get('OS_PROJECT_NAME'),
        'project_domain_name': openrc_vars.get('OS_PROJECT_DOMAIN_NAME'),
        'user_domain_name': openrc_vars.get('OS_USER_DOMAIN_NAME'),
        'project_domain_id': openrc_vars.get('OS_PROJECT_DOMAIN_ID'),
        'user_domain_id': openrc_vars.get('OS_USER_DOMAIN_ID'),
    }
    auth = v3.Password(**auth_args)
    ostack_sess = session.Session(auth=auth)
    ostack_conn = connection.Connection(session=ostack_sess)
    return ostack_sess, ostack_conn

def get_ostack_project(ostack_connection, project_name):
    project = None
    for i_project in ostack_connection.list_projects():
        if i_project.name == project_name:
            project = i_project
    return project

def get_ostack_project_keypairs(ostack_connection, project_name):
    return ostack_connection.compute.keypairs()

def get_ostack_project_security_groups(ostack_connection, project_name):
    return ostack_connection.network.security_groups()

def get_ostack_project_servers(ostack_connection, project_name):
    return ostack_connection.compute.servers()

def get_ostack_project_volumes(ostack_connection, project_name):
    return ostack_connection.block_store.volumes()


def remote_cmd_exec(hostname, username, key_filename, command):
    # Create SSH client
    ssh_client = paramiko.SSHClient()

    # Automatically add untrusted hosts
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        # Connect to the remote host
        pkey = paramiko.RSAKey.from_private_key_file(key_filename)
        ssh_client.connect(hostname, username=username, pkey=pkey, look_for_keys=False)

        # Execute the command
        stdin, stdout, stderr = ssh_client.exec_command(command)

        # Read the output
        output = stdout.read().decode().strip()

        # Close the SSH connection
        ssh_client.close()

        return output

    except Exception as e:
        print("Error:", e)
        return None

def main(args):
    """ """
    # connect to source cloud
    source_openrc = get_openrc(args.source_openrc)
    source_sess, source_conn = get_ostack_session_connection(source_openrc)

    # connect to destination cloud
    destination_openrc = get_openrc(args.destination_openrc)
    destination_sess, destination_conn = get_ostack_session_connection(destination_openrc)

    # check project exists in source and destination
    source_project = get_ostack_project(source_conn, args.project_name)
    destination_project = get_ostack_project(destination_conn, args.project_name)

    # connect to migrator node
    reply = remote_cmd_exec(args.ceph_migrator_host, args.ceph_migrator_user,
                            args.ceph_migrator_sshkeyfile.name, 'uname -a')

    # get source/destination keypairs
    source_keypairs = get_ostack_project_keypairs(source_conn, args.project_name)
    destination_keypairs = get_ostack_project_keypairs(destination_conn, args.project_name)


    # get source/destination security groups
    source_security_groups = get_ostack_project_security_groups(source_conn, args.project_name)
    destination_security_groups = get_ostack_project_security_groups(destination_conn, args.project_name)

    # get source/destination servers in the project
    source_project_servers = get_ostack_project_servers(source_conn, args.project_name)
    destination_project_servers = get_ostack_project_servers(destination_conn, args.project_name)

    # get source/destination volumes in the project
    source_project_volumes = get_ostack_project_volumes(source_conn, args.project_name)
    destination_project_volumes = get_ostack_project_volumes(destination_conn, args.project_name)



    #print(dir(source_conn))
    print(locals())

    # connect to source cloud

    # connect to ceph migrator node

    pass


# main() call (argument parsing)
# ---------------------------------------------------------------------------
if __name__ == "__main__":
    AP = argparse.ArgumentParser(epilog=globals().get('__doc__'),
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
    AP.add_argument('--source-openrc', default=None, type=argparse.FileType('r'),
                    required=True, help='Source cloud authentication (OpenRC file)')
    AP.add_argument('--destination-openrc', default=None, type=argparse.FileType('r'),
                    required=True, help='Destination cloud authentication (OpenRC file)')
    AP.add_argument('--ceph-migrator-host', default='controller-ostack.stage.cloud.muni.cz',
                    help='OpenStack migrator ceph node host')
    AP.add_argument('--ceph-migrator-user', default='root',
                    help='OpenStack migrator ceph node username')
    AP.add_argument('--ceph-migrator-sshkeyfile', default=None, type=argparse.FileType('r'),
                    help='OpenStack migrator SSH keyfile')

    AP.add_argument('--project-name', default=None, required=True,
                    help='OpenStack project name (identical in both clouds)')
    AP.add_argument('--keypair-id', default=None, required=True,
                    help='Any keypair ID within selected OpenStack project')

    #AP.add_argument('--hide-skipped-actions', default=False, action='store_true',
    #                help="Hide skipped ansible actions")

    sys.exit(main(AP.parse_args()))