annet 3.4.1__py3-none-any.whl → 3.5.0__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.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- annet/__init__.py +1 -1
- annet/adapters/netbox/common/models.py +14 -6
- annet/annlib/command.py +2 -1
- annet/annlib/filter_acl.py +2 -1
- annet/annlib/patching.py +1 -1
- annet/annlib/rbparser/syntax.py +2 -1
- annet/api/__init__.py +2 -3
- annet/bgp_models.py +9 -1
- annet/deploy_ui.py +2 -1
- annet/diff.py +2 -2
- annet/executor.py +1 -1
- annet/gen.py +2 -1
- annet/generators/__init__.py +2 -2
- annet/generators/base.py +2 -1
- annet/implicit.py +2 -2
- annet/mesh/device_models.py +6 -0
- annet/mesh/executor.py +10 -18
- annet/mesh/models_converter.py +12 -2
- annet/mesh/peer_models.py +1 -5
- annet/parallel.py +0 -6
- annet/rpl/match_builder.py +1 -1
- annet/rpl_generators/aspath.py +16 -0
- annet/rpl_generators/community.py +37 -3
- annet/rpl_generators/policy.py +340 -5
- annet/rpl_generators/prefix_lists.py +51 -1
- annet/rpl_generators/rd.py +16 -0
- annet/rulebook/__init__.py +1 -1
- annet/rulebook/b4com/iface.py +5 -1
- annet/rulebook/juniper/__init__.py +1 -1
- annet/rulebook/texts/b4com.order +1 -0
- annet/rulebook/texts/b4com.rul +2 -1
- annet/rulebook/texts/cisco.order +2 -0
- annet/rulebook/texts/huawei.rul +5 -0
- annet/rulebook/texts/iosxr.order +21 -4
- annet/rulebook/texts/juniper.rul +5 -0
- annet/vendors/base.py +1 -1
- annet/vendors/library/arista.py +1 -1
- annet/vendors/library/aruba.py +1 -1
- annet/vendors/library/b4com.py +1 -1
- annet/vendors/library/cisco.py +1 -1
- annet/vendors/library/h3c.py +1 -1
- annet/vendors/library/huawei.py +1 -1
- annet/vendors/library/iosxr.py +1 -1
- annet/vendors/library/juniper.py +1 -1
- annet/vendors/library/nexus.py +1 -1
- annet/vendors/library/nokia.py +1 -1
- annet/vendors/library/optixtrans.py +1 -1
- annet/vendors/library/pc.py +1 -1
- annet/vendors/library/ribbon.py +1 -1
- annet/vendors/library/routeros.py +1 -1
- annet/vendors/registry.py +1 -1
- annet/{annlib → vendors}/tabparser.py +3 -3
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/METADATA +1 -1
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/RECORD +60 -60
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/WHEEL +1 -1
- annet_generators/rpl_example/items.py +13 -5
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/entry_points.txt +0 -0
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/licenses/AUTHORS +0 -0
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {annet-3.4.1.dist-info → annet-3.5.0.dist-info}/top_level.txt +0 -0
annet/rpl_generators/policy.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import Iterator, Sequence
|
|
2
|
+
from collections.abc import Iterator, Sequence, Iterable
|
|
3
3
|
from typing import Any, cast, Literal
|
|
4
4
|
|
|
5
5
|
from annet.generators import PartialGenerator
|
|
6
6
|
from annet.rpl import (
|
|
7
7
|
CommunityActionValue,
|
|
8
8
|
ResultType, RoutingPolicyStatement, RoutingPolicy, ConditionOperator, SingleCondition, SingleAction, ActionType,
|
|
9
|
-
MatchField,
|
|
9
|
+
MatchField, PrefixMatchValue,
|
|
10
10
|
)
|
|
11
11
|
from annet.rpl.statement_builder import AsPathActionValue, NextHopActionValue, ThenField
|
|
12
12
|
from annet.rpl_generators.entities import (
|
|
@@ -15,7 +15,6 @@ from annet.rpl_generators.entities import (
|
|
|
15
15
|
IpPrefixList, group_community_members, CommunityType,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
HUAWEI_MATCH_COMMAND_MAP: dict[str, str] = {
|
|
20
19
|
MatchField.as_path_filter: "as-path-filter {option_value}",
|
|
21
20
|
MatchField.metric: "cost {option_value}",
|
|
@@ -58,6 +57,28 @@ ARISTA_THEN_COMMAND_MAP: dict[str, str] = {
|
|
|
58
57
|
# unsupported: resolution
|
|
59
58
|
# unsupported: rpki_valid_state
|
|
60
59
|
}
|
|
60
|
+
IOSXR_MATCH_COMMAND_MAP: dict[str, str] = {
|
|
61
|
+
MatchField.as_path_filter: "as-path in {option_value}",
|
|
62
|
+
MatchField.protocol: "protocol is {option_value}",
|
|
63
|
+
# unsupported: interface
|
|
64
|
+
# unsupported: metric
|
|
65
|
+
# unsupported: large-community
|
|
66
|
+
}
|
|
67
|
+
IOSXR_THEN_COMMAND_MAP: dict[str, str] = {
|
|
68
|
+
ThenField.local_pref: "local-preference {option_value}",
|
|
69
|
+
ThenField.origin: "origin {option_value}",
|
|
70
|
+
ThenField.tag: "tag {option_value}",
|
|
71
|
+
ThenField.metric_type: "metric-type {option_value}",
|
|
72
|
+
# unsupported: mpls_label # label?
|
|
73
|
+
# unsupported: resolution
|
|
74
|
+
# unsupported: rpki_valid_state
|
|
75
|
+
# unsupported: large_community
|
|
76
|
+
}
|
|
77
|
+
IOSXR_RESULT_MAP = {
|
|
78
|
+
ResultType.ALLOW: "done",
|
|
79
|
+
ResultType.DENY: "drop",
|
|
80
|
+
ResultType.NEXT: "pass"
|
|
81
|
+
}
|
|
61
82
|
|
|
62
83
|
|
|
63
84
|
class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
@@ -408,7 +429,8 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
408
429
|
|
|
409
430
|
for policy in self.get_policies(device):
|
|
410
431
|
for statement in policy.statements:
|
|
411
|
-
yield from self._huawei_statement(communities, rd_filters, device, policy, statement,
|
|
432
|
+
yield from self._huawei_statement(communities, rd_filters, device, policy, statement,
|
|
433
|
+
prefix_name_generator)
|
|
412
434
|
|
|
413
435
|
# arista
|
|
414
436
|
def acl_arista(self, device):
|
|
@@ -564,7 +586,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
564
586
|
else:
|
|
565
587
|
yield "set", "large-community large-community-list", community_name, "additive"
|
|
566
588
|
if action.value.added:
|
|
567
|
-
yield "set", "large-community large-community-list",
|
|
589
|
+
yield "set", "large-community large-community-list", *action.value.added, "additive"
|
|
568
590
|
if action.value.removed:
|
|
569
591
|
yield "set large-community large-community-list", *action.value.removed, "delete"
|
|
570
592
|
|
|
@@ -764,6 +786,8 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
764
786
|
statement: RoutingPolicyStatement,
|
|
765
787
|
prefix_name_generator: PrefixListNameGenerator,
|
|
766
788
|
) -> Iterator[Sequence[str]]:
|
|
789
|
+
if statement.number is None:
|
|
790
|
+
raise RuntimeError(f"Statement number should not be empty on Arista (found for policy: {policy.name})")
|
|
767
791
|
with self.block(
|
|
768
792
|
"route-map",
|
|
769
793
|
policy.name,
|
|
@@ -789,3 +813,314 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
789
813
|
yield from self._arista_statement(
|
|
790
814
|
communities, rd_filters, device, policy, statement, prefix_name_generator,
|
|
791
815
|
)
|
|
816
|
+
|
|
817
|
+
# Cisco IOS XR
|
|
818
|
+
def acl_iosxr(self, device):
|
|
819
|
+
return r"""
|
|
820
|
+
route-policy *
|
|
821
|
+
~ %global=1
|
|
822
|
+
"""
|
|
823
|
+
|
|
824
|
+
def _iosxr_match_community(
|
|
825
|
+
self,
|
|
826
|
+
community_type: Literal["community", "extcommunity rt", "extcommunity soo"],
|
|
827
|
+
community: CommunityList,
|
|
828
|
+
) -> Iterator[Sequence[str]]:
|
|
829
|
+
if community.logic is CommunityLogic.AND:
|
|
830
|
+
yield community_type, "matches-every", community.name
|
|
831
|
+
elif community.logic is CommunityLogic.OR:
|
|
832
|
+
yield community_type, "matches-any", community.name
|
|
833
|
+
else:
|
|
834
|
+
raise ValueError(f"Unknown community logic {community.logic}")
|
|
835
|
+
|
|
836
|
+
def _iosxr_match_communities(
|
|
837
|
+
self,
|
|
838
|
+
operator: ConditionOperator,
|
|
839
|
+
community_type: Literal["community", "extcommunity rt", "extcommunity soo"],
|
|
840
|
+
community_names: list[str],
|
|
841
|
+
communities: dict[str, CommunityList],
|
|
842
|
+
) -> Iterator[Sequence[str]]:
|
|
843
|
+
if operator == ConditionOperator.HAS_ANY:
|
|
844
|
+
yield self._iosxr_or_matches(
|
|
845
|
+
self._iosxr_match_community(community_type, communities[name])
|
|
846
|
+
for name in community_names
|
|
847
|
+
),
|
|
848
|
+
elif operator == ConditionOperator.HAS:
|
|
849
|
+
for name in community_names:
|
|
850
|
+
yield from self._iosxr_match_community(community_type, communities[name])
|
|
851
|
+
else:
|
|
852
|
+
raise NotImplementedError(f"Operator {operator} is not supported for {community_type} on Cisco IOS XR")
|
|
853
|
+
|
|
854
|
+
def _iosxr_match_rd(
|
|
855
|
+
self,
|
|
856
|
+
condition: SingleCondition[Sequence[str]],
|
|
857
|
+
rd_filters: dict[str, RDFilter],
|
|
858
|
+
) -> Iterator[Sequence[str]]:
|
|
859
|
+
if condition.operator == ConditionOperator.HAS_ANY:
|
|
860
|
+
if len(condition.value) == 1:
|
|
861
|
+
yield "rd", "in", condition.value[0]
|
|
862
|
+
return
|
|
863
|
+
conds = " or ".join(f"rd in {name}" for name in condition.value)
|
|
864
|
+
yield f"({conds})",
|
|
865
|
+
return
|
|
866
|
+
elif condition.operator == ConditionOperator.HAS:
|
|
867
|
+
for name in condition.value:
|
|
868
|
+
yield "rd", "in", name
|
|
869
|
+
else:
|
|
870
|
+
raise NotImplementedError(f"Operator {condition.operator} is not supported for {condition.field} on Cisco IOS XR")
|
|
871
|
+
|
|
872
|
+
def _iosxr_match_prefix_lists(
|
|
873
|
+
self,
|
|
874
|
+
condition: SingleCondition[PrefixMatchValue],
|
|
875
|
+
name_generator: PrefixListNameGenerator
|
|
876
|
+
) -> Iterator[Sequence[str]]:
|
|
877
|
+
matches = []
|
|
878
|
+
for name in condition.value.names:
|
|
879
|
+
plist = name_generator.get_prefix(name, condition.value)
|
|
880
|
+
matches.append(("destination in", plist.name))
|
|
881
|
+
if len(matches) == 1:
|
|
882
|
+
yield matches[0]
|
|
883
|
+
else:
|
|
884
|
+
yield self._iosxr_or_matches([matches]),
|
|
885
|
+
|
|
886
|
+
def _iosxr_match_local_pref(self, condition: SingleCondition) -> Iterator[Sequence[str]]:
|
|
887
|
+
if condition.operator is ConditionOperator.EQ:
|
|
888
|
+
yield "local-preference", "eq", str(condition.value)
|
|
889
|
+
elif condition.operator is ConditionOperator.LE:
|
|
890
|
+
yield "local-preference", "le", str(condition.value)
|
|
891
|
+
elif condition.operator is ConditionOperator.GE:
|
|
892
|
+
yield "local-preference", "ge", str(condition.value)
|
|
893
|
+
elif condition.operator is ConditionOperator.BETWEEN_INCLUDED:
|
|
894
|
+
yield "local-preference", "ge", str(condition.value[0])
|
|
895
|
+
yield "local-preference", "le", str(condition.value[1])
|
|
896
|
+
else:
|
|
897
|
+
raise NotImplementedError(
|
|
898
|
+
f"Operator {condition.operator} is not supported for {condition.field} on Cisco IOS XR",
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
def _iosxr_match_as_path_length(self, condition: SingleCondition) -> Iterator[Sequence[str]]:
|
|
902
|
+
if condition.operator is ConditionOperator.EQ:
|
|
903
|
+
yield "as-path length", "eq", str(condition.value)
|
|
904
|
+
elif condition.operator is ConditionOperator.LE:
|
|
905
|
+
yield "as-path length", "le", str(condition.value)
|
|
906
|
+
elif condition.operator is ConditionOperator.GE:
|
|
907
|
+
yield "as-path length", "ge", str(condition.value)
|
|
908
|
+
elif condition.operator is ConditionOperator.BETWEEN_INCLUDED:
|
|
909
|
+
yield "as-path length", "ge", str(condition.value[0])
|
|
910
|
+
yield "as-path length", "le", str(condition.value[1])
|
|
911
|
+
else:
|
|
912
|
+
raise NotImplementedError(
|
|
913
|
+
f"Operator {condition.operator} is not supported for {condition.field} on Cisco IOS XR",
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
def _iosxr_and_matches(self, conditions: Iterable[Iterable[Sequence[str]]]) -> str:
|
|
917
|
+
return " and ".join(" ".join(c) for seq in conditions for c in seq)
|
|
918
|
+
|
|
919
|
+
def _iosxr_or_matches(self, conditions: Iterable[Iterable[Sequence[str]]]) -> str:
|
|
920
|
+
return "(" + " or ".join(" ".join(c) for seq in conditions for c in seq) + ")"
|
|
921
|
+
|
|
922
|
+
def _iosxr_match(
|
|
923
|
+
self,
|
|
924
|
+
device: Any,
|
|
925
|
+
condition: SingleCondition[Any],
|
|
926
|
+
communities: dict[str, CommunityList],
|
|
927
|
+
rd_filters: dict[str, RDFilter],
|
|
928
|
+
name_generator: PrefixListNameGenerator,
|
|
929
|
+
) -> Iterator[Sequence[str]]:
|
|
930
|
+
if condition.field == MatchField.community:
|
|
931
|
+
yield from self._iosxr_match_communities(condition.operator, "community", condition.value, communities)
|
|
932
|
+
return
|
|
933
|
+
elif condition.field == MatchField.extcommunity_rt:
|
|
934
|
+
yield from self._iosxr_match_communities(condition.operator, "extcommunity rt", condition.value, communities)
|
|
935
|
+
return
|
|
936
|
+
elif condition.field == MatchField.extcommunity_soo:
|
|
937
|
+
yield from self._iosxr_match_communities(condition.operator, "extcommunity soo", condition.value, communities)
|
|
938
|
+
return
|
|
939
|
+
elif condition.field == MatchField.local_pref:
|
|
940
|
+
yield from self._iosxr_match_local_pref(condition)
|
|
941
|
+
return
|
|
942
|
+
elif condition.field == MatchField.as_path_length:
|
|
943
|
+
yield from self._iosxr_match_as_path_length(condition)
|
|
944
|
+
return
|
|
945
|
+
elif condition.field == MatchField.rd:
|
|
946
|
+
yield from self._iosxr_match_rd(condition, rd_filters)
|
|
947
|
+
return
|
|
948
|
+
elif condition.field == MatchField.ip_prefix:
|
|
949
|
+
yield from self._iosxr_match_prefix_lists(condition, name_generator)
|
|
950
|
+
return
|
|
951
|
+
elif condition.field == MatchField.ipv6_prefix:
|
|
952
|
+
yield from self._iosxr_match_prefix_lists(condition, name_generator)
|
|
953
|
+
return
|
|
954
|
+
|
|
955
|
+
if condition.operator is not ConditionOperator.EQ:
|
|
956
|
+
raise NotImplementedError(
|
|
957
|
+
f"`{condition.field}` with operator {condition.operator} is not supported for Cisco IOS XR",
|
|
958
|
+
)
|
|
959
|
+
if condition.field not in IOSXR_MATCH_COMMAND_MAP:
|
|
960
|
+
raise NotImplementedError(f"Match using `{condition.field}` is not supported for Cisco IOS XR")
|
|
961
|
+
yield IOSXR_MATCH_COMMAND_MAP[condition.field].format(option_value=condition.value),
|
|
962
|
+
|
|
963
|
+
def _iosxr_then_as_path(
|
|
964
|
+
self,
|
|
965
|
+
device: Any,
|
|
966
|
+
action: SingleAction[AsPathActionValue],
|
|
967
|
+
) -> Iterator[Sequence[str]]:
|
|
968
|
+
if action.value.set is not None:
|
|
969
|
+
raise RuntimeError("as_path.set is not supported for Cisco IOS XR")
|
|
970
|
+
if action.value.prepend:
|
|
971
|
+
for asn in action.value.prepend:
|
|
972
|
+
yield "prepend as-path", asn
|
|
973
|
+
if action.value.expand:
|
|
974
|
+
raise RuntimeError("as_path.expand is not supported for Cisco IOS XR")
|
|
975
|
+
if action.value.delete:
|
|
976
|
+
raise RuntimeError("as_path.delete is not supported for Cisco IOS XR")
|
|
977
|
+
if action.value.expand_last_as:
|
|
978
|
+
raise RuntimeError("as_path.expand_last_as is not supported for Cisco IOS XR")
|
|
979
|
+
|
|
980
|
+
def _iosxr_then_next_hop(
|
|
981
|
+
self,
|
|
982
|
+
device: Any,
|
|
983
|
+
action: SingleAction[NextHopActionValue],
|
|
984
|
+
) -> Iterator[Sequence[str]]:
|
|
985
|
+
if action.value.target == "self":
|
|
986
|
+
yield "set", "next-hop", "self"
|
|
987
|
+
elif action.value.target == "discard":
|
|
988
|
+
yield "set", "next-hop", "discard"
|
|
989
|
+
elif action.value.target == "peer":
|
|
990
|
+
pass
|
|
991
|
+
elif action.value.target == "ipv4_addr":
|
|
992
|
+
yield "set", "next-hop", action.value.addr
|
|
993
|
+
elif action.value.target == "ipv6_addr":
|
|
994
|
+
yield "set", "next-hop", action.value.addr.lower()
|
|
995
|
+
elif action.value.target == "mapped_ipv4":
|
|
996
|
+
yield "set", "next-hop", f"::ffff:{action.value.addr}"
|
|
997
|
+
else:
|
|
998
|
+
raise RuntimeError(f"Next_hop target {action.value.target} is not supported for Cisco IOS XR")
|
|
999
|
+
|
|
1000
|
+
def _iosxr_then_metric(
|
|
1001
|
+
self,
|
|
1002
|
+
device: Any,
|
|
1003
|
+
action: SingleAction[NextHopActionValue],
|
|
1004
|
+
) -> Iterator[Sequence[str]]:
|
|
1005
|
+
if action.type is ActionType.ADD:
|
|
1006
|
+
yield "set", f"med +{action.value}"
|
|
1007
|
+
elif action.type is ActionType.REMOVE:
|
|
1008
|
+
yield "set", f"med -{action.value}"
|
|
1009
|
+
elif action.type is ActionType.SET:
|
|
1010
|
+
yield "set", f"med {action.value}"
|
|
1011
|
+
else:
|
|
1012
|
+
raise NotImplementedError(f"Action type {action.type} for metric is not supported for Cisco IOS XR")
|
|
1013
|
+
|
|
1014
|
+
def _iosxr_then_community(
|
|
1015
|
+
self,
|
|
1016
|
+
communities: dict[str, CommunityList],
|
|
1017
|
+
device: Any,
|
|
1018
|
+
action: SingleAction[CommunityActionValue],
|
|
1019
|
+
) -> Iterator[Sequence[str]]:
|
|
1020
|
+
added = []
|
|
1021
|
+
if action.value.replaced is not None:
|
|
1022
|
+
yield "delete", "community", "all"
|
|
1023
|
+
added.extend(action.value.replaced)
|
|
1024
|
+
added.extend(action.value.added)
|
|
1025
|
+
for name in added:
|
|
1026
|
+
yield "set", "community", name, "additive"
|
|
1027
|
+
for community_name in action.value.removed:
|
|
1028
|
+
yield "delete", "community", "in", community_name
|
|
1029
|
+
|
|
1030
|
+
def _iosxr_community_type_str(self, comm_type: CommunityType) -> str:
|
|
1031
|
+
if comm_type is CommunityType.RT:
|
|
1032
|
+
return "rt"
|
|
1033
|
+
elif comm_type is CommunityType.LARGE:
|
|
1034
|
+
raise ValueError("Large community is not subtype of extcommunity")
|
|
1035
|
+
elif comm_type is CommunityType.BASIC:
|
|
1036
|
+
raise ValueError("Basic community is not subtype of extcommunity")
|
|
1037
|
+
else:
|
|
1038
|
+
raise NotImplementedError(f"Community type {comm_type} is not supported on Cisco IOS XR")
|
|
1039
|
+
|
|
1040
|
+
def _iosxr_then_extcommunity(
|
|
1041
|
+
self,
|
|
1042
|
+
communities: dict[str, CommunityList],
|
|
1043
|
+
device: Any,
|
|
1044
|
+
action: SingleAction[CommunityActionValue],
|
|
1045
|
+
) -> Iterator[Sequence[str]]:
|
|
1046
|
+
added = []
|
|
1047
|
+
if action.value.replaced is not None:
|
|
1048
|
+
yield "delete", "extcommunity", "rt", "all"
|
|
1049
|
+
added.extend(action.value.replaced)
|
|
1050
|
+
|
|
1051
|
+
added.extend(action.value.replaced)
|
|
1052
|
+
for member_name in added:
|
|
1053
|
+
typename = self._iosxr_community_type_str(communities[member_name].type)
|
|
1054
|
+
yield "set", "extcommunity", typename, member_name, "additive"
|
|
1055
|
+
for member_name in action.value.removed:
|
|
1056
|
+
typename = self._iosxr_community_type_str(communities[member_name].type)
|
|
1057
|
+
yield "delete", "extcommunity", typename, "in", member_name
|
|
1058
|
+
|
|
1059
|
+
def _iosxr_then(
|
|
1060
|
+
self,
|
|
1061
|
+
communities: dict[str, CommunityList],
|
|
1062
|
+
device: Any,
|
|
1063
|
+
action: SingleAction[Any],
|
|
1064
|
+
) -> Iterator[Sequence[str]]:
|
|
1065
|
+
if action.field == ThenField.community:
|
|
1066
|
+
yield from self._iosxr_then_community(communities, device, action)
|
|
1067
|
+
return
|
|
1068
|
+
if action.field == ThenField.extcommunity:
|
|
1069
|
+
yield from self._iosxr_then_extcommunity(communities, device, action)
|
|
1070
|
+
return
|
|
1071
|
+
if action.field == ThenField.metric:
|
|
1072
|
+
yield from self._iosxr_then_metric(device, action)
|
|
1073
|
+
return
|
|
1074
|
+
if action.field == ThenField.as_path:
|
|
1075
|
+
yield from self._iosxr_then_as_path(device, cast(SingleAction[AsPathActionValue], action))
|
|
1076
|
+
return
|
|
1077
|
+
if action.field == ThenField.next_hop:
|
|
1078
|
+
yield from self._iosxr_then_next_hop(device, cast(SingleAction[NextHopActionValue], action))
|
|
1079
|
+
return
|
|
1080
|
+
if action.type is not ActionType.SET:
|
|
1081
|
+
raise NotImplementedError(f"Action type {action.type} for `{action.field}` is not supported for Cisco IOS XR")
|
|
1082
|
+
if action.field not in IOSXR_THEN_COMMAND_MAP:
|
|
1083
|
+
raise NotImplementedError(f"Then action using `{action.field}` is not supported for Cisco IOS XR")
|
|
1084
|
+
cmd = IOSXR_THEN_COMMAND_MAP[action.field]
|
|
1085
|
+
yield "set", cmd.format(option_value=action.value)
|
|
1086
|
+
|
|
1087
|
+
def _iosxr_statement(
|
|
1088
|
+
self,
|
|
1089
|
+
communities: dict[str, CommunityList],
|
|
1090
|
+
rd_filters: dict[str, RDFilter],
|
|
1091
|
+
device: Any,
|
|
1092
|
+
policy: RoutingPolicy,
|
|
1093
|
+
statement: RoutingPolicyStatement,
|
|
1094
|
+
prefix_name_generator: PrefixListNameGenerator,
|
|
1095
|
+
) -> Iterator[Sequence[str]]:
|
|
1096
|
+
if statement.match:
|
|
1097
|
+
condition_expr = self._iosxr_and_matches(
|
|
1098
|
+
self._iosxr_match(device, condition, communities, rd_filters, prefix_name_generator)
|
|
1099
|
+
for condition in statement.match
|
|
1100
|
+
)
|
|
1101
|
+
with self.block(
|
|
1102
|
+
"if", condition_expr, "then",
|
|
1103
|
+
):
|
|
1104
|
+
for action in statement.then:
|
|
1105
|
+
yield from self._iosxr_then(communities, device, action)
|
|
1106
|
+
yield IOSXR_RESULT_MAP[statement.result],
|
|
1107
|
+
else:
|
|
1108
|
+
for action in statement.then:
|
|
1109
|
+
yield from self._iosxr_then(communities, device, action)
|
|
1110
|
+
yield IOSXR_RESULT_MAP[statement.result],
|
|
1111
|
+
|
|
1112
|
+
def run_iosxr(self, device):
|
|
1113
|
+
prefix_lists = self.get_prefix_lists(device)
|
|
1114
|
+
policies = self.get_policies(device)
|
|
1115
|
+
prefix_name_generator = PrefixListNameGenerator(prefix_lists, policies)
|
|
1116
|
+
communities = {c.name: c for c in self.get_community_lists(device)}
|
|
1117
|
+
rd_filters = {f.name: f for f in self.get_rd_filters(device)}
|
|
1118
|
+
|
|
1119
|
+
for policy in policies:
|
|
1120
|
+
with self.block(
|
|
1121
|
+
"route-policy", policy.name,
|
|
1122
|
+
):
|
|
1123
|
+
for statement in policy.statements:
|
|
1124
|
+
yield from self._iosxr_statement(
|
|
1125
|
+
communities, rd_filters, device, policy, statement, prefix_name_generator,
|
|
1126
|
+
)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from collections.abc import Sequence, Iterable
|
|
3
|
-
from ipaddress import ip_interface
|
|
4
3
|
from typing import Any, Literal
|
|
5
4
|
|
|
6
5
|
from annet.generators import PartialGenerator
|
|
@@ -120,3 +119,54 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
120
119
|
continue
|
|
121
120
|
yield from self._arista_prefix_list("ipv6", plist)
|
|
122
121
|
processed_names.add(plist.name)
|
|
122
|
+
|
|
123
|
+
# Cisco IOS XR
|
|
124
|
+
def acl_iosxr(self, device: Any):
|
|
125
|
+
return r"""
|
|
126
|
+
prefix-set
|
|
127
|
+
~ %global=1
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def _iosxr_prefixlist(self, prefixlist: IpPrefixList):
|
|
131
|
+
with self.block("prefix-set", prefixlist.name):
|
|
132
|
+
for n, member in enumerate(prefixlist.members):
|
|
133
|
+
if n + 1 < len(prefixlist.members):
|
|
134
|
+
comma = ","
|
|
135
|
+
else:
|
|
136
|
+
comma = ""
|
|
137
|
+
|
|
138
|
+
ge, le = member.or_longer
|
|
139
|
+
if ge is le is None:
|
|
140
|
+
yield f"{member.prefix}{comma}",
|
|
141
|
+
elif ge is None:
|
|
142
|
+
yield f"{member.prefix} le {le}{comma}",
|
|
143
|
+
elif le is None:
|
|
144
|
+
yield f"{member.prefix} ge {ge}{comma}",
|
|
145
|
+
elif ge == le:
|
|
146
|
+
yield f"{member.prefix} eq {ge}{comma}",
|
|
147
|
+
else:
|
|
148
|
+
yield f"{member.prefix} ge {ge} le {le}{comma}",
|
|
149
|
+
|
|
150
|
+
def run_iosxr(self, device: Any):
|
|
151
|
+
prefix_lists = self.get_prefix_lists(device)
|
|
152
|
+
policies = self.get_policies(device)
|
|
153
|
+
|
|
154
|
+
name_generator = PrefixListNameGenerator(prefix_lists, policies)
|
|
155
|
+
processed_names = set()
|
|
156
|
+
for policy in policies:
|
|
157
|
+
for statement in policy.statements:
|
|
158
|
+
cond: SingleCondition[PrefixMatchValue]
|
|
159
|
+
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
160
|
+
for name in cond.value.names:
|
|
161
|
+
plist = name_generator.get_prefix(name, cond.value)
|
|
162
|
+
if plist.name in processed_names:
|
|
163
|
+
continue
|
|
164
|
+
yield from self._iosxr_prefixlist(plist)
|
|
165
|
+
processed_names.add(plist.name)
|
|
166
|
+
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
167
|
+
for name in cond.value.names:
|
|
168
|
+
plist = name_generator.get_prefix(name, cond.value)
|
|
169
|
+
if plist.name in processed_names:
|
|
170
|
+
continue
|
|
171
|
+
yield from self._iosxr_prefixlist(plist)
|
|
172
|
+
processed_names.add(plist.name)
|
annet/rpl_generators/rd.py
CHANGED
|
@@ -38,3 +38,19 @@ class RDFilterFilterGenerator(PartialGenerator, ABC):
|
|
|
38
38
|
for i, route_distinguisher in enumerate(rd_filter.members):
|
|
39
39
|
rd_id = (i + 1) * 10 + 5
|
|
40
40
|
yield "ip rd-filter", rd_filter.number, f"index {rd_id}", "permit", route_distinguisher
|
|
41
|
+
|
|
42
|
+
def acl_iosxr(self, _):
|
|
43
|
+
return r"""
|
|
44
|
+
rd-set *
|
|
45
|
+
~ %global=1
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def run_iosxr(self, device: Any):
|
|
49
|
+
for rd_filter in self.get_used_rd_filters(device):
|
|
50
|
+
with self.block("rd-set", rd_filter.name):
|
|
51
|
+
for i, route_distinguisher in enumerate(rd_filter.members):
|
|
52
|
+
if i + 1 < len(rd_filter.members):
|
|
53
|
+
comma = ","
|
|
54
|
+
else:
|
|
55
|
+
comma = ""
|
|
56
|
+
yield f"{route_distinguisher}{comma}",
|
annet/rulebook/__init__.py
CHANGED
|
@@ -99,7 +99,7 @@ class DefaultRulebookProvider(RulebookProvider):
|
|
|
99
99
|
return self._escaped_rul_cache[name]
|
|
100
100
|
for root_dir in self.root_dir:
|
|
101
101
|
try:
|
|
102
|
-
with open(path.join(root_dir, "texts", name), "r") as f:
|
|
102
|
+
with open(path.join(root_dir, "texts", name), "r", encoding="utf-8") as f:
|
|
103
103
|
self._escaped_rul_cache[name] = self._escape_mako(f.read())
|
|
104
104
|
return self._escaped_rul_cache[name]
|
|
105
105
|
except FileNotFoundError:
|
annet/rulebook/b4com/iface.py
CHANGED
|
@@ -42,11 +42,15 @@ def sflow(rule, key, diff, **kwargs):
|
|
|
42
42
|
|
|
43
43
|
def lldp(rule, key, diff, **kwargs):
|
|
44
44
|
"""
|
|
45
|
-
|
|
45
|
+
Обрабатываем блок lldp-agent
|
|
46
46
|
"""
|
|
47
47
|
result = common.default(rule, key, diff, **kwargs)
|
|
48
48
|
for op, cmd, ch in result:
|
|
49
|
+
# Не удаляем все что начинается с set, т.к. set перезаписывает предыдущий конфиг
|
|
49
50
|
if diff[Op.REMOVED] and "set lldp" in cmd:
|
|
50
51
|
pass
|
|
52
|
+
# В случае lldp tlv ... select удаляем все что до select
|
|
53
|
+
elif diff[Op.REMOVED] and cmd.endswith("select"):
|
|
54
|
+
yield (op, " ".join(cmd.split()[:-1]), ch)
|
|
51
55
|
else:
|
|
52
56
|
yield (op, cmd, ch)
|
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from collections import OrderedDict as odict
|
|
4
4
|
|
|
5
5
|
from annet.annlib.lib import jun_activate, jun_is_inactive, merge_dicts
|
|
6
|
-
from annet.
|
|
6
|
+
from annet.vendors.tabparser import JuniperFormatter
|
|
7
7
|
from annet.annlib.types import Op
|
|
8
8
|
from annet.rulebook import common
|
|
9
9
|
|
annet/rulebook/texts/b4com.order
CHANGED
annet/rulebook/texts/b4com.rul
CHANGED
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
# Physical
|
|
14
14
|
sflow *
|
|
15
15
|
|
|
16
|
-
interface */(ce|xe|eth)[0-9\/]+$/ %logic=common.permanent %diff_logic=cisco.iface.diff
|
|
16
|
+
interface */(ce|xe|eth|vlan)[0-9\/]+$/ %logic=common.permanent %diff_logic=cisco.iface.diff
|
|
17
17
|
description * %logic=b4com.iface.description
|
|
18
18
|
ip vrf forwarding *
|
|
19
19
|
ip address *
|
|
20
20
|
ipv6 address *
|
|
21
|
+
ipv6 nd *
|
|
21
22
|
mtu * %logic=b4com.iface.mtu
|
|
22
23
|
sflow * %logic=b4com.iface.sflow
|
|
23
24
|
lldp-agent
|
annet/rulebook/texts/cisco.order
CHANGED
annet/rulebook/texts/huawei.rul
CHANGED
|
@@ -55,6 +55,11 @@ dhcp snooping user-bind http
|
|
|
55
55
|
|
|
56
56
|
ntp server disable
|
|
57
57
|
ntp ipv6 server disable
|
|
58
|
+
ntp unicast-server domain *
|
|
59
|
+
ntp unicast-server ipv6 *
|
|
60
|
+
ntp unicast-server *
|
|
61
|
+
ntp-service unicast-server ipv6 *
|
|
62
|
+
ntp-service unicast-server *
|
|
58
63
|
ntp ~
|
|
59
64
|
dns snooping ttl delay-time
|
|
60
65
|
dns ~
|
annet/rulebook/texts/iosxr.order
CHANGED
|
@@ -72,14 +72,31 @@ interface *
|
|
|
72
72
|
|
|
73
73
|
interface */\S+\.\d+/
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
# all referenced in policy
|
|
77
|
+
as-path-set *
|
|
78
|
+
~
|
|
79
|
+
prefix-set *
|
|
80
|
+
~
|
|
81
|
+
community-set *
|
|
82
|
+
~
|
|
83
|
+
extcommunity-set *
|
|
84
|
+
~
|
|
85
|
+
|
|
86
|
+
route-policy *
|
|
87
|
+
~
|
|
76
88
|
|
|
77
89
|
# удалять eth-trunk можно только после того, как вычистим member interfaces
|
|
78
90
|
undo interface */port-channel\d+/ %order_reverse
|
|
79
91
|
|
|
80
|
-
|
|
92
|
+
|
|
81
93
|
router bgp
|
|
82
|
-
|
|
83
|
-
neighbor
|
|
94
|
+
bgp ~
|
|
95
|
+
neighbor-group *
|
|
96
|
+
~
|
|
97
|
+
vrf *
|
|
98
|
+
~
|
|
99
|
+
neighbor *
|
|
100
|
+
~
|
|
84
101
|
|
|
85
102
|
line
|
annet/rulebook/texts/juniper.rul
CHANGED
annet/vendors/base.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import ClassVar
|
|
|
3
3
|
|
|
4
4
|
from annet.annlib.command import CommandList
|
|
5
5
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
6
|
-
from annet.
|
|
6
|
+
from annet.vendors.tabparser import CommonFormatter
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class AbstractVendor(abc.ABC):
|
annet/vendors/library/arista.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import AristaFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|
annet/vendors/library/aruba.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import ArubaFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|
annet/vendors/library/b4com.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import B4comFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|
annet/vendors/library/cisco.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import CiscoFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|
annet/vendors/library/h3c.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import HuaweiFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|
annet/vendors/library/huawei.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from annet.annlib.command import Command, CommandList
|
|
2
2
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
3
|
-
from annet.
|
|
3
|
+
from annet.vendors.tabparser import HuaweiFormatter
|
|
4
4
|
from annet.vendors.base import AbstractVendor
|
|
5
5
|
from annet.vendors.registry import registry
|
|
6
6
|
|