Newer
Older
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
#
# 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.
"""
P4 unit tests.
"""
import copy
import logging
import operator
import grpc
import pytest
from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceId,\
DeviceOperationalStatusEnum
from common.tools.grpc.Tools import grpc_message_to_json_string
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from device.service.DeviceService import DeviceService
from device.service.driver_api._Driver import _Driver
from .PrepareTestScenario import ( # pylint: disable=unused-import
mock_service, device_service, context_client, device_client,
monitoring_client, test_prepare_environment)
from .mock_p4runtime_service import MockP4RuntimeService
try:
from .device_p4 import(
DEVICE_P4, DEVICE_P4_ID, DEVICE_P4_UUID,
DEVICE_P4_IP_ADDR, DEVICE_P4_PORT, DEVICE_P4_WORKERS,
DEVICE_P4_GRACE_PERIOD, DEVICE_P4_CONNECT_RULES,
DEVICE_P4_CONFIG_TABLE_ENTRY, DEVICE_P4_DECONFIG_TABLE_ENTRY)
ENABLE_P4 = True
except ImportError:
ENABLE_P4 = False
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG)
@pytest.fixture(scope='session')
def p4runtime_service():
"""
Spawn a mock P4Runtime server.
:return: void
"""
address=DEVICE_P4_IP_ADDR,
port=DEVICE_P4_PORT,
max_workers=DEVICE_P4_WORKERS,
grace_period=DEVICE_P4_GRACE_PERIOD)
_service.start()
yield _service
_service.stop()
# ----- Test Device Driver P4 --------------------------------------------------
def test_device_p4_add_error_cases(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService): # pylint: disable=redefined-outer-name
"""
Test AddDevice RPC with wrong inputs.
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:return:
"""
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
with pytest.raises(grpc.RpcError) as ex:
device_p4_with_extra_rules = copy.deepcopy(DEVICE_P4)
device_p4_with_extra_rules['device_config']['config_rules'].extend(
DEVICE_P4_CONNECT_RULES)
device_p4_with_extra_rules['device_config']['config_rules'].extend(
device_client.AddDevice(Device(**device_p4_with_extra_rules))
assert ex.value.code() == grpc.StatusCode.INVALID_ARGUMENT
msg_tail = ']) is invalid; RPC method AddDevice only accepts connection '\
'Config Rules that should start with "_connect/" tag. '\
'Others should be configured after adding the device.'
except_msg = str(ex.value.details())
assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail)
def test_device_p4_add_correct(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService, # pylint: disable=redefined-outer-name
p4runtime_service: MockP4RuntimeService): # pylint: disable=redefined-outer-name
"""
Test AddDevice RPC with correct inputs.
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:param p4runtime_service: Mock P4Runtime service
:return:
"""
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
device_p4_with_connect_rules = copy.deepcopy(DEVICE_P4)
device_p4_with_connect_rules['device_config']['config_rules'].extend(
DEVICE_P4_CONNECT_RULES)
device_client.AddDevice(Device(**device_p4_with_connect_rules))
driver_instance_cache = device_service.device_servicer.driver_instance_cache
driver: _Driver = driver_instance_cache.get(DEVICE_P4_UUID)
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
config_rules = [
(
ConfigActionEnum.Name(config_rule.action),
config_rule.custom.resource_key,
config_rule.custom.resource_value
)
for config_rule in device_data.device_config.config_rules
if config_rule.WhichOneof('config_rule') == 'custom'
]
LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
'\n'.join(['{:s} {:s} = {:s}'.format(*config_rule)
for config_rule in config_rules])))
def test_device_p4_get(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService, # pylint: disable=redefined-outer-name
p4runtime_service: MockP4RuntimeService): # pylint: disable=redefined-outer-name
"""
Test GetDevice RPC.
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:param p4runtime_service: Mock P4Runtime service
:return:
"""
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_P4_ID))
assert len(initial_config.config_rules) == 0
LOGGER.info('initial_config = %s',
grpc_message_to_json_string(initial_config))
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
LOGGER.info('device_data = %s', grpc_message_to_json_string(device_data))
def test_device_p4_configure(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService, # pylint: disable=redefined-outer-name
p4runtime_service: MockP4RuntimeService): # pylint: disable=redefined-outer-name
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:param p4runtime_service: Mock P4Runtime service
:return:
"""
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
driver_instance_cache = device_service.device_servicer.driver_instance_cache
driver: _Driver = driver_instance_cache.get(DEVICE_P4_UUID)
assert driver is not None
# No entries should exist at this point in time
driver_config = sorted(driver.GetConfig(), key=operator.itemgetter(0))
assert len(driver_config) == len(driver.get_manager().get_resource_keys())
assert driver.get_manager().count_active_entries() == 0
# Flip the operational status and check it is correctly flipped in Context
device_p4_with_operational_status = copy.deepcopy(DEVICE_P4)
device_p4_with_operational_status['device_operational_status'] = \
DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
device_client.ConfigureDevice(Device(**device_p4_with_operational_status))
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
assert device_data.device_operational_status == \
DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
# Insert a new table entry
device_p4_with_config_rules = copy.deepcopy(DEVICE_P4)
device_p4_with_config_rules['device_config']['config_rules'].extend(
DEVICE_P4_CONFIG_TABLE_ENTRY)
device_client.ConfigureDevice(Device(**device_p4_with_config_rules))
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
config_rules = [
(ConfigActionEnum.Name(config_rule.action),
config_rule.custom.resource_key,
config_rule.custom.resource_value)
for config_rule in device_data.device_config.config_rules
if config_rule.WhichOneof('config_rule') == 'custom'
]
LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
'\n'.join(
['{:s} {:s} = {:s}'.format(*config_rule)
for config_rule in config_rules]))
)
for config_rule in DEVICE_P4_CONFIG_TABLE_ENTRY:
assert 'custom' in config_rule
def test_device_p4_deconfigure(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService, # pylint: disable=redefined-outer-name
p4runtime_service: MockP4RuntimeService): # pylint: disable=redefined-outer-name
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:param p4runtime_service: Mock P4Runtime service
:return:
"""
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
driver_instance_cache = device_service.device_servicer.driver_instance_cache
driver: _Driver = driver_instance_cache.get(DEVICE_P4_UUID)
assert driver is not None
# Delete a table entry
device_p4_with_config_rules = copy.deepcopy(DEVICE_P4)
device_p4_with_config_rules['device_config']['config_rules'].extend(
DEVICE_P4_DECONFIG_TABLE_ENTRY)
device_client.ConfigureDevice(Device(**device_p4_with_config_rules))
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
config_rules = [
(ConfigActionEnum.Name(config_rule.action),
config_rule.custom.resource_key,
config_rule.custom.resource_value)
for config_rule in device_data.device_config.config_rules
if config_rule.WhichOneof('config_rule') == 'custom'
]
LOGGER.info('device_data.device_config.config_rules = \n{:s}'.format(
'\n'.join(
['{:s} {:s} = {:s}'.format(*config_rule)
for config_rule in config_rules]))
)
for config_rule in DEVICE_P4_CONFIG_TABLE_ENTRY:
assert 'custom' in config_rule
# Flip the operational status and check it is correctly flipped in Context
device_p4_with_operational_status = copy.deepcopy(DEVICE_P4)
device_p4_with_operational_status['device_operational_status'] = \
DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
device_client.ConfigureDevice(Device(**device_p4_with_operational_status))
device_data = context_client.GetDevice(DeviceId(**DEVICE_P4_ID))
assert device_data.device_operational_status == \
DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
def test_device_p4_delete(
context_client: ContextClient, # pylint: disable=redefined-outer-name
device_client: DeviceClient, # pylint: disable=redefined-outer-name
device_service: DeviceService, # pylint: disable=redefined-outer-name
p4runtime_service: MockP4RuntimeService): # pylint: disable=redefined-outer-name
"""
Test DeleteDevice RPC.
:param context_client: context component client
:param device_client: device component client
:param device_service: device component service
:param p4runtime_service: Mock P4Runtime service
:return:
"""
if not ENABLE_P4:
pytest.skip('Skipping test: No P4 device has been configured')
device_client.DeleteDevice(DeviceId(**DEVICE_P4_ID))
driver_instance_cache = device_service.device_servicer.driver_instance_cache
driver: _Driver = driver_instance_cache.get(DEVICE_P4_UUID)