catalystwan 0.41.2.dev8__py3-none-any.whl → 0.41.2.dev9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,30 +3,45 @@
3
3
  # mypy: disable-error-code="empty-body"
4
4
  from uuid import UUID
5
5
 
6
- from catalystwan.endpoints import APIEndpoints, delete, get, post, versions
6
+ from catalystwan.endpoints import APIEndpoints, delete, get, post, put, versions
7
7
  from catalystwan.models.configuration.feature_profile.parcel import Parcel, ParcelCreationResponse
8
8
  from catalystwan.models.configuration.network_hierarchy.cflowd import CflowdParcel
9
9
  from catalystwan.models.configuration.network_hierarchy.node import NodeInfo
10
+ from catalystwan.models.configuration.network_hierarchy.security_logging import SecurityLoggingParcel
10
11
  from catalystwan.typed_list import DataSequence
11
12
 
12
13
 
13
14
  class NetworkHierarchy(APIEndpoints):
14
15
  @get("/v1/network-hierarchy")
15
16
  @versions(">=20.10")
16
- def list_nodes(self) -> DataSequence[NodeInfo]:
17
- ...
17
+ def list_nodes(self) -> DataSequence[NodeInfo]: ...
18
18
 
19
19
  @post("/v1/network-hierarchy/{node_id}/network-settings/cflowd")
20
20
  @versions(">20.12")
21
- def create_cflowd(self, node_id: UUID, payload: CflowdParcel) -> ParcelCreationResponse:
22
- ...
21
+ def create_cflowd(self, node_id: UUID, payload: CflowdParcel) -> ParcelCreationResponse: ...
23
22
 
24
23
  @get("/v1/network-hierarchy/{node_id}/network-settings/cflowd", resp_json_key="data")
25
24
  @versions(">20.12")
26
- def get_cflowd(self, node_id: UUID) -> DataSequence[Parcel[CflowdParcel]]:
27
- ...
25
+ def get_cflowd(self, node_id: UUID) -> DataSequence[Parcel[CflowdParcel]]: ...
28
26
 
29
27
  @delete("/v1/network-hierarchy/{node_id}/network-settings/cflowd/{parcel_id}")
30
28
  @versions(">20.12")
31
- def delete_cflowd(self, node_id: UUID, parcel_id: UUID) -> None:
32
- ...
29
+ def delete_cflowd(self, node_id: UUID, parcel_id: UUID) -> None: ...
30
+
31
+ @post("/v1/network-hierarchy/{node_id}/network-settings/security-logging")
32
+ @versions(">20.12")
33
+ def create_security_logging(self, node_id: UUID, payload: SecurityLoggingParcel) -> ParcelCreationResponse: ...
34
+
35
+ @get("/v1/network-hierarchy/{node_id}/network-settings/security-logging", resp_json_key="data")
36
+ @versions(">20.12")
37
+ def get_security_logging(self, node_id: UUID) -> DataSequence[Parcel[SecurityLoggingParcel]]: ...
38
+
39
+ @put("/v1/network-hierarchy/{node_id}/network-settings/security-logging/{parcel_id}")
40
+ @versions(">20.12")
41
+ def edit_security_logging(
42
+ self, node_id: UUID, payload: SecurityLoggingParcel, parcel_id: UUID
43
+ ) -> ParcelCreationResponse: ...
44
+
45
+ @delete("/v1/network-hierarchy/{node_id}/network-settings/security-logging/{parcel_id}")
46
+ @versions(">20.12")
47
+ def delete_security_logging(self, node_id: UUID, parcel_id: UUID) -> None: ...
@@ -0,0 +1,36 @@
1
+ # Copyright 2026 Cisco Systems, Inc. and its affiliates
2
+
3
+ # mypy: disable-error-code="empty-body"
4
+
5
+ from pathlib import Path
6
+
7
+ from catalystwan.endpoints import APIEndpoints, CustomPayloadType, PreparedPayload, post, versions
8
+ from catalystwan.models.configuration.packages import (
9
+ CatalogPackageExportRequest,
10
+ CatalogPackageImportResponse,
11
+ TemplatePackageExportRequest,
12
+ )
13
+
14
+
15
+ class CatalogImportTarballFile(CustomPayloadType):
16
+ def __init__(self, filename: Path):
17
+ self.filename = filename
18
+
19
+ def prepared(self) -> PreparedPayload:
20
+ data = open(self.filename, "rb")
21
+ return PreparedPayload(files={"file": (Path(data.name).name, data)})
22
+
23
+
24
+ class DeviceConfigPackageEndpoints(APIEndpoints):
25
+
26
+ @versions(supported_versions=(">=20.12"), raises=False)
27
+ @post("/templates/package/export")
28
+ def export_templates(self, payload: TemplatePackageExportRequest = TemplatePackageExportRequest()) -> bytes: ...
29
+
30
+ @versions(supported_versions=(">=20.14"), raises=False)
31
+ @post("/v1/packages/export")
32
+ def export_catalog(self, payload: CatalogPackageExportRequest) -> bytes: ...
33
+
34
+ @versions(supported_versions=(">=20.14"), raises=False)
35
+ @post("/v1/packages/import")
36
+ def import_catalog(self, payload: CatalogImportTarballFile) -> CatalogPackageImportResponse: ...
@@ -19,6 +19,7 @@ from catalystwan.endpoints.configuration.feature_profile.sdwan.system import Sys
19
19
  from catalystwan.endpoints.configuration.feature_profile.sdwan.topology import TopologyFeatureProfile
20
20
  from catalystwan.endpoints.configuration.feature_profile.sdwan.transport import TransportFeatureProfile
21
21
  from catalystwan.endpoints.configuration.network_hierarchy import NetworkHierarchy
22
+ from catalystwan.endpoints.configuration.packages import DeviceConfigPackageEndpoints
22
23
  from catalystwan.endpoints.configuration.policy.definition.access_control_list import ConfigurationPolicyAclDefinition
23
24
  from catalystwan.endpoints.configuration.policy.definition.access_control_list_ipv6 import (
24
25
  ConfigurationPolicyAclIPv6Definition,
@@ -230,8 +231,11 @@ class ConfigurationContainer:
230
231
  def __init__(self, session: ManagerSession):
231
232
  self.policy = ConfigurationPolicyContainer(session)
232
233
  self.feature_profile = ConfigurationFeatureProfileContainer(session)
234
+ self.config_group = ConfigurationGroup(session)
233
235
  self.topology_group = TopologyGroupEndpoints(session)
234
236
  self.policy_group = PolicyGroupEndpoints(session)
237
+ self.feature_profile = ConfigurationFeatureProfileContainer(session=session)
238
+ self.packages = DeviceConfigPackageEndpoints(session)
235
239
 
236
240
 
237
241
  class TroubleshootingToolsContainer:
@@ -94,6 +94,7 @@ ParcelType = Literal[
94
94
  "security-ipssignature",
95
95
  "security-localapp",
96
96
  "security-localdomain",
97
+ "security-logging",
97
98
  "security-port",
98
99
  "security-protocolname",
99
100
  "security-scalablegrouptag",
@@ -5,7 +5,7 @@ from typing import List, Union
5
5
  from pydantic import Field
6
6
  from typing_extensions import Annotated
7
7
 
8
- from .policy.app_probe import AppProbeMapItem, AppProbeParcel
8
+ from .policy.app_probe import AppProbeEntry, AppProbeMapItem, AppProbeParcel
9
9
  from .policy.application_list import ApplicationFamilyListEntry, ApplicationListEntry, ApplicationListParcel
10
10
  from .policy.as_path import AsPathParcel
11
11
  from .policy.color_list import ColorEntry, ColorParcel
@@ -20,7 +20,7 @@ from .policy.policer import PolicerEntry, PolicerParcel
20
20
  from .policy.prefered_group_color import Preference, PreferredColorGroupEntry, PreferredColorGroupParcel
21
21
  from .policy.prefix_list import PrefixListEntry, PrefixListParcel
22
22
  from .policy.service_object_group import ServiceObjectGroupParcel
23
- from .policy.sla_class import SLAClassListEntry, SLAClassParcel
23
+ from .policy.sla_class import FallbackBestTunnel, SLAClassCriteria, SLAClassListEntry, SLAClassParcel
24
24
  from .policy.standard_community import StandardCommunityEntry, StandardCommunityParcel
25
25
  from .policy.tloc_list import TlocEntry, TlocParcel
26
26
  from .security.aip import AdvancedInspectionProfileParcel
@@ -43,7 +43,7 @@ from .security.scalable_group_tag import ScalableGroupTagEntry, ScalableGroupTag
43
43
  from .security.security_port import SecurityPortListEntry, SecurityPortParcel
44
44
  from .security.ssl_decryption import SslDecryptionParcel
45
45
  from .security.ssl_decryption_profile import SslDecryptionProfileParcel
46
- from .security.url import BaseURLListEntry, URLAllowParcel, URLBlockParcel, URLParcel
46
+ from .security.url import BaseURLListEntry, URLParcel
47
47
  from .security.url_filtering import UrlFilteringParcel
48
48
  from .security.zone import SecurityZoneListEntry, SecurityZoneListParcel
49
49
 
@@ -93,12 +93,12 @@ __all__ = (
93
93
  "AdvancedInspectionProfileParcel",
94
94
  "AdvancedMalwareProtectionParcel",
95
95
  "AnyPolicyObjectParcel",
96
- "ApplicationFamilyListEntry",
97
- "ApplicationListEntry",
98
- "ApplicationListParcel",
99
96
  "AppProbeEntry",
100
97
  "AppProbeMapItem",
101
98
  "AppProbeParcel",
99
+ "ApplicationFamilyListEntry",
100
+ "ApplicationListEntry",
101
+ "ApplicationListParcel",
102
102
  "AsPathParcel",
103
103
  "BaseURLListEntry",
104
104
  "ColorEntry",
@@ -107,24 +107,22 @@ __all__ = (
107
107
  "DataPrefixParcel",
108
108
  "ExpandedCommunityParcel",
109
109
  "ExtendedCommunityParcel",
110
+ "FQDNDomainParcel",
111
+ "FQDNListEntry",
110
112
  "FallbackBestTunnel",
111
113
  "FowardingClassParcel",
112
114
  "FowardingClassQueueEntry",
113
- "FQDNDomainParcel",
114
- "FQDNListEntry",
115
115
  "GeoLocationListEntry",
116
116
  "GeoLocationListParcel",
117
- "IdentityEntry",
118
- "IdentityEntry",
119
- "IdentityParcel",
120
- "IdentityParcel",
121
- "IntrusionPreventionParcel",
122
117
  "IPSSignatureListEntry",
123
118
  "IPSSignatureParcel",
124
119
  "IPv6DataPrefixEntry",
125
120
  "IPv6DataPrefixParcel",
126
121
  "IPv6PrefixListEntry",
127
122
  "IPv6PrefixListParcel",
123
+ "IdentityEntry",
124
+ "IdentityParcel",
125
+ "IntrusionPreventionParcel",
128
126
  "LocalDomainListEntry",
129
127
  "LocalDomainParcel",
130
128
  "MirrorParcel",
@@ -137,9 +135,11 @@ __all__ = (
137
135
  "PrefixListParcel",
138
136
  "ProtocolListEntry",
139
137
  "ProtocolListParcel",
138
+ "SLAClassCriteria",
139
+ "SLAClassListEntry",
140
+ "SLAClassParcel",
140
141
  "ScalableGroupTagEntry",
141
142
  "ScalableGroupTagParcel",
142
- "ServiceObjectGroupParcel",
143
143
  "SecurityApplicationFamilyListEntry",
144
144
  "SecurityApplicationListEntry",
145
145
  "SecurityApplicationListParcel",
@@ -151,18 +151,13 @@ __all__ = (
151
151
  "SecurityPortParcel",
152
152
  "SecurityZoneListEntry",
153
153
  "SecurityZoneListParcel",
154
- "SLAAppProbeClass",
155
- "SLAClassCriteria",
156
- "SLAClassListEntry",
157
- "SLAClassParcel",
154
+ "ServiceObjectGroupParcel",
158
155
  "SslDecryptionParcel",
159
156
  "SslDecryptionProfileParcel",
160
157
  "StandardCommunityEntry",
161
158
  "StandardCommunityParcel",
162
159
  "TlocEntry",
163
160
  "TlocParcel",
164
- "URLAllowParcel",
165
- "URLBlockParcel",
166
161
  "URLParcel",
167
162
  )
168
163
 
@@ -1,10 +1,12 @@
1
1
  # Copyright 2024 Cisco Systems, Inc. and its affiliates
2
2
 
3
- from typing import List, Literal
3
+ from typing import List, Literal, Optional
4
4
 
5
- from pydantic import AliasPath, ConfigDict, Field
5
+ from pydantic import AliasPath, ConfigDict, Field, model_serializer
6
6
 
7
- from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry, as_global
7
+ from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, _ParcelEntry
8
+
9
+ URLListSubtype = Literal["urlallowed", "urlblocked"]
8
10
 
9
11
 
10
12
  class BaseURLListEntry(_ParcelEntry):
@@ -14,19 +16,33 @@ class BaseURLListEntry(_ParcelEntry):
14
16
 
15
17
  class URLParcel(_ParcelBase):
16
18
  model_config = ConfigDict(populate_by_name=True)
19
+ subtype: Optional[URLListSubtype] = Field( # field name must not clash with any of aliases
20
+ default=None,
21
+ validation_alias=AliasPath("data", "type"),
22
+ # serialization alias handled by _BaseParcel model serializer
23
+ description="must be used only for versions > 20.15.2",
24
+ )
25
+ legacy_subtype: Optional[URLListSubtype] = Field( # field name must not clash with any of aliases
26
+ exclude=True,
27
+ default=None,
28
+ validation_alias="type",
29
+ serialization_alias="type",
30
+ description="must be used only for versions <= 20.15.2",
31
+ )
17
32
  type_: Literal["security-urllist"] = Field(default="security-urllist", exclude=True)
18
- type: Literal["urlallowed", "urlblocked"]
19
33
  entries: List[BaseURLListEntry] = Field(default_factory=list, validation_alias=AliasPath("data", "entries"))
20
34
 
21
35
  def add_url(self, pattern: str):
22
- self.entries.append(BaseURLListEntry(pattern=as_global(pattern)))
23
-
24
-
25
- class URLAllowParcel(URLParcel):
26
- model_config = ConfigDict(populate_by_name=True)
27
- type: Literal["urlallowed"] = "urlallowed"
28
-
29
-
30
- class URLBlockParcel(URLParcel):
31
- model_config = ConfigDict(populate_by_name=True)
32
- type: Literal["urlblocked"] = "urlblocked"
36
+ self.entries.append(BaseURLListEntry(pattern=Global[str](value=pattern)))
37
+
38
+ def use_legacy_subtype(self):
39
+ if self.subtype is not None:
40
+ self.legacy_subtype = self.subtype
41
+ self.subtype = None
42
+
43
+ @model_serializer(mode="wrap")
44
+ def serialize_model(self, handler, info):
45
+ serialized = _ParcelBase.envelope_parcel_data(self, handler, info)
46
+ if self.legacy_subtype:
47
+ serialized["type"] = self.legacy_subtype
48
+ return serialized
@@ -334,8 +334,8 @@ class InterfaceEthernetParcel(_ParcelBase):
334
334
  ethernet_description: Optional[Union[Global[str], Variable, Default[None]]] = Field(
335
335
  default=Default[None](value=None), validation_alias=AliasPath("data", "description")
336
336
  )
337
- interface_ip_address: Union[InterfaceDynamicIPv4Address, InterfaceStaticIPv4Address] = Field(
338
- validation_alias=AliasPath("data", "intfIpAddress"), default_factory=InterfaceStaticIPv4Address
337
+ interface_ip_address: Optional[Union[InterfaceDynamicIPv4Address, InterfaceStaticIPv4Address]] = Field(
338
+ validation_alias=AliasPath("data", "intfIpAddress"), default=None
339
339
  )
340
340
  dhcp_helper: Optional[Union[Variable, Global[List[str]], Default[None]]] = Field(
341
341
  validation_alias=AliasPath("data", "dhcpHelper"), default=None
@@ -75,7 +75,7 @@ class UserItem(BaseModel):
75
75
  populate_by_name=True,
76
76
  )
77
77
  name: Global[str] = Field(..., description="Name of the SNMP user")
78
- auth: Optional[Union[Global[Literal["sha"]], Variable, Default[None]]] = Field(
78
+ auth: Optional[Union[Global[Literal["sha", "sha256"]], Variable, Default[None]]] = Field(
79
79
  default=None, description="Configure authentication protocol"
80
80
  )
81
81
  auth_password: Optional[Union[Global[str], Variable, Default[None]]] = Field(
@@ -308,8 +308,8 @@ class InterfaceEthernetParcel(_ParcelBase):
308
308
  default_factory=list, validation_alias=AliasPath("data", "encapsulation"), description="Encapsulation for TLOC"
309
309
  )
310
310
  interface_name: Union[Variable, Global[str]] = Field(validation_alias=AliasPath("data", "interfaceName"))
311
- interface_ip_address: Union[InterfaceDynamicIPv4Address, InterfaceStaticIPv4Address] = Field(
312
- validation_alias=AliasPath("data", "intfIpAddress"), default_factory=InterfaceStaticIPv4Address
311
+ interface_ip_address: Optional[Union[InterfaceDynamicIPv4Address, InterfaceStaticIPv4Address]] = Field(
312
+ validation_alias=AliasPath("data", "intfIpAddress"), default=None
313
313
  )
314
314
  interface_description: Optional[Union[Variable, Global[str], Default[None]]] = Field(
315
315
  default=None, validation_alias=AliasPath("data", "description")
@@ -6,14 +6,16 @@ from typing_extensions import Annotated
6
6
 
7
7
  from .cflowd import CflowdParcel
8
8
  from .node import NodeInfo
9
+ from .security_logging import SecurityLoggingParcel
9
10
 
10
11
  AnyNetworkHierarchyParcel = Annotated[
11
- Union[CflowdParcel],
12
+ Union[CflowdParcel, SecurityLoggingParcel],
12
13
  Field(discriminator="type_"),
13
14
  ]
14
15
 
15
16
  __all__ = [
16
17
  "CflowdParcel",
18
+ "SecurityLoggingParcel",
17
19
  "NodeInfo",
18
20
  ]
19
21
 
@@ -0,0 +1,91 @@
1
+ # Copyright 2026 Cisco Systems, Inc. and its affiliates
2
+ from ipaddress import IPv4Address, IPv6Address
3
+ from typing import List, Literal, Optional, Tuple, Union, get_args
4
+
5
+ from pydantic import AliasPath, BaseModel, ConfigDict, Field
6
+
7
+ from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase
8
+
9
+ HSLName = Literal[
10
+ "server1",
11
+ "server2",
12
+ "server3",
13
+ "server4",
14
+ ]
15
+ SERVER_NAMES: Tuple[HSLName, ...] = get_args(HSLName)
16
+
17
+
18
+ class HSLEntry(BaseModel):
19
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
20
+ name: Optional[HSLName] = Field(
21
+ default=None,
22
+ validation_alias="name",
23
+ serialization_alias="name",
24
+ description="This field is required in version >=20.16. In versions <20.16 it must be None",
25
+ )
26
+ vrf: Global[str] = Field(validation_alias="vrf", serialization_alias="vrf")
27
+ serverIp: Global[Union[IPv4Address, IPv6Address]] = Field(
28
+ validation_alias="serverIp", serialization_alias="serverIp"
29
+ )
30
+ port: Global[int] = Field(validation_alias="port", serialization_alias="port")
31
+
32
+
33
+ class UTDSyslog(BaseModel):
34
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
35
+ vpn: Global[str] = Field(validation_alias="vpn", serialization_alias="vpn")
36
+ server_ip: Global[IPv4Address] = Field(validation_alias="serverIp", serialization_alias="serverIp")
37
+
38
+
39
+ class SecurityLoggingParcel(_ParcelBase):
40
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
41
+ type_: Literal["security-logging"] = Field(default="security-logging", exclude=True)
42
+ parcel_name: str = Field(
43
+ default="SecurityLogging",
44
+ exclude=True,
45
+ description="This field is not defined for this model",
46
+ serialization_alias="name",
47
+ validation_alias="name",
48
+ )
49
+ high_speed_logging: List[HSLEntry] = Field(
50
+ default_factory=list,
51
+ max_length=4,
52
+ validation_alias=AliasPath("data", "highSpeedLogging"),
53
+ description="High Speed Logging",
54
+ )
55
+ utd_syslog: List[UTDSyslog] = Field(
56
+ default_factory=list,
57
+ validation_alias=AliasPath("data", "utdSyslog"),
58
+ description="UTD Syslog",
59
+ )
60
+
61
+ def add_hsl_server(
62
+ self,
63
+ vrf: str,
64
+ serverIp: Union[IPv4Address, IPv6Address],
65
+ port: int,
66
+ name: Optional[HSLName] = None,
67
+ ):
68
+ if self.high_speed_logging is None:
69
+ self.high_speed_logging = []
70
+
71
+ if len(self.high_speed_logging) >= 4:
72
+ raise ValueError("Max 4 servers allowed")
73
+
74
+ taken_server_names = {entry.name for entry in self.high_speed_logging}
75
+
76
+ if name is not None and name in taken_server_names:
77
+ raise ValueError(f"{name} is already used")
78
+
79
+ self.high_speed_logging.append(
80
+ HSLEntry(
81
+ name=name,
82
+ vrf=Global[str](value=vrf),
83
+ serverIp=Global[Union[IPv4Address, IPv6Address]](value=serverIp),
84
+ port=Global[int](value=port),
85
+ )
86
+ )
87
+
88
+ def get_next_server_name(self) -> Optional[HSLName]:
89
+ taken_server_names = {entry.name for entry in self.high_speed_logging}
90
+ name: Optional[HSLName] = next((name for name in SERVER_NAMES if name not in taken_server_names), None)
91
+ return name
@@ -0,0 +1,57 @@
1
+ # Copyright 2026 Cisco Systems, Inc. and its affiliates
2
+ from typing import Iterable, List, Literal, Optional
3
+ from uuid import UUID
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+ from typing_extensions import Self
7
+
8
+ CatalogObjectType = Literal[
9
+ "config-group",
10
+ "policy-group",
11
+ "topology-group",
12
+ ]
13
+
14
+
15
+ class CatalogPackageContents(BaseModel):
16
+ model_config = ConfigDict(populate_by_name=True)
17
+ object_list: List[UUID] = Field(
18
+ default_factory=list,
19
+ validation_alias="objectList",
20
+ serialization_alias="objectList",
21
+ description="list of object uuids",
22
+ )
23
+ object_type: Optional[CatalogObjectType] = Field(
24
+ default=None, validation_alias="objectType", serialization_alias="objectType"
25
+ )
26
+
27
+
28
+ class CatalogPackageExportRequest(BaseModel):
29
+ model_config = ConfigDict(populate_by_name=True)
30
+ package_contents: List[CatalogPackageContents] = Field(
31
+ default_factory=list,
32
+ validation_alias="packageContents",
33
+ serialization_alias="packageContents",
34
+ description="package contents",
35
+ )
36
+
37
+ def add_objects(self, object_type: CatalogObjectType, ids: Iterable[UUID]) -> Self:
38
+ contents = CatalogPackageContents(object_type=object_type, object_list=list(ids))
39
+ self.package_contents.append(contents)
40
+ return self
41
+
42
+
43
+ class CatalogPackageImportResponse(BaseModel):
44
+ model_config = ConfigDict(populate_by_name=True)
45
+ task_id: UUID = Field(validation_alias="taskId", serialization_alias="taskId")
46
+
47
+
48
+ class TemplatePackageExportRequest(BaseModel):
49
+ model_config = ConfigDict(populate_by_name=True)
50
+ template_ids: Optional[List[UUID]] = Field(
51
+ default=list(),
52
+ validation_alias="templateIds",
53
+ serialization_alias="templateIds",
54
+ description="Select Device Templates for export by providing list of Ids"
55
+ "when list empty or parameter is missing all templates will be provided",
56
+ )
57
+ extras: bool = Field(default=True, description="Include extra items for Config Migration Tool")
catalystwan/session.py CHANGED
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
11
11
  from urllib.parse import urljoin, urlparse, urlunparse
12
12
 
13
13
  from packaging.version import Version # type: ignore
14
- from requests import PreparedRequest, Request, Response, Session, get, head
14
+ from requests import PreparedRequest, Request, Response, Session, Timeout, get, head
15
15
  from requests.exceptions import ConnectionError, HTTPError, RequestException
16
16
 
17
17
  from catalystwan import USER_AGENT
@@ -394,10 +394,13 @@ class ManagerSession(ManagerResponseAdapter, APIEndpointClient):
394
394
  headers={"User-Agent": USER_AGENT},
395
395
  )
396
396
  self.logger.debug(self.response_trace(resp, None))
397
- if resp.status_code != 503:
397
+ if resp.ok:
398
398
  available = True
399
- except ConnectionError as error:
399
+ except (ConnectionError, Timeout) as error:
400
400
  self.logger.debug(self.response_trace(error.response, error.request))
401
+ except RequestException as exception:
402
+ self.logger.debug(self.response_trace(exception.response, exception.request))
403
+ raise ManagerRequestException(*exception.args)
401
404
  if not available:
402
405
  sleep(poll_period)
403
406
  continue
@@ -0,0 +1,73 @@
1
+ # Copyright 2026 Cisco Systems, Inc. and its affiliates
2
+ import json
3
+
4
+ from catalystwan.models.configuration.feature_profile.sdwan.policy_object.security.url import URLParcel
5
+
6
+
7
+ def test_url_parcel_round_trip():
8
+ # Arrange
9
+ json_data = """
10
+ {
11
+ "name": "Forbidden",
12
+ "description": "Uses new format with type field inside data structure > 20.15.2",
13
+ "data": {
14
+ "type": "urlblocked",
15
+ "entries": [
16
+ {
17
+ "pattern": {
18
+ "optionType": "global",
19
+ "value": "bbc.com"
20
+ }
21
+ }
22
+ ]
23
+ }
24
+ }
25
+ """
26
+ # Act
27
+ validated_parcel = URLParcel.model_validate_json(json_data)
28
+ serialized_data = validated_parcel.model_dump_json(by_alias=True, exclude_none=True)
29
+ serialized_data_dict = json.loads(serialized_data)
30
+ round_trip_parcel = URLParcel.model_validate_json(serialized_data)
31
+
32
+ # Assert
33
+ assert validated_parcel.subtype == "urlblocked"
34
+ assert validated_parcel.legacy_subtype is None
35
+ assert serialized_data_dict.get("data", {}).get("type") == "urlblocked"
36
+ assert serialized_data_dict.get("type") is None
37
+ assert round_trip_parcel.subtype == "urlblocked"
38
+ assert round_trip_parcel.legacy_subtype is None
39
+ assert validated_parcel == round_trip_parcel
40
+
41
+
42
+ def test_url_parcel_round_trip_legacy_type_field():
43
+ json_data = """
44
+ {
45
+ "name": "Forbidden",
46
+ "description": "Uses legacy format with type field on top level <= 20.15.2",
47
+ "type": "urlblocked",
48
+ "data": {
49
+ "entries": [
50
+ {
51
+ "pattern": {
52
+ "optionType": "global",
53
+ "value": "bbc.com"
54
+ }
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ """
60
+ # Act
61
+ validated_parcel = URLParcel.model_validate_json(json_data)
62
+ serialized_data = validated_parcel.model_dump_json(by_alias=True, exclude_none=True)
63
+ serialized_data_dict = json.loads(serialized_data)
64
+ round_trip_parcel = URLParcel.model_validate_json(serialized_data)
65
+
66
+ # Assert
67
+ assert validated_parcel.subtype is None
68
+ assert validated_parcel.legacy_subtype == "urlblocked"
69
+ assert serialized_data_dict.get("data", {}).get("type") is None
70
+ assert serialized_data_dict.get("type") == "urlblocked"
71
+ assert round_trip_parcel.subtype is None
72
+ assert round_trip_parcel.legacy_subtype == "urlblocked"
73
+ assert validated_parcel == round_trip_parcel
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: catalystwan
3
- Version: 0.41.2.dev8
3
+ Version: 0.41.2.dev9
4
4
  Summary: Cisco Catalyst WAN SDK for Python
5
5
  Author: kagorski
6
6
  Author-email: kagorski@cisco.com
@@ -109,7 +109,8 @@ catalystwan/endpoints/configuration/feature_profile/sdwan/system.py,sha256=b8QFq
109
109
  catalystwan/endpoints/configuration/feature_profile/sdwan/topology.py,sha256=h1pZ3WXhAnOOFRSj8XfaMb4hC1DZPhfx3tBoXeIzGSc,3676
110
110
  catalystwan/endpoints/configuration/feature_profile/sdwan/transport.py,sha256=EFAsy9AvnGrh6I70jesrxbLChQGUfG_cAzlJPnfl780,9711
111
111
  catalystwan/endpoints/configuration/feature_profile/sdwan/uc_voice.py,sha256=0yn4peVqLxljGvYGC5G6BpXUseP4lOC4WPxZ1jxJyoo,3398
112
- catalystwan/endpoints/configuration/network_hierarchy.py,sha256=ZorYXyH4Rs6y0lXtMNlSsVX0k1iucv_wyvaagvX1TJk,1260
112
+ catalystwan/endpoints/configuration/network_hierarchy.py,sha256=WgJA6CvU4m6ptKOe5nsCSBIGh3Sdq_WA2WQHo9kVsZE,2243
113
+ catalystwan/endpoints/configuration/packages.py,sha256=hcKee_1YYWLlwzSQ7fAoHIGvdGAlxDBVIhEnt0R27_o,1309
113
114
  catalystwan/endpoints/configuration/policy/abstractions.py,sha256=d9W8YCNclZAHAEwRW3L113dKzPdHvUDYH042tM4bDGI,1417
114
115
  catalystwan/endpoints/configuration/policy/definition/access_control_list.py,sha256=XJ6CfF_6QNjTy782yv_0mF-1GFca-KIlTb9ZyVO8RHI,1969
115
116
  catalystwan/endpoints/configuration/policy/definition/access_control_list_ipv6.py,sha256=HhRNMZYyRgMhdkHfBGMg3FgfPt9pFgdU_6fNgj_M9PU,2024
@@ -199,7 +200,7 @@ catalystwan/endpoints/configuration_general_template.py,sha256=s5haSNhTFZwts9rcu
199
200
  catalystwan/endpoints/configuration_group.py,sha256=gyzDKieiARmLx0dbod8zVkByheuyIB2oCALYh_Mj5lQ,6387
200
201
  catalystwan/endpoints/configuration_settings.py,sha256=sQmBHQsDi8bAjLQ9ehNY22sibTnm0YnCL35fXG8uYQ4,28843
201
202
  catalystwan/endpoints/configuration_template_master.py,sha256=1TFFp32cGZf6SUXSqa2LgFrMerxkG1xZl6mBDI8fMgk,1494
202
- catalystwan/endpoints/endpoints_container.py,sha256=2WzprZWaw3ElfrV4X_DQKwISd4HJxXGtTa-z1PxoBsU,19463
203
+ catalystwan/endpoints/endpoints_container.py,sha256=NedpkjeZOg-Uqv2H8rr8wX1zW3kxH50141cmEr7dN8w,19752
203
204
  catalystwan/endpoints/misc.py,sha256=1XaP-xVq0SgjlBL2jmDbdeHswRYikELl4BbhCYQabgU,760
204
205
  catalystwan/endpoints/monitoring/device_details.py,sha256=9OjJbH7hXGnKkhb8tAI3RFMDcDyrDbAH1i2d4LmJhBc,8272
205
206
  catalystwan/endpoints/monitoring/security_policy.py,sha256=oE58nDgcQwn6l2DHigwjzJe8SjqVrn5UcPYwWQDwdVw,400
@@ -246,7 +247,7 @@ catalystwan/models/configuration/common.py,sha256=2X9gj5awWPKtLPFgWfxhkRrMoX22t1
246
247
  catalystwan/models/configuration/feature_profile/README.md,sha256=IQMhbnEAbLtotT80aL2emekH-ou8CqR0WSYXhZXilJw,10050
247
248
  catalystwan/models/configuration/feature_profile/builder.py,sha256=GDqK7ZxnWHTKWHRw13xvD1aDl60xe6gFsP_B9z5T8a8,663
248
249
  catalystwan/models/configuration/feature_profile/common.py,sha256=a5xohaoXzAewtqYYdk2GJy7B-rxxlxYatlP2WZ1eZqY,30423
249
- catalystwan/models/configuration/feature_profile/parcel.py,sha256=OD7rG4j55M_uAnA_rLGnX_Y0xddcxuEflAOL9-DPXPQ,7582
250
+ catalystwan/models/configuration/feature_profile/parcel.py,sha256=Ai8hoV4gbb8UyiS47Kg08nqx_8QDwjkDVJPpLLhieks,7606
250
251
  catalystwan/models/configuration/feature_profile/sdwan/acl/__init__.py,sha256=-AXvVMLyaAYWUnwzTT_mvar37ZpO2vNRy_ZsUIA5dGo,324
251
252
  catalystwan/models/configuration/feature_profile/sdwan/acl/ipv4acl.py,sha256=HpiY0DCZWE9EHCbMbyIJ-iRharSRO3IGcSV3kWbMXjY,12167
252
253
  catalystwan/models/configuration/feature_profile/sdwan/acl/ipv6acl.py,sha256=MRtDNb2v1TNNf3yijLFlzL3H1KxHOquPmww1V9ZCacI,12969
@@ -267,7 +268,7 @@ catalystwan/models/configuration/feature_profile/sdwan/other/__init__.py,sha256=
267
268
  catalystwan/models/configuration/feature_profile/sdwan/other/cybervision.py,sha256=lrnbpHvceKTYZM73FbYtY_1_G_dXjfat7x5g_BAnfSM,2070
268
269
  catalystwan/models/configuration/feature_profile/sdwan/other/thousandeyes.py,sha256=aA5PmFl5v_uvGIIAt16tlVf0OxQKuc-FkEEyg2OmD_I,4806
269
270
  catalystwan/models/configuration/feature_profile/sdwan/other/ucse.py,sha256=fdaqQQSutvGXClaXsn89IfR2fQS2AJNKZqhCg3pWqPg,4021
270
- catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py,sha256=RbQgaZxz7dEikwAYVdh4oSA6rZUA0muZIS-bhOUWzBM,6047
271
+ catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py,sha256=P-i2iM9De62DZuy_YxC2RHhwFVlXx60PlLdBs_GTCm4,5957
271
272
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/app_probe.py,sha256=sLmhwa63b5OnxXqHrCMLMSQspUDx3LBcmEyCssup9CY,1989
272
273
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/application_list.py,sha256=-ctN0OraZt3rG-Wddn8oQ4R4z4rxYcz2YXhiQptQtec,1275
273
274
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/as_path.py,sha256=vBB7ZMET0P6im7ydGVvEC5fr_fjRX1h5InTZBPqFxZM,894
@@ -307,7 +308,7 @@ catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/sc
307
308
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/security_port.py,sha256=t2EMKO9EKAIdiTnsVVSBhaodJIhfjJJdI-_NFhsaQgw,1537
308
309
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ssl_decryption.py,sha256=e8d0kMGFJgA5Vq1S1IwtS5cfoJwUQeZSX9N76TdSjIw,6060
309
310
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ssl_decryption_profile.py,sha256=cZrB1zD-I_bwVmO4RUy1JqwCoDzi2g54j6h_xUfrxY4,5686
310
- catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py,sha256=XlfqSuw7fQ0ZMHiP0kd8G-mOQYjazpm5ou6lGAOGKZw,1076
311
+ catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py,sha256=DCE40uXFsomS9FM8HN4KFbocoVk21Uty4DO7SuJPu50,1855
311
312
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url_filtering.py,sha256=q3bxXSORhiawD7bkC6FS_MzZ5O1F40PRjff13--jgdE,5006
312
313
  catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/zone.py,sha256=AvVybeixunDcAYvAc_SrxgO9hqMLklI3jgAV5Pynp-8,1366
313
314
  catalystwan/models/configuration/feature_profile/sdwan/routing/__init__.py,sha256=THVlfBCt30hlpTOZ9XqCEdcO5IF9hhXxf5q7CfoDmBk,667
@@ -320,7 +321,7 @@ catalystwan/models/configuration/feature_profile/sdwan/service/dhcp_server.py,sh
320
321
  catalystwan/models/configuration/feature_profile/sdwan/service/dual_router_ha.py,sha256=MMwnBKBth3E91EYCPLtGJqwQYilZPWelGF6KLJmtOPw,1134
321
322
  catalystwan/models/configuration/feature_profile/sdwan/service/eigrp.py,sha256=d02hMwUFq5ycoknQci0SAgRlxuiYh0kSKDBxOzn-ves,4075
322
323
  catalystwan/models/configuration/feature_profile/sdwan/service/lan/common.py,sha256=-cLq6fmlJS0rXUP07QH-ItMfZrqzj5bJCFl2D1t0iU0,1349
323
- catalystwan/models/configuration/feature_profile/sdwan/service/lan/ethernet.py,sha256=t4LI4VroBqRDAn54ZSv1BYg_YHCZojNsbRnQEnUvJE8,18738
324
+ catalystwan/models/configuration/feature_profile/sdwan/service/lan/ethernet.py,sha256=icQXfd8dUa1AIjRCHDIfu6laxXKS8crKtQPw3dNfzE4,18718
324
325
  catalystwan/models/configuration/feature_profile/sdwan/service/lan/gre.py,sha256=gGyTnQPVmJqj2PF-976XwoItSG3ZiNBtE9vB0zQPbHc,6912
325
326
  catalystwan/models/configuration/feature_profile/sdwan/service/lan/ipsec.py,sha256=jFwQK-xLl_l9aahCX326wRIcEobElfdFxeZY0ofXD5w,7043
326
327
  catalystwan/models/configuration/feature_profile/sdwan/service/lan/multilink.py,sha256=EKUIQDq40P01irYFWZ1fACFK6R8AusT6s96bH4qHWUg,5403
@@ -349,7 +350,7 @@ catalystwan/models/configuration/feature_profile/sdwan/system/mrf.py,sha256=8GWp
349
350
  catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py,sha256=CzCUErRvlYR27r90G2PM8symb-9pCJaU7O3MxfxODTI,4008
350
351
  catalystwan/models/configuration/feature_profile/sdwan/system/omp.py,sha256=Fat_1uTQ4MA3vUz5klBBTdxtPouHWGRchP6AJvsvD6M,5603
351
352
  catalystwan/models/configuration/feature_profile/sdwan/system/security.py,sha256=tU81HD7lU6DHCXZT_8ssYHqX9awmcUfBG-I671BQNio,6663
352
- catalystwan/models/configuration/feature_profile/sdwan/system/snmp.py,sha256=mNLdqslMlti637lRA-ImZDbRJ17w-Mn7AiyPocQEAfc,6962
353
+ catalystwan/models/configuration/feature_profile/sdwan/system/snmp.py,sha256=f9ZgOnEdeTPrAHD1QX2RjowJ57BllG7jIsfDFlmG-jU,6972
353
354
  catalystwan/models/configuration/feature_profile/sdwan/topology/__init__.py,sha256=lDDba2HdpMV6yBnFFfRMjZ_5pZU1IAdzezl5HPVhct0,782
354
355
  catalystwan/models/configuration/feature_profile/sdwan/topology/custom_control.py,sha256=0ssncF2vpEweExSDoUwv1xxiJthoMiIVH_n-Nhor_pY,16414
355
356
  catalystwan/models/configuration/feature_profile/sdwan/topology/hubspoke.py,sha256=UeCZacMO_t5UGmKjlMkH33fLtHi1XVDGWMTidQe60yw,2228
@@ -365,7 +366,7 @@ catalystwan/models/configuration/feature_profile/sdwan/transport/management/ethe
365
366
  catalystwan/models/configuration/feature_profile/sdwan/transport/t1e1controller.py,sha256=CGYgVu6S0bH699KMutVLNaUwSN2W1r-7FpGirIBt-ig,4456
366
367
  catalystwan/models/configuration/feature_profile/sdwan/transport/vpn.py,sha256=rHHtuOC_6YGhuOFJkMd4vH_vNfwwJLQBDCOJYFxMs0s,10966
367
368
  catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/cellular.py,sha256=UB5LQo9G-b4R7rE-jcJFKcjeGB173UPYK-9beXjcJ5I,8834
368
- catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/ethernet.py,sha256=2jWoQv-A2LuRGsoRhBt6IRbeq3lb2g_y8AW0HYuEgf4,18669
369
+ catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/ethernet.py,sha256=XLflic6reXo85kjCqpoXblg0hdSXX7UryxCAp8TfCnU,18649
369
370
  catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/gre.py,sha256=FwvbIr4I9Vz2CGPYmGgnJRfZQHd8Sy8FQWpVvdEpwsI,2169
370
371
  catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/ipsec.py,sha256=_PEFBj2gufDmrTY4L4cG1xkStR1Y144-KcauVGq_yeA,7447
371
372
  catalystwan/models/configuration/feature_profile/sdwan/transport/wan/interface/multilink.py,sha256=n3CKi19rvGcspk7g6a8hUv_wOeWqYvCuszf9FAbT4m0,11487
@@ -385,9 +386,11 @@ catalystwan/models/configuration/feature_profile/sdwan/uc_voice/translation_rule
385
386
  catalystwan/models/configuration/feature_profile/sdwan/uc_voice/trunk_group.py,sha256=ZoL4g00rczi9VRDwhx11sskV7RqUfp8hK3cCsf2LA6M,1425
386
387
  catalystwan/models/configuration/feature_profile/sdwan/uc_voice/voice_global.py,sha256=9v3-IBHo0Qzxlguq6k8UbCE1SKJdoeoOO0q65HsoWIQ,2290
387
388
  catalystwan/models/configuration/feature_profile/sdwan/uc_voice/voice_tenant.py,sha256=7av6UzCBbztqFNbfhRIbJGDJg87n3QrTzZL9KT7Pems,1365
388
- catalystwan/models/configuration/network_hierarchy/__init__.py,sha256=jGlyKaRRsfrK0gGKoPXqRPmO-myVgfbZVm3FzVkWSts,425
389
+ catalystwan/models/configuration/network_hierarchy/__init__.py,sha256=mOJHS_J4vCt6A6Zj1SD8iUQmK8f97zF1zBAP4U_iwgo,529
389
390
  catalystwan/models/configuration/network_hierarchy/cflowd.py,sha256=3C6UHFpc1isuBvpui9HLoIn63esS2-ucpnLBQS8I-Ak,5530
390
391
  catalystwan/models/configuration/network_hierarchy/node.py,sha256=Ex1OMLl00uU3y4fSSD-xl-t1AJYotYhFRr0KQ826pK8,2792
392
+ catalystwan/models/configuration/network_hierarchy/security_logging.py,sha256=gE-bkLnAKAJTsLfxK1A3hSbET1bQCWH8X7ZQ-WFtF8I,3281
393
+ catalystwan/models/configuration/packages.py,sha256=udhaMRv4zatrxUKCdpnXacsCdgjM61M4NAvlTAjjma8,2037
391
394
  catalystwan/models/configuration/policy_group.py,sha256=8A_TsNmjSRVx1BAlYUgQQWkpp_SizDL_ZTS8zUTtHgs,2235
392
395
  catalystwan/models/configuration/profile_type.py,sha256=qIlobJmEYQ5qEGZeYJZsVMaXtFbR7RgFr6eH8YyoNAo,157
393
396
  catalystwan/models/configuration/topology_group.py,sha256=zN2O9oEc_h1iwI9p8t6MNx5Oa-pA_Z2GreqMoHh9t6E,1804
@@ -479,11 +482,12 @@ catalystwan/models/tenant.py,sha256=UCACzI8n12UT-zpPvPqEQKfbkX9SsDMkET3Lm1kTP0Q,
479
482
  catalystwan/models/url_monitoring.py,sha256=y8xl0BefAFy5oJCGduiaCRowXRtadm8-ABiQVwQ7p34,924
480
483
  catalystwan/request_limiter.py,sha256=-SQCiA7CIjVHLXThxq034ufFigHEKA3xGPH9akBRK-w,577
481
484
  catalystwan/response.py,sha256=GRg2ivRb88TXLsXCRZRW1Ix6uyTF9iEyXmLEEYUzeMM,10561
482
- catalystwan/session.py,sha256=uSMOeDitZ8lyuFV9iFpDE5YdQKfXvQp27q6GcyilCJQ,22844
485
+ catalystwan/session.py,sha256=mF4NyNnQG5opLNXtl_LxYS5XqBFpi-L3MvgU6A_P1Po,23055
483
486
  catalystwan/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
484
487
  catalystwan/tests/builders/test_report.py,sha256=BAppdGUpkH_SQrYmnB5NYutK-beRsz7aoiDPk5o74zw,2227
485
488
  catalystwan/tests/builders/uc_voice.py,sha256=9oU5mxRK2quLXeutxVEhdMSbnj-UEY7gugYNupUYX_8,3265
486
489
  catalystwan/tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
490
+ catalystwan/tests/models/configuration/feature_profile/sdwan/policy_object/security/test_url.py,sha256=xpfNl5RSWQMRW4PPX51IbcqVUbsumXth4F0c41cd_YQ,2528
487
491
  catalystwan/tests/models/test_digital_interface.py,sha256=tYz6xAjy-IqVhYjY5_C_dGLZnFwlm0s9WJEUjhzl8uw,2302
488
492
  catalystwan/tests/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
489
493
  catalystwan/tests/templates/definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -631,7 +635,7 @@ catalystwan/version.py,sha256=X5yIMPIrWPbdoF9eJfwxfjcvWr7JMfWQF98hQhlGB5I,3032
631
635
  catalystwan/vmanage_auth.py,sha256=UZ57lTihsLsXw4AdU7Vn8XuY1jo3732Vc9WuFoHSiiQ,10780
632
636
  catalystwan/workflows/backup_restore_device_templates.py,sha256=BoR2KlRbpCI6RxIWG7j7tucaLpw5w3ESN20dLhsrZFY,19796
633
637
  catalystwan/workflows/tenant_migration.py,sha256=4KLbqUwH4gEegE92PCGfRd226eZd_crCUpaYUWoGXDI,9790
634
- catalystwan-0.41.2.dev8.dist-info/LICENSE,sha256=97ROi91Vxrj_Jio2v8FI9E8-y6x2uYdQRaFlrEbVjkc,11375
635
- catalystwan-0.41.2.dev8.dist-info/METADATA,sha256=E80Sr2BaABziMkStuVLgJ3yWOf5DNI0qgYgGIKzr9N4,21275
636
- catalystwan-0.41.2.dev8.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
637
- catalystwan-0.41.2.dev8.dist-info/RECORD,,
638
+ catalystwan-0.41.2.dev9.dist-info/LICENSE,sha256=97ROi91Vxrj_Jio2v8FI9E8-y6x2uYdQRaFlrEbVjkc,11375
639
+ catalystwan-0.41.2.dev9.dist-info/METADATA,sha256=OwxflrQMhQNMqE-1wyD756PVtpLJH5bFTEjYa_M0WFY,21275
640
+ catalystwan-0.41.2.dev9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
641
+ catalystwan-0.41.2.dev9.dist-info/RECORD,,