Merge "Update api-replay for nsx-v->nsx-v3 migration"
This commit is contained in:
commit
aa0473a7b9
@ -18,6 +18,12 @@ from neutronclient.v2_0 import client
|
|||||||
|
|
||||||
class ApiReplayClient(object):
|
class ApiReplayClient(object):
|
||||||
|
|
||||||
|
basic_ignore_fields = ['updated_at',
|
||||||
|
'created_at',
|
||||||
|
'tags',
|
||||||
|
'revision',
|
||||||
|
'revision_number']
|
||||||
|
|
||||||
def __init__(self, source_os_username, source_os_tenant_name,
|
def __init__(self, source_os_username, source_os_tenant_name,
|
||||||
source_os_password, source_os_auth_url,
|
source_os_password, source_os_auth_url,
|
||||||
dest_os_username, dest_os_tenant_name,
|
dest_os_username, dest_os_tenant_name,
|
||||||
@ -93,6 +99,12 @@ class ApiReplayClient(object):
|
|||||||
body[k] = v
|
body[k] = v
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
def fix_description(self, body):
|
||||||
|
# neutron doesn't like description being None even though its
|
||||||
|
# what it returns to us.
|
||||||
|
if 'description' in body and body['description'] is None:
|
||||||
|
body['description'] = ''
|
||||||
|
|
||||||
def migrate_qos_rule(self, dest_policy, source_rule):
|
def migrate_qos_rule(self, dest_policy, source_rule):
|
||||||
"""Add the QoS rule from the source to the QoS policy
|
"""Add the QoS rule from the source to the QoS policy
|
||||||
|
|
||||||
@ -129,17 +141,19 @@ class ApiReplayClient(object):
|
|||||||
|
|
||||||
# first fetch the QoS policies from both the
|
# first fetch the QoS policies from both the
|
||||||
# source and destination neutron server
|
# source and destination neutron server
|
||||||
try:
|
|
||||||
source_qos_pols = self.source_neutron.list_qos_policies()[
|
|
||||||
'policies']
|
|
||||||
except n_exc.NotFound:
|
|
||||||
# QoS disabled on source
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
dest_qos_pols = self.dest_neutron.list_qos_policies()['policies']
|
dest_qos_pols = self.dest_neutron.list_qos_policies()['policies']
|
||||||
except n_exc.NotFound:
|
except n_exc.NotFound:
|
||||||
# QoS disabled on dest
|
# QoS disabled on dest
|
||||||
print("QoS is disabled on destination: ignoring QoS policies")
|
print("QoS is disabled on destination: ignoring QoS policies")
|
||||||
|
self.dest_qos_support = False
|
||||||
|
return
|
||||||
|
self.dest_qos_support = True
|
||||||
|
try:
|
||||||
|
source_qos_pols = self.source_neutron.list_qos_policies()[
|
||||||
|
'policies']
|
||||||
|
except n_exc.NotFound:
|
||||||
|
# QoS disabled on source
|
||||||
return
|
return
|
||||||
|
|
||||||
drop_qos_policy_fields = ['revision']
|
drop_qos_policy_fields = ['revision']
|
||||||
@ -158,6 +172,7 @@ class ApiReplayClient(object):
|
|||||||
qos_rules = pol.pop('rules')
|
qos_rules = pol.pop('rules')
|
||||||
try:
|
try:
|
||||||
body = self.drop_fields(pol, drop_qos_policy_fields)
|
body = self.drop_fields(pol, drop_qos_policy_fields)
|
||||||
|
self.fix_description(body)
|
||||||
new_pol = self.dest_neutron.create_qos_policy(
|
new_pol = self.dest_neutron.create_qos_policy(
|
||||||
body={'policy': body})
|
body={'policy': body})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -179,7 +194,7 @@ class ApiReplayClient(object):
|
|||||||
source_sec_groups = source_sec_groups['security_groups']
|
source_sec_groups = source_sec_groups['security_groups']
|
||||||
dest_sec_groups = dest_sec_groups['security_groups']
|
dest_sec_groups = dest_sec_groups['security_groups']
|
||||||
|
|
||||||
drop_sg_fields = ['revision']
|
drop_sg_fields = self.basic_ignore_fields + ['policy']
|
||||||
|
|
||||||
for sg in source_sec_groups:
|
for sg in source_sec_groups:
|
||||||
dest_sec_group = self.have_id(sg['id'], dest_sec_groups)
|
dest_sec_group = self.have_id(sg['id'], dest_sec_groups)
|
||||||
@ -193,6 +208,7 @@ class ApiReplayClient(object):
|
|||||||
is False):
|
is False):
|
||||||
try:
|
try:
|
||||||
body = self.drop_fields(sg_rule, drop_sg_fields)
|
body = self.drop_fields(sg_rule, drop_sg_fields)
|
||||||
|
self.fix_description(body)
|
||||||
print(
|
print(
|
||||||
self.dest_neutron.create_security_group_rule(
|
self.dest_neutron.create_security_group_rule(
|
||||||
{'security_group_rule': body}))
|
{'security_group_rule': body}))
|
||||||
@ -209,6 +225,7 @@ class ApiReplayClient(object):
|
|||||||
sg_rules = sg.pop('security_group_rules')
|
sg_rules = sg.pop('security_group_rules')
|
||||||
try:
|
try:
|
||||||
body = self.drop_fields(sg, drop_sg_fields)
|
body = self.drop_fields(sg, drop_sg_fields)
|
||||||
|
self.fix_description(body)
|
||||||
new_sg = self.dest_neutron.create_security_group(
|
new_sg = self.dest_neutron.create_security_group(
|
||||||
{'security_group': body})
|
{'security_group': body})
|
||||||
print("Created security-group %s" % new_sg)
|
print("Created security-group %s" % new_sg)
|
||||||
@ -216,9 +233,12 @@ class ApiReplayClient(object):
|
|||||||
# TODO(arosen): improve exception handing here.
|
# TODO(arosen): improve exception handing here.
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
# Note - policy security groups will have no rules, and will
|
||||||
|
# be created on the destination with the default rules only
|
||||||
for sg_rule in sg_rules:
|
for sg_rule in sg_rules:
|
||||||
try:
|
try:
|
||||||
body = self.drop_fields(sg_rule, drop_sg_fields)
|
body = self.drop_fields(sg_rule, drop_sg_fields)
|
||||||
|
self.fix_description(body)
|
||||||
rule = self.dest_neutron.create_security_group_rule(
|
rule = self.dest_neutron.create_security_group_rule(
|
||||||
{'security_group_rule': body})
|
{'security_group_rule': body})
|
||||||
print("created security group rule %s " % rule['id'])
|
print("created security group rule %s " % rule['id'])
|
||||||
@ -247,16 +267,18 @@ class ApiReplayClient(object):
|
|||||||
if router.get('routes'):
|
if router.get('routes'):
|
||||||
update_routes[router['id']] = router['routes']
|
update_routes[router['id']] = router['routes']
|
||||||
|
|
||||||
drop_router_fields = ['status',
|
drop_router_fields = self.basic_ignore_fields + [
|
||||||
'routes',
|
'status',
|
||||||
'ha',
|
'routes',
|
||||||
'external_gateway_info',
|
'ha',
|
||||||
'router_type',
|
'external_gateway_info',
|
||||||
'availability_zone_hints',
|
'router_type',
|
||||||
'availability_zones',
|
'availability_zone_hints',
|
||||||
'distributed',
|
'availability_zones',
|
||||||
'revision']
|
'distributed',
|
||||||
|
'flavor_id']
|
||||||
body = self.drop_fields(router, drop_router_fields)
|
body = self.drop_fields(router, drop_router_fields)
|
||||||
|
self.fix_description(body)
|
||||||
new_router = (self.dest_neutron.create_router(
|
new_router = (self.dest_neutron.create_router(
|
||||||
{'router': body}))
|
{'router': body}))
|
||||||
print("created router %s" % new_router)
|
print("created router %s" % new_router)
|
||||||
@ -269,6 +291,40 @@ class ApiReplayClient(object):
|
|||||||
{'router': {'routes': routes}})
|
{'router': {'routes': routes}})
|
||||||
print("Added routes to router %s" % router_id)
|
print("Added routes to router %s" % router_id)
|
||||||
|
|
||||||
|
def migrate_subnetpools(self):
|
||||||
|
source_subnetpools = self.source_neutron.list_subnetpools()[
|
||||||
|
'subnetpools']
|
||||||
|
dest_subnetpools = self.dest_neutron.list_subnetpools()[
|
||||||
|
'subnetpools']
|
||||||
|
drop_subnetpool_fields = self.basic_ignore_fields + [
|
||||||
|
'id',
|
||||||
|
'ip_version']
|
||||||
|
|
||||||
|
subnetpools_map = {}
|
||||||
|
for pool in source_subnetpools:
|
||||||
|
# a default subnetpool (per ip-version) should be unique.
|
||||||
|
# so do not create one if already exists
|
||||||
|
if pool['is_default']:
|
||||||
|
for dpool in dest_subnetpools:
|
||||||
|
if (dpool['is_default'] and
|
||||||
|
dpool['ip_version'] == pool['ip_version']):
|
||||||
|
subnetpools_map[pool['id']] = dpool['id']
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
old_id = pool['id']
|
||||||
|
body = self.drop_fields(pool, drop_subnetpool_fields)
|
||||||
|
self.fix_description(body)
|
||||||
|
if 'default_quota' in body and body['default_quota'] is None:
|
||||||
|
del body['default_quota']
|
||||||
|
|
||||||
|
new_id = self.dest_neutron.create_subnetpool(
|
||||||
|
{'subnetpool': body})['subnetpool']['id']
|
||||||
|
subnetpools_map[old_id] = new_id
|
||||||
|
# refresh the list of existing subnetpools
|
||||||
|
dest_subnetpools = self.dest_neutron.list_subnetpools()[
|
||||||
|
'subnetpools']
|
||||||
|
return subnetpools_map
|
||||||
|
|
||||||
def migrate_networks_subnets_ports(self):
|
def migrate_networks_subnets_ports(self):
|
||||||
"""Migrates networks/ports/router-uplinks from src to dest neutron."""
|
"""Migrates networks/ports/router-uplinks from src to dest neutron."""
|
||||||
source_ports = self.source_neutron.list_ports()['ports']
|
source_ports = self.source_neutron.list_ports()['ports']
|
||||||
@ -277,37 +333,40 @@ class ApiReplayClient(object):
|
|||||||
dest_networks = self.dest_neutron.list_networks()['networks']
|
dest_networks = self.dest_neutron.list_networks()['networks']
|
||||||
dest_ports = self.dest_neutron.list_ports()['ports']
|
dest_ports = self.dest_neutron.list_ports()['ports']
|
||||||
|
|
||||||
# NOTE: These are fields we drop of when creating a subnet as the
|
# Remove some fields before creating the new object.
|
||||||
# network api doesn't allow us to specify them.
|
# Some fields are not supported for a new object, and some are not
|
||||||
# TODO(arosen): revisit this to make these fields passable.
|
# supported by the nsx-v3 plugin
|
||||||
drop_subnet_fields = ['updated_at',
|
drop_subnet_fields = self.basic_ignore_fields + [
|
||||||
'created_at',
|
'advanced_service_providers',
|
||||||
'network_id',
|
'id']
|
||||||
'advanced_service_providers',
|
|
||||||
'id', 'revision']
|
|
||||||
|
|
||||||
drop_port_fields = ['updated_at',
|
drop_port_fields = self.basic_ignore_fields + [
|
||||||
'created_at',
|
'status',
|
||||||
'status',
|
'port_security_enabled',
|
||||||
'port_security_enabled',
|
'binding:vif_details',
|
||||||
'binding:vif_details',
|
'binding:vif_type',
|
||||||
'binding:vif_type',
|
'binding:host_id',
|
||||||
'binding:host_id',
|
'vnic_index',
|
||||||
'revision',
|
'dns_assignment']
|
||||||
'vnic_index']
|
|
||||||
|
|
||||||
drop_network_fields = ['status', 'subnets', 'availability_zones',
|
drop_network_fields = self.basic_ignore_fields + [
|
||||||
'created_at', 'updated_at', 'tags',
|
'status',
|
||||||
'ipv4_address_scope', 'ipv6_address_scope',
|
'subnets',
|
||||||
'mtu', 'revision']
|
'availability_zones',
|
||||||
|
'availability_zone_hints',
|
||||||
|
'ipv4_address_scope',
|
||||||
|
'ipv6_address_scope',
|
||||||
|
'mtu']
|
||||||
|
|
||||||
|
if not self.dest_qos_support:
|
||||||
|
drop_network_fields.append('qos_policy_id')
|
||||||
|
drop_port_fields.append('qos_policy_id')
|
||||||
|
|
||||||
|
subnetpools_map = self.migrate_subnetpools()
|
||||||
for network in source_networks:
|
for network in source_networks:
|
||||||
|
#TODO(asarfaty): We may need special code for external net migrate
|
||||||
body = self.drop_fields(network, drop_network_fields)
|
body = self.drop_fields(network, drop_network_fields)
|
||||||
|
self.fix_description(body)
|
||||||
# neutron doesn't like description being None even though its
|
|
||||||
# what it returns to us.
|
|
||||||
if 'description' in body and body['description'] is None:
|
|
||||||
body['description'] = ''
|
|
||||||
|
|
||||||
# only create network if the dest server doesn't have it
|
# only create network if the dest server doesn't have it
|
||||||
if self.have_id(network['id'], dest_networks) is False:
|
if self.have_id(network['id'], dest_networks) is False:
|
||||||
@ -323,14 +382,17 @@ class ApiReplayClient(object):
|
|||||||
# specify the network_id that we just created above
|
# specify the network_id that we just created above
|
||||||
body['network_id'] = network['id']
|
body['network_id'] = network['id']
|
||||||
self.subnet_drop_ipv6_fields_if_v4(body)
|
self.subnet_drop_ipv6_fields_if_v4(body)
|
||||||
if 'description' in body and body['description'] is None:
|
self.fix_description(body)
|
||||||
body['description'] = ''
|
# translate the old subnetpool id to the new one
|
||||||
|
if body.get('subnetpool_id'):
|
||||||
|
body['subnetpool_id'] = subnetpools_map.get(
|
||||||
|
body['subnetpool_id'])
|
||||||
try:
|
try:
|
||||||
created_subnet = self.dest_neutron.create_subnet(
|
created_subnet = self.dest_neutron.create_subnet(
|
||||||
{'subnet': body})['subnet']
|
{'subnet': body})['subnet']
|
||||||
print("Created subnet: " + created_subnet['id'])
|
print("Created subnet: " + created_subnet['id'])
|
||||||
except n_exc.BadRequest as e:
|
except n_exc.BadRequest as e:
|
||||||
print(e)
|
print("Failed to create subnet: " + str(e))
|
||||||
# NOTE(arosen): this occurs here if you run the script
|
# NOTE(arosen): this occurs here if you run the script
|
||||||
# multiple times as we don't currently
|
# multiple times as we don't currently
|
||||||
# perserve the subnet_id. Also, 409 would be a better
|
# perserve the subnet_id. Also, 409 would be a better
|
||||||
@ -341,6 +403,7 @@ class ApiReplayClient(object):
|
|||||||
for port in ports:
|
for port in ports:
|
||||||
|
|
||||||
body = self.drop_fields(port, drop_port_fields)
|
body = self.drop_fields(port, drop_port_fields)
|
||||||
|
self.fix_description(body)
|
||||||
|
|
||||||
# specify the network_id that we just created above
|
# specify the network_id that we just created above
|
||||||
port['network_id'] = network['id']
|
port['network_id'] = network['id']
|
||||||
@ -379,15 +442,20 @@ class ApiReplayClient(object):
|
|||||||
print("Uplinked router %s to subnet %s" %
|
print("Uplinked router %s to subnet %s" %
|
||||||
(port['device_id'], created_subnet['id']))
|
(port['device_id'], created_subnet['id']))
|
||||||
continue
|
continue
|
||||||
except n_exc.BadRequest as e:
|
except Exception as e:
|
||||||
# NOTE(arosen): this occurs here if you run the
|
# NOTE(arosen): this occurs here if you run the
|
||||||
# script multiple times as we don't track this.
|
# script multiple times as we don't track this.
|
||||||
print(e)
|
print("Failed to add router interface: " + str(e))
|
||||||
raise
|
|
||||||
|
|
||||||
created_port = self.dest_neutron.create_port(
|
try:
|
||||||
{'port': body})['port']
|
created_port = self.dest_neutron.create_port(
|
||||||
print("Created port: " + created_port['id'])
|
{'port': body})['port']
|
||||||
|
except Exception as e:
|
||||||
|
# NOTE(arosen): this occurs here if you run the
|
||||||
|
# script multiple times as we don't track this.
|
||||||
|
print("Failed to create port: " + str(e))
|
||||||
|
else:
|
||||||
|
print("Created port: " + created_port['id'])
|
||||||
|
|
||||||
def migrate_floatingips(self):
|
def migrate_floatingips(self):
|
||||||
"""Migrates floatingips from source to dest neutron."""
|
"""Migrates floatingips from source to dest neutron."""
|
||||||
|
@ -461,11 +461,12 @@ class VMManager(VCManagerBase):
|
|||||||
return vm_ref.value
|
return vm_ref.value
|
||||||
|
|
||||||
def get_vm_spec(self, vm_moref):
|
def get_vm_spec(self, vm_moref):
|
||||||
vm_spec = self._session.invoke_api(vim_util,
|
vm_specs = self._session.invoke_api(vim_util,
|
||||||
'get_object_properties',
|
'get_object_properties',
|
||||||
self._session.vim,
|
self._session.vim,
|
||||||
vm_moref, ['network'])[0]
|
vm_moref, ['network'])
|
||||||
return vm_spec
|
if vm_specs:
|
||||||
|
return vm_specs[0]
|
||||||
|
|
||||||
def _build_vm_spec_attach(self, neutron_port_id, port_mac,
|
def _build_vm_spec_attach(self, neutron_port_id, port_mac,
|
||||||
nsx_net_id, device_type):
|
nsx_net_id, device_type):
|
||||||
|
@ -234,6 +234,9 @@ def migrate_compute_ports_vms(resource, event, trigger, **kwargs):
|
|||||||
# get the vm moref & spec from the DVS
|
# get the vm moref & spec from the DVS
|
||||||
vm_moref = vm_mng.get_vm_moref_obj(device_id)
|
vm_moref = vm_mng.get_vm_moref_obj(device_id)
|
||||||
vm_spec = vm_mng.get_vm_spec(vm_moref)
|
vm_spec = vm_mng.get_vm_spec(vm_moref)
|
||||||
|
if not vm_spec:
|
||||||
|
LOG.error(_LE("Failed to get the spec of vm %s"), device_id)
|
||||||
|
continue
|
||||||
|
|
||||||
# Go over the VM interfaces and check if it should be updated
|
# Go over the VM interfaces and check if it should be updated
|
||||||
update_spec = False
|
update_spec = False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user