¡@

Home 

OpenStack Study: ip_lib.py

OpenStack Index

**** CubicPower OpenStack Study ****

# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2012 OpenStack Foundation

# 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 netaddr

from oslo.config import cfg

from neutron.agent.linux import utils

from neutron.common import exceptions

OPTS = [

cfg.BoolOpt('ip_lib_force_root',

default=False,

help=_('Force ip_lib calls to use the root helper')),

]

LOOPBACK_DEVNAME = 'lo'

# NOTE(ethuleau): depend of the version of iproute2, the vlan

# interface details vary.

VLAN_INTERFACE_DETAIL = ['vlan protocol 802.1q',

'vlan protocol 802.1Q',

'vlan id']

**** CubicPower OpenStack Study ****

class SubProcessBase(object):

**** CubicPower OpenStack Study ****

    def __init__(self, root_helper=None, namespace=None):

        self.root_helper = root_helper

        self.namespace = namespace

        try:

            self.force_root = cfg.CONF.ip_lib_force_root

        except cfg.NoSuchOptError:

            # Only callers that need to force use of the root helper

            # need to register the option.

            self.force_root = False

**** CubicPower OpenStack Study ****

    def _run(self, options, command, args):

        if self.namespace:

            return self._as_root(options, command, args)

        elif self.force_root:

            # Force use of the root helper to ensure that commands

            # will execute in dom0 when running under XenServer/XCP.

            return self._execute(options, command, args, self.root_helper)

        else:

            return self._execute(options, command, args)

**** CubicPower OpenStack Study ****

    def _as_root(self, options, command, args, use_root_namespace=False):

        if not self.root_helper:

            raise exceptions.SudoRequired()

        namespace = self.namespace if not use_root_namespace else None

        return self._execute(options,

                             command,

                             args,

                             self.root_helper,

                             namespace)

    @classmethod

**** CubicPower OpenStack Study ****

    def _execute(cls, options, command, args, root_helper=None,

                 namespace=None):

        opt_list = ['-%s' % o for o in options]

        if namespace:

            ip_cmd = ['ip', 'netns', 'exec', namespace, 'ip']

        else:

            ip_cmd = ['ip']

        return utils.execute(ip_cmd + opt_list + [command] + list(args),

                             root_helper=root_helper)

**** CubicPower OpenStack Study ****

class IPWrapper(SubProcessBase):

**** CubicPower OpenStack Study ****

    def __init__(self, root_helper=None, namespace=None):

        super(IPWrapper, self).__init__(root_helper=root_helper,

                                        namespace=namespace)

        self.netns = IpNetnsCommand(self)

**** CubicPower OpenStack Study ****

    def device(self, name):

        return IPDevice(name, self.root_helper, self.namespace)

**** CubicPower OpenStack Study ****

    def get_devices(self, exclude_loopback=False):

        retval = []

        output = self._execute(['o', 'd'], 'link', ('list',),

                               self.root_helper, self.namespace)

        for line in output.split('\n'):

            if '<' not in line:

                continue

            tokens = line.split(' ', 2)

            if len(tokens) == 3:

                if any(v in tokens[2] for v in VLAN_INTERFACE_DETAIL):

                    delimiter = '@'

                else:

                    delimiter = ':'

                name = tokens[1].rpartition(delimiter)[0].strip()

                if exclude_loopback and name == LOOPBACK_DEVNAME:

                    continue

                retval.append(IPDevice(name,

                                       self.root_helper,

                                       self.namespace))

        return retval

**** CubicPower OpenStack Study ****

    def add_tuntap(self, name, mode='tap'):

        self._as_root('', 'tuntap', ('add', name, 'mode', mode))

        return IPDevice(name, self.root_helper, self.namespace)

**** CubicPower OpenStack Study ****

    def add_veth(self, name1, name2, namespace2=None):

        args = ['add', name1, 'type', 'veth', 'peer', 'name', name2]

        if namespace2 is None:

            namespace2 = self.namespace

        else:

            self.ensure_namespace(namespace2)

            args += ['netns', namespace2]

        self._as_root('', 'link', tuple(args))

        return (IPDevice(name1, self.root_helper, self.namespace),

                IPDevice(name2, self.root_helper, namespace2))

**** CubicPower OpenStack Study ****

    def ensure_namespace(self, name):

        if not self.netns.exists(name):

            ip = self.netns.add(name)

            lo = ip.device(LOOPBACK_DEVNAME)

            lo.link.set_up()

        else:

            ip = IPWrapper(self.root_helper, name)

        return ip

**** CubicPower OpenStack Study ****

    def namespace_is_empty(self):

        return not self.get_devices(exclude_loopback=True)

**** CubicPower OpenStack Study ****

    def garbage_collect_namespace(self):

        """Conditionally destroy the namespace if it is empty."""

        if self.namespace and self.netns.exists(self.namespace):

            if self.namespace_is_empty():

                self.netns.delete(self.namespace)

                return True

        return False

**** CubicPower OpenStack Study ****

    def add_device_to_namespace(self, device):

        if self.namespace:

            device.link.set_netns(self.namespace)

**** CubicPower OpenStack Study ****

    def add_vxlan(self, name, vni, group=None, dev=None, ttl=None, tos=None,

                  local=None, port=None, proxy=False):

        cmd = ['add', name, 'type', 'vxlan', 'id', vni]

        if group:

                cmd.extend(['group', group])

        if dev:

                cmd.extend(['dev', dev])

        if ttl:

                cmd.extend(['ttl', ttl])

        if tos:

                cmd.extend(['tos', tos])

        if local:

                cmd.extend(['local', local])

        if proxy:

                cmd.append('proxy')

        # tuple: min,max

        if port and len(port) == 2:

                cmd.extend(['port', port[0], port[1]])

        elif port:

            raise exceptions.NetworkVxlanPortRangeError(vxlan_range=port)

        self._as_root('', 'link', cmd)

        return (IPDevice(name, self.root_helper, self.namespace))

    @classmethod

**** CubicPower OpenStack Study ****

    def get_namespaces(cls, root_helper):

        output = cls._execute('', 'netns', ('list',), root_helper=root_helper)

        return [l.strip() for l in output.split('\n')]

**** CubicPower OpenStack Study ****

class IPDevice(SubProcessBase):

**** CubicPower OpenStack Study ****

    def __init__(self, name, root_helper=None, namespace=None):

        super(IPDevice, self).__init__(root_helper=root_helper,

                                       namespace=namespace)

        self.name = name

        self.link = IpLinkCommand(self)

        self.addr = IpAddrCommand(self)

        self.route = IpRouteCommand(self)

**** CubicPower OpenStack Study ****

    def __eq__(self, other):

        return (other is not None and self.name == other.name

                and self.namespace == other.namespace)

**** CubicPower OpenStack Study ****

    def __str__(self):

        return self.name

**** CubicPower OpenStack Study ****

class IpCommandBase(object):

COMMAND = ''

**** CubicPower OpenStack Study ****

    def __init__(self, parent):

        self._parent = parent

**** CubicPower OpenStack Study ****

    def _run(self, *args, **kwargs):

        return self._parent._run(kwargs.get('options', []), self.COMMAND, args)

**** CubicPower OpenStack Study ****

    def _as_root(self, *args, **kwargs):

        return self._parent._as_root(kwargs.get('options', []),

                                     self.COMMAND,

                                     args,

                                     kwargs.get('use_root_namespace', False))

**** CubicPower OpenStack Study ****

class IpDeviceCommandBase(IpCommandBase):

@property

**** CubicPower OpenStack Study ****

    def name(self):

        return self._parent.name

**** CubicPower OpenStack Study ****

class IpLinkCommand(IpDeviceCommandBase):

COMMAND = 'link'

**** CubicPower OpenStack Study ****

    def set_address(self, mac_address):

        self._as_root('set', self.name, 'address', mac_address)

**** CubicPower OpenStack Study ****

    def set_mtu(self, mtu_size):

        self._as_root('set', self.name, 'mtu', mtu_size)

**** CubicPower OpenStack Study ****

    def set_up(self):

        self._as_root('set', self.name, 'up')

**** CubicPower OpenStack Study ****

    def set_down(self):

        self._as_root('set', self.name, 'down')

**** CubicPower OpenStack Study ****

    def set_netns(self, namespace):

        self._as_root('set', self.name, 'netns', namespace)

        self._parent.namespace = namespace

**** CubicPower OpenStack Study ****

    def set_name(self, name):

        self._as_root('set', self.name, 'name', name)

        self._parent.name = name

**** CubicPower OpenStack Study ****

    def set_alias(self, alias_name):

        self._as_root('set', self.name, 'alias', alias_name)

**** CubicPower OpenStack Study ****

    def delete(self):

        self._as_root('delete', self.name)

    @property

**** CubicPower OpenStack Study ****

    def address(self):

        return self.attributes.get('link/ether')

    @property

**** CubicPower OpenStack Study ****

    def state(self):

        return self.attributes.get('state')

    @property

**** CubicPower OpenStack Study ****

    def mtu(self):

        return self.attributes.get('mtu')

    @property

**** CubicPower OpenStack Study ****

    def qdisc(self):

        return self.attributes.get('qdisc')

    @property

**** CubicPower OpenStack Study ****

    def qlen(self):

        return self.attributes.get('qlen')

    @property

**** CubicPower OpenStack Study ****

    def alias(self):

        return self.attributes.get('alias')

    @property

**** CubicPower OpenStack Study ****

    def attributes(self):

        return self._parse_line(self._run('show', self.name, options='o'))

**** CubicPower OpenStack Study ****

    def _parse_line(self, value):

        if not value:

            return {}

        device_name, settings = value.replace("\\", '').split('>', 1)

        tokens = settings.split()

        keys = tokens[::2]

        values = [int(v) if v.isdigit() else v for v in tokens[1::2]]

        retval = dict(zip(keys, values))

        return retval

**** CubicPower OpenStack Study ****

class IpAddrCommand(IpDeviceCommandBase):

COMMAND = 'addr'

**** CubicPower OpenStack Study ****

    def add(self, ip_version, cidr, broadcast, scope='global'):

        self._as_root('add',

                      cidr,

                      'brd',

                      broadcast,

                      'scope',

                      scope,

                      'dev',

                      self.name,

                      options=[ip_version])

**** CubicPower OpenStack Study ****

    def delete(self, ip_version, cidr):

        self._as_root('del',

                      cidr,

                      'dev',

                      self.name,

                      options=[ip_version])

**** CubicPower OpenStack Study ****

    def flush(self):

        self._as_root('flush', self.name)

**** CubicPower OpenStack Study ****

    def list(self, scope=None, to=None, filters=None):

        if filters is None:

            filters = []

        retval = []

        if scope:

            filters += ['scope', scope]

        if to:

            filters += ['to', to]

        for line in self._run('show', self.name, *filters).split('\n'):

            line = line.strip()

            if not line.startswith('inet'):

                continue

            parts = line.split()

            if parts[0] == 'inet6':

                version = 6

                scope = parts[3]

                broadcast = '::'

            else:

                version = 4

                if parts[2] == 'brd':

                    broadcast = parts[3]

                    scope = parts[5]

                else:

                    # sometimes output of 'ip a' might look like:

                    # inet 192.168.100.100/24 scope global eth0

                    # and broadcast needs to be calculated from CIDR

                    broadcast = str(netaddr.IPNetwork(parts[1]).broadcast)

                    scope = parts[3]

            retval.append(dict(cidr=parts[1],

                               broadcast=broadcast,

                               scope=scope,

                               ip_version=version,

                               dynamic=('dynamic' == parts[-1])))

        return retval

**** CubicPower OpenStack Study ****

class IpRouteCommand(IpDeviceCommandBase):

COMMAND = 'route'

**** CubicPower OpenStack Study ****

    def add_gateway(self, gateway, metric=None):

        args = ['replace', 'default', 'via', gateway]

        if metric:

            args += ['metric', metric]

        args += ['dev', self.name]

        self._as_root(*args)

**** CubicPower OpenStack Study ****

    def delete_gateway(self, gateway):

        self._as_root('del',

                      'default',

                      'via',

                      gateway,

                      'dev',

                      self.name)

**** CubicPower OpenStack Study ****

    def get_gateway(self, scope=None, filters=None):

        if filters is None:

            filters = []

        retval = None

        if scope:

            filters += ['scope', scope]

        route_list_lines = self._run('list', 'dev', self.name,

                                     *filters).split('\n')

        default_route_line = next((x.strip() for x in

                                   route_list_lines if

                                   x.strip().startswith('default')), None)

        if default_route_line:

            gateway_index = 2

            parts = default_route_line.split()

            retval = dict(gateway=parts[gateway_index])

            if 'metric' in parts:

                metric_index = parts.index('metric') + 1

                retval.update(metric=int(parts[metric_index]))

        return retval

**** CubicPower OpenStack Study ****

    def pullup_route(self, interface_name):

        """Ensures that the route entry for the interface is before all

        others on the same subnet.

        """

        device_list = []

        device_route_list_lines = self._run('list', 'proto', 'kernel',

                                            'dev', interface_name).split('\n')

        for device_route_line in device_route_list_lines:

            try:

                subnet = device_route_line.split()[0]

            except Exception:

                continue

            subnet_route_list_lines = self._run('list', 'proto', 'kernel',

                                                'match', subnet).split('\n')

            for subnet_route_line in subnet_route_list_lines:

                i = iter(subnet_route_line.split())

                while(i.next() != 'dev'):

                    pass

                device = i.next()

                try:

                    while(i.next() != 'src'):

                        pass

                    src = i.next()

                except Exception:

                    src = ''

                if device != interface_name:

                    device_list.append((device, src))

                else:

                    break

            for (device, src) in device_list:

                self._as_root('del', subnet, 'dev', device)

                if (src != ''):

                    self._as_root('append', subnet, 'proto', 'kernel',

                                  'src', src, 'dev', device)

                else:

                    self._as_root('append', subnet, 'proto', 'kernel',

                                  'dev', device)

**** CubicPower OpenStack Study ****

class IpNetnsCommand(IpCommandBase):

COMMAND = 'netns'

**** CubicPower OpenStack Study ****

    def add(self, name):

        self._as_root('add', name, use_root_namespace=True)

        return IPWrapper(self._parent.root_helper, name)

**** CubicPower OpenStack Study ****

    def delete(self, name):

        self._as_root('delete', name, use_root_namespace=True)

**** CubicPower OpenStack Study ****

    def execute(self, cmds, addl_env={}, check_exit_code=True):

        if not self._parent.root_helper:

            raise exceptions.SudoRequired()

        ns_params = []

        if self._parent.namespace:

            ns_params = ['ip', 'netns', 'exec', self._parent.namespace]

        env_params = []

        if addl_env:

            env_params = (['env'] +

                          ['%s=%s' % pair for pair in addl_env.items()])

        return utils.execute(

            ns_params + env_params + list(cmds),

            root_helper=self._parent.root_helper,

            check_exit_code=check_exit_code)

**** CubicPower OpenStack Study ****

    def exists(self, name):

        output = self._parent._execute('o', 'netns', ['list'])

        for line in output.split('\n'):

            if name == line.strip():

                return True

        return False

def device_exists(device_name, root_helper=None, namespace=None):

    try:

        address = IPDevice(device_name, root_helper, namespace).link.address

    except RuntimeError:

        return False

    return bool(address)

def iproute_arg_supported(command, arg, root_helper=None):

    command += ['help']

    stdout, stderr = utils.execute(command, root_helper=root_helper,

                                   check_exit_code=False, return_stderr=True)

    return any(arg in line for line in stderr.split('\n'))

**** CubicPower OpenStack Study ****

def device_exists(device_name, root_helper=None, namespace=None):

    try:

        address = IPDevice(device_name, root_helper, namespace).link.address

    except RuntimeError:

        return False

    return bool(address)

**** CubicPower OpenStack Study ****

def iproute_arg_supported(command, arg, root_helper=None):

    command += ['help']

    stdout, stderr = utils.execute(command, root_helper=root_helper,

                                   check_exit_code=False, return_stderr=True)

    return any(arg in line for line in stderr.split('\n'))