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:
Adit Sarfaty 2017-05-28 09:24:26 +03:00
parent 75ee16ce71
commit 33f639494d
2 changed files with 118 additions and 38 deletions

View File

@ -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()

View File

@ -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: