blob: c7147e0d727e7d06592da58ecacf4a57786d1cf7 [file] [log] [blame]
Wei-Yu Chen49950b92021-11-08 19:19:18 +08001"""
2Copyright 2020 The Magma Authors.
3
4This source code is licensed under the BSD-style license found in the
5LICENSE file in the root directory of this source tree.
6
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12"""
13
14# pylint: disable=protected-access
15from unittest import TestCase
16
17from state_machines.enb_acs_manager import StateMachineManager
18from tests.test_utils.enb_acs_builder import (
19 EnodebAcsStateMachineBuilder,
20)
21from tests.test_utils.spyne_builder import (
22 get_spyne_context_with_ip,
23)
24from tests.test_utils.tr069_msg_builder import Tr069MessageBuilder
25from tr069 import models
26
27
28class StateMachineManagerTests(TestCase):
29 def test_handle_one_ip(self):
30 manager = self._get_manager()
31
32 # Send in an Inform message, and we should get an InformResponse
33 ctx = get_spyne_context_with_ip()
34 inform = Tr069MessageBuilder.get_inform()
35 req = manager.handle_tr069_message(ctx, inform)
36 self.assertTrue(
37 isinstance(req, models.InformResponse),
38 'State machine handler should reply with an '
39 'InformResponse',
40 )
41
42 def test_serial_not_found(self):
43 """
44 Test that the SM manager doesn't crash if serial number is not found
45 in an Inform message under any expected param path.
46 """
47 manager = self._get_manager()
48 ctx = get_spyne_context_with_ip("192.168.60.145")
49 inform_msg = models.Inform(
50 DeviceId=models.DeviceIdStruct(
51 Manufacturer='Unused',
52 OUI='48BF74',
53 ProductClass='Unused',
54 ),
55 Event=models.EventList(EventStruct=[]),
56 ParameterList=models.ParameterValueList(
57 ParameterValueStruct=[
58 Tr069MessageBuilder.get_parameter_value_struct(
59 name='Device.DeviceInfo.HardwareVersion',
60 val_type='string',
61 data='VER.C',
62 ),
63 Tr069MessageBuilder.get_parameter_value_struct(
64 name='Device.DeviceInfo.ManufacturerOUI',
65 val_type='string',
66 data='48BF74',
67 ),
68 Tr069MessageBuilder.get_parameter_value_struct(
69 name='Device.DeviceInfo.SoftwareVersion',
70 val_type='string',
71 data='BaiBS_RTS_3.1.6',
72 ),
73 ],
74 ),
75 )
76
77 # No exception should be thrown, and we should return an empty response
78 resp = manager.handle_tr069_message(ctx, inform_msg)
79 self.assertTrue(isinstance(resp, models.DummyInput))
80
81 def test_handle_two_ips(self):
82 manager = self._get_manager()
83 ctx1 = get_spyne_context_with_ip("192.168.60.145")
84 ctx2 = get_spyne_context_with_ip("192.168.60.99")
85
86 ##### Start session for the first IP #####
87 # Send an Inform message, wait for an InformResponse
88 inform_msg = Tr069MessageBuilder.get_inform(
89 '48BF74',
90 'BaiBS_RTS_3.1.6',
91 '120200002618AGP0001',
92 )
93 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
94 self.assertTrue(
95 isinstance(resp1, models.InformResponse),
96 'Should respond with an InformResponse',
97 )
98
99 # Send an empty http request to kick off the rest of provisioning
100 req1 = models.DummyInput()
101 resp1 = manager.handle_tr069_message(ctx1, req1)
102
103 # Expect a request for an optional parameter, three times
104 self.assertTrue(
105 isinstance(resp1, models.GetParameterValues),
106 'State machine should be requesting param values',
107 )
108 req1 = Tr069MessageBuilder.get_fault()
109 resp1 = manager.handle_tr069_message(ctx1, req1)
110 self.assertTrue(
111 isinstance(resp1, models.GetParameterValues),
112 'State machine should be requesting param values',
113 )
114
115 ##### Start session for the second IP #####
116 # Send an Inform message, wait for an InformResponse
117 inform_msg = Tr069MessageBuilder.get_inform(
118 '48BF74',
119 'BaiBS_RTS_3.1.6',
120 '120200002618AGP0002',
121 )
122 resp2 = manager.handle_tr069_message(ctx2, inform_msg)
123 self.assertTrue(
124 isinstance(resp2, models.InformResponse),
125 'Should respond with an InformResponse',
126 )
127
128 ##### Continue session for the first IP #####
129 req1 = Tr069MessageBuilder.get_fault()
130 resp1 = manager.handle_tr069_message(ctx1, req1)
131 self.assertTrue(
132 isinstance(resp1, models.GetParameterValues),
133 'State machine should be requesting param values',
134 )
135 req1 = Tr069MessageBuilder.get_fault()
136 resp1 = manager.handle_tr069_message(ctx1, req1)
137 # Expect a request for read-only params
138 self.assertTrue(
139 isinstance(resp1, models.GetParameterValues),
140 'State machine should be requesting param values',
141 )
142
143 ##### Continue session for the second IP #####
144 # Send an empty http request to kick off the rest of provisioning
145 req2 = models.DummyInput()
146 resp2 = manager.handle_tr069_message(ctx2, req2)
147 # Expect a request for an optional parameter, three times
148 self.assertTrue(
149 isinstance(resp2, models.GetParameterValues),
150 'State machine should be requesting param values',
151 )
152 req2 = Tr069MessageBuilder.get_fault()
153 resp2 = manager.handle_tr069_message(ctx2, req2)
154 self.assertTrue(
155 isinstance(resp2, models.GetParameterValues),
156 'State machine should be requesting param values',
157 )
158 req2 = Tr069MessageBuilder.get_fault()
159 resp2 = manager.handle_tr069_message(ctx2, req2)
160 self.assertTrue(
161 isinstance(resp2, models.GetParameterValues),
162 'State machine should be requesting param values',
163 )
164 req2 = Tr069MessageBuilder.get_fault()
165 resp2 = manager.handle_tr069_message(ctx2, req2)
166 # Expect a request for read-only params
167 self.assertTrue(
168 isinstance(resp2, models.GetParameterValues),
169 'State machine should be requesting param values',
170 )
171
172 def test_handle_registered_enb(self):
173 """
174 When we have a config with eNB registered per serial, we should accept
175 TR-069 sessions from any registered eNB, and ereject from unregistered
176 eNB devices.
177 """
178 manager = self._get_manager_multi_enb()
179 ip1 = "192.168.60.145"
180 ctx1 = get_spyne_context_with_ip(ip1)
181 inform_msg = Tr069MessageBuilder.get_inform(
182 '48BF74',
183 'BaiBS_RTS_3.1.6',
184 '120200002618AGP0003',
185 )
186 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
187 self.assertTrue(
188 isinstance(resp1, models.InformResponse),
189 'Should respond with an InformResponse',
190 )
191
192 ip2 = "192.168.60.146"
193 ctx2 = get_spyne_context_with_ip(ip2)
194 inform_msg = Tr069MessageBuilder.get_inform(
195 '48BF74',
196 'BaiBS_RTS_3.1.6',
197 'unregistered_serial',
198 )
199
200 resp2 = manager.handle_tr069_message(ctx2, inform_msg)
201 self.assertTrue(
202 isinstance(resp2, models.DummyInput),
203 'Should respond with an empty HTTP response',
204 )
205
206 def test_ip_change(self) -> None:
207 manager = self._get_manager()
208
209 # Send an Inform
210 ip1 = "192.168.60.145"
211 ctx1 = get_spyne_context_with_ip(ip1)
212 inform_msg = Tr069MessageBuilder.get_inform(
213 '48BF74',
214 'BaiBS_RTS_3.1.6',
215 '120200002618AGP0003',
216 )
217 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
218 self.assertTrue(
219 isinstance(resp1, models.InformResponse),
220 'Should respond with an InformResponse',
221 )
222 handler1 = manager.get_handler_by_ip(ip1)
223
224 # Send an Inform from the same serial, but different IP
225 ip2 = "192.168.60.99"
226 ctx2 = get_spyne_context_with_ip(ip2)
227 inform_msg = Tr069MessageBuilder.get_inform(
228 '48BF74',
229 'BaiBS_RTS_3.1.6',
230 '120200002618AGP0003',
231 )
232 resp2 = manager.handle_tr069_message(ctx2, inform_msg)
233 self.assertTrue(
234 isinstance(resp2, models.InformResponse),
235 'Should respond with an InformResponse',
236 )
237 handler2 = manager.get_handler_by_ip(ip2)
238
239 # Now check that the serial is associated with the second ip
240 self.assertTrue(
241 (handler1 is handler2),
242 'After an IP switch, the manager should have moved '
243 'the handler to a new IP',
244 )
245
246 def test_serial_change(self) -> None:
247 manager = self._get_manager()
248 ip = "192.168.60.145"
249
250 # Send an Inform
251 ctx1 = get_spyne_context_with_ip(ip)
252 inform_msg = Tr069MessageBuilder.get_inform(
253 '48BF74',
254 'BaiBS_RTS_3.1.6',
255 '120200002618AGP0001',
256 )
257 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
258 self.assertTrue(
259 isinstance(resp1, models.InformResponse),
260 'Should respond with an InformResponse',
261 )
262 handler1 = manager.get_handler_by_ip(ip)
263
264 # Send an Inform from the same serial, but different IP
265 ctx2 = get_spyne_context_with_ip(ip)
266 inform_msg = Tr069MessageBuilder.get_inform(
267 '48BF74',
268 'BaiBS_RTS_3.1.6',
269 '120200002618AGP0002',
270 )
271 resp2 = manager.handle_tr069_message(ctx2, inform_msg)
272 self.assertTrue(
273 isinstance(resp2, models.InformResponse),
274 'Should respond with an InformResponse',
275 )
276 handler2 = manager.get_handler_by_ip(ip)
277
278 # Now check that the serial is associated with the second ip
279 self.assertTrue(
280 (handler1 is not handler2),
281 'After an IP switch, the manager should have moved '
282 'the handler to a new IP',
283 )
284
285 def test_inform_from_baicells_qafb(self) -> None:
286 manager = self._get_manager()
287 ip = "192.168.60.145"
288
289 # Send an Inform
290 ctx1 = get_spyne_context_with_ip(ip)
291 inform_msg = Tr069MessageBuilder.get_qafb_inform(
292 '48BF74',
293 'BaiBS_QAFB_v1234',
294 '120200002618AGP0001',
295 )
296 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
297 self.assertTrue(
298 isinstance(resp1, models.InformResponse),
299 'Should respond with an InformResponse',
300 )
301
302 def test_inform_from_unrecognized(self) -> None:
303 manager = self._get_manager()
304 ip = "192.168.60.145"
305
306 # Send an Inform
307 ctx1 = get_spyne_context_with_ip(ip)
308 inform_msg = Tr069MessageBuilder.get_qafb_inform(
309 '48BF74',
310 'Unrecognized device',
311 '120200002618AGP0001',
312 )
313 resp1 = manager.handle_tr069_message(ctx1, inform_msg)
314 self.assertTrue(
315 isinstance(resp1, models.DummyInput),
316 'Should end provisioninng session with empty response',
317 )
318
319 def _get_manager(self) -> StateMachineManager:
320 service = EnodebAcsStateMachineBuilder.build_magma_service()
321 return StateMachineManager(service)
322
323 def _get_manager_multi_enb(self) -> StateMachineManager:
324 service = EnodebAcsStateMachineBuilder.build_multi_enb_magma_service()
325 return StateMachineManager(service)