Hyper-V Network Virtualization (HNV) was first introduced in Windows Hyper-V / Server 2012 and has the purpose of enabling the virtualization of Layer 2 and Layer 3 networking models. One of the HNV configuration approches is called NVGRE (Network Virtualization through GRE). Adds NVGRE related Utils and Ops class. Adds check in HyperVMechanismDriver if the given agent has NVGRE in it's reported configuration, in order to properly bind NVGRE neutron ports. Adds neutron_client implementation to fetch necessary information for NVGRE CustomerRoutes and LookupRecords. Emits ``lookup_update`` notifications when a new LookupRecord is updated. Registers HyperVNeutronAgent to ``lookup_update`` notifications and updates the given LookupRecord locally. Adds handle for ``tunnel_update`` notifications. Emits ``tunnel_update`` notification when HyperVNeutronAgent starts, in order for OpenVSwitch agents to create their own tunnels towards the agent. Implements: blueprint hyper-v-nvgre Change-Id: I8cf07770ae567ad3a1f3c906417e94133b00958c
		
			
				
	
	
		
			177 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Copyright 2015 Cloudbase Solutions SRL
 | 
						|
# 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.
 | 
						|
 | 
						|
from oslo_config import cfg
 | 
						|
from oslo_log import log as logging
 | 
						|
 | 
						|
from hyperv.common.i18n import _LI, _LW, _LE  # noqa
 | 
						|
from hyperv.neutron import constants
 | 
						|
from hyperv.neutron import hyperv_agent_notifier
 | 
						|
from hyperv.neutron import neutron_client
 | 
						|
from hyperv.neutron import utils_nvgre
 | 
						|
from hyperv.neutron import utilsfactory
 | 
						|
 | 
						|
CONF = cfg.CONF
 | 
						|
 | 
						|
LOG = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class HyperVNvgreOps(object):
 | 
						|
 | 
						|
    def __init__(self, physical_networks):
 | 
						|
        self.topic = constants.AGENT_TOPIC
 | 
						|
        self._vswitch_ips = {}
 | 
						|
        self._tunneling_agents = {}
 | 
						|
        self._nvgre_ports = []
 | 
						|
        self._network_vsids = {}
 | 
						|
 | 
						|
        self._hyperv_utils = utilsfactory.get_hypervutils()
 | 
						|
        self._nvgre_utils = utils_nvgre.NvgreUtils()
 | 
						|
        self._n_client = neutron_client.NeutronAPIClient()
 | 
						|
 | 
						|
        self._init_nvgre(physical_networks)
 | 
						|
 | 
						|
    def init_notifier(self, context, rpc_client):
 | 
						|
        self.context = context
 | 
						|
        self._notifier = hyperv_agent_notifier.AgentNotifierApi(
 | 
						|
            self.topic, rpc_client)
 | 
						|
 | 
						|
    def _init_nvgre(self, physical_networks):
 | 
						|
        for network in physical_networks:
 | 
						|
            LOG.info(_LI("Adding provider route and address for network: %s"),
 | 
						|
                     network)
 | 
						|
            self._nvgre_utils.create_provider_route(network)
 | 
						|
            self._nvgre_utils.create_provider_address(
 | 
						|
                network, CONF.NVGRE.provider_vlan_id)
 | 
						|
            ip_addr, length = self._nvgre_utils.get_network_iface_ip(network)
 | 
						|
            self._vswitch_ips[network] = ip_addr
 | 
						|
 | 
						|
    def _refresh_tunneling_agents(self):
 | 
						|
        self._tunneling_agents.update(self._n_client.get_tunneling_agents())
 | 
						|
 | 
						|
    def lookup_update(self, kwargs):
 | 
						|
        lookup_ip = kwargs.get('lookup_ip')
 | 
						|
        lookup_details = kwargs.get('lookup_details')
 | 
						|
 | 
						|
        LOG.info(_LI("Lookup Received: %(lookup_ip)s, %(lookup_details)s"),
 | 
						|
                 {'lookup_ip': lookup_ip, 'lookup_details': lookup_details})
 | 
						|
        if not lookup_ip or not lookup_details:
 | 
						|
            return
 | 
						|
 | 
						|
        self._register_lookup_record(lookup_ip,
 | 
						|
                                     lookup_details['customer_addr'],
 | 
						|
                                     lookup_details['mac_addr'],
 | 
						|
                                     lookup_details['customer_vsid'])
 | 
						|
 | 
						|
    def tunnel_update(self, context, tunnel_ip, tunnel_type):
 | 
						|
        if tunnel_type != constants.TYPE_NVGRE:
 | 
						|
            return
 | 
						|
        self._notifier.tunnel_update(context, CONF.NVGRE.provider_tunnel_ip,
 | 
						|
                                     tunnel_type)
 | 
						|
 | 
						|
    def _register_lookup_record(self, prov_addr, cust_addr, mac_addr, vsid):
 | 
						|
        LOG.info(_LI('Creating LookupRecord: VSID: %(vsid)s MAC: %(mac_addr)s '
 | 
						|
                     'Customer IP: %(cust_addr)s Provider IP: %(prov_addr)s'),
 | 
						|
                 dict(vsid=vsid,
 | 
						|
                      mac_addr=mac_addr,
 | 
						|
                      cust_addr=cust_addr,
 | 
						|
                      prov_addr=prov_addr))
 | 
						|
 | 
						|
        self._nvgre_utils.create_lookup_record(
 | 
						|
            prov_addr, cust_addr, mac_addr, vsid)
 | 
						|
 | 
						|
    def bind_nvgre_port(self, segmentation_id, network_name, port_id):
 | 
						|
        mac_addr = self._hyperv_utils.get_vnic_mac_address(port_id)
 | 
						|
        provider_addr = self._nvgre_utils.get_network_iface_ip(network_name)[0]
 | 
						|
        customer_addr = self._n_client.get_port_ip_address(port_id)
 | 
						|
 | 
						|
        if not provider_addr or not customer_addr:
 | 
						|
            LOG.warning(_LW('Cannot bind NVGRE port. Could not determine '
 | 
						|
                            'provider address (%(prov_addr)s) or customer '
 | 
						|
                            'address (%(cust_addr)s).'),
 | 
						|
                        {'prov_addr': provider_addr,
 | 
						|
                         'cust_addr': customer_addr})
 | 
						|
            return
 | 
						|
 | 
						|
        LOG.info(_LI('Binding VirtualSubnetID %(segmentation_id)s '
 | 
						|
                     'to switch port %(port_id)s'),
 | 
						|
                 dict(segmentation_id=segmentation_id, port_id=port_id))
 | 
						|
        self._hyperv_utils.set_vswitch_port_vsid(segmentation_id, port_id)
 | 
						|
 | 
						|
        # normal lookup record.
 | 
						|
        self._register_lookup_record(
 | 
						|
            provider_addr, customer_addr, mac_addr, segmentation_id)
 | 
						|
 | 
						|
        # lookup record for dhcp requests.
 | 
						|
        self._register_lookup_record(
 | 
						|
            self._vswitch_ips[network_name], constants.IPV4_DEFAULT,
 | 
						|
            mac_addr, segmentation_id)
 | 
						|
 | 
						|
        LOG.info('Fanning out LookupRecord...')
 | 
						|
        self._notifier.lookup_update(self.context,
 | 
						|
                                     provider_addr,
 | 
						|
                                     {'customer_addr': customer_addr,
 | 
						|
                                      'mac_addr': mac_addr,
 | 
						|
                                      'customer_vsid': segmentation_id})
 | 
						|
 | 
						|
    def bind_nvgre_network(self, segmentation_id, net_uuid, vswitch_name):
 | 
						|
        subnets = self._n_client.get_network_subnets(net_uuid)
 | 
						|
        if len(subnets) > 1:
 | 
						|
            LOG.warning(_LW("Multiple subnets in the same network is not "
 | 
						|
                            "supported."))
 | 
						|
        subnet = subnets[0]
 | 
						|
        try:
 | 
						|
            cidr, gw = self._n_client.get_network_subnet_cidr_and_gateway(
 | 
						|
                subnet)
 | 
						|
            self._nvgre_utils.create_customer_routes(
 | 
						|
                segmentation_id, vswitch_name, cidr, gw)
 | 
						|
        except Exception as ex:
 | 
						|
            LOG.error(_LE("Exception caught: %s"), ex)
 | 
						|
 | 
						|
        self._network_vsids[net_uuid] = segmentation_id
 | 
						|
        self.refresh_nvgre_records(network_id=net_uuid)
 | 
						|
        self._notifier.tunnel_update(
 | 
						|
            self.context, CONF.NVGRE.provider_tunnel_ip, segmentation_id)
 | 
						|
 | 
						|
    def refresh_nvgre_records(self, **kwargs):
 | 
						|
        self._refresh_tunneling_agents()
 | 
						|
        ports = self._n_client.get_network_ports(**kwargs)
 | 
						|
 | 
						|
        # process ports that were not processed yet.
 | 
						|
        # process ports that are bound to tunneling_agents.
 | 
						|
        ports = [p for p in ports if p['id'] not in self._nvgre_ports and
 | 
						|
                 p['binding:host_id'] in self._tunneling_agents and
 | 
						|
                 p['network_id'] in self._network_vsids.keys()]
 | 
						|
 | 
						|
        for port in ports:
 | 
						|
            tunneling_ip = self._tunneling_agents[port['binding:host_id']]
 | 
						|
            customer_addr = port['fixed_ips'][0]['ip_address']
 | 
						|
            mac_addr = port['mac_address'].replace(':', '')
 | 
						|
            segmentation_id = self._network_vsids[port['network_id']]
 | 
						|
            try:
 | 
						|
                self._register_lookup_record(
 | 
						|
                    tunneling_ip, customer_addr, mac_addr, segmentation_id)
 | 
						|
 | 
						|
                self._nvgre_ports.append(port['id'])
 | 
						|
            except Exception as ex:
 | 
						|
                LOG.error(_LE("Exception while adding lookup_record: %(ex)s. "
 | 
						|
                              "VSID: %(vsid)s MAC: %(mac_address)s Customer "
 | 
						|
                              "IP:%(cust_addr)s Provider IP: %(prov_addr)s"),
 | 
						|
                          dict(ex=ex,
 | 
						|
                               vsid=segmentation_id,
 | 
						|
                               mac_address=mac_addr,
 | 
						|
                               cust_addr=customer_addr,
 | 
						|
                               prov_addr=tunneling_ip))
 |