From 648ab82a4bf07845b97c5cb62347fbde86cdc9d2 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Wed, 19 Dec 2018 13:44:37 +0100 Subject: [PATCH] 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) --- neutron/db/agents_db.py | 1 + .../alembic_migrations/versions/EXPAND_HEAD | 2 +- .../fb0167bd9639_agent_resources_synced.py | 36 +++++++++++++++++++ neutron/db/models/agent.py | 3 ++ neutron/extensions/agent_resources_synced.py | 20 +++++++++++ neutron/objects/agent.py | 11 +++++- neutron/plugins/ml2/plugin.py | 4 ++- neutron/tests/unit/objects/test_agent.py | 6 ++++ neutron/tests/unit/objects/test_objects.py | 2 +- 9 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/stein/expand/fb0167bd9639_agent_resources_synced.py create mode 100644 neutron/extensions/agent_resources_synced.py diff --git a/neutron/db/agents_db.py b/neutron/db/agents_db.py index 3dae4f34bc3..c3aebc5c59f 100644 --- a/neutron/db/agents_db.py +++ b/neutron/db/agents_db.py @@ -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() diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index f0c44d5166c..3db98c6fdb9 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -195176fb410d +fb0167bd9639 diff --git a/neutron/db/migration/alembic_migrations/versions/stein/expand/fb0167bd9639_agent_resources_synced.py b/neutron/db/migration/alembic_migrations/versions/stein/expand/fb0167bd9639_agent_resources_synced.py new file mode 100644 index 00000000000..e60c342e6c2 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/stein/expand/fb0167bd9639_agent_resources_synced.py @@ -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)) diff --git a/neutron/db/models/agent.py b/neutron/db/models/agent.py index a89b5004f93..d6c0473d543 100644 --- a/neutron/db/models/agent.py +++ b/neutron/db/models/agent.py @@ -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): diff --git a/neutron/extensions/agent_resources_synced.py b/neutron/extensions/agent_resources_synced.py new file mode 100644 index 00000000000..adf9fe1fb42 --- /dev/null +++ b/neutron/extensions/agent_resources_synced.py @@ -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 diff --git a/neutron/objects/agent.py b/neutron/objects/agent.py index 40009e83e36..f947674989b 100644 --- a/neutron/objects/agent.py +++ b/neutron/objects/agent.py @@ -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) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 20859ae9ef8..94d9e0885fe 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -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 diff --git a/neutron/tests/unit/objects/test_agent.py b/neutron/tests/unit/objects/test_agent.py index 007bcfcf6e0..1c944bbdc09 100644 --- a/neutron/tests/unit/objects/test_agent.py +++ b/neutron/tests/unit/objects/test_agent.py @@ -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']) diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index f7998b9dab6..bb73ea0ddf2 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -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',