Files
deb-networking-hyperv/hyperv/neutron/nvgre_ops.py
Claudiu Belu 18b564a180 Adds Hyper-V NVGRE support
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
2015-08-28 19:20:16 +03:00

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