Integrate NetworkSegment OVO

This patch integrates the NetworkSegment Oslo-Versioned Object.

Change-Id: Ie1569578fca45f3a705fbac44204a3d1e7bea7fe
Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db
This commit is contained in:
Victor Morales 2016-10-11 17:09:00 -05:00 committed by Kevin Benton
parent 143a6e8546
commit d26b96b7a2
7 changed files with 161 additions and 106 deletions

View File

@ -43,6 +43,7 @@ from neutron.extensions import ip_allocation as ipa
from neutron.extensions import segment
from neutron.ipam import exceptions as ipam_exceptions
from neutron.ipam import utils as ipam_utils
from neutron.objects import network as network_obj
from neutron.objects import subnet as subnet_obj
from neutron.services.segments import exceptions as segment_exc
@ -334,10 +335,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
network_id=network_id)
if segment_id:
query = context.session.query(segment_model.NetworkSegment)
query = query.filter(
segment_model.NetworkSegment.id == segment_id)
segment = query.one()
segment = network_obj.NetworkSegment.get_object(context,
id=segment_id)
if segment.network_id != network_id:
raise segment_exc.NetworkIdsDontMatch(
subnet_network=network_id,

View File

@ -12,7 +12,6 @@
from oslo_log import log as logging
from oslo_utils import uuidutils
from sqlalchemy.orm import exc
from neutron._i18n import _LI
from neutron.callbacks import events
@ -20,6 +19,8 @@ from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.db import api as db_api
from neutron.db.models import segment as segments_model
from neutron.objects import base as base_obj
from neutron.objects import network as network_obj
LOG = logging.getLogger(__name__)
@ -28,38 +29,35 @@ PHYSICAL_NETWORK = segments_model.NetworkSegment.physical_network.name
SEGMENTATION_ID = segments_model.NetworkSegment.segmentation_id.name
def _make_segment_dict(record):
"""Make a segment dictionary out of a DB record."""
return {'id': record.id,
NETWORK_TYPE: record.network_type,
PHYSICAL_NETWORK: record.physical_network,
SEGMENTATION_ID: record.segmentation_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}
def add_network_segment(context, network_id, segment, segment_index=0,
is_dynamic=False):
with db_api.context_manager.writer.using(context):
record = segments_model.NetworkSegment(
id=uuidutils.generate_uuid(),
network_id=network_id,
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
)
context.session.add(record)
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=record)
segment['id'] = record.id
segment=netseg_obj)
segment['id'] = netseg_obj.id
LOG.info(_LI("Added segment %(id)s of type %(network_type)s for network "
"%(network_id)s"),
{'id': record.id,
'network_type': record.network_type,
'network_id': record.network_id})
{'id': netseg_obj.id,
'network_type': netseg_obj.network_type,
'network_id': netseg_obj.network_id})
def get_network_segments(context, network_id, filter_dynamic=False):
@ -72,57 +70,54 @@ def get_networks_segments(context, network_ids, filter_dynamic=False):
return {}
with db_api.context_manager.reader.using(context):
query = (context.session.query(segments_model.NetworkSegment).
filter(segments_model.NetworkSegment.network_id
.in_(network_ids)).
order_by(segments_model.NetworkSegment.segment_index))
filters = {
'network_id': network_ids,
}
if filter_dynamic is not None:
query = query.filter_by(is_dynamic=filter_dynamic)
records = query.all()
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 records:
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_manager.reader.using(context):
try:
record = (context.session.query(segments_model.NetworkSegment).
filter_by(id=segment_id).
one())
return _make_segment_dict(record)
except exc.NoResultFound:
return
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_manager.reader.using(context):
query = (context.session.query(segments_model.NetworkSegment).
filter_by(network_id=network_id, is_dynamic=True))
filters = {
'network_id': network_id,
'is_dynamic': True,
}
if physical_network:
query = query.filter_by(physical_network=physical_network)
filters['physical_network'] = physical_network
if segmentation_id:
query = query.filter_by(segmentation_id=segmentation_id)
record = query.first()
filters['segmentation_id'] = segmentation_id
pager = base_obj.Pager(limit=1)
objs = network_obj.NetworkSegment.get_objects(
context, _pager=pager, **filters)
if record:
return _make_segment_dict(record)
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})
return None
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_manager.writer.using(context):
(context.session.query(segments_model.NetworkSegment).
filter_by(id=segment_id).delete())
network_obj.NetworkSegment.delete_objects(context, id=segment_id)

View File

@ -41,19 +41,69 @@ class NetworkSegment(base.NeutronDbObject):
fields = {
'id': common_types.UUIDField(),
'network_id': common_types.UUIDField(),
'name': obj_fields.StringField(),
'name': obj_fields.StringField(nullable=True),
'network_type': obj_fields.StringField(),
'physical_network': obj_fields.StringField(nullable=True),
'segmentation_id': obj_fields.IntegerField(nullable=True),
'is_dynamic': obj_fields.BooleanField(default=False),
'segment_index': obj_fields.IntegerField(default=0)
'segment_index': obj_fields.IntegerField(default=0),
'hosts': obj_fields.ListOfStringsField(nullable=True)
}
synthetic_fields = ['hosts']
foreign_keys = {
'Network': {'network_id': 'id'},
'PortBindingLevel': {'id': 'segment_id'},
}
def create(self):
fields = self.obj_get_changes()
with db_api.autonested_transaction(self.obj_context.session):
hosts = self.hosts
if hosts is None:
hosts = []
super(NetworkSegment, self).create()
if 'hosts' in fields:
self._attach_hosts(hosts)
def update(self):
fields = self.obj_get_changes()
with db_api.autonested_transaction(self.obj_context.session):
super(NetworkSegment, self).update()
if 'hosts' in fields:
self._attach_hosts(fields['hosts'])
def _attach_hosts(self, hosts):
SegmentHostMapping.delete_objects(
self.obj_context, segment_id=self.id,
)
if hosts:
for host in hosts:
SegmentHostMapping(
self.obj_context, segment_id=self.id, host=host).create()
self.hosts = hosts
self.obj_reset_changes(['hosts'])
def obj_load_attr(self, attrname):
if attrname == 'hosts':
return self._load_hosts()
super(NetworkSegment, self).obj_load_attr(attrname)
def _load_hosts(self, db_obj=None):
if db_obj:
hosts = db_obj.get('segment_host_mapping', [])
else:
hosts = SegmentHostMapping.get_objects(self.obj_context,
segment_id=self.id)
self.hosts = [host['host'] for host in hosts]
self.obj_reset_changes(['hosts'])
def from_db_object(self, db_obj):
super(NetworkSegment, self).from_db_object(db_obj)
self._load_hosts(db_obj)
@classmethod
def get_objects(cls, context, _pager=None, **kwargs):
if not _pager:

View File

@ -15,15 +15,12 @@
# under the License.
import functools
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from oslo_db import exception as db_exc
from oslo_log import helpers as log_helpers
from oslo_utils import uuidutils
from sqlalchemy.orm import exc
from neutron.callbacks import events
from neutron.callbacks import registry
@ -35,6 +32,7 @@ from neutron.db.models import segment as segment_model
from neutron.db import segments_db as db
from neutron.extensions import segment as extension
from neutron import manager
from neutron.objects import base as base_obj
from neutron.objects import network
from neutron.services.segments import exceptions
@ -43,25 +41,23 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
"""Mixin class to add segment."""
@staticmethod
def _make_segment_dict(segment_db, fields=None):
res = {'id': segment_db['id'],
'network_id': segment_db['network_id'],
'name': segment_db['name'],
'description': segment_db['description'],
db.PHYSICAL_NETWORK: segment_db[db.PHYSICAL_NETWORK],
db.NETWORK_TYPE: segment_db[db.NETWORK_TYPE],
db.SEGMENTATION_ID: segment_db[db.SEGMENTATION_ID],
'hosts': [mapping.host for mapping in
segment_db.segment_host_mapping],
'segment_index': segment_db['segment_index']}
def _make_segment_dict(segment_obj, fields=None):
res = {'id': segment_obj['id'],
'network_id': segment_obj['network_id'],
'name': segment_obj['name'],
'description': segment_obj['description'],
db.PHYSICAL_NETWORK: segment_obj[db.PHYSICAL_NETWORK],
db.NETWORK_TYPE: segment_obj[db.NETWORK_TYPE],
db.SEGMENTATION_ID: segment_obj[db.SEGMENTATION_ID],
'hosts': segment_obj['hosts'],
'segment_index': segment_obj['segment_index']}
return db_utils.resource_fields(res, fields)
def _get_segment(self, context, segment_id):
try:
return self._get_by_id(
context, segment_model.NetworkSegment, segment_id)
except exc.NoResultFound:
segment = network.NetworkSegment.get_object(context, id=segment_id)
if not segment:
raise exceptions.SegmentNotFound(segment_id=segment_id)
return segment
@log_helpers.log_method_call
def create_segment(self, context, segment):
@ -113,12 +109,13 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
segment_index = (segments[-1].get('segment_index') + 1)
args['segment_index'] = segment_index
new_segment = segment_model.NetworkSegment(**args)
context.session.add(new_segment)
new_segment = network.NetworkSegment(context, **args)
new_segment.create()
# Do some preliminary operations before committing the segment to
# db
registry.notify(resources.SEGMENT, events.PRECOMMIT_CREATE, self,
context=context, segment=new_segment)
registry.notify(
resources.SEGMENT, events.PRECOMMIT_CREATE, self,
context=context, segment=new_segment)
return new_segment
@log_helpers.log_method_call
@ -127,7 +124,8 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
segment = segment['segment']
with db_api.context_manager.writer.using(context):
curr_segment = self._get_segment(context, uuid)
curr_segment.update(segment)
curr_segment.update_fields(segment)
curr_segment.update()
return self._make_segment_dict(curr_segment)
@log_helpers.log_method_call
@ -139,23 +137,16 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
def get_segments(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
marker_obj = self._get_marker_obj(context, 'segment', limit, marker)
make_segment_dict = functools.partial(self._make_segment_dict)
return self._get_collection(context,
segment_model.NetworkSegment,
make_segment_dict,
filters=filters,
fields=fields,
sorts=sorts,
limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
filters = filters or {}
pager = base_obj.Pager(sorts, limit, page_reverse, marker)
segment_objs = network.NetworkSegment.get_objects(
context, _pager=pager, **filters)
return [self._make_segment_dict(obj) for obj in segment_objs]
@log_helpers.log_method_call
def get_segments_count(self, context, filters=None):
return self._get_collection_count(context,
segment_model.NetworkSegment,
filters=filters)
filters = filters or {}
return network.NetworkSegment.count(context, **filters)
@log_helpers.log_method_call
def get_segments_by_hosts(self, context, hosts):
@ -168,11 +159,11 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
@log_helpers.log_method_call
def delete_segment(self, context, uuid, for_net_delete=False):
"""Delete an existing segment."""
segment = self.get_segment(context, uuid)
segment_dict = self.get_segment(context, uuid)
# Do some preliminary operations before deleting the segment
registry.notify(resources.SEGMENT, events.BEFORE_DELETE,
self.delete_segment, context=context,
segment=segment, for_net_delete=for_net_delete)
segment=segment_dict, for_net_delete=for_net_delete)
# Delete segment in DB
with db_api.context_manager.writer.using(context):
@ -183,11 +174,11 @@ class SegmentDbMixin(common_db_mixin.CommonDbMixin):
# Do some preliminary operations before deleting segment in db
registry.notify(resources.SEGMENT, events.PRECOMMIT_DELETE,
self.delete_segment, context=context,
segment=segment)
segment=segment_dict)
registry.notify(resources.SEGMENT, events.AFTER_DELETE,
self.delete_segment, context=context,
segment=segment)
segment=segment_dict)
def update_segment_host_mapping(context, host, current_segment_ids):
@ -221,7 +212,7 @@ def _get_phys_nets(agent):
mappings = configurations_dict.get('bridge_mappings', {})
mappings.update(configurations_dict.get('interface_mappings', {}))
mappings.update(configurations_dict.get('device_mappings', {}))
return mappings.keys()
return list(mappings.keys())
reported_hosts = set()
@ -242,9 +233,8 @@ def get_segments_with_phys_nets(context, phys_nets):
return []
with db_api.context_manager.reader.using(context):
segments = context.session.query(segment_model.NetworkSegment).filter(
segment_model.NetworkSegment.physical_network.in_(phys_nets))
return segments
return network.NetworkSegment.get_objects(
context, physical_network=phys_nets)
def map_segment_to_hosts(context, segment_id, hosts):

View File

@ -1338,9 +1338,9 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
return port
def _create_test_segment(self, network):
self._segment = net_obj.NetworkSegment(self.context,
network_id=network['id'],
network_type='vxlan')
attr = self.get_random_object_fields(net_obj.NetworkSegment)
attr['network_id'] = network['id']
self._segment = net_obj.NetworkSegment(self.context, **attr)
self._segment.create()
def _create_test_router(self):

View File

@ -54,6 +54,27 @@ class NetworkSegmentDbObjTestCase(obj_test_base.BaseDbObjectTestCase,
network = self._create_network()
self.update_obj_fields({'network_id': network.id})
def test_hosts(self):
hosts = ['host1', 'host2']
obj = self._make_object(self.obj_fields[0])
obj.hosts = hosts
obj.create()
obj = network.NetworkSegment.get_object(self.context, id=obj.id)
self.assertEqual(hosts, obj.hosts)
obj.hosts = ['host3']
obj.update()
obj = network.NetworkSegment.get_object(self.context, id=obj.id)
self.assertEqual(['host3'], obj.hosts)
obj.hosts = None
obj.update()
obj = network.NetworkSegment.get_object(self.context, id=obj.id)
self.assertFalse(obj.hosts)
class NetworkObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase):
_test_class = network.Network

View File

@ -51,7 +51,7 @@ object_data = {
'Network': '1.0-f2f6308f79731a767b92b26b0f4f3849',
'NetworkDNSDomain': '1.0-420db7910294608534c1e2e30d6d8319',
'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
'NetworkSegment': '1.0-40707ef6bd9a0bf095038158d995cc7d',
'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8',
'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5',
'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578',
'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060',