| 685 | [[Image(topo2.png)]] |
| 686 | |
| 687 | The code for this is below: |
| 688 | |
| 689 | {{{ |
| 690 | def add_links(top): |
| 691 | ''' |
| 692 | For each router - Computer with FreeBSD assigned as an OS - select one of |
| 693 | its Linux children and make a connection to one of the other router's |
| 694 | children. |
| 695 | ''' |
| 696 | |
| 697 | def has_os(e, os_name): |
| 698 | ''' |
| 699 | Return true if any of e's os classes has the given os_name |
| 700 | ''' |
| 701 | if e.os is None: return False |
| 702 | else: return any([ o.name == os_name for o in e.os ]) |
| 703 | |
| 704 | # At the end of this loop, leaves contains a list of leaves for each router |
| 705 | # in the topology. |
| 706 | leaves = [ ] |
| 707 | for e in top.elements: |
| 708 | if not isinstance(e, topdl.Computer): continue |
| 709 | if not has_os(e, 'FreeBSD'): continue |
| 710 | # This is a Computer running FreeBsd |
| 711 | my_leaves = [] |
| 712 | # walk the interfaces |
| 713 | for inf in e.interface: |
| 714 | # Walk the interface's substrates |
| 715 | for s in inf.subs: |
| 716 | # Walk the substrate's interfaces looking for the other |
| 717 | # elements - not this router. |
| 718 | for sinf in s.interfaces: |
| 719 | ee = sinf.element |
| 720 | # all the Linux computers get attached to the leaves dict |
| 721 | if isinstance(ee, topdl.Computer) and ee != e: |
| 722 | if has_os(ee, 'Linux'): |
| 723 | my_leaves.append(ee) |
| 724 | # One router has no leaves. Ignore it. |
| 725 | if len(my_leaves) > 0: |
| 726 | leaves.append(my_leaves) |
| 727 | |
| 728 | |
| 729 | # This loop takes the first computer in one entry in leaves and connects it |
| 730 | # to the last computer in the next entry in leaves (wrapping around at the |
| 731 | # top). |
| 732 | for i in range(0, len(leaves)): |
| 733 | link_name = 'new-link%d' % i |
| 734 | f = leaves[i][0] |
| 735 | t = leaves[(i+1) % len(leaves)][-1] |
| 736 | f.interface.append(topdl.Interface(substrate=[link_name])) |
| 737 | t.interface.append(topdl.Interface(substrate=[link_name])) |
| 738 | top.substrates.append(topdl.Substrate(name=link_name)) |
| 739 | |
| 740 | # Now incorporate the new links |
| 741 | top.incorporate_elements() |
| 742 | return top |
| 743 | }}} |
| 744 | |
| 745 | This code demonstrates the basic loop that walks through a topology, looking at neighbors of elements. To look at an element's neighbors, code must walk the element's interfaces to find the substrates it is connected to. Then for each substrate, the other elements having an interface on the substrate are examined. |
| 746 | |
| 747 | The {{{has_os}}} nested function is typical of this sort of loop. It encapsulates a bit of complexity - addressing the possibility that a Computer has more than one OS - in a side function. In most cases that complexity is not used, but the snippet covers the possibility. |
| 748 | |
| 749 | It is also important to note the {{{Topology.incorporate_elements}}} call near the end. That call completes the connections between interface objects and substrate objects (filling in the {{{Interface.subs}}} members and {{{Substrate.intrefaces}}} member) and between Interfaces and elements (by filling in the {{{Interface.element}}} member). Without that call further use of the topology is difficult. That call will also throw exceptions if the changes have introduced inconsistencies - multiple substrates with the same name, etc. |
| 750 | |
| 751 | == Output to NS2 == |
| 752 | |
| 753 | Finally, we want to output out example topology in ns2 format so that we can directly swap it in to DETER. Of course, fedd and the containers system will take (and prefer) topdl, but as an example, we show outputting ns2. |
| 754 | |
| 755 | This shows the use of a filter to tweak ns2 output for local requirements. The {{{os_filter}}} function defined in {{{output_ns}}} below converts a single [TopdlLibrary#OperatingSystemClass OperatingSystem object] attached to a computer into a local DETER OSID. Notice that the filter always returns a string - the empty one when no changes are made. Do not return None from a filter. |
| 756 | |
| 757 | Here is the output code: |
| 758 | |
| 759 | {{{ |
| 760 | def output_ns(top, filename): |
| 761 | ''' |
| 762 | Output ns2 with the OperatingSystem specifications converted to local OSIDs |
| 763 | when known. |
| 764 | ''' |
| 765 | def os_filter(e): |
| 766 | ''' |
| 767 | Convert OS id to local equivalents: |
| 768 | FreeBSD -> FBSD8-STD |
| 769 | Lunix/Ubuntu -> Ubuntu-64-STD |
| 770 | ''' |
| 771 | # Only work on Computers |
| 772 | if not isinstance(e, topdl.Computer): return '' |
| 773 | # Only handle exactly 1 specified OS |
| 774 | if len(e.os) != 1 : return '' |
| 775 | # String translation |
| 776 | if e.os[0].name == 'FreeBSD': |
| 777 | return 'tb-set-node-os ${%s} FBSD8-STD\n' % \ |
| 778 | topdl.to_tcl_name(e.name) |
| 779 | elif e.os[0].name == 'Linux' and e.os[0].distribution == 'Ubuntu': |
| 780 | return 'tb-set-node-os ${%s} Ubuntu1204-64-STD\n' % \ |
| 781 | topdl.to_tcl_name(e.name) |
| 782 | else: |
| 783 | return '' |
| 784 | |
| 785 | # Main line. Open the file and write the otcl to it. |
| 786 | try: |
| 787 | f = open(filename, 'w') |
| 788 | # Include the os_filter |
| 789 | f.write(topdl.topology_to_ns2(top, |
| 790 | filters=[os_filter], routing='Static')) |
| 791 | f.close() |
| 792 | except EnvironmentError, e: |
| 793 | sys.exit('Cannot write %s: %s' % (e.filename, e.strerror)) |
| 794 | |
| 795 | }}} |
| 796 | |
| 797 | == Putting it all together == |