Fat Tree Topology

[Description]

   Please refer to this paper for fat tree topology used in the data center. In this lab, I will mainly show how to write the script for fat tree topology in the mininet environment.

Also, I will show a fast way that allows hosts can ping each other. As we know that when the topology becomes bigger, the more time is needed for ARP operation. So it takes time for hosts to ping each other. In order to speed up the evaluation, we can make some assumptions. The hosts are fixed in the data center. So when the hosts are connected to the switches, they won’t change. Therefore, we can use “arp –s” to add the static mapping records of IP to MAC for all hosts in each host’s ARP cache. We can also tell the controller that the switch port number that the host is connected to. With this information, the ARP operations can be omitted. Consequently, it will save a lot of time.

 

[mininet script for fat tree topology with k=4, test_fat_tree.py]

from mininet.topo import Topo

from mininet.net import Mininet

from mininet.node import CPULimitedHost

from mininet.link import TCLink

from mininet.util import dumpNodeConnections

from mininet.log import setLogLevel

from mininet.node import RemoteController

from mininet.cli import CLI

from threading import Thread

import threading

import time

import os

import random

from subprocess import Popen

import socket

 

CoreSwitchList = []

AggSwitchList = []

EdgeSwitchList = []

HostList = []

 

def topology(k):

  net = Mininet(host=CPULimitedHost, link=TCLink, controller = RemoteController)

  c1 = net.addController('c1',controller=RemoteController,ip='127.0.0.1',port = 6633)  

  POD = k

  pod = POD

  end = pod/2

  iCoreLayerSwitch = (k/2)**2

  iAggLayerSwitch = k*(k/2)

  iEdgeLayerSwitch = k*(k/2)

  iHost = iEdgeLayerSwitch * (k/2)

  SCount = 0

  for x in range(1, pod*(pod/2)+1):

    PREFIX = "s"

    EdgeSwitchList.append(net.addSwitch(PREFIX + str(x)))

    SCount = SCount+1    

    print "ESwitch[",SCount,"]"

 

  for x in range(SCount+1,SCount+pod*(pod/2)+1):

    PREFIX = "s"

    AggSwitchList.append(net.addSwitch(PREFIX + str(x)))

    SCount = SCount+1 

    print "ASwitch[",SCount,"]"

 

  for x in range(SCount+1,SCount+((pod/2)**2)+1):

    PREFIX = "s"

    CoreSwitchList.append(net.addSwitch(PREFIX + str(x)))

    SCount = SCount+1 

    print "CSwitch[",SCount,"]"

 

  f1 = open('/home/ubuntu/pyretic/pyretic/tutorial/f1.txt', 'w')

  count = 0

  digit2 = 0

  digit3 = 0

  for a in range(0,pod):

    for b in range(0,pod/2):

      for c in range(2,2+(pod/2)):

        count = count+1

        digit2 = count/100

        digit3 = count/10000 

        PREFIX = "h"

        #print "digit2:",digit2

        #print "digit3:",digit3

        #print "count:",count

        print "host ip:","10."+str(a)+"."+str(b)+"."+str(c)

        print "host mac:","00:00:00:"+str(digit3%100).zfill(2)+":"+str(digit2%100).zfill(2)+":"+str(count%100).zfill(2)

        f1.write(PREFIX + str(count) + " " + "00:00:00:"+str(digit3%100).zfill(2)+":"+str(digit2%100).zfill(2)+":"+str(count%100).zfill(2)+"\n")

        HostList.append(net.addHost(PREFIX + str(count),ip="10."+str(a)+"."+str(b)+"."+str(c),mac="00:00:00:"+str(digit3%100).zfill(2)+":"+str(digit2%100).zfill(2)+":"+str(count%100).zfill(2)))

  f1.close() 

  f2=open('/home/ubuntu/pyretic/pyretic/tutorial/f2.txt', 'w')

  for x in range(0, iEdgeLayerSwitch):

    for y in range(0,end):

      net.addLink(EdgeSwitchList[x], HostList[end*x+y],bw=10)

      f2.write(str(HostList[end*x+y]) + " " + str(EdgeSwitchList[x])[1] + " " + str(y+1) +"\n")

  f2.close()

 

  print "iAggLayerSwitch=",iAggLayerSwitch

  for x in range(0, iAggLayerSwitch):

    for y in range(0,end):

      net.addLink(AggSwitchList[x], EdgeSwitchList[end*(x/end)+y], bw=10)

 

 

  for x in range(0, iAggLayerSwitch, end):

    for y in range(0,end):

      for z in range(0,end):

        net.addLink(CoreSwitchList[y*end+z], AggSwitchList[x+y], bw=10)

 

  print "*** Starting network"

  net.build()

  c1.start()

  for sw in EdgeSwitchList:

    sw.start([c1])

  for sw in AggSwitchList:

    sw.start([c1])

  for sw in CoreSwitchList:

    sw.start([c1])

 

  print "Dumpling host connections"

  dumpNodeConnections(net.hosts)

 

  #use arp -s to add static mapping of MAC to IP in each host

  print "len(HostList):",len(HostList)

  for x in HostList:

    for y in HostList:

      if x!=y:

        y.cmd('arp -s '+x.IP()+' '+x.MAC())

 

  net.hosts[0].cmd("join /home/ubuntu/pyretic/pyretic/tutorial/f1.txt /home/ubuntu/pyretic/pyretic/tutorial/f2.txt > /home/ubuntu/pyretic/pyretic/tutorial/f3.txt")

  CLI(net)

  net.stop()

 

if __name__ == '__main__':

  #setLogLevel( 'info' )

  topology(4) 

 

[pyretic controller: myroute_dijkstra_fat_tree.py, please put this file under ~/pyretic/pyretic/tutorial]

from pyretic.lib.corelib import*

from pyretic.lib.std import *

from multiprocessing import Lock

from pyretic.lib.query import *

from collections import defaultdict

import os

 

#switches

switches = []

 

#myhost[srcmac]->(switch, port)

myhost={}

 

#adjacency map [sw1][sw2]->port from sw1 to sw2

adjacency=defaultdict(lambda:defaultdict(lambda:None))

 

def minimum_distance(distance, Q):

  min = float('Inf')

  node = 0

  for v in Q:

    if distance[v] < min:

      min = distance[v]

      node = v

  return node

 

def get_path (src,dst,first_port,final_port):

  #Dijkstra's algorithm

  print "src=",src," dst=",dst, " first_port=", first_port, " final_port=", final_port

  distance = {}

  previous = {}

  for dpid in switches:

    distance[dpid] = float('Inf')

    previous[dpid] = None

 

  distance[src]=0

  Q=set(switches)

 

  while len(Q)>0:

    u = minimum_distance(distance, Q)

    Q.remove(u)

 

    for p in switches:

      if adjacency[u][p]!=None:

        w = 1

        if distance[u] + w < distance[p]:

          distance[p] = distance[u] + w

          previous[p] = u

  r=[]

  p=dst

  r.append(p)

  q=previous[p]

  while q is not None:

    if q == src:

      r.append(q)

      break

    p=q

    r.append(p)

    q=previous[p]

  r.reverse()

  if src==dst:

    path=[src]

  else:

    path=r

 

  # Now add the ports

  r = []

  in_port = first_port

  for s1,s2 in zip(path[:-1],path[1:]):

    out_port = adjacency[s1][s2]

    r.append((s1,in_port,out_port))

    in_port = adjacency[s2][s1]

  r.append((dst,in_port,final_port))

  return r

 

class find_route(DynamicPolicy):

  def __init__(self):

    super(find_route,self).__init__()

    self.flood = flood()

    self.set_initial_state()

 

  def set_initial_state(self):

    self.query = packets(1,['srcmac','dstmac', 'srcip', 'dstip'])

    self.query.register_callback(self.myroute)

    self.forward = self.flood

    self.update_policy()

 

  def set_network(self,network):

    self.set_initial_state()

 

  def update_policy(self):

    self.policy = self.forward + self.query

 

  def myroute(self,pkt):

    print "In myroute()->",pkt['srcmac'], pkt['dstmac'], pkt['srcip'], pkt['dstip']

    if ( str(pkt['srcmac']) not in myhost.keys() ) or (str(pkt['dstmac']) not in myhost.keys()):

      return

 

    #if match(ethtype=IP_TYPE):

    #  print "ipv4 packet"

 

    p1 = get_path(myhost[str(pkt['srcmac'])][0], myhost[str(pkt['dstmac'])][0],myhost[str(pkt['srcmac'])][1], myhost[str(pkt['dstmac'])][1])

    print p1

 

    r1 = parallel([(match(switch=a,srcip=pkt['srcip'],dstip=pkt['dstip']) >> fwd(c)) for a,b,c in p1])

    #print r1

 

    self.forward = if_(match(dstip=pkt['dstip'],srcip=pkt['srcip']),r1,self.forward)

    self.update_policy()

 

class find_switch(DynamicPolicy):

    def __init__(self):

        self.last_topology = None

        self.lock = Lock()

        super(find_switch,self).__init__()

 

    def set_network(self, network):

        with self.lock:

            for x in network.switch_list():

              switches.append(x)

 

            for (s1,s2,data) in network.topology.edges(data=True):

              adjacency[s1][s2]=data[s1]

              adjacency[s2][s1]=data[s2]

 

            self.last_topology = network.topology

 

def arp_and_ip():

  global myhost

  fin = open("/home/ubuntu/pyretic/pyretic/tutorial/f3.txt", "r")

  for line in fin:

    a=line.split()

    myhost[str(a[1])]=(int(a[2]), int(a[3]))

  fin.close()

  #print "myhost=", myhost

  #We don't need ARP packet, so we drop the ARP packets.

  return if_(match(ethtype=ARP_TYPE), drop, find_route())

   

def main():

  return ( find_switch() + arp_and_ip())

 

[Execution]

 

Open another window. Open the f3.txt and we can see that the content of f3 is for the controller. The first column shows the host, the second is MAC address, the third is switch’s dpid, and fourth is the port number.

h1 ping h16. Don’t need to spend time that we can see the following results. Without this method, it will take a very long time that h1 can ping h16.

 

Reference

成功大學資訊工程學系梁文宣碩士論文

 

Dr. Chih-Heng Ke (smallko@gmail.com)

Department of Computer Science and Information Engineering,

National Quemoy University, Kinmen, Taiwan.