Support only Fluorine+ distributions
[integration/test.git] / csit / libraries / Sxp.py
1 import json
2 from netaddr import IPAddress
3 from string import Template
4
5
6 def get_active_controller_from_json(resp, service):
7     """Gets index of active controller running specified service
8
9     :param resp: JSON formatted response from EOS
10     :type resp: str
11     :param service: EOS Service to look for
12     :type service: str
13     :return: Index of controller
14     """
15     entities = json.loads(resp)["entity-owners"]["entity-type"]
16     for entity in entities:
17         if entity["type"] == "org.opendaylight.mdsal.ServiceEntityType":
18             for instance in entity["entity"]:
19                 if service in instance["id"]:
20                     return int(instance["owner"][-1:])
21     return 0
22
23
24 def mod(num, base):
25     """Gets modulo of number
26
27     :param num: Number to be used
28     :type num: str
29     :param base: Base used
30     :type base: str
31     :returns: Int representing modulo of specified numbers.
32
33     """
34     return int(num) % int(base)
35
36
37 def get_average_of_items(items):
38     """Gets average of items in provided list
39
40     :param items: To be proceed
41     :return: Average value
42
43     """
44     return sum(items) / len(items)
45
46
47 def get_opposing_mode(mode):
48     """Generate string representing opposing SXP peer mode
49
50     :param mode: SXP peer mode
51     :type mode: str
52     :returns: String with opposing SXP peer mode.
53
54     """
55     if "speaker" == mode:
56         return "listener"
57     elif "listener" == mode:
58         return "speaker"
59     return "both"
60
61
62 def get_ip_from_number(n, base=2130706432):
63     """Generate string representing Ipv4 from specified number plus base value
64
65     :param n: Number to be converted
66     :type n: int
67     :param base: Starting index
68     :type base: int
69     :returns: String containing Ipv4.
70
71     """
72     ip = IPAddress(int(base) + n)
73     return str(ip)
74
75
76 def get_ip_from_number_and_ip(n, ip_address):
77     """Generate string representing Ipv4 from specified number and IPAddress
78
79     :param n: Number to be converted
80     :type n: int
81     :param ip_address: Base address
82     :type ip_address: str
83     :returns: String containing Ipv4.
84
85     """
86     ip = IPAddress(int(IPAddress(ip_address)) + n)
87     return str(ip)
88
89
90 def lower_version(ver1, ver2):
91     """Generate xml containing SGT mach data
92
93     :param ver1: Version of SXP protocol for compare
94     :type ver1: str
95     :param ver2: Version of SXP protocol for compare
96     :type ver2: str
97     :returns: String containing lower from those two specified versions.
98
99     """
100     v1 = int(ver1[-1:])
101     v2 = int(ver2[-1:])
102     if v1 <= v2:
103         return ver1
104     else:
105         return ver2
106
107
108 def get_filter_entry(
109     seq, entry_type, sgt="", esgt="", acl="", eacl="", pl="", epl="", ps=""
110 ):
111     """Generate xml containing FilterEntry data
112
113     :param seq: Sequence of entry
114     :type seq: str
115     :param entry_type: Type of entry (permit/deny)
116     :type entry_type: str
117     :param sgt: SGT matches to be added to entry
118     :type sgt: str
119     :param esgt: SGT ranges match to be added to entry
120     :type esgt: str
121     :param acl: ACL matches to be added to entry
122     :type acl: str
123     :param eacl: EACL matches to be added to entry
124     :type eacl: str
125     :param pl: PrefixList matches to be added to entry
126     :type pl: str
127     :param epl: ExtendedPrefixList matches to be added to entry
128     :type epl: str
129     :param ps: PeerSequence matches to be added to entry
130     :type ps: str
131     :returns: String containing xml data for request
132
133     """
134     entries = ""
135     # Generate XML request containing combination of Matches of different types
136     if sgt:
137         args = sgt.split(",")
138         entries += add_sgt_matches_xml(args)
139     elif esgt:
140         args = esgt.split(",")
141         entries += add_sgt_range_xml(args[0], args[1])
142     if pl:
143         entries += add_pl_entry_xml(pl)
144     elif epl:
145         args = epl.split(",")
146         entries += add_epl_entry_xml(args[0], args[1], args[2])
147     if acl:
148         args = acl.split(",")
149         entries += add_acl_entry_xml(args[0], args[1])
150     elif eacl:
151         args = eacl.split(",")
152         entries += add_eacl_entry_xml(args[0], args[1], args[2], args[3])
153     if ps:
154         args = ps.split(",")
155         entries += add_ps_entry_xml(args[0], args[1])
156     # Wrap entries in ACL/PrefixList according to specified values
157     if pl or epl:
158         return add_pl_entry_default_xml(seq, entry_type, entries)
159     elif ps:
160         return add_ps_entry_default_xml(seq, entry_type, entries)
161     return add_acl_entry_default_xml(seq, entry_type, entries)
162
163
164 def add_peers(*args):
165     """Generate xml containing Peer mach data
166
167     :param args: Peers data
168     :type args: dict
169     :returns: String containing xml data for request
170
171     """
172     templ = Template(
173         """
174         <sxp-peer>
175             <peer-address>$ip</peer-address>
176         </sxp-peer>"""
177     )
178     peers = ""
179     for count, value in enumerate(args):
180         peers += templ.substitute({"ip": value})
181     return peers
182
183
184 def add_domains(*args):
185     """Generate xml containing Domain mach data
186
187     :param args: Domain data
188     :type args: dict
189     :returns: String containing xml data for request
190
191     """
192     templ = Template(
193         """
194         <domain>
195             <name>$name</name>
196         </domain>"""
197     )
198     peers = ""
199     for count, value in enumerate(args):
200         peers += templ.substitute({"name": value})
201     return peers
202
203
204 def add_sgt_matches_xml(sgt_entries):
205     """Generate xml containing SGT mach data
206
207     :param sgt_entries: SGT matches
208     :type sgt_entries: str
209     :returns: String containing xml data for request
210
211     """
212     templ = Template(
213         """
214         <matches>$sgt</matches>"""
215     )
216     matches = ""
217     for sgt in sgt_entries:
218         matches += templ.substitute({"sgt": sgt})
219     return matches
220
221
222 def add_sgt_range_xml(start, end):
223     """Generate xml containing SGT RangeMach data
224
225     :param start: Start range of SGT
226     :type start: str
227     :param end: End range of SGT
228     :type end: str
229     :returns: String containing xml data for request
230
231     """
232     templ = Template(
233         """
234         <sgt-start>$start</sgt-start>
235         <sgt-end>$end</sgt-end>"""
236     )
237     match = templ.substitute({"start": start, "end": end})
238     return match
239
240
241 def add_acl_entry_default_xml(seq, entry_type, acl_entries):
242     """Generate xml containing AccessList data
243
244     :param seq: Sequence of PrefixList entry
245     :type seq: str
246     :param entry_type: Entry type (permit/deny)
247     :type entry_type: str
248     :param acl_entries: XML data containing AccessList entries
249     :type acl_entries: str
250     :returns: String containing xml data for request
251
252     """
253     templ = Template(
254         """
255         <acl-entry>
256             <entry-type>$entry_type</entry-type>
257             <entry-seq>$seq</entry-seq>$acl_entries
258         </acl-entry>"""
259     )
260     matches = templ.substitute(
261         {"seq": seq, "entry_type": entry_type, "acl_entries": acl_entries}
262     )
263     return matches
264
265
266 def add_acl_entry_xml(ip, mask):
267     """Generate xml containing AccessList data
268
269     :param ip: Ipv4/6 address
270     :type ip: str
271     :param mask: Ipv4/6 wildcard mask
272     :type mask: str
273     :returns: String containing xml data for request
274
275     """
276     templ = Template(
277         """
278         <acl-match>
279             <ip-address>$ip</ip-address>
280             <wildcard-mask>$mask</wildcard-mask>
281         </acl-match>"""
282     )
283     return templ.substitute({"ip": ip, "mask": mask})
284
285
286 def add_eacl_entry_xml(ip, mask, amask, wmask):
287     """Generate xml containing ExtendedAccessList data
288
289     :param ip: Ipv4/6 address
290     :type ip: str
291     :param mask: Ipv4/6 wildcard mask
292     :type mask: str
293     :param amask: Ipv4/6 address mask
294     :type amask: str
295     :param wmask: Ipv4/6 address wildcard mask
296     :type wmask: str
297     :returns: String containing xml data for request
298
299     """
300     templ = Template(
301         """
302         <acl-match>
303             <ip-address>$ip</ip-address>
304             <wildcard-mask>$mask</wildcard-mask>
305             <mask>
306               <address-mask>$amask</address-mask>
307               <wildcard-mask>$wmask</wildcard-mask>
308             </mask>
309         </acl-match>"""
310     )
311     return templ.substitute({"ip": ip, "mask": mask, "amask": amask, "wmask": wmask})
312
313
314 def add_ps_entry_default_xml(seq, entry_type, ps_entries):
315     """Generate xml containing PeerSequence data
316
317     :param seq: Sequence of PrefixList entry
318     :type seq: str
319     :param entry_type: Entry type (permit/deny)
320     :type entry_type: str
321     :param ps_entries: XML data containing PeerSequence entries
322     :type ps_entries: str
323     :returns: String containing xml data for request
324
325     """
326     templ = Template(
327         """
328     <peer-sequence-entry xmlns="urn:opendaylight:sxp:controller">
329           <entry-type>$entry_type</entry-type>
330           <entry-seq>$seq</entry-seq>$ps_entries
331     </peer-sequence-entry>"""
332     )
333     return templ.substitute(
334         {"seq": seq, "entry_type": entry_type, "ps_entries": ps_entries}
335     )
336
337
338 def add_pl_entry_default_xml(seq, entry_type, pl_entries):
339     """Generate xml containing PrefixList data
340
341     :param seq: Sequence of PrefixList entry
342     :type seq: str
343     :param entry_type: Entry type (permit/deny)
344     :type entry_type: str
345     :param pl_entries: XML data containing PrefixList entries
346     :type pl_entries: str
347     :returns: String containing xml data for request
348
349     """
350     templ = Template(
351         """
352     <prefix-list-entry xmlns="urn:opendaylight:sxp:controller">
353           <entry-type>$entry_type</entry-type>
354           <entry-seq>$seq</entry-seq>$pl_entries
355     </prefix-list-entry>"""
356     )
357     return templ.substitute(
358         {"seq": seq, "entry_type": entry_type, "pl_entries": pl_entries}
359     )
360
361
362 def add_pl_entry_xml(prefix):
363     """Generate xml containing PrefixList data
364
365     :param prefix: Ipv4/6 prefix
366     :type prefix: str
367     :returns: String containing xml data for request
368
369     """
370     templ = Template(
371         """
372         <prefix-list-match>
373             <ip-prefix>$prefix</ip-prefix>
374         </prefix-list-match>"""
375     )
376     return templ.substitute({"prefix": prefix})
377
378
379 def add_epl_entry_xml(prefix, op, mask):
380     """Generate xml containing Extended PrefixList data
381
382     :param prefix: Ipv4/6 prefix
383     :type prefix: str
384     :param op: PrefixList option (ge/le/eq)
385     :type op: str
386     :param mask: Ipv4/6 Mask
387     :type mask: str
388     :returns: String containing xml data for request
389
390     """
391     templ = Template(
392         """
393         <prefix-list-match>
394             <ip-prefix>$prefix</ip-prefix>
395             <mask>
396                 <mask-range>$op</mask-range>
397                 <mask-value>$mask</mask-value>
398             </mask>
399         </prefix-list-match>"""
400     )
401     return templ.substitute({"prefix": prefix, "mask": mask, "op": op})
402
403
404 def add_ps_entry_xml(op, length):
405     """Generate xml containing Extended PrefixList data
406
407     :param op: PrefixList option (ge/le/eq)
408     :type op: str
409     :param length: PeerSequence length
410     :type length: str
411     :returns: String containing xml data for request
412
413     """
414     templ = Template(
415         """
416         <peer-sequence-length>$length</peer-sequence-length>
417         <peer-sequence-range>$op</peer-sequence-range>
418         """
419     )
420     return templ.substitute({"length": length, "op": op})
421
422
423 def parse_peer_groups(groups_json):
424     """Parse JSON string into Array of PeerGroups
425
426     :param groups_json: JSON containing PeerGroups
427     :type groups_json: str
428     :returns: Array containing PeerGroups.
429
430     """
431     data = json.loads(groups_json)
432     groups = data["output"]
433     output = []
434     for group in groups.values():
435         output += group
436     return output
437
438
439 def parse_connections(connections_json):
440     """Parse JSON string into Array of Connections
441
442     :param connections_json: JSON containing Connections
443     :type connections_json: str
444     :returns: Array containing Connections.
445
446     """
447     data = json.loads(connections_json)
448     output = data["output"]
449     result = []
450     if output:
451         connections = output["connections"]
452         for connection in connections.values():
453             result += connection
454     return result
455
456
457 def find_connection(connections_json, version, mode, ip, port, state):
458     """Test if Connection with specified values is contained in JSON
459
460     :param connections_json: JSON containing Connections
461     :type connections_json: str
462     :param version: Version of SXP protocol (version1/2/3/4)
463     :type version: str
464     :param mode: Mode of SXP peer (speaker/listener/both)
465     :type mode: str
466     :param ip: Ipv4/6 address of remote peer
467     :type ip: str
468     :param port: Port on with remote peer listens
469     :type port: str
470     :param state: State of connection (on/off/pendingOn/deleteHoldDown)
471     :type state: str
472     :returns: True if Connection with specified params was found, otherwise False.
473
474     """
475     for connection in parse_connections(connections_json):
476         if (
477             connection["peer-address"] == ip
478             and connection["tcp-port"] == int(port)
479             and (mode.strip() == "any" or connection["mode"] == mode)
480             and connection["version"] == version
481         ):
482             if state == "none":
483                 return True
484             elif connection["state"] == state:
485                 return True
486     return False
487
488
489 def parse_bindings(bindings_json):
490     """Parse JSON string into Array of Bindings
491
492     :param bindings_json: JSON containing Bindings
493     :type bindings_json: str
494     :returns: Array containing Bindings.
495
496     """
497     data = json.loads(bindings_json)
498     output = []
499     for bindings_json in data["output"].values():
500         for binding in bindings_json:
501             output.append(binding)
502     return output
503
504
505 def find_binding(bindings, sgt, prefix):
506     """Test if Binding with specified values is contained in JSON
507
508     :param bindings: JSON containing Bindings
509     :type bindings: str
510     :param sgt: Source Group Tag
511     :type sgt: str
512     :param prefix: Ipv4/6 prefix
513     :type prefix: str
514     :returns: True if Binding with specified params was found, otherwise False.
515
516     """
517     for binding in parse_bindings(bindings):
518         if binding["sgt"] == int(sgt):
519             for ip_prefix in binding["ip-prefix"]:
520                 if ip_prefix == prefix:
521                     return True
522     return False
523
524
525 def parse_prefix_groups(prefix_groups_json, source_):
526     """Parse JSON string into Array of PrefixGroups
527
528     :param prefix_groups_json: JSON containing PrefixGroups
529     :type prefix_groups_json: str
530     :param source_: Source of PrefixGroups (sxp/local)
531     :type source_: str
532     :returns: Array containing PrefixGroups.
533
534     """
535     data = json.loads(prefix_groups_json)
536     bindings = data["sxp-node:master-database"]
537     output = []
538     for binding in bindings.values():
539         for binding_source in binding:
540             if source_ == "any" or binding_source["binding-source"] == source_:
541                 for prefix_group in binding_source["prefix-group"]:
542                     output.append(prefix_group)
543     return output
544
545
546 def find_binding_legacy(prefix_groups_json, sgt, prefix, source_, action):
547     """Test if Binding with specified values is contained in JSON
548
549     :param prefix_groups_json: JSON containing Bindings and PrefixGroups
550     :type prefix_groups_json: str
551     :param sgt: Source Group Tag
552     :type sgt: str
553     :param prefix: Ipv4/6 prefix
554     :type prefix: str
555     :param source_: Source of binding (local/sxp)
556     :type source_: str
557     :param action: Action for binding (add/delete)
558     :type action: str
559     :returns: True if Binding with specified params was found, otherwise False.
560
561     """
562     found = False
563     for prefixgroup in parse_prefix_groups(prefix_groups_json, source_):
564         if prefixgroup["sgt"] == int(sgt):
565             for binding in prefixgroup["binding"]:
566                 if binding["ip-prefix"] == prefix and binding["action"] == action:
567                     found = True
568     return found
569
570
571 def add_connection_xml(
572     version,
573     mode,
574     ip,
575     port,
576     node,
577     password_,
578     domain_name,
579     bindings_timeout=0,
580     security_mode="",
581 ):
582     """Generate xml for Add Connection request
583
584     :param version: Version of SXP protocol (version1/2/3/4)
585     :type version: str
586     :param mode: Mode of SXP peer (speaker/listener/both)
587     :type mode: str
588     :param ip: Ipv4/6 address of remote peer
589     :type ip: str
590     :param port: Port on with remote peer listens
591     :type port: str
592     :param node: Ipv4 address of node
593     :type node: str
594     :param password_: Password type (none/default)
595     :type password_: str
596     :param domain_name: Name of Domain
597     :type domain_name: str
598     :param security_mode: Default/TSL security
599     :type security_mode: str
600     :param bindings_timeout: Specifies DHD and Reconciliation timers
601     :type bindings_timeout: int
602     :returns: String containing xml data for request
603
604     """
605     templ = Template(
606         """<input>
607    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
608    $domain
609    <connections xmlns="urn:opendaylight:sxp:controller">
610       <connection>
611          <peer-address>$ip</peer-address>
612          <tcp-port>$port</tcp-port>
613          <password>$password_</password>
614          <mode>$mode</mode>
615          <version>$version</version>
616          <description>Connection to ISR-G2</description>
617          $security_type
618          <connection-timers>
619             <hold-time-min-acceptable>45</hold-time-min-acceptable>
620             <keep-alive-time>30</keep-alive-time>
621             <reconciliation-time>$timeout</reconciliation-time>
622             <delete-hold-down-time>$timeout</delete-hold-down-time>
623             <hold-time>90</hold-time>
624             <hold-time-max>180</hold-time-max>
625             <hold-time-min>90</hold-time-min>
626          </connection-timers>
627       </connection>
628    </connections>
629 </input>
630 """
631     )
632     data = templ.substitute(
633         {
634             "ip": ip,
635             "port": port,
636             "mode": mode,
637             "version": version,
638             "node": node,
639             "password_": password_,
640             "domain": get_domain_name(domain_name),
641             "timeout": bindings_timeout,
642             "security_type": "<security-type>" + security_mode + "</security-type>"
643             if security_mode
644             else "",
645         }
646     )
647     return data
648
649
650 def delete_connections_xml(address, port, node, domain_name):
651     """Generate xml for Delete Connection request
652
653     :param address: Ipv4/6 address of remote peer
654     :type address: str
655     :param port: Port on with remote peer listens
656     :type port: str
657     :param node: Ipv4 address of node
658     :type node: str
659     :param domain_name: Name of Domain
660     :type domain_name: str
661     :returns: String containing xml data for request
662
663     """
664     templ = Template(
665         """<input>
666    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
667    $domain
668    <peer-address xmlns="urn:opendaylight:sxp:controller">$address</peer-address>
669    <tcp-port xmlns="urn:opendaylight:sxp:controller">$port</tcp-port>
670 </input>"""
671     )
672     data = templ.substitute(
673         {
674             "address": address,
675             "port": port,
676             "node": node,
677             "domain": get_domain_name(domain_name),
678         }
679     )
680     return data
681
682
683 def add_peer_group_xml(name, peers, ip):
684     """Generate xml for Add PeerGroups request
685
686     :param name: Name of PeerGroup
687     :type name: str
688     :param peers: XML formatted peers that will be added to group
689     :type peers: str
690     :param ip: Ipv4 address of node
691     :type ip: str
692     :returns: String containing xml data for request
693
694     """
695     templ = Template(
696         """<input>
697   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
698   <sxp-peer-group xmlns="urn:opendaylight:sxp:controller">
699     <name xmlns="urn:opendaylight:sxp:controller">$name</name>
700     <sxp-peers xmlns="urn:opendaylight:sxp:controller">$peers</sxp-peers>
701     </sxp-peer-group>
702 </input>"""
703     )
704     data = templ.substitute({"name": name, "peers": peers, "ip": ip})
705     return data
706
707
708 def delete_peer_group_xml(name, ip):
709     """Generate xml for Delete PeerGroup request
710
711     :param name: Name of PeerGroup
712     :type name: str
713     :param ip: Ipv4 address of node
714     :type ip: str
715     :returns: String containing xml data for request
716
717     """
718     templ = Template(
719         """<input>
720   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
721   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$name</peer-group-name>
722 </input>"""
723     )
724     data = templ.substitute({"name": name, "ip": ip})
725     return data
726
727
728 def get_peer_groups_from_node_xml(ip):
729     """Generate xml for Get PeerGroups request
730
731     :param ip: Ipv4 address of node
732     :type ip: str
733     :returns: String containing xml data for request
734
735     """
736     templ = Template(
737         """<input>
738    <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
739 </input>"""
740     )
741     data = templ.substitute({"ip": ip})
742     return data
743
744
745 def add_filter_xml(group, filter_type, entries, ip, policy=None):
746     """Generate xml for Add Filter request
747
748     :param group: Name of group containing filter
749     :type group: str
750     :param filter_type: Type of filter
751     :type filter_type: str
752     :param entries: XML formatted entries that will be added in filter
753     :type entries: str
754     :param ip: Ipv4 address of node
755     :type ip: str
756     :param policy: Policy of filter update mechanism
757     :type policy: str
758     :returns: String containing xml data for request
759
760     """
761     if policy:
762         policy = "<filter-policy>" + policy + "</filter-policy>"
763     else:
764         policy = ""
765     templ = Template(
766         """<input>
767   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
768   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
769   <sxp-filter xmlns="urn:opendaylight:sxp:controller">
770     $filter_policy
771     <filter-type>$filter_type</filter-type>$entries
772   </sxp-filter>
773 </input>"""
774     )
775     data = templ.substitute(
776         {
777             "group": group,
778             "filter_type": filter_type,
779             "ip": ip,
780             "entries": entries,
781             "filter_policy": policy,
782         }
783     )
784     return data
785
786
787 def add_domain_filter_xml(domain, domains, entries, ip, filter_name=None):
788     """Generate xml for Add Domain Filter request
789
790     :param domain: Name of Domain containing filter
791     :type domain: str
792     :param domains: Domains on which filter will be applied
793     :type domains: str
794     :param entries: XML formatted entries that will be added in filter
795     :type entries: str
796     :param ip: Ipv4 address of node
797     :type ip: str
798     :param filter_name: Name of filter
799     :type filter_name: str
800     :returns: String containing xml data for request
801
802     """
803     if filter_name:
804         filter_name = "<filter-name>" + filter_name + "</filter-name>"
805     templ = Template(
806         """<input>
807   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
808   <domain-name xmlns="urn:opendaylight:sxp:controller">$domain</domain-name>
809   <sxp-domain-filter xmlns="urn:opendaylight:sxp:controller">
810     $filter_name
811     <domains>$domains</domains>
812     $entries
813   </sxp-domain-filter>
814 </input>"""
815     )
816     data = templ.substitute(
817         {
818             "domain": domain,
819             "domains": domains,
820             "ip": ip,
821             "entries": entries,
822             "filter_name": filter_name,
823         }
824     )
825     return data
826
827
828 def delete_filter_xml(group, filter_type, ip):
829     """Generate xml for Delete Filter request
830
831     :param group: Name of group containing filter
832     :type group: str
833     :param filter_type: Type of filter
834     :type filter_type: str
835     :param ip: Ipv4 address of node
836     :type ip: str
837     :returns: String containing xml data for request
838
839     """
840     templ = Template(
841         """<input>
842   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
843   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
844   <filter-type xmlns="urn:opendaylight:sxp:controller">$filter_type</filter-type>
845 </input>"""
846     )
847     data = templ.substitute({"group": group, "filter_type": filter_type, "ip": ip})
848     return data
849
850
851 def delete_domain_filter_xml(domain, ip, filter_name=None):
852     """Generate xml for Delete Filter request
853
854     :param domain: Name of Domain containing filter
855     :type domain: str
856     :param ip: Ipv4 address of node
857     :type ip: str
858     :param filter_name: Name of filter
859     :type filter_name: str
860     :returns: String containing xml data for request
861
862     """
863     if filter_name:
864         filter_name = (
865             '<filter-name xmlns="urn:opendaylight:sxp:controller">'
866             + filter_name
867             + "</filter-name>"
868         )
869     templ = Template(
870         """<input>
871   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
872   <domain-name xmlns="urn:opendaylight:sxp:controller">$domain</domain-name>
873   $filter_name
874 </input>"""
875     )
876     data = templ.substitute({"domain": domain, "ip": ip, "filter_name": filter_name})
877     return data
878
879
880 def get_connections_from_node_xml(ip, domain_name):
881     """Generate xml for Get Connections request
882
883     :param ip: Ipv4 address of node
884     :type ip: str
885     :param domain_name: Name of Domain
886     :type domain_name: str
887     :returns: String containing xml data for request
888
889     """
890     templ = Template(
891         """<input>
892    <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
893    $domain
894 </input>"""
895     )
896     data = templ.substitute({"ip": ip, "domain": get_domain_name(domain_name)})
897     return data
898
899
900 def get_bindings_from_node_xml(ip, binding_range, domain_name):
901     """Generate xml for Get Bindings request
902
903     :param binding_range: All or only Local bindings
904     :type binding_range: str
905     :param ip: Ipv4 address of node
906     :type ip: str
907     :param domain_name: Name of Domain
908     :type domain_name: str
909     :returns: String containing xml data for request
910
911     """
912     templ = Template(
913         """<input>
914   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
915   <bindings-range xmlns="urn:opendaylight:sxp:controller">$range</bindings-range>
916   $domain
917 </input>"""
918     )
919     data = templ.substitute(
920         {"ip": ip, "range": binding_range, "domain": get_domain_name(domain_name)}
921     )
922     return data
923
924
925 def add_node_xml(
926     node_id,
927     port,
928     password,
929     version,
930     node_ip=None,
931     expansion=0,
932     bindings_timeout=0,
933     keystores=None,
934     retry_open_timer=1,
935 ):
936     """Generate xml for Add Node request
937
938     :param node_id: Ipv4 address formatted node id
939     :type node_id: str
940     :param node_ip: Ipv4 address of node
941     :type node_ip: strl
942     :param port: Node port number
943     :type port: int
944     :param password: TCP-MD5 password
945     :type password: str
946     :param version: Sxp device version
947     :type version: str
948     :param expansion: Bindings expansion
949     :type expansion: int
950     :param bindings_timeout: Specifies DHD and Reconciliation timers
951     :type bindings_timeout: int
952     :param keystores: SSL keystore and truststore specification
953     :type keystores: dict
954     :returns: String containing xml data for request
955
956     """
957     tls = ""
958     if keystores:
959         tls = Template(
960             """
961         <tls>
962             <keystore>
963               <location>$keystore</location>
964               <type>JKS</type>
965               <path-type>PATH</path-type>
966               <password>$passwd</password>
967             </keystore>
968             <truststore>
969               <location>$truststore</location>
970               <type>JKS</type>
971               <path-type>PATH</path-type>
972               <password>$passwd</password>
973             </truststore>
974             <certificate-password>$passwd</certificate-password>
975         </tls>
976     """
977         ).substitute(
978             {
979                 "keystore": keystores["keystore"],
980                 "truststore": keystores["truststore"],
981                 "passwd": keystores["password"],
982             }
983         )
984
985     templ = Template(
986         """<input xmlns="urn:opendaylight:sxp:controller">
987     <node-id>$id</node-id>
988     <timers>
989         <retry-open-time>$retry_open_timer</retry-open-time>
990         <hold-time-min-acceptable>120</hold-time-min-acceptable>
991         <delete-hold-down-time>$timeout</delete-hold-down-time>
992         <hold-time-min>90</hold-time-min>
993         <reconciliation-time>$timeout</reconciliation-time>
994         <hold-time>90</hold-time>
995         <hold-time-max>180</hold-time-max>
996         <keep-alive-time>30</keep-alive-time>
997     </timers>
998     <mapping-expanded>$expansion</mapping-expanded>
999     <security>
1000         $tls
1001         <password>$password</password>
1002     </security>
1003     <tcp-port>$port</tcp-port>
1004     <version>$version</version>
1005     <description>ODL SXP Controller</description>
1006     <source-ip>$ip</source-ip>
1007 </input>"""
1008     )
1009     data = templ.substitute(
1010         {
1011             "ip": node_ip or node_id,
1012             "id": node_id,
1013             "port": port,
1014             "password": password,
1015             "version": version,
1016             "expansion": expansion,
1017             "timeout": bindings_timeout,
1018             "tls": tls,
1019             "retry_open_timer": retry_open_timer,
1020         }
1021     )
1022     return data
1023
1024
1025 def delete_node_xml(node_id):
1026     """Generate xml for Delete node request
1027
1028     :param node_id: Ipv4 address formatted node id
1029     :type node_id: str
1030     :returns: String containing xml data for request
1031
1032     """
1033     templ = Template(
1034         """<input xmlns="urn:opendaylight:sxp:controller">
1035   <node-id>$id</node-id>
1036 </input>"""
1037     )
1038     data = templ.substitute({"id": node_id})
1039     return data
1040
1041
1042 def add_domain_xml_fluorine(node_id, name, sgt, prefixes, origin):
1043     """Generate xml for Add Domain request (Fluorine version: bindings with origin)
1044
1045     :param node_id: Id of node
1046     :type node_id: str
1047     :param name: Name of Domain
1048     :type name: str
1049     :param sgt: Security group
1050     :type sgt: int
1051     :param prefixes: List of ip-prefixes
1052     :type prefixes: str
1053     :param origin: Origin of added bindings
1054     :type origin: str
1055     :returns: String containing xml data for request
1056
1057     """
1058     master_database = ""
1059     if prefixes != "None":
1060         xml_prefixes = ""
1061         for prefix in prefixes.split(","):
1062             xml_prefixes += "\n" + "<ip-prefix>" + prefix + "</ip-prefix>"
1063         if xml_prefixes:
1064             master_database += """<master-database>
1065             <binding>
1066                 <sgt>$sgt</sgt>
1067                 $xml_prefixes
1068             </binding>
1069         </master-database>"""
1070             master_database = Template(master_database).substitute(
1071                 ({"sgt": sgt, "xml_prefixes": xml_prefixes})
1072             )
1073
1074     templ = Template(
1075         """<input xmlns="urn:opendaylight:sxp:controller">
1076     <node-id>$id</node-id>
1077     <domain-name>$name</domain-name>
1078     <origin>$origin</origin>
1079     $master_database
1080 </input>"""
1081     )
1082
1083     data = templ.substitute(
1084         {
1085             "name": name,
1086             "id": node_id,
1087             "origin": origin,
1088             "master_database": master_database,
1089         }
1090     )
1091     return data
1092
1093
1094 def delete_domain_xml(node_id, name):
1095     """Generate xml for Remove Domain request
1096
1097     :param node_id: Id of node
1098     :type node_id: str
1099     :param name: Name of Domain
1100     :type name: str
1101     :returns: String containing xml data for request
1102
1103     """
1104     templ = Template(
1105         """<input xmlns="urn:opendaylight:sxp:controller">
1106     <node-id>$node_id</node-id>
1107     <domain-name>$name</domain-name>
1108 </input>"""
1109     )
1110
1111     data = templ.substitute({"node_id": node_id, "name": name})
1112     return data
1113
1114
1115 def get_domain_name(domain_name):
1116     """Generate xml for Get Bindings request
1117
1118     :param domain_name: Name of Domain
1119     :type domain_name: str
1120     :returns: String containing xml data for request
1121
1122     """
1123     if domain_name == "global":
1124         return ""
1125     else:
1126         return (
1127             '<domain-name xmlns="urn:opendaylight:sxp:controller">'
1128             + domain_name
1129             + "</domain-name>"
1130         )
1131
1132
1133 def add_bindings_xml_fluorine(node_id, domain, sgt, prefixes, origin):
1134     """Generate xml for Add Bindings request (Fluorine version with origin type)
1135
1136     :param node_id: Id of node
1137     :type node_id: str
1138     :param domain: Name of Domain
1139     :type domain: str
1140     :param sgt: Security group
1141     :type sgt: int
1142     :param prefixes: List of ip-prefixes
1143     :type prefixes: str
1144     :param origin: Origin of added bindings
1145     :type origin: str
1146     :returns: String containing xml data for request
1147
1148     """
1149     xml_prefixes = ""
1150     for prefix in prefixes.split(","):
1151         xml_prefixes += "\n" + "<ip-prefix>" + prefix + "</ip-prefix>"
1152     templ = Template(
1153         """<input xmlns="urn:opendaylight:sxp:controller">
1154     <node-id>$id</node-id>
1155     <domain-name>$name</domain-name>
1156     <origin>$origin</origin>
1157     <master-database>
1158         <binding>
1159             <sgt>$sgt</sgt>
1160             $xml_prefixes
1161         </binding>
1162     </master-database>
1163 </input>"""
1164     )
1165     data = templ.substitute(
1166         {
1167             "name": domain,
1168             "id": node_id,
1169             "sgt": sgt,
1170             "xml_prefixes": xml_prefixes,
1171             "origin": origin,
1172         }
1173     )
1174     return data
1175
1176
1177 def delete_bindings_xml(node_id, domain, sgt, prefixes):
1178     """Generate xml for Remove Bindings request
1179
1180     :param node_id: Id of node
1181     :type node_id: str
1182     :param domain: Name of Domain
1183     :type domain: str
1184     :param sgt: Security group
1185     :type sgt: int
1186     :param prefixes: Comma separated list of ip-prefixes
1187     :type prefixes: str
1188     :returns: String containing xml data for request
1189
1190     """
1191     xml_prefixes = ""
1192     for prefix in prefixes.split(","):
1193         xml_prefixes += "\n" + "<ip-prefix>" + prefix + "</ip-prefix>"
1194     templ = Template(
1195         """<input xmlns="urn:opendaylight:sxp:controller">
1196     <node-id>$id</node-id>
1197     <domain-name>$name</domain-name>
1198     <binding>
1199         <sgt>$sgt</sgt>
1200         $xml_prefixes
1201     </binding>
1202 </input>"""
1203     )
1204     data = templ.substitute(
1205         {"name": domain, "id": node_id, "sgt": sgt, "xml_prefixes": xml_prefixes}
1206     )
1207     return data
1208
1209
1210 def prefix_range(start, end):
1211     """Generate and concatenate ip-prefixes
1212
1213     :param start: Start index
1214     :type start: str
1215     :param end: End index
1216     :type end: str
1217     :returns: String containing concatenated ip-prefixes
1218
1219     """
1220     start = int(start)
1221     end = int(end)
1222     index = 0
1223     prefixes = ""
1224     while index < end:
1225         prefixes += get_ip_from_number(index + start) + "/32"
1226         index += 1
1227         if index < end:
1228             prefixes += ","
1229     return prefixes
1230
1231
1232 def route_definition_xml(virtual_ip, net_mask, interface):
1233     """Generate xml for Add Bindings request
1234
1235     :param interface: Network interface name
1236     :type interface: str
1237     :param net_mask: NetMask of virtual ip
1238     :type net_mask: str
1239     :param virtual_ip: Virtual ip
1240     :type virtual_ip: str
1241     :returns: String containing xml data for request
1242
1243     """
1244     templ = Template(
1245         """
1246     <routing-definition>
1247         <ip-address>$vip</ip-address>
1248         <interface>$interface</interface>
1249         <netmask>$mask</netmask>
1250     </routing-definition>
1251     """
1252     )
1253     data = templ.substitute(
1254         {"mask": net_mask, "vip": virtual_ip, "interface": interface}
1255     )
1256     return data
1257
1258
1259 def route_definitions_xml(routes, old_routes=None):
1260     """Generate xml for Add Bindings request
1261
1262     :param routes: XML formatted data containing RouteDefinitions
1263     :type routes: str
1264     :param old_routes: Routes add to request that needs to persist
1265     :type old_routes: str
1266     :returns: String containing xml data for request
1267
1268     """
1269     if old_routes and "</sxp-cluster-route>" in old_routes:
1270         templ = Template(
1271             old_routes.replace("</sxp-cluster-route>", "$routes</sxp-cluster-route>")
1272         )
1273     else:
1274         templ = Template(
1275             """<sxp-cluster-route xmlns="urn:opendaylight:sxp:cluster:route">
1276     $routes
1277 </sxp-cluster-route>
1278     """
1279         )
1280     data = templ.substitute({"routes": routes})
1281     return data
1282
1283
1284 def add_binding_origin_xml(origin, priority):
1285     """Generate xml for Add Binding Origin request
1286
1287     :param origin: Origin type
1288     :type origin: str
1289     :param priority: Origin priority
1290     :type priority: str
1291     :returns: String containing xml data for request
1292
1293     """
1294     templ = Template(
1295         """<input xmlns="urn:opendaylight:sxp:config:controller">
1296     <origin>$origin</origin>
1297     <priority>$priority</priority>
1298 </input>"""
1299     )
1300     data = templ.substitute({"origin": origin, "priority": priority})
1301     return data
1302
1303
1304 def update_binding_origin_xml(origin, priority):
1305     """Generate xml for Update Binding Origin request
1306
1307     :param origin: Origin type
1308     :type origin: str
1309     :param priority: Origin priority
1310     :type priority: str
1311     :returns: String containing xml data for request
1312
1313     """
1314     templ = Template(
1315         """<input xmlns="urn:opendaylight:sxp:config:controller">
1316     <origin>$origin</origin>
1317     <priority>$priority</priority>
1318 </input>"""
1319     )
1320     data = templ.substitute({"origin": origin, "priority": priority})
1321     return data
1322
1323
1324 def delete_binding_origin_xml(origin):
1325     """Generate xml for Delete Binding Origin request
1326
1327     :param origin: Origin type
1328     :type origin: str
1329     :returns: String containing xml data for request
1330
1331     """
1332     templ = Template(
1333         """<input xmlns="urn:opendaylight:sxp:config:controller">
1334     <origin>$origin</origin>
1335 </input>"""
1336     )
1337     data = templ.substitute({"origin": origin})
1338     return data
1339
1340
1341 def find_binding_origin(origins_json, origin):
1342     """Test if Binding origin of specified value is contained in JSON
1343
1344     :param origins_json: JSON containing Binding origins
1345     :type origins_json: str
1346     :param origin: Origin to be found
1347     :type origin: str
1348     :returns: True if Binding origin of specified origin type was found, otherwise False.
1349
1350     """
1351     for json_origin in parse_binding_origins(origins_json):
1352         if json_origin["origin"] == origin:
1353             return True
1354     return False
1355
1356
1357 def find_binding_origin_with_priority(origins_json, origin, priority):
1358     """Test if Binding origin of specified value and priority is contained in JSON
1359
1360     :param origins_json: JSON containing Binding origins
1361     :type origins_json: str
1362     :param origin: Origin to be found
1363     :type origin: str
1364     :param priority: desired priority of origin
1365     :type priority: str
1366     :returns: True if Binding origin of specified origin type with desired priority was found, otherwise False.
1367
1368     """
1369     for json_origin in parse_binding_origins(origins_json):
1370         if json_origin["origin"] == origin:
1371             if json_origin["priority"] == int(priority):
1372                 return True
1373     return False
1374
1375
1376 def parse_binding_origins(origins_json):
1377     """Parse JSON string into Array of Binding origins
1378
1379     :param origins_json: JSON containing Binding origins
1380     :type origins_json: str
1381     :returns: Array containing Binding origins.
1382
1383     """
1384     output = []
1385     for origins in origins_json["binding-origins"].values():
1386         for origin in origins:
1387             output.append(origin)
1388     return output