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