From 7b697fe0bc8687de8bff9ab4bc06d6847e2891da Mon Sep 17 00:00:00 2001 From: markharden817 <38341596+markharden817@users.noreply.github.com> Date: Fri, 2 Jul 2021 17:06:27 -0500 Subject: [PATCH] feat: Add RIP support (#330) --- panos/network.py | 238 +++++++++++++++++++++++++++++++++++-- tests/live/test_network.py | 165 ++++++++++++++++++++++++- 2 files changed, 394 insertions(+), 9 deletions(-) diff --git a/panos/network.py b/panos/network.py index c9194161..8ec04b2d 100644 --- a/panos/network.py +++ b/panos/network.py @@ -1765,9 +1765,9 @@ class StaticRoute(VersionedPanObject): admin_dist (str): Administrative distance metric (int): Metric (Default: 10) enable_path_monitor (bool): Enable Path Monitor - failure_condition (str): Path Monitor failure condition set 'any' or 'all' + failure_condition (str): Path Monitor failure condition set 'any' or 'all' preemptive_hold_time (int): Path Monitor Preemptive Hold Time in minutes - + """ SUFFIX = ENTRY @@ -1832,7 +1832,7 @@ class StaticRouteV6(VersionedPanObject): admin_dist (str): Administrative distance metric (int): Metric (Default: 10) enable_path_monitor (bool): Enable Path Monitor - failure_condition (str): Path Monitor failure condition set 'any' or 'all' + failure_condition (str): Path Monitor failure condition set 'any' or 'all' preemptive_hold_time (int): Path Monitor Preemptive Hold Time in minutes """ @@ -1886,16 +1886,16 @@ def _setup(self): class PathMonitorDestination(VersionedPanObject): - """PathMonitorDestination Static Route + """PathMonitorDestination Static Route Args: - name (str): Name of Path Monitor Destination - enable (bool): Enable Path Monitor Destination + name (str): Name of Path Monitor Destination + enable (bool): Enable Path Monitor Destination source (str): Source ip of interface - destination (str): Destination ip + destination (str): Destination ip interval (int): Ping Interval (sec) (Default: 3) count (int): Ping count (Default: 5) - + """ SUFFIX = ENTRY @@ -1947,6 +1947,7 @@ class VirtualRouter(VsysOperations): "network.RedistributionProfileIPv6", "network.Ospf", "network.Bgp", + "network.Rip", ) def _setup(self): @@ -2125,6 +2126,227 @@ def _setup(self): RedistributionProfileBase._setup(self) +class Rip(VersionedPanObject): + """Rip + + Add to a :class:`panos.network.VirtualRouter` instance. + + Args: + enable (bool): Enable RIP + reject_default_route (bool): Reject default route + allow_redist_default_route (bool): Allow Redistribute Default Route + delete_intervals (int): Delete Intervals + expire_intervals (int): Expire Intervals + interval_seconds (int): Interval Seconds (sec) + update_intervals (int): Update Intervals + """ + + NAME = None + CHILDTYPES = ( + "network.RipInterface", + "network.RipAuthProfile", + "network.RipExportRule", + ) + + def _setup(self): + # xpaths + self._xpaths.add_profile(value="/protocol/rip") + + # params + params = [] + + params.append( + VersionedParamPath("enable", path="enable", default=True, vartype="yesno") + ) + params.append( + VersionedParamPath("reject_default_route", default=True, vartype="yesno",) + ) + params.append( + VersionedParamPath( + "allow_redist_default_route", + path="allow-redist-default-route", + vartype="yesno", + ) + ) + params.append( + VersionedParamPath( + "delete_intervals", + path="timers/delete-intervals", + vartype="int", + default=120, + ) + ) + params.append( + VersionedParamPath( + "expire_intervals", + path="timers/expire-intervals", + vartype="int", + default=180, + ) + ) + params.append( + VersionedParamPath( + "interval_seconds", + path="timers/interval-seconds", + vartype="int", + default=1, + ) + ) + params.append( + VersionedParamPath( + "update_intervals", + path="timers/update-intervals", + vartype="int", + default=30, + ) + ) + params.append( + VersionedParamPath("global_bfd_profile", path="global-bfd/profile") + ) + + self._params = tuple(params) + + +class RipInterface(VersionedPanObject): + """Rip Interface + + Add to a :class:`panos.network.Rip` instance. + + Args: + name (str): Interface name + enable (bool): Enable + advertise_default_route: Advertise default route + * advertise + * disable + metric (int): Default route metric. Requires {advertise_default_route: "advertise"} + auth_profile (str): Auth profile name + mode (str): Mode of RipInterface + * normal (default) + * passive + * send-only + """ + + SUFFIX = ENTRY + + def _setup(self): + self._xpaths.add_profile(value="/interface") + + params = [] + + params.append( + VersionedParamPath("enable", path="enable", vartype="yesno", default=True) + ) + params.append( + VersionedParamPath( + "advertise_default_route", + values=["advertise", "disable"], + default="disable", + path="default-route/{advertise_default_route}", + ) + ) + params.append( + VersionedParamPath( + "metric", + path="default-route/{advertise_default_route}/metric", + vartype="int", + default=10, + condition={"advertise_default_route": "advertise"}, + ) + ) + params.append(VersionedParamPath("auth_profile", path="authentication")) + params.append( + VersionedParamPath( + "mode", + path="mode", + values=["normal", "passive", "send-only"], + default="normal", + ) + ) + + self._params = tuple(params) + + +class RipAuthProfile(VersionedPanObject): + """Rip Authentication Profile + + Args: + name (str): Name of Auth Profile + auth_type (str): 'password' or 'md5' + password (str): The password if auth_type is set to 'password'. + If auth_type is set to 'md5', add a :class:`panos.network.RipAuthProfileMd5` + + """ + + SUFFIX = ENTRY + CHILDTYPES = ("network.RipAuthProfileMd5",) + + def _setup(self): + self._xpaths.add_profile(value="/auth-profile") + + params = [] + params.append(VersionedParamPath("name")) + params.append( + VersionedParamPath( + "auth_type", values=["password", "md5"], path="{auth_type}" + ) + ) + params.append( + VersionedParamPath( + "password", + condition={"auth_type": "password"}, + path="{auth_type}", + vartype="encrypted", + ) + ) + + self._params = tuple(params) + + +class RipAuthProfileMd5(VersionedPanObject): + """Rip Authentication Profile + + Args: + keyid (int): Identifier for key + key (str): The authentication key + preferred (bool): This key is preferred + + """ + + SUFFIX = ENTRY + NAME = "keyid" + + def _setup(self): + self._xpaths.add_profile(value="/md5") + + params = [] + + params.append(VersionedParamPath("key", vartype="encrypted")) + params.append(VersionedParamPath("preferred", vartype="yesno")) + + self._params = tuple(params) + + +class RipExportRule(VersionedPanObject): + """Rip Export Rules + + Args: + name (str): IP subnet or :class:`panos.network.RedistributionProfile` + metric (int): Metric + + """ + + SUFFIX = ENTRY + + def _setup(self): + self._xpaths.add_profile(value="/export-rules") + + params = [] + + params.append(VersionedParamPath("metric", vartype="int")) + + self._params = tuple(params) + + class Ospf(VersionedPanObject): """OSPF Process diff --git a/tests/live/test_network.py b/tests/live/test_network.py index 1c7ba852..e62238df 100644 --- a/tests/live/test_network.py +++ b/tests/live/test_network.py @@ -1,7 +1,7 @@ import random -from tests.live import testlib from panos import network +from tests.live import testlib class TestZoneBasic(testlib.FwFlow): @@ -669,6 +669,11 @@ class MakeVirtualRouter(testlib.FwFlow): WITH_BGP_PEER = False WITH_BGP_IMPORT_RULE = False WITH_BGP_EXPORT_RULE = False + WITH_RIP = False + WITH_RIP_AUTH_PROFILE = False + WITH_RIP_AUTH_PROFILE_MD5 = False + WITH_RIP_EXPORT_RULES = False + WITH_RIP_INTERFACE = False def create_dependencies(self, fw, state): state.eths = testlib.get_available_interfaces(fw, 2) @@ -707,6 +712,69 @@ def create_dependencies(self, fw, state): state.vr.add(state.redist_profile) state.redist_profile.create() + if any( + ( + self.WITH_RIP, + self.WITH_RIP_AUTH_PROFILE, + self.WITH_RIP_AUTH_PROFILE_MD5, + self.WITH_RIP_EXPORT_RULES, + self.WITH_RIP_INTERFACE, + ) + ): + state.rip = network.Rip( + enable=True, + reject_default_route=False, + allow_redist_default_route=True, + delete_intervals=random.randint(1, 255), + expire_intervals=random.randint(1, 255), + interval_seconds=random.randint(1, 60), + update_intervals=random.randint(1, 255), + ) + state.vr.add(state.rip) + + if self.WITH_RIP_AUTH_PROFILE: + state.rip_auth_profile = network.RipAuthProfile( + testlib.random_name(), + auth_type="password", + password=testlib.random_name(), + ) + state.rip.add(state.rip_auth_profile) + + if self.WITH_RIP_AUTH_PROFILE_MD5: + state.rip_auth_profile = network.RipAuthProfile( + testlib.random_name(), type="md5" + ) + state.md5 = network.RipAuthProfileMd5( + keyid=random.randint(1, 255), + key=testlib.random_name(), + preferred=True, + ) + state.rip_auth_profile.add(state.md5) + state.rip.add(state.rip_auth_profile) + + if self.WITH_RIP_EXPORT_RULES and self.WITH_REDISTRIBUTION_PROFILE: + state.rip_export_rules = network.RipExportRule( + name=str(state.redist_profile), metric=random.randint(1, 15) + ) + state.rip.add(state.rip_export_rules) + + if self.WITH_RIP_INTERFACE: + auth_profile = ( + str(state.rip_auth_profile) if self.WITH_RIP_AUTH_PROFILE else None + ) + state.rip.add( + network.RipInterface( + name=state.eths[0], + enable=True, + advertise_default_route="advertise", + metric=random.randint(1, 15), + auth_profile=auth_profile, + mode="passive", + ) + ) + + state.rip.create() + if any( ( self.WITH_OSPF, @@ -819,6 +887,101 @@ def cleanup_dependencies(self, fw, state): pass +class TestRip(MakeVirtualRouter): + def setup_state_obj(self, fw, state): + state.obj = network.Rip( + enable=True, + reject_default_route=True, + allow_redist_default_route=True, + delete_intervals=random.randint(1, 255), + expire_intervals=random.randint(1, 255), + interval_seconds=random.randint(1, 60), + update_intervals=random.randint(1, 255), + ) + state.vr.add(state.obj) + + def update_state_obj(self, fw, state): + state.obj.enable = True + state.obj.reject_default_route = False + state.obj.allow_redist_default_route = True + state.obj.delete_intervals = random.randint(1, 255) + state.obj.expire_intervals = random.randint(1, 255) + state.obj.interval_seconds = random.randint(1, 60) + state.obj.update_intervals = random.randint(1, 255) + + +class TestRipAuthProfile(MakeVirtualRouter): + WITH_RIP = True + + def setup_state_obj(self, fw, state): + state.obj = network.RipAuthProfile( + name=testlib.random_name(), + auth_type="password", + password=testlib.random_name(), + ) + state.rip.add(state.obj) + + def update_state_obj(self, fw, state): + state.obj.password = testlib.random_name() + + +class TestRipAuthProfileMd5(MakeVirtualRouter): + WITH_RIP_AUTH_PROFILE = True + + def setup_state_obj(self, fw, state): + state.obj = network.RipAuthProfileMd5(keyid="1", key="secret1", preferred=False) + state.rip_auth_profile.add(state.obj) + + def update_state_obj(self, fw, state): + state.obj.preferred = True + + def test_05_add_second_profile_not_preferred(self, fw, state_map): + state = self.sanity(fw, state_map) + + state.rip_auth_profile_md5 = network.RipAuthProfileMd5( + keyid="1", key="secret2", preferred=False + ) + + state.rip_auth_profile.add(state.rip_auth_profile_md5) + state.rip_auth_profile_md5.create() + + +class TestRipInterface(MakeVirtualRouter): + WITH_RIP = True + WITH_RIP_AUTH_PROFILE = True + + def setup_state_obj(self, fw, state): + state.obj = network.RipInterface( + name=state.eths[0], + enable=True, + auth_profile=str(state.rip_auth_profile), + mode="normal", + ) + state.rip.add(state.obj) + + def update_state_obj(self, fw, state): + state.obj.enable = True + state.obj.advertise_default_route = "advertise" + state.obj.metric = random.randint(1, 15) + state.obj.auth_profile = None + state.obj.mode = "passive" + + +class TestRipExportRule(MakeVirtualRouter): + WITH_RIP = True + WITH_REDISTRIBUTION_PROFILE = True + + def setup_state_obj(self, fw, state): + state.obj = network.RipExportRule( + name=str(state.redist_profile), metric=random.randint(1, 15) + ) + state.rip.add(state.obj) + + def update_state_obj(self, fw, state): + state.obj.new_path_type = str(state.redist_profile) + state.obj.metric = random.randint(1, 15) + + class TestRedistributionProfile(MakeVirtualRouter): def setup_state_obj(self, fw, state): some_ip = testlib.random_ip()