From 3155a5ec9b77a867885e65d31fea10f876e4aaf2 Mon Sep 17 00:00:00 2001 From: Jakub Krajewski Date: Tue, 23 Apr 2024 14:50:56 +0200 Subject: [PATCH] Fix integration test for 20.13. Add delete_parcel for service api. --- catalystwan/api/feature_profile_api.py | 6 ++ .../feature_profile/sdwan/service.py | 5 ++ .../feature_profile/sdwan/test_service.py | 69 +++++++++++-------- .../feature_profile/sdwan/service/lan/gre.py | 30 ++++---- .../feature_profile/sdwan/service/ospf.py | 10 +-- catalystwan/tests/test_feature_profile_api.py | 35 ++++++++-- 6 files changed, 101 insertions(+), 54 deletions(-) diff --git a/catalystwan/api/feature_profile_api.py b/catalystwan/api/feature_profile_api.py index 152ecdf3..f06ec20d 100644 --- a/catalystwan/api/feature_profile_api.py +++ b/catalystwan/api/feature_profile_api.py @@ -252,6 +252,12 @@ def create_parcel( ) return self.endpoint.create_service_parcel(profile_uuid, payload._get_parcel_type(), payload) + def delete_parcel(self, profile_uuid: UUID, parcel_type: Type[AnyServiceParcel], parcel_uuid: UUID) -> None: + """ + Delete Service Parcel for selected profile_uuid based on payload type + """ + return self.endpoint.delete_service_parcel(profile_uuid, parcel_type._get_parcel_type(), parcel_uuid) + class SystemFeatureProfileAPI: """ diff --git a/catalystwan/endpoints/configuration/feature_profile/sdwan/service.py b/catalystwan/endpoints/configuration/feature_profile/sdwan/service.py index e600a6ea..1b3bf387 100644 --- a/catalystwan/endpoints/configuration/feature_profile/sdwan/service.py +++ b/catalystwan/endpoints/configuration/feature_profile/sdwan/service.py @@ -47,6 +47,11 @@ def create_service_parcel( ) -> ParcelCreationResponse: ... + @versions(supported_versions=(">=20.9"), raises=False) + @delete("/v1/feature-profile/sdwan/service/{profile_uuid}/{parcel_type}/{parcel_uuid}") + def delete_service_parcel(self, profile_uuid: UUID, parcel_type: str, parcel_uuid: UUID) -> None: + ... + @versions(supported_versions=(">=20.9"), raises=False) @post("/v1/feature-profile/sdwan/service/{profile_uuid}/lan/vpn/{vpn_uuid}/{parcel_type}") def create_lan_vpn_sub_parcel( diff --git a/catalystwan/integration_tests/feature_profile/sdwan/test_service.py b/catalystwan/integration_tests/feature_profile/sdwan/test_service.py index eeac7997..057d9108 100644 --- a/catalystwan/integration_tests/feature_profile/sdwan/test_service.py +++ b/catalystwan/integration_tests/feature_profile/sdwan/test_service.py @@ -1,5 +1,4 @@ from ipaddress import IPv4Address -from secrets import token_hex from uuid import UUID from catalystwan.api.configuration_groups.parcel import Global, as_global, as_variable @@ -17,12 +16,12 @@ SummaryAddress, ) from catalystwan.models.configuration.feature_profile.sdwan.service.lan.ethernet import InterfaceEthernetParcel -from catalystwan.models.configuration.feature_profile.sdwan.service.lan.gre import BasicGre, InterfaceGreParcel -from catalystwan.models.configuration.feature_profile.sdwan.service.lan.ipsec import ( - InterfaceIpsecParcel, - IpsecAddress, - IpsecTunnelMode, +from catalystwan.models.configuration.feature_profile.sdwan.service.lan.gre import ( + BasicGre, + GreAddress, + InterfaceGreParcel, ) +from catalystwan.models.configuration.feature_profile.sdwan.service.lan.ipsec import InterfaceIpsecParcel, IpsecAddress from catalystwan.models.configuration.feature_profile.sdwan.service.lan.svi import InterfaceSviParcel from catalystwan.models.configuration.feature_profile.sdwan.service.lan.vpn import LanVpnParcel from catalystwan.models.configuration.feature_profile.sdwan.service.multicast import ( @@ -212,21 +211,14 @@ def test_when_default_values_acl_ipv4_expect_successful_post(self): # Assert assert parcel_id - def test_when_default_values_switchport_expect_successful_post(self): + def test_when_correct_values_switchport_parcel_expect_successful_post(self): # Arrange - switchport_parcel = SwitchportParcel( - parcel_name="TestSwitchportParcel", + switchport_default_values_parcel = SwitchportParcel( + parcel_name="TestSwitchportParcelDefaultValues", parcel_description="Test Switchport Parcel", ) - # Act - parcel_id = self.api.create_parcel(self.profile_uuid, switchport_parcel).id - # Assert - assert parcel_id - - def test_when_fully_specified_values_switchport_expect_successful_post(self): - # Arrange - switchport_parcel = SwitchportParcel( - parcel_name="TestSwitchportParcel", + switchport_fully_specified_parcel = SwitchportParcel( + parcel_name="TestSwitchportParcelFullySpecified", parcel_description="Test Switchport Parcel", age_time=Global[int](value=100), static_mac_address=[ @@ -257,10 +249,15 @@ def test_when_fully_specified_values_switchport_expect_successful_post(self): ) ], ) + switchport_parcels = [switchport_default_values_parcel, switchport_fully_specified_parcel] # Act - parcel_id = self.api.create_parcel(self.profile_uuid, switchport_parcel).id - # Assert - assert parcel_id + for switchport_parcel in switchport_parcels: + with self.subTest(switchport_parcel=switchport_parcel.parcel_name): + parcel_id = self.api.create_parcel(self.profile_uuid, switchport_parcel).id + # Assert + assert parcel_id + # Cleanup + self.api.delete_parcel(self.profile_uuid, SwitchportParcel, parcel_id) def test_when_default_values_multicast_expect_successful_post(self): # Arrange @@ -376,7 +373,7 @@ def test_when_fully_specified_values_wireless_lan_expect_successful_post(self): enable_5G=as_global(True), country=as_global("US", CountryCode), username=as_global("admin"), - password=as_global(token_hex(16) + "TEST!@#"), + password=as_variable("{{wireless_lan_password}}"), ssid=[ SSID( name=as_global("TestSSID"), @@ -413,7 +410,7 @@ def tearDownClass(cls) -> None: super().tearDownClass() -class TestServiceFeatureProfileVPNInterfaceModels(TestFeatureProfileModels): +class TestServiceFeatureProfileVPNSubparcelModels(TestFeatureProfileModels): vpn_parcel_uuid: UUID @classmethod @@ -433,7 +430,14 @@ def test_when_default_values_gre_parcel_expect_successful_post(self): gre_parcel = InterfaceGreParcel( parcel_name="TestGreParcel", parcel_description="Test Gre Parcel", - basic=BasicGre(if_name=as_global("gre1"), tunnel_destination=as_global(IPv4Address("4.4.4.4"))), + basic=BasicGre( + if_name=as_global("gre1"), + address=GreAddress( + address=as_global("1.1.1.1"), + mask=as_global("255.255.255.0"), + ), + tunnel_destination=as_global(IPv4Address("4.4.4.4")), + ), ) # Act parcel_id = self.api.create_parcel(self.profile_uuid, gre_parcel, self.vpn_parcel_uuid).id @@ -477,20 +481,27 @@ def test_when_default_values_ipsec_parcel_expect_successful_post(self): ike_local_id=as_global("123"), ike_remote_id=as_global("123"), application=as_variable("{{ipsec_application}}"), - tunnel_mode=Global[IpsecTunnelMode](value="ipv6"), - tunnel_destination_v6=as_variable("{{ipsec_tunnelDestinationV6}}"), - tunnel_source_v6=Global[str](value="::"), tunnel_source_interface=as_variable("{{ipsec_ipsecSourceInterface}}"), - ipv6_address=as_variable("{{test}}"), address=IpsecAddress(address=as_global("10.0.0.1"), mask=as_global("255.255.255.0")), tunnel_destination=IpsecAddress(address=as_global("10.0.0.5"), mask=as_global("255.255.255.0")), - mtu_v6=as_variable("{{test}}"), ) # Act parcel_id = self.api.create_parcel(self.profile_uuid, ipsec_parcel, self.vpn_parcel_uuid).id # Assert assert parcel_id + def test_when_routing_parcel_and_vpn_uuid_present_expect_create_then_assign_to_vpn(self): + # Arrange + multicast_parcel = MulticastParcel( + parcel_name="TestMulticastParcel", + parcel_description="Test Multicast Parcel", + basic=MulticastBasicAttributes(), + ) + # Act + parcel_id = self.api.create_parcel(self.profile_uuid, multicast_parcel, self.vpn_parcel_uuid).id + # Assert + assert parcel_id + @classmethod def tearDownClass(cls) -> None: cls.api.delete_profile(cls.profile_uuid) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/service/lan/gre.py b/catalystwan/models/configuration/feature_profile/sdwan/service/lan/gre.py index e3f704c2..aaec20ba 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/service/lan/gre.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/service/lan/gre.py @@ -102,10 +102,10 @@ class BasicGre(BaseModel): ) shutdown: Optional[Union[Global[bool], Variable, Default[bool]]] = Default[bool](value=False) tunnel_protection: Optional[Union[Global[bool], Variable, Default[bool]]] = Field( - serialization_alias="tunnelProtection", validation_alias="tunnelProtection", default=Default[bool](value=False) + serialization_alias="tunnelProtection", validation_alias="tunnelProtection", default=None ) - tunnel_mode: Union[Global[GreTunnelMode], Default[GreTunnelMode]] = Field( - default=Default[GreTunnelMode](value="ipv4"), + tunnel_mode: Optional[Union[Global[GreTunnelMode], Default[GreTunnelMode]]] = Field( + default=None, serialization_alias="tunnelMode", validation_alias="tunnelMode", ) @@ -131,30 +131,30 @@ class BasicGre(BaseModel): clear_dont_fragment: Optional[Union[Global[bool], Variable, Default[bool]]] = Field( serialization_alias="clearDontFragment", validation_alias="clearDontFragment", - default=Default[bool](value=False), + default=None, ) dpd_interval: Optional[Union[Global[int], Variable, Default[int]]] = Field( - serialization_alias="dpdInterval", validation_alias="dpdInterval", default=Default[int](value=10) + serialization_alias="dpdInterval", validation_alias="dpdInterval", default=None ) dpd_retries: Optional[Union[Global[int], Variable, Default[int]]] = Field( - serialization_alias="dpdRetries", validation_alias="dpdRetries", default=Default[int](value=3) + serialization_alias="dpdRetries", validation_alias="dpdRetries", default=None ) ike_version: Optional[Union[Global[int], Default[int]]] = Field( - serialization_alias="ikeVersion", validation_alias="ikeVersion", default=Default[int](value=1) + serialization_alias="ikeVersion", validation_alias="ikeVersion", default=None ) ike_mode: Optional[Union[Global[IkeMode], Variable, Default[IkeMode]]] = Field( - serialization_alias="ikeMode", validation_alias="ikeMode", default=Default[IkeMode](value="main") + serialization_alias="ikeMode", validation_alias="ikeMode", default=None ) ike_rekey_interval: Optional[Union[Global[int], Variable, Default[int]]] = Field( - serialization_alias="ikeRekeyInterval", validation_alias="ikeRekeyInterval", default=Default[int](value=14400) + serialization_alias="ikeRekeyInterval", validation_alias="ikeRekeyInterval", default=None ) ike_ciphersuite: Optional[Union[Global[IkeCiphersuite], Variable, Default[IkeCiphersuite]]] = Field( serialization_alias="ikeCiphersuite", validation_alias="ikeCiphersuite", - default=Default[IkeCiphersuite](value="aes256-cbc-sha1"), + default=None, ) ike_group: Optional[Union[Global[IkeGroup], Variable, Default[IkeGroup]]] = Field( - serialization_alias="ikeGroup", validation_alias="ikeGroup", default=Default[IkeGroup](value="16") + serialization_alias="ikeGroup", validation_alias="ikeGroup", default=None ) pre_shared_secret: Optional[Union[Global[str], Variable, Default[None]]] = Field( serialization_alias="preSharedSecret", validation_alias="preSharedSecret", default=None @@ -168,20 +168,20 @@ class BasicGre(BaseModel): ipsec_rekey_interval: Optional[Union[Global[int], Variable, Default[int]]] = Field( serialization_alias="ipsecRekeyInterval", validation_alias="ipsecRekeyInterval", - default=Default[int](value=3600), + default=None, ) ipsec_replay_window: Optional[Union[Global[int], Variable, Default[int]]] = Field( - serialization_alias="ipsecReplayWindow", validation_alias="ipsecReplayWindow", default=Default[int](value=512) + serialization_alias="ipsecReplayWindow", validation_alias="ipsecReplayWindow", default=None ) ipsec_ciphersuite: Optional[Union[Global[IpsecCiphersuite], Variable, Default[IpsecCiphersuite]]] = Field( serialization_alias="ipsecCiphersuite", validation_alias="ipsecCiphersuite", - default=Default[IpsecCiphersuite](value="aes256-gcm"), + default=None, ) perfect_forward_secrecy: Optional[Union[Global[PfsGroup], Variable, Default[PfsGroup]]] = Field( serialization_alias="perfectForwardSecrecy", validation_alias="perfectForwardSecrecy", - default=Default[PfsGroup](value="group-16"), + default=None, ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/service/ospf.py b/catalystwan/models/configuration/feature_profile/sdwan/service/ospf.py index 3f448ac4..79d595c9 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/service/ospf.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/service/ospf.py @@ -120,16 +120,16 @@ class OspfParcel(_ParcelBase): type_: Literal["routing/ospf"] = Field(default="routing/ospf", exclude=True) model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True, extra="forbid") - router_id: Optional[Union[Global[str], Global[IPv4Address], Variable, Default[None]]] = Field( + router_id: Union[Global[str], Global[IPv4Address], Variable, Default[None]] = Field( validation_alias=AliasPath("data", "routerId"), default=Default[None](value=None) ) - reference_bandwidth: Optional[Union[Global[int], Variable, Default[int]]] = Field( + reference_bandwidth: Union[Global[int], Variable, Default[int]] = Field( validation_alias=AliasPath("data", "referenceBandwidth"), default=as_default(100) ) - rfc1583: Optional[Union[Global[bool], Variable, Default[bool]]] = Field( - validation_alias=AliasPath("data", "rfc1583"), default=as_default(False) + rfc1583: Union[Global[bool], Variable, Default[bool]] = Field( + validation_alias=AliasPath("data", "rfc1583"), default=as_default(True) ) - originate: Optional[Union[Global[bool], Default[bool]]] = Field( + originate: Union[Global[bool], Default[bool]] = Field( validation_alias=AliasPath("data", "originate"), default=as_default(False) ) always: Optional[Union[Global[bool], Variable, Default[bool]]] = Field( diff --git a/catalystwan/tests/test_feature_profile_api.py b/catalystwan/tests/test_feature_profile_api.py index 0471b02b..d252e3b3 100644 --- a/catalystwan/tests/test_feature_profile_api.py +++ b/catalystwan/tests/test_feature_profile_api.py @@ -9,6 +9,7 @@ from catalystwan.api.feature_profile_api import ServiceFeatureProfileAPI, SystemFeatureProfileAPI from catalystwan.endpoints.configuration.feature_profile.sdwan.service import ServiceFeatureProfile from catalystwan.endpoints.configuration.feature_profile.sdwan.system import SystemFeatureProfile +from catalystwan.models.configuration.feature_profile.common import ParcelAssociationPayload, ParcelCreationResponse from catalystwan.models.configuration.feature_profile.sdwan.service import ( AppqoeParcel, InterfaceEthernetParcel, @@ -125,7 +126,7 @@ def test_update_method_with_valid_arguments(self, parcel, expected_path): service_interface_parcels = [ ( - "gre", + "interface/gre", InterfaceGreParcel( parcel_name="TestGreParcel", parcel_description="Test Gre Parcel", @@ -133,7 +134,7 @@ def test_update_method_with_valid_arguments(self, parcel, expected_path): ), ), ( - "svi", + "interface/svi", InterfaceSviParcel( parcel_name="TestSviParcel", parcel_description="Test Svi Parcel", @@ -142,7 +143,7 @@ def test_update_method_with_valid_arguments(self, parcel, expected_path): ), ), ( - "ethernet", + "interface/ethernet", InterfaceEthernetParcel( parcel_name="TestEthernetParcel", parcel_description="Test Ethernet Parcel", @@ -151,7 +152,7 @@ def test_update_method_with_valid_arguments(self, parcel, expected_path): ), ), ( - "ipsec", + "interface/ipsec", InterfaceIpsecParcel( parcel_name="TestIpsecParcel", parcel_description="Test Ipsec Parcel", @@ -173,6 +174,16 @@ def test_update_method_with_valid_arguments(self, parcel, expected_path): ), ] +service_vpn_sub_parcels = [ + ( + "routing/multicast", + MulticastParcel( + parcel_name="TestMulticastParcel", + parcel_description="Test Multicast Parcel", + ), + ) +] + class TestServiceFeatureProfileAPI(unittest.TestCase): def setUp(self): @@ -198,6 +209,20 @@ def test_post_method_interface_parcel(self, parcel_type, parcel): self.api.create_parcel(self.profile_uuid, parcel, self.vpn_uuid) # Assert - self.mock_endpoint.create_lan_vpn_interface_parcel.assert_called_once_with( + self.mock_endpoint.create_lan_vpn_sub_parcel.assert_called_once_with( self.profile_uuid, self.vpn_uuid, parcel_type, parcel ) + + @parameterized.expand(service_vpn_sub_parcels) + def test_post_method_create_then_assigin_subparcel(self, parcel_type, parcel): + # Arrange + self.mock_endpoint.create_service_parcel.return_value = ParcelCreationResponse(id=self.parcel_uuid) + + # Act + self.api.create_parcel(self.profile_uuid, parcel, self.vpn_uuid) + + # Assert + self.mock_endpoint.create_service_parcel.assert_called_once_with(self.profile_uuid, parcel_type, parcel) + self.mock_endpoint.associate_parcel_with_vpn.assert_called_once_with( + self.profile_uuid, self.vpn_uuid, parcel_type, ParcelAssociationPayload(parcel_id=self.parcel_uuid) + )