blob: c7147e0d727e7d06592da58ecacf4a57786d1cf7 [file] [log] [blame]
"""
Copyright 2020 The Magma Authors.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
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.
"""
# pylint: disable=protected-access
from unittest import TestCase
from state_machines.enb_acs_manager import StateMachineManager
from tests.test_utils.enb_acs_builder import (
EnodebAcsStateMachineBuilder,
)
from tests.test_utils.spyne_builder import (
get_spyne_context_with_ip,
)
from tests.test_utils.tr069_msg_builder import Tr069MessageBuilder
from tr069 import models
class StateMachineManagerTests(TestCase):
def test_handle_one_ip(self):
manager = self._get_manager()
# Send in an Inform message, and we should get an InformResponse
ctx = get_spyne_context_with_ip()
inform = Tr069MessageBuilder.get_inform()
req = manager.handle_tr069_message(ctx, inform)
self.assertTrue(
isinstance(req, models.InformResponse),
'State machine handler should reply with an '
'InformResponse',
)
def test_serial_not_found(self):
"""
Test that the SM manager doesn't crash if serial number is not found
in an Inform message under any expected param path.
"""
manager = self._get_manager()
ctx = get_spyne_context_with_ip("192.168.60.145")
inform_msg = models.Inform(
DeviceId=models.DeviceIdStruct(
Manufacturer='Unused',
OUI='48BF74',
ProductClass='Unused',
),
Event=models.EventList(EventStruct=[]),
ParameterList=models.ParameterValueList(
ParameterValueStruct=[
Tr069MessageBuilder.get_parameter_value_struct(
name='Device.DeviceInfo.HardwareVersion',
val_type='string',
data='VER.C',
),
Tr069MessageBuilder.get_parameter_value_struct(
name='Device.DeviceInfo.ManufacturerOUI',
val_type='string',
data='48BF74',
),
Tr069MessageBuilder.get_parameter_value_struct(
name='Device.DeviceInfo.SoftwareVersion',
val_type='string',
data='BaiBS_RTS_3.1.6',
),
],
),
)
# No exception should be thrown, and we should return an empty response
resp = manager.handle_tr069_message(ctx, inform_msg)
self.assertTrue(isinstance(resp, models.DummyInput))
def test_handle_two_ips(self):
manager = self._get_manager()
ctx1 = get_spyne_context_with_ip("192.168.60.145")
ctx2 = get_spyne_context_with_ip("192.168.60.99")
##### Start session for the first IP #####
# Send an Inform message, wait for an InformResponse
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0001',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.InformResponse),
'Should respond with an InformResponse',
)
# Send an empty http request to kick off the rest of provisioning
req1 = models.DummyInput()
resp1 = manager.handle_tr069_message(ctx1, req1)
# Expect a request for an optional parameter, three times
self.assertTrue(
isinstance(resp1, models.GetParameterValues),
'State machine should be requesting param values',
)
req1 = Tr069MessageBuilder.get_fault()
resp1 = manager.handle_tr069_message(ctx1, req1)
self.assertTrue(
isinstance(resp1, models.GetParameterValues),
'State machine should be requesting param values',
)
##### Start session for the second IP #####
# Send an Inform message, wait for an InformResponse
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0002',
)
resp2 = manager.handle_tr069_message(ctx2, inform_msg)
self.assertTrue(
isinstance(resp2, models.InformResponse),
'Should respond with an InformResponse',
)
##### Continue session for the first IP #####
req1 = Tr069MessageBuilder.get_fault()
resp1 = manager.handle_tr069_message(ctx1, req1)
self.assertTrue(
isinstance(resp1, models.GetParameterValues),
'State machine should be requesting param values',
)
req1 = Tr069MessageBuilder.get_fault()
resp1 = manager.handle_tr069_message(ctx1, req1)
# Expect a request for read-only params
self.assertTrue(
isinstance(resp1, models.GetParameterValues),
'State machine should be requesting param values',
)
##### Continue session for the second IP #####
# Send an empty http request to kick off the rest of provisioning
req2 = models.DummyInput()
resp2 = manager.handle_tr069_message(ctx2, req2)
# Expect a request for an optional parameter, three times
self.assertTrue(
isinstance(resp2, models.GetParameterValues),
'State machine should be requesting param values',
)
req2 = Tr069MessageBuilder.get_fault()
resp2 = manager.handle_tr069_message(ctx2, req2)
self.assertTrue(
isinstance(resp2, models.GetParameterValues),
'State machine should be requesting param values',
)
req2 = Tr069MessageBuilder.get_fault()
resp2 = manager.handle_tr069_message(ctx2, req2)
self.assertTrue(
isinstance(resp2, models.GetParameterValues),
'State machine should be requesting param values',
)
req2 = Tr069MessageBuilder.get_fault()
resp2 = manager.handle_tr069_message(ctx2, req2)
# Expect a request for read-only params
self.assertTrue(
isinstance(resp2, models.GetParameterValues),
'State machine should be requesting param values',
)
def test_handle_registered_enb(self):
"""
When we have a config with eNB registered per serial, we should accept
TR-069 sessions from any registered eNB, and ereject from unregistered
eNB devices.
"""
manager = self._get_manager_multi_enb()
ip1 = "192.168.60.145"
ctx1 = get_spyne_context_with_ip(ip1)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0003',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.InformResponse),
'Should respond with an InformResponse',
)
ip2 = "192.168.60.146"
ctx2 = get_spyne_context_with_ip(ip2)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'unregistered_serial',
)
resp2 = manager.handle_tr069_message(ctx2, inform_msg)
self.assertTrue(
isinstance(resp2, models.DummyInput),
'Should respond with an empty HTTP response',
)
def test_ip_change(self) -> None:
manager = self._get_manager()
# Send an Inform
ip1 = "192.168.60.145"
ctx1 = get_spyne_context_with_ip(ip1)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0003',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.InformResponse),
'Should respond with an InformResponse',
)
handler1 = manager.get_handler_by_ip(ip1)
# Send an Inform from the same serial, but different IP
ip2 = "192.168.60.99"
ctx2 = get_spyne_context_with_ip(ip2)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0003',
)
resp2 = manager.handle_tr069_message(ctx2, inform_msg)
self.assertTrue(
isinstance(resp2, models.InformResponse),
'Should respond with an InformResponse',
)
handler2 = manager.get_handler_by_ip(ip2)
# Now check that the serial is associated with the second ip
self.assertTrue(
(handler1 is handler2),
'After an IP switch, the manager should have moved '
'the handler to a new IP',
)
def test_serial_change(self) -> None:
manager = self._get_manager()
ip = "192.168.60.145"
# Send an Inform
ctx1 = get_spyne_context_with_ip(ip)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0001',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.InformResponse),
'Should respond with an InformResponse',
)
handler1 = manager.get_handler_by_ip(ip)
# Send an Inform from the same serial, but different IP
ctx2 = get_spyne_context_with_ip(ip)
inform_msg = Tr069MessageBuilder.get_inform(
'48BF74',
'BaiBS_RTS_3.1.6',
'120200002618AGP0002',
)
resp2 = manager.handle_tr069_message(ctx2, inform_msg)
self.assertTrue(
isinstance(resp2, models.InformResponse),
'Should respond with an InformResponse',
)
handler2 = manager.get_handler_by_ip(ip)
# Now check that the serial is associated with the second ip
self.assertTrue(
(handler1 is not handler2),
'After an IP switch, the manager should have moved '
'the handler to a new IP',
)
def test_inform_from_baicells_qafb(self) -> None:
manager = self._get_manager()
ip = "192.168.60.145"
# Send an Inform
ctx1 = get_spyne_context_with_ip(ip)
inform_msg = Tr069MessageBuilder.get_qafb_inform(
'48BF74',
'BaiBS_QAFB_v1234',
'120200002618AGP0001',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.InformResponse),
'Should respond with an InformResponse',
)
def test_inform_from_unrecognized(self) -> None:
manager = self._get_manager()
ip = "192.168.60.145"
# Send an Inform
ctx1 = get_spyne_context_with_ip(ip)
inform_msg = Tr069MessageBuilder.get_qafb_inform(
'48BF74',
'Unrecognized device',
'120200002618AGP0001',
)
resp1 = manager.handle_tr069_message(ctx1, inform_msg)
self.assertTrue(
isinstance(resp1, models.DummyInput),
'Should end provisioninng session with empty response',
)
def _get_manager(self) -> StateMachineManager:
service = EnodebAcsStateMachineBuilder.build_magma_service()
return StateMachineManager(service)
def _get_manager_multi_enb(self) -> StateMachineManager:
service = EnodebAcsStateMachineBuilder.build_multi_enb_magma_service()
return StateMachineManager(service)