This patch enables basic CRUD operations on trunk ports and defines related API extensions. Trunk ports and sub-ports can be persisted in the Neutron model and are made visible through the API, but the L2 agent is not notified and no trunk ports or subports are actually instantiated on compute hosts. This one of the main patches in the series that implement the end to end functionality. Partially-implements: blueprint vlan-aware-vms Co-Authored-By: Armando Migliaccio <armamig@gmail.com> Change-Id: I26453eb9a1b25e116193417271400994ac57e4c1
117 lines
4.8 KiB
Python
117 lines
4.8 KiB
Python
# 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.
|
|
|
|
from neutron_lib import constants as n_const
|
|
from neutron_lib import exceptions as n_exc
|
|
|
|
from neutron._i18n import _
|
|
from neutron.extensions import portbindings
|
|
from neutron.extensions import trunk
|
|
from neutron import manager
|
|
from neutron.objects import trunk as trunk_objects
|
|
from neutron.services.trunk import exceptions as trunk_exc
|
|
|
|
|
|
# This layer is introduced for keeping busines logic and
|
|
# data persistence decoupled.
|
|
|
|
class TrunkPortValidator(object):
|
|
|
|
def __init__(self, port_id):
|
|
self.port_id = port_id
|
|
|
|
def validate(self, context):
|
|
"""Validate that the port can be used in a trunk."""
|
|
# TODO(tidwellr): there is a chance of a race between the
|
|
# time these checks are performed and the time the trunk
|
|
# creation is executed. To be revisited, if it bites.
|
|
|
|
# Validate that the given port_id is not used by a subport.
|
|
subports = trunk_objects.SubPort.get_objects(
|
|
context, port_id=self.port_id)
|
|
if subports:
|
|
raise trunk_exc.TrunkPortInUse(port_id=self.port_id)
|
|
|
|
# Validate that the given port_id is not used by a trunk.
|
|
trunks = trunk_objects.Trunk.get_objects(context, port_id=self.port_id)
|
|
if trunks:
|
|
raise trunk_exc.ParentPortInUse(port_id=self.port_id)
|
|
|
|
if self.is_bound(context):
|
|
raise trunk_exc.ParentPortInUse(port_id=self.port_id)
|
|
|
|
return self.port_id
|
|
|
|
def is_bound(self, context):
|
|
"""Return true if the port is bound, false otherwise."""
|
|
# Validate that the given port_id does not have a port binding.
|
|
core_plugin = manager.NeutronManager.get_plugin()
|
|
port = core_plugin.get_port(context, self.port_id)
|
|
device_owner = port.get('device_owner', '')
|
|
return port.get(portbindings.HOST_ID) or \
|
|
device_owner.startswith(n_const.DEVICE_OWNER_COMPUTE_PREFIX)
|
|
|
|
|
|
class SubPortsValidator(object):
|
|
|
|
def __init__(self, segmentation_types, subports, trunk_port_id=None):
|
|
self._segmentation_types = segmentation_types
|
|
self.subports = subports
|
|
self.trunk_port_id = trunk_port_id
|
|
|
|
def validate(self, context,
|
|
basic_validation=False, trunk_validation=True):
|
|
"""Validate that subports can be used in a trunk."""
|
|
# Perform basic validation on subports, in case subports
|
|
# are not automatically screened by the API layer.
|
|
if basic_validation:
|
|
msg = trunk.validate_subports(self.subports)
|
|
if msg:
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
if trunk_validation:
|
|
return [self._validate(context, s) for s in self.subports]
|
|
else:
|
|
return self.subports
|
|
|
|
def _validate(self, context, subport):
|
|
# Check that the subport doesn't reference the same port_id as a
|
|
# trunk we may be in the middle of trying to create, in other words
|
|
# make the validation idiot proof.
|
|
if subport['port_id'] == self.trunk_port_id:
|
|
raise trunk_exc.ParentPortInUse(port_id=subport['port_id'])
|
|
|
|
# If the segmentation details are missing, we will need to
|
|
# figure out defaults when the time comes to support Ironic.
|
|
# We can reasonably expect segmentation details to be provided
|
|
# in all other cases for now.
|
|
segmentation_id = subport.get("segmentation_id")
|
|
segmentation_type = subport.get("segmentation_type")
|
|
if not segmentation_id or not segmentation_type:
|
|
msg = _("Invalid subport details '%s': missing segmentation "
|
|
"information. Must specify both segmentation_id and "
|
|
"segmentation_type") % subport
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
|
|
if segmentation_type not in self._segmentation_types:
|
|
msg = _("Invalid segmentation_type '%s'") % segmentation_type
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
|
|
if not self._segmentation_types[segmentation_type](segmentation_id):
|
|
msg = _("Invalid segmentation id '%s'") % segmentation_id
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
|
|
trunk_validator = TrunkPortValidator(subport['port_id'])
|
|
trunk_validator.validate(context)
|
|
return subport
|