NSX Migration: support keystone v3 & other fixes
- support for keystone V3, and adding domain names as arguments - add argument to still use keystone v2 for the source connection (--use-old-keystone) - in case network creation fails - print the source network - skip routers & floatingips creation in case that the source setup does not have L3 support - rename tenant arguments to project Change-Id: I374dc9eb5ace18804fafcff31dfd96e917db0261
This commit is contained in:
parent
740a2ec3f5
commit
efc6a2d25b
@ -14,20 +14,27 @@ import argparse
|
||||
|
||||
from vmware_nsx.api_replay import client
|
||||
|
||||
DEFAULT_DOMAIN_ID = 'default'
|
||||
|
||||
|
||||
class ApiReplayCli(object):
|
||||
|
||||
def __init__(self):
|
||||
args = self._setup_argparse()
|
||||
client.ApiReplayClient(
|
||||
source_os_tenant_name=args.source_os_tenant_name,
|
||||
source_os_tenant_name=args.source_os_project_name,
|
||||
source_os_tenant_domain_id=args.source_os_project_domain_id,
|
||||
source_os_username=args.source_os_username,
|
||||
source_os_user_domain_id=args.source_os_user_domain_id,
|
||||
source_os_password=args.source_os_password,
|
||||
source_os_auth_url=args.source_os_auth_url,
|
||||
dest_os_tenant_name=args.dest_os_project_name,
|
||||
dest_os_tenant_domain_id=args.dest_os_project_domain_id,
|
||||
dest_os_username=args.dest_os_username,
|
||||
dest_os_tenant_name=args.dest_os_tenant_name,
|
||||
dest_os_user_domain_id=args.dest_os_user_domain_id,
|
||||
dest_os_password=args.dest_os_password,
|
||||
dest_os_auth_url=args.dest_os_auth_url)
|
||||
dest_os_auth_url=args.dest_os_auth_url,
|
||||
use_old_keystone=args.use_old_keystone)
|
||||
|
||||
def _setup_argparse(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
@ -40,9 +47,19 @@ class ApiReplayCli(object):
|
||||
help="The source os-username to use to "
|
||||
"gather neutron resources with.")
|
||||
parser.add_argument(
|
||||
"--source-os-tenant-name",
|
||||
"--source-os-user-domain-id",
|
||||
default=DEFAULT_DOMAIN_ID,
|
||||
help="The source os-user-domain-id to use to "
|
||||
"gather neutron resources with.")
|
||||
parser.add_argument(
|
||||
"--source-os-project-name",
|
||||
required=True,
|
||||
help="The source os-tenant-name to use to "
|
||||
help="The source os-project-name to use to "
|
||||
"gather neutron resource with.")
|
||||
parser.add_argument(
|
||||
"--source-os-project-domain-id",
|
||||
default=DEFAULT_DOMAIN_ID,
|
||||
help="The source os-project-domain-id to use to "
|
||||
"gather neutron resource with.")
|
||||
parser.add_argument(
|
||||
"--source-os-password",
|
||||
@ -61,9 +78,19 @@ class ApiReplayCli(object):
|
||||
help="The dest os-username to use to"
|
||||
"gather neutron resources with.")
|
||||
parser.add_argument(
|
||||
"--dest-os-tenant-name",
|
||||
"--dest-os-user-domain-id",
|
||||
default=DEFAULT_DOMAIN_ID,
|
||||
help="The dest os-user-domain-id to use to"
|
||||
"gather neutron resources with.")
|
||||
parser.add_argument(
|
||||
"--dest-os-project-name",
|
||||
required=True,
|
||||
help="The dest os-tenant-name to use to "
|
||||
help="The dest os-project-name to use to "
|
||||
"gather neutron resource with.")
|
||||
parser.add_argument(
|
||||
"--dest-os-project-domain-id",
|
||||
default=DEFAULT_DOMAIN_ID,
|
||||
help="The dest os-project-domain-id to use to "
|
||||
"gather neutron resource with.")
|
||||
parser.add_argument(
|
||||
"--dest-os-password",
|
||||
@ -74,6 +101,12 @@ class ApiReplayCli(object):
|
||||
required=True,
|
||||
help="They keystone api endpoint for this user.")
|
||||
|
||||
parser.add_argument(
|
||||
"--use-old-keystone",
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Use old keystone client for source authentication.")
|
||||
|
||||
# NOTE: this will return an error message if any of the
|
||||
# require options are missing.
|
||||
return parser.parse_args()
|
||||
|
@ -12,8 +12,11 @@
|
||||
|
||||
import six
|
||||
|
||||
from keystoneauth1 import identity
|
||||
from keystoneauth1 import session
|
||||
from neutronclient.common import exceptions as n_exc
|
||||
from neutronclient.v2_0 import client
|
||||
from oslo_utils import excutils
|
||||
|
||||
|
||||
class ApiReplayClient(object):
|
||||
@ -24,33 +27,42 @@ class ApiReplayClient(object):
|
||||
'revision',
|
||||
'revision_number']
|
||||
|
||||
def __init__(self, source_os_username, source_os_tenant_name,
|
||||
def __init__(self,
|
||||
source_os_username, source_os_user_domain_id,
|
||||
source_os_tenant_name, source_os_tenant_domain_id,
|
||||
source_os_password, source_os_auth_url,
|
||||
dest_os_username, dest_os_tenant_name,
|
||||
dest_os_password, dest_os_auth_url):
|
||||
dest_os_username, dest_os_user_domain_id,
|
||||
dest_os_tenant_name, dest_os_tenant_domain_id,
|
||||
dest_os_password, dest_os_auth_url,
|
||||
use_old_keystone):
|
||||
|
||||
self._source_os_username = source_os_username
|
||||
self._source_os_tenant_name = source_os_tenant_name
|
||||
self._source_os_password = source_os_password
|
||||
self._source_os_auth_url = source_os_auth_url
|
||||
# connect to both clients
|
||||
if use_old_keystone:
|
||||
# Since we are not sure what keystone version will be used on the
|
||||
# source setup, we add an option to use the v2 client
|
||||
self.source_neutron = client.Client(
|
||||
username=source_os_username,
|
||||
tenant_name=source_os_tenant_name,
|
||||
password=source_os_password,
|
||||
auth_url=source_os_auth_url)
|
||||
else:
|
||||
self.source_neutron = self.connect_to_client(
|
||||
username=source_os_username,
|
||||
user_domain_id=source_os_user_domain_id,
|
||||
tenant_name=source_os_tenant_name,
|
||||
tenant_domain_id=source_os_tenant_domain_id,
|
||||
password=source_os_password,
|
||||
auth_url=source_os_auth_url)
|
||||
|
||||
self._dest_os_username = dest_os_username
|
||||
self._dest_os_tenant_name = dest_os_tenant_name
|
||||
self._dest_os_password = dest_os_password
|
||||
self._dest_os_auth_url = dest_os_auth_url
|
||||
|
||||
self.source_neutron = client.Client(
|
||||
username=self._source_os_username,
|
||||
tenant_name=self._source_os_tenant_name,
|
||||
password=self._source_os_password,
|
||||
auth_url=self._source_os_auth_url)
|
||||
|
||||
self.dest_neutron = client.Client(
|
||||
username=self._dest_os_username,
|
||||
tenant_name=self._dest_os_tenant_name,
|
||||
password=self._dest_os_password,
|
||||
auth_url=self._dest_os_auth_url)
|
||||
self.dest_neutron = self.connect_to_client(
|
||||
username=dest_os_username,
|
||||
user_domain_id=dest_os_user_domain_id,
|
||||
tenant_name=dest_os_tenant_name,
|
||||
tenant_domain_id=dest_os_tenant_domain_id,
|
||||
password=dest_os_password,
|
||||
auth_url=dest_os_auth_url)
|
||||
|
||||
# Migrate all the objects
|
||||
self.migrate_security_groups()
|
||||
self.migrate_qos_policies()
|
||||
routers_routes = self.migrate_routers()
|
||||
@ -58,6 +70,19 @@ class ApiReplayClient(object):
|
||||
self.migrate_floatingips()
|
||||
self.migrate_routers_routes(routers_routes)
|
||||
|
||||
def connect_to_client(self, username, user_domain_id,
|
||||
tenant_name, tenant_domain_id,
|
||||
password, auth_url):
|
||||
auth = identity.Password(username=username,
|
||||
user_domain_id=user_domain_id,
|
||||
password=password,
|
||||
project_name=tenant_name,
|
||||
project_domain_id=tenant_domain_id,
|
||||
auth_url=auth_url)
|
||||
sess = session.Session(auth=auth)
|
||||
neutron = client.Client(session=sess)
|
||||
return neutron
|
||||
|
||||
def find_subnet_by_id(self, subnet_id, subnets):
|
||||
for subnet in subnets:
|
||||
if subnet['id'] == subnet_id:
|
||||
@ -257,7 +282,12 @@ class ApiReplayClient(object):
|
||||
each router. Static routes must be added later, after the router
|
||||
ports are set.
|
||||
"""
|
||||
source_routers = self.source_neutron.list_routers()['routers']
|
||||
try:
|
||||
source_routers = self.source_neutron.list_routers()['routers']
|
||||
except Exception:
|
||||
# L3 might be disabled in the source
|
||||
source_routers = []
|
||||
|
||||
dest_routers = self.dest_neutron.list_routers()['routers']
|
||||
update_routes = {}
|
||||
|
||||
@ -328,17 +358,22 @@ class ApiReplayClient(object):
|
||||
def fix_network(self, body, dest_default_public_net):
|
||||
# neutron doesn't like some fields being None even though its
|
||||
# what it returns to us.
|
||||
for field in ['provider:physical_network']:
|
||||
for field in ['provider:physical_network',
|
||||
'provider:segmentation_id']:
|
||||
if field in body and body[field] is None:
|
||||
del body[field]
|
||||
|
||||
# vxlan network with segmentation id should be translated to regular
|
||||
# networks in nsx-v3.
|
||||
# vxlan network with segmentation id should be translated to a regular
|
||||
# network in nsx-v3.
|
||||
if (body.get('provider:network_type') == 'vxlan' and
|
||||
body.get('provider:segmentation_id') is not None):
|
||||
del body['provider:network_type']
|
||||
del body['provider:segmentation_id']
|
||||
|
||||
# flat network should be translated to a regular network in nsx-v3.
|
||||
if (body.get('provider:network_type') == 'flat'):
|
||||
del body['provider:network_type']
|
||||
|
||||
# external networks needs some special care
|
||||
if body.get('router:external'):
|
||||
fields_reset = False
|
||||
@ -408,9 +443,16 @@ class ApiReplayClient(object):
|
||||
|
||||
# only create network if the dest server doesn't have it
|
||||
if self.have_id(network['id'], dest_networks) is False:
|
||||
created_net = self.dest_neutron.create_network(
|
||||
{'network': body})['network']
|
||||
print("Created network: %s " % created_net)
|
||||
try:
|
||||
created_net = self.dest_neutron.create_network(
|
||||
{'network': body})['network']
|
||||
print("Created network: %s " % created_net)
|
||||
except Exception as e:
|
||||
# Print the network and exception to help debugging
|
||||
with excutils.save_and_reraise_exception():
|
||||
print("Failed to create network: " + str(body))
|
||||
print("Source network: " + str(network))
|
||||
raise e
|
||||
|
||||
created_subnet = None
|
||||
for subnet_id in network['subnets']:
|
||||
@ -497,7 +539,12 @@ class ApiReplayClient(object):
|
||||
|
||||
def migrate_floatingips(self):
|
||||
"""Migrates floatingips from source to dest neutron."""
|
||||
source_fips = self.source_neutron.list_floatingips()['floatingips']
|
||||
try:
|
||||
source_fips = self.source_neutron.list_floatingips()['floatingips']
|
||||
except Exception:
|
||||
# L3 might be disabled in the source
|
||||
source_fips = []
|
||||
|
||||
drop_fip_fields = ['status', 'router_id', 'id', 'revision']
|
||||
|
||||
for source_fip in source_fips:
|
||||
|
Loading…
Reference in New Issue
Block a user