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

Commit

Permalink
Migration Feature Template with IPSec route fails #60
Browse files Browse the repository at this point in the history
  • Loading branch information
jpkrajewski committed Jul 9, 2024
1 parent 4767d9d commit 8f5e4c8
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@
from catalystwan.models.configuration.feature_profile.sdwan.service.lan.ipsec import InterfaceIpsecParcel
from catalystwan.models.configuration.feature_profile.sdwan.service.lan.multilink import InterfaceMultilinkParcel
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.lan.vpn import (
LanVpnParcel,
RoutePrefix,
ServiceRoute,
StaticGreRouteIPv4,
StaticIpsecRouteIPv4,
)
from catalystwan.models.configuration.feature_profile.sdwan.service.multicast import (
AutoRpAttributes,
BsrCandidateAttributes,
Expand Down Expand Up @@ -140,6 +146,27 @@ def test_when_default_values_service_vpn_parcel_expect_successful_post(self):
parcel_name="TestVpnParcel",
parcel_description="Test Vpn Parcel",
vpn_id=Global[int](value=2),
ipsec_route=[
StaticIpsecRouteIPv4(
prefix=RoutePrefix(ip_address=as_global("1.2.3.4"), subnet_mask=as_global("0.0.0.0")),
)
],
gre_route=[
StaticGreRouteIPv4(
prefix=RoutePrefix(
ip_address=as_global("8.6.5.4"),
subnet_mask=as_global("255.255.255.0"),
)
)
],
service_route=[
ServiceRoute(
prefix=RoutePrefix(
ip_address=as_global("8.6.5.2"),
subnet_mask=as_global("255.255.255.0"),
)
)
],
)
# Act
parcel_id = self.api.create_parcel(self.profile_uuid, vpn_parcel).id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023 Cisco Systems, Inc. and its affiliates
# Copyright 2024 Cisco Systems, Inc. and its affiliates

from ipaddress import IPv4Address, IPv6Address, IPv6Interface
from typing import List, Literal, Optional, Union
Expand Down Expand Up @@ -342,7 +342,7 @@ class Service(BaseModel):
class ServiceRoute(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)

prefix: AddressWithMask
prefix: RoutePrefix
service: Union[Variable, Global[ServiceRouteType], Default[ServiceRouteType]] = Default[ServiceRouteType](
value="SIG"
)
Expand All @@ -355,15 +355,15 @@ class ServiceRoute(BaseModel):
class StaticGreRouteIPv4(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)

prefix: AddressWithMask
prefix: RoutePrefix
interface: Union[Variable, Global[List[str]], Default[None]] = Default[None](value=None)
vpn: Global[int] = Global[int](value=0)


class StaticIpsecRouteIPv4(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)

prefix: AddressWithMask
prefix: RoutePrefix
interface: Union[Variable, Global[List[str]], Default[None]] = Default[None](value=None)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023 Cisco Systems, Inc. and its affiliates
# Copyright 2024 Cisco Systems, Inc. and its affiliates
import logging
from copy import deepcopy
from ipaddress import IPv4Interface, IPv6Interface
Expand All @@ -8,7 +8,7 @@

from catalystwan.api.configuration_groups.parcel import Default, Global, OptionType, Variable, as_default, as_global
from catalystwan.models.common import SubnetMask
from catalystwan.models.configuration.feature_profile.common import AddressWithMask, DNSIPv4, DNSIPv6, HostMapping
from catalystwan.models.configuration.feature_profile.common import DNSIPv4, DNSIPv6, HostMapping
from catalystwan.models.configuration.feature_profile.sdwan.service.lan.vpn import (
DHCP,
Direction,
Expand Down Expand Up @@ -73,6 +73,8 @@

logger = logging.getLogger(__name__)

RouteUX2Field = Literal["gre_route", "service_route", "ipsec_route"]


class RouteLeakMappingItem(BaseModel):
ux2_model: Type[Union[RouteLeakFromGlobal, RouteLeakFromService, RouteLeakBetweenServices]]
Expand All @@ -81,7 +83,7 @@ class RouteLeakMappingItem(BaseModel):

class RouteMappingItem(BaseModel):
ux2_model: Type[Union[StaticGreRouteIPv4, StaticIpsecRouteIPv4, ServiceRoute]]
ux2_field: Literal["route_gre", "route_service", "ipsec_route"]
ux2_field: RouteUX2Field


class OmpMappingItem(BaseModel):
Expand Down Expand Up @@ -294,8 +296,8 @@ class ServiceVpnConverter(FTConverter):
}

routes_mapping = {
"route_gre": RouteMappingItem(ux2_model=StaticGreRouteIPv4, ux2_field="route_gre"),
"route_service": RouteMappingItem(ux2_model=ServiceRoute, ux2_field="route_service"),
"gre_route": RouteMappingItem(ux2_model=StaticGreRouteIPv4, ux2_field="gre_route"),
"service_route": RouteMappingItem(ux2_model=ServiceRoute, ux2_field="service_route"),
"ipsec_route": RouteMappingItem(ux2_model=StaticIpsecRouteIPv4, ux2_field="ipsec_route"),
}

Expand Down Expand Up @@ -344,8 +346,8 @@ def create_parcel(self, name: str, description: str, template_values: dict) -> L

omp_advertise_ipv4 = data.get("omp_advertise_ipv4")
omp_advertise_ipv6 = data.get("omp_advertise_ipv6")
route_gre = data.get("route_gre")
route_service = data.get("route_service")
gre_route = data.get("gre_route")
service_route = data.get("service_route")
ipsec_route = data.get("ipsec_route")
route_leak_from_global = data.get("route_leak_from_global")
route_leak_from_service = data.get("route_leak_from_service")
Expand Down Expand Up @@ -373,8 +375,8 @@ def create_parcel(self, name: str, description: str, template_values: dict) -> L
service=service,
omp_advertise_ipv4=omp_advertise_ipv4,
omp_advertise_ipv6=omp_advertise_ipv6,
route_gre=route_gre,
route_service=route_service,
gre_route=gre_route,
service_route=service_route,
ipsec_route=ipsec_route,
route_leak_from_global=route_leak_from_global,
route_leak_from_service=route_leak_from_service,
Expand Down Expand Up @@ -686,15 +688,28 @@ def parse_routes(self, values: dict) -> None:
pydantic_field = self.routes_mapping[route].ux2_field
self._parse_route(values, routes, pydantic_model, pydantic_field)

def _parse_route(self, values: dict, routes: list, pydantic_model, pydantic_field) -> None:
def _parse_route(
self,
values: dict,
routes: list,
pydantic_model,
pydantic_field: RouteUX2Field,
) -> None:
items = []
for route in routes:
ipv4_interface = IPv4Interface(route.get("prefix").value)
service_prefix = AddressWithMask(
address=as_global(ipv4_interface.network.network_address),
mask=as_global(str(ipv4_interface.netmask)),
)
items.append(pydantic_model(prefix=service_prefix, vpn=route.get("vpn")))
prefix = route.get("prefix")
if prefix.option_type == OptionType.VARIABLE:
route_prefix = RoutePrefix(ip_address=prefix, subnet_mask=as_global("0.0.0.0"))
else:
ipv4_interface = IPv4Interface(route.get("prefix").value)
route_prefix = RoutePrefix(
ip_address=as_global(ipv4_interface.network.network_address),
subnet_mask=as_global(str(ipv4_interface.netmask)),
)
route_item = pydantic_model(prefix=route_prefix)
if pydantic_field in ["ipsec_route", "gre_route"]:
route_item.interface = route.get("interface")
items.append(route_item)
values[pydantic_field] = items

def parse_route_leaks(self, values: dict) -> None:
Expand Down

0 comments on commit 8f5e4c8

Please sign in to comment.