components/openstack/neutron/files/agent/solaris/interface.py
author Girish Moodalbail <Girish.Moodalbail@oracle.COM>
Fri, 16 Oct 2015 15:53:02 -0700
changeset 4975 6445e44cfccd
parent 4389 a44bb9a2917e
child 5405 66fd59fecd68
permissions -rw-r--r--
21978756 addrconf addresses must be created for stateless and slaac Neutron subnets 21978743 ndpd.conf entries are incorrectly formatted for IPv6 subnets 21919000 neutron-dhcp-agent and neutron-server have timing issues 21918991 database times out when attempting various actions 21682493 Neutron fails due to mysql transaction locks when creating multiple instances 22024767 Remove annoying "Arguments dropped when creating context" logging

# Copyright (c) 2013, 2015, 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 rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
import rad.client as radcli
import rad.connect as radcon

from oslo.config import cfg

from neutron.agent.linux import utils
from neutron.agent.solaris import net_lib
from neutron.common import exceptions
from neutron.openstack.common import log as logging


LOG = logging.getLogger(__name__)

OPTS = [
    cfg.StrOpt('evs_controller', default='ssh://evsuser@localhost',
               help=_("An URI that specifies an EVS controller"))
]


class EVSControllerError(exceptions.NeutronException):
    message = _("EVS controller: %(errmsg)s")

    def __init__(self, evs_errmsg):
        super(EVSControllerError, self).__init__(errmsg=evs_errmsg)


class SolarisVNICDriver(object):
    """Driver used to manage Solaris EVS VNICs.

    This class provides methods to create/delete an EVS VNIC and
    plumb/unplumb ab IP interface and addresses on the EVS VNIC.
    """

    # TODO(gmoodalb): dnsmasq uses old style `ifreq', so 16 is the maximum
    # length including the NUL character. If we change it to use new style
    # `lifreq', then we will be able to extend the length to 32 characters.
    VNIC_NAME_MAXLEN = 15
    VNIC_NAME_PREFIX = 'dh'
    VNIC_NAME_SUFFIX = '_0'
    VNIC_NAME_LEN_WO_SUFFIX = VNIC_NAME_MAXLEN - \
        len(VNIC_NAME_SUFFIX)

    def __init__(self, conf):
        self.conf = conf
        # Since there is no connect_uri() yet, we need to do this ourselves
        # parse ssh://user@hostname
        suh = self.conf.evs_controller.split('://')
        if len(suh) != 2 or suh[0] != 'ssh' or not suh[1].strip():
            raise SystemExit(_("Specified evs_controller is invalid"))
        uh = suh[1].split('@')
        if len(uh) != 2 or not uh[0].strip() or not uh[1].strip():
            raise SystemExit(_("'user' and 'hostname' need to be specified "
                               "for evs_controller"))

        # save the user and EVS controller info
        self.uh = uh
        self._rad_connection = None
        # set the controller property for this host
        cmd = ['/usr/sbin/evsadm', 'show-prop', '-co', 'value', '-p',
               'controller']
        stdout = utils.execute(cmd)
        if conf.evs_controller != stdout.strip():
            cmd = ['/usr/sbin/evsadm', 'set-prop', '-p',
                   'controller=%s' % (conf.evs_controller)]
            utils.execute(cmd)

    @property
    def rad_connection(self):
        if (self._rad_connection is not None and
                self._rad_connection._closed is None):
            return self._rad_connection

        LOG.debug(_("Connecting to EVS Controller at %s as %s") %
                  (self.uh[1], self.uh[0]))

        self._rad_connection = radcon.connect_ssh(self.uh[1], user=self.uh[0])
        return self._rad_connection

    def fini_l3(self, device_name):
        ipif = net_lib.IPInterface(device_name)
        ipif.delete_ip()

    def init_l3(self, device_name, ip_cidrs, addrconf=False):
        """Set the L3 settings for the interface using data from the port.
           ip_cidrs: list of 'X.X.X.X/YY' strings
        """
        ipif = net_lib.IPInterface(device_name)
        for ip_cidr in ip_cidrs:
            ipif.create_address(ip_cidr)
        if addrconf:
            ipif.create_addrconf()

    # TODO(gmoodalb): - probably take PREFIX?? for L3
    def get_device_name(self, port):
        vnicname = (self.VNIC_NAME_PREFIX +
                    port.id)[:self.VNIC_NAME_LEN_WO_SUFFIX]
        vnicname += self.VNIC_NAME_SUFFIX
        return vnicname.replace('-', '_')

    def plug(self, tenant_id, network_id, port_id, datalink_name,
             namespace=None, prefix=None, protection=False):
        """Plug in the interface."""

        try:
            evsc = self.rad_connection.get_object(evsbind.EVSController())
            vports_info = evsc.getVPortInfo("vport=%s" % (port_id))
            if vports_info:
                vport_info = vports_info[0]
                # This is to handle HA when the 1st DHCP/L3 agent is down and
                # the second DHCP/L3 agent tries to connect its VNIC to EVS, we
                # will end up in "vport in use" error. So, we need to reset the
                # vport before we connect the VNIC to EVS.
                if vport_info.status == evsbind.VPortStatus.USED:
                    LOG.debug(_("Retrieving EVS: %s"), vport_info.evsuuid)
                    pat = radcli.ADRGlobPattern({'uuid': network_id,
                                                 'tenant': tenant_id})
                    evs_objs = self.rad_connection.list_objects(evsbind.EVS(),
                                                                pat)
                    if evs_objs:
                        evs = self.rad_connection.get_object(evs_objs[0])
                        evs.resetVPort(port_id, "force=yes")

                if not protection:
                    LOG.debug(_("Retrieving VPort: %s"), port_id)
                    pat = radcli.ADRGlobPattern({'uuid': port_id,
                                                 'tenant': tenant_id,
                                                 'evsuuid': network_id})
                    vport_objs = self.rad_connection.list_objects(
                        evsbind.VPort(), pat)
                    if vport_objs:
                        vport = self.rad_connection.get_object(vport_objs[0])
                        vport.setProperty("protection=none")
        except radcli.ObjectError as oe:
            raise EVSControllerError(oe.get_payload().errmsg)
        finally:
            self.rad_connection.close()

        dl = net_lib.Datalink(datalink_name)
        evs_vport = "%s/%s" % (network_id, port_id)
        dl.connect_vnic(evs_vport, tenant_id)

    def unplug(self, device_name, namespace=None, prefix=None):
        """Unplug the interface."""

        dl = net_lib.Datalink(device_name)
        dl.delete_vnic()