From 4fb8e94b15c5c4385769539d331c9317ba386c3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Franti=C5=A1ek=20=C5=98ezn=C3=AD=C4=8Dek?=
<246254@mail.muni.cz>
Date: Fri, 22 Mar 2024 09:17:42 +0100
Subject: [PATCH] feat: add possibility to create project networking (when not
used default one)
---
ci/lib.py | 79 ++++++++++++++++++++++++++++++++++++++++++
ci/project-migrator.py | 27 +++++++--------
2 files changed, 92 insertions(+), 14 deletions(-)
diff --git a/ci/lib.py b/ci/lib.py
index 217cd05..e555140 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 342e379..c1cd69a 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)')
--
GitLab