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