[stable-only] Change the agent AZ query by agent type

In PostgreSQL, it is needed to add the field used in the query
field ("select") clause in the "group_by" one. This patch changes
the scope of the query to only select the filtered fields (availability
zones and agent type).

Because the PostgreSQL support has been dropped in master branch
(Epoxy, 2025.1), this patch is only for stable branches.

Closes-Bug: #2086787
Change-Id: Ifb5ab94ca68a9ab84407b54ac632164860b7a3a8
(cherry picked from commit ebafc58e69)
This commit is contained in:
Rodolfo Alonso Hernandez 2024-11-12 10:00:08 +00:00 committed by Rodolfo Alonso
parent 098b94906a
commit 3273f11115
3 changed files with 96 additions and 10 deletions

View File

@ -78,10 +78,8 @@ AZ_ATTRIBUTE_MAP = {
def get_availability_zones_by_agent_type(context, agent_type,
availability_zones):
"""Get list of availability zones based on agent type"""
agents = agent_obj.Agent.get_agents_by_availability_zones_and_agent_type(
return agent_obj.Agent.get_availability_zones_by_agent_type(
context, agent_type=agent_type, availability_zones=availability_zones)
return set(agent.availability_zone for agent in agents)
class AgentAvailabilityZoneMixin(az_ext.AvailabilityZonePluginBase):

View File

@ -154,15 +154,18 @@ class Agent(base.NeutronDbObject):
@classmethod
@db_api.CONTEXT_READER
def get_agents_by_availability_zones_and_agent_type(
def get_availability_zones_by_agent_type(
cls, context, agent_type, availability_zones):
query = context.session.query(agent_model.Agent).filter_by(
agent_type=agent_type).group_by(
agent_model.Agent.availability_zone)
query = context.session.query(
agent_model.Agent.availability_zone,
agent_model.Agent.agent_type)
query = query.filter(
agent_model.Agent.availability_zone.in_(availability_zones)).all()
agents = [cls._load_object(context, record) for record in query]
return agents
agent_model.Agent.availability_zone.in_(availability_zones),
agent_model.Agent.agent_type == agent_type)
agents = query.group_by(
agent_model.Agent.availability_zone,
agent_model.Agent.agent_type).all()
return [agent[0] for agent in agents]
@classmethod
def get_objects_by_agent_mode(cls, context, agent_mode=None, **kwargs):

View File

@ -0,0 +1,85 @@
# Copyright 2021 Red Hat, Inc.
# 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 collections import defaultdict
from neutron_lib import context as n_context
from neutron_lib.db import api as db_api
from oslo_utils import timeutils
from oslo_utils import uuidutils
from neutron.objects import agent as agent_obj
from neutron.tests.unit import testlib_api
class _AgentSql(testlib_api.SqlTestCase):
def setUp(self):
super().setUp()
self.context = n_context.get_admin_context()
@db_api.CONTEXT_WRITER
def _create_agent(self, context, agent_type, az, host=None):
host = host or uuidutils.generate_uuid()
agent = agent_obj.Agent(context, agent_type=agent_type,
availability_zone=az, host=host,
binary=uuidutils.generate_uuid(),
topic=uuidutils.generate_uuid(),
admin_state_up=True,
created_at=timeutils.utcnow(),
started_at=timeutils.utcnow(),
heartbeat_timestamp=timeutils.utcnow(),
configurations='{}',
load=0,
)
agent.create()
def test_get_agents_by_availability_zones_and_agent_type(self):
self.agents = defaultdict(dict)
agent_types = ('dhcp', 'ovs', 'l3agent')
azs = ('az1', 'az2', 'az3')
for type_ in agent_types:
for az in azs:
# Create up to 5 agents per AZ and agent type. That will check
# the query GROUP BY clause.
for _ in range(5):
self._create_agent(self.context, type_, az)
method = agent_obj.Agent.get_availability_zones_by_agent_type
for type_ in agent_types:
for az in azs:
res_azs = method(self.context, type_, [az])
self.assertEqual(1, len(res_azs))
self.assertEqual(az, res_azs[0])
# Non-existing types, correct AZs
for type_ in ('type1', 'type2'):
for az in azs:
res_azs = method(self.context, type_, [az])
self.assertEqual(0, len(res_azs))
# Correct types, non-existing AZs
for type_ in agent_types:
for az in ('az23', 'az42'):
res_azs = method(self.context, type_, [az])
self.assertEqual(0, len(res_azs))
class TestAgentMySQL(testlib_api.MySQLTestCaseMixin, _AgentSql):
pass
class TestAgentPostgreSQL(testlib_api.PostgreSQLTestCaseMixin, _AgentSql):
pass