diff --git a/catalystwan/api/feature_profile_api.py b/catalystwan/api/feature_profile_api.py index f0cbed5c..c79a4365 100644 --- a/catalystwan/api/feature_profile_api.py +++ b/catalystwan/api/feature_profile_api.py @@ -14,6 +14,7 @@ from catalystwan.endpoints.configuration.feature_profile.sdwan.system import SystemFeatureProfile from catalystwan.endpoints.configuration.feature_profile.sdwan.topology import TopologyFeatureProfile from catalystwan.endpoints.configuration.feature_profile.sdwan.transport import TransportFeatureProfile +from catalystwan.endpoints.configuration.feature_profile.sdwan.uc_voice import UcVoiceFeatureProfile from catalystwan.exceptions import CatalystwanException, ManagerHTTPError from catalystwan.models.configuration.feature_profile.sdwan.acl.ipv4acl import Ipv4AclParcel from catalystwan.models.configuration.feature_profile.sdwan.acl.ipv6acl import Ipv6AclParcel @@ -106,6 +107,10 @@ InterfaceEthPPPoEParcel, ) from catalystwan.models.configuration.feature_profile.sdwan.transport.wan.interface.t1e1serial import T1E1SerialParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice import AnyUcVoiceParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice.dsp_farm import DspFarmParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice.media_profile import MediaProfileParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice.trunk_group import TrunkGroupParcel from catalystwan.typed_list import DataSequence if TYPE_CHECKING: @@ -193,17 +198,18 @@ def __init__(self, session: ManagerSession): class SDWANFeatureProfilesAPI: def __init__(self, session: ManagerSession): - self.policy_object = PolicyObjectFeatureProfileAPI(session=session) - self.system = SystemFeatureProfileAPI(session=session) + self.application_priority = ApplicationPriorityFeatureProfileAPI(session=session) + self.cli = CliFeatureProfileAPI(session=session) + self.dns_security = DnsSecurityFeatureProfileAPI(session=session) + self.embedded_security = EmbeddedSecurityFeatureProfileAPI(session=session) self.other = OtherFeatureProfileAPI(session=session) + self.policy_object = PolicyObjectFeatureProfileAPI(session=session) self.service = ServiceFeatureProfileAPI(session=session) + self.sig_security = SIGSecurityAPI(session=session) + self.system = SystemFeatureProfileAPI(session=session) self.topology = TopologyFeatureProfileAPI(session=session) self.transport = TransportFeatureProfileAPI(session=session) - self.embedded_security = EmbeddedSecurityFeatureProfileAPI(session=session) - self.cli = CliFeatureProfileAPI(session=session) - self.dns_security = DnsSecurityFeatureProfileAPI(session=session) - self.sig_security = SIGSecurityAPI(session=session) - self.application_priority = ApplicationPriorityFeatureProfileAPI(session=session) + self.uc_voice = UcVoiceFeatureProfileAPI(session=session) class FeatureProfileAPI(Protocol): @@ -2246,3 +2252,132 @@ def get_parcel( Get one Topology Parcel given profile id, parcel type and parcel id """ return self.endpoint.get_any_parcel_by_id(profile_id, parcel_type._get_parcel_type(), parcel_id) + + +class UcVoiceFeatureProfileAPI: + """ + SDWAN Feature Profile UC Voice APIs + """ + + def __init__(self, session: ManagerSession): + self.session = session + self.endpoint = UcVoiceFeatureProfile(session) + + def get_profiles( + self, limit: Optional[int] = None, offset: Optional[int] = None + ) -> DataSequence[FeatureProfileInfo]: + """ + Get all UC Voice Feature Profiles + """ + payload = GetFeatureProfilesParams(limit=limit if limit else None, offset=offset if offset else None) + + return self.endpoint.get_uc_voice_feature_profiles(payload) + + def create_profile(self, name: str, description: str) -> FeatureProfileCreationResponse: + """ + Create UC Voice Feature Profile + """ + payload = FeatureProfileCreationPayload(name=name, description=description) + return self.endpoint.create_uc_voice_feature_profile(payload) + + def delete_profile(self, profile_id: UUID) -> None: + """ + Delete UC Voice Feature Profile + """ + self.endpoint.delete_uc_voice_feature_profile(str(profile_id)) + + def delete_all_profiles(self) -> None: + """ + Delete all UC Voice Feature Profiles + """ + profiles = self.get_profiles() + for profile in profiles: + self.delete_profile(profile.profile_id) + + @overload + def get_parcels( + self, + profile_id: UUID, + parcel_type: Type[DspFarmParcel], + ) -> DataSequence[Parcel[DspFarmParcel]]: + ... + + @overload + def get_parcels( + self, + profile_id: UUID, + parcel_type: Type[MediaProfileParcel], + ) -> DataSequence[Parcel[MediaProfileParcel]]: + ... + + @overload + def get_parcels( + self, + profile_id: UUID, + parcel_type: Type[TrunkGroupParcel], + ) -> DataSequence[Parcel[TrunkGroupParcel]]: + ... + + def get_parcels(self, profile_id: UUID, parcel_type: Type[AnyUcVoiceParcel]) -> DataSequence[Parcel]: + """ + Get all UC Voice Parcels given profile id and parcel type + """ + return self.endpoint.get_all(profile_id, parcel_type._get_parcel_type()) + + @overload + def get_parcel( + self, + profile_id: UUID, + parcel_type: Type[DspFarmParcel], + parcel_id: UUID, + ) -> Parcel[DspFarmParcel]: + ... + + @overload + def get_parcel( + self, + profile_id: UUID, + parcel_type: Type[MediaProfileParcel], + parcel_id: UUID, + ) -> Parcel[MediaProfileParcel]: + ... + + @overload + def get_parcel( + self, + profile_id: UUID, + parcel_type: Type[TrunkGroupParcel], + parcel_id: UUID, + ) -> Parcel[TrunkGroupParcel]: + ... + + def get_parcel( + self, + profile_id: UUID, + parcel_type: Type[AnyUcVoiceParcel], + parcel_id: UUID, + ) -> Parcel: + """ + Get one UC Voice Parcel given profile id, parcel type and parcel id + """ + return self.endpoint.get_by_id(profile_id, parcel_type._get_parcel_type(), parcel_id) + + def create_parcel(self, profile_id: UUID, payload: AnyUcVoiceParcel) -> ParcelCreationResponse: + """ + Create UC Voice Parcel for selected profile_id based on payload type + """ + + return self.endpoint.create(profile_id, payload._get_parcel_type(), payload) + + def update_parcel(self, profile_id: UUID, payload: AnyUcVoiceParcel, parcel_id: UUID) -> ParcelCreationResponse: + """ + Update UC Voice Parcel for selected profile_id based on payload type + """ + + return self.endpoint.update(profile_id, payload._get_parcel_type(), parcel_id, payload) + + def delete_parcel(self, profile_id: UUID, parcel_type: Type[AnyUcVoiceParcel], parcel_id: UUID) -> None: + """ + Delete UC Voice Parcel for selected profile_id based on payload type + """ + return self.endpoint.delete(profile_id, parcel_type._get_parcel_type(), parcel_id) diff --git a/catalystwan/endpoints/configuration/feature_profile/sdwan/uc_voice.py b/catalystwan/endpoints/configuration/feature_profile/sdwan/uc_voice.py new file mode 100644 index 00000000..b12024d9 --- /dev/null +++ b/catalystwan/endpoints/configuration/feature_profile/sdwan/uc_voice.py @@ -0,0 +1,77 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates + +# mypy: disable-error-code="empty-body" +from uuid import UUID + +from catalystwan.endpoints import APIEndpoints, delete, get, post, put, versions +from catalystwan.models.configuration.feature_profile.common import ( + FeatureProfileCreationPayload, + FeatureProfileCreationResponse, + FeatureProfileDetail, + FeatureProfileEditPayload, + FeatureProfileInfo, + GetFeatureProfilesParams, +) +from catalystwan.models.configuration.feature_profile.parcel import Parcel, ParcelCreationResponse +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice import AnyUcVoiceParcel +from catalystwan.typed_list import DataSequence + + +class UcVoiceFeatureProfile(APIEndpoints): + @versions(supported_versions=(">=20.13"), raises=False) + @post("/v1/feature-profile/sdwan/uc-voice") + def create_uc_voice_feature_profile(self, payload: FeatureProfileCreationPayload) -> FeatureProfileCreationResponse: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @get("/v1/feature-profile/sdwan/uc-voice") + def get_uc_voice_feature_profiles(self, params: GetFeatureProfilesParams) -> DataSequence[FeatureProfileInfo]: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @get("/v1/feature-profile/sdwan/uc-voice/{profile_id}") + def get_uc_voice_feature_profile(self, profile_id: str, params: GetFeatureProfilesParams) -> FeatureProfileDetail: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @put("/v1/feature-profile/sdwan/uc-voice/{profile_id}") + def edit_uc_voice_feature_profile( + self, profile_id: str, payload: FeatureProfileEditPayload + ) -> FeatureProfileCreationResponse: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @delete("/v1/feature-profile/sdwan/uc-voice/{profile_id}") + def delete_uc_voice_feature_profile(self, profile_id: str) -> None: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @post("/v1/feature-profile/sdwan/uc-voice/{profile_id}/{uc_voice_list_type}") + def create(self, profile_id: UUID, uc_voice_list_type: str, payload: AnyUcVoiceParcel) -> ParcelCreationResponse: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @delete("/v1/feature-profile/sdwan/uc-voice/{profile_id}/{uc_voice_list_type}/{parcel_id}") + def delete(self, profile_id: UUID, uc_voice_list_type: str, parcel_id: UUID) -> None: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @put("/v1/feature-profile/sdwan/uc-voice/{profile_id}/{uc_voice_list_type}/{parcel_id}") + def update( + self, + profile_id: UUID, + uc_voice_list_type: str, + parcel_id: UUID, + payload: AnyUcVoiceParcel, + ) -> ParcelCreationResponse: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @get("/v1/feature-profile/sdwan/uc-voice/{profile_id}/{uc_voice_list_type}/{parcel_id}") + def get_by_id(self, profile_id: UUID, uc_voice_list_type: str, parcel_id: UUID) -> Parcel[AnyUcVoiceParcel]: + ... + + @versions(supported_versions=(">=20.13"), raises=False) + @get("/v1/feature-profile/sdwan/uc-voice/{profile_id}/{uc_voice_list_type}", resp_json_key="data") + def get_all(self, profile_id: UUID, uc_voice_list_type: str) -> DataSequence[Parcel[AnyUcVoiceParcel]]: + ... diff --git a/catalystwan/models/configuration/feature_profile/parcel.py b/catalystwan/models/configuration/feature_profile/parcel.py index 8ffded4c..b1c0663b 100644 --- a/catalystwan/models/configuration/feature_profile/parcel.py +++ b/catalystwan/models/configuration/feature_profile/parcel.py @@ -18,6 +18,7 @@ from catalystwan.models.configuration.feature_profile.sdwan.system import AnySystemParcel from catalystwan.models.configuration.feature_profile.sdwan.topology import AnyTopologyParcel from catalystwan.models.configuration.feature_profile.sdwan.transport import AnyTransportParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice import AnyUcVoiceParcel from catalystwan.models.configuration.network_hierarchy import AnyNetworkHierarchyParcel from catalystwan.utils.model import resolve_nested_base_model_unions @@ -42,6 +43,7 @@ "data-prefix", "dhcp-server", "dns", + "dsp-farm", "expanded-community", "ext-community", "full-config", @@ -60,6 +62,7 @@ "logging", "management/vpn", "management/vpn/interface/ethernet", + "media-profile", "mesh", "mirror", "mrf", @@ -100,6 +103,7 @@ "tracker", "trackergroup", "traffic-policy", + "trunk-group", "unified/advanced-inspection-profile", "unified/advanced-malware-protection", "unified/intrusion-prevention", @@ -124,19 +128,20 @@ AnyParcel = Annotated[ Union[ - AnySystemParcel, - AnyPolicyObjectParcel, - AnyServiceParcel, - AnyOtherParcel, - AnyTransportParcel, - AnyEmbeddedSecurityParcel, + AnyApplicationPriorityParcel, AnyCliParcel, AnyDnsSecurityParcel, + AnyEmbeddedSecurityParcel, + AnyNetworkHierarchyParcel, + AnyOtherParcel, + AnyPolicyObjectParcel, + AnyRoutingParcel, + AnyServiceParcel, AnySIGSecurityParcel, - AnyApplicationPriorityParcel, + AnySystemParcel, AnyTopologyParcel, - AnyRoutingParcel, - AnyNetworkHierarchyParcel, + AnyTransportParcel, + AnyUcVoiceParcel, ], Field(discriminator="type_"), ] diff --git a/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/__init__.py b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/__init__.py new file mode 100644 index 00000000..d85d9533 --- /dev/null +++ b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates + +from typing import List, Union + +from pydantic import Field +from typing_extensions import Annotated + +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice.media_profile import MediaProfileParcel +from catalystwan.models.configuration.feature_profile.sdwan.uc_voice.trunk_group import TrunkGroupParcel + +from .dsp_farm import DspFarmParcel + +AnyUcVoiceParcel = Annotated[ + Union[ + DspFarmParcel, + MediaProfileParcel, + TrunkGroupParcel, + ], + Field(discriminator="type_"), +] + +__all__ = ("AnyUcVoiceParcel", "DspFarmParcel", "MediaProfileParcel", "TrunkGroupParcel") + + +def __dir__() -> "List[str]": + return list(__all__) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/dsp_farm.py b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/dsp_farm.py new file mode 100644 index 00000000..3619c77d --- /dev/null +++ b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/dsp_farm.py @@ -0,0 +1,189 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates +from typing import List, Literal, Optional, Union + +from pydantic import AliasPath, BaseModel, ConfigDict, Field + +from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase + +SlotId = Literal[ + "0/1", + "0/2", + "0/3", + "0/4", + "1/0", + "1/1", + "2/0", + "2/1", + "3/0", + "4/0", +] + + +class MediaResource(BaseModel): + slot_id: Optional[Union[Variable, Global[SlotId]]] = Field( + default=None, validation_alias="slotId", serialization_alias="slotId" + ) + + +class ServerList(BaseModel): + ip: Union[Variable, Global[str]] = Field() + identifier: Optional[Global[int]] = Field(default=None) + + +class CucmServerListPriority(BaseModel): + cucm_server_identifier: Union[Variable, Global[str]] = Field( + validation_alias="cucmServerIdentifier", serialization_alias="cucmServerIdentifier" + ) + cucm_server_priority: Optional[Global[int]] = Field( + default=None, validation_alias="cucmServerPriority", serialization_alias="cucmServerPriority" + ) + + +class CucmMediaResourceGroupList(BaseModel): + media_resource_group_name: Union[Variable, Global[str]] = Field( + validation_alias="mediaResourceGroupName", serialization_alias="mediaResourceGroupName" + ) + profile_name: Union[Variable, Global[str]] = Field( + validation_alias="profileName", serialization_alias="profileName" + ) + + +CucmSwitchover = Literal[ + "graceful", + "immediate", +] + + +CucmSwitchback = Literal[ + "graceful", + "guard", + "immediate", +] + + +class CucmGroup(BaseModel): + bind_interface: Union[Variable, Global[str]] = Field( + validation_alias="bindInterface", serialization_alias="bindInterface" + ) + cucm_media_resource_group_list: List[CucmMediaResourceGroupList] = Field( + validation_alias="cucmMediaResourceGroupList", + serialization_alias="cucmMediaResourceGroupList", + description="CUCM Media Resource Group List", + ) + cucm_server_list_priority: List[CucmServerListPriority] = Field( + validation_alias="cucmServerListPriority", + serialization_alias="cucmServerListPriority", + description="CUCM Server List Priority", + ) + cucm_switchback: Union[Variable, Default[Literal["guard"]], Global[CucmSwitchback]] = Field( + validation_alias="cucmSwitchback", serialization_alias="cucmSwitchback" + ) + cucm_switchover: Union[Variable, Default[Literal["graceful"]], Global[CucmSwitchover]] = Field( + validation_alias="cucmSwitchover", serialization_alias="cucmSwitchover" + ) + keep_alive_retries: Union[Variable, Default[int], Global[int]] = Field( + validation_alias="keepAliveRetries", serialization_alias="keepAliveRetries" + ) + keep_alive_time_out: Union[Variable, Default[int], Global[int]] = Field( + validation_alias="keepAliveTimeOut", serialization_alias="keepAliveTimeOut" + ) + cucm_group_id: Optional[Global[int]] = Field( + default=None, validation_alias="cucmGroupId", serialization_alias="cucmGroupId" + ) + + +ProfileType = Literal[ + "conference", + "mtp", + "transcode", +] + +DspVoiceCodec = Literal[ + "g711alaw", + "g711ulaw", + "g722-64", + "g729abr8", + "g729ar8", + "g729br8", + "g729r8", + "ilbc", + "isac", + "opus", + "pass-through", +] + +DspVoiceFeature = Literal[ + "acoustic-shock-protection", + "call-progress-analysis", + "cng-fax-detect", + "dtmf suppress", + "noise-reduction", +] + +Application = Literal[ + "cube", + "sccp", +] + +ConferenceMaxParticipants = Literal[ + "16", + "32", + "8", +] + + +class Profile(BaseModel): + model_config = ConfigDict(populate_by_name=True) + codec_list: Union[Variable, Default[List[DspVoiceCodec]], Global[List[DspVoiceCodec]]] = Field( + validation_alias="codecList", serialization_alias="codecList" + ) + profile_name: Union[Variable, Global[str]] = Field( + validation_alias="profileName", serialization_alias="profileName" + ) + application: Optional[Global[Application]] = Field(default=None) + conference_max_participants: Union[ + Variable, Default[Literal["8"]], Global[ConferenceMaxParticipants], None + ] = Field( + default=None, validation_alias="conferenceMaxParticipants", serialization_alias="conferenceMaxParticipants" + ) + feature: Union[Variable, Default[None], Global[List[DspVoiceFeature]]] = Field(default=Default[None](value=None)) + max_session: Optional[Union[Variable, Global[int]]] = Field( + default=None, validation_alias="maxSession", serialization_alias="maxSession" + ) + mtp_hardware: Optional[Global[bool]] = Field( + default=None, validation_alias="mtpHardware", serialization_alias="mtpHardware" + ) + mtp_max_sessions_hardware: Optional[Union[Variable, Global[int]]] = Field( + default=None, validation_alias="mtpMaxSessionsHardware", serialization_alias="mtpMaxSessionsHardware" + ) + mtp_max_sessions_software: Optional[Union[Variable, Global[int]]] = Field( + default=None, validation_alias="mtpMaxSessionsSoftware", serialization_alias="mtpMaxSessionsSoftware" + ) + mtp_software: Optional[Global[bool]] = Field( + default=None, validation_alias="mtpSoftware", serialization_alias="mtpSoftware" + ) + profile_id: Global[int] = Field(validation_alias="profileId", serialization_alias="profileId") + profile_type: Global[ProfileType] = Field(validation_alias="profileType", serialization_alias="profileType") + shutdown: Optional[Union[Variable, Global[bool], Default[bool]]] = Field(default=None) + universal: Optional[Union[Variable, Global[bool], Default[bool]]] = Field(default=None) + + +class DspFarmParcel(_ParcelBase): + model_config = ConfigDict(populate_by_name=True) + type_: Literal["dsp-farm"] = Field(default="dsp-farm", exclude=True) + profile: List[Profile] = Field(validation_alias=AliasPath("data", "profile"), description="Configure Profile") + cucm_group: Optional[List[CucmGroup]] = Field( + default=None, validation_alias=AliasPath("data", "cucmGroup"), description="Configure CUCM Group" + ) + ip_precedence: Optional[Union[Variable, Default[int], Global[int]]] = Field( + default=None, validation_alias=AliasPath("data", "ipPrecedence") + ) + local_interface: Optional[Union[Variable, Global[str]]] = Field( + default=None, validation_alias=AliasPath("data", "localInterface") + ) + media_resource: Optional[List[MediaResource]] = Field( + default=None, validation_alias=AliasPath("data", "mediaResource"), description="Configure Media Resource" + ) + server_list: Optional[List[ServerList]] = Field( + default=None, validation_alias=AliasPath("data", "serverList"), description="Configure Server List" + ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/media_profile.py b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/media_profile.py new file mode 100644 index 00000000..5728cea0 --- /dev/null +++ b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/media_profile.py @@ -0,0 +1,50 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates +from typing import List, Literal, Optional, Union + +from pydantic import AliasPath, BaseModel, ConfigDict, Field + +from catalystwan.api.configuration_groups.parcel import Global, Variable, _ParcelBase + +MpVoiceCodec = Literal[ + "G711aLaw", + "G711uLaw", + "G722", + "G729r8", + "ilbc", +] + + +class Codec(BaseModel): + model_config = ConfigDict(populate_by_name=True) + value: Union[Variable, Global[MpVoiceCodec]] = Field() + pref_num: Optional[str] = Field( + default=None, validation_alias="prefNum", serialization_alias="prefNum", description="Preference number" + ) + + +Dtmf = Literal[ + "inband", + "rtp-nte", + "rtp-nte sip-kpml", + "rtp-nte sip-kpml sip-notify", + "rtp-nte sip-notify", + "rtp-nte sip-notify sip-kpml", + "sip-kpml", + "sip-kpml rtp-nte", + "sip-kpml rtp-nte sip-notify", + "sip-kpml sip-notify", + "sip-kpml sip-notify rtp-nte", + "sip-notify", + "sip-notify rtp-nte", + "sip-notify rtp-nte sip-kpml", + "sip-notify sip-kpml", + "sip-notify sip-kpml rtp-nte", +] + + +class MediaProfileParcel(_ParcelBase): + model_config = ConfigDict(populate_by_name=True) + type_: Literal["media-profile"] = Field(default="media-profile", exclude=True) + codec: List[Codec] = Field(validation_alias=AliasPath("data", "codec")) + dtmf: Union[Variable, Global[Dtmf]] = Field(validation_alias=AliasPath("data", "dtmf")) + media_profile_number: Union[Variable, Global[int]] = Field(validation_alias=AliasPath("data", "mediaProfileNumber")) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/trunk_group.py b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/trunk_group.py new file mode 100644 index 00000000..f9155001 --- /dev/null +++ b/catalystwan/models/configuration/feature_profile/sdwan/uc_voice/trunk_group.py @@ -0,0 +1,45 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates +from typing import Literal, Optional, Union + +from pydantic import AliasPath, ConfigDict, Field + +from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase + +HuntSchemeMethod = Literal[ + "least-idle", + "least-used", + "longest-idle", + "random", + "round-robin", + "sequential", +] + +HuntSchemeChannel = Literal[ + "both", + "even", + "odd", +] + +HuntSchemeDirection = Literal[ + "down", + "up", +] + + +class TrunkGroupParcel(_ParcelBase): + model_config = ConfigDict(populate_by_name=True) + type_: Literal["trunk-group"] = Field(default="trunk-group", exclude=True) + hunt_scheme_method: Union[Variable, Global[HuntSchemeMethod], Default[None]] = Field( + validation_alias=AliasPath("data", "huntSchemeMethod") + ) + max_calls_in: Union[Variable, Global[int], Default[None]] = Field(validation_alias=AliasPath("data", "maxCallsIn")) + max_calls_out: Union[Variable, Global[int], Default[None]] = Field( + validation_alias=AliasPath("data", "maxCallsOut") + ) + max_retries: Union[Variable, Global[int], Default[None]] = Field(validation_alias=AliasPath("data", "maxRetries")) + hunt_scheme_channel: Optional[Union[Variable, Global[HuntSchemeChannel], Default[None]]] = Field( + default=None, validation_alias=AliasPath("data", "huntSchemeChannel") + ) + hunt_scheme_direction: Optional[Union[Global[HuntSchemeDirection], Default[None], Variable]] = Field( + default=None, validation_alias=AliasPath("data", "huntSchemeDirection") + )