2 from ipaddr import IPAddress
3 from string import Template
7 """Gets modulo of number
9 :param num: Number to be used
11 :param base: Base used
13 :returns: Int representing modulo of specified numbers.
16 return int(num) % int(base)
19 def get_ip_from_number(n):
20 """Generate string representing Ipv4 from specified number that is added number 2130706432
22 :param n: Number to be converted
24 :returns: String containing Ipv4.
27 ip = IPAddress(2130706432 + n)
31 def lower_version(ver1, ver2):
32 """Generate xml containing SGT mach data
34 :param ver1: Version of SXP protocol for compare
36 :param ver2: Version of SXP protocol for compare
38 :returns: String containing lower from those two specified versions.
49 def get_filter_entry(seq, entry_type, sgt="", esgt="", acl="", eacl="", pl="", epl="", ps=""):
50 """Generate xml containing FilterEntry data
52 :param seq: Sequence of entry
54 :param entry_type: Type of entry (permit/deny)
55 :type entry_type: string
56 :param sgt: SGT matches to be added to entry
58 :param esgt: SGT ranges match to be added to entry
60 :param acl: ACL matches to be added to entry
62 :param eacl: EACL matches to be added to entry
64 :param pl: PrefixList matches to be added to entry
66 :param epl: ExtendedPrefixList matches to be added to entry
68 :param ps: PeerSequence matches to be added to entry
70 :returns: String containing xml data for request
74 # Generate XML request containing combination of Matches of different types
77 entries += add_sgt_matches_xml(args)
79 args = esgt.split(',')
80 entries += add_sgt_range_xml(args[0], args[1])
82 entries += add_pl_entry_xml(pl)
85 entries += add_epl_entry_xml(args[0], args[1], args[2])
88 entries += add_acl_entry_xml(args[0], args[1])
90 args = eacl.split(',')
91 entries += add_eacl_entry_xml(args[0], args[1], args[2], args[3])
94 entries += add_ps_entry_xml(args[0], args[1])
95 # Wrap entries in ACL/PrefixList according to specified values
97 return add_pl_entry_default_xml(seq, entry_type, entries)
99 return add_ps_entry_default_xml(seq, entry_type, entries)
100 return add_acl_entry_default_xml(seq, entry_type, entries)
103 def add_peers(*args):
104 """Generate xml containing Peer mach data
106 :param args: Peers data
108 :returns: String containing xml data for request
113 <peer-address>$ip</peer-address>
116 for count, value in enumerate(args):
117 peers += templ.substitute({'ip': value})
121 def add_sgt_matches_xml(sgt_entries):
122 """Generate xml containing SGT mach data
124 :param sgt_entries: SGT matches
125 :type sgt_entries: string
126 :returns: String containing xml data for request
130 <matches>$sgt</matches>''')
132 for sgt in sgt_entries:
133 matches += templ.substitute({'sgt': sgt})
137 def add_sgt_range_xml(start, end):
138 """Generate xml containing SGT RangeMach data
140 :param start: Start range of SGT
142 :param end: End range of SGT
144 :returns: String containing xml data for request
148 <sgt-start>$start</sgt-start>
149 <sgt-end>$end</sgt-end>''')
150 match = templ.substitute({'start': start, 'end': end})
154 def add_acl_entry_default_xml(seq, entry_type, acl_entries):
155 """Generate xml containing AccessList data
157 :param seq: Sequence of PrefixList entry
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
168 <entry-type>$entry_type</entry-type>
169 <entry-seq>$seq</entry-seq>$acl_entries
171 matches = templ.substitute(
172 {'seq': seq, 'entry_type': entry_type, 'acl_entries': acl_entries})
176 def add_acl_entry_xml(ip, mask):
177 """Generate xml containing AccessList data
179 :param ip: Ipv4/6 address
181 :param mask: Ipv4/6 wildcard mask
183 :returns: String containing xml data for request
188 <ip-address>$ip</ip-address>
189 <wildcard-mask>$mask</wildcard-mask>
191 return templ.substitute({'ip': ip, 'mask': mask})
194 def add_eacl_entry_xml(ip, mask, amask, wmask):
195 """Generate xml containing ExtendedAccessList data
197 :param ip: Ipv4/6 address
199 :param mask: Ipv4/6 wildcard mask
201 :param amask: Ipv4/6 address mask
203 :param wmask: Ipv4/6 address wildcard mask
205 :returns: String containing xml data for request
210 <ip-address>$ip</ip-address>
211 <wildcard-mask>$mask</wildcard-mask>
213 <address-mask>$amask</address-mask>
214 <wildcard-mask>$wmask</wildcard-mask>
217 return templ.substitute({'ip': ip, 'mask': mask, 'amask': amask, 'wmask': wmask})
220 def add_ps_entry_default_xml(seq, entry_type, ps_entries):
221 """Generate xml containing PeerSequence data
223 :param seq: Sequence of PrefixList entry
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
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})
240 def add_pl_entry_default_xml(seq, entry_type, pl_entries):
241 """Generate xml containing PrefixList data
243 :param seq: Sequence of PrefixList entry
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
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})
260 def add_pl_entry_xml(prefix):
261 """Generate xml containing PrefixList data
263 :param prefix: Ipv4/6 prefix
265 :returns: String containing xml data for request
270 <ip-prefix>$prefix</ip-prefix>
271 </prefix-list-match>''')
272 return templ.substitute({'prefix': prefix})
275 def add_epl_entry_xml(prefix, op, mask):
276 """Generate xml containing Extended PrefixList data
278 :param prefix: Ipv4/6 prefix
280 :param op: PrefixList option (ge/le/eq)
282 :param mask: Ipv4/6 Mask
284 :returns: String containing xml data for request
289 <ip-prefix>$prefix</ip-prefix>
291 <mask-range>$op</mask-range>
292 <mask-value>$mask</mask-value>
294 </prefix-list-match>''')
295 return templ.substitute({'prefix': prefix, 'mask': mask, 'op': op})
298 def add_ps_entry_xml(op, length):
299 """Generate xml containing Extended PrefixList data
301 :param op: PrefixList option (ge/le/eq)
303 :param length: PeerSequence length
305 :returns: String containing xml data for request
309 <peer-sequence-length>$length</peer-sequence-length>
310 <peer-sequence-range>$op</peer-sequence-range>
312 return templ.substitute({'length': length, 'op': op})
315 def parse_peer_groups(groups_json):
316 """Parse JSON string into Array of PeerGroups
318 :param groups_json: JSON containing PeerGroups
319 :type groups_json: string
320 :returns: Array containing PeerGroups.
323 data = json.loads(groups_json)
324 groups = data['output']
326 for group in groups.values():
331 def parse_connections(connections_json):
332 """Parse JSON string into Array of Connections
334 :param connections_json: JSON containing Connections
335 :type connections_json: string
336 :returns: Array containing Connections.
339 data = json.loads(connections_json)
340 connections = data['output']['connections']
342 for connection in connections.values():
347 def find_connection(connections_json, version, mode, ip, port, state):
348 """Test if Connection with specified values is contained in JSON
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)
356 :param ip: Ipv4/6 address of remote peer
358 :param port: Port on with remote peer listens
360 :param state: State of connection (on/off/pendingOn/deleteHoldDown)
362 :returns: True if Connection with specified params was found, otherwise False.
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):
370 elif connection['state'] == state:
375 def parse_bindings(bindings_json):
376 """Parse JSON string into Array of Bindings
378 :param bindings_json: JSON containing Bindings
379 :type bindings_json: string
380 :returns: Array containing Bindings.
383 data = json.loads(bindings_json)
385 for bindings_json in data['output'].values():
386 for binding in bindings_json:
387 output.append(binding)
391 def find_binding(bindings, sgt, prefix):
392 """Test if Binding with specified values is contained in JSON
394 :param bindings: JSON containing Bindings
395 :type bindings: string
396 :param sgt: Source Group Tag
398 :param prefix: Ipv4/6 prefix
400 :returns: True if Binding with specified params was found, otherwise False.
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:
411 def parse_prefix_groups(prefix_groups_json, source_):
412 """Parse JSON string into Array of PrefixGroups
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.
421 data = json.loads(prefix_groups_json)
422 bindings = data['sxp-node:master-database']
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)
432 def find_binding_legacy(prefix_groups_json, sgt, prefix, source_, action):
433 """Test if Binding with specified values is contained in JSON
435 :param prefix_groups_json: JSON containing Bindings and PrefixGroups
436 :type prefix_groups_json: string
437 :param sgt: Source Group Tag
439 :param prefix: Ipv4/6 prefix
441 :param source_: Source of binding (local/sxp)
442 :type source_: string
443 :param action: Action for binding (add/delete)
445 :returns: True if Binding with specified params was found, otherwise 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:
457 def add_entry_xml(sgt, prefix, ip, domain_name):
458 """Generate xml for Add Bindings request
460 :param sgt: Source Group Tag
462 :param prefix: Ipv4/6 prefix
464 :param ip: Ipv4 address of node
466 :param domain_name: Name of Domain
467 :type domain_name: string
468 :returns: String containing xml data for request
471 templ = Template('''<input>
472 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
474 <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
475 <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
477 data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip, 'domain': get_domain_name(domain_name)})
481 def add_connection_xml(version, mode, ip, port, node, password_, domain_name):
482 """Generate xml for Add Connection request
484 :param version: Version of SXP protocol (version1/2/3/4)
485 :type version: string
486 :param mode: Mode of SXP peer (speaker/listener/both)
488 :param ip: Ipv4/6 address of remote peer
490 :param port: Port on with remote peer listens
492 :param node: Ipv4 address of node
494 :param password_: Password type (none/default)
495 :type password_: string
496 :param domain_name: Name of Domain
497 :type domain_name: string
498 :returns: String containing xml data for request
501 templ = Template('''<input>
502 <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
504 <connections xmlns="urn:opendaylight:sxp:controller">
506 <peer-address>$ip</peer-address>
507 <tcp-port>$port</tcp-port>
508 <password>$password_</password>
510 <version>$version</version>
511 <description>Connection to ISR-G2</description>
513 <hold-time-min-acceptable>45</hold-time-min-acceptable>
514 <keep-alive-time>30</keep-alive-time>
515 <reconciliation-time>120</reconciliation-time>
521 data = templ.substitute(
522 {'ip': ip, 'port': port, 'mode': mode, 'version': version, 'node': node,
523 'password_': password_, 'domain': get_domain_name(domain_name)})
527 def delete_connections_xml(address, port, node, domain_name):
528 """Generate xml for Delete Connection request
530 :param address: Ipv4/6 address of remote peer
531 :type address: string
532 :param port: Port on with remote peer listens
534 :param node: Ipv4 address of node
536 :param domain_name: Name of Domain
537 :type domain_name: string
538 :returns: String containing xml data for request
541 templ = Template('''<input>
542 <requested-node xmlns="urn:opendaylight:sxp:controller">$node</requested-node>
544 <peer-address xmlns="urn:opendaylight:sxp:controller">$address</peer-address>
545 <tcp-port xmlns="urn:opendaylight:sxp:controller">$port</tcp-port>
547 data = templ.substitute({'address': address, 'port': port, 'node': node, 'domain': get_domain_name(domain_name)})
551 def update_binding_xml(sgt0, prefix0, sgt1, prefix1, ip, domain_name):
552 """Generate xml for Update Binding request
554 :param sgt0: Original Source Group Tag
556 :param prefix0: Original Ipv4/6 prefix
557 :type prefix0: string
558 :param sgt1: New Source Group Tag
560 :param prefix1: New Ipv4/6 prefix
561 :type prefix1: string
562 :param ip: Ipv4 address of node
564 :param domain_name: Name of Domain
565 :type domain_name: string
566 :returns: String containing xml data for request
569 templ = Template('''<input>
570 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
572 <original-binding xmlns="urn:opendaylight:sxp:controller">
574 <ip-prefix>$prefix0</ip-prefix>
576 <new-binding xmlns="urn:opendaylight:sxp:controller">
578 <ip-prefix>$prefix1</ip-prefix>
581 data = templ.substitute(
582 {'sgt0': sgt0, 'sgt1': sgt1, 'prefix0': prefix0, 'prefix1': prefix1, 'ip': ip,
583 'domain': get_domain_name(domain_name)})
587 def delete_binding_xml(sgt, prefix, ip, domain_name):
588 """Generate xml for Delete Binding request
590 :param sgt: Source Group Tag
592 :param prefix: Ipv4/6 prefix
594 :param ip: Ipv4 address of node
596 :param domain_name: Name of Domain
597 :type domain_name: string
598 :returns: String containing xml data for request
601 templ = Template('''<input>
602 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
603 <sgt xmlns="urn:opendaylight:sxp:controller">$sgt</sgt>
604 <ip-prefix xmlns="urn:opendaylight:sxp:controller">$prefix</ip-prefix>
607 data = templ.substitute({'sgt': sgt, 'prefix': prefix, 'ip': ip, 'domain': get_domain_name(domain_name)})
611 def add_peer_group_xml(name, peers, ip):
612 """Generate xml for Add PeerGroups request
614 :param name: Name of PeerGroup
616 :param peers: XML formatted peers that will be added to group
618 :param ip: Ipv4 address of node
620 :returns: String containing xml data for request
623 templ = Template('''<input>
624 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
625 <sxp-peer-group xmlns="urn:opendaylight:sxp:controller">
626 <name xmlns="urn:opendaylight:sxp:controller">$name</name>
627 <sxp-peers xmlns="urn:opendaylight:sxp:controller">$peers</sxp-peers>
630 data = templ.substitute({'name': name, 'peers': peers, 'ip': ip})
634 def delete_peer_group_xml(name, ip):
635 """Generate xml for Delete PeerGroup request
637 :param name: Name of PeerGroup
639 :param ip: Ipv4 address of node
641 :returns: String containing xml data for request
644 templ = Template('''<input>
645 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
646 <peer-group-name xmlns="urn:opendaylight:sxp:controller">$name</peer-group-name>
648 data = templ.substitute({'name': name, 'ip': ip})
652 def get_peer_groups_from_node_xml(ip):
653 """Generate xml for Get PeerGroups request
655 :param ip: Ipv4 address of node
657 :returns: String containing xml data for request
660 templ = Template('''<input>
661 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
663 data = templ.substitute({'ip': ip})
667 def add_filter_xml(group, filter_type, entries, ip):
668 """Generate xml for Add Filter request
670 :param group: Name of group containing filter
672 :param filter_type: Type of filter
673 :type filter_type: string
674 :param entries: XML formatted entries that will be added in filter
675 :type entries: string
676 :param ip: Ipv4 address of node
678 :returns: String containing xml data for request
682 templ = Template('''<input>
683 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
684 <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
685 <sxp-filter xmlns="urn:opendaylight:sxp:controller">
686 <filter-type>$filter_type</filter-type>$entries
689 data = templ.substitute(
690 {'group': group, 'filter_type': filter_type, 'ip': ip, 'entries': entries})
694 def delete_filter_xml(group, filter_type, ip):
695 """Generate xml for Delete Filter request
697 :param group: Name of group containing filter
699 :param filter_type: Type of filter
700 :type filter_type: string
701 :param ip: Ipv4 address of node
703 :returns: String containing xml data for request
706 templ = Template('''<input>
707 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
708 <peer-group-name xmlns="urn:opendaylight:sxp:controller">$group</peer-group-name>
709 <filter-type xmlns="urn:opendaylight:sxp:controller">$filter_type</filter-type>
711 data = templ.substitute(
712 {'group': group, 'filter_type': filter_type, 'ip': ip})
716 def get_connections_from_node_xml(ip, domain_name):
717 """Generate xml for Get Connections request
719 :param ip: Ipv4 address of node
721 :param domain_name: Name of Domain
722 :type domain_name: string
723 :returns: String containing xml data for request
726 templ = Template('''<input>
727 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
730 data = templ.substitute({'ip': ip, 'domain': get_domain_name(domain_name)})
734 def get_bindings_from_node_xml(ip, binding_range, domain_name):
735 """Generate xml for Get Bindings request
737 :param binding_range: All or only Local bindings
738 :type binding_range: string
739 :param ip: Ipv4 address of node
741 :param domain_name: Name of Domain
742 :type domain_name: string
743 :returns: String containing xml data for request
746 templ = Template('''<input>
747 <requested-node xmlns="urn:opendaylight:sxp:controller">$ip</requested-node>
748 <bindings-range xmlns="urn:opendaylight:sxp:controller">$range</bindings-range>
751 data = templ.substitute({'ip': ip, 'range': binding_range, 'domain': get_domain_name(domain_name)})
755 def add_node_xml(node_id, port, password, version, node_ip=None, expansion=0):
756 """Generate xml for Add Node request
758 :param node_id: Ipv4 address formatted node id
759 :type node_id: string
760 :param node_ip: Ipv4 address of node
761 :type node_ip: string
762 :param port: Node port number
764 :param expansion: Bindings expansion
766 :returns: String containing xml data for request
771 templ = Template('''<input xmlns="urn:opendaylight:sxp:controller">
772 <node-id>$id</node-id>
774 <retry-open-time>1</retry-open-time>
775 <hold-time-min-acceptable>120</hold-time-min-acceptable>
776 <delete-hold-down-time>120</delete-hold-down-time>
777 <hold-time-min>90</hold-time-min>
778 <reconciliation-time>120</reconciliation-time>
779 <hold-time>90</hold-time>
780 <hold-time-max>180</hold-time-max>
781 <keep-alive-time>30</keep-alive-time>
783 <mapping-expanded>$expansion</mapping-expanded>
785 <password>$password</password>
787 <tcp-port>$port</tcp-port>
788 <version>$version</version>
789 <description>ODL SXP Controller</description>
790 <source-ip>$ip</source-ip>
791 <master-database></master-database>
793 data = templ.substitute(
794 {'ip': node_ip, 'id': node_id, 'port': port, 'password': password, 'version': version, 'expansion': expansion})
798 def delete_node_xml(node_id):
799 """Generate xml for Delete node request
801 :param node_id: Ipv4 address formatted node id
802 :type node_id: string
803 :returns: String containing xml data for request
806 templ = Template('''<input xmlns="urn:opendaylight:sxp:controller">
807 <node-id>$id</node-id>
809 data = templ.substitute({'id': node_id})
813 def add_domain_xml(node_id, name):
814 """Generate xml for Add Domain request
816 :param node_id: Id of node
817 :type node_id: string
818 :param name: Name of Domain
820 :returns: String containing xml data for request
823 templ = Template('''<input>
824 <node-id xmlns="urn:opendaylight:sxp:controller">$id</node-id>
825 <domain-name xmlns="urn:opendaylight:sxp:controller">$name</domain-name>
827 data = templ.substitute({'name': name, 'id': node_id})
831 def delete_domain_xml(node_id, name):
832 """Generate xml for Remove Domain request
834 :param node_id: Id of node
835 :type node_id: string
836 :param name: Name of Domain
838 :returns: String containing xml data for request
841 return add_domain_xml(name, node_id)
844 def get_domain_name(domain_name):
845 """Generate xml for Get Bindings request
847 :param domain_name: Name of Domain
848 :type domain_name: string
849 :returns: String containing xml data for request
852 if domain_name == 'global':
855 return '<domain-name xmlns="urn:opendaylight:sxp:controller">' + domain_name + '</domain-name>'
858 def add_bindings_xml(node_id, domain, sgt, prefixes):
859 """Generate xml for Add Bindings request
861 :param node_id: Id of node
862 :type node_id: string
863 :param domain: Name of Domain
865 :param sgt: Security group
867 :param prefixes: List of ip-prefixes
868 :type prefixes: string
869 :returns: String containing xml data for request
873 for prefix in prefixes.split(','):
874 bindings += '\n' + '<ip-prefix>' + prefix + '</ip-prefix>'
875 templ = Template('''<input>
876 <node-id xmlns="urn:opendaylight:sxp:controller">$id</node-id>
877 <domain-name xmlns="urn:opendaylight:sxp:controller">$name</domain-name>
878 <binding xmlns="urn:opendaylight:sxp:controller">
883 data = templ.substitute({'name': domain, 'id': node_id, 'sgt': sgt, 'bindings': bindings})
887 def delete_bindings_xml(node_id, domain, sgt, prefixes):
888 """Generate xml for Remove Bindings request
890 :param node_id: Id of node
891 :type node_id: string
892 :param domain: Name of Domain
894 :param sgt: Security group
896 :param prefixes: List of ip-prefixes
897 :type prefixes: string
898 :returns: String containing xml data for request
901 return add_bindings_xml(node_id, domain, sgt, prefixes)
904 def prefix_range(start, end):
905 """Generate and concatenate ip-prefixes
907 :param start: Start index
909 :param end: End index
911 :returns: String containing concatenated ip-prefixes
919 prefixes += get_ip_from_number(start + index) + '/32'