12
12
# License for the specific language governing permissions and limitations
13
13
# under the License.
14
14
15
+ import collections
15
16
import datetime
17
+ import random
16
18
from unittest import mock
17
19
18
20
import eventlet
@@ -30,18 +32,43 @@ def setUp(self):
30
32
super ().setUp ()
31
33
self .agent_cache = neutron_agent .AgentCache (driver = mock .ANY )
32
34
self .addCleanup (self ._clean_agent_cache )
33
- self .names_ref = []
34
- for i in range (10 ): # Add 10 agents.
35
+ self .agents = {}
36
+ self .num_agents = 10 # Add 10 agents.
37
+ for i in range (self .num_agents ):
38
+ agent_type = random .choice (ovn_const .OVN_AGENT_TYPES )
39
+ other_config = {}
40
+ if agent_type == ovn_const .OVN_CONTROLLER_GW_AGENT :
41
+ # 'enable-chassis-as-gw' is mandatory if the controller is
42
+ # a gateway chassis; if not, it will default to
43
+ # 'OVN Controller agent'. Check ``ControllerGatewayAgent``
44
+ # class.
45
+ other_config = {'ovn-cms-options' : 'enable-chassis-as-gw' }
35
46
chassis = fakes .FakeOvsdbRow .create_one_ovsdb_row (
36
- attrs = {'other_config' : {}})
47
+ attrs = {'other_config' : other_config ,
48
+ 'hostname' : f'host{ i :d} ' ,
49
+ })
50
+ ext_ids = {}
51
+ if agent_type == ovn_const .OVN_METADATA_AGENT :
52
+ ext_ids = {
53
+ ovn_const .OVN_AGENT_METADATA_ID_KEY : 'chassis' + str (i )}
54
+ elif agent_type == ovn_const .OVN_NEUTRON_AGENT :
55
+ ext_ids = {
56
+ ovn_const .OVN_AGENT_NEUTRON_ID_KEY : 'chassis' + str (i )}
37
57
chassis_private = fakes .FakeOvsdbRow .create_one_ovsdb_row (
38
58
attrs = {'name' : 'chassis' + str (i ),
39
59
'other_config' : {},
40
60
'chassis' : [chassis ],
41
- 'nb_cfg_timestamp' : timeutils .utcnow_ts () * 1000 })
42
- self .agent_cache .update (ovn_const .OVN_CONTROLLER_AGENT ,
43
- chassis_private )
44
- self .names_ref .append ('chassis' + str (i ))
61
+ 'nb_cfg_timestamp' : timeutils .utcnow_ts () * 1000 ,
62
+ 'external_ids' : ext_ids ,
63
+ })
64
+ self .agent_cache .update (agent_type , chassis_private )
65
+ self .agents ['chassis' + str (i )] = agent_type
66
+
67
+ self .assertEqual (self .num_agents , len (list (self .agent_cache )))
68
+ for agent_class in (neutron_agent .NeutronAgent ,
69
+ neutron_agent .MetadataAgent ,
70
+ neutron_agent .OVNNeutronAgent ):
71
+ mock .patch .object (agent_class , 'alive' , return_value = True ).start ()
45
72
46
73
def _clean_agent_cache (self ):
47
74
del self .agent_cache
@@ -69,18 +96,19 @@ def test_update_while_iterating_agents(self):
69
96
pool .spawn (self ._list_agents )
70
97
pool .spawn (self ._add_and_delete_agents )
71
98
pool .waitall ()
72
- self .assertEqual (self .names_ref , self .names_read )
99
+ self .assertEqual (list ( self .agents . keys ()) , self .names_read )
73
100
74
101
def test_agents_by_chassis_private (self ):
102
+ ext_ids = {ovn_const .OVN_AGENT_METADATA_ID_KEY : 'chassis5' }
75
103
chassis_private = fakes .FakeOvsdbRow .create_one_ovsdb_row (
76
- attrs = {'name' : 'chassis5' })
104
+ attrs = {'name' : 'chassis5' ,
105
+ 'external_ids' : ext_ids })
77
106
agents = self .agent_cache .agents_by_chassis_private (chassis_private )
78
107
agents = list (agents )
79
108
self .assertEqual (1 , len (agents ))
80
109
self .assertEqual ('chassis5' , agents [0 ].agent_id )
81
110
82
- @mock .patch .object (neutron_agent .ControllerAgent , 'alive' )
83
- def test_heartbeat_timestamp_format (self , agent_alive ):
111
+ def test_heartbeat_timestamp_format (self ):
84
112
chassis_private = fakes .FakeOvsdbRow .create_one_ovsdb_row (
85
113
attrs = {'name' : 'chassis5' })
86
114
agents = self .agent_cache .agents_by_chassis_private (chassis_private )
@@ -89,8 +117,85 @@ def test_heartbeat_timestamp_format(self, agent_alive):
89
117
agent .updated_at = datetime .datetime (
90
118
year = 2023 , month = 2 , day = 23 , hour = 1 , minute = 2 , second = 3 ,
91
119
microsecond = 456789 ).replace (tzinfo = datetime .timezone .utc )
92
- agent_alive .return_value = True
93
120
94
121
# Verify that both microseconds and timezone are dropped
95
122
self .assertEqual (str (agent .as_dict ()['heartbeat_timestamp' ]),
96
123
'2023-02-23 01:02:03' )
124
+
125
+ def test_list_agents_filtering_host_same_type (self ):
126
+ for idx in range (len (self .agents )):
127
+ host = f'host{ idx :d} '
128
+ agents = self .agent_cache .get_agents (filters = {'host' : host })
129
+ self .assertEqual (1 , len (agents ))
130
+ self .assertEqual (host , agents [0 ].as_dict ()['host' ])
131
+
132
+ def test_list_agents_filtering_host_as_iterable (self ):
133
+ hosts = []
134
+ for idx in range (len (self .agents )):
135
+ hosts .append (f'host{ idx :d} ' )
136
+
137
+ agents = self .agent_cache .get_agents (filters = {'host' : hosts })
138
+ self .assertEqual (len (self .agents ), len (agents ))
139
+
140
+ def test_list_agents_filtering_agent_type_same_type (self ):
141
+ agent_types = collections .defaultdict (int )
142
+ for _type in self .agents .values ():
143
+ agent_types [_type ] = agent_types [_type ] + 1
144
+
145
+ for _type in agent_types :
146
+ agents = self .agent_cache .get_agents (
147
+ filters = {'agent_type' : _type })
148
+ self .assertEqual (agent_types [_type ], len (agents ))
149
+ self .assertEqual (_type , agents [0 ].as_dict ()['agent_type' ])
150
+
151
+ def test_list_agents_filtering_agent_type_as_iterable (self ):
152
+ agents = self .agent_cache .get_agents (
153
+ filters = {'agent_type' : ovn_const .OVN_AGENT_TYPES })
154
+ self .assertEqual (self .num_agents , len (agents ))
155
+
156
+ @mock .patch .object (neutron_agent , 'LOG' )
157
+ def test_list_agents_filtering_wrong_type (self , mock_log ):
158
+ agents = self .agent_cache .get_agents (filters = {'host' : 111 })
159
+ self .assertEqual (0 , len (agents ))
160
+ mock_log .info .assert_called_once ()
161
+
162
+ def test_list_agents_filtering_same_string_in_filter (self ):
163
+ # As reported in LP#2110094, if two registers have the same substring,
164
+ # the filter didn't work.
165
+ # Chassis 1, hostname: compute-0
166
+ chassis = fakes .FakeOvsdbRow .create_one_ovsdb_row (
167
+ attrs = {'other_config' : {},
168
+ 'hostname' : 'compute-0' })
169
+ chassis_private = fakes .FakeOvsdbRow .create_one_ovsdb_row (
170
+ attrs = {'name' : 'chassis1' ,
171
+ 'other_config' : {},
172
+ 'chassis' : [chassis ],
173
+ 'nb_cfg_timestamp' : timeutils .utcnow_ts () * 1000 ,
174
+ 'external_ids' : {}})
175
+ self .agent_cache .update (ovn_const .OVN_CONTROLLER_AGENT ,
176
+ chassis_private )
177
+
178
+ # Chassis 2, hostname: dcn1-compute-0
179
+ chassis = fakes .FakeOvsdbRow .create_one_ovsdb_row (
180
+ attrs = {'other_config' : {},
181
+ 'hostname' : 'dcn1-compute-0' })
182
+ chassis_private = fakes .FakeOvsdbRow .create_one_ovsdb_row (
183
+ attrs = {'name' : 'chassis2' ,
184
+ 'other_config' : {},
185
+ 'chassis' : [chassis ],
186
+ 'nb_cfg_timestamp' : timeutils .utcnow_ts () * 1000 ,
187
+ 'external_ids' : {}})
188
+ self .agent_cache .update (ovn_const .OVN_CONTROLLER_AGENT ,
189
+ chassis_private )
190
+
191
+ agents = self .agent_cache .get_agents (
192
+ filters = {'host' : 'compute-0' })
193
+ self .assertEqual (1 , len (agents ))
194
+
195
+ agents = self .agent_cache .get_agents (
196
+ filters = {'host' : 'dcn1-compute-0' })
197
+ self .assertEqual (1 , len (agents ))
198
+
199
+ agents = self .agent_cache .get_agents (
200
+ filters = {'host' : ['compute-0' , 'dcn1-compute-0' ]})
201
+ self .assertEqual (2 , len (agents ))
0 commit comments