1# -*- coding: utf-8 -*-
2# Copyright 2021 Red Hat
3# GNU General Public License v3.0+
4# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7
8__metaclass__ = type
9
10"""
11The Route_maps parser templates file. This contains
12a list of parser definitions and associated functions that
13facilitates both facts gathering and native command generation for
14the given network resource.
15"""
16
17import re
18from ansible.module_utils.six import iteritems
19from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
20    NetworkTemplate,
21)
22
23
24def _tmplt_route_map_match(config_data):
25    if (
26        config_data.get("match")
27        and not config_data["match"].get("ip")
28        and not config_data["match"].get("ipv6")
29    ):
30        command = []
31        match = config_data["match"]
32        if match and match.get("additional_paths"):
33            cmd = "match additional-paths advertise-set"
34            if config_data["match"]["additional_paths"].get("all"):
35                cmd += " all"
36            if config_data["match"]["additional_paths"].get("best"):
37                cmd += " best {best}".format(
38                    **config_data["match"]["additional_paths"]
39                )
40            if config_data["match"]["additional_paths"].get("best_range"):
41                cmd += " best-range"
42                if config_data["match"]["additional_paths"]["best_range"].get(
43                    "lower_limit"
44                ):
45                    cmd += " lower-limit {lower_limit}".format(
46                        **config_data["match"]["additional_paths"][
47                            "best_range"
48                        ]
49                    )
50                if config_data["match"]["additional_paths"]["best_range"].get(
51                    "upper_limit"
52                ):
53                    cmd += " upper-limit {upper_limit}".format(
54                        **config_data["match"]["additional_paths"][
55                            "best_range"
56                        ]
57                    )
58            if config_data["match"]["additional_paths"].get("group_best"):
59                cmd += " group-best"
60            command.append(cmd)
61        if match.get("as_path"):
62            cmd = "match as-path "
63            if match["as_path"].get("acls"):
64                temp = []
65                for k, v in iteritems(match["as_path"]["acls"]):
66                    temp.append(str(v))
67                cmd += " ".join(sorted(temp))
68            command.append(cmd)
69        if match.get("clns"):
70            cmd = "match clns"
71            if match["clns"].get("address"):
72                cmd += " address {address}".format(**match["clns"])
73            elif match["clns"].get("next_hop"):
74                cmd = " next-hop {next_hop}".format(**match["clns"])
75            elif match["clns"].get("route_source"):
76                cmd = " route-source {route_source}".format(**match["clns"])
77            command.append(cmd)
78        if match.get("community"):
79            cmd = "match community "
80            temp = []
81            for k, v in iteritems(match["community"]["name"]):
82                temp.append(v)
83            cmd += " ".join(sorted(temp))
84            if match["community"].get("exact_match"):
85                cmd += " exact-match"
86            command.append(cmd)
87        if match.get("extcommunity"):
88            cmd = "match extcommunity "
89            temp = []
90            for k, v in iteritems(match["extcommunity"]):
91                temp.append(v)
92            cmd += " ".join(sorted(temp))
93            command.append(cmd)
94        if match.get("interfaces"):
95            cmd = "match interface "
96            temp = []
97            for k, v in iteritems(match["interfaces"]):
98                temp.append(v)
99            cmd += " ".join(sorted(temp))
100            command.append(cmd)
101        if match.get("length"):
102            command.append(
103                "match length {minimum} {maximum}".format(**match["length"])
104            )
105        if match.get("local_preference"):
106            cmd = "match local-preference "
107            if match["local_preference"].get("value"):
108                temp = []
109                for k, v in iteritems(match["local_preference"]["value"]):
110                    temp.append(v)
111                cmd += " ".join(sorted(temp))
112            command.append(cmd)
113        if match.get("mdt_group"):
114            cmd = "match mdt-group "
115            if match["mdt_group"].get("acls"):
116                temp = []
117                for k, v in iteritems(match["mdt_group"]["acls"]):
118                    temp.append(v)
119                cmd += " ".join(sorted(temp))
120            command.append(cmd)
121        if match.get("metric"):
122            cmd = "match metric"
123            if match["metric"].get("external"):
124                cmd += " external"
125            if match["metric"].get("value"):
126                cmd += " {value}".format(**match["metric"])
127            if match["metric"].get("deviation"):
128                cmd += " +-"
129                if match["metric"].get("deviation_value"):
130                    cmd += " {deviation_value}".format(**match["metric"])
131            command.append(cmd)
132        if match.get("mpls_label"):
133            command.append("match mpls-label")
134        if match.get("policy_lists"):
135            cmd = "match policy-list "
136            temp = []
137            for k, v in iteritems(match["policy_lists"]):
138                temp.append(v)
139            cmd += " ".join(sorted(temp))
140            command.append(cmd)
141        if match.get("route_type"):
142            cmd = "match route-type"
143            if match["route_type"].get("external"):
144                cmd += " external"
145                if match["route_type"]["external"].get("type_1"):
146                    cmd += " type-1"
147                elif match["route_type"]["external"].get("type_2"):
148                    cmd += " type-2"
149            elif match["route_type"].get("internal"):
150                cmd += " internal"
151            elif match["route_type"].get("level_1"):
152                cmd += " level-1"
153            elif match["route_type"].get("level_2"):
154                cmd += " level-2"
155            elif match["route_type"].get("local"):
156                cmd += " local"
157            elif match["route_type"].get("nssa_external"):
158                cmd += " nssa-external"
159                if match["route_type"]["nssa_external"].get("type_1"):
160                    cmd += " type-1"
161                elif match["route_type"]["nssa_external"].get("type_2"):
162                    cmd += " type-2"
163            command.append(cmd)
164        if match.get("rpki"):
165            cmd = "match rpki"
166            if match["rpki"].get("invalid"):
167                cmd += " invalid"
168            if match["rpki"].get("not_found"):
169                cmd += " not-found"
170            if match["rpki"].get("valid"):
171                cmd += " valid"
172            command.append(cmd)
173        if match.get("security_group"):
174            cmd = "match security-group"
175            if match["security_group"].get("source"):
176                cmd += " source tag "
177                temp = []
178                for k, v in iteritems(match["security_group"]["source"]):
179                    temp.append(str(v))
180                cmd += " ".join(sorted(temp))
181            elif match["security_group"].get("destination"):
182                cmd += " destination tag"
183                for each in match["destination"]:
184                    cmd += " {0}".format(each)
185            command.append(cmd)
186        if match.get("source_protocol"):
187            cmd = "match source-protocol"
188            if match["source_protocol"].get("bgp"):
189                cmd += " bgp {bgp}".format(**match["source_protocol"])
190            if match["source_protocol"].get("connected"):
191                cmd += " connected"
192            if match["source_protocol"].get("eigrp"):
193                cmd += " eigrp {eigrp}".format(**match["source_protocol"])
194            if match["source_protocol"].get("isis"):
195                cmd += " isis"
196            if match["source_protocol"].get("lisp"):
197                cmd += " lisp"
198            if match["source_protocol"].get("mobile"):
199                cmd += " mobile"
200            if match["source_protocol"].get("ospf"):
201                cmd += " ospf {ospf}".format(**match["source_protocol"])
202            if match["source_protocol"].get("ospfv3"):
203                cmd += " ospfv3 {ospfv3}".format(**match["source_protocol"])
204            if match["source_protocol"].get("rip"):
205                cmd += " rip"
206            if match["source_protocol"].get("static"):
207                cmd += " static"
208            command.append(cmd)
209        if match.get("tag"):
210            cmd = "match tag"
211            if match["tag"].get("tag_list"):
212                cmd += " list"
213                for each in match["tag"]["tag_list"]:
214                    cmd += " {0}".format(each)
215            elif match["tag"].get("value"):
216                for each in match["tag"]["value"]:
217                    cmd += " {0}".format(each)
218            command.append(cmd)
219        if match.get("track"):
220            command.append("match track {track}".format(**match))
221        return command
222
223
224def _tmplt_route_map_match_ip(config_data):
225    if config_data.get("match") and config_data["match"].get("ip"):
226
227        def construct_cmd_from_list(cmd, config):
228            temp = []
229            for k, v in iteritems(config):
230                temp.append(v)
231            cmd += " " + " ".join(sorted(temp))
232            return cmd
233
234        cmd = "match ip"
235        if config_data["match"]["ip"].get("address"):
236            cmd += " address"
237            if config_data["match"]["ip"]["address"].get("prefix_lists"):
238                cmd += " prefix-list"
239                cmd = construct_cmd_from_list(
240                    cmd, config_data["match"]["ip"]["address"]["prefix_lists"]
241                )
242            elif config_data["match"]["ip"]["address"].get("acls"):
243                cmd = construct_cmd_from_list(
244                    cmd, config_data["match"]["ip"]["address"]["acls"]
245                )
246        if config_data["match"]["ip"].get("flowspec"):
247            cmd += " flowspec"
248            if config_data["match"]["ip"]["flowspec"].get("dest_pfx"):
249                cmd += " dest-pfx"
250            elif config_data["match"]["ip"]["flowspec"].get("src_pfx"):
251                cmd += " src-pfx"
252            if config_data["match"]["ip"]["flowspec"].get("prefix_lists"):
253                cmd += " prefix-list"
254                cmd = construct_cmd_from_list(
255                    cmd, config_data["match"]["ip"]["flowspec"]["prefix_lists"]
256                )
257            elif config_data["match"]["ip"]["flowspec"].get("acls"):
258                cmd = construct_cmd_from_list(
259                    cmd, config_data["match"]["ip"]["flowspec"]["acls"]
260                )
261        if config_data["match"]["ip"].get("next_hop"):
262            cmd += " next-hop"
263            if config_data["match"]["ip"]["next_hop"].get("prefix_lists"):
264                cmd += " prefix-list"
265                cmd = construct_cmd_from_list(
266                    cmd, config_data["match"]["ip"]["next_hop"]["prefix_lists"]
267                )
268            elif config_data["match"]["ip"]["next_hop"].get("acls"):
269                cmd = construct_cmd_from_list(
270                    cmd, config_data["match"]["ip"]["next_hop"]["acls"]
271                )
272        if config_data["match"]["ip"].get("redistribution_source"):
273            cmd += " redistribution-source"
274            if config_data["match"]["ip"]["redistribution_source"].get(
275                "prefix_lists"
276            ):
277                cmd += " prefix-list"
278                cmd = construct_cmd_from_list(
279                    cmd,
280                    config_data["match"]["ip"]["redistribution_source"][
281                        "prefix_lists"
282                    ],
283                )
284            elif config_data["match"]["ip"]["redistribution_source"].get(
285                "acls"
286            ):
287                cmd = construct_cmd_from_list(
288                    cmd,
289                    config_data["match"]["ip"]["redistribution_source"][
290                        "acls"
291                    ],
292                )
293        if config_data["match"]["ip"].get("route_source"):
294            cmd += " route-source"
295            if config_data["match"]["ip"]["route_source"].get(
296                "redistribution_source"
297            ):
298                cmd += " redistribution-source"
299            if config_data["match"]["ip"]["route_source"].get("prefix_lists"):
300                cmd += " prefix-list"
301                cmd = construct_cmd_from_list(
302                    cmd,
303                    config_data["match"]["ip"]["route_source"]["prefix_lists"],
304                )
305            elif config_data["match"]["ip"]["route_source"].get("acls"):
306                cmd = construct_cmd_from_list(
307                    cmd, config_data["match"]["ip"]["route_source"]["acls"]
308                )
309        return cmd
310
311
312def _tmplt_route_map_match_ipv6(config_data):
313    if config_data.get("match") and config_data["match"].get("ipv6"):
314        cmd = "match ipv6"
315        if config_data["match"]["ipv6"].get("address"):
316            cmd += " address"
317            if config_data["match"]["ipv6"]["address"].get("prefix_list"):
318                cmd += " prefix-list {prefix_list}".format(
319                    **config_data["match"]["ipv6"]["address"]
320                )
321            elif config_data["match"]["ipv6"]["address"].get("acl"):
322                cmd += " {acl}".format(
323                    **config_data["match"]["ipv6"]["address"]
324                )
325        if config_data["match"]["ipv6"].get("flowspec"):
326            cmd += " flowspec"
327            if config_data["match"]["ipv6"]["flowspec"].get("dest_pfx"):
328                cmd += " dest-pfx"
329            elif config_data["match"]["ipv6"]["flowspec"].get("src_pfx"):
330                cmd += " src-pfx"
331            if config_data["match"]["ipv6"]["flowspec"].get("prefix_list"):
332                cmd += " prefix-list {prefix_list}".format(
333                    **config_data["match"]["ipv6"]["flowspec"]
334                )
335            elif config_data["match"]["ipv6"]["flowspec"].get("acl"):
336                cmd += " {acl}".format(
337                    **config_data["match"]["ipv6"]["flowspec"]
338                )
339        if config_data["match"]["ipv6"].get("next_hop"):
340            cmd += " next-hop"
341            if config_data["match"]["ipv6"]["next_hop"].get("prefix_list"):
342                cmd += " prefix-list {prefix_list}".format(
343                    **config_data["match"]["ipv6"]["next_hop"]
344                )
345            elif config_data["match"]["ipv6"]["next_hop"].get("acl"):
346                cmd += " {acl}".format(
347                    **config_data["match"]["ipv6"]["next_hop"]
348                )
349        if config_data["match"]["ipv6"].get("route_source"):
350            cmd += " route-source"
351            if config_data["match"]["ipv6"]["route_source"].get("prefix_list"):
352                cmd += " prefix-list {prefix_list}".format(
353                    **config_data["match"]["ipv6"]["route_source"]
354                )
355            elif config_data["match"]["ipv6"]["route_source"].get("acl"):
356                cmd += " {acl}".format(
357                    **config_data["match"]["ipv6"]["route_source"]
358                )
359        return cmd
360
361
362def _tmplt_route_map_set(config_data):
363    if config_data.get("set"):
364        command = []
365        set = config_data["set"]
366        if set.get("aigp_metric"):
367            cmd = "set aigp-metric"
368            if set["aigp_metric"].get("value"):
369                cmd += " {value}".format(**set["aigp_metric"])
370            elif set["aigp_metric"].get("igp_metric"):
371                cmd += " igp-metric"
372            command.append(cmd)
373        if set.get("as_path"):
374            cmd = "set as-path"
375            if set["as_path"].get("prepend"):
376                cmd += " prepend"
377                if set["as_path"]["prepend"].get("as_number"):
378                    cmd += " {as_number}".format(**set["as_path"]["prepend"])
379                elif set["as_path"]["prepend"].get("last_as"):
380                    cmd += " last-as {last_as}".format(
381                        **set["as_path"]["prepend"]
382                    )
383            if set["as_path"].get("tag"):
384                cmd += " tag"
385            command.append(cmd)
386        if set.get("automatic_tag"):
387            command.append("set automatic-tag")
388        if set.get("clns"):
389            command.append("set clns next-hop {clns}".format(**set))
390        if set.get("comm_list"):
391            command.append("set comm-list {comm_list} delete".format(**set))
392        if set.get("community"):
393            cmd = "set community"
394            if set["community"].get("number"):
395                cmd += " {number}".format(**set["community"])
396            if set["community"].get("additive"):
397                cmd += " additive"
398            if set["community"].get("gshut"):
399                cmd += " gshut"
400            if set["community"].get("internet"):
401                cmd += " internet"
402            if set["community"].get("local_as"):
403                cmd += " local-as"
404            if set["community"].get("no_advertise"):
405                cmd += " no-advertise"
406            if set["community"].get("no_export"):
407                cmd += " no-export"
408            if set["community"].get("none"):
409                cmd += " none"
410            command.append(cmd)
411        if set.get("dampening"):
412            command.append(
413                "set dampening {penalty_half_time} {reuse_route_val} {suppress_route_val} {max_suppress}".format(
414                    **set["dampening"]
415                )
416            )
417        if set.get("default"):
418            command.append(
419                "set default interface {default}".format(**set["default"])
420            )
421        if set.get("extcomm_list"):
422            command.append(
423                "set extcomm-list {extcomm_list} delete".format(**set)
424            )
425        if set.get("extcommunity"):
426            if set["extcommunity"].get("cost"):
427                cmd = "set extcommunity cost"
428                if set["extcommunity"]["cost"].get("igp"):
429                    cmd += " igp"
430                elif set["extcommunity"]["cost"].get("pre_bestpath"):
431                    cmd += " pre-bestpath"
432                if set["extcommunity"]["cost"].get("id"):
433                    cmd += " {id}".format(**set["extcommunity"]["cost"])
434                if set["extcommunity"]["cost"].get("cost_value"):
435                    cmd += " {cost_value}".format(
436                        **set["extcommunity"]["cost"]
437                    )
438                command.append(cmd)
439            if set["extcommunity"].get("rt"):
440                cmd = "set extcommunity rt"
441                if set["extcommunity"]["rt"].get("range"):
442                    cmd += " range {lower_limit} {upper_limit}".format(
443                        **set["extcommunity"]["rt"]["range"]
444                    )
445                elif set["extcommunity"]["rt"].get("address"):
446                    cmd += " {address}".format(**set["extcommunity"]["rt"])
447                if set["extcommunity"]["rt"].get("additive"):
448                    cmd += " additive"
449                command.append(cmd)
450            if set["extcommunity"].get("soo"):
451                command.append(
452                    "set extcommunity soo {soo}".format(**set["extcommunity"])
453                )
454            if set["extcommunity"].get("vpn_distinguisher"):
455                cmd = "set extcommunity vpn-distinguisher"
456                if set["extcommunity"]["vpn_distinguisher"].get("range"):
457                    cmd += " range {lower_limit} {upper_limit}".format(
458                        **set["extcommunity"]["vpn_distinguisher"]["range"]
459                    )
460                elif set["extcommunity"]["vpn_distinguisher"].get("address"):
461                    cmd += " {address}".format(
462                        **set["extcommunity"]["vpn_distinguisher"]
463                    )
464                if set["extcommunity"]["vpn_distinguisher"].get("additive"):
465                    cmd += " additive"
466                command.append(cmd)
467        if set.get("global"):
468            command.append("set global")
469        if set.get("interfaces"):
470            cmd = "set interface "
471            temp = []
472            for k, v in iteritems(set["interfaces"]):
473                temp.append(v)
474            cmd += " ".join(sorted(temp))
475            command.append(cmd)
476        if set.get("level"):
477            cmd = "set level"
478            if set["level"].get("level_1"):
479                cmd += " level-1"
480            elif set["level"].get("level_1_2"):
481                cmd += " level-1-2"
482            elif set["level"].get("level_2"):
483                cmd += " level-2"
484            elif set["level"].get("nssa_only"):
485                cmd += " nssa-only"
486        if set.get("lisp"):
487            command.append("set lisp locator-set {lisp}".format(**set))
488        if set.get("local_preference"):
489            command.append(
490                "set local-preference {local_preference}".format(**set)
491            )
492        if set.get("metric"):
493            cmd = "set metric"
494            if set["metric"].get("metric_value"):
495                cmd += " {metric_value}".format(**set["metric"])
496                if set["metric"].get("deviation"):
497                    if set["metric"]["deviation"] == "plus":
498                        cmd += " +{eigrp_delay} {metric_reliability} {metric_bandwidth} {mtu}".format(
499                            **set["metric"]
500                        )
501                    elif set["metric"]["deviation"] == "minus":
502                        cmd += " -{eigrp_delay} {metric_reliability} {metric_bandwidth} {mtu}".format(
503                            **set["metric"]
504                        )
505            if set["metric"].get("deviation") and not set["metric"].get(
506                "eigrp_delay"
507            ):
508                if set["metric"]["deviation"] == "plus":
509                    cmd = "set metric +{metric_value}".format(**set["metric"])
510                elif set["metric"]["deviation"] == "minus":
511                    cmd = "set metric -{metric_value}".format(**set["metric"])
512            command.append(cmd)
513        if set.get("metric_type"):
514            cmd = "set metric-type"
515            if set["metric_type"].get("external"):
516                cmd += " external"
517            elif set["metric_type"].get("internal"):
518                cmd += " internal"
519            elif set["metric_type"].get("type_1"):
520                cmd += " type-1"
521            elif set["metric_type"].get("type_2"):
522                cmd += " type-2"
523            command.append(cmd)
524        if set.get("mpls_label"):
525            command.append("set mpls-label")
526        if set.get("origin"):
527            cmd = "set origin"
528            if set["origin"].get("igp"):
529                cmd += " igp"
530            elif set["origin"].get("incomplete"):
531                cmd += " incomplete"
532        if set.get("tag"):
533            command.append("set tag {tag}".format(**set))
534        if set.get("traffic_index"):
535            command.append("set traffic-index {traffic_index}".format(**set))
536        if set.get("vrf"):
537            command.append("set vrf {vrf}".format(**set))
538        if set.get("weight"):
539            command.append("set weight {weight}".format(**set))
540        return command
541
542
543def _tmplt_route_map_set_ip(config_data):
544    if config_data.get("set") and config_data["set"].get("ip"):
545        command = []
546        set_ip = config_data["set"]["ip"]
547        cmd = "set ip"
548        if set_ip.get("address"):
549            command.append(
550                "{0} address prefix-list {address}".format(cmd, **set_ip)
551            )
552        if set_ip.get("df"):
553            command.append("{0} df {df}".format(cmd, **set_ip))
554        if set_ip.get("global_route"):
555            cmd += " global next-hop"
556            if set_ip["global_route"].get("verify_availability"):
557                cmd += " verify-availability {address} {sequence} track {track}".format(
558                    **set_ip["global_route"]["verify_availability"]
559                )
560            elif set_ip["global_route"].get("address"):
561                cmd += " {address}".format(**set_ip["global_route"])
562            command.append(cmd)
563        if set_ip.get("next_hop"):
564            cmd += " next-hop"
565            if set_ip["next_hop"].get("address"):
566                command.append(
567                    "{0} {address}".format(cmd, **set_ip["next_hop"])
568                )
569            if set_ip["next_hop"].get("dynamic"):
570                command.append("{0} dynamic dhcp".format(cmd))
571            if set_ip["next_hop"].get("encapsulate"):
572                command.append(
573                    "{0} encapsulate l3vpn {encapsulate}".format(
574                        cmd, **set_ip["next_hop"]
575                    )
576                )
577            if set_ip["next_hop"].get("peer_address"):
578                command.append("{0} peer-address".format(cmd))
579            if set_ip["next_hop"].get("recursive"):
580                child_cmd = "{0} recursive".format(cmd)
581                if set_ip["next_hop"]["recursive"].get("global_route"):
582                    child_cmd += " global"
583                elif set_ip["next_hop"]["recursive"].get("vrf"):
584                    child_cmd += " vrf {vrf}".format(
585                        **set_ip["next_hop"]["recursive"]
586                    )
587                if set_ip["next_hop"]["recursive"].get("address"):
588                    child_cmd += " {address}".format(
589                        **set_ip["next_hop"]["recursive"]
590                    )
591                command.append(child_cmd)
592            if set_ip["next_hop"].get("self"):
593                command.append("{0} self".format(cmd))
594            if set_ip["next_hop"].get("verify_availability"):
595                command.append(
596                    "{0} verify-availability {address} {sequence} track {track}".format(
597                        cmd, **set_ip["next_hop"]["verify_availability"]
598                    )
599                )
600        if set_ip.get("precedence"):
601            cmd += " precedence"
602            if set_ip["precedence"].get("critical"):
603                cmd += " critical"
604            elif set_ip["precedence"].get("flash"):
605                cmd += " flash"
606            elif set_ip["precedence"].get("flash_override"):
607                cmd += " flash-override"
608            elif set_ip["precedence"].get("immediate"):
609                cmd += " immediate"
610            elif set_ip["precedence"].get("internet"):
611                cmd += " internet"
612            elif set_ip["precedence"].get("network"):
613                cmd += " network"
614            elif set_ip["precedence"].get("priority"):
615                cmd += " priority"
616            elif set_ip["precedence"].get("routine"):
617                cmd += " routine"
618            command.append(cmd)
619        if set_ip.get("qos_group"):
620            command.append("{0} qos-group {qos_group}".format(cmd, **set_ip))
621        if set_ip.get("tos"):
622            cmd += " tos"
623            if set_ip["tos"].get("max_reliability"):
624                cmd += " max-reliability"
625            elif set_ip["tos"].get("max_throughput"):
626                cmd += " max-throughput"
627            elif set_ip["tos"].get("min_delay"):
628                cmd += " min-delay"
629            elif set_ip["tos"].get("min_monetary_cost"):
630                cmd += " min-monetary-cost"
631            elif set_ip["tos"].get("normal"):
632                cmd += " normal"
633            command.append(cmd)
634        if set_ip.get("vrf"):
635            cmd += " vrf {vrf} next-hop".format(**set_ip)
636            if set_ip["vrf"].get("verify_availability").get("address"):
637                cmd += " verify-availability {address} {sequence} track {track}".format(
638                    **set_ip["vrf"]["verify_availability"]
639                )
640            elif set_ip["vrf"].get("address"):
641                cmd += " {address}".format(**set_ip["vrf"])
642            command.append(cmd)
643        return command
644
645
646def _tmplt_route_map_set_ipv6(config_data):
647    if config_data.get("set") and config_data["set"].get("ipv6"):
648        set_ipv6 = config_data["set"]["ipv6"]
649        cmd = "set ipv6"
650        if set_ipv6.get("address"):
651            cmd += " address prefix-list {address}".format(**set_ipv6)
652        if set_ipv6.get("default"):
653            cmd += " default"
654        if set_ipv6.get("global_route"):
655            cmd += " global next-hop"
656            if set_ipv6["global_route"].get("verify_availability"):
657                cmd += " verify-availability {address} {sequence} track {track}".format(
658                    **set_ipv6["global_route"]["verify_availability"]
659                )
660            elif set_ipv6["global_route"].get("address"):
661                cmd += " {address}".format(**set_ipv6["global_route"])
662        if set_ipv6.get("next_hop"):
663            cmd += " next-hop"
664            if set_ipv6["next_hop"].get("address"):
665                cmd += " {address}".format(**set_ipv6["next_hop"])
666            if set_ipv6["next_hop"].get("encapsulate"):
667                cmd += " encapsulate l3vpn {encapsulate}".format(
668                    **set_ipv6["next_hop"]
669                )
670            if set_ipv6["next_hop"].get("peer_address"):
671                cmd += " peer-address"
672        if set_ipv6.get("precedence"):
673            cmd += " precedence {precedence}".format(**set_ipv6)
674        if set_ipv6.get("vrf"):
675            cmd += " vrf {vrf} next-hop verify-availability {address} {sequence} track {track}".format(
676                **set_ipv6["vrf"]["verify_availability"]
677            )
678        return cmd
679
680
681class Route_mapsTemplate(NetworkTemplate):
682    def __init__(self, lines=None):
683        super(Route_mapsTemplate, self).__init__(lines=lines, tmplt=self)
684
685    PARSERS = [
686        {
687            "name": "route_map",
688            "getval": re.compile(
689                r"""
690                ^route-map*
691                \s*(?P<route_map>\S+)*
692                \s*(?P<action>deny|permit)*
693                \s*(?P<sequence>\d+)*
694                $""",
695                re.VERBOSE,
696            ),
697            "setval": "",
698            "result": {
699                "{{ route_map }}": {
700                    "route_map": "{{ route_map }}",
701                    "{{ action|d() + '_' + sequence|d() }}": {
702                        "entries": {
703                            "action": "{{ action }}",
704                            "sequence": "{{ sequence }}",
705                        }
706                    },
707                }
708            },
709            "shared": True,
710        },
711        {
712            "name": "continue_entry",
713            "getval": re.compile(
714                r"""
715                \s+continue*
716                \s*(?P<entry_sequence>\d+)*
717                $""",
718                re.VERBOSE,
719            ),
720            "setval": "continue {{ continue_entry.entry_sequence }}",
721            "result": {
722                "{{ route_map }}": {
723                    "{{ action|d() + '_' + sequence|d() }}": {
724                        "entries": {
725                            "continue_entry": {
726                                "set": "{{ True if entry_sequence is not defined }}",
727                                "entry_sequence": "{{ entry_sequence }}",
728                            }
729                        }
730                    }
731                }
732            },
733        },
734        {
735            "name": "description",
736            "getval": re.compile(
737                r"""
738                \s+description*
739                \s*(?P<description>\S.*)*
740                $""",
741                re.VERBOSE,
742            ),
743            "setval": "description {{ description }}",
744            "result": {
745                "{{ route_map }}": {
746                    "{{ action|d() + '_' + sequence|d() }}": {
747                        "entries": {"description": "{{ description }}"}
748                    }
749                }
750            },
751        },
752        {
753            "name": "match",
754            "getval": re.compile(
755                r"""
756                \s+match*
757                \s*(?P<additional_paths>additional-paths\sadvertise-set\s\S.*)*
758                \s*(?P<as_path>as-path.*|as-path)*
759                \s*(?P<clns>clns\s(address\s\S+|next-hop\s\S+|route-source\s\S+))*
760                \s*(?P<community>community\s\S.*)*
761                \s*(?P<extcommunity>extcommunity\s\S.*)*
762                \s*(?P<interfaces>interface\s\S.*)*
763                \s*(?P<length>length\s\d+\s\d+)*
764                \s*(?P<local_preference>local-preference\s\d.*|local-preference)*
765                \s*(?P<mdt_group>mdt-group\s\S.*|mdt-group)*
766                \s*(?P<metric>metric\sexternal\s\S.*|metric\s\d+\S.*)*
767                \s*(?P<mpls_label>mpls-label)*
768                \s*(?P<policy_list>policy-list\s\S.*)*
769                \s*(?P<route_type>route-type\s(external\s(type-1|type-2)|internal|level-1|level-2|local|nssa-external\s(type-1|type-2)))*
770                \s*(?P<rpki>rpki\s(invalid|not-found|valid))*
771                \s*(?P<security_group>security-group\s(destination\stag\s\d.*|source\stag\s\d.*))*
772                \s*(?P<source_protocol>source-protocol\s\S.*)*
773                \s*(?P<tag>tag\slist\s\S.*|tag\s\S.*)*
774                \s*(?P<track>track\s*\d+)*
775                $""",
776                re.VERBOSE,
777            ),
778            "setval": _tmplt_route_map_match,
779            "result": {
780                "{{ route_map }}": {
781                    "{{ action|d() + '_' + sequence|d() }}": {
782                        "entries": {
783                            "match": {
784                                "additional_paths": {
785                                    "all": "{{ True if additional_paths is defined and 'all' in additional_paths }}",
786                                    "best": "{{ additional_paths.split('best ')[1].split(' ')[0]|int if additional_paths is defined and\
787                                             'best' in additional_paths and 'best-range' not in additional_paths }}",
788                                    "best_range": {
789                                        "lower_limit": "{{ additional_paths.split('best-range ')[1].split(' ')[0]|int if additional_paths is defined and\
790                                                 'best-range' in additional_paths }}",
791                                        "upper_limit": "{{ additional_paths.split('best-range ')[1].split(' ')[1]|int if additional_paths is defined and\
792                                                 'best-range' in additional_paths }}",
793                                    },
794                                    "group_best": "{{ True if additional_paths is defined and 'group-best' in additional_paths }}",
795                                },
796                                "as_path": {
797                                    "set": "{{ True if as_path is defined and as_path.split(' ')|length == 1 }}",
798                                    "acls": "{{ as_path.split('as-path ')[1].split(' ') if as_path is defined and as_path.split(' ')|length > 1 }}",
799                                },
800                                "clns": {
801                                    "address": "{{ clns.split('clns address ')[1] if clns is defined }}",
802                                    "next_hop": "{{ clns.split('clns next-hop ')[1] if clns is defined }}",
803                                    "route_source": "{{ clns.split('clns route-source ')[1] if clns is defined }}",
804                                },
805                                "community": {
806                                    "name": "{{ community.split('community ')[1].split(' exact-match')[0].split(' ') if community is defined }}",
807                                    "exact_match": "{{ True if community is defined and 'exact-match' in community }}",
808                                },
809                                "extcommunity": "{{ extcommunity.split('extcommunity ')[1].split(' ') if extcommunity is defined }}",
810                                "interfaces": "{{ interfaces.split('interface ')[1].split(' ') if interfaces is defined }}",
811                                "length": {
812                                    "minimum": "{{ length.split(' ')[1] if length is defined }}",
813                                    "maximum": "{{ length.split(' ')[2] if length is defined }}",
814                                },
815                                "local_preference": {
816                                    "set": "{{ True if local_preference is defined and local_preference.split(' ')|length == 1 }}",
817                                    "value": "{{ local_preference.split('local-preference ')[1].split(' ') if local_preference is defined }}",
818                                },
819                                "mdt_group": {
820                                    "set": "{{ True if mdt_group is defined and mdt_group.split(' ')|length == 1 }}",
821                                    "acls": "{{ mdt_group.split('mdt-group ')[1].split(' ') if mdt_group is defined }}",
822                                },
823                                "metric": {
824                                    "external": "{{ True if metric is defined and 'external' in metric.split(' ') }}",
825                                    "value": "{% if metric is defined and 'external' not in metric.split(' ') %}{{ metric.split(' ')[1] }}\
826                                            {% elif metric is defined and 'external' in metric.split(' ') %}{{ metric.split(' ')[2] }}\
827                                            {% endif %}",
828                                    "deviation": "{{ True if metric is defined and '+-' in metric }}",
829                                    "deviation_value": "{% if metric is defined and 'external' in metric and '+-' in metric %}{{ metric.split(' ')[4] }}\
830                                            {% elif metric is defined and 'external' not in metric and '+-' in metric %}{{ metric.split(' ')[3] }}{% endif %}",
831                                },
832                                "mpls_label": "{{ True if mpls_label is defined }}",
833                                "policy_lists": "{{ policy_list.split('policy-list ')[1].split(' ') if policy_list is defined }}",
834                                "route_type": {
835                                    "external": {
836                                        "set": "{{ True if route_type is defined and 'type-1' not in route_type and 'type-2' not in route_type}}",
837                                        "type_1": "{{ True if route_type is defined and 'type-1' in route_type }}",
838                                        "type_2": "{{ True if route_type is defined and 'type-2' in route_type }}",
839                                    },
840                                    "internal": "{{ True if route_type is defined and 'internal' in route_type }}",
841                                    "level_1": "{{ True if route_type is defined and 'level-1' in route_type }}",
842                                    "level_2": "{{ True if route_type is defined and 'level-2' in route_type }}",
843                                    "local": "{{ True if route_type is defined and 'local' in route_type }}",
844                                    "nssa_external": {
845                                        "set": "{{ True if route_type is defined and 'type-1' not in route_type and 'type-2' not in route_type}}",
846                                        "type_1": "{{ True if route_type is defined and 'type-1' in route_type }}",
847                                        "type_2": "{{ True if route_type is defined and 'type-2' in route_type }}",
848                                    },
849                                },
850                                "rpki": {
851                                    "invalid": "{{ True if rpki is defined and 'invalid' in rpki and 'valid' not in rpki.split(' ') }}",
852                                    "not_found": "{{ True if rpki is defined and 'not-found' in rpki }}",
853                                    "valid": "{{ True if rpki is defined and 'valid' in rpki and 'invalid' not in rpki.split(' ') }}",
854                                },
855                                "security_group": {
856                                    "destination": "{{ security_group.split('destination tag ')[1].split(' ') if security_group is defined and\
857                                        'destination' in security_group }}",
858                                    "source": "{{ security_group.split('source tag ')[1].split(' ') if security_group is defined and\
859                                        'source' in security_group }}",
860                                },
861                                "source_protocol": {
862                                    "bgp": "{{ source_protocol.split('bgp ')[1].split(' ')[0] if source_protocol is defined and 'bgp' in source_protocol }}",
863                                    "connected": "{{ True if source_protocol is defined and 'connected' in source_protocol }}",
864                                    "eigrp": "{{ source_protocol.split('eigrp ')[1].split(' ')[0] if source_protocol is defined and\
865                                            'eigrp' in source_protocol }}",
866                                    "isis": "{{ True if source_protocol is defined and 'isis' in source_protocol }}",
867                                    "lisp": "{{ True if source_protocol is defined and 'lisp' in source_protocol }}",
868                                    "mobile": "{{ True if source_protocol is defined and 'mobile' in source_protocol }}",
869                                    "ospf": "{{ source_protocol.split('ospf ')[1].split(' ')[0] if source_protocol is defined and\
870                                            'ospf' in source_protocol }}",
871                                    "ospfv3": "{{ source_protocol.split('ospfv3 ')[1].split(' ')[0] if source_protocol is defined and\
872                                            'ospfv3' in source_protocol }}",
873                                    "rip": "{{ True if source_protocol is defined and 'rip' in source_protocol }}",
874                                    "static": "{{ True if source_protocol is defined and 'static' in source_protocol }}",
875                                },
876                                "tag": {
877                                    "value": "{{ tag.split('tag ')[1].split(' ') if tag is defined and 'list' not in tag }}",
878                                    "tag_list": "{{ tag.split('tag list ')[1].split(' ') if tag is defined and 'list' in tag }}",
879                                },
880                                "track": "{{ track.split('track ')[1] if track is defined }}",
881                            }
882                        }
883                    }
884                }
885            },
886        },
887        {
888            "name": "match.ip",
889            "getval": re.compile(
890                r"""
891                \s+match*
892                \s*(?P<ip>ip)*
893                \s*(?P<address>address\sprefix-list\s\S.*|address\s\S.*)*
894                \s*(?P<flowspec>flowspec\sdest-pfx\s(prefix-list\s\S.*|\S.*)|flowspec\ssrc-pfx\s(prefix-list\s\S.*|\S.*))*
895                \s*(?P<next_hop>next-hop\sprefix-list\s\S.*|next-hop\s\S.*|next-hop)*
896                \s*(?P<redistribution_source>redistribution-source\sprefix-list\s\S.*|redistribution-source\s\S.*|redistribution-source)*
897                \s*(?P<route_source>route-source\sredistribution-source\sprefix-list\s\S.*|route-source\sredistribution-source\s\S.*|route-source\sprefix-list\s\S.*|route-source\s\S.*|route-source\sredistribution-source|route-source)*
898                $""",
899                re.VERBOSE,
900            ),
901            "setval": _tmplt_route_map_match_ip,
902            "compval": "match",
903            "result": {
904                "{{ route_map }}": {
905                    "{{ action|d() + '_' + sequence|d() }}": {
906                        "entries": {
907                            "match": {
908                                "ip": {
909                                    "address": {
910                                        "acls": "{{ address.split('address ')[1].split(' ') if address is defined and\
911                                            'prefix-list' not in address else none }}",
912                                        "prefix_lists": "{{ address.split('address prefix-list ')[1].split(' ') if address is defined and\
913                                            'prefix-list' in address else None }}",
914                                    },
915                                    "flowspec": {
916                                        "dest_pfx": "{{ True if flowspec is defined and 'dest-pfx' in flowspec }}",
917                                        "src_pfx": "{{ True if flowspec is defined and 'src-pfx' in flowspec }}",
918                                        "acls": "{{ flowspec.split('flowspec ')[1].split(' ')|d() if flowspec is defined and\
919                                            'prefix-list' not in flowspec else '' }}",
920                                        "prefix_lists": "{{ flowspec.split('flowspec prefix-list ')[1].split(' ')|d() if flowspec is defined and\
921                                            'prefix-list' in flowspec else ''}}",
922                                    },
923                                    "next_hop": {
924                                        "set": "{{ True if next_hop is defined and next_hop.split(' ')|length == 1 }}",
925                                        "acls": "{{ next_hop.split('next-hop ')[1].split(' ') if next_hop is defined and\
926                                            'prefix-list' not in next_hop else '' }}",
927                                        "prefix_lists": "{{ next_hop.split('next-hop prefix-list ')[1].split(' ') if next_hop is defined and\
928                                             'prefix-list' in next_hop and next_hop.split('next-hop prefix-list ')[1] is not none else '' }}",
929                                    },
930                                    "redistribution_source": {
931                                        "set": "{{ True if redistribution_source is defined and redistribution_source.split(' ')|length == 1 }}",
932                                        "acls": "{{ redistribution_source.split('redistribution-source ')[1].split(' ')|d()\
933                                            if redistribution_source is defined and 'prefix-list' not in redistribution_source else '' }}",
934                                        "prefix_lists": "{{ redistribution_source.split('redistribution-source prefix-list ')[1].split(' ')|d()\
935                                            if redistribution_source is defined and 'prefix-list' in redistribution_source else '' }}",
936                                    },
937                                    "route_source": {
938                                        "set": "{{ True if route_source is defined and route_source.split(' ')|length == 1 }}",
939                                        "redistribution_source": "{{ True if route_source is defined and 'redistribution-source' in route_source }}",
940                                        "acls": "{{ route_source.split('route-source ')[1].split(' ') if route_source is defined and\
941                                                'prefix-list' not in route_source else '' }}",
942                                        "prefix_lists": "{{ route_source.split('route-source prefix-list ')[1].split(' ') if route_source is defined and\
943                                                'prefix-list' in route_source else '' }}",
944                                    },
945                                }
946                            }
947                        }
948                    }
949                }
950            },
951        },
952        {
953            "name": "match.ipv6",
954            "getval": re.compile(
955                r"""
956                \s+match*
957                \s*(?P<ipv6>ipv6)*
958                \s*(?P<address>address\sprefix-list\s\S.*|address\s\S.*)*
959                \s*(?P<flowspec>flowspec\sdest-pfx\s(prefix-list\s\S.*|\S.*)|flowspec\ssrc-pfx\s(prefix-list\s\S.*|\S.*))*
960                \s*(?P<next_hop>next-hop\sprefix-list\s\S.*|next-hop\s\S.*)*
961                \s*(?P<route_source>route-source\sprefix-list\s\S.*|route-source\s\S.*)*
962                $""",
963                re.VERBOSE,
964            ),
965            "setval": _tmplt_route_map_match_ipv6,
966            "compval": "match",
967            "result": {
968                "{{ route_map }}": {
969                    "{{ action|d() + '_' + sequence|d() }}": {
970                        "entries": {
971                            "match": {
972                                "ipv6": {
973                                    "address": {
974                                        "acl": "{{ address.split('address ')[1] if address is defined and 'prefix-list' not in address }}",
975                                        "prefix_list": "{{ address.split('address prefix-list ')[1] if address is defined and 'prefix-list' in address }}",
976                                    },
977                                    "flowspec": {
978                                        "dest_pfx": "{{ True if flowspec is defined and 'dest-pfx' in flowspec }}",
979                                        "src_pfx": "{{ True if flowspec is defined and 'src-pfx' in flowspec }}",
980                                        "acl": "{{ flowspec.split('flowspec ')[1] if flowspec is defined and 'prefix-list' not in flowspec }}",
981                                        "prefix_list": "{{ flowspec.split('flowspec prefix-list ')[1] if flowspec is defined and 'prefix-list' in flowspec }}",
982                                    },
983                                    "next_hop": {
984                                        "acl": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and 'prefix-list' not in next_hop }}",
985                                        "prefix_list": "{{ next_hop.split('next-hop prefix-list ')[1] if next_hop is defined and 'prefix-list' in next_hop }}",
986                                    },
987                                    "route_source": {
988                                        "acl": "{{ route_source.split('route-source ')[1] if route_source is defined and 'prefix-list' not in route_source }}",
989                                        "prefix_list": "{{ route_source.split('route-source prefix-list ')[1] if route_source is defined and\
990                                                'prefix-list' in route_source }}",
991                                    },
992                                }
993                            }
994                        }
995                    }
996                }
997            },
998        },
999        {
1000            "name": "set",
1001            "getval": re.compile(
1002                r"""
1003                \s+set*
1004                \s*(?P<aigp_metric>aigp-metric\sigp-metric|aigp-metric\s\d+)*
1005                \s*(?P<as_path>as-path\s(prepend\s(last-as\s\d+|\S+)|tag))*
1006                \s*(?P<automatic_tag>automatic-tag)*
1007                \s*(?P<clns>clns\snext-hop\s\S.*)*
1008                \s*(?P<comm_list>comm-list\s\S+\sdelete)*
1009                \s*(?P<community>community\s\S.*)*
1010                \s*(?P<dampening>dampening\s\d+\s\d+\s\d+\s\d+)*
1011                \s*(?P<default>default\sinterface\s\S.*)*
1012                \s*(?P<extcomm_list>extcomm-list\s\S+\sdelete)*
1013                \s*(?P<extcommunity>extcommunity\s\S.*)*
1014                \s*(?P<global>global)*
1015                \s*(?P<interfaces>interface\s\S.*)*
1016                \s*(?P<level>level\s(level-1-2|level-1|level-2|nssa-only))*
1017                \s*(?P<lisp>lisp\slocator-set\s\S+)*
1018                \s*(?P<local_preference>local-preference\s\d+)*
1019                \s*(?P<metric>metric\s\S.*)*
1020                \s*(?P<metric_type>metric-type\s(external|internal|type-1|type-2))*
1021                \s*(?P<mpls_label>mpls-label)*
1022                \s*(?P<origin>origin\s(igp|incomplete))*
1023                \s*(?P<tag>tag\s(([0-9]{1,3}\.?){4}|\d+))*
1024                \s*(?P<traffic_index>traffic-index\s\d+)*
1025                \s*(?P<vrf>vrf\s\S+)*
1026                \s*(?P<weight>weight\s\d+)*
1027                $""",
1028                re.VERBOSE,
1029            ),
1030            "setval": _tmplt_route_map_set,
1031            "result": {
1032                "{{ route_map }}": {
1033                    "{{ action|d() + '_' + sequence|d() }}": {
1034                        "entries": {
1035                            "set": {
1036                                "aigp_metric": {
1037                                    "value": "{{ aigp_metric.split('aigp-metric ')[1] if aigp_metric is defined and\
1038                                            'igp-metric' not in aigp_metric.split(' ') }}",
1039                                    "igp_metric": "{{ True if aigp_metric is defined and 'igp-metric' in aigp_metric.split(' ') }}",
1040                                },
1041                                "as_path": {
1042                                    "prepend": {
1043                                        "as_number": "{{ as_path.split('as-path prepend ')[1].split(' ')\
1044                                            if as_path is defined and 'prepend' in as_path and 'last-as' not in as_path }}",
1045                                        "last_as": "{{ as_path.split('as-path prepend last-as ')[1] if as_path is defined and 'prepend' in as_path and\
1046                                                'last-as' in as_path }}",
1047                                    },
1048                                    "tag": "{{ True if as_path is defined and 'tag' in as_path }}",
1049                                },
1050                                "automatic_tag": "{{ True if automatic_tag is defined }}",
1051                                "clns": "{{ clns.split('clns next-hop ')[1] if clns is defined }}",
1052                                "comm_list": "{{ comm_list.split(' ')[1] if comm_list is defined }}",
1053                                "community": {
1054                                    "number": "{{ community.split(' ')[1] if community is defined and 'additive' not in community.split(' ')\
1055                                                 and 'gshut' not in community.split(' ') and 'internet' not in community.split(' ')\
1056                                                     and 'internet' not in community.split(' ') and 'local_as' not in community.split(' ')\
1057                                                         and 'no_advertise' not in community.split(' ') and 'no_export' not in community.split(' ') }}",
1058                                    "additive": "{{ True if community is defined and 'additive' in community }}",
1059                                    "gshut": "{{ True if community is defined and 'gshut' in community }}",
1060                                    "internet": "{{ True if community is defined and 'internet' in community }}",
1061                                    "local_as": "{{ True if community is defined and 'local_as' in community }}",
1062                                    "no_advertise": "{{ True if community is defined and 'no_advertise' in community }}",
1063                                    "no_export": "{{ True if community is defined and 'no_export' in community }}",
1064                                    "none": "{{ True if community is defined and 'none' in community }}",
1065                                },
1066                                "dampening": {
1067                                    "penalty_half_time": "{{ dampening.split(' ')[1] if dampening is defined }}",
1068                                    "reuse_route_val": "{{ dampening.split(' ')[2] if dampening is defined }}",
1069                                    "suppress_route_val": "{{ dampening.split(' ')[3] if dampening is defined }}",
1070                                    "max_suppress": "{{ dampening.split(' ')[4] if dampening is defined }}",
1071                                },
1072                                "default": "{{ default.split('default interface ')[1] if default is defined }}",
1073                                "extcomm_list": "{{ extcomm_list.split(' ')[1] if extcomm_list is defined }}",
1074                                "extcommunity": {
1075                                    "cost": {
1076                                        "id": "{%- if extcommunity is defined and 'cost' in extcommunity and\
1077                                            'igp' not in extcommunity and 'pre-bestpath' not in extcommunity -%} {{ extcommunity.split(' ')[2] }}\
1078                                                {%- elif extcommunity is defined and 'cost' in extcommunity and ('igp' in extcommunity or\
1079                                                    'pre-bestpath' in extcommunity) -%} {{ extcommunity.split(' ')[3] }} {%- endif -%}",
1080                                        "cost_value": "{% if extcommunity is defined and 'cost' in extcommunity and 'igp' not in extcommunity and\
1081                                            'pre-bestpath' not in extcommunity %} {{ extcommunity.split(' ')[3] }}\
1082                                                {% elif extcommunity is defined and 'cost' in extcommunity and ('igp' in extcommunity or\
1083                                                    'pre-bestpath' in extcommunity) %} {{ extcommunity.split(' ')[4] }} {% endif %}",
1084                                        "igp": "{{ True if extcommunity is defined and 'cost' in extcommunity and 'igp' in extcommunity }}",
1085                                        "pre_bestpath": "{{ True if extcommunity is defined and 'cost' in extcommunity and 'pre-bestpath' in extcommunity }}",
1086                                    },
1087                                    "rt": {
1088                                        "address": "{{ extcommunity.split(' ')[2] if extcommunity is defined and 'rt' in extcommunity and\
1089                                                'range' not in extcommunity }}",
1090                                        "range": {
1091                                            "lower_limit": "{{ extcommunity.split('range ')[1].split(' ')[0] if extcommunity is defined and\
1092                                                    'rt' in extcommunity and 'range' in extcommunity }}",
1093                                            "upper_limit": "{{ extcommunity.split('range ')[1].split(' ')[1] if extcommunity is defined and\
1094                                                    'rt' in extcommunity and 'range' in extcommunity }}",
1095                                        },
1096                                        "additive": "{{ True if extcommunity is defined and 'rt' in extcommunity and 'additive' in extcommunity  }}",
1097                                    },
1098                                    "soo": "{{ extcommunity.split(' ')[2] if extcommunity is defined and 'soo' in extcommunity }}",
1099                                    "vpn_distinguisher": {
1100                                        "address": "{{ extcommunity.split(' ')[2] if extcommunity is defined and\
1101                                                'vpn-distinguisher' in extcommunity and 'range' not in extcommunity }}",
1102                                        "range": {
1103                                            "lower_limit": "{{ extcommunity.split('range ')[1].split(' ')[0] if extcommunity is defined and\
1104                                                    'vpn-distinguisher' in extcommunity and 'range' in extcommunity }}",
1105                                            "upper_limit": "{{ extcommunity.split('range ')[1].split(' ')[1] if extcommunity is defined and\
1106                                                    'vpn-distinguisher' in extcommunity and 'range' in extcommunity }}",
1107                                        },
1108                                        "additive": "{{ True if extcommunity is defined and 'vpn-distinguisher' in extcommunity and\
1109                                                 'additive' in extcommunity }}",
1110                                    },
1111                                },
1112                                "global_route": "{{ True if global is defined }}",
1113                                "interfaces": "{{ interfaces.split('interface ')[1].split(' ') if interfaces is defined }}",
1114                                "level": {
1115                                    "level_1": "{{ True if level is defined and 'level-1' in level and 'level-1-2' not in level }}",
1116                                    "level_1_2": "{{ True if level is defined and 'level-1-2' in level }}",
1117                                    "level_2": "{{ True if level is defined and 'level-2' in level }}",
1118                                    "nssa_only": "{{ True if level is defined and 'nssa-only' in level }}",
1119                                },
1120                                "lisp": "{{ lisp.split('lisp locator-set ')[1] if lisp is defined }}",
1121                                "local_preference": "{{ local_preference.split('local-preference ')[1] if local_preference is defined }}",
1122                                "metric": {
1123                                    "deviation": "{%- if metric is defined and '+' in metric -%}{{ 'plus' }}\
1124                                                {%- elif metric is defined and '-' in metric -%}{{ 'minus' }}{%- endif -%}",
1125                                    "metric_value": "{{ metric.split(' ')[1] if metric is defined and\
1126                                             (metric.split(' ')[1] != '+' or metric.split(' ')[1] != '-') }}",
1127                                    "eigrp_delay": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1128                                            {{ metric.split('+')[1].split(' ')[0] }}\
1129                                                {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1130                                                    {{ metric.split('-')[1].split(' ')[0] }}\
1131                                                    {% endif %}",
1132                                    "metric_reliability": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1133                                            {{ metric.split('+')[1].split(' ')[1] }}\
1134                                                {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1135                                                    {{ metric.split('-')[1].split(' ')[1] }}\
1136                                                    {% endif %}",
1137                                    "metric_bandwidth": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1138                                            {{ metric.split('+')[1].split(' ')[2] }}\
1139                                                {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1140                                                    {{ metric.split('-')[1].split(' ')[2] }}\
1141                                                    {% endif %}",
1142                                    "mtu": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1143                                            {{ metric.split('+')[1].split(' ')[3] }}\
1144                                                {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\
1145                                                    {{ metric.split('-')[1].split(' ')[3] }}\
1146                                                    {% endif %}",
1147                                },
1148                                "metric_type": {
1149                                    "external": "{{ True if metric_type is defined and 'external' in metric_type }}",
1150                                    "internal": "{{ True if metric_type is defined and 'internal' in metric_type }}",
1151                                    "type_1": "{{ True if metric_type is defined and 'type-1' in metric_type }}",
1152                                    "type_2": "{{ True if metric_type is defined and 'type-2' in metric_type }}",
1153                                },
1154                                "mpls_label": "{{ True if mpls_label is defined }}",
1155                                "origin": {
1156                                    "igp": "{{ True if origin is defined and 'igp' in origin }}",
1157                                    "incomplete": "{{ True if origin is defined and 'incomplete' in origin }}",
1158                                },
1159                                "tag": "{{ tag.split('tag ')[1] if tag is defined }}",
1160                                "traffic_index": "{{ traffic_index.split('traffic-index ')[1] if traffic_index is defined }}",
1161                                "vrf": "{{ vrf.split('vrf ')[1] if vrf is defined }}",
1162                                "weight": "{{ weight.split('weight ')[1] if weight is defined }}",
1163                            }
1164                        }
1165                    }
1166                }
1167            },
1168        },
1169        {
1170            "name": "set.ip",
1171            "getval": re.compile(
1172                r"""
1173                \s+set*
1174                \s*(?P<ip>ip)*
1175                \s*(?P<address>address\sprefix-list\s\S+)*
1176                \s*(?P<default>default)*
1177                \s*(?P<df>df\s\d)*
1178                \s*(?P<global>global\snext-hop\s(verify-availability\s([0-9]{1,3}\.?){4}\s\d+\strack\s\d+|(([0-9]{1,3}\.?){4}).*))*
1179                \s*(?P<precedence>precedence\s(critical|flash|flash-override|immediate|internet|network|priority|routine)|precedence)*
1180                \s*(?P<qos_group>qos_group\s\d+)*
1181                \s*(?P<tos>tos\s(max-reliability|max-throughput|min-delay|min-monetary-cost|normal)|tos)*
1182                \s*(?P<vrf>vrf\s\S+\snext-hop\s\S.*)*
1183                \s*(?P<next_hop>next-hop\s\S.*)*
1184                $""",
1185                re.VERBOSE,
1186            ),
1187            "setval": _tmplt_route_map_set_ip,
1188            "compval": "set",
1189            "result": {
1190                "{{ route_map }}": {
1191                    "{{ action|d() + '_' + sequence|d() }}": {
1192                        "entries": {
1193                            "set": {
1194                                "ip": {
1195                                    "address": "{{ address.split('address prefix-list ')[1] if address is defined }}",
1196                                    "default": "{{ True if default is defined }}",
1197                                    "df": "{{ df.split('df ')[1] if df is defined }}",
1198                                    "global_route": {
1199                                        "address": "{% if global is defined and 'verify-availability' not in global %}{{ global.split('global next-hop ')[1] }}\
1200                                                        {% elif global is defined and 'verify-availability' in global %}{{ global.split(' ')[3] }}{% endif %}",
1201                                        "verify_availability": {
1202                                            "address": "{{ global.split(' ')[3] if global is defined and 'verify-availability' in global }}",
1203                                            "sequence": "{{ global.split(' ')[4] if global is defined and 'verify-availability' in global }}",
1204                                            "track": "{{ global.split('track ')[1] if global is defined and 'verify-availability' in global }}",
1205                                        },
1206                                    },
1207                                    "next_hop": {
1208                                        "address": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and 'peer-address' not in next_hop and\
1209                                                 'self' not in next_hop and next_hop.split(' ')|length == 2 }}",
1210                                        "dynamic": "{{ True if next_hop is defined and 'dynamic dhcp' in next_hop }}",
1211                                        "encapsulate": "{{ next_hop.split('next-hop encapsulate l3vpn ')[1] if next_hop is defined and\
1212                                                'encapsulate' in next_hop }}",
1213                                        "peer_address": "{{ True if next_hop is defined and 'peer-address' in next_hop }}",
1214                                        "recursive": {
1215                                            "global_route": "{{ True if next_hop is defined and 'global' in next_hop.split(' ') }}",
1216                                            "vrf": "{{ next_hop.split(' ')[3] if next_hop is defined and 'vrf' in next_hop }}",
1217                                            "address": "{%- if next_hop is defined and 'global' in next_hop.split(' ') -%}\
1218                                                {{  next_hop.split('next-hop recursive global ')[1] }}\
1219                                                {%- elif next_hop is defined and 'vrf' in next_hop.split(' ') -%}{{  next_hop.split(' ')[4] }}\
1220                                                {%- elif next_hop is defined and 'vrf' not in next_hop.split(' ') and 'global' not in next_hop.split(' ') -%}\
1221                                                    {{ next_hop.split(' ')[2] }} {%- endif -%}",
1222                                        },
1223                                        "self": "{{ True if next_hop is defined and 'self' in next_hop }}",
1224                                        "verify_availability": {
1225                                            "set": "{{ True if next_hop is defined and 'verify-availability' in next_hop and 'track' not in next_hop }}",
1226                                            "address": "{{ next_hop.split(' ')[2] if next_hop is defined and 'verify-availability' in next_hop and\
1227                                                     'track' in next_hop }}",
1228                                            "sequence": "{{ next_hop.split(' ')[3] if next_hop is defined and 'verify-availability' in next_hop and\
1229                                                     'track' in next_hop }}",
1230                                            "track": "{{ next_hop.split('track ')[1] if next_hop is defined and 'verify-availability' in next_hop and\
1231                                                     'track' in next_hop }}",
1232                                        },
1233                                    },
1234                                    "precedence": {
1235                                        "set": "{{ True if precedence is defined and precedence.split(' ')|length == 1 }}",
1236                                        "critical": "{{ True if precedence is defined and 'critical' in precedence }}",
1237                                        "flash": "{{ True if precedence is defined and 'flash' in precedence }}",
1238                                        "flash_override": "{{ True if precedence is defined and 'flash-override' in precedence }}",
1239                                        "immediate": "{{ True if precedence is defined and 'immediate' in precedence }}",
1240                                        "internet": "{{ True if precedence is defined and 'internet' in precedence }}",
1241                                        "network": "{{ True if precedence is defined and 'network' in precedence }}",
1242                                        "priority": "{{ True if precedence is defined and 'priority' in precedence }}",
1243                                        "routine": "{{ True if precedence is defined and 'routine' in precedence }}",
1244                                    },
1245                                    "qos_group": "{{ qos_group.split('qos-group ')[1] if qos_group is defined }}",
1246                                    "tos": {
1247                                        "set": "{{ True if tos is defined and tos.split(' ')|length == 1 }}",
1248                                        "max_reliability": "{{ True if tos is defined and 'max-reliability' in tos }}",
1249                                        "max_throughput": "{{ True if tos is defined and 'max-throughput' in tos }}",
1250                                        "min_delay": "{{ True if tos is defined and 'min-delay' in tos }}",
1251                                        "min_monetary_cost": "{{ True if tos is defined and 'min-monetary-cost' in tos }}",
1252                                        "normal": "{{ True if tos is defined and 'normal' in tos }}",
1253                                    },
1254                                    "vrf": {
1255                                        "name": "{{ vrf.split(' ')[1] if vrf is defined }}",
1256                                        "address": "{{ vrf.split('next-hop ')[1] if vrf is defined and 'verify-availability' not in vrf }}",
1257                                        "verify_availability": {
1258                                            "set": "{{ True if vrf is defined and 'track' not in vrf }}",
1259                                            "address": "{{ vrf.split(' ')[4] if vrf is defined and 'track' in vrf }}",
1260                                            "sequence": "{{ vrf.split(' ')[5] if vrf is defined and 'track' in vrf }}",
1261                                            "track": "{{ vrf.split('track ')[1] if vrf is defined and 'track' in vrf }}",
1262                                        },
1263                                    },
1264                                }
1265                            }
1266                        }
1267                    }
1268                }
1269            },
1270        },
1271        {
1272            "name": "set.ipv6",
1273            "getval": re.compile(
1274                r"""
1275                \s+set*
1276                \s*(?P<ipv6>ipv6)*
1277                \s*(?P<address>address\sprefix-list\s\S+)*
1278                \s*(?P<default>default\snext-hop\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+)*
1279                \s*(?P<global>global\snext-hop\sverify-availability\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+\s\d+\strack\s\d+)*
1280                \s*(?P<precedence>precedence\s\d+)*
1281                \s*(?P<vrf>vrf\s\S+\snext-hop\sverify-availability\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+\s\d+\strack\s\d+)*
1282                \s*(?P<next_hop>next-hop\s\S.*)*
1283                $""",
1284                re.VERBOSE,
1285            ),
1286            "setval": _tmplt_route_map_set_ipv6,
1287            "compval": "set",
1288            "result": {
1289                "{{ route_map }}": {
1290                    "{{ action|d() + '_' + sequence|d() }}": {
1291                        "entries": {
1292                            "set": {
1293                                "ipv6": {
1294                                    "address": "{{ address.split('address prefix-list ')[1] if address is defined }}",
1295                                    "default": "{{ default.split('default next-hop ')[1] if default is defined }}",
1296                                    "global_route": {
1297                                        "verify_availability": {
1298                                            "address": "{{ global.split(' ')[3] if global is defined and 'verify-availability' in global }}",
1299                                            "sequence": "{{ global.split(' ')[4] if global is defined and 'verify-availability' in global }}",
1300                                            "track": "{{ global.split('track ')[1] if global is defined and 'verify-availability' in global }}",
1301                                        },
1302                                        "address": "{{ global.split(' ')[2] if global is defined and 'verify-availability' not in global }}",
1303                                    },
1304                                    "next_hop": {
1305                                        "address": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and\
1306                                                 next_hop.split(' ')|length == 2 and 'peer-address' not in next_hop }}",
1307                                        "encapsulate": "{{ next_hop.split('next-hop encapsulate l3vpn ')[1] if next_hop is defined and\
1308                                                 'encapsulate' in next_hop}}",
1309                                        "peer_address": "{{ True if next_hop is defined and next_hop.split(' ')|length == 2 and 'peer-address' in next_hop }}",
1310                                        "recursive": "{{ next_hop.split('next-hop recursive ')[1] if next_hop is defined }}",
1311                                    },
1312                                    "precedence": "{{ precedence.split(' ')[1] if precedence is defined }}",
1313                                    "vrf": {
1314                                        "name": "{{ vrf.split(' ')[1] if vrf is defined }}",
1315                                        "verify_availability": {
1316                                            "address": "{{ vrf.split(' ')[4] if vrf is defined and 'verify-availability' in vrf }}",
1317                                            "sequence": "{{ vrf.split(' ')[5] if vrf is defined and 'verify-availability' in vrf }}",
1318                                            "track": "{{ vrf.split('track ')[1] if vrf is defined and 'verify-availability' in vrf }}",
1319                                        },
1320                                    },
1321                                }
1322                            }
1323                        }
1324                    }
1325                }
1326            },
1327        },
1328    ]
1329