components/openstack/neutron/files/agent/solaris/net_lib.py
author Drew Fisher <drew.fisher@oracle.com>
Wed, 02 Jul 2014 13:34:33 -0700
branchs11-update
changeset 3196 4c06db2d9388
parent 3178 77584387a894
child 2083 87196737f09f
permissions -rw-r--r--
19073547 editing built-in flavors fails with name too long 19056291 A couple of links to zone consoles still exist 19049098 unable to set gateway interface or gateway port for neutron router 19034270 IPv6 filter rules are not properly handled 19031678 The Instances pop-up in Network Topology should remove 'open console' 19031443 nova package needs a require dependency on library/python-2/sqlalchemy 19026639 solaris.css doesn't display network topology pop-ups correctly 19026240 Extract tenantid using the framework provided function 19026100 every tenant should be able to allocate floating IPs for themselves 19022958 Unable to specify solaris device names when attaching volumes to instances 18646500 Unable to attach a volume to a KZ instance using Horizon

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

# Copyright (c) 2014, Oracle and/or its affiliates. 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.
#
# @author: Girish Moodalbail, Oracle, Inc.
#

import netaddr

from neutron.agent.linux import utils


class CommandBase(object):
    @classmethod
    def execute_with_pfexec(cls, cmd, **kwargs):
        # uses pfexec
        cmd.insert(0, '/usr/bin/pfexec')
        return utils.execute(cmd, **kwargs)

    @classmethod
    def execute(cls, cmd, **kwargs):
        return utils.execute(cmd, **kwargs)


class IPInterface(CommandBase):
    '''Wrapper around Solaris ipadm(1m) command.'''

    def __init__(self, ifname):
        self._ifname = ifname

    @classmethod
    def ifname_exists(cls, ifname):

        cmd = ['/usr/sbin/ipadm', 'show-if', '-po', 'ifname']
        stdout = cls.execute(cmd)

        return ifname in stdout

    @classmethod
    def ipaddr_exists(cls, ifname, ipaddr):

        if not cls.ifname_exists(ifname):
            return False

        cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'addr', ifname]
        stdout = cls.execute(cmd)

        return ipaddr in stdout

    def ipaddr_list(self, filters=None):
        cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'type,addr',
               self._ifname]
        stdout = self.execute(cmd)
        atype_addrs = stdout.strip().split('\n')
        result = {}
        for atype_addr in atype_addrs:
            atype, addr = atype_addr.split(':', 1)
            val = result.get(atype)
            if val is None:
                result[atype] = []
                val = result.get(atype)
            # in the case of IPv6 addresses remove any escape '\' character
            val.append(addr.replace("\\", ""))
        return result

    def create_address(self, ipaddr, addrobjname=None, temp=True):
        if not self.ifname_exists(self._ifname):
            # create ip interface
            cmd = ['/usr/sbin/ipadm', 'create-ip', self._ifname]
            if temp:
                cmd.append('-t')
            self.execute_with_pfexec(cmd)

        if self.ipaddr_exists(self._ifname, ipaddr):
            return

        # If an address is IPv6, then to create a static IPv6 address
        # we need to create link-local address first
        if netaddr.IPNetwork(ipaddr).version == 6:
            # check if link-local address already exists
            cmd = ['/usr/sbin/dladm', 'show-linkprop', '-co', 'value',
                   '-p', 'mac-address', self._ifname]
            stdout = self.execute(cmd)
            mac_addr = stdout.splitlines()[0].strip()
            ll_addr = netaddr.EUI(mac_addr).ipv6_link_local()

            if not self.ipaddr_exists(self._ifname, str(ll_addr)):
                # create a link-local address
                cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'static', '-a',
                       str(ll_addr), self._ifname]
                if temp:
                    cmd.append('-t')
                self.execute_with_pfexec(cmd)

        cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'static', '-a',
               ipaddr, self._ifname]
        if temp:
            cmd.append('-t')

        return self.execute_with_pfexec(cmd)

    def delete_address(self, ipaddr):
        if not self.ipaddr_exists(self._ifname, ipaddr):
            return

        cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'addrobj,addr',
               self._ifname]
        stdout = self.execute(cmd)
        aobj_addrs = stdout.strip().split('\n')
        for aobj_addr in aobj_addrs:
            if ipaddr not in aobj_addr:
                continue
            aobj = aobj_addr.split(':')[0]
            cmd = ['/usr/sbin/ipadm', 'delete-addr', aobj]
            self.execute_with_pfexec(cmd)
            break

        isV6 = netaddr.IPNetwork(ipaddr).version == 6
        if len(aobj_addrs) == 1 or (isV6 and len(aobj_addrs) == 2):
            # delete the interface as well
            cmd = ['/usr/sbin/ipadm', 'delete-ip', self._ifname]
            self.execute_with_pfexec(cmd)

    def delete_ip(self):
        if not self.ifname_exists(self._ifname):
            return

        cmd = ['/usr/sbin/ipadm', 'delete-ip', self._ifname]
        self.execute_with_pfexec(cmd)


class Datalink(CommandBase):
    '''Wrapper around Solaris dladm(1m) command.'''

    def __init__(self, dlname):
        self._dlname = dlname

    @classmethod
    def datalink_exists(cls, dlname):

        cmd = ['/usr/sbin/dladm', 'show-link', '-po', 'link']
        stdout = utils.execute(cmd)

        return dlname in stdout

    def connect_vnic(self, evsvport, tenantname=None, temp=True):
        if self.datalink_exists(self._dlname):
            return

        cmd = ['/usr/sbin/dladm', 'create-vnic', '-c', evsvport, self._dlname]
        if temp:
            cmd.append('-t')
        if tenantname:
            cmd.append('-T')
            cmd.append(tenantname)

        return self.execute_with_pfexec(cmd)

    def create_vnic(self, lower_link, mac_address=None, vid=None, temp=True):
        if self.datalink_exists(self._dlname):
            return

        if vid:
            # If the default_tag of lower_link is same as vid, then there
            # is no need to set vid
            cmd = ['/usr/sbin/dladm', 'show-linkprop', '-co', 'value',
                   '-p', 'default_tag', lower_link]
            stdout = utils.execute(cmd)
            if stdout.splitlines()[0].strip() == vid:
                vid = '0'
        else:
            vid = '0'
        cmd = ['/usr/sbin/dladm', 'create-vnic', '-l', lower_link,
               '-m', mac_address, '-v', vid, self._dlname]
        if temp:
            cmd.append('-t')

        return self.execute_with_pfexec(cmd)

    def delete_vnic(self):
        if not self.datalink_exists(self._dlname):
            return

        cmd = ['/usr/sbin/dladm', 'delete-vnic', self._dlname]
        return self.execute_with_pfexec(cmd)


class IPpoolCommand(CommandBase):
    '''Wrapper around Solaris ippool(1m) command'''

    def __init__(self, pool_name, role='ipf', pool_type='tree'):
        self._pool_name = pool_name
        self._role = role
        self._pool_type = pool_type

    def pool_exists(self):
        cmd = ['/usr/sbin/ippool', '-l', '-m', self._pool_name,
               '-t', self._pool_type]
        stdout = self.execute_with_pfexec(cmd)
        return str(self._pool_name) in stdout

    def pool_split_nodes(self, ip_cidrs):
        cmd = ['/usr/sbin/ippool', '-l', '-m', self._pool_name,
               '-t', self._pool_type]
        stdout = self.execute_with_pfexec(cmd)
        existing_nodes = []
        non_existing_nodes = []
        for ip_cidr in ip_cidrs:
            if ip_cidr in stdout:
                existing_nodes.append(ip_cidr)
            else:
                non_existing_nodes.append(ip_cidr)
        return existing_nodes, non_existing_nodes

    def add_pool_nodes(self, ip_cidrs):
        ip_cidrs = self.pool_split_nodes(ip_cidrs)[1]

        for ip_cidr in ip_cidrs:
            cmd = ['/usr/sbin/ippool', '-a', '-m', self._pool_name,
                   '-i', ip_cidr]
            self.execute_with_pfexec(cmd)

    def remove_pool_nodes(self, ip_cidrs):
        ip_cidrs = self.pool_split_nodes(ip_cidrs)[0]

        for ip_cidr in ip_cidrs:
            cmd = ['/usr/sbin/ippool', '-r', '-m', self._pool_name,
                   '-i', ip_cidr]
            self.execute_with_pfexec(cmd)

    def add_pool(self):
        if self.pool_exists():
            return

        cmd = ['/usr/sbin/ippool', '-A', '-m', self._pool_name,
               '-o', self._role, '-t', self._pool_type]
        self.execute_with_pfexec(cmd)

    def remove_pool(self):
        if not self.pool_exists():
            return

        # This command will fail if ippool is in use by ipf, so the
        # caller has to ensure that it's not being used in an ipf rule
        cmd = ['/usr/sbin/ippool', '-R', '-m', self._pool_name,
               '-o', self._role, '-t', self._pool_type]
        self.execute_with_pfexec(cmd)


class IPfilterCommand(CommandBase):
    '''Wrapper around Solaris ipf(1m) command'''

    def _split_rules(self, rules, version):
        # assumes that rules are inbound!
        cmd = ['/usr/sbin/ipfstat', '-i']
        if version == 6:
            cmd.insert(1, '-6')
        stdout = self.execute_with_pfexec(cmd)
        existing_rules = []
        non_existing_rules = []
        for rule in rules:
            if rule in stdout:
                existing_rules.append(rule)
            else:
                non_existing_rules.append(rule)

        return existing_rules, non_existing_rules

    def add_rules(self, rules, version=4):
        rules = self._split_rules(rules, version)[1]
        process_input = '\n'.join(rules)
        cmd = ['/usr/sbin/ipf', '-f', '-']
        if version == 6:
            cmd.insert(1, '-6')
        return self.execute_with_pfexec(cmd, process_input=process_input)

    def remove_rules(self, rules, version=4):
        rules = self._split_rules(rules, version)[0]
        process_input = '\n'.join(rules)
        cmd = ['/usr/sbin/ipf', '-r', '-f', '-']
        if version == 6:
            cmd.insert(1, '-6')
        return self.execute_with_pfexec(cmd, process_input=process_input)


class IPnatCommand(CommandBase):
    '''Wrapper around Solaris ipnat(1m) command'''

    def add_rules(self, rules):
        process_input = '\n'.join(rules)
        cmd = ['/usr/sbin/ipnat', '-f', '-']
        return self.execute_with_pfexec(cmd, process_input=process_input)

    def remove_rules(self, rules):
        process_input = '\n'.join(rules)
        cmd = ['/usr/sbin/ipnat', '-r', '-f', '-']
        return self.execute_with_pfexec(cmd, process_input=process_input)