From 7c7b2d75aa17ce698ef7e74887e6c92bcc97727b Mon Sep 17 00:00:00 2001 From: Nakul Dahiwade Date: Thu, 10 Nov 2016 11:35:04 +0100 Subject: [PATCH] OVO for NetworkDhcpAgentBinding This patch introduces and implements Oslo-Versioned Objects for NetworkDhcpAgentBinding Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db Co-Authored-By: Manjeet Singh Bhatia Change-Id: Ie6220f8a1455ea721f0d9c7a1b58240cc5fde05a --- neutron/db/agentschedulers_db.py | 89 +++++------ .../db/network_dhcp_agent_binding/models.py | 2 +- neutron/objects/network.py | 26 ++++ neutron/scheduler/dhcp_agent_scheduler.py | 24 +-- .../scheduler/test_dhcp_agent_scheduler.py | 43 +++--- neutron/tests/unit/objects/test_base.py | 12 +- neutron/tests/unit/objects/test_network.py | 20 +++ neutron/tests/unit/objects/test_objects.py | 1 + .../scheduler/test_dhcp_agent_scheduler.py | 143 ++++++++++++------ 9 files changed, 227 insertions(+), 133 deletions(-) diff --git a/neutron/db/agentschedulers_db.py b/neutron/db/agentschedulers_db.py index 3e1a7c7f973..d233e965d4d 100644 --- a/neutron/db/agentschedulers_db.py +++ b/neutron/db/agentschedulers_db.py @@ -23,7 +23,6 @@ from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_utils import timeutils -from sqlalchemy import orm from sqlalchemy.orm import exc from neutron._i18n import _ @@ -32,10 +31,9 @@ from neutron.common import constants as n_const from neutron.common import utils from neutron.db import agents_db from neutron.db.availability_zone import network as network_az -from neutron.db.models import agent as agent_model -from neutron.db.network_dhcp_agent_binding import models as ndab_model from neutron.extensions import agent as ext_agent from neutron.extensions import dhcpagentscheduler +from neutron.objects import network from neutron import worker as neutron_worker @@ -234,9 +232,8 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler """ agent_dead_limit = datetime.timedelta( seconds=self.agent_dead_limit_seconds()) - network_count = (context.session.query(ndab_model. - NetworkDhcpAgentBinding). - filter_by(dhcp_agent_id=agent['id']).count()) + network_count = network.NetworkDhcpAgentBinding.count( + context, dhcp_agent_id=agent['id']) # amount of networks assigned to agent affect amount of time we give # it so startup. Tests show that it's more or less sage to assume # that DHCP agent processes each network in less than 2 seconds. @@ -285,9 +282,10 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler checked_agents = {} for binding in bindings: try: - agent_id = binding.dhcp_agent['id'] + agent_id = binding.db_obj.dhcp_agent['id'] if agent_id not in checked_agents: - if self.agent_starting_up(context, binding.dhcp_agent): + if self.agent_starting_up(context, + binding.db_obj.dhcp_agent): # When agent starts and it has many networks to process # it may fail to send state reports in defined interval # The server will consider it dead and try to remove @@ -316,11 +314,8 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler context = ncontext.get_admin_context() try: - down_bindings = ( - context.session.query(ndab_model.NetworkDhcpAgentBinding). - join(agent_model.Agent). - filter(agent_model.Agent.heartbeat_timestamp < cutoff, - agent_model.Agent.admin_state_up)) + down_bindings = network.NetworkDhcpAgentBinding.get_down_bindings( + context, cutoff) dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) dead_bindings = [b for b in self._filter_bindings(context, down_bindings)] @@ -381,23 +376,23 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler hosts=None): if not network_ids: return [] - query = context.session.query(ndab_model.NetworkDhcpAgentBinding) - query = query.options(orm.contains_eager( - ndab_model.NetworkDhcpAgentBinding.dhcp_agent)) - query = query.join(ndab_model.NetworkDhcpAgentBinding.dhcp_agent) - if network_ids: - query = query.filter( - ndab_model.NetworkDhcpAgentBinding.network_id.in_(network_ids)) - if hosts: - query = query.filter(agent_model.Agent.host.in_(hosts)) + # get all the NDAB objects, which will also fetch (from DB) + # the related dhcp_agent objects because of the synthetic field + bindings = network.NetworkDhcpAgentBinding.get_objects( + context, network_id=network_ids) + # get the already fetched dhcp_agent objects + agent_objs = [binding.db_obj.dhcp_agent for binding in bindings] + # filter the dhcp_agent objects on admin_state_up if admin_state_up is not None: - query = query.filter(agent_model.Agent.admin_state_up == - admin_state_up) - - return [binding.dhcp_agent - for binding in query - if self.is_eligible_agent(context, active, - binding.dhcp_agent)] + agent_objs = [agent for agent in agent_objs + if agent.admin_state_up == admin_state_up] + # filter the dhcp_agent objects on hosts + if hosts: + agent_objs = [agent for agent in agent_objs + if agent.host in hosts] + # finally filter if the agents are eligible + return [agent for agent in agent_objs + if self.is_eligible_agent(context, active, agent)] def add_network_to_dhcp_agent(self, context, id, network_id): self._get_network(context, network_id) @@ -412,10 +407,8 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler if id == dhcp_agent.id: raise dhcpagentscheduler.NetworkHostedByDHCPAgent( network_id=network_id, agent_id=id) - binding = ndab_model.NetworkDhcpAgentBinding() - binding.dhcp_agent_id = id - binding.network_id = network_id - context.session.add(binding) + network.NetworkDhcpAgentBinding(context, dhcp_agent_id=id, + network_id=network_id).create() dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) if dhcp_notifier: dhcp_notifier.network_added_to_agent( @@ -424,12 +417,9 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler def remove_network_from_dhcp_agent(self, context, id, network_id, notify=True): agent = self._get_agent(context, id) - try: - query = context.session.query(ndab_model.NetworkDhcpAgentBinding) - binding = query.filter( - ndab_model.NetworkDhcpAgentBinding.network_id == network_id, - ndab_model.NetworkDhcpAgentBinding.dhcp_agent_id == id).one() - except exc.NoResultFound: + binding_obj = network.NetworkDhcpAgentBinding.get_object( + context, network_id=network_id, dhcp_agent_id=id) + if not binding_obj: raise dhcpagentscheduler.NetworkNotHostedByDhcpAgent( network_id=network_id, agent_id=id) @@ -444,8 +434,7 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler for port in ports: port['device_id'] = n_const.DEVICE_ID_RESERVED_DHCP_PORT self.update_port(context, port['id'], dict(port=port)) - with context.session.begin(): - context.session.delete(binding) + binding_obj.delete() if not notify: return @@ -455,12 +444,9 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler context, network_id, agent.host) def list_networks_on_dhcp_agent(self, context, id): - query = context.session.query( - ndab_model.NetworkDhcpAgentBinding.network_id) - query = query.filter( - ndab_model.NetworkDhcpAgentBinding.dhcp_agent_id == id) - - net_ids = [item[0] for item in query] + objs = network.NetworkDhcpAgentBinding.get_objects(context, + dhcp_agent_id=id) + net_ids = [item.network_id for item in objs] if net_ids: return {'networks': self.get_networks(context, filters={'id': net_ids})} @@ -479,12 +465,11 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler if not services_available(agent.admin_state_up): return [] - query = context.session.query( - ndab_model.NetworkDhcpAgentBinding.network_id) - query = query.filter( - ndab_model.NetworkDhcpAgentBinding.dhcp_agent_id == agent.id) - net_ids = [item[0] for item in query] + query = network.NetworkDhcpAgentBinding.get_objects( + context, dhcp_agent_id=agent.id) + + net_ids = [item.network_id for item in query] if net_ids: return self.get_networks( context, diff --git a/neutron/db/network_dhcp_agent_binding/models.py b/neutron/db/network_dhcp_agent_binding/models.py index 57723873006..f63da01b178 100644 --- a/neutron/db/network_dhcp_agent_binding/models.py +++ b/neutron/db/network_dhcp_agent_binding/models.py @@ -23,7 +23,7 @@ class NetworkDhcpAgentBinding(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id", ondelete='CASCADE'), primary_key=True) - dhcp_agent = orm.relation(agent_model.Agent) + dhcp_agent = orm.relation(agent_model.Agent, lazy='subquery') dhcp_agent_id = sa.Column(sa.String(36), sa.ForeignKey("agents.id", ondelete='CASCADE'), diff --git a/neutron/objects/network.py b/neutron/objects/network.py index d9d3c40fc68..39a9de17d12 100644 --- a/neutron/objects/network.py +++ b/neutron/objects/network.py @@ -20,9 +20,11 @@ from neutron.db.models import dns as dns_models from neutron.db.models import external_net as ext_net_model from neutron.db.models import segment as segment_model from neutron.db import models_v2 +from neutron.db.network_dhcp_agent_binding import models as ndab_models from neutron.db.port_security import models as ps_models from neutron.db import rbac_db_models from neutron.extensions import availability_zone as az_ext +from neutron.objects import agent as agent_obj from neutron.objects import base from neutron.objects import common_types from neutron.objects.extensions import port_security as base_ps @@ -30,6 +32,30 @@ from neutron.objects.qos import binding from neutron.objects import rbac_db +@obj_base.VersionedObjectRegistry.register +class NetworkDhcpAgentBinding(base.NeutronDbObject): + # Version 1.0: Initial version + VERSION = '1.0' + + db_model = ndab_models.NetworkDhcpAgentBinding + + primary_keys = ['network_id', 'dhcp_agent_id'] + + fields = { + 'network_id': common_types.UUIDField(), + 'dhcp_agent_id': common_types.UUIDField(), + } + + # NOTE(ndahiwade): The join was implemented this way as get_objects + # currently doesn't support operators like '<' or '>' + @classmethod + def get_down_bindings(cls, context, cutoff): + agent_objs = agent_obj.Agent.get_objects(context) + dhcp_agent_ids = [obj.id for obj in agent_objs + if obj.heartbeat_timestamp < cutoff] + return cls.get_objects(context, dhcp_agent_id=dhcp_agent_ids) + + @obj_base.VersionedObjectRegistry.register class NetworkSegment(base.NeutronDbObject): # Version 1.0: Initial version diff --git a/neutron/scheduler/dhcp_agent_scheduler.py b/neutron/scheduler/dhcp_agent_scheduler.py index 166993d2623..b1f859c31a4 100644 --- a/neutron/scheduler/dhcp_agent_scheduler.py +++ b/neutron/scheduler/dhcp_agent_scheduler.py @@ -18,15 +18,13 @@ import collections from operator import itemgetter from neutron_lib import constants +from neutron_lib.objects import exceptions from oslo_config import cfg -from oslo_db import exception as db_exc from oslo_log import log as logging from sqlalchemy import sql from neutron.agent.common import utils as agent_utils -from neutron.db import api as db_api from neutron.db.models import agent as agent_model -from neutron.db.network_dhcp_agent_binding import models as ndab_model from neutron.extensions import availability_zone as az_ext from neutron.objects import network from neutron.scheduler import base_resource_filter @@ -181,15 +179,10 @@ class DhcpFilter(base_resource_filter.BaseResourceFilter): # saving agent_id to use it after rollback to avoid # DetachedInstanceError agent_id = agent.id - binding = ndab_model.NetworkDhcpAgentBinding() - binding.dhcp_agent_id = agent_id - binding.network_id = network_id try: - with db_api.autonested_transaction(context.session): - context.session.add(binding) - # try to actually write the changes and catch integrity - # DBDuplicateEntry - except db_exc.DBDuplicateEntry: + network.NetworkDhcpAgentBinding(context, + dhcp_agent_id=agent_id, network_id=network_id).create() + except exceptions.NeutronDbObjectDuplicateEntry: # it's totally ok, someone just did our job! bound_agents.remove(agent) LOG.info('Agent %s already present', agent_id) @@ -273,15 +266,14 @@ class DhcpFilter(base_resource_filter.BaseResourceFilter): az_hints = (network.get(az_ext.AZ_HINTS) or cfg.CONF.default_availability_zones) active_dhcp_agents = self._get_active_agents(plugin, context, az_hints) + hosted_agent_ids = [agent['id'] for agent in hosted_agents] if not active_dhcp_agents: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': hosted_agents} hostable_dhcp_agents = [ - agent for agent in set(active_dhcp_agents) - if agent not in hosted_agents and plugin.is_eligible_agent( - context, True, agent) - ] - + agent for agent in active_dhcp_agents + if agent.id not in hosted_agent_ids and plugin.is_eligible_agent( + context, True, agent)] hostable_dhcp_agents = self._filter_agents_with_network_access( plugin, context, network, hostable_dhcp_agents) diff --git a/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py b/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py index f53b1e04cb6..07952b3c457 100644 --- a/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py +++ b/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py @@ -19,12 +19,13 @@ from operator import attrgetter from neutron_lib.api.definitions import provider_net as providernet from neutron_lib import constants from neutron_lib import context +from oslo_utils import uuidutils import testscenarios from neutron.db import agents_db from neutron.db import agentschedulers_db from neutron.db import common_db_mixin -from neutron.db.network_dhcp_agent_binding import models as ndab_model +from neutron.objects import network from neutron.scheduler import dhcp_agent_scheduler from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import test_plugin @@ -359,10 +360,10 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, def get_subnets(self, context, fields=None): subnets = [] - for net_id in self._networks: - enable_dhcp = (not self._strip_host_index(net_id) in + for net in self._networks: + enable_dhcp = (self._strip_host_index(net['name']) not in self.networks_with_dhcp_disabled) - subnets.append({'network_id': net_id, + subnets.append({'network_id': net.id, 'enable_dhcp': enable_dhcp, 'segment_id': None}) return subnets @@ -374,13 +375,9 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, return {'availability_zone_hints': az_hints} def _get_hosted_networks_on_dhcp_agent(self, agent_id): - query = self.ctx.session.query( - ndab_model.NetworkDhcpAgentBinding.network_id) - query = query.filter( - ndab_model.NetworkDhcpAgentBinding.dhcp_agent_id == - agent_id) - - return [item[0] for item in query] + binding_objs = network.NetworkDhcpAgentBinding.get_objects( + self.ctx, dhcp_agent_id=agent_id) + return [item.network_id for item in binding_objs] def _test_auto_schedule(self, host_index): self.config(dhcp_agents_per_network=self.max_agents_per_network) @@ -394,9 +391,16 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, dhcp_agents = self._create_and_set_agents_down(hosts) # create networks - self._networks = ['%s-network-%s' % (host_index, i) - for i in range(self.network_count)] - self._save_networks(self._networks) + self._networks = [ + network.Network( + self.ctx, + id=uuidutils.generate_uuid(), + name='%s-network-%s' % (host_index, i)) + for i in range(self.network_count) + ] + for i in range(len(self._networks)): + self._networks[i].create() + network_ids = [net.id for net in self._networks] # pre schedule the networks to the agents defined in # self.hosted_networks before calling auto_schedule_network @@ -406,7 +410,7 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, net_index = self._extract_index(net) scheduler.resource_filter.bind(self.ctx, [dhcp_agents[agent_index]], - self._networks[net_index]) + network_ids[net_index]) retval = scheduler.auto_schedule_networks(self, self.ctx, hosts[host_index]) @@ -415,11 +419,14 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, agent_id = dhcp_agents[host_index].id hosted_networks = self._get_hosted_networks_on_dhcp_agent(agent_id) - hosted_net_ids = [self._strip_host_index(net) - for net in hosted_networks] + hosted_net_names = [ + self._strip_host_index(net['name']) + for net in network.Network.get_objects( + self.ctx, id=hosted_networks) + ] expected_hosted_networks = self.expected_hosted_networks['agent-%s' % host_index] - self.assertItemsEqual(hosted_net_ids, expected_hosted_networks, msg) + self.assertItemsEqual(hosted_net_names, expected_hosted_networks, msg) def test_auto_schedule(self): for i in range(self.agent_count): diff --git a/neutron/tests/unit/objects/test_base.py b/neutron/tests/unit/objects/test_base.py index ddb0ff71769..5423f9f53f7 100644 --- a/neutron/tests/unit/objects/test_base.py +++ b/neutron/tests/unit/objects/test_base.py @@ -1715,7 +1715,17 @@ class BaseDbObjectTestCase(_BaseObjectTestCase, # check that the stored database model does not have non-empty # relationships dbattr = obj.fields_need_translation.get(field, field) - self.assertFalse(getattr(obj.db_obj, dbattr, None)) + # Skipping empty relationships for the following reasons: + # 1) db_obj have the related object loaded - In this case we do not + # have to create the related objects and the loop can continue. + # 2) when the related objects are not loaded - In this + # case they need to be created because of the foreign key + # relationships. But we still need to check whether the + # relationships are loaded or not. That is achieved by the + # assertTrue statement after retrieving the dbattr in + # this method. + if getattr(obj.db_obj, dbattr, None): + continue if isinstance(cls_.fields[field], obj_fields.ObjectField): objclass_fields = self._get_non_synth_fields(objclass, diff --git a/neutron/tests/unit/objects/test_network.py b/neutron/tests/unit/objects/test_network.py index d84f133eb50..f86ad727f16 100644 --- a/neutron/tests/unit/objects/test_network.py +++ b/neutron/tests/unit/objects/test_network.py @@ -20,6 +20,26 @@ from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api +class NetworkDhcpAgentBindingObjectIfaceTestCase( + obj_test_base.BaseObjectIfaceTestCase): + + _test_class = network.NetworkDhcpAgentBinding + + +class NetworkDhcpAgentBindingDbObjectTestCase( + obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): + + _test_class = network.NetworkDhcpAgentBinding + + def setUp(self): + super(NetworkDhcpAgentBindingDbObjectTestCase, self).setUp() + self._network = self._create_test_network() + + self.update_obj_fields( + {'network_id': self._network.id, + 'dhcp_agent_id': lambda: self._create_test_agent_id()}) + + class NetworkPortSecurityIfaceObjTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.NetworkPortSecurity diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index 4c1b4cc0126..4ff29fbc963 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -54,6 +54,7 @@ object_data = { 'MeteringLabelRule': '1.0-b5c5717e7bab8d1af1623156012a5842', 'Log': '1.0-6391351c0f34ed34375a19202f361d24', 'Network': '1.0-f2f6308f79731a767b92b26b0f4f3849', + 'NetworkDhcpAgentBinding': '1.0-6eeceb5fb4335cd65a305016deb41c68', 'NetworkDNSDomain': '1.0-420db7910294608534c1e2e30d6d8319', 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8', diff --git a/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py b/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py index 219c2e23063..fab94947f04 100644 --- a/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import datetime import random import mock @@ -20,13 +21,15 @@ from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_utils import importutils +from oslo_utils import uuidutils import testscenarios from neutron.db import agentschedulers_db as sched_db from neutron.db import common_db_mixin from neutron.db import models_v2 -from neutron.db.network_dhcp_agent_binding import models as ndab_model from neutron.extensions import dhcpagentscheduler +from neutron.objects import agent +from neutron.objects import network as network_obj from neutron.scheduler import dhcp_agent_scheduler from neutron.services.segments import db as segments_service_db from neutron.tests.common import helpers @@ -47,8 +50,8 @@ class TestDhcpSchedulerBaseTestCase(testlib_api.SqlTestCase): super(TestDhcpSchedulerBaseTestCase, self).setUp() self.setup_coreplugin(self.CORE_PLUGIN) self.ctx = context.get_admin_context() - self.network = {'id': 'foo_network_id'} - self.network_id = 'foo_network_id' + self.network = {'id': uuidutils.generate_uuid()} + self.network_id = self.network['id'] self._save_networks([self.network_id]) def _create_and_set_agents_down(self, hosts, down_agent_count=0, @@ -72,11 +75,10 @@ class TestDhcpSchedulerBaseTestCase(testlib_api.SqlTestCase): def _test_schedule_bind_network(self, agents, network_id): scheduler = dhcp_agent_scheduler.ChanceScheduler() scheduler.resource_filter.bind(self.ctx, agents, network_id) - results = self.ctx.session.query( - ndab_model.NetworkDhcpAgentBinding).filter_by( - network_id=network_id).all() - self.assertEqual(len(agents), len(results)) - for result in results: + binding_objs = network_obj.NetworkDhcpAgentBinding.get_objects( + self.ctx, network_id=network_id) + self.assertEqual(len(agents), len(binding_objs)) + for result in binding_objs: self.assertEqual(network_id, result.network_id) @@ -135,9 +137,8 @@ class TestDhcpScheduler(TestDhcpSchedulerBaseTestCase): self._test_reschedule_vs_network_on_dead_agent(False) def _get_agent_binding_from_db(self, agent): - return self.ctx.session.query( - ndab_model.NetworkDhcpAgentBinding - ).filter_by(dhcp_agent_id=agent[0].id).all() + return network_obj.NetworkDhcpAgentBinding.get_objects( + self.ctx, dhcp_agent_id=agent[0].id) def _test_auto_reschedule_vs_network_on_dead_agent(self, active_hosts_only): @@ -289,9 +290,9 @@ class TestAutoScheduleNetworks(TestDhcpSchedulerBaseTestCase): observed_ret_value = scheduler.auto_schedule_networks( plugin, self.ctx, host) self.assertEqual(expected_result, observed_ret_value) - hosted_agents = self.ctx.session.query( - ndab_model.NetworkDhcpAgentBinding).all() - self.assertEqual(expected_hosted_agents, len(hosted_agents)) + count_hosted_agents = network_obj.NetworkDhcpAgentBinding.count( + self.ctx) + self.assertEqual(expected_hosted_agents, count_hosted_agents) class TestAutoScheduleSegments(test_plugin.Ml2PluginV2TestCase, @@ -409,10 +410,11 @@ class TestNetworksFailover(TestDhcpSchedulerBaseTestCase, sched_db.DhcpAgentSchedulerDbMixin, common_db_mixin.CommonDbMixin): def test_reschedule_network_from_down_agent(self): + net_id = uuidutils.generate_uuid() agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1) self._test_schedule_bind_network([agents[0]], self.network_id) - self._save_networks(["foo-network-2"]) - self._test_schedule_bind_network([agents[1]], "foo-network-2") + self._save_networks([net_id]) + self._test_schedule_bind_network([agents[1]], net_id) with mock.patch.object(self, 'remove_network_from_dhcp_agent') as rn,\ mock.patch.object(self, 'schedule_network', @@ -459,24 +461,70 @@ class TestNetworksFailover(TestDhcpSchedulerBaseTestCase, rn_side_effect=dhcpagentscheduler.NetworkNotHostedByDhcpAgent( network_id='foo', agent_id='bar')) + def _create_test_networks(self, num_net=0): + networks = [network_obj.Network( + self.ctx, + id=uuidutils.generate_uuid(), + name='network-%s' % (i)) + for i in range(num_net)] + for net in networks: + net.create() + return [net.id for net in networks] + + def _create_dhcp_agents(self): + timestamp = datetime.datetime.now() + dhcp_agent_ids = [uuidutils.generate_uuid() for x in range(2)] + dhcp_agent_1 = agent.Agent(self.ctx, id=dhcp_agent_ids[0], + agent_type='DHCP Agent', + topic='fake_topic', + host='fake_host', + binary='fake_binary', + created_at=timestamp, + started_at=timestamp, + heartbeat_timestamp=timestamp, + configurations={}, + load=0) + dhcp_agent_1.create() + + dhcp_agent_2 = agent.Agent(self.ctx, id=dhcp_agent_ids[1], + agent_type='DHCP Agent', + topic='fake_topic', + host='fake_host_1', + binary='fake_binary', + created_at=timestamp, + started_at=timestamp, + heartbeat_timestamp=timestamp, + configurations={}, + load=0) + dhcp_agent_2.create() + return [dhcp_agent_1.id, dhcp_agent_2.id] + def test_filter_bindings(self): - bindings = [ - ndab_model.NetworkDhcpAgentBinding(network_id='foo1', - dhcp_agent={'id': 'id1'}), - ndab_model.NetworkDhcpAgentBinding(network_id='foo2', - dhcp_agent={'id': 'id1'}), - ndab_model.NetworkDhcpAgentBinding(network_id='foo3', - dhcp_agent={'id': 'id2'}), - ndab_model.NetworkDhcpAgentBinding(network_id='foo4', - dhcp_agent={'id': 'id2'})] + self.ctx = context.get_admin_context() + dhcp_agt_ids = self._create_dhcp_agents() + network_ids = self._create_test_networks(num_net=4) + ndab_obj1 = network_obj.NetworkDhcpAgentBinding(self.ctx, + network_id=network_ids[0], dhcp_agent_id=dhcp_agt_ids[0]) + ndab_obj1.create() + ndab_obj2 = network_obj.NetworkDhcpAgentBinding(self.ctx, + network_id=network_ids[1], dhcp_agent_id=dhcp_agt_ids[0]) + ndab_obj2.create() + ndab_obj3 = network_obj.NetworkDhcpAgentBinding(self.ctx, + network_id=network_ids[2], dhcp_agent_id=dhcp_agt_ids[1]) + ndab_obj3.create() + ndab_obj4 = network_obj.NetworkDhcpAgentBinding(self.ctx, + network_id=network_ids[3], dhcp_agent_id=dhcp_agt_ids[1]) + ndab_obj4.create() + bindings_objs = network_obj.NetworkDhcpAgentBinding.get_objects( + self.ctx) with mock.patch.object(self, 'agent_starting_up', side_effect=[True, False]): - res = [b for b in self._filter_bindings(None, bindings)] + res = [b for b in self._filter_bindings(None, bindings_objs)] # once per each agent id1 and id2 self.assertEqual(2, len(res)) res_ids = [b.network_id for b in res] - self.assertIn('foo3', res_ids) - self.assertIn('foo4', res_ids) + self.assertIn(network_ids[2], res_ids) + self.assertIn(network_ids[3], res_ids) def test_reschedule_network_from_down_agent_failed_on_unexpected(self): agents = self._create_and_set_agents_down(['host-a'], 1) @@ -717,7 +765,7 @@ class TestDhcpSchedulerFilter(TestDhcpSchedulerBaseTestCase, admin_state_up=False) def test_get_dhcp_agents_hosting_many_networks(self): - net_id = 'another-net-id' + net_id = uuidutils.generate_uuid() self._save_networks([net_id]) networks = [net_id, self.network_id] self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b', @@ -745,35 +793,38 @@ class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase): cfg.CONF.set_override("dhcp_load_type", "networks") def test_az_scheduler_one_az_hints(self): - self._save_networks(['1111']) + net_id = uuidutils.generate_uuid() + self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, - {'id': '1111', 'availability_zone_hints': ['az2']}) + {'id': net_id, 'availability_zone_hints': ['az2']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, - ['1111']) + [net_id]) self.assertEqual(1, len(agents)) self.assertEqual('az2-host1', agents[0]['host']) def test_az_scheduler_default_az_hints(self): + net_id = uuidutils.generate_uuid() cfg.CONF.set_override('default_availability_zones', ['az1']) - self._save_networks(['1111']) + self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, - {'id': '1111', 'availability_zone_hints': []}) + {'id': net_id, 'availability_zone_hints': []}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, - ['1111']) + [net_id]) self.assertEqual(1, len(agents)) self.assertEqual('az1-host1', agents[0]['host']) def test_az_scheduler_two_az_hints(self): + net_id = uuidutils.generate_uuid() cfg.CONF.set_override('dhcp_agents_per_network', 2) - self._save_networks(['1111']) + self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') @@ -781,17 +832,18 @@ class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase): helpers.register_dhcp_agent('az3-host1', networks=5, az='az3') helpers.register_dhcp_agent('az3-host2', networks=6, az='az3') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, - {'id': '1111', 'availability_zone_hints': ['az1', 'az3']}) + {'id': net_id, 'availability_zone_hints': ['az1', 'az3']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, - ['1111']) + [net_id]) self.assertEqual(2, len(agents)) expected_hosts = set(['az1-host1', 'az3-host1']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def test_az_scheduler_two_az_hints_one_available_az(self): + net_id = uuidutils.generate_uuid() cfg.CONF.set_override('dhcp_agents_per_network', 2) - self._save_networks(['1111']) + self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, alive=False, @@ -799,26 +851,27 @@ class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase): helpers.register_dhcp_agent('az2-host2', networks=4, admin_state_up=False, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, - {'id': '1111', 'availability_zone_hints': ['az1', 'az2']}) + {'id': net_id, 'availability_zone_hints': ['az1', 'az2']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, - ['1111']) + [net_id]) self.assertEqual(2, len(agents)) expected_hosts = set(['az1-host1', 'az1-host2']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def _test_az_scheduler_no_az_hints(self, multiple_agent=False): + net_id = uuidutils.generate_uuid() num_agent = 2 if multiple_agent else 1 cfg.CONF.set_override('dhcp_agents_per_network', num_agent) - self._save_networks(['1111']) + self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=2, az='az1') helpers.register_dhcp_agent('az1-host2', networks=3, az='az1') helpers.register_dhcp_agent('az2-host1', networks=2, az='az2') helpers.register_dhcp_agent('az2-host2', networks=1, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, - {'id': '1111', 'availability_zone_hints': []}) + {'id': net_id, 'availability_zone_hints': []}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, - ['1111']) + [net_id]) self.assertEqual(num_agent, len(agents)) if multiple_agent: expected_hosts = set(['az1-host1', 'az2-host2']) @@ -834,7 +887,7 @@ class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase): self._test_az_scheduler_no_az_hints() def test_az_scheduler_select_az_with_least_weight(self): - self._save_networks(['1111']) + self._save_networks([uuidutils.generate_uuid()]) dhcp_agents = [] # Register 6 dhcp agents in 3 AZs, every AZ will have 2 agents. dhcp_agents.append(