Updated SXP CSIT with new feature PeerSequence filter,
[integration/test.git] / csit / libraries / Sxp.py
1 import json
2 from ipaddr import IPAddress
3 from string import Template
4
5
6 def mod(num, base):
7     """Gets modulo of number
8
9     :param num: Number to be used
10     :type num: string
11     :param base: Base used
12     :type base: string
13     :returns: Int representing modulo of specified numbers.
14
15     """
16     return int(num) % int(base)
17
18
19 def get_ip_from_number(n):
20     """Generate string representing Ipv4 from specified number that is added number 2130706432
21
22     :param n: Number to be converted
23     :type n: int
24     :returns: String containing Ipv4.
25
26     """
27     ip = IPAddress(2130706432 + n)
28     return str(ip)
29
30
31 def lower_version(ver1, ver2):
32     """Generate xml containing SGT mach data
33
34     :param ver1: Version of SXP protocol for compare
35     :type ver1: string
36     :param ver2: Version of SXP protocol for compare
37     :type ver2: string
38     :returns: String containing lower from those two specified versions.
39
40     """
41     v1 = int(ver1[-1:])
42     v2 = int(ver2[-1:])
43     if v1 <= v2:
44         return ver1
45     else:
46         return ver2
47
48
49 def get_filter_entry(seq, entry_type, sgt="", esgt="", acl="", eacl="", pl="", epl="", ps=""):
50     """Generate xml containing FilterEntry data
51
52     :param seq: Sequence of entry
53     :type seq: string
54     :param entry_type: Type of entry (permit/deny)
55     :type entry_type: string
56     :param sgt: SGT matches to be added to entry
57     :type sgt: string
58     :param esgt: SGT ranges match to be added to entry
59     :type esgt: string
60     :param acl: ACL matches to be added to entry
61     :type acl: string
62     :param eacl: EACL matches to be added to entry
63     :type eacl: string
64     :param pl: PrefixList matches to be added to entry
65     :type pl: string
66     :param epl: ExtendedPrefixList matches to be added to entry
67     :type epl: string
68     :param ps: PeerSequence matches to be added to entry
69     :type ps: string
70     :returns: String containing xml data for request
71
72     """
73     entries = ""
74     # Generate XML request containing combination of Matches of different types
75     if sgt:
76         args = sgt.split(',')
77         entries += add_sgt_matches_xml(args)
78     elif esgt:
79         args = esgt.split(',')
80         entries += add_sgt_range_xml(args[0], args[1])
81     if pl:
82         entries += add_pl_entry_xml(pl)
83     elif epl:
84         args = epl.split(',')
85         entries += add_epl_entry_xml(args[0], args[1], args[2])
86     if acl:
87         args = acl.split(',')
88         entries += add_acl_entry_xml(args[0], args[1])
89     elif eacl:
90         args = eacl.split(',')
91         entries += add_eacl_entry_xml(args[0], args[1], args[2], args[3])
92     if ps:
93         args = ps.split(',')
94         entries += add_ps_entry_xml(args[0], args[1])
95     # Wrap entries in ACL/PrefixList according to specified values
96     if pl or epl:
97         return add_pl_entry_default_xml(seq, entry_type, entries)
98     elif ps:
99         return add_ps_entry_default_xml(seq, entry_type, entries)
100     return add_acl_entry_default_xml(seq, entry_type, entries)
101
102
103 def add_peers(*args):
104     """Generate xml containing Peer mach data
105
106     :param args: Peers data
107     :type args: dict
108     :returns: String containing xml data for request
109
110     """
111     templ = Template('''
112         <sxp-peer>
113             <peer-address>$ip</peer-address>
114         </sxp-peer>''')
115     peers = ""
116     for count, value in enumerate(args):
117         peers += templ.substitute({'ip': value})
118     return peers
119
120
121 def add_sgt_matches_xml(sgt_entries):
122     """Generate xml containing SGT mach data
123
124     :param sgt_entries: SGT matches
125     :type sgt_entries: string
126     :returns: String containing xml data for request
127
128     """
129     templ = Template('''
130         <matches>$sgt</matches>''')
131     matches = ""
132     for sgt in sgt_entries:
133         matches += templ.substitute({'sgt': sgt})
134     return matches
135
136
137 def add_sgt_range_xml(start, end):
138     """Generate xml containing SGT RangeMach data
139
140     :param start: Start range of SGT
141     :type start: string
142     :param end: End range of SGT
143     :type end: string
144     :returns: String containing xml data for request
145
146     """
147     templ = Template('''
148         <sgt-start>$start</sgt-start>
149         <sgt-end>$end</sgt-end>''')
150     match = templ.substitute({'start': start, 'end': end})
151     return match
152
153
154 def add_acl_entry_default_xml(seq, entry_type, acl_entries):
155     """Generate xml containing AccessList data
156
157     :param seq: Sequence of PrefixList entry
158     :type seq: string
159     :param entry_type: Entry type (permit/deny)
160     :type entry_type: string
161     :param acl_entries: XML data containing AccessList entries
162     :type acl_entries: string
163     :returns: String containing xml data for request
164
165     """
166     templ = Template('''
167         <acl-entry>
168             <entry-type>$entry_type</entry-type>
169             <entry-seq>$seq</entry-seq>$acl_entries
170         </acl-entry>''')
171     matches = templ.substitute(
172         {'seq': seq, 'entry_type': entry_type, 'acl_entries': acl_entries})
173     return matches
174
175
176 def add_acl_entry_xml(ip, mask):
177     """Generate xml containing AccessList data
178
179     :param ip: Ipv4/6 address
180     :type ip: string
181     :param mask: Ipv4/6 wildcard mask
182     :type mask: string
183     :returns: String containing xml data for request
184
185     """
186     templ = Template('''
187         <acl-match>
188             <ip-address>$ip</ip-address>
189             <wildcard-mask>$mask</wildcard-mask>
190         </acl-match>''')
191     return templ.substitute({'ip': ip, 'mask': mask})
192
193
194 def add_eacl_entry_xml(ip, mask, amask, wmask):
195     """Generate xml containing ExtendedAccessList data
196
197     :param ip: Ipv4/6 address
198     :type ip: string
199     :param mask: Ipv4/6 wildcard mask
200     :type mask: string
201     :param amask: Ipv4/6 address mask
202     :type amask: string
203     :param wmask: Ipv4/6 address wildcard mask
204     :type wmask: string
205     :returns: String containing xml data for request
206
207     """
208     templ = Template('''
209         <acl-match>
210             <ip-address>$ip</ip-address>
211             <wildcard-mask>$mask</wildcard-mask>
212             <mask>
213               <address-mask>$amask</address-mask>
214               <wildcard-mask>$wmask</wildcard-mask>
215             </mask>
216         </acl-match>''')
217     return templ.substitute({'ip': ip, 'mask': mask, 'amask': amask, 'wmask': wmask})
218
219
220 def add_ps_entry_default_xml(seq, entry_type, ps_entries):
221     """Generate xml containing PeerSequence data
222
223     :param seq: Sequence of PrefixList entry
224     :type seq: string
225     :param entry_type: Entry type (permit/deny)
226     :type entry_type: string
227     :param ps_entries: XML data containing PeerSequence entries
228     :type ps_entries: string
229     :returns: String containing xml data for request
230
231     """
232     templ = Template('''
233     <peer-sequence-entry xmlns="urn:opendaylight:sxp:controller">
234           <entry-type>$entry_type</entry-type>
235           <entry-seq>$seq</entry-seq>$ps_entries
236     </peer-sequence-entry>''')
237     return templ.substitute({'seq': seq, 'entry_type': entry_type, 'ps_entries': ps_entries})
238
239
240 def add_pl_entry_default_xml(seq, entry_type, pl_entries):
241     """Generate xml containing PrefixList data
242
243     :param seq: Sequence of PrefixList entry
244     :type seq: string
245     :param entry_type: Entry type (permit/deny)
246     :type entry_type: string
247     :param pl_entries: XML data containing PrefixList entries
248     :type pl_entries: string
249     :returns: String containing xml data for request
250
251     """
252     templ = Template('''
253     <prefix-list-entry xmlns="urn:opendaylight:sxp:controller">
254           <entry-type>$entry_type</entry-type>
255           <entry-seq>$seq</entry-seq>$pl_entries
256     </prefix-list-entry>''')
257     return templ.substitute({'seq': seq, 'entry_type': entry_type, 'pl_entries': pl_entries})
258
259
260 def add_pl_entry_xml(prefix):
261     """Generate xml containing PrefixList data
262
263     :param prefix: Ipv4/6 prefix
264     :type prefix: string
265     :returns: String containing xml data for request
266
267     """
268     templ = Template('''
269         <prefix-list-match>
270             <ip-prefix>$prefix</ip-prefix>
271         </prefix-list-match>''')
272     return templ.substitute({'prefix': prefix})
273
274
275 def add_epl_entry_xml(prefix, op, mask):
276     """Generate xml containing Extended PrefixList data
277
278     :param prefix: Ipv4/6 prefix
279     :type prefix: string
280     :param op: PrefixList option (ge/le/eq)
281     :type op: string
282     :param mask: Ipv4/6 Mask
283     :type mask: string
284     :returns: String containing xml data for request
285
286     """
287     templ = Template('''
288         <prefix-list-match>
289             <ip-prefix>$prefix</ip-prefix>
290             <mask>
291                 <mask-range>$op</mask-range>
292                 <mask-value>$mask</mask-value>
293             </mask>
294         </prefix-list-match>''')
295     return templ.substitute({'prefix': prefix, 'mask': mask, 'op': op})
296
297
298 def add_ps_entry_xml(op, length):
299     """Generate xml containing Extended PrefixList data
300
301     :param op: PrefixList option (ge/le/eq)
302     :type op: string
303     :param length: PeerSequence length
304     :type length: string
305     :returns: String containing xml data for request
306
307     """
308     templ = Template('''
309         <peer-sequence-length>$length</peer-sequence-length>
310         <peer-sequence-range>$op</peer-sequence-range>
311         ''')
312     return templ.substitute({'length': length, 'op': op})
313
314
315 def parse_peer_groups(groups_json):
316     """Parse JSON string into Array of PeerGroups
317
318     :param groups_json: JSON containing PeerGroups
319     :type groups_json: string
320     :returns: Array containing PeerGroups.
321
322     """
323     data = json.loads(groups_json)
324     groups = data['output']
325     output = []
326     for group in groups.values():
327         output += group
328     return output
329
330
331 def parse_connections(connections_json):
332     """Parse JSON string into Array of Connections
333
334     :param connections_json: JSON containing Connections
335     :type connections_json: string
336     :returns: Array containing Connections.
337
338     """
339     data = json.loads(connections_json)
340     connections = data['output']['connections']
341     output = []
342     for connection in connections.values():
343         output += connection
344     return output
345
346
347 def find_connection(connections_json, version, mode, ip, port, state):
348     """Test if Connection with specified values is contained in JSON
349
350     :param connections_json: JSON containing Connections
351     :type connections_json: string
352     :param version: Version of SXP protocol (version1/2/3/4)
353     :type version: string
354     :param mode: Mode of SXP peer (speaker/listener/both)
355     :type mode: string
356     :param ip: Ipv4/6 address of remote peer
357     :type ip: string
358     :param port: Port on with remote peer listens
359     :type port: string
360     :param state: State of connection (on/off/pendingOn/deleteHoldDown)
361     :type state: string
362     :returns: True if Connection with specified params was found, otherwise False.
363
364     """
365     for connection in parse_connections(connections_json):
366         if (connection['peer-address'] == ip and connection['tcp-port'] == int(port) and connection['mode'] == mode and
367                 connection['version'] == version):
368             if state == 'none':
369                 return True
370             elif connection['state'] == state:
371                 return True
372     return False
373
374
375 def parse_bindings(bindings_json):
376     """Parse JSON string into Array of Bindings
377
378     :param bindings_json: JSON containing Bindings
379     :type bindings_json: string
380     :returns: Array containing Bindings.
381
382     """
383     data = json.loads(bindings_json)
384     output = []
385     for bindings_json in data['output'].values():
386         for binding in bindings_json:
387             output.append(binding)
388     return output
389
390
391 def find_binding(bindings, sgt, prefix):
392     """Test if Binding with specified values is contained in JSON
393
394     :param bindings: JSON containing Bindings
395     :type bindings: string
396     :param sgt: Source Group Tag
397     :type sgt: string
398     :param prefix: Ipv4/6 prefix
399     :type prefix: string
400     :returns: True if Binding with specified params was found, otherwise False.
401
402     """
403     for binding in parse_bindings(bindings):
404         if binding['sgt'] == int(sgt):
405             for ip_prefix in binding['ip-prefix']:
406                 if ip_prefix == prefix:
407                     return True
408     return False
409
410
411 def parse_prefix_groups(prefix_groups_json, source_):
412     """Parse JSON string into Array of PrefixGroups
413
414     :param prefix_groups_json: JSON containing PrefixGroups
415     :type prefix_groups_json: string
416     :param source_: Source of PrefixGroups (sxp/local)
417     :type source_: string
418     :returns: Array containing PrefixGroups.
419
420     """
421     data = json.loads(prefix_groups_json)
422     bindings = data['sxp-node:master-database']
423     output = []
424     for binding in bindings.values():
425         for binding_source in binding:
426             if source_ == "any" or binding_source['binding-source'] == source_:
427                 for prefix_group in binding_source['prefix-group']:
428                     output.append(prefix_group)
429     return output
430
431
432 def find_binding_legacy(prefix_groups_json, sgt, prefix, source_, action):
433     """Test if Binding with specified values is contained in JSON
434
435     :param prefix_groups_json: JSON containing Bindings and PrefixGroups
436     :type prefix_groups_json: string
437     :param sgt: Source Group Tag
438     :type sgt: string
439     :param prefix: Ipv4/6 prefix
440     :type prefix: string
441     :param source_: Source of binding (local/sxp)
442     :type source_: string
443     :param action: Action for binding (add/delete)
444     :type action: string
445     :returns: True if Binding with specified params was found, otherwise False.
446
447     """
448     found = False
449     for prefixgroup in parse_prefix_groups(prefix_groups_json, source_):
450         if prefixgroup['sgt'] == int(sgt):
451             for binding in prefixgroup['binding']:
452                 if binding['ip-prefix'] == prefix and binding['action'] == action:
453                     found = True
454     return found
455
456
457 def add_entry_xml(sgt, prefix, ip):
458     """Generate xml for Add Bindings request
459
460     :param sgt: Source Group Tag
461     :type sgt: string
462     :param prefix: Ipv4/6 prefix
463     :type prefix: string
464     :param ip: Ipv4 address of node
465     :type ip: string
466     :returns: String containing xml data for request
467
468     """
469     templ = Template('''<input>
470   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
471   <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
472   <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
473 </input>''')
474     data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip})
475     return data
476
477
478 def add_connection_xml(version, mode, ip, port, node, password_):
479     """Generate xml for Add Connection request
480
481     :param version: Version of SXP protocol (version1/2/3/4)
482     :type version: string
483     :param mode: Mode of SXP peer (speaker/listener/both)
484     :type mode: string
485     :param ip: Ipv4/6 address of remote peer
486     :type ip: string
487     :param port: Port on with remote peer listens
488     :type port: string
489     :param node: Ipv4 address of node
490     :type node: string
491     :param password_: Password type (none/default)
492     :type password_: string
493     :returns: String containing xml data for request
494
495     """
496     templ = Template('''<input>
497    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
498    <connections xmlns="urn:opendaylight:sxp:controller">
499       <connection>
500          <peer-address>$ip</peer-address>
501          <tcp-port>$port</tcp-port>
502          <password>$password_</password>
503          <mode>$mode</mode>
504          <version>$version</version>
505          <description>Connection to ISR-G2</description>
506          <connection-timers>
507             <hold-time-min-acceptable>45</hold-time-min-acceptable>
508             <keep-alive-time>30</keep-alive-time>
509             <reconciliation-time>120</reconciliation-time>
510          </connection-timers>
511       </connection>
512    </connections>
513 </input>
514 ''')
515     data = templ.substitute(
516         {'ip': ip, 'port': port, 'mode': mode, 'version': version, 'node': node, 'password_': password_})
517     return data
518
519
520 def delete_connections_xml(address, port, node):
521     """Generate xml for Delete Connection request
522
523     :param address: Ipv4/6 address of remote peer
524     :type address: string
525     :param port: Port on with remote peer listens
526     :type port: string
527     :param node: Ipv4 address of node
528     :type node: string
529     :returns: String containing xml data for request
530
531     """
532     templ = Template('''<input>
533    <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
534    <peer-address xmlns="urn:opendaylight:sxp:controller">$address</peer-address>
535    <tcp-port xmlns="urn:opendaylight:sxp:controller">$port</tcp-port>
536 </input>''')
537     data = templ.substitute({'address': address, 'port': port, 'node': node})
538     return data
539
540
541 def update_binding_xml(sgt0, prefix0, sgt1, prefix1, ip):
542     """Generate xml for Update Binding request
543
544     :param sgt0: Original Source Group Tag
545     :type sgt0: string
546     :param prefix0: Original Ipv4/6 prefix
547     :type prefix0: string
548     :param sgt1: New Source Group Tag
549     :type sgt1: string
550     :param prefix1: New Ipv4/6 prefix
551     :type prefix1: string
552     :param ip: Ipv4 address of node
553     :type ip: string
554     :returns: String containing xml data for request
555
556     """
557     templ = Template('''<input>
558   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
559   <original-binding xmlns="urn:opendaylight:sxp:controller">
560     <sgt>$sgt0</sgt>
561     <ip-prefix>$prefix0</ip-prefix>
562   </original-binding>
563   <new-binding xmlns="urn:opendaylight:sxp:controller">
564     <sgt>$sgt1</sgt>
565     <ip-prefix>$prefix1</ip-prefix>
566   </new-binding>
567 </input>''')
568     data = templ.substitute(
569         {'sgt0': sgt0, 'sgt1': sgt1, 'prefix0': prefix0, 'prefix1': prefix1, 'ip': ip})
570     return data
571
572
573 def delete_binding_xml(sgt, prefix, ip):
574     """Generate xml for Delete Binding request
575
576     :param sgt: Source Group Tag
577     :type sgt: string
578     :param prefix: Ipv4/6 prefix
579     :type prefix: string
580     :param ip: Ipv4 address of node
581     :type ip: string
582     :returns: String containing xml data for request
583
584     """
585     templ = Template('''<input>
586   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
587   <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
588   <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
589 </input>''')
590     data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip})
591     return data
592
593
594 def add_peer_group_xml(name, peers, ip):
595     """Generate xml for Add PeerGroups request
596
597     :param name: Name of PeerGroup
598     :type name: string
599     :param peers: XML formatted peers that will be added to group
600     :type peers: string
601     :param ip: Ipv4 address of node
602     :type ip: string
603     :returns: String containing xml data for request
604
605     """
606     templ = Template('''<input>
607   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
608   <sxp-peer-group xmlns="urn:opendaylight:sxp:controller">
609     <name xmlns="urn:opendaylight:sxp:controller">$name</name>
610     <sxp-peers xmlns="urn:opendaylight:sxp:controller">$peers</sxp-peers>
611     </sxp-peer-group>
612 </input>''')
613     data = templ.substitute({'name': name, 'peers': peers, 'ip': ip})
614     return data
615
616
617 def delete_peer_group_xml(name, ip):
618     """Generate xml for Delete PeerGroup request
619
620     :param name: Name of PeerGroup
621     :type name: string
622     :param ip: Ipv4 address of node
623     :type ip: string
624     :returns: String containing xml data for request
625
626     """
627     templ = Template('''<input>
628   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
629   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$name</peer-group-name>
630 </input>''')
631     data = templ.substitute({'name': name, 'ip': ip})
632     return data
633
634
635 def get_peer_groups_from_node_xml(ip):
636     """Generate xml for Get PeerGroups request
637
638     :param ip: Ipv4 address of node
639     :type ip: string
640     :returns: String containing xml data for request
641
642     """
643     templ = Template('''<input>
644    <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
645 </input>''')
646     data = templ.substitute({'ip': ip})
647     return data
648
649
650 def add_filter_xml(group, filter_type, entries, ip):
651     """Generate xml for Add Filter request
652
653     :param group: Name of group containing filter
654     :type group: string
655     :param filter_type: Type of filter
656     :type filter_type: string
657     :param entries: XML formatted entries that will be added in filter
658     :type entries: string
659     :param ip: Ipv4 address of node
660     :type ip: string
661     :returns: String containing xml data for request
662
663
664     """
665     templ = Template('''<input>
666   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
667   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
668   <sxp-filter xmlns="urn:opendaylight:sxp:controller">
669     <filter-type>$filter_type</filter-type>$entries
670   </sxp-filter>
671 </input>''')
672     data = templ.substitute(
673         {'group': group, 'filter_type': filter_type, 'ip': ip, 'entries': entries})
674     return data
675
676
677 def delete_filter_xml(group, filter_type, ip):
678     """Generate xml for Delete Filter request
679
680     :param group: Name of group containing filter
681     :type group: string
682     :param filter_type: Type of filter
683     :type filter_type: string
684     :param ip: Ipv4 address of node
685     :type ip: string
686     :returns: String containing xml data for request
687
688     """
689     templ = Template('''<input>
690   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
691   <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
692   <filter-type xmlns="urn:opendaylight:sxp:controller">$filter_type</filter-type>
693 </input>''')
694     data = templ.substitute(
695         {'group': group, 'filter_type': filter_type, 'ip': ip})
696     return data
697
698
699 def get_connections_from_node_xml(ip):
700     """Generate xml for Get Connections request
701
702     :param ip: Ipv4 address of node
703     :type ip: string
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 </input>''')
710     data = templ.substitute({'ip': ip})
711     return data
712
713
714 def get_bindings_from_node_xml(ip, binding_range):
715     """Generate xml for Get Bindings request
716
717     :param binding_range: All or only Local bindings
718     :type binding_range: string
719     :param ip: Ipv4 address of node
720     :type ip: string
721     :returns: String containing xml data for request
722
723     """
724     templ = Template('''<input>
725   <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
726   <bindings-range xmlns="urn:opendaylight:sxp:controller">$range</bindings-range>
727 </input>''')
728     data = templ.substitute({'ip': ip, 'range': binding_range})
729     return data