Fix docs path in common rpm spec file
[apex.git] / build / neutron / agent / interface / interface.py
1 # Copyright 2012 OpenStack Foundation
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 import abc
17 import eventlet
18 import netaddr
19 from oslo_config import cfg
20 from oslo_log import log as logging
21 import six
22
23 from neutron._i18n import _, _LE, _LI, _LW
24 from neutron.agent.common import ovs_lib
25 from neutron.agent.linux import ip_lib
26 from neutron.agent.linux import utils
27 from neutron.common import constants as n_const
28 from neutron.common import exceptions
29 from neutron.common import ipv6_utils
30
31
32 LOG = logging.getLogger(__name__)
33
34 OPTS = [
35     cfg.StrOpt('ovs_integration_bridge',
36                default='br-int',
37                help=_('Name of Open vSwitch bridge to use')),
38     cfg.BoolOpt('ovs_use_veth',
39                 default=False,
40                 help=_('Uses veth for an OVS interface or not. '
41                        'Support kernels with limited namespace support '
42                        '(e.g. RHEL 6.5) so long as ovs_use_veth is set to '
43                        'True.')),
44     cfg.IntOpt('network_device_mtu',
45                deprecated_for_removal=True,
46                help=_('MTU setting for device. This option will be removed in '
47                       'Newton. Please use the system-wide segment_mtu setting '
48                       'which the agents will take into account when wiring '
49                       'VIFs.')),
50 ]
51
52
53 @six.add_metaclass(abc.ABCMeta)
54 class LinuxInterfaceDriver(object):
55
56     # from linux IF_NAMESIZE
57     DEV_NAME_LEN = 14
58     DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
59
60     def __init__(self, conf):
61         self.conf = conf
62         if self.conf.network_device_mtu:
63             self._validate_network_device_mtu()
64
65     def _validate_network_device_mtu(self):
66         if (ipv6_utils.is_enabled() and
67             self.conf.network_device_mtu < n_const.IPV6_MIN_MTU):
68             LOG.error(_LE("IPv6 protocol requires a minimum MTU of "
69                           "%(min_mtu)s, while the configured value is "
70                           "%(current_mtu)s"), {'min_mtu': n_const.IPV6_MIN_MTU,
71                           'current_mtu': self.conf.network_device_mtu})
72             raise SystemExit(1)
73
74     @property
75     def use_gateway_ips(self):
76         """Whether to use gateway IPs instead of unique IP allocations.
77
78         In each place where the DHCP agent runs, and for each subnet for
79         which DHCP is handling out IP addresses, the DHCP port needs -
80         at the Linux level - to have an IP address within that subnet.
81         Generally this needs to be a unique Neutron-allocated IP
82         address, because the subnet's underlying L2 domain is bridged
83         across multiple compute hosts and network nodes, and for HA
84         there may be multiple DHCP agents running on that same bridged
85         L2 domain.
86
87         However, if the DHCP ports - on multiple compute/network nodes
88         but for the same network - are _not_ bridged to each other,
89         they do not need each to have a unique IP address.  Instead
90         they can all share the same address from the relevant subnet.
91         This works, without creating any ambiguity, because those
92         ports are not all present on the same L2 domain, and because
93         no data within the network is ever sent to that address.
94         (DHCP requests are broadcast, and it is the network's job to
95         ensure that such a broadcast will reach at least one of the
96         available DHCP servers.  DHCP responses will be sent _from_
97         the DHCP port address.)
98
99         Specifically, for networking backends where it makes sense,
100         the DHCP agent allows all DHCP ports to use the subnet's
101         gateway IP address, and thereby to completely avoid any unique
102         IP address allocation.  This behaviour is selected by running
103         the DHCP agent with a configured interface driver whose
104         'use_gateway_ips' property is True.
105
106         When an operator deploys Neutron with an interface driver that
107         makes use_gateway_ips True, they should also ensure that a
108         gateway IP address is defined for each DHCP-enabled subnet,
109         and that the gateway IP address doesn't change during the
110         subnet's lifetime.
111         """
112         return False
113
114     def init_l3(self, device_name, ip_cidrs, namespace=None,
115                 preserve_ips=None, clean_connections=False):
116         """Set the L3 settings for the interface using data from the port.
117
118         ip_cidrs: list of 'X.X.X.X/YY' strings
119         preserve_ips: list of ip cidrs that should not be removed from device
120         clean_connections: Boolean to indicate if we should cleanup connections
121           associated to removed ips
122         """
123         preserve_ips = preserve_ips or []
124         device = ip_lib.IPDevice(device_name, namespace=namespace)
125
126         # The LLA generated by the operating system is not known to
127         # Neutron, so it would be deleted if we added it to the 'previous'
128         # list here
129         default_ipv6_lla = ip_lib.get_ipv6_lladdr(device.link.address)
130         previous = {addr['cidr'] for addr in device.addr.list(
131             filters=['permanent'])} - {default_ipv6_lla}
132
133         # add new addresses
134         for ip_cidr in ip_cidrs:
135
136             net = netaddr.IPNetwork(ip_cidr)
137             # Convert to compact IPv6 address because the return values of
138             # "ip addr list" are compact.
139             if net.version == 6:
140                 ip_cidr = str(net)
141             if ip_cidr in previous:
142                 previous.remove(ip_cidr)
143                 continue
144
145             device.addr.add(ip_cidr)
146
147         # clean up any old addresses
148         for ip_cidr in previous:
149             if ip_cidr not in preserve_ips:
150                 if clean_connections:
151                     device.delete_addr_and_conntrack_state(ip_cidr)
152                 else:
153                     device.addr.delete(ip_cidr)
154
155     def init_router_port(self,
156                          device_name,
157                          ip_cidrs,
158                          namespace,
159                          preserve_ips=None,
160                          extra_subnets=None,
161                          clean_connections=False):
162         """Set the L3 settings for a router interface using data from the port.
163
164         ip_cidrs: list of 'X.X.X.X/YY' strings
165         preserve_ips: list of ip cidrs that should not be removed from device
166         clean_connections: Boolean to indicate if we should cleanup connections
167           associated to removed ips
168         extra_subnets: An iterable of cidrs to add as routes without address
169         """
170         LOG.debug("init_router_port: device_name(%s), namespace(%s)",
171                   device_name, namespace)
172         self.init_l3(device_name=device_name,
173                      ip_cidrs=ip_cidrs,
174                      namespace=namespace,
175                      preserve_ips=preserve_ips or [],
176                      clean_connections=clean_connections)
177
178         device = ip_lib.IPDevice(device_name, namespace=namespace)
179
180         # Manage on-link routes (routes without an associated address)
181         new_onlink_cidrs = set(s['cidr'] for s in extra_subnets or [])
182
183         v4_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_4)
184         v6_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_6)
185         existing_onlink_cidrs = set(r['cidr'] for r in v4_onlink + v6_onlink)
186
187         for route in new_onlink_cidrs - existing_onlink_cidrs:
188             LOG.debug("adding onlink route(%s)", route)
189             device.route.add_onlink_route(route)
190         for route in (existing_onlink_cidrs - new_onlink_cidrs -
191                       set(preserve_ips or [])):
192             LOG.debug("deleting onlink route(%s)", route)
193             device.route.delete_onlink_route(route)
194
195     def add_ipv6_addr(self, device_name, v6addr, namespace, scope='global'):
196         device = ip_lib.IPDevice(device_name,
197                                  namespace=namespace)
198         net = netaddr.IPNetwork(v6addr)
199         device.addr.add(str(net), scope)
200
201     def delete_ipv6_addr(self, device_name, v6addr, namespace):
202         device = ip_lib.IPDevice(device_name,
203                                  namespace=namespace)
204         device.delete_addr_and_conntrack_state(v6addr)
205
206     def delete_ipv6_addr_with_prefix(self, device_name, prefix, namespace):
207         """Delete the first listed IPv6 address that falls within a given
208         prefix.
209         """
210         device = ip_lib.IPDevice(device_name, namespace=namespace)
211         net = netaddr.IPNetwork(prefix)
212         for address in device.addr.list(scope='global', filters=['permanent']):
213             ip_address = netaddr.IPNetwork(address['cidr'])
214             if ip_address in net:
215                 device.delete_addr_and_conntrack_state(address['cidr'])
216                 break
217
218     def get_ipv6_llas(self, device_name, namespace):
219         device = ip_lib.IPDevice(device_name,
220                                  namespace=namespace)
221
222         return device.addr.list(scope='link', ip_version=6)
223
224     def check_bridge_exists(self, bridge):
225         if not ip_lib.device_exists(bridge):
226             raise exceptions.BridgeDoesNotExist(bridge=bridge)
227
228     def get_device_name(self, port):
229         return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
230
231     @staticmethod
232     def configure_ipv6_ra(namespace, dev_name):
233         """Configure acceptance of IPv6 route advertisements on an intf."""
234         # Learn the default router's IP address via RAs
235         ip_lib.IPWrapper(namespace=namespace).netns.execute(
236             ['sysctl', '-w', 'net.ipv6.conf.%s.accept_ra=2' % dev_name])
237
238     @abc.abstractmethod
239     def plug_new(self, network_id, port_id, device_name, mac_address,
240                  bridge=None, namespace=None, prefix=None, mtu=None):
241         """Plug in the interface only for new devices that don't exist yet."""
242
243     def plug(self, network_id, port_id, device_name, mac_address,
244              bridge=None, namespace=None, prefix=None, mtu=None):
245         if not ip_lib.device_exists(device_name,
246                                     namespace=namespace):
247             try:
248                 self.plug_new(network_id, port_id, device_name, mac_address,
249                               bridge, namespace, prefix, mtu)
250             except TypeError:
251                 self.plug_new(network_id, port_id, device_name, mac_address,
252                               bridge, namespace, prefix)
253         else:
254             LOG.info(_LI("Device %s already exists"), device_name)
255
256     @abc.abstractmethod
257     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
258         """Unplug the interface."""
259
260     @property
261     def bridged(self):
262         """Whether the DHCP port is bridged to the VM TAP interfaces.
263
264         When the DHCP port is bridged to the TAP interfaces for the
265         VMs for which it is providing DHCP service - as is the case
266         for most Neutron network implementations - the DHCP server
267         only needs to listen on the DHCP port, and will still receive
268         DHCP requests from all the relevant VMs.
269
270         If the DHCP port is not bridged to the relevant VM TAP
271         interfaces, the DHCP server needs to listen explicitly on
272         those TAP interfaces, and to treat those as aliases of the
273         DHCP port where the IP subnet is defined.
274         """
275         return True
276
277
278 class NullDriver(LinuxInterfaceDriver):
279     def plug_new(self, network_id, port_id, device_name, mac_address,
280                  bridge=None, namespace=None, prefix=None, mtu=None):
281         pass
282
283     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
284         pass
285
286 class NSDriver(LinuxInterfaceDriver):
287     """Device independent driver enabling creation of a non device specific
288     interface in network spaces.  Attachment to the device is not performed.
289     """
290     MAX_TIME_FOR_DEVICE_EXISTENCE = 30
291
292     @classmethod
293     def _device_is_created_in_time(cls, device_name):
294         """See if device is created, within time limit."""
295         attempt = 0
296         while attempt < NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE:
297             if ip_lib.device_exists(device_name):
298                 return True
299             attempt += 1
300             eventlet.sleep(1)
301         LOG.error(_LE("Device %(dev)s was not created in %(time)d seconds"),
302                   {'dev': device_name,
303                    'time': NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE})
304         return False
305
306     def _configure_mtu(self, ns_dev, mtu=None):
307         # Need to set MTU, after added to namespace. See review
308         # https://review.openstack.org/327651
309         try:
310             # Note: network_device_mtu will be deprecated in future
311             mtu_override = self.conf.network_device_mtu
312         except cfg.NoSuchOptError:
313             LOG.warning(_LW("Config setting for MTU deprecated - any "
314                             "override will be ignored."))
315             mtu_override = None
316         if mtu_override:
317             mtu = mtu_override
318             LOG.debug("Overriding MTU to %d", mtu)
319         if mtu:
320             ns_dev.link.set_mtu(mtu)
321         else:
322             LOG.debug("No MTU provided - skipping setting value")
323
324     def plug(self, network_id, port_id, device_name, mac_address,
325              bridge=None, namespace=None, prefix=None, mtu=None):
326
327         # Overriding this, we still want to add an existing device into the
328         # namespace.
329         self.plug_new(network_id, port_id, device_name, mac_address,
330                       bridge, namespace, prefix, mtu)
331
332     def plug_new(self, network_id, port_id, device_name, mac_address,
333                  bridge=None, namespace=None, prefix=None, mtu=None):
334
335         ip = ip_lib.IPWrapper()
336         ns_dev = ip.device(device_name)
337
338         LOG.debug("Plugging dev: '%s' into namespace: '%s' ",
339                   device_name, namespace)
340
341         # Wait for device creation
342         if not self._device_is_created_in_time(device_name):
343             return
344
345         ns_dev.link.set_address(mac_address)
346
347         if namespace:
348             namespace_obj = ip.ensure_namespace(namespace)
349             namespace_obj.add_device_to_namespace(ns_dev)
350
351         self._configure_mtu(ns_dev, mtu)
352
353         ns_dev.link.set_up()
354
355     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
356         # Device removal is done externally. Just remove the namespace
357         LOG.debug("Removing namespace: '%s'", namespace)
358         ip_lib.IPWrapper(namespace).garbage_collect_namespace()
359
360
361 class OVSInterfaceDriver(LinuxInterfaceDriver):
362     """Driver for creating an internal interface on an OVS bridge."""
363
364     DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
365
366     def __init__(self, conf):
367         super(OVSInterfaceDriver, self).__init__(conf)
368         if self.conf.ovs_use_veth:
369             self.DEV_NAME_PREFIX = 'ns-'
370
371     def _get_tap_name(self, dev_name, prefix=None):
372         if self.conf.ovs_use_veth:
373             dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
374                                         n_const.TAP_DEVICE_PREFIX)
375         return dev_name
376
377     def _ovs_add_port(self, bridge, device_name, port_id, mac_address,
378                       internal=True):
379         attrs = [('external_ids', {'iface-id': port_id,
380                                    'iface-status': 'active',
381                                    'attached-mac': mac_address})]
382         if internal:
383             attrs.insert(0, ('type', 'internal'))
384
385         ovs = ovs_lib.OVSBridge(bridge)
386         ovs.replace_port(device_name, *attrs)
387
388     def plug_new(self, network_id, port_id, device_name, mac_address,
389                  bridge=None, namespace=None, prefix=None, mtu=None):
390         """Plug in the interface."""
391         if not bridge:
392             bridge = self.conf.ovs_integration_bridge
393
394         self.check_bridge_exists(bridge)
395
396         ip = ip_lib.IPWrapper()
397         tap_name = self._get_tap_name(device_name, prefix)
398
399         if self.conf.ovs_use_veth:
400             # Create ns_dev in a namespace if one is configured.
401             root_dev, ns_dev = ip.add_veth(tap_name,
402                                            device_name,
403                                            namespace2=namespace)
404             root_dev.disable_ipv6()
405         else:
406             ns_dev = ip.device(device_name)
407
408         internal = not self.conf.ovs_use_veth
409         self._ovs_add_port(bridge, tap_name, port_id, mac_address,
410                            internal=internal)
411
412         ns_dev.link.set_address(mac_address)
413
414         # Add an interface created by ovs to the namespace.
415         if not self.conf.ovs_use_veth and namespace:
416             namespace_obj = ip.ensure_namespace(namespace)
417             namespace_obj.add_device_to_namespace(ns_dev)
418
419         # NOTE(ihrachys): the order here is significant: we must set MTU after
420         # the device is moved into a namespace, otherwise OVS bridge does not
421         # allow to set MTU that is higher than the least of all device MTUs on
422         # the bridge
423         mtu = self.conf.network_device_mtu or mtu
424         if mtu:
425             ns_dev.link.set_mtu(mtu)
426             if self.conf.ovs_use_veth:
427                 root_dev.link.set_mtu(mtu)
428         else:
429             LOG.warning(_LW("No MTU configured for port %s"), port_id)
430
431         ns_dev.link.set_up()
432         if self.conf.ovs_use_veth:
433             root_dev.link.set_up()
434
435     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
436         """Unplug the interface."""
437         if not bridge:
438             bridge = self.conf.ovs_integration_bridge
439
440         tap_name = self._get_tap_name(device_name, prefix)
441         self.check_bridge_exists(bridge)
442         ovs = ovs_lib.OVSBridge(bridge)
443
444         try:
445             ovs.delete_port(tap_name)
446             if self.conf.ovs_use_veth:
447                 device = ip_lib.IPDevice(device_name, namespace=namespace)
448                 device.link.delete()
449                 LOG.debug("Unplugged interface '%s'", device_name)
450         except RuntimeError:
451             LOG.error(_LE("Failed unplugging interface '%s'"),
452                       device_name)
453
454
455 class IVSInterfaceDriver(LinuxInterfaceDriver):
456     """Driver for creating an internal interface on an IVS bridge."""
457
458     DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
459
460     def __init__(self, conf):
461         super(IVSInterfaceDriver, self).__init__(conf)
462         self.DEV_NAME_PREFIX = 'ns-'
463
464     def _get_tap_name(self, dev_name, prefix=None):
465         dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
466                                     n_const.TAP_DEVICE_PREFIX)
467         return dev_name
468
469     def _ivs_add_port(self, device_name, port_id, mac_address):
470         cmd = ['ivs-ctl', 'add-port', device_name]
471         utils.execute(cmd, run_as_root=True)
472
473     def plug_new(self, network_id, port_id, device_name, mac_address,
474                  bridge=None, namespace=None, prefix=None, mtu=None):
475         """Plug in the interface."""
476         ip = ip_lib.IPWrapper()
477         tap_name = self._get_tap_name(device_name, prefix)
478
479         root_dev, ns_dev = ip.add_veth(tap_name, device_name)
480         root_dev.disable_ipv6()
481
482         self._ivs_add_port(tap_name, port_id, mac_address)
483
484         ns_dev = ip.device(device_name)
485         ns_dev.link.set_address(mac_address)
486
487         mtu = self.conf.network_device_mtu or mtu
488         if mtu:
489             ns_dev.link.set_mtu(mtu)
490             root_dev.link.set_mtu(mtu)
491         else:
492             LOG.warning(_LW("No MTU configured for port %s"), port_id)
493
494         if namespace:
495             namespace_obj = ip.ensure_namespace(namespace)
496             namespace_obj.add_device_to_namespace(ns_dev)
497
498         ns_dev.link.set_up()
499         root_dev.link.set_up()
500
501     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
502         """Unplug the interface."""
503         tap_name = self._get_tap_name(device_name, prefix)
504         try:
505             cmd = ['ivs-ctl', 'del-port', tap_name]
506             utils.execute(cmd, run_as_root=True)
507             device = ip_lib.IPDevice(device_name, namespace=namespace)
508             device.link.delete()
509             LOG.debug("Unplugged interface '%s'", device_name)
510         except RuntimeError:
511             LOG.error(_LE("Failed unplugging interface '%s'"),
512                       device_name)
513
514
515 class BridgeInterfaceDriver(LinuxInterfaceDriver):
516     """Driver for creating bridge interfaces."""
517
518     DEV_NAME_PREFIX = 'ns-'
519
520     def plug_new(self, network_id, port_id, device_name, mac_address,
521                  bridge=None, namespace=None, prefix=None, mtu=None):
522         """Plugin the interface."""
523         ip = ip_lib.IPWrapper()
524
525         # Enable agent to define the prefix
526         tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX,
527                                        n_const.TAP_DEVICE_PREFIX)
528         # Create ns_veth in a namespace if one is configured.
529         root_veth, ns_veth = ip.add_veth(tap_name, device_name,
530                                          namespace2=namespace)
531         root_veth.disable_ipv6()
532         ns_veth.link.set_address(mac_address)
533
534         mtu = self.conf.network_device_mtu or mtu
535         if mtu:
536             root_veth.link.set_mtu(mtu)
537             ns_veth.link.set_mtu(mtu)
538         else:
539             LOG.warning(_LW("No MTU configured for port %s"), port_id)
540
541         root_veth.link.set_up()
542         ns_veth.link.set_up()
543
544     def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
545         """Unplug the interface."""
546         device = ip_lib.IPDevice(device_name, namespace=namespace)
547         try:
548             device.link.delete()
549             LOG.debug("Unplugged interface '%s'", device_name)
550         except RuntimeError:
551             LOG.error(_LE("Failed unplugging interface '%s'"),
552                       device_name)