
This commit removes almost all the pep8 rules from ignore list. Most of these rules only required minor changes or were redundant and could be enabled without any changes. The only rule exclusion left active is E128 to keep the commit small enough. Change-Id: Iec4f00cff9e023515082caae525207054341b94b
224 lines
8.6 KiB
Python
224 lines
8.6 KiB
Python
# 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 abc
|
|
import six
|
|
|
|
from oslo_log import log
|
|
from oslo_utils import importutils
|
|
|
|
from neutronclient.common import exceptions as n_exceptions
|
|
|
|
from kuryr.lib import constants as lib_const
|
|
from kuryr.lib import exceptions
|
|
from kuryr.lib import utils as lib_utils
|
|
from kuryr_libnetwork import app
|
|
from kuryr_libnetwork import config
|
|
from kuryr_libnetwork import utils as libnet_utils
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class Driver(object):
|
|
"""Interface class for port drivers.
|
|
|
|
In order to create compliant implementations subclasses must be named with
|
|
the 'Driver' suffix.
|
|
"""
|
|
|
|
@abc.abstractmethod
|
|
def get_default_network_id(self):
|
|
"""Returns a Neutron network ID as per driver logic, if any.
|
|
|
|
Endpoints associated to certain type of drivers might need to join a
|
|
specific, possibly pre-existing, network to be able to work correctly.
|
|
For these drivers a Neutron network ID is returned or an exception
|
|
raised if failed, None returned in all the other cases.
|
|
|
|
:returns: the Neutron network UUID as a string when available or None
|
|
:raises: exceptions.KuryrException
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def create_host_iface(self, endpoint_id, neutron_port, subnets,
|
|
network=None):
|
|
"""Instantiates a host interface and binds it to the host.
|
|
|
|
A host interface will be created for the specific Neutron port and
|
|
bound to the related network subsystem on the host by delegating to the
|
|
pre-selected kuryr-lib driver.
|
|
|
|
:param endpoint_id: the ID of the endpoint as string
|
|
:param neutron_port: the container Neutron port dictionary as returned
|
|
by python-neutronclient
|
|
:param subnets: an iterable of all the Neutron subnets which the
|
|
endpoint is trying to join
|
|
:param network: the Neutron network which the endpoint is trying
|
|
to join
|
|
:returns: the tuple of stdout and stderr returned by
|
|
processutils.execute invoked with the executable script for
|
|
binding
|
|
:raises: exceptions.VethCreationFailure,
|
|
exceptions.BindingNotSupportedFailure
|
|
exceptions.KuryrException,
|
|
neutronclient.common.NeutronClientException,
|
|
processutils.ProcessExecutionError
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def delete_host_iface(self, endpoint_id, neutron_port):
|
|
"""Deletes a host interface after unbinding it from the host.
|
|
|
|
The host interface associated to the Neutron port will be unbound from
|
|
its network subsystem and deleted by delegating to the selected
|
|
kuryr-lib driver.
|
|
|
|
:param endpoint_id: the ID of the Docker container as string
|
|
:param neutron_port: a port dictionary returned from
|
|
python-neutronclient
|
|
:returns: the tuple of stdout and stderr returned by
|
|
processutils.execute invoked with the executable script for
|
|
unbinding
|
|
:raises: exceptions.VethDeletionFailure,
|
|
exceptions.KuryrException,
|
|
neutronclient.common.NeutronClientException,
|
|
processutils.ProcessExecutionError
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def get_container_iface_name(self, neutron_port_id):
|
|
"""Returns interface name of a container in the default namespace.
|
|
|
|
:param neutron_port_id: The ID of a neutron port as string
|
|
:returns: interface name as string
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def get_supported_bindings(self):
|
|
"""Returns a tuple of supported binding driver names for the driver.
|
|
|
|
:returns: a tuple of strings
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def update_port(self, port, endpoint_id, interface_mac):
|
|
"""Updates port information and performs extra driver-specific actions.
|
|
|
|
It returns the updated port dictionary after the required actions
|
|
performed depending on the binding driver.
|
|
|
|
:param port: a neutron port dictionary returned from
|
|
python-neutronclient
|
|
:param endpoint_id: the ID of the endpoint as string
|
|
:param interface_mac: the MAC address of the endpoint
|
|
:returns: the updated Neutron port id dictionary as returned by
|
|
python-neutronclient
|
|
"""
|
|
port['name'] = libnet_utils.get_neutron_port_name(endpoint_id)
|
|
try:
|
|
updated_port = {
|
|
'name': port['name'],
|
|
'device_owner': lib_const.DEVICE_OWNER,
|
|
'device_id': endpoint_id,
|
|
'binding:host_id': lib_utils.get_hostname(),
|
|
}
|
|
if interface_mac:
|
|
updated_port['mac_address'] = interface_mac
|
|
response_port = app.neutron.update_port(port['id'],
|
|
{'port': updated_port})
|
|
except n_exceptions.NeutronClientException as ex:
|
|
LOG.error("Error happened during updating a "
|
|
"Neutron port: %s", ex)
|
|
raise
|
|
return response_port['port']
|
|
|
|
def __str__(self):
|
|
return self.__class__.__name__
|
|
|
|
|
|
def get_driver_instance():
|
|
"""Instantiate a driver instance accordingly to the file configuration.
|
|
|
|
:returns: a Driver instance
|
|
:raises: exceptions.KuryrException
|
|
"""
|
|
module, name, classname = _parse_port_driver_config()
|
|
|
|
# TODO(apuimedo): switch to the openstack/stevedore plugin system
|
|
try:
|
|
driver = importutils.import_object("{0}.{1}".format(module, classname))
|
|
except ImportError as ie:
|
|
raise exceptions.KuryrException(
|
|
"Cannot load port driver '{0}': {1}".format(module, ie))
|
|
|
|
_verify_port_driver_compliancy(driver, name)
|
|
_verify_binding_driver_compatibility(driver, name)
|
|
|
|
return driver
|
|
|
|
|
|
def _parse_port_driver_config():
|
|
"""Read the port driver related config value and parse it.
|
|
|
|
:returns: the provided full module path as per config file, the name of the
|
|
driver/module and the class name of the Driver class inside it
|
|
"""
|
|
config_value = config.CONF.port_driver
|
|
config_tokens = config_value.rsplit('.', 1)
|
|
if len(config_tokens) == 1: # not a path, just a name
|
|
name = config_tokens[0]
|
|
if len(name) == 0:
|
|
raise exceptions.KuryrException("No port driver provided")
|
|
else:
|
|
# Attempt to use the name only by prepending the default location
|
|
module = "kuryr_libnetwork.port_driver.drivers." + name
|
|
else:
|
|
module = config_value
|
|
name = config_tokens[1]
|
|
|
|
classname = name.capitalize() + 'Driver'
|
|
|
|
return module, name, classname
|
|
|
|
|
|
def _verify_port_driver_compliancy(driver, port_driver_name):
|
|
if not issubclass(driver.__class__, Driver):
|
|
raise exceptions.KuryrException("Cannot load port driver '{0}': "
|
|
"driver is not compliant with the {1} interface"
|
|
.format(port_driver_name, Driver.__name__))
|
|
|
|
|
|
def _verify_binding_driver_compatibility(driver, port_driver_name):
|
|
tokens = config.CONF.binding.driver.rsplit('.', 1)
|
|
binding_driver_name = tokens[0] if len(tokens) == 1 else tokens[1]
|
|
binding_driver_name.lower()
|
|
|
|
# TODO(mchiappe): find a clean way to test the binding driver
|
|
# is also loadable before we start
|
|
supported_bindings = driver.get_supported_bindings()
|
|
|
|
if binding_driver_name not in supported_bindings:
|
|
raise exceptions.KuryrException("Configuration file error: "
|
|
"port driver '{0}' is not compatible with binding driver '{1}'"
|
|
.format(port_driver_name, binding_driver_name))
|
|
|
|
# Temporarily ban IPVLAN, to be removed in the future
|
|
if binding_driver_name == 'ipvlan':
|
|
raise exceptions.KuryrException("Configuration file error: "
|
|
"binding driver '{0}' is currently not supported with '{1}' "
|
|
"port driver".format(binding_driver_name, port_driver_name))
|