NSX|V3: Update upgrade scripts for native DHCP/Metadata

1. Upgrade ports with IPv4 address only
2. Delete previous internal metadata networks
3. Add list function for metadata proxy
4. Add more checking and logs
5. Pass required UUID from command-line
6. Refactor codes

DocImpact

Change-Id: I4e0f05b2dff9394cc6d0d567abf58507efaf6685
This commit is contained in:
Shih-Hao Li 2016-08-23 07:54:15 -07:00 committed by garyk
parent 38c163b3e1
commit c8b984ed5b
6 changed files with 227 additions and 47 deletions

View File

@ -205,6 +205,17 @@ Security Groups
nsx -r nsx-security-groups -o migrate-to-dynamic-criteria
Metadata Proxy
~~~~~~~~~~~~~~
- List version 1.0.0 metadata networks in Neutron::
nsxadmin -r metadata-proxy -o list
- Resync metadata proxies for NSXv3 version 1.1.0 and above::
nsxadmin -r metadata-proxy -o nsx-update --property metadata_proxy_uuid=<metadata_proxy_uuid>
DHCP Bindings
~~~~~~~~~~~~~
@ -212,6 +223,30 @@ DHCP Bindings
nsxadmin -r dhcp-binding -o list
- Resync DHCP bindings for NSXv3 CrossHairs::
- Resync DHCP bindings for NSXv3 version 1.1.0 and above::
nsxadmin -r dhcp-binding -o nsx-update
nsxadmin -r dhcp-binding -o nsx-update --property dhcp_profile_uuid=<dhcp_profile_uuid>
Upgrade Steps (Version 1.0.0 to Version 1.1.0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Upgrade NSX backend from version 1.0.0 to version 1.1.0
2. Create a DHCP-Profile and a Metadata-Proxy in NSX backend
3. Stop Neutron
4. Install version 1.1.0 Neutron plugin
5. Run admin tools to migrate version 1.0.0 objects to version 1.1.0 objects
nsxadmin -r metadata-proxy -o nsx-update --property metadata_proxy_uuid=<UUID of Metadata-Proxy created in Step 2>
nsxadmin -r dhcp-binding -o nsx-update --property dhcp_profile_uuid=<UUID of DHCP-Profile created in Step 2>
6. Start Neutron
7. Make sure /etc/nova/nova.conf has
metadata_proxy_shared_secret = <Secret of Metadata-Proxy created in Step 2>
8. Restart VMs or ifdown/ifup their network interface to get new DHCP options

View File

@ -157,6 +157,31 @@ Example
}
Upgrade Steps (Version 1.0.0 to Version 1.1.0)
----------------------------------------------
1. Upgrade NSX backend from version 1.0.0 to version 1.1.0
2. Create a DHCP-Profile and a Metadata-Proxy in NSX backend
3. Stop Neutron
4. Install version 1.1.0 Neutron plugin
5. Run admin tools to migrate version 1.0.0 objects to version 1.1.0 objects
* nsxadmin -r metadata-proxy -o nsx-update --property metadata_proxy_uuid=<UUID of Metadata-Proxy created in Step 2>
* nsxadmin -r dhcp-binding -o nsx-update --property dhcp_profile_uuid=<UUID of DHCP-Profile created in Step 2>
6. Start Neutron
7. Make sure /etc/nova/nova.conf has
metadata_proxy_shared_secret = <Secret of Metadata-Proxy created in Step 2>
8. Restart VMs or ifdown/ifup their network interface to get new DHCP options
Help
----
::

View File

@ -13,14 +13,15 @@
# under the License.
import logging
import netaddr
from neutron.callbacks import registry
from neutron_lib import constants as const
from oslo_config import cfg
from vmware_nsx._i18n import _LI
from vmware_nsx._i18n import _LE, _LI
from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils as comm_utils
from vmware_nsx.common import utils as nsx_utils
from vmware_nsx.nsxlib.v3 import native_dhcp
from vmware_nsx.nsxlib.v3 import resources
from vmware_nsx.shell.admin.plugins.common import constants
@ -37,9 +38,9 @@ neutron_client = utils.NeutronDbClient()
def list_dhcp_bindings(resource, event, trigger, **kwargs):
"""List DHCP bindings in Neutron."""
ports = neutron_client.get_ports()
comp_ports = [port for port in ports if port['device_owner'].startswith(
const.DEVICE_OWNER_COMPUTE_PREFIX)]
comp_ports = [port for port in neutron_client.get_ports()
if port['device_owner'].startswith(
const.DEVICE_OWNER_COMPUTE_PREFIX)]
LOG.info(formatters.output_formatter(constants.DHCP_BINDING, comp_ports,
['id', 'mac_address', 'fixed_ips']))
@ -49,51 +50,81 @@ def nsx_update_dhcp_bindings(resource, event, trigger, **kwargs):
"""Resync DHCP bindings for NSXv3 CrossHairs."""
nsx_version = utils.get_connected_nsxlib().get_version()
if not comm_utils.is_nsx_version_1_1_0(nsx_version):
if not nsx_utils.is_nsx_version_1_1_0(nsx_version):
LOG.info(_LI("This utility is not available for NSX version %s"),
nsx_version)
return
dhcp_profile_uuid = None
if kwargs.get('property'):
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
dhcp_profile_uuid = properties.get('dhcp_profile_uuid')
if not dhcp_profile_uuid:
LOG.error(_LE("dhcp_profile_uuid is not defined"))
return
cfg.CONF.set_override('dhcp_agent_notification', False)
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
cfg.CONF.set_override('dhcp_profile_uuid', dhcp_profile_uuid, 'nsx_v3')
nsx_client = utils.get_nsxv3_client()
port_resource = resources.LogicalPort(nsx_client)
dhcp_server_resource = resources.LogicalDhcpServer(nsx_client)
port_bindings = {} # lswitch_id: [(mac, ip, prefix_length), ...]
port_bindings = {} # lswitch_id: [(port_id, mac, ip), ...]
server_bindings = {} # lswitch_id: dhcp_server_id
ports = neutron_client.get_ports()
for port in ports:
network_id = port['network_id']
device_owner = port['device_owner']
if device_owner == const.DEVICE_OWNER_DHCP:
# For each DHCP-enabled network, create a logical DHCP server
# and update the attachment type to DHCP on the corresponding
# logical port of the Neutron DHCP port.
subnet_id = port['fixed_ips'][0]['subnet_id']
subnet = neutron_client.get_subnet(subnet_id)
network = neutron_client.get_network(port['network_id'])
if len(port['fixed_ips']) > 1:
LOG.info(_LI("Network %(network)s has multiple subnets - "
"only enable native DHCP on subnet %(subnet)s"),
{'network': port['network_id'], 'subnet': subnet_id})
server_data = native_dhcp.build_dhcp_server_config(
network, subnet, port, 'NSX Neutron plugin upgrade')
dhcp_server = dhcp_server_resource.create(**server_data)
lswitch_id, lport_id = neutron_client.get_lswitch_and_lport_id(
port['id'])
port_resource.update(lport_id, dhcp_server['id'],
attachment_type=nsx_constants.ATTACHMENT_DHCP)
server_bindings[lswitch_id] = dhcp_server['id']
elif device_owner.startswith(const.DEVICE_OWNER_COMPUTE_PREFIX):
lswitch_id = neutron_client.net_id_to_lswitch_id(network_id)
bindings = port_bindings.get(lswitch_id, [])
bindings.append((port['mac_address'],
port['fixed_ips'][0]['ip_address']))
port_bindings[lswitch_id] = bindings
if (device_owner != const.DEVICE_OWNER_DHCP and
not device_owner.startswith(const.DEVICE_OWNER_COMPUTE_PREFIX)):
continue
for fixed_ip in port['fixed_ips']:
if netaddr.IPNetwork(fixed_ip['ip_address']).version == 6:
continue
network_id = port['network_id']
subnet = neutron_client.get_subnet(fixed_ip['subnet_id'])
if device_owner == const.DEVICE_OWNER_DHCP:
# For each DHCP-enabled network, create a logical DHCP server
# and update the attachment type to DHCP on the corresponding
# logical port of the Neutron DHCP port.
network = neutron_client.get_network(port['network_id'])
server_data = native_dhcp.build_dhcp_server_config(
network, subnet, port, 'admin')
dhcp_server = dhcp_server_resource.create(**server_data)
LOG.info(_LI("Created logical DHCP server %(server)s for "
"network %(network)s"),
{'server': dhcp_server['id'],
'network': port['network_id']})
# Add DHCP service binding in neutron DB.
neutron_client.add_dhcp_service_binding(
network['id'], port['id'], dhcp_server['id'])
# Update logical port for DHCP purpose.
lswitch_id, lport_id = (
neutron_client.get_lswitch_and_lport_id(port['id']))
port_resource.update(
lport_id, dhcp_server['id'],
attachment_type=nsx_constants.ATTACHMENT_DHCP)
server_bindings[lswitch_id] = dhcp_server['id']
LOG.info(_LI("Updated DHCP logical port %(port)s for "
"network %(network)s"),
{'port': lport_id, 'network': port['network_id']})
elif subnet['enable_dhcp']:
# Store (mac, ip) binding of each compute port in a
# DHCP-enabled subnet.
lswitch_id = neutron_client.net_id_to_lswitch_id(network_id)
bindings = port_bindings.get(lswitch_id, [])
bindings.append((port['id'], port['mac_address'],
fixed_ip['ip_address']))
port_bindings[lswitch_id] = bindings
break # process only the first IPv4 address
# Populate mac/IP bindings in each logical DHCP server.
for lswitch_id, bindings in port_bindings.items():
dhcp_server_id = server_bindings[lswitch_id]
for (mac, ip) in bindings:
dhcp_server_id = server_bindings.get(lswitch_id)
if not dhcp_server_id:
continue
for (port_id, mac, ip) in bindings:
hostname = 'host-%s' % ip.replace('.', '-')
options = {'option121': {'static_routes': [
{'network': '%s' % cfg.CONF.nsx_v3.native_metadata_route,
@ -101,6 +132,9 @@ def nsx_update_dhcp_bindings(resource, event, trigger, **kwargs):
dhcp_server_resource.create_binding(
dhcp_server_id, mac, ip, hostname,
cfg.CONF.nsx_v3.dhcp_lease_time, options)
LOG.info(_LI("Added DHCP binding (mac: %(mac)s, ip: %(ip)s) "
"for neutron port %(port)s"),
{'mac': mac, 'ip': ip, 'port': port_id})
registry.subscribe(list_dhcp_bindings,

View File

@ -15,13 +15,16 @@
import logging
from neutron.callbacks import registry
from neutron_lib import constants as const
from oslo_config import cfg
from vmware_nsx._i18n import _LI, _LE
from vmware_nsx._i18n import _LE, _LI
from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils as nsx_utils
from vmware_nsx.dhcp_meta import rpc as nsx_rpc
from vmware_nsx.nsxlib.v3 import resources
from vmware_nsx.shell.admin.plugins.common import constants
from vmware_nsx.shell.admin.plugins.common import formatters
from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils
import vmware_nsx.shell.resources as shell
@ -30,30 +33,92 @@ LOG = logging.getLogger(__name__)
neutron_client = utils.NeutronDbClient()
def _is_metadata_network(network):
# If a Neutron network has only one subnet with 169.254.169.252/30 CIDR,
# then it is an internal metadata network.
if len(network['subnets']) == 1:
subnet = neutron_client.get_subnet(network['subnets'][0])
if subnet['cidr'] == nsx_rpc.METADATA_SUBNET_CIDR:
return True
return False
@admin_utils.output_header
def list_metadata_networks(resource, event, trigger, **kwargs):
"""List Metadata networks in Neutron."""
meta_networks = [network for network in neutron_client.get_networks()
if _is_metadata_network(network)]
LOG.info(formatters.output_formatter(constants.METADATA_PROXY,
meta_networks,
['id', 'name', 'subnets']))
@admin_utils.output_header
def nsx_update_metadata_proxy(resource, event, trigger, **kwargs):
"""Update Metadata proxy for NSXv3 CrossHairs."""
nsx_version = utils.get_connected_nsxlib().get_version()
if not nsx_utils.is_nsx_version_1_1_0(nsx_version):
LOG.info(_LI("This utility is not available for NSX version %s"),
nsx_version)
return
metadata_proxy_uuid = None
if kwargs.get('property'):
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
metadata_proxy_uuid = properties.get('metadata_proxy_uuid')
if not metadata_proxy_uuid:
LOG.error(_LE("metadata_proxy_uuid is not defined"))
return
cfg.CONF.set_override('dhcp_agent_notification', False)
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
cfg.CONF.set_override('metadata_proxy_uuid', metadata_proxy_uuid, 'nsx_v3')
plugin = utils.NsxV3PluginWrapper()
nsx_client = utils.get_nsxv3_client()
port_resource = resources.LogicalPort(nsx_client)
# For each Neutron network, check if it is an internal metadata network.
# If yes, delete the network and associated router interface.
# Otherwise, create a logical switch port with MD-Proxy attachment.
for network in neutron_client.get_networks():
# For each Neutron network, create a logical switch port with
# MD-Proxy attachment.
lswitch_id = neutron_client.net_id_to_lswitch_id(network['id'])
if lswitch_id:
if _is_metadata_network(network):
# It is a metadata network, find the attached router,
# remove the router interface and the network.
filters = {'device_owner': const.ROUTER_INTERFACE_OWNERS,
'fixed_ips': {
'subnet_id': [network['subnets'][0]],
'ip_address': [nsx_rpc.METADATA_GATEWAY_IP]}}
ports = neutron_client.get_ports(filters=filters)
if not ports:
continue
router_id = ports[0]['device_id']
interface = {'subnet_id': network['subnets'][0]}
plugin.remove_router_interface(router_id, interface)
LOG.info(_LI("Removed metadata interface on router %s"), router_id)
plugin.delete_network(network['id'])
LOG.info(_LI("Removed metadata network %s"), network['id'])
else:
lswitch_id = neutron_client.net_id_to_lswitch_id(network['id'])
if not lswitch_id:
continue
tags = nsx_utils.build_v3_tags_payload(
network, resource_type='os-neutron-net-id',
project_name='NSX Neutron plugin upgrade')
project_name='admin')
name = nsx_utils.get_name_and_uuid('%s-%s' % (
'mdproxy', network['name'] or 'network'), network['id'])
port_resource.create(
lswitch_id, cfg.CONF.nsx_v3.metadata_proxy_uuid, tags=tags,
lswitch_id, metadata_proxy_uuid, tags=tags, name=name,
attachment_type=nsx_constants.ATTACHMENT_MDPROXY)
LOG.info(_LI("Enabled native metadata proxy for network %s"),
network['id'])
else:
LOG.error(_LE("Unable to find logical switch for network %s"),
network['id'])
registry.subscribe(list_metadata_networks,
constants.METADATA_PROXY,
shell.Operations.LIST.value)
registry.subscribe(nsx_update_metadata_proxy,
constants.METADATA_PROXY,
shell.Operations.NSX_UPDATE.value)

View File

@ -17,6 +17,7 @@ from neutron import context
from neutron.db import db_base_plugin_v2
from oslo_config import cfg
from vmware_nsx.common import nsx_constants
from vmware_nsx.db import db as nsx_db
from vmware_nsx.nsxlib import v3
from vmware_nsx.plugins.nsx_v3 import plugin
@ -72,8 +73,17 @@ class NeutronDbClient(db_base_plugin_v2.NeutronDbPluginV2):
lswitch_ids = nsx_db.get_nsx_switch_ids(self.context.session, net_id)
return lswitch_ids[0] if lswitch_ids else None
def add_dhcp_service_binding(self, network_id, port_id, server_id):
return nsx_db.add_neutron_nsx_service_binding(
self.context.session, network_id, port_id,
nsx_constants.SERVICE_DHCP, server_id)
class NsxV3PluginWrapper(plugin.NsxV3Plugin):
def __init__(self):
super(NsxV3PluginWrapper, self).__init__()
self.context = context.get_admin_context()
def _init_dhcp_metadata(self):
pass
@ -90,3 +100,11 @@ class NsxV3PluginWrapper(plugin.NsxV3Plugin):
self._extend_network_dict_provider(context, net)
# skip getting the Qos policy ID because get_object calls
# plugin init again on admin-util environment
def delete_network(self, network_id):
return super(NsxV3PluginWrapper, self).delete_network(
self.context, network_id)
def remove_router_interface(self, router_id, interface):
return super(NsxV3PluginWrapper, self).remove_router_interface(
self.context, router_id, interface)

View File

@ -79,6 +79,9 @@ nsxv3_resources = {
constants.DHCP_BINDING: Resource(constants.DHCP_BINDING,
[Operations.LIST.value,
Operations.NSX_UPDATE.value]),
constants.METADATA_PROXY: Resource(constants.METADATA_PROXY,
[Operations.LIST.value,
Operations.NSX_UPDATE.value]),
}
# Add supported NSX-V resources in this dictionary