Loading proto/qos_profile.proto +13 −6 Original line number Diff line number Diff line Loading @@ -47,12 +47,19 @@ message QoSProfile { int32 packetErrorLossRate = 16; } message QoSProfileList { repeated QoSProfile qos_profiles = 1; } message ConstraintList { repeated context.Constraint constraints = 1; } service QoSProfileService { rpc CreateQoSProfile (QoSProfile ) returns (QoSProfile ) {} rpc UpdateQoSProfile (QoSProfile ) returns (QoSProfile ) {} rpc DeleteQoSProfile (context.QoSProfileId ) returns (context.Empty ) {} rpc GetQoSProfile (context.QoSProfileId ) returns (QoSProfile ) {} rpc GetQoSProfiles (context.Empty ) returns (stream QoSProfile ) {} rpc GetConstraintListFromQoSProfile (QoDConstraintsRequest) returns (stream context.Constraint) {} rpc GetQoSProfiles (context.Empty ) returns (QoSProfileList) {} rpc GetConstraintsFromQoSProfile(QoDConstraintsRequest) returns (ConstraintList) {} } src/common/tests/MockServicerImpl_QoSProfile.py 0 → 100644 +75 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI 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. import grpc, logging from typing import Iterator from common.proto.context_pb2 import Constraint, Empty, QoSProfileId from common.proto.qos_profile_pb2 import ConstraintList, QoDConstraintsRequest, QoSProfile, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceServicer from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Constraint import json_constraint_qos_profile, json_constraint_schedule from .InMemoryObjectDatabase import InMemoryObjectDatabase LOGGER = logging.getLogger(__name__) class MockServicerImpl_QoSProfile(QoSProfileServiceServicer): def __init__(self): LOGGER.debug('[__init__] Creating Servicer...') self.obj_db = InMemoryObjectDatabase() LOGGER.debug('[__init__] Servicer Created') def GetQoSProfiles(self, request : Empty, context : grpc.ServicerContext) -> QoSProfileList: LOGGER.debug('[GetQoSProfiles] request={:s}'.format(grpc_message_to_json_string(request))) reply = QoSProfileList(qos_profiles=self.obj_db.get_entries('qos_profile')) LOGGER.debug('[GetQoSProfiles] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def GetQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[GetQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.get_entry('qos_profile', request.qos_profile_id.uuid, context) LOGGER.debug('[GetQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def CreateQoSProfile(self, request : QoSProfile, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[CreateQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.set_entry('qos_profile', request.qos_profile_id.qos_profile_id.uuid, request) LOGGER.debug('[CreateQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def UpdateQoSProfile(self, request : QoSProfile, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[UpdateQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.set_entry('qos_profile', request.qos_profile_id.qos_profile_id.uuid, request) LOGGER.debug('[UpdateQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def DeleteQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> Empty: LOGGER.debug('[DeleteQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) self.obj_db.del_entry('qos_profile', request.qos_profile_id.uuid, context) reply = Empty() LOGGER.debug('[DeleteQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def GetConstraintsFromQoSProfile( self, request: QoDConstraintsRequest, context: grpc.ServicerContext ) -> ConstraintList: LOGGER.debug('[GetConstraintsFromQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) qos_profile = self.obj_db.get_entry( 'qos_profile', request.qos_profile_id.qos_profile_id.uuid, context ) reply = ConstraintList(constraints=[ Constraint(**json_constraint_qos_profile(qos_profile.qos_profile_id, qos_profile.name)), Constraint(**json_constraint_schedule(request.start_timestamp, request.duration / 86400)), ]) LOGGER.debug('[GetConstraintsFromQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply src/qos_profile/client/QoSProfileClient.py +6 −8 Original line number Diff line number Diff line Loading @@ -12,13 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Iterator import grpc, logging from common.Constants import ServiceNameEnum from common.Settings import get_service_host, get_service_port_grpc from common.proto.context_pb2 import Empty, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfile, QoDConstraintsRequest from common.proto.context_pb2 import Constraint from common.proto.qos_profile_pb2 import ConstraintList, QoSProfile, QoDConstraintsRequest, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceStub from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string Loading Loading @@ -77,15 +75,15 @@ class QoSProfileClient: return response @RETRY_DECORATOR def GetQoSProfiles(self, request: Empty) -> Iterator[QoSProfile]: def GetQoSProfiles(self, request: Empty) -> QoSProfileList: LOGGER.debug('GetQoSProfiles request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetQoSProfiles(request) LOGGER.debug('GetQoSProfiles result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetConstraintListFromQoSProfile(self, request: QoDConstraintsRequest) -> Iterator[Constraint]: LOGGER.debug('GetConstraintListFromQoSProfile request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConstraintListFromQoSProfile(request) LOGGER.debug('GetConstraintListFromQoSProfile result: {:s}'.format(grpc_message_to_json_string(response))) def GetConstraintsFromQoSProfile(self, request: QoDConstraintsRequest) -> ConstraintList: LOGGER.debug('GetConstraintsFromQoSProfile request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConstraintsFromQoSProfile(request) #LOGGER.debug('GetConstraintsFromQoSProfile result: {:s}'.format(grpc_message_to_json_string(response))) return response src/qos_profile/service/QoSProfileServiceServicerImpl.py +17 −21 Original line number Diff line number Diff line Loading @@ -13,13 +13,12 @@ # limitations under the License. import grpc, logging, sqlalchemy from typing import Iterator import grpc._channel from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import Constraint, ConstraintActionEnum, Constraint_QoSProfile, Constraint_Schedule, Empty, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfile, QoDConstraintsRequest from common.proto.context_pb2 import Constraint, Empty, QoSProfileId from common.proto.qos_profile_pb2 import ConstraintList, QoSProfile, QoDConstraintsRequest, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceServicer from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Constraint import json_constraint_qos_profile, json_constraint_schedule from .database.QoSProfile import set_qos_profile, delete_qos_profile, get_qos_profile, get_qos_profiles Loading Loading @@ -71,26 +70,23 @@ class QoSProfileServiceServicerImpl(QoSProfileServiceServicer): return qos_profile @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetQoSProfiles(self, request: Empty, context: grpc.ServicerContext) -> Iterator[QoSProfile]: yield from get_qos_profiles(self.db_engine, request) def GetQoSProfiles(self, request: Empty, context: grpc.ServicerContext) -> QoSProfileList: return QoSProfileList(qos_profiles=get_qos_profiles(self.db_engine, request)) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetConstraintListFromQoSProfile(self, request: QoDConstraintsRequest, context: grpc.ServicerContext) -> Iterator[Constraint]: def GetConstraintsFromQoSProfile( self, request: QoDConstraintsRequest, context: grpc.ServicerContext ) -> ConstraintList: LOGGER.debug('[GetConstraintsFromQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) qos_profile = get_qos_profile(self.db_engine, request.qos_profile_id.qos_profile_id.uuid) if qos_profile is None: context.set_details(f'QoSProfile {request.qos_profile_id.qos_profile_id.uuid} not found') context.set_code(grpc.StatusCode.NOT_FOUND) yield Constraint() return ConstraintList() qos_profile_constraint = Constraint_QoSProfile() qos_profile_constraint.qos_profile_name = qos_profile.name qos_profile_constraint.qos_profile_id.CopyFrom(qos_profile.qos_profile_id) constraint_qos = Constraint() constraint_qos.action = ConstraintActionEnum.CONSTRAINTACTION_SET constraint_qos.qos_profile.CopyFrom(qos_profile_constraint) yield constraint_qos constraint_schedule = Constraint() constraint_schedule.action = ConstraintActionEnum.CONSTRAINTACTION_SET constraint_schedule.schedule.CopyFrom(Constraint_Schedule(start_timestamp=request.start_timestamp, duration_days=request.duration/86400)) yield constraint_schedule reply = ConstraintList(constraints=[ Constraint(**json_constraint_qos_profile(qos_profile.qos_profile_id, qos_profile.name)), Constraint(**json_constraint_schedule(request.start_timestamp, request.duration)), ]) LOGGER.debug('[GetConstraintsFromQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply src/qos_profile/tests/test_constraints.py +10 −6 Original line number Diff line number Diff line Loading @@ -78,15 +78,19 @@ def test_get_constraints(qos_profile_client: QoSProfileClient): qos_profile = create_qos_profile_from_json(qos_profile_data) qos_profile_created = qos_profile_client.CreateQoSProfile(qos_profile) LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profile_created))) constraints = list(qos_profile_client.GetConstraintListFromQoSProfile(QoDConstraintsRequest( qos_profile_id=qos_profile.qos_profile_id, start_timestamp=1726063284.25332, duration=86400) )) constraint_1 = constraints[0] constraint_2 = constraints[1] assert len(constraints) == 2 constraints = qos_profile_client.GetConstraintsFromQoSProfile( QoDConstraintsRequest( qos_profile_id=qos_profile.qos_profile_id, start_timestamp=1726063284.25332, duration=86400 ) ) assert len(constraints.constraints) == 2 constraint_1 = constraints.constraints[0] assert constraint_1.WhichOneof('constraint') == 'qos_profile' assert constraint_1.qos_profile.qos_profile_id == qos_profile.qos_profile_id assert constraint_1.qos_profile.qos_profile_name == 'QCI_2_voice' constraint_2 = constraints.constraints[1] assert constraint_2.WhichOneof('constraint') == 'schedule' assert constraint_2.schedule.start_timestamp == 1726063284.25332 assert constraint_2.schedule.duration_days == 1 Loading Loading
proto/qos_profile.proto +13 −6 Original line number Diff line number Diff line Loading @@ -47,12 +47,19 @@ message QoSProfile { int32 packetErrorLossRate = 16; } message QoSProfileList { repeated QoSProfile qos_profiles = 1; } message ConstraintList { repeated context.Constraint constraints = 1; } service QoSProfileService { rpc CreateQoSProfile (QoSProfile ) returns (QoSProfile ) {} rpc UpdateQoSProfile (QoSProfile ) returns (QoSProfile ) {} rpc DeleteQoSProfile (context.QoSProfileId ) returns (context.Empty ) {} rpc GetQoSProfile (context.QoSProfileId ) returns (QoSProfile ) {} rpc GetQoSProfiles (context.Empty ) returns (stream QoSProfile ) {} rpc GetConstraintListFromQoSProfile (QoDConstraintsRequest) returns (stream context.Constraint) {} rpc GetQoSProfiles (context.Empty ) returns (QoSProfileList) {} rpc GetConstraintsFromQoSProfile(QoDConstraintsRequest) returns (ConstraintList) {} }
src/common/tests/MockServicerImpl_QoSProfile.py 0 → 100644 +75 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI 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. import grpc, logging from typing import Iterator from common.proto.context_pb2 import Constraint, Empty, QoSProfileId from common.proto.qos_profile_pb2 import ConstraintList, QoDConstraintsRequest, QoSProfile, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceServicer from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Constraint import json_constraint_qos_profile, json_constraint_schedule from .InMemoryObjectDatabase import InMemoryObjectDatabase LOGGER = logging.getLogger(__name__) class MockServicerImpl_QoSProfile(QoSProfileServiceServicer): def __init__(self): LOGGER.debug('[__init__] Creating Servicer...') self.obj_db = InMemoryObjectDatabase() LOGGER.debug('[__init__] Servicer Created') def GetQoSProfiles(self, request : Empty, context : grpc.ServicerContext) -> QoSProfileList: LOGGER.debug('[GetQoSProfiles] request={:s}'.format(grpc_message_to_json_string(request))) reply = QoSProfileList(qos_profiles=self.obj_db.get_entries('qos_profile')) LOGGER.debug('[GetQoSProfiles] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def GetQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[GetQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.get_entry('qos_profile', request.qos_profile_id.uuid, context) LOGGER.debug('[GetQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def CreateQoSProfile(self, request : QoSProfile, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[CreateQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.set_entry('qos_profile', request.qos_profile_id.qos_profile_id.uuid, request) LOGGER.debug('[CreateQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def UpdateQoSProfile(self, request : QoSProfile, context : grpc.ServicerContext) -> QoSProfile: LOGGER.debug('[UpdateQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) reply = self.obj_db.set_entry('qos_profile', request.qos_profile_id.qos_profile_id.uuid, request) LOGGER.debug('[UpdateQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def DeleteQoSProfile(self, request : QoSProfileId, context : grpc.ServicerContext) -> Empty: LOGGER.debug('[DeleteQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) self.obj_db.del_entry('qos_profile', request.qos_profile_id.uuid, context) reply = Empty() LOGGER.debug('[DeleteQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply def GetConstraintsFromQoSProfile( self, request: QoDConstraintsRequest, context: grpc.ServicerContext ) -> ConstraintList: LOGGER.debug('[GetConstraintsFromQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) qos_profile = self.obj_db.get_entry( 'qos_profile', request.qos_profile_id.qos_profile_id.uuid, context ) reply = ConstraintList(constraints=[ Constraint(**json_constraint_qos_profile(qos_profile.qos_profile_id, qos_profile.name)), Constraint(**json_constraint_schedule(request.start_timestamp, request.duration / 86400)), ]) LOGGER.debug('[GetConstraintsFromQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply
src/qos_profile/client/QoSProfileClient.py +6 −8 Original line number Diff line number Diff line Loading @@ -12,13 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Iterator import grpc, logging from common.Constants import ServiceNameEnum from common.Settings import get_service_host, get_service_port_grpc from common.proto.context_pb2 import Empty, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfile, QoDConstraintsRequest from common.proto.context_pb2 import Constraint from common.proto.qos_profile_pb2 import ConstraintList, QoSProfile, QoDConstraintsRequest, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceStub from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string Loading Loading @@ -77,15 +75,15 @@ class QoSProfileClient: return response @RETRY_DECORATOR def GetQoSProfiles(self, request: Empty) -> Iterator[QoSProfile]: def GetQoSProfiles(self, request: Empty) -> QoSProfileList: LOGGER.debug('GetQoSProfiles request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetQoSProfiles(request) LOGGER.debug('GetQoSProfiles result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetConstraintListFromQoSProfile(self, request: QoDConstraintsRequest) -> Iterator[Constraint]: LOGGER.debug('GetConstraintListFromQoSProfile request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConstraintListFromQoSProfile(request) LOGGER.debug('GetConstraintListFromQoSProfile result: {:s}'.format(grpc_message_to_json_string(response))) def GetConstraintsFromQoSProfile(self, request: QoDConstraintsRequest) -> ConstraintList: LOGGER.debug('GetConstraintsFromQoSProfile request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConstraintsFromQoSProfile(request) #LOGGER.debug('GetConstraintsFromQoSProfile result: {:s}'.format(grpc_message_to_json_string(response))) return response
src/qos_profile/service/QoSProfileServiceServicerImpl.py +17 −21 Original line number Diff line number Diff line Loading @@ -13,13 +13,12 @@ # limitations under the License. import grpc, logging, sqlalchemy from typing import Iterator import grpc._channel from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import Constraint, ConstraintActionEnum, Constraint_QoSProfile, Constraint_Schedule, Empty, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfile, QoDConstraintsRequest from common.proto.context_pb2 import Constraint, Empty, QoSProfileId from common.proto.qos_profile_pb2 import ConstraintList, QoSProfile, QoDConstraintsRequest, QoSProfileList from common.proto.qos_profile_pb2_grpc import QoSProfileServiceServicer from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Constraint import json_constraint_qos_profile, json_constraint_schedule from .database.QoSProfile import set_qos_profile, delete_qos_profile, get_qos_profile, get_qos_profiles Loading Loading @@ -71,26 +70,23 @@ class QoSProfileServiceServicerImpl(QoSProfileServiceServicer): return qos_profile @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetQoSProfiles(self, request: Empty, context: grpc.ServicerContext) -> Iterator[QoSProfile]: yield from get_qos_profiles(self.db_engine, request) def GetQoSProfiles(self, request: Empty, context: grpc.ServicerContext) -> QoSProfileList: return QoSProfileList(qos_profiles=get_qos_profiles(self.db_engine, request)) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetConstraintListFromQoSProfile(self, request: QoDConstraintsRequest, context: grpc.ServicerContext) -> Iterator[Constraint]: def GetConstraintsFromQoSProfile( self, request: QoDConstraintsRequest, context: grpc.ServicerContext ) -> ConstraintList: LOGGER.debug('[GetConstraintsFromQoSProfile] request={:s}'.format(grpc_message_to_json_string(request))) qos_profile = get_qos_profile(self.db_engine, request.qos_profile_id.qos_profile_id.uuid) if qos_profile is None: context.set_details(f'QoSProfile {request.qos_profile_id.qos_profile_id.uuid} not found') context.set_code(grpc.StatusCode.NOT_FOUND) yield Constraint() return ConstraintList() qos_profile_constraint = Constraint_QoSProfile() qos_profile_constraint.qos_profile_name = qos_profile.name qos_profile_constraint.qos_profile_id.CopyFrom(qos_profile.qos_profile_id) constraint_qos = Constraint() constraint_qos.action = ConstraintActionEnum.CONSTRAINTACTION_SET constraint_qos.qos_profile.CopyFrom(qos_profile_constraint) yield constraint_qos constraint_schedule = Constraint() constraint_schedule.action = ConstraintActionEnum.CONSTRAINTACTION_SET constraint_schedule.schedule.CopyFrom(Constraint_Schedule(start_timestamp=request.start_timestamp, duration_days=request.duration/86400)) yield constraint_schedule reply = ConstraintList(constraints=[ Constraint(**json_constraint_qos_profile(qos_profile.qos_profile_id, qos_profile.name)), Constraint(**json_constraint_schedule(request.start_timestamp, request.duration)), ]) LOGGER.debug('[GetConstraintsFromQoSProfile] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply
src/qos_profile/tests/test_constraints.py +10 −6 Original line number Diff line number Diff line Loading @@ -78,15 +78,19 @@ def test_get_constraints(qos_profile_client: QoSProfileClient): qos_profile = create_qos_profile_from_json(qos_profile_data) qos_profile_created = qos_profile_client.CreateQoSProfile(qos_profile) LOGGER.info('qos_profile_data = {:s}'.format(grpc_message_to_json_string(qos_profile_created))) constraints = list(qos_profile_client.GetConstraintListFromQoSProfile(QoDConstraintsRequest( qos_profile_id=qos_profile.qos_profile_id, start_timestamp=1726063284.25332, duration=86400) )) constraint_1 = constraints[0] constraint_2 = constraints[1] assert len(constraints) == 2 constraints = qos_profile_client.GetConstraintsFromQoSProfile( QoDConstraintsRequest( qos_profile_id=qos_profile.qos_profile_id, start_timestamp=1726063284.25332, duration=86400 ) ) assert len(constraints.constraints) == 2 constraint_1 = constraints.constraints[0] assert constraint_1.WhichOneof('constraint') == 'qos_profile' assert constraint_1.qos_profile.qos_profile_id == qos_profile.qos_profile_id assert constraint_1.qos_profile.qos_profile_name == 'QCI_2_voice' constraint_2 = constraints.constraints[1] assert constraint_2.WhichOneof('constraint') == 'schedule' assert constraint_2.schedule.start_timestamp == 1726063284.25332 assert constraint_2.schedule.duration_days == 1 Loading