components/openstack/neutron/files/agent/linux/device.py
branchs11-update
changeset 3178 77584387a894
parent 3175 1ff833d174d4
child 3179 07c03b663108
equal deleted inserted replaced
3175:1ff833d174d4 3178:77584387a894
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
       
     2 
       
     3 # Copyright 2012 OpenStack Foundation
       
     4 # All Rights Reserved.
       
     5 #
       
     6 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
       
     7 #    not use this file except in compliance with the License. You may obtain
       
     8 #    a copy of the License at
       
     9 #
       
    10 #         http://www.apache.org/licenses/LICENSE-2.0
       
    11 #
       
    12 #    Unless required by applicable law or agreed to in writing, software
       
    13 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    14 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    15 #    License for the specific language governing permissions and limitations
       
    16 #    under the License.
       
    17 
       
    18 import socket
       
    19 import uuid
       
    20 
       
    21 import netaddr
       
    22 from oslo.config import cfg
       
    23 
       
    24 from quantum.agent.common import config
       
    25 from quantum.agent.linux import interface
       
    26 from quantum.agent.linux import ip_lib
       
    27 from quantum.common import exceptions
       
    28 from quantum.openstack.common import importutils
       
    29 from quantum.openstack.common import log as logging
       
    30 
       
    31 LOG = logging.getLogger(__name__)
       
    32 NS_PREFIX = 'qdhcp-'
       
    33 METADATA_DEFAULT_PREFIX = 16
       
    34 METADATA_DEFAULT_IP = '169.254.169.254/%d' % METADATA_DEFAULT_PREFIX
       
    35 
       
    36 
       
    37 class DeviceManager(object):
       
    38     OPTS = [
       
    39         cfg.StrOpt('interface_driver',
       
    40                    help=_("The driver used to manage the virtual interface."))
       
    41     ]
       
    42 
       
    43     def __init__(self, conf, plugin):
       
    44         self.conf = conf
       
    45         self.root_helper = config.get_root_helper(conf)
       
    46         self.plugin = plugin
       
    47         cfg.CONF.register_opts(DeviceManager.OPTS)
       
    48         cfg.CONF.register_opts(interface.OPTS)
       
    49         if not conf.interface_driver:
       
    50             raise SystemExit(_('You must specify an interface driver'))
       
    51         try:
       
    52             self.driver = importutils.import_object(conf.interface_driver,
       
    53                                                     conf)
       
    54         except:
       
    55             msg = _("Error importing interface driver "
       
    56                     "'%s'") % conf.interface_driver
       
    57             raise SystemExit(msg)
       
    58 
       
    59     def get_interface_name(self, network, port=None):
       
    60         """Return interface(device) name for use by the DHCP process."""
       
    61         if not port:
       
    62             device_id = self.get_device_id(network)
       
    63             port = self.plugin.get_dhcp_port(network.id, device_id)
       
    64         return self.driver.get_device_name(port)
       
    65 
       
    66     def get_device_id(self, network):
       
    67         """Return a unique DHCP device ID for this host on the network."""
       
    68         # There could be more than one dhcp server per network, so create
       
    69         # a device id that combines host and network ids
       
    70 
       
    71         host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, socket.gethostname())
       
    72         return 'dhcp%s-%s' % (host_uuid, network.id)
       
    73 
       
    74     def _get_device(self, network):
       
    75         """Return DHCP ip_lib device for this host on the network."""
       
    76         device_id = self.get_device_id(network)
       
    77         port = self.plugin.get_dhcp_port(network.id, device_id)
       
    78         interface_name = self.get_interface_name(network, port)
       
    79         namespace = NS_PREFIX + network.id
       
    80         return ip_lib.IPDevice(interface_name,
       
    81                                self.root_helper,
       
    82                                namespace)
       
    83 
       
    84     def _set_default_route(self, network):
       
    85         """Sets the default gateway for this dhcp namespace.
       
    86 
       
    87         This method is idempotent and will only adjust the route if adjusting
       
    88         it would change it from what it already is.  This makes it safe to call
       
    89         and avoids unnecessary perturbation of the system.
       
    90         """
       
    91         device = self._get_device(network)
       
    92         gateway = device.route.get_gateway()
       
    93 
       
    94         for subnet in network.subnets:
       
    95             skip_subnet = (
       
    96                 subnet.ip_version != 4
       
    97                 or not subnet.enable_dhcp
       
    98                 or subnet.gateway_ip is None)
       
    99 
       
   100             if skip_subnet:
       
   101                 continue
       
   102 
       
   103             if gateway != subnet.gateway_ip:
       
   104                 m = _('Setting gateway for dhcp netns on net %(n)s to %(ip)s')
       
   105                 LOG.debug(m, {'n': network.id, 'ip': subnet.gateway_ip})
       
   106 
       
   107                 device.route.add_gateway(subnet.gateway_ip)
       
   108 
       
   109             return
       
   110 
       
   111         # No subnets on the network have a valid gateway.  Clean it up to avoid
       
   112         # confusion from seeing an invalid gateway here.
       
   113         if gateway is not None:
       
   114             msg = _('Removing gateway for dhcp netns on net %s')
       
   115             LOG.debug(msg, network.id)
       
   116 
       
   117             device.route.delete_gateway(gateway)
       
   118 
       
   119     def setup(self, network, reuse_existing=False):
       
   120         """Create and initialize a device for network's DHCP on this host."""
       
   121         device_id = self.get_device_id(network)
       
   122         port = self.plugin.get_dhcp_port(network.id, device_id)
       
   123 
       
   124         interface_name = self.get_interface_name(network, port)
       
   125 
       
   126         if self.conf.use_namespaces:
       
   127             namespace = NS_PREFIX + network.id
       
   128         else:
       
   129             namespace = None
       
   130 
       
   131         if ip_lib.device_exists(interface_name,
       
   132                                 self.root_helper,
       
   133                                 namespace):
       
   134             if not reuse_existing:
       
   135                 raise exceptions.PreexistingDeviceFailure(
       
   136                     dev_name=interface_name)
       
   137 
       
   138             LOG.debug(_('Reusing existing device: %s.'), interface_name)
       
   139         else:
       
   140             self.driver.plug(network.id,
       
   141                              port.id,
       
   142                              interface_name,
       
   143                              port.mac_address,
       
   144                              namespace=namespace)
       
   145         ip_cidrs = []
       
   146         for fixed_ip in port.fixed_ips:
       
   147             subnet = fixed_ip.subnet
       
   148             net = netaddr.IPNetwork(subnet.cidr)
       
   149             ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
       
   150             ip_cidrs.append(ip_cidr)
       
   151 
       
   152         if (self.conf.enable_isolated_metadata and
       
   153             self.conf.use_namespaces):
       
   154             ip_cidrs.append(METADATA_DEFAULT_IP)
       
   155 
       
   156         self.driver.init_l3(interface_name, ip_cidrs,
       
   157                             namespace=namespace)
       
   158 
       
   159         # ensure that the dhcp interface is first in the list
       
   160         if namespace is None:
       
   161             device = ip_lib.IPDevice(interface_name,
       
   162                                      self.root_helper)
       
   163             device.route.pullup_route(interface_name)
       
   164 
       
   165         if self.conf.enable_metadata_network:
       
   166             meta_cidr = netaddr.IPNetwork(METADATA_DEFAULT_IP)
       
   167             metadata_subnets = [s for s in network.subnets if
       
   168                                 netaddr.IPNetwork(s.cidr) in meta_cidr]
       
   169             if metadata_subnets:
       
   170                 # Add a gateway so that packets can be routed back to VMs
       
   171                 device = ip_lib.IPDevice(interface_name,
       
   172                                          self.root_helper,
       
   173                                          namespace)
       
   174                 # Only 1 subnet on metadata access network
       
   175                 gateway_ip = metadata_subnets[0].gateway_ip
       
   176                 device.route.add_gateway(gateway_ip)
       
   177         elif self.conf.use_namespaces:
       
   178             self._set_default_route(network)
       
   179 
       
   180         return interface_name
       
   181 
       
   182     def update(self, network):
       
   183         """Update device settings for the network's DHCP on this host."""
       
   184         if self.conf.use_namespaces and not self.conf.enable_metadata_network:
       
   185             self._set_default_route(network)
       
   186 
       
   187     def destroy(self, network, device_name):
       
   188         """Destroy the device used for the network's DHCP on this host."""
       
   189         if self.conf.use_namespaces:
       
   190             namespace = NS_PREFIX + network.id
       
   191         else:
       
   192             namespace = None
       
   193 
       
   194         self.driver.unplug(device_name, namespace=namespace)
       
   195 
       
   196         self.plugin.release_dhcp_port(network.id,
       
   197                                       self.get_device_id(network))