vmware-nsx/vmware_nsx/shell/admin/plugins/nsxv/resources/lbaas_migrate.py

415 lines
17 KiB
Python

# Copyright 2015 VMware, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from neutron.callbacks import registry
from neutron.db import servicetype_db
from neutron.plugins.common import constants as neutron_const
from neutron_lbaas.db.loadbalancer import loadbalancer_db as neutron_lbaas_v1
from neutron_lbaas.db.loadbalancer import models as neutron_lbaas_v2
from neutron_lbaas.services.loadbalancer import constants as lb_const
from neutron_lbaas.services.loadbalancer.drivers.vmware import db as nsx_lb_v1
from vmware_nsx._i18n import _LI
from vmware_nsx.db import nsxv_db
from vmware_nsx.shell.admin.plugins.common import constants
import vmware_nsx.shell.admin.plugins.common.utils as admin_utils
import vmware_nsx.shell.admin.plugins.nsxv.resources.utils as utils
from vmware_nsx.shell import resources
from oslo_utils import uuidutils
LOG = logging.getLogger(__name__)
neutron_db = utils.NeutronDbClient()
# default values for new V2 fields
default_provis_status = neutron_const.ACTIVE
default_operating_status = lb_const.ONLINE
def _create_v2_healthmonitor(v1_db_instance, v1_pool_id):
"""get one of the health-monitor entries of this pool,
create a matching V2 entry, and return it's id, and also the matching v1 id
If no entry was found - return None.
Please note - lbaas v1 can have multiple health-monitors per pool,
Here we choose one of those, arbitrarily for the V2 loadbalancer
"""
query = v1_db_instance._model_query(
neutron_db.context,
neutron_lbaas_v1.PoolMonitorAssociation)
hm_association = query.filter_by(pool_id=v1_pool_id).first()
if hm_association:
v1_hm_id = hm_association['monitor_id']
hm_data = v1_db_instance.get_health_monitor(
neutron_db.context,
v1_hm_id)
v2_hm_id = uuidutils.generate_uuid()
v2_hm_data = {'id': v2_hm_id,
'tenant_id': hm_data['tenant_id'],
'type': hm_data['type'],
'delay': hm_data['delay'],
'timeout': hm_data['timeout'],
'max_retries': hm_data['max_retries'],
'admin_state_up': hm_data['admin_state_up'],
'provisioning_status': default_provis_status,
'name': None
}
# the existence of those attributes depends on the hm type
for attr in ['url_path', 'http_method', 'expected_codes']:
if attr in hm_data:
v2_hm_data[attr] = hm_data[attr]
else:
v2_hm_data[attr] = None
db_entry = neutron_lbaas_v2.HealthMonitorV2(**v2_hm_data)
neutron_db.context.session.add(db_entry)
return v2_hm_id, v1_hm_id
else:
# no matching health monitor entry was found
return None, None
def _create_v2_lb_stats(v1_db_instance, v1_pool_id, v2_lb_id):
query = v1_db_instance._model_query(neutron_db.context,
neutron_lbaas_v1.PoolStatistics)
pool_stats = query.filter_by(pool_id=v1_pool_id).first()
if pool_stats:
v2_lb_stats = {'loadbalancer_id': v2_lb_id,
'bytes_in': pool_stats['bytes_in'],
'bytes_out': pool_stats['bytes_out'],
'active_connections': pool_stats['active_connections'],
'total_connections': pool_stats['total_connections'],
}
else:
# if there is a v1 matching entry, create an empty one
v2_lb_stats = {'loadbalancer_id': v2_lb_id,
'bytes_in': 0,
'bytes_out': 0,
'active_connections': 0,
'total_connections': 0,
}
db_entry = neutron_lbaas_v2.LoadBalancerStatistics(**v2_lb_stats)
neutron_db.context.session.add(db_entry)
def _create_v2_pool(v1_neutron_db, v1_pool_data, v2_lb_id, v2_hm_id):
# Create the v2 pool entry, and return its id
v2_pool_id = uuidutils.generate_uuid()
v2_pool = {
'id': v2_pool_id,
'loadbalancer_id': v2_lb_id,
'tenant_id': v1_pool_data['tenant_id'],
'name': v1_pool_data['name'],
'description': v1_pool_data['description'],
'protocol': v1_pool_data['protocol'],
'lb_algorithm': v1_pool_data['lb_method'],
'healthmonitor_id': v2_hm_id,
'admin_state_up': v1_pool_data['admin_state_up'],
'provisioning_status': default_provis_status,
'operating_status': default_operating_status
}
db_entry = neutron_lbaas_v2.PoolV2(**v2_pool)
neutron_db.context.session.add(db_entry)
return v2_pool_id
def _create_v2_listener(v1_vip_data, v2_lb_id, v2_pool_id):
# Create the v2 listener entry and return the id
v2_listener_id = uuidutils.generate_uuid()
v2_listener = {'id': v2_listener_id,
'tenant_id': v1_vip_data['tenant_id'],
'name': None,
'description': None,
'protocol': v1_vip_data['protocol'],
'protocol_port': v1_vip_data['protocol_port'],
'connection_limit': v1_vip_data['connection_limit'],
'loadbalancer_id': v2_lb_id,
'default_pool_id': v2_pool_id,
'admin_state_up': v1_vip_data['admin_state_up'],
'provisioning_status': default_provis_status,
'operating_status': default_operating_status,
'default_tls_container_id': None # Not supported by V1
}
db_entry = neutron_lbaas_v2.Listener(**v2_listener)
neutron_db.context.session.add(db_entry)
return v2_listener_id
def _create_v2_member(v1_member_data, v2_pool_id, subnet_id):
# create a member entry in lbaas v2
v2_member_id = uuidutils.generate_uuid()
v2_memeber = {'id': v2_member_id,
'tenant_id': v1_member_data['tenant_id'],
'pool_id': v2_pool_id,
'subnet_id': subnet_id,
'address': v1_member_data['address'],
'protocol_port': v1_member_data['protocol_port'],
'weight': v1_member_data['weight'],
'admin_state_up': v1_member_data['admin_state_up'],
'provisioning_status': default_provis_status,
'operating_status': default_operating_status,
'name': None
}
db_entry = neutron_lbaas_v2.MemberV2(**v2_memeber)
neutron_db.context.session.add(db_entry)
def _create_v2_sess_persistence(v1_vip_id, v2_pool_id):
# create V2 session persistence entry if one was created in v1
sess_qry = neutron_db.context.session.query(
neutron_lbaas_v1.SessionPersistence)
v1_entry = sess_qry.filter_by(vip_id=v1_vip_id).first()
if v1_entry:
v2_entry = {'pool_id': v2_pool_id,
'type': v1_entry['type'],
'cookie_name': v1_entry['cookie_name']
}
db_entry = neutron_lbaas_v2.SessionPersistenceV2(**v2_entry)
neutron_db.context.session.add(db_entry)
def _create_v2_nsx_mappings(v1_pool_id, v1_hm_id, v2_pool_id, v2_lb_id,
v2_listener_id, v2_hm_id, vip_address):
# NSX health-monitor edge mapping
v1_monitor_edge_mappings = nsx_lb_v1.get_nsxv_edge_monitor_mapping_all(
neutron_db.context, v1_hm_id)
if len(v1_monitor_edge_mappings) > 0:
v1_hm_map = v1_monitor_edge_mappings[0]
nsxv_db.add_nsxv_lbaas_monitor_binding(
neutron_db.context.session,
v2_lb_id,
v2_pool_id,
v2_hm_id,
v1_hm_map['edge_id'],
v1_hm_map['edge_monitor_id'])
# NSX edge pool mapping
if v2_listener_id is not None:
v1_pool_map = nsx_lb_v1.get_nsxv_edge_pool_mapping(
neutron_db.context, v1_pool_id)
if v1_pool_map is not None:
nsxv_db.add_nsxv_lbaas_pool_binding(
neutron_db.context.session,
v2_lb_id,
v2_pool_id,
v1_pool_map['edge_pool_id'])
# NSX V1 edge vip mappings -> loadbalancer & listener binding
v1_vip_map = nsx_lb_v1.get_nsxv_edge_vip_mapping(
neutron_db.context, v1_pool_id)
if v1_vip_map is not None:
nsxv_db.add_nsxv_lbaas_loadbalancer_binding(
neutron_db.context.session,
v2_lb_id,
v1_vip_map['edge_id'],
v1_vip_map['edge_fw_rule_id'],
vip_address)
if v2_listener_id is not None:
nsxv_db.add_nsxv_lbaas_listener_binding(
neutron_db.context.session,
v2_lb_id,
v2_listener_id,
v1_vip_map['edge_app_profile_id'],
v1_vip_map['edge_vse_id'])
def _delete_v1_entries_for_pool(v1_db_instance, v1_pool_id, pool_data):
# Delete all lbaas v1 db entries related to a specific pool id
# delete nsx mappings of this pool
v1_pool_map = nsx_lb_v1.get_nsxv_edge_pool_mapping(
neutron_db.context, v1_pool_id)
if v1_pool_map is not None:
neutron_db.context.session.delete(v1_pool_map)
v1_vip_map = nsx_lb_v1.get_nsxv_edge_vip_mapping(
neutron_db.context, v1_pool_id)
if v1_vip_map is not None:
neutron_db.context.session.delete(v1_vip_map)
# delete the provider-resource-association entry for this pool
provider_resource_association = None
query = neutron_db.context.session.query(
servicetype_db.ProviderResourceAssociation)
entry = query.filter_by(resource_id=v1_pool_id).first()
if entry is not None:
provider_resource_association = entry
neutron_db.context.session.delete(entry)
# delete the pool itself (which will automatically delete pool-stats,
# pool-monitor-association & member)
pool = v1_db_instance._get_resource(neutron_db.context,
neutron_lbaas_v1.Pool,
v1_pool_id)
neutron_db.context.session.delete(pool)
# delete vip and related entries
if 'vip_id' in pool_data and pool_data['vip_id'] is not None:
v1_vip_id = pool_data['vip_id']
# delete the vip entry
# delete of the session-persistence will be done automatically
# when the vip is deleted
vip = v1_db_instance._get_resource(neutron_db.context,
neutron_lbaas_v1.Vip,
v1_vip_id)
if vip is not None:
neutron_db.context.session.delete(vip)
if provider_resource_association:
association = servicetype_db.ProviderResourceAssociation(
provider_name=provider_resource_association.provider_name,
resource_id=provider_resource_association.resource_id)
neutron_db.context.session.add(association)
def _delete_v1_healthmonitors(v1_db_instance):
# delete all health monitor entries, and their nsxv mappings
hms = v1_db_instance.get_health_monitors(neutron_db.context)
for hm in hms:
# delete the nsxv mapping
v1_monitor_edge_mappings = nsx_lb_v1.get_nsxv_edge_monitor_mapping_all(
neutron_db.context, hm['id'])
for entry in v1_monitor_edge_mappings:
neutron_db.context.session.delete(entry)
hm_entry = v1_db_instance._get_resource(
neutron_db.context, neutron_lbaas_v1.HealthMonitor, hm['id'])
neutron_db.context.session.delete(hm_entry)
@admin_utils.output_header
def nsx_migrate_lbaas(resource, event, trigger, **kwargs):
"""Migrate lbaas-v1 db to lbaas-v2"""
v1_neutron_db = neutron_lbaas_v1.LoadBalancerPluginDb()
pools = v1_neutron_db.get_pools(neutron_db.context)
if not len(pools):
LOG.info(_LI("No Lbaas V1 configuration to migrate"))
return
props = kwargs.get('property')
delete_v1_entries = True if props and props[0] == 'delete-v1' else False
if delete_v1_entries:
extra_msg = _LI("and deleting the lbaas V1 database entries")
else:
extra_msg = _LI("without deleting the lbaas V1 database entries")
LOG.info(_LI("Migrating lbaas V1 db entries to lbaas V2 %s"), extra_msg)
# the entire migration process will be done under the same transaction to
# allow full rollback in case of error
with neutron_db.context.session.begin(subtransactions=True):
# Go over all lbaas V1 pools
for pool_data in pools:
v1_pool_id = pool_data['id']
tenant_id = pool_data['tenant_id']
v1_pool_with_vip = False
# get all the values from the pool and the related vip
# in order to create the loadbalancer object for V2
if 'vip_id' in pool_data and pool_data['vip_id'] is not None:
v1_pool_with_vip = True
v1_vip_id = pool_data['vip_id']
v1_vip_data = v1_neutron_db.get_vip(neutron_db.context,
v1_vip_id)
# get the information from the old vip entry for
# the new loadbalancer entry
lb_name = v1_vip_data['name']
lb_description = v1_vip_data['description']
lb_vip_port_id = v1_vip_data['port_id']
lb_vip_addr = v1_vip_data['address']
lb_admin_state_up = v1_vip_data['admin_state_up']
else:
# no vip entry - default/empty values
lb_name = None
lb_description = None
lb_vip_port_id = None
lb_vip_addr = None
lb_admin_state_up = 0
# create the V2 load-balancer entry
# keep the old pool id as the id of the new loadbalancer
v2_lb_id = v1_pool_id
v2_lb = {'id': v2_lb_id,
'tenant_id': tenant_id,
'vip_subnet_id': pool_data['subnet_id'],
'provisioning_status': default_provis_status,
'operating_status': default_operating_status,
'name': lb_name,
'description': lb_description,
'vip_port_id': lb_vip_port_id,
'vip_address': lb_vip_addr,
'admin_state_up': lb_admin_state_up,
'flavor_id': None
}
db_entry = neutron_lbaas_v2.LoadBalancer(**v2_lb)
neutron_db.context.session.add(db_entry)
# Create the loadbalancers stats entry
_create_v2_lb_stats(v1_neutron_db, v1_pool_id, v2_lb_id)
# create the health monitor entry (if existed in v1)
v2_hm_id, v1_hm_id = _create_v2_healthmonitor(v1_neutron_db,
v1_pool_id)
# Create the v2 pool entry
v2_pool_id = _create_v2_pool(v1_neutron_db,
pool_data,
v2_lb_id,
v2_hm_id)
# Create lbaas V2 members for each v1 member of the same pool
filters = {'pool_id': [v1_pool_id]}
v1_members = v1_neutron_db.get_members(neutron_db.context,
filters=filters)
for member in v1_members:
# create member in lbaas v2
_create_v2_member(member, v2_pool_id, pool_data['subnet_id'])
# Create lbaas V2 listener entry in V1 had vip in this pool
v2_listener_id = None
if v1_pool_with_vip:
v2_listener_id = _create_v2_listener(v1_vip_data,
v2_lb_id,
v2_pool_id)
# create V2 session-persistence entry based on the v1 entry
_create_v2_sess_persistence(v1_vip_id, v2_pool_id)
# Update the NSX mappings table from v1 to v2
_create_v2_nsx_mappings(v1_pool_id, v1_hm_id,
v2_pool_id, v2_lb_id,
v2_listener_id, v2_hm_id,
lb_vip_addr)
if delete_v1_entries:
_delete_v1_entries_for_pool(v1_neutron_db,
v1_pool_id, pool_data)
if delete_v1_entries:
# Delete all health monitors,
# because they do not belong to a specific pool
_delete_v1_healthmonitors(v1_neutron_db)
LOG.info(_LI("Finished migrating %d Lbaas V1 pools"), len(pools))
return
registry.subscribe(nsx_migrate_lbaas,
constants.LBAAS,
resources.Operations.NSX_MIGRATE_V1_V2.value)