Add SXP SSL functionality tests
[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     connections = data['output']['connections']
418     output = []
419     for connection in connections.values():
420         output += connection
421     return output
422
423
424 def find_connection(connections_json, version, mode, ip, port, state):
425     """Test if Connection with specified values is contained in JSON
426
427     :param connections_json: JSON containing Connections
428     :type connections_json: str
429     :param version: Version of SXP protocol (version1/2/3/4)
430     :type version: str
431     :param mode: Mode of SXP peer (speaker/listener/both)
432     :type mode: str
433     :param ip: Ipv4/6 address of remote peer
434     :type ip: str
435     :param port: Port on with remote peer listens
436     :type port: str
437     :param state: State of connection (on/off/pendingOn/deleteHoldDown)
438     :type state: str
439     :returns: True if Connection with specified params was found, otherwise False.
440
441     """
442     for connection in parse_connections(connections_json):
443         if (connection['peer-address'] == ip and connection['tcp-port'] == int(port) and (
444                         mode.strip() == 'any' or connection['mode'] == mode) and connection['version'] == version):
445             if state == 'none':
446                 return True
447             elif connection['state'] == state:
448                 return True
449     return False
450
451
452 def parse_bindings(bindings_json):
453     """Parse JSON string into Array of Bindings
454
455     :param bindings_json: JSON containing Bindings
456     :type bindings_json: str
457     :returns: Array containing Bindings.
458
459     """
460     data = json.loads(bindings_json)
461     output = []
462     for bindings_json in data['output'].values():
463         for binding in bindings_json:
464             output.append(binding)
465     return output
466
467
468 def find_binding(bindings, sgt, prefix):
469     """Test if Binding with specified values is contained in JSON
470
471     :param bindings: JSON containing Bindings
472     :type bindings: str
473     :param sgt: Source Group Tag
474     :type sgt: str
475     :param prefix: Ipv4/6 prefix
476     :type prefix: str
477     :returns: True if Binding with specified params was found, otherwise False.
478
479     """
480     for binding in parse_bindings(bindings):
481         if binding['sgt'] == int(sgt):
482             for ip_prefix in binding['ip-prefix']:
483                 if ip_prefix == prefix:
484                     return True
485     return False
486
487
488 def parse_prefix_groups(prefix_groups_json, source_):
489     """Parse JSON string into Array of PrefixGroups
490
491     :param prefix_groups_json: JSON containing PrefixGroups
492     :type prefix_groups_json: str
493     :param source_: Source of PrefixGroups (sxp/local)
494     :type source_: str
495     :returns: Array containing PrefixGroups.
496
497     """
498     data = json.loads(prefix_groups_json)
499     bindings = data['sxp-node:master-database']
500     output = []
501     for binding in bindings.values():
502         for binding_source in binding:
503             if source_ == "any" or binding_source['binding-source'] == source_:
504                 for prefix_group in binding_source['prefix-group']:
505                     output.append(prefix_group)
506     return output
507
508
509 def find_binding_legacy(prefix_groups_json, sgt, prefix, source_, action):
510     """Test if Binding with specified values is contained in JSON
511
512     :param prefix_groups_json: JSON containing Bindings and PrefixGroups
513     :type prefix_groups_json: str
514     :param sgt: Source Group Tag
515     :type sgt: str
516     :param prefix: Ipv4/6 prefix
517     :type prefix: str
518     :param source_: Source of binding (local/sxp)
519     :type source_: str
520     :param action: Action for binding (add/delete)
521     :type action: str
522     :returns: True if Binding with specified params was found, otherwise False.
523
524     """
525     found = False
526     for prefixgroup in parse_prefix_groups(prefix_groups_json, source_):
527         if prefixgroup['sgt'] == int(sgt):
528             for binding in prefixgroup['binding']:
529                 if binding['ip-prefix'] == prefix and binding['action'] == action:
530                     found = True
531     return found
532
533
534 def add_entry_xml(sgt, prefix, ip, domain_name):
535     """Generate xml for Add Bindings request
536
537     :param sgt: Source Group Tag
538     :type sgt: str
539     :param prefix: Ipv4/6 prefix
540     :type prefix: str
541     :param ip: Ipv4 address of node
542     :type ip: str
543     :param domain_name: Name of Domain
544     :type domain_name: str
545     :returns: String containing xml data for request
546
547     """
548     templ = Template('''<input>
549   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
550   $domain
551   <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
552   <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
553 </input>''')
554     data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip, 'domain': get_domain_name(domain_name)})
555     return data
556
557
558 def add_connection_xml(version, mode, ip, port, node, password_, domain_name, bindings_timeout=0, security_mode=''):
559     """Generate xml for Add Connection request
560
561     :param version: Version of SXP protocol (version1/2/3/4)
562     :type version: str
563     :param mode: Mode of SXP peer (speaker/listener/both)
564     :type mode: str
565     :param ip: Ipv4/6 address of remote peer
566     :type ip: str
567     :param port: Port on with remote peer listens
568     :type port: str
569     :param node: Ipv4 address of node
570     :type node: str
571     :param password_: Password type (none/default)
572     :type password_: str
573     :param domain_name: Name of Domain
574     :type domain_name: str
575     :param security_mode: Default/TSL security
576     :type security_mode: str
577     :param bindings_timeout: Specifies DHD and Reconciliation timers
578     :type bindings_timeout: int
579     :returns: String containing xml data for request
580
581     """
582     templ = Template('''<input>
583    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
584    $domain
585    <connections xmlns="urn:opendaylight:sxp:controller">
586       <connection>
587          <peer-address>$ip</peer-address>
588          <tcp-port>$port</tcp-port>
589          <password>$password_</password>
590          <mode>$mode</mode>
591          <version>$version</version>
592          <description>Connection to ISR-G2</description>
593          $security_type
594          <connection-timers>
595             <hold-time-min-acceptable>45</hold-time-min-acceptable>
596             <keep-alive-time>30</keep-alive-time>
597             <reconciliation-time>$timeout</reconciliation-time>
598             <delete-hold-down-time>$timeout</delete-hold-down-time>
599          </connection-timers>
600       </connection>
601    </connections>
602 </input>
603 ''')
604     data = templ.substitute(
605         {'ip': ip, 'port': port, 'mode': mode, 'version': version, 'node': node,
606          'password_': password_, 'domain': get_domain_name(domain_name), 'timeout': bindings_timeout,
607          'security_type': '<security-type>' + security_mode + '</security-type>' if security_mode else ''})
608     return data
609
610
611 def delete_connections_xml(address, port, node, domain_name):
612     """Generate xml for Delete Connection request
613
614     :param address: Ipv4/6 address of remote peer
615     :type address: str
616     :param port: Port on with remote peer listens
617     :type port: str
618     :param node: Ipv4 address of node
619     :type node: str
620     :param domain_name: Name of Domain
621     :type domain_name: str
622     :returns: String containing xml data for request
623
624     """
625     templ = Template('''<input>
626    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
627    $domain
628    <peer-address xmlns="urn:opendaylight:sxp:controller">$address</peer-address>
629    <tcp-port xmlns="urn:opendaylight:sxp:controller">$port</tcp-port>
630 </input>''')
631     data = templ.substitute({'address': address, 'port': port, 'node': node, 'domain': get_domain_name(domain_name)})
632     return data
633
634
635 def update_binding_xml(sgt0, prefix0, sgt1, prefix1, ip, domain_name):
636     """Generate xml for Update Binding request
637
638     :param sgt0: Original Source Group Tag
639     :type sgt0: str
640     :param prefix0: Original Ipv4/6 prefix
641     :type prefix0: str
642     :param sgt1: New Source Group Tag
643     :type sgt1: str
644     :param prefix1: New Ipv4/6 prefix
645     :type prefix1: str
646     :param ip: Ipv4 address of node
647     :type ip: str
648     :param domain_name: Name of Domain
649     :type domain_name: str
650     :returns: String containing xml data for request
651
652     """
653     templ = Template('''<input>
654   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
655   $domain
656   <original-binding xmlns="urn:opendaylight:sxp:controller">
657     <sgt>$sgt0</sgt>
658     <ip-prefix>$prefix0</ip-prefix>
659   </original-binding>
660   <new-binding xmlns="urn:opendaylight:sxp:controller">
661     <sgt>$sgt1</sgt>
662     <ip-prefix>$prefix1</ip-prefix>
663   </new-binding>
664 </input>''')
665     data = templ.substitute(
666         {'sgt0': sgt0, 'sgt1': sgt1, 'prefix0': prefix0, 'prefix1': prefix1, 'ip': ip,
667          'domain': get_domain_name(domain_name)})
668     return data
669
670
671 def delete_binding_xml(sgt, prefix, ip, domain_name):
672     """Generate xml for Delete Binding request
673
674     :param sgt: Source Group Tag
675     :type sgt: str
676     :param prefix: Ipv4/6 prefix
677     :type prefix: str
678     :param ip: Ipv4 address of node
679     :type ip: str
680     :param domain_name: Name of Domain
681     :type domain_name: str
682     :returns: String containing xml data for request
683
684     """
685     templ = Template('''<input>
686   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
687   <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
688   <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
689   $domain
690 </input>''')
691     data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip, 'domain': get_domain_name(domain_name)})
692     return data
693
694
695 def add_peer_group_xml(name, peers, ip):
696     """Generate xml for Add PeerGroups request
697
698     :param name: Name of PeerGroup
699     :type name: str
700     :param peers: XML formatted peers that will be added to group
701     :type peers: str
702     :param ip: Ipv4 address of node
703     :type ip: str
704     :returns: String containing xml data for request
705
706     """
707     templ = Template('''<input>
708   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
709   <sxp-peer-group xmlns="urn:opendaylight:sxp:controller">
710     <name xmlns="urn:opendaylight:sxp:controller">$name</name>
711     <sxp-peers xmlns="urn:opendaylight:sxp:controller">$peers</sxp-peers>
712     </sxp-peer-group>
713 </input>''')
714     data = templ.substitute({'name': name, 'peers': peers, 'ip': ip})
715     return data
716
717
718 def delete_peer_group_xml(name, ip):
719     """Generate xml for Delete PeerGroup request
720
721     :param name: Name of PeerGroup
722     :type name: str
723     :param ip: Ipv4 address of node
724     :type ip: str
725     :returns: String containing xml data for request
726
727     """
728     templ = Template('''<input>
729   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
730   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$name</peer-group-name>
731 </input>''')
732     data = templ.substitute({'name': name, 'ip': ip})
733     return data
734
735
736 def get_peer_groups_from_node_xml(ip):
737     """Generate xml for Get PeerGroups request
738
739     :param ip: Ipv4 address of node
740     :type ip: str
741     :returns: String containing xml data for request
742
743     """
744     templ = Template('''<input>
745    <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
746 </input>''')
747     data = templ.substitute({'ip': ip})
748     return data
749
750
751 def add_filter_xml(group, filter_type, entries, ip, policy=None):
752     """Generate xml for Add Filter request
753
754     :param group: Name of group containing filter
755     :type group: str
756     :param filter_type: Type of filter
757     :type filter_type: str
758     :param entries: XML formatted entries that will be added in filter
759     :type entries: str
760     :param ip: Ipv4 address of node
761     :type ip: str
762     :param policy: Policy of filter update mechanism
763     :type policy: str
764     :returns: String containing xml data for request
765
766     """
767     if policy:
768         policy = "<filter-policy>" + policy + "</filter-policy>"
769     else:
770         policy = ""
771     templ = Template('''<input>
772   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
773   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
774   <sxp-filter xmlns="urn:opendaylight:sxp:controller">
775     $filter_policy
776     <filter-type>$filter_type</filter-type>$entries
777   </sxp-filter>
778 </input>''')
779     data = templ.substitute(
780         {'group': group, 'filter_type': filter_type, 'ip': ip, 'entries': entries, 'filter_policy': policy})
781     return data
782
783
784 def add_domain_filter_xml(domain, domains, entries, ip, filter_name=None):
785     """Generate xml for Add Domain Filter request
786
787     :param domain: Name of Domain containing filter
788     :type domain: str
789     :param domains: Domains on which filter will be applied
790     :type domains: str
791     :param entries: XML formatted entries that will be added in filter
792     :type entries: str
793     :param ip: Ipv4 address of node
794     :type ip: str
795     :param filter_name: Name of filter
796     :type filter_name: str
797     :returns: String containing xml data for request
798
799     """
800     if filter_name:
801         filter_name = "<filter-name>" + filter_name + "</filter-name>"
802     templ = Template('''<input>
803   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
804   <domain-name xmlns="urn:opendaylight:sxp:controller">$domain</domain-name>
805   <sxp-domain-filter xmlns="urn:opendaylight:sxp:controller">
806     $filter_name
807     <domains>$domains</domains>
808     $entries
809   </sxp-domain-filter>
810 </input>''')
811     data = templ.substitute(
812         {'domain': domain, 'domains': domains, 'ip': ip, 'entries': entries, 'filter_name': filter_name})
813     return data
814
815
816 def delete_filter_xml(group, filter_type, ip):
817     """Generate xml for Delete Filter request
818
819     :param group: Name of group containing filter
820     :type group: str
821     :param filter_type: Type of filter
822     :type filter_type: str
823     :param ip: Ipv4 address of node
824     :type ip: str
825     :returns: String containing xml data for request
826
827     """
828     templ = Template('''<input>
829   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
830   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
831   <filter-type xmlns="urn:opendaylight:sxp:controller">$filter_type</filter-type>
832 </input>''')
833     data = templ.substitute(
834         {'group': group, 'filter_type': filter_type, 'ip': ip})
835     return data
836
837
838 def delete_domain_filter_xml(domain, ip, filter_name=None):
839     """Generate xml for Delete Filter request
840
841     :param domain: Name of Domain containing filter
842     :type domain: str
843     :param ip: Ipv4 address of node
844     :type ip: str
845     :param filter_name: Name of filter
846     :type filter_name: str
847     :returns: String containing xml data for request
848
849     """
850     if filter_name:
851         filter_name = '<filter-name xmlns="urn:opendaylight:sxp:controller">' + filter_name + "</filter-name>"
852     templ = Template('''<input>
853   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
854   <domain-name xmlns="urn:opendaylight:sxp:controller">$domain</domain-name>
855   $filter_name
856 </input>''')
857     data = templ.substitute(
858         {'domain': domain, 'ip': ip, 'filter_name': filter_name})
859     return data
860
861
862 def get_connections_from_node_xml(ip, domain_name):
863     """Generate xml for Get Connections request
864
865     :param ip: Ipv4 address of node
866     :type ip: str
867     :param domain_name: Name of Domain
868     :type domain_name: str
869     :returns: String containing xml data for request
870
871     """
872     templ = Template('''<input>
873    <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
874    $domain
875 </input>''')
876     data = templ.substitute({'ip': ip, 'domain': get_domain_name(domain_name)})
877     return data
878
879
880 def get_bindings_from_node_xml(ip, binding_range, domain_name):
881     """Generate xml for Get Bindings request
882
883     :param binding_range: All or only Local bindings
884     :type binding_range: str
885     :param ip: Ipv4 address of node
886     :type ip: str
887     :param domain_name: Name of Domain
888     :type domain_name: str
889     :returns: String containing xml data for request
890
891     """
892     templ = Template('''<input>
893   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
894   <bindings-range xmlns="urn:opendaylight:sxp:controller">$range</bindings-range>
895   $domain
896 </input>''')
897     data = templ.substitute({'ip': ip, 'range': binding_range, 'domain': get_domain_name(domain_name)})
898     return data
899
900
901 def add_node_xml(node_id, port, password, version, node_ip=None, expansion=0, bindings_timeout=0, keystores=None):
902     """Generate xml for Add Node request
903
904     :param node_id: Ipv4 address formatted node id
905     :type node_id: str
906     :param node_ip: Ipv4 address of node
907     :type node_ip: strl
908     :param port: Node port number
909     :type port: int
910     :param password: TCP-MD5 password
911     :type password: str
912     :param version: Sxp device version
913     :type version: str
914     :param expansion: Bindings expansion
915     :type expansion: int
916     :param bindings_timeout: Specifies DHD and Reconciliation timers
917     :type bindings_timeout: int
918     :param keystores: SSL keystore and truststore specification
919     :type keystores: dict
920     :returns: String containing xml data for request
921
922     """
923     tls = ''
924     if keystores:
925         tls = Template('''
926         <tls>
927             <keystore>
928               <location>$keystore</location>
929               <type>JKS</type>
930               <path-type>PATH</path-type>
931               <password>$passwd</password>
932             </keystore>
933             <truststore>
934               <location>$truststore</location>
935               <type>JKS</type>
936               <path-type>PATH</path-type>
937               <password>$passwd</password>
938             </truststore>
939             <certificate-password>$passwd</certificate-password>
940         </tls>
941     ''').substitute(
942             {'keystore': keystores['keystore'], 'truststore': keystores['truststore'], 'passwd': keystores['password']})
943
944     templ = Template('''<input xmlns="urn:opendaylight:sxp:controller">
945     <node-id>$id</node-id>
946     <timers>
947         <retry-open-time>1</retry-open-time>
948         <hold-time-min-acceptable>120</hold-time-min-acceptable>
949         <delete-hold-down-time>$timeout</delete-hold-down-time>
950         <hold-time-min>90</hold-time-min>
951         <reconciliation-time>$timeout</reconciliation-time>
952         <hold-time>90</hold-time>
953         <hold-time-max>180</hold-time-max>
954         <keep-alive-time>30</keep-alive-time>
955     </timers>
956     <mapping-expanded>$expansion</mapping-expanded>
957     <security>
958         $tls
959         <password>$password</password>
960     </security>
961     <tcp-port>$port</tcp-port>
962     <version>$version</version>
963     <description>ODL SXP Controller</description>
964     <source-ip>$ip</source-ip>
965     <master-database></master-database>
966 </input>''')
967     data = templ.substitute(
968         {'ip': node_ip or node_id, 'id': node_id, 'port': port, 'password': password,
969          'version': version, 'expansion': expansion, 'timeout': bindings_timeout, 'tls': tls})
970     return data
971
972
973 def delete_node_xml(node_id):
974     """Generate xml for Delete node request
975
976     :param node_id: Ipv4 address formatted node id
977     :type node_id: str
978     :returns: String containing xml data for request
979
980     """
981     templ = Template('''<input xmlns="urn:opendaylight:sxp:controller">
982   <node-id>$id</node-id>
983 </input>''')
984     data = templ.substitute({'id': node_id})
985     return data
986
987
988 def add_domain_xml(node_id, name):
989     """Generate xml for Add Domain request
990
991     :param node_id: Id of node
992     :type node_id: str
993     :param name: Name of Domain
994     :type name: str
995     :returns: String containing xml data for request
996
997     """
998     templ = Template('''<input>
999   <node-id xmlns="urn:opendaylight:sxp:controller">$id</node-id>
1000   <domain-name xmlns="urn:opendaylight:sxp:controller">$name</domain-name>
1001 </input>''')
1002     data = templ.substitute({'name': name, 'id': node_id})
1003     return data
1004
1005
1006 def delete_domain_xml(node_id, name):
1007     """Generate xml for Remove Domain request
1008
1009     :param node_id: Id of node
1010     :type node_id: str
1011     :param name: Name of Domain
1012     :type name: str
1013     :returns: String containing xml data for request
1014
1015     """
1016     return add_domain_xml(node_id, name)
1017
1018
1019 def get_domain_name(domain_name):
1020     """Generate xml for Get Bindings request
1021
1022     :param domain_name: Name of Domain
1023     :type domain_name: str
1024     :returns: String containing xml data for request
1025
1026     """
1027     if domain_name == 'global':
1028         return ''
1029     else:
1030         return '<domain-name xmlns="urn:opendaylight:sxp:controller">' + domain_name + '</domain-name>'
1031
1032
1033 def add_bindings_xml(node_id, domain, sgt, prefixes):
1034     """Generate xml for Add Bindings request
1035
1036     :param node_id: Id of node
1037     :type node_id: str
1038     :param domain: Name of Domain
1039     :type domain: str
1040     :param sgt: Security group
1041     :type sgt: int
1042     :param prefixes: List of ip-prefixes
1043     :type prefixes: str
1044     :returns: String containing xml data for request
1045
1046     """
1047     bindings = ''
1048     for prefix in prefixes.split(','):
1049         bindings += '\n' + '<ip-prefix>' + prefix + '</ip-prefix>'
1050     templ = Template('''<input>
1051   <node-id xmlns="urn:opendaylight:sxp:controller">$id</node-id>
1052   <domain-name xmlns="urn:opendaylight:sxp:controller">$name</domain-name>
1053   <binding xmlns="urn:opendaylight:sxp:controller">
1054       <sgt>$sgt</sgt>
1055       $bindings
1056   </binding>
1057 </input>''')
1058     data = templ.substitute({'name': domain, 'id': node_id, 'sgt': sgt, 'bindings': bindings})
1059     return data
1060
1061
1062 def delete_bindings_xml(node_id, domain, sgt, prefixes):
1063     """Generate xml for Remove Bindings request
1064
1065     :param node_id: Id of node
1066     :type node_id: str
1067     :param domain: Name of Domain
1068     :type domain: str
1069     :param sgt: Security group
1070     :type sgt: int
1071     :param prefixes: List of ip-prefixes
1072     :type prefixes: str
1073     :returns: String containing xml data for request
1074
1075     """
1076     return add_bindings_xml(node_id, domain, sgt, prefixes)
1077
1078
1079 def prefix_range(start, end):
1080     """Generate and concatenate ip-prefixes
1081
1082     :param start: Start index
1083     :type start: str
1084     :param end: End index
1085     :type end: str
1086     :returns: String containing concatenated ip-prefixes
1087
1088     """
1089     start = int(start)
1090     end = int(end)
1091     index = 0
1092     prefixes = ''
1093     while index < end:
1094         prefixes += get_ip_from_number(index + start) + '/32'
1095         index += 1
1096         if index < end:
1097             prefixes += ','
1098     return prefixes