#!/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()))