neutron/neutron/plugins/ml2/drivers/macvtap/agent/macvtap_neutron_agent.py

227 lines
8.5 KiB
Python

# Copyright (c) 2016 IBM Corp.
#
# 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 os
import sys
from neutron_lib import constants
from neutron_lib.utils import helpers
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_service import service
from neutron.agent.linux import ip_lib
from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc
from neutron.common import config as common_config
from neutron.common import topics
from neutron.conf.plugins.ml2.drivers import macvtap as config
from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb
from neutron.plugins.ml2.drivers.agent import _common_agent as ca
from neutron.plugins.ml2.drivers.macvtap import macvtap_common
LOG = logging.getLogger(__name__)
MACVTAP_AGENT_BINARY = "neutron-macvtap-agent"
MACVTAP_FS = "/sys/class/net/"
EXTENSION_DRIVER_TYPE = 'macvtap'
config.register_macvtap_opts()
class MacvtapRPCCallBack(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
amb.CommonAgentManagerRpcCallBackBase):
# Set RPC API version to 1.0 by default.
# history
# 1.1 Support Security Group RPC
# 1.3 Added param devices_to_update to security_groups_provider_updated
# 1.4 Added support for network_update
target = oslo_messaging.Target(version='1.4')
def network_delete(self, context, **kwargs):
LOG.debug("network_delete received")
network_id = kwargs.get('network_id')
if network_id not in self.network_map:
LOG.error("Network %s is not available.", network_id)
return
segment = self.network_map.get(network_id)
if segment and segment.network_type == constants.TYPE_VLAN:
if_mappings = self.agent.mgr.interface_mappings
vlan_device_name = macvtap_common.get_vlan_device_name(
if_mappings[segment.physical_network],
str(segment.segmentation_id))
ip_dev = ip_lib.IPDevice(vlan_device_name)
if ip_dev.exists():
LOG.debug("Delete %s", ip_dev.name)
ip_dev.link.delete()
else:
LOG.debug("Cannot delete vlan device %s; it does not exist",
vlan_device_name)
def port_update(self, context, **kwargs):
port = kwargs['port']
LOG.debug("port_update received for port %s ", port)
mac = port['mac_address']
# Put the device name in the updated_devices set.
# Do not store port details, as if they're used for processing
# notifications there is no guarantee the notifications are
# processed in the same order as the relevant API requests.
self.updated_devices.add(mac)
class MacvtapManager(amb.CommonAgentManagerBase):
def __init__(self, interface_mappings):
self.interface_mappings = interface_mappings
self.validate_interface_mappings()
self.mac_device_name_mappings = dict()
def validate_interface_mappings(self):
for physnet, interface in self.interface_mappings.items():
if not ip_lib.device_exists(interface):
LOG.error("Interface %(intf)s for physical network "
"%(net)s does not exist. Agent terminated!",
{'intf': interface, 'net': physnet})
sys.exit(1)
def ensure_port_admin_state(self, device, admin_state_up):
LOG.debug("Setting admin_state_up to %s for device %s",
admin_state_up, device)
dev = ip_lib.IPDevice(self.mac_device_name_mappings[device])
if admin_state_up:
dev.link.set_up()
else:
dev.link.set_down()
def get_agent_configurations(self):
return {'interface_mappings': self.interface_mappings}
def get_agent_id(self):
devices = ip_lib.IPWrapper().get_devices(True)
if devices:
mac = ip_lib.get_device_mac(devices[0].name)
return 'macvtap%s' % mac.replace(":", "")
else:
LOG.error("Unable to obtain MAC address for unique ID. "
"Agent terminated!")
sys.exit(1)
def get_devices_modified_timestamps(self, devices):
# TODO(kevinbenton): this should be implemented to detect
# rapid Nova instance rebuilds.
return {}
def get_all_devices(self):
devices = set()
all_device_names = os.listdir(MACVTAP_FS)
# Refresh the mac_device_name mapping
self.mac_device_name_mappings = dict()
for device_name in all_device_names:
if device_name.startswith(constants.MACVTAP_DEVICE_PREFIX):
mac = ip_lib.get_device_mac(device_name)
self.mac_device_name_mappings[mac] = device_name
devices.add(mac)
return devices
def get_extension_driver_type(self):
return EXTENSION_DRIVER_TYPE
def get_rpc_callbacks(self, context, agent, sg_agent):
return MacvtapRPCCallBack(context, agent, sg_agent)
def get_agent_api(self, **kwargs):
pass
def get_rpc_consumers(self):
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE],
[topics.SECURITY_GROUP, topics.UPDATE]]
return consumers
def plug_interface(self, network_id, network_segment, device,
device_owner):
# Setting ALLMULTICAST Flag on macvtap device to allow the guest
# receiving traffic for arbitrary multicast addresses.
# The alternative would be to let libvirt instantiate the macvtap
# device with the 'trustGuestRxFilters' option. But doing so, the guest
# would be able to change its mac address and therefore the mac
# address of the macvtap device.
dev = ip_lib.IPDevice(self.mac_device_name_mappings[device])
dev.link.set_allmulticast_on()
return True
def setup_arp_spoofing_protection(self, device, device_details):
pass
def delete_arp_spoofing_protection(self, devices):
pass
def delete_unreferenced_arp_protection(self, current_devices):
pass
def parse_interface_mappings():
if not cfg.CONF.macvtap.physical_interface_mappings:
LOG.error("No physical_interface_mappings provided, but at least "
"one mapping is required. Agent terminated!")
sys.exit(1)
try:
interface_mappings = helpers.parse_mappings(
cfg.CONF.macvtap.physical_interface_mappings)
LOG.info("Interface mappings: %s", interface_mappings)
return interface_mappings
except ValueError as e:
LOG.error("Parsing physical_interface_mappings failed: %s. "
"Agent terminated!", e)
sys.exit(1)
def validate_firewall_driver():
fw_driver = cfg.CONF.SECURITYGROUP.firewall_driver
supported_fw_drivers = ['neutron.agent.firewall.NoopFirewallDriver',
'noop']
if fw_driver not in supported_fw_drivers:
LOG.error('Unsupported configuration option for "SECURITYGROUP.'
'firewall_driver"! Only the NoopFirewallDriver is '
'supported by macvtap agent, but "%s" is configured. '
'Set the firewall_driver to "noop" and start the '
'agent again. Agent terminated!',
fw_driver)
sys.exit(1)
def main():
common_config.init(sys.argv[1:])
common_config.setup_logging()
validate_firewall_driver()
interface_mappings = parse_interface_mappings()
manager = MacvtapManager(interface_mappings)
polling_interval = cfg.CONF.AGENT.polling_interval
quitting_rpc_timeout = cfg.CONF.AGENT.quitting_rpc_timeout
agent = ca.CommonAgentLoop(manager, polling_interval,
quitting_rpc_timeout,
constants.AGENT_TYPE_MACVTAP,
MACVTAP_AGENT_BINARY)
LOG.info("Agent initialized successfully, now running... ")
launcher = service.launch(cfg.CONF, agent)
launcher.wait()