6f26f4026b
In the ML2 plugin, allow to update the segmentation ID of a single provider network. A new method in the "SimpleAgentMechanismDriverBase" is added: "provider_network_attribute_updates_supported". This method returns, if implemented in the specific agent, which network attributes can be updated on a live network with ports bound to this network back-end. By default, an empty list is returned. Partial-Bug: #1806052 Change-Id: I2595335d6fbc51562b070f14eaeaadf49cf7c418
194 lines
7.6 KiB
Python
194 lines
7.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.
|
|
|
|
from neutron_lib.api.definitions import segment as segment_def
|
|
from neutron_lib.callbacks import events
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib.callbacks import resources
|
|
from neutron_lib.db import api as db_api
|
|
from neutron_lib.plugins.ml2 import api as ml2_api
|
|
from oslo_log import log as logging
|
|
from oslo_utils import uuidutils
|
|
|
|
from neutron.objects import base as base_obj
|
|
from neutron.objects import network as network_obj
|
|
from neutron.services.segments import exceptions as segments_exceptions
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
NETWORK_TYPE = segment_def.NETWORK_TYPE
|
|
PHYSICAL_NETWORK = segment_def.PHYSICAL_NETWORK
|
|
SEGMENTATION_ID = segment_def.SEGMENTATION_ID
|
|
NETWORK_ID = 'network_id'
|
|
|
|
|
|
def _make_segment_dict(obj):
|
|
"""Make a segment dictionary out of an object."""
|
|
return {'id': obj.id,
|
|
NETWORK_TYPE: obj.network_type,
|
|
PHYSICAL_NETWORK: obj.physical_network,
|
|
SEGMENTATION_ID: obj.segmentation_id,
|
|
NETWORK_ID: obj.network_id}
|
|
|
|
|
|
def add_network_segment(context, network_id, segment, segment_index=0,
|
|
is_dynamic=False):
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
netseg_obj = network_obj.NetworkSegment(
|
|
context, id=uuidutils.generate_uuid(), network_id=network_id,
|
|
network_type=segment.get(NETWORK_TYPE),
|
|
physical_network=segment.get(PHYSICAL_NETWORK),
|
|
segmentation_id=segment.get(SEGMENTATION_ID),
|
|
segment_index=segment_index, is_dynamic=is_dynamic)
|
|
netseg_obj.create()
|
|
registry.notify(resources.SEGMENT,
|
|
events.PRECOMMIT_CREATE,
|
|
trigger=add_network_segment,
|
|
context=context,
|
|
segment=netseg_obj)
|
|
segment['id'] = netseg_obj.id
|
|
LOG.info("Added segment %(id)s of type %(network_type)s for network "
|
|
"%(network_id)s",
|
|
{'id': netseg_obj.id,
|
|
'network_type': netseg_obj.network_type,
|
|
'network_id': netseg_obj.network_id})
|
|
|
|
|
|
def update_network_segment(context, segment_id, segmentation_id):
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
netseg_obj = network_obj.NetworkSegment.get_object(context,
|
|
id=segment_id)
|
|
if not netseg_obj:
|
|
raise segments_exceptions.SegmentNotFound(segment_id=segment_id)
|
|
netseg_obj[ml2_api.SEGMENTATION_ID] = segmentation_id
|
|
netseg_obj.update()
|
|
|
|
LOG.info("Updated segment %(id)s, segmentation_id: %(segmentation_id)s)",
|
|
{'id': segment_id, 'segmentation_id': segmentation_id})
|
|
|
|
|
|
def get_network_segments(context, network_id, filter_dynamic=False):
|
|
return get_networks_segments(
|
|
context, [network_id], filter_dynamic)[network_id]
|
|
|
|
|
|
def get_networks_segments(context, network_ids, filter_dynamic=False):
|
|
if not network_ids:
|
|
return {}
|
|
|
|
with db_api.CONTEXT_READER.using(context):
|
|
filters = {
|
|
'network_id': network_ids,
|
|
}
|
|
if filter_dynamic is not None:
|
|
filters['is_dynamic'] = filter_dynamic
|
|
objs = network_obj.NetworkSegment.get_objects(context, **filters)
|
|
result = {net_id: [] for net_id in network_ids}
|
|
for record in objs:
|
|
result[record.network_id].append(_make_segment_dict(record))
|
|
return result
|
|
|
|
|
|
def get_segment_by_id(context, segment_id):
|
|
with db_api.CONTEXT_READER.using(context):
|
|
net_obj = network_obj.NetworkSegment.get_object(context, id=segment_id)
|
|
if net_obj:
|
|
return _make_segment_dict(net_obj)
|
|
|
|
|
|
def get_dynamic_segment(context, network_id, physical_network=None,
|
|
segmentation_id=None):
|
|
"""Return a dynamic segment for the filters provided if one exists."""
|
|
with db_api.CONTEXT_READER.using(context):
|
|
filters = {
|
|
'network_id': network_id,
|
|
'is_dynamic': True,
|
|
}
|
|
if physical_network:
|
|
filters['physical_network'] = physical_network
|
|
if segmentation_id:
|
|
filters['segmentation_id'] = segmentation_id
|
|
pager = base_obj.Pager(limit=1)
|
|
objs = network_obj.NetworkSegment.get_objects(
|
|
context, _pager=pager, **filters)
|
|
|
|
if objs:
|
|
return _make_segment_dict(objs[0])
|
|
else:
|
|
LOG.debug("No dynamic segment found for "
|
|
"Network:%(network_id)s, "
|
|
"Physical network:%(physnet)s, "
|
|
"segmentation_id:%(segmentation_id)s",
|
|
{'network_id': network_id,
|
|
'physnet': physical_network,
|
|
'segmentation_id': segmentation_id})
|
|
|
|
|
|
def delete_network_segment(context, segment_id):
|
|
"""Release a dynamic segment for the params provided if one exists."""
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
network_obj.NetworkSegment.delete_objects(context, id=segment_id)
|
|
|
|
|
|
def network_segments_exist_in_range(context, network_type, physical_network,
|
|
segment_range=None):
|
|
"""Check whether one or more network segments exist in a range."""
|
|
with db_api.CONTEXT_READER.using(context):
|
|
filters = {
|
|
'network_type': network_type,
|
|
'physical_network': physical_network,
|
|
}
|
|
segment_objs = network_obj.NetworkSegment.get_objects(
|
|
context, **filters)
|
|
if segment_range:
|
|
minimum_id = segment_range['minimum']
|
|
maximum_id = segment_range['maximum']
|
|
segment_objs = [
|
|
segment for segment in segment_objs if
|
|
minimum_id <= segment.segmentation_id <= maximum_id]
|
|
return len(segment_objs) > 0
|
|
|
|
|
|
def min_max_actual_segments_in_range(context, network_type, physical_network,
|
|
segment_range=None):
|
|
"""Return the minimum and maximum segmentation IDs used in a network
|
|
segment range
|
|
"""
|
|
with db_api.CONTEXT_READER.using(context):
|
|
filters = {
|
|
'network_type': network_type,
|
|
'physical_network': physical_network,
|
|
}
|
|
pager = base_obj.Pager()
|
|
# (NOTE) True means ASC, False is DESC
|
|
pager.sorts = [('segmentation_id', True)]
|
|
segment_objs = network_obj.NetworkSegment.get_objects(
|
|
context, _pager=pager, **filters)
|
|
|
|
if segment_range:
|
|
minimum_id = segment_range['minimum']
|
|
maximum_id = segment_range['maximum']
|
|
segment_objs = [
|
|
segment for segment in segment_objs if
|
|
minimum_id <= segment.segmentation_id <= maximum_id]
|
|
|
|
if segment_objs:
|
|
return (segment_objs[0].segmentation_id,
|
|
segment_objs[-1].segmentation_id)
|
|
else:
|
|
LOG.debug("No existing segment found for "
|
|
"Network type:%(network_type)s, "
|
|
"Physical network:%(physical_network)s",
|
|
{'network_type': network_type,
|
|
'physical_network': physical_network})
|
|
return None, None
|