New agent attribute: resources_synced

Agents supporting the guaranteed minimum bandwidth feature need to share
their resource view with neutron-server and in turn with Placement too.
The resource information is synchronized to neutron-server via the
periodic agent heartbeat therefore transient synchronization errors are
fixed by the next heartbeat. But synchronization to Placement is not
done periodically, but on a (mostly) on demand basis. Therefore to fix
transient errors of the synchronization to Placement we must remember
the success/failure of the last synchronization attempt.

This change implements the extension 'agent-resources-synced' and
therefore extends the agent db model and object with a new attribute:
'resources_synced'. This attribute in only meant to be updated
internally. But it can be read via the API for debugging purposes.

APIImpact: The agent resource has a new attribute: resources_synced.

Change-Id: I757d659cea63c8172ca3618d1f581d10236f5e71
Depends-On: https://review.openstack.org/626210
Partial-Bug: #1578989
See-Also: https://review.openstack.org/502306 (nova spec)
See-Also: https://review.openstack.org/508149 (neutron spec)
This commit is contained in:
Bence Romsics 2018-12-19 13:44:37 +01:00
parent 66e74a6101
commit 648ab82a4b
9 changed files with 81 additions and 4 deletions

View File

@ -261,6 +261,7 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin):
res['resource_versions'] = self._get_dict(agent, 'resource_versions',
ignore_missing=True)
res['availability_zone'] = agent['availability_zone']
res['resources_synced'] = agent['resources_synced']
return db_utils.resource_fields(res, fields)
@db_api.retry_if_session_inactive()

View File

@ -1 +1 @@
195176fb410d
fb0167bd9639

View File

@ -0,0 +1,36 @@
# Copyright 2019 OpenStack Foundation
#
# 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 alembic import op
import sqlalchemy as sa
"""agent_resources_synced
Revision ID: fb0167bd9639
Revises: 195176fb410d
Create Date: 2019-01-04 12:34:44.563725
"""
# revision identifiers, used by Alembic.
revision = 'fb0167bd9639'
down_revision = '195176fb410d'
def upgrade():
op.add_column(
'agents',
sa.Column('resources_synced', sa.Boolean(), server_default=None))

View File

@ -53,6 +53,9 @@ class Agent(model_base.BASEV2, model_base.HasId):
resource_versions = sa.Column(sa.String(8191))
# load - number of resources hosted by the agent
load = sa.Column(sa.Integer, server_default='0', nullable=False)
# resources_synced: nullable boolean, success of last sync to Placement
resources_synced = sa.Column(
sa.Boolean, default=None, server_default=None, nullable=True)
@property
def is_active(self):

View File

@ -0,0 +1,20 @@
# Copyright (c) 2019 Ericsson
#
# 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 agent_resources_synced as apidef
from neutron_lib.api import extensions
class Agent_resources_synced(extensions.APIExtensionDescriptor):
api_definition = apidef

View File

@ -14,6 +14,7 @@
from neutron_lib import constants as const
from neutron_lib.objects import utils as obj_utils
from oslo_utils import versionutils
from oslo_versionedobjects import fields as obj_fields
from sqlalchemy import func
@ -29,7 +30,8 @@ from neutron.objects import common_types
@base.NeutronObjectRegistry.register
class Agent(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
# Version 1.1: Added resources_synced
VERSION = '1.1'
db_model = agent_model.Agent
@ -48,6 +50,7 @@ class Agent(base.NeutronDbObject):
'configurations': common_types.DictOfMiscValuesField(),
'resource_versions': common_types.DictOfMiscValuesField(nullable=True),
'load': obj_fields.IntegerField(default=0),
'resources_synced': obj_fields.BooleanField(nullable=True),
}
@classmethod
@ -80,6 +83,12 @@ class Agent(base.NeutronDbObject):
cls.load_json_from_str(fields['resource_versions']))
return fields
def obj_make_compatible(self, primitive, target_version):
super(Agent, self).obj_make_compatible(primitive, target_version)
_target_version = versionutils.convert_version_to_tuple(target_version)
if _target_version < (1, 1):
primitive.pop('resources_synced', None)
@property
def is_active(self):
return not utils.is_agent_down(self.heartbeat_timestamp)

View File

@ -18,6 +18,7 @@ from neutron_lib.agent import constants as agent_consts
from neutron_lib.agent import topics
from neutron_lib.api.definitions import address_scope
from neutron_lib.api.definitions import agent as agent_apidef
from neutron_lib.api.definitions import agent_resources_synced
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import availability_zone_filter
@ -191,7 +192,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
empty_string_filtering.ALIAS,
filter_apidef.ALIAS,
port_mac_address_regenerate.ALIAS,
pbe_ext.ALIAS]
pbe_ext.ALIAS,
agent_resources_synced.ALIAS]
# List of agent types for which all binding_failed ports should try to be
# rebound when agent revive

View File

@ -73,3 +73,9 @@ class AgentDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
db_fields = obj.modify_fields_to_db(obj)
self.assertIsNone(db_fields['resource_versions'])
def test_resources_synced_10(self):
obj = agent.Agent()
primitive = obj.obj_to_primitive(target_version='1.0')
self.assertNotIn(
'resources_synced', primitive['versioned_object.data'])

View File

@ -27,7 +27,7 @@ from neutron.tests import base as test_base
# alphabetic order.
object_data = {
'AddressScope': '1.0-dd0dfdb67775892d3adc090e28e43bd8',
'Agent': '1.0-7106cb40117a8d1f042545796ed8787d',
'Agent': '1.1-64b670752d57b3c7602cb136e0338507',
'AllowedAddressPair': '1.0-9f9186b6f952fbf31d257b0458b852c0',
'AutoAllocatedTopology': '1.0-74642e58c53bf3610dc224c59f81b242',
'DefaultSecurityGroup': '1.0-971520cb2e0ec06d747885a0cf78347f',