Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
ensure parcel entries are hashable so duplicates can be removed easily (
Browse files Browse the repository at this point in the history
  • Loading branch information
sbasan authored Nov 11, 2024
1 parent e4f760d commit 7ab5ce3
Show file tree
Hide file tree
Showing 31 changed files with 110 additions and 100 deletions.
26 changes: 13 additions & 13 deletions ENDPOINTS.md

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion catalystwan/api/configuration_groups/parcel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright 2024 Cisco Systems, Inc. and its affiliates

from typing import Any, Dict, Generic, List, Literal, Optional, Tuple, TypeVar
from typing import Any, Dict, Generic, List, Literal, Optional, Sequence, Tuple, TypeVar

from pydantic import (
AliasPath,
Expand All @@ -21,6 +21,11 @@
T = TypeVar("T")


class _ParcelEntry(BaseModel):
def __hash__(self) -> int:
return hash(self.model_dump_json())


class _ParcelBase(BaseModel):
model_config = ConfigDict(
extra="allow", arbitrary_types_allowed=True, populate_by_name=True, json_schema_mode_override="validation"
Expand All @@ -39,6 +44,7 @@ class _ParcelBase(BaseModel):
description="Set the parcel description",
)
data: Optional[Any] = None
entries: Optional[Sequence[_ParcelEntry]] = Field(default=None, validation_alias=AliasPath("data", "entries"))
_parcel_data_key: str = PrivateAttr(default="data")

@model_serializer(mode="wrap")
Expand Down Expand Up @@ -80,6 +86,10 @@ def _get_parcel_type(cls) -> str:
return str(field_info.default)
raise CatalystwanException(f"{cls.__name__} field parcel type is not set.")

def remove_duplicated_entries(self) -> None:
if self.entries:
self.entries = list(set(self.entries))


# https://github.com/pydantic/pydantic/discussions/6090
# Usage: Global[str](value="test")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from pydantic import AliasPath, BaseModel, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global, as_optional_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global, as_optional_global
from catalystwan.models.common import TLOCColor
from catalystwan.models.configuration.feature_profile.common import RefIdItem

Expand All @@ -15,7 +15,7 @@ class AppProbeMapItem(BaseModel):
dscp: Optional[Global[int]] = Field(default=None)


class AppProbeEntry(BaseModel):
class AppProbeEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
map: List[AppProbeMapItem] = Field(default_factory=list)
forwarding_class: Union[Global[str], RefIdItem] = Field(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

from typing import List, Literal, Union

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class ApplicationListEntry(BaseModel):
class ApplicationListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
app_list: Global[str] = Field(serialization_alias="app", validation_alias="app")


class ApplicationFamilyListEntry(BaseModel):
class ApplicationFamilyListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
app_list_family: Global[str] = Field(serialization_alias="appFamily", validation_alias="appFamily")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class AsPathEntry(BaseModel):
class AsPathEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
as_path: Global[str] = Field(validation_alias="asPath", serialization_alias="asPath")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import TLOCColor


class ColorEntry(BaseModel):
class ColorEntry(_ParcelEntry):
color: Global[TLOCColor]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv4Address, IPv4Network
from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class DataPrefixEntry(BaseModel):
class DataPrefixEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
ipv4_address: Global[IPv4Address] = Field(serialization_alias="ipv4Address", validation_alias="ipv4Address")
ipv4_prefix_length: Global[int] = Field(serialization_alias="ipv4PrefixLength", validation_alias="ipv4PrefixLength")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv4Address
from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class ExtendedCommunityEntry(BaseModel):
class ExtendedCommunityEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
extended_community: Global[str] = Field(serialization_alias="extCommunity", validation_alias="extCommunity")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator
from pydantic import AliasPath, ConfigDict, Field, field_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class FowardingClassQueueEntry(BaseModel):
class FowardingClassQueueEntry(_ParcelEntry):
queue: Global[str]

@field_validator("queue")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv6Address, IPv6Interface
from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class IPv6DataPrefixEntry(BaseModel):
class IPv6DataPrefixEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
ipv6_address: Global[IPv6Address] = Field(serialization_alias="ipv6Address", validation_alias="ipv6Address")
ipv6_prefix_length: Global[int] = Field(serialization_alias="ipv6PrefixLength", validation_alias="ipv6PrefixLength")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv6Address, IPv6Interface
from typing import List, Literal, Optional

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class IPv6PrefixListEntry(BaseModel):
class IPv6PrefixListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
ipv6_address: Global[IPv6Address] = Field(serialization_alias="ipv6Address", validation_alias="ipv6Address")
ipv6_prefix_length: Global[int] = Field(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field
from pydantic.networks import IPvAnyAddress

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class MirrorEntry(BaseModel):
class MirrorEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
remote_dest_ip: Global[str] = Field(validation_alias="remoteDestIp", serialization_alias="remoteDestIp")
source_ip: Global[str] = Field(validation_alias="sourceIp", serialization_alias="sourceIp")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator
from pydantic import AliasPath, ConfigDict, Field, field_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.policy.list.policer import PolicerExceedAction


class PolicerEntry(BaseModel):
class PolicerEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
burst: Global[int]
exceed: Global[PolicerExceedAction]
rate: Global[int]

@field_validator("burst")
@classmethod
def check_burst(cls, burst_str: Global):
def check_burst(cls, burst_str: Global[int]):
assert 15000 <= burst_str.value <= 10_000_000
return burst_str

@field_validator("rate")
@classmethod
def check_rate(cls, rate_str: Global):
def check_rate(cls, rate_str: Global[int]):
assert 8 <= rate_str.value <= 100_000_000_000
return rate_str

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator, model_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import TLOCColor

PathPreference = Literal[
Expand All @@ -24,7 +24,7 @@ class Preference(BaseModel):
)


class PreferredColorGroupEntry(BaseModel):
class PreferredColorGroupEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
primary_preference: Preference = Field(
serialization_alias="primaryPreference", validation_alias="primaryPreference"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv4Address, IPv4Network
from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class PrefixListEntry(BaseModel):
class PrefixListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
ipv4_address: Global[IPv4Address] = Field(serialization_alias="ipv4Address", validation_alias="ipv4Address")
ipv4_prefix_length: Global[int] = Field(serialization_alias="ipv4PrefixLength", validation_alias="ipv4PrefixLength")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import SLAClassCriteria
from catalystwan.models.configuration.feature_profile.common import RefIdItem

Expand Down Expand Up @@ -73,7 +73,7 @@ def add_criteria(
raise ValueError(f"Criteria {e} is not in configured criteria {self.criteria.value}")


class SLAClassListEntry(BaseModel):
class SLAClassListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)

latency: Optional[Global[int]] = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import WellKnownBGPCommunities


class StandardCommunityEntry(BaseModel):
class StandardCommunityEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
standard_community: Global[str] = Field(
serialization_alias="standardCommunity", validation_alias="standardCommunity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from ipaddress import IPv4Address
from typing import List, Literal, Optional

from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator
from pydantic import AliasPath, ConfigDict, Field, field_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import EncapType, TLOCColor


class TlocEntry(BaseModel):
class TlocEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
tloc: Global[IPv4Address]
color: Global[TLOCColor]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

from typing import List, Literal, Union

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class SecurityApplicationListEntry(BaseModel):
class SecurityApplicationListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
app_list: Global[str] = Field(serialization_alias="app", validation_alias="app")


class SecurityApplicationFamilyListEntry(BaseModel):
class SecurityApplicationFamilyListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
app_list_family: Global[str] = Field(serialization_alias="appFamily", validation_alias="appFamily")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from ipaddress import IPv4Network
from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class SecurityDataPrefixEntry(BaseModel):
class SecurityDataPrefixEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
ip_prefix: Global[IPv4Network] = Field(serialization_alias="ipPrefix", validation_alias="ipPrefix")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from typing import List, Literal

from pydantic import AliasPath, BaseModel, ConfigDict, Field
from pydantic import AliasPath, ConfigDict, Field

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global


class FQDNListEntry(BaseModel):
class FQDNListEntry(_ParcelEntry):
model_config = ConfigDict(populate_by_name=True)
pattern: Global[str] = Field(
description="Ex: cisco.com, .*cisco.com, .*.cisco.com. Should not start with '*' or '+'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from typing import List, Literal, Optional

from pydantic import AliasPath, BaseModel, ConfigDict, Field, model_validator
from pydantic import AliasPath, ConfigDict, Field, model_validator

from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global
from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
from catalystwan.models.common import check_fields_exclusive


class GeoLocationListEntry(BaseModel):
class GeoLocationListEntry(_ParcelEntry):
country: Optional[Global[str]] = Field(default=None, description="ISO-3166 alpha-3 country code eg: FRA")
continent: Optional[Global[str]] = Field(
default=None, description="One of 2-letter continent codes: AF, NA, OC, AN, AS, EU, SA"
Expand Down
Loading

0 comments on commit 7ab5ce3

Please sign in to comment.