diff --git a/ci/lib.py b/ci/lib.py index 217cd05d72085a595714ee86a1b46d4a4008cfdb..e5551409eb23250cdd245f28c561f41e32936ef3 100644 --- a/ci/lib.py +++ b/ci/lib.py @@ -14,6 +14,7 @@ import openstack from keystoneauth1.identity import v3 from keystoneauth1 import session +#import IPython def wait_for_keypress(msg="Press Enter to continue..."): """ """ @@ -45,6 +46,12 @@ def executed_in_ci(): return {i_envvar_name in os.environ for i_envvar_name in envvar_names} == {True} +def get_ostack_project_names(project_name): + """ get source and destination ostack project names """ + if '->' in project_name: + return project_name.split('->', 1) + return project_name, project_name + def get_destination_network(source_network): """ LUT for networks """ network_mapping = { @@ -705,3 +712,75 @@ def migrate_rbd_image(args, server_block_device_mapping): "G.17 Source OpenStack VM RBD image snapshot does not exist anymore " \ f"{server_block_device_mapping['source']['ceph_pool_name']}/{source_server_rbd_image}@{source_rbd_image_snapshot_name}", ecode != 0, locals()) + + + +def create_destination_networking(args, src_ostack_conn, dst_ostack_conn, src_project, dst_project, src_network_name): + """ Create matching OpenStack networking (network, subnet, router) """ + # read source network details + src_network = src_ostack_conn.network.find_network(src_network_name, project_id=src_project.id) + # read matching subnets details + src_subnets = [ src_ostack_conn.network.find_subnet(i_src_subnet_id) for i_src_subnet_id in src_network.subnet_ids ] + # read linked routers + src_network_router_ports = [ i_src_router_port for i_src_router_port in src_ostack_conn.list_ports(filters={'network_id': src_network.id}) if i_src_router_port.device_owner == 'network:router_interface' ] + src_network_routers_subnets = [ (src_ostack_conn.network.find_router(router_port.device_id), [rp_fixed_ip['subnet_id'] for rp_fixed_ip in router_port.fixed_ips if 'subnet_id' in rp_fixed_ip]) for router_port in src_network_router_ports ] + + + # read external network + dst_ext_network = dst_ostack_conn.network.find_network(args.destination_ipv4_external_network) + + # create network + dst_network_name = get_migrated_resource_name(args, src_network_name) + dst_network = dst_ostack_conn.network.find_network(dst_network_name, + project_id=dst_project.id) + #IPython.embed() + if not dst_network: + dst_network = dst_ostack_conn.network.create_network(name=dst_network_name, + project_id=dst_project.id, + mtu=src_network.mtu, + description=f"{src_network.description}, g1 migrated id:{src_network.id}", + port_security_enabled=src_network.is_port_security_enabled) + + # create subnets + dst_subnets = [] + subnet_mapping = {} + for i_src_subnet in src_subnets: + i_dst_subnet_name = get_migrated_resource_name(args, i_src_subnet.name) + i_dst_subnet = dst_ostack_conn.network.find_subnet(get_migrated_resource_name(args, i_src_subnet.name), + project_id=dst_project.id) + if not i_dst_subnet: + i_dst_subnet = dst_ostack_conn.network.create_subnet(network_id=dst_network.id, + name=i_dst_subnet_name, + cidr=i_src_subnet.cidr, + ip_version=i_src_subnet.ip_version, + enable_dhcp=i_src_subnet.is_dhcp_enabled, + project_id=dst_project.id, + allocation_pools=i_src_subnet.allocation_pools, + gateway_ip=i_src_subnet.gateway_ip, + host_routes=i_src_subnet.host_routes, + dns_nameservers=i_src_subnet.dns_nameservers, + description=f"{i_src_subnet.description}, g1 migrated id:{i_src_subnet.id}") + subnet_mapping[i_src_subnet.id] = i_dst_subnet.id + dst_subnets.append(i_dst_subnet) + + # create router(s) and associate with subnet(s) (if needed) + dst_network_routers = [] + for i_src_network_router, i_src_network_router_subnets in src_network_routers_subnets: + + i_dst_network_router_name = get_migrated_resource_name(args, i_src_network_router.name) + i_dst_network_router = dst_ostack_conn.network.find_router(i_dst_network_router_name, + project_id=dst_project.id) + if not i_dst_network_router: + i_dst_network_router = dst_ostack_conn.network.create_router(name=i_dst_network_router_name, + description=f"{i_src_network_router.description}, g1 migrated id:{i_src_network_router.id}", + project_id=dst_project.id, + external_gateway_info={"network_id": dst_ext_network.id}) + for i_src_network_router_subnet in i_src_network_router_subnets: + # TODO: Principally there may be also foreign subnets, find more general sulution + if i_src_network_router_subnet in subnet_mapping: + dst_ostack_conn.add_router_interface(i_dst_network_router, subnet_id=subnet_mapping[i_src_network_router_subnet]) + + dst_network_routers.append(i_dst_network_router) + + return dst_network, dst_subnets, dst_network_routers + diff --git a/ci/project-migrator.py b/ci/project-migrator.py index 342e379fb1c3fbe47775565b8f363226a2ea3e35..c1cd69ad8465b3c0f98632703e45026c6ac69923 100755 --- a/ci/project-migrator.py +++ b/ci/project-migrator.py @@ -36,13 +36,14 @@ def main(args): args.logger.info("A.2 Destination OpenStack cloud connected as migrator user") # check project exists in source and destination - source_project = lib.get_ostack_project(source_migrator_conn, args.project_name) + source_project_name, destination_project_name = lib.get_ostack_project_names(args.project_name) + source_project = lib.get_ostack_project(source_migrator_conn, source_project_name) lib.log_or_assert(args, "B.1 Source OpenStack cloud project exists", source_project) source_project_type = lib.get_ostack_project_type(source_migrator_conn, source_project) lib.log_or_assert(args, f"B.2 Source OpenStack cloud project type is {source_project_type}", source_project_type) - destination_project = lib.get_ostack_project(destination_migrator_conn, args.project_name) + destination_project = lib.get_ostack_project(destination_migrator_conn, destination_project_name) lib.log_or_assert(args, "B.10 Destination OpenStack cloud project exists", destination_project) destination_project_type = lib.get_ostack_project_type(destination_migrator_conn, destination_project) lib.log_or_assert(args, f"B.11 Destination OpenStack cloud project type is {destination_project_type}", @@ -140,21 +141,22 @@ def main(args): f"block_device_mapping: {i_source_server_detail.block_device_mapping}, " \ f"attached-volumes: {i_source_server_detail.attached_volumes}") - # network, subnet detection, TODO: better + # network/subnet/router detection & creation i_source_server_network_names = i_source_server_detail.addresses.keys() i_destination_server_networks = [] for i_source_network_name in i_source_server_network_names: i_destination_network_name = lib.get_destination_network(i_source_network_name) - if not i_destination_network_name and args.destination_group_project_network_name != "": - # if network is not mapped use network provided from switch --destination-group-project-network-name - i_destination_network_name = args.destination_group_project_network_name - lib.log_or_assert(args, - f"F.2 Source to Destination network mapping succeeeded ({i_source_network_name}->{i_destination_network_name}). " \ - f"Read --destination-group-project-network-name description for more details", - i_destination_network_name) - i_destination_network = destination_project_conn.network.find_network(i_destination_network_name, project_id=destination_project.id) + if not i_destination_network_name: + # if network is not mapped we need to create matching one + i_dst_network, i_dst_subnets, i_dst_routers = lib.create_destination_networking(args, + source_project_conn, destination_project_conn, + source_project, destination_project, + i_source_network_name) + i_destination_network = destination_project_conn.network.find_network(i_destination_network_name or i_dst_network.id, + project_id=destination_project.id) lib.log_or_assert(args, f"F.3 Destination network exists ({i_destination_network})", i_destination_network) + i_destination_server_networks.append(i_destination_network) # flavor detection @@ -424,9 +426,6 @@ if __name__ == "__main__": help='Destination cloud IPV4 external network.') AP.add_argument('--destination-entity-prefix', default='migrated-', help='Destination cloud migrated cloud entity names prefix.') - AP.add_argument('--destination-group-project-network-name', default='group-project-network', - help='Use pre-created network for group project entities (created by cloud-entities), this is preferred. ' \ - 'Set to "" for creation new router/subnet/network this part is not yet implemented.') AP.add_argument('--project-name', default=None, required=True, help='OpenStack project name (identical name in both clouds required)')