Contents

Python-第三方库-mininet-Mininet简介

mininet官网

mininet官网文档

mininet官网api

mininet简介 github

什么是迷你网?

Mininet 是一个网络仿真器,或者更准确地说是一个 网络仿真编排系统。

为什么 Mininet 很酷?

  1. 它很快——启动一个简单的网络只需几秒钟。
  2. 可以创建自定义拓扑:单个交换机、更大的类似 Internet 的拓扑、斯坦福骨干网、数据中心或其他任何东西。
  3. 可以运行真正的程序:任何在 Linux 上运行的程序都可以运行,从 Web 服务器到 TCP 窗口监控工具再到 Wireshark。
  4. 可以自定义数据包转发:Mininet 的交换机可以使用 OpenFlow 协议进行编程。在 Mininet 中运行的自定义软件定义网络设计可以轻松转移到硬件 OpenFlow 交换机以进行线速数据包转发。
  5. 可以在笔记本电脑、服务器、虚拟机、本地 Linux 机器(Ubuntu 12.10+ 中包含 Mininet!)或云(例如 Amazon EC2)上运行 Mininet。
  6. 可以共享代码和复现结果。
  7. 可以通过编写简单(或必要时复杂)Python 脚本来创建和运行 Mininet 实验。

Mininet 的局限性是什么?

尽管我们认为 Mininet 很棒,但它确实有一些局限性。例如,

  • 在单个系统上运行很方便,但它施加了资源限制: CPU 和 带宽 将需要在您的虚拟主机和交换机之间进行平衡和共享。
  • Mininet 为所有虚拟主机使用单个 Linux 内核;不能运行依赖于 BSD、Windows 或其他操作系统内核的软件。
  • Mininet 不会为您编写 OpenFlow 控制器;如果您需要自定义路由器或交换机行为,则需要查找或开发具有所需功能的控制器。
  • 默认情况下,Mininet 网络与您的 LAN 和 Internet 隔离。可以使用 NAT对象和/或–nat选项通过网络地址转换将 Mininet 网络连接到 LAN。可以将真实(或虚拟)硬件接口附加到您的 Mininet 网络。
  • 默认情况下,所有 Mininet 主机共享主机文件系统和 PID 空间;如果正在运行需要在 /etc 中配置的守护进程,必须小心不要错误地杀死错误的进程。
  • 与模拟器不同,Mininet 没有强烈的虚拟时间概念;时序测量将基于实时,并且无法轻易模拟比实时更快的结果(例如 100 Gbps 网络)。

关于性能的旁白:对于网络受限的实验,您必须牢记的主要事情是您可能需要使用较慢的链接,例如 10 或 100 Mb/秒而不是 10 Gb/秒,因为数据包由一组共享 CPU 和内存资源的软件交换机(例如 Open vSwitch)转发,通常性能低于专用交换硬件。对于 CPU 限制的实验,您还需要确保仔细限制 Mininet 主机的 CPU 带宽。如果您主要关心功能的正确性,则可以在没有特定带宽限制的情况下运行 Mininet - 这是运行 Mininet 的快速简便的方法,并且它还以牺牲负载下的计时精度为代价提供最高的性能。

使用 Mininet

创建拓扑

Mininet 支持参数化拓扑,根据您传递给它的参数进行配置,并可重复用于多个实验。

例如,这是一个简单的网络拓扑(基于mininet/topo.py:SingleSwitchTopo),它由连接到单个交换机(s1)的指定数量的主机(h1通过hN)组成。

请注意,这是 Mininet 2.2 中引入的推荐(简化)拓扑语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel

class SingleSwitchTopo(Topo):
    "Single switch connected to n hosts."
    def build(self, n=2):
        switch = self.addSwitch('s1')
        # Python's range(N) generates 0..N-1
        for h in range(n):
            host = self.addHost('h%s' % (h + 1))
            self.addLink(host, switch)

def simpleTest():
    "Create and test a simple network"
    topo = SingleSwitchTopo(n=4)
    net = Mininet(topo)
    net.start()
    print( "Dumping host connections" )
    dumpNodeConnections(net.hosts)
    print( "Testing network connectivity" )
    net.pingAll()
    net.stop()

if __name__ == '__main__':
    # Tell mininet to print useful information
    setLogLevel('info')
    simpleTest()

上述代码中重要的类、方法、函数和变量包括:

  • Topo: Mininet 拓扑的基类
  • build(): 在拓扑类中重写的方法。构造函数参数 (n) 将由Topo.init()自动传递给它。此方法创建一个模板(基本上是一个节点名称图和一个配置信息数据库),然后使用它来创建Mininet实际拓扑。
  • addSwitch(): 添加一个交换机到拓扑并返回交换机名称
  • addHost(): 将主机添加到拓扑并返回主机名
  • addLink():添加双向链接到拓扑(并返回链接键,但这并不重要)。除非另有说明,否则 Mininet 中的链接是双向的。
  • Mininet: 创建和管理网络的主类
  • start(): 启动你的网络
  • pingAll():通过尝试让所有节点相互 ping 来测试连通性
  • stop(): 停止你的网络
  • net.hosts:网络中的所有主机
  • dumpNodeConnections():dumps(打印) connections to/from a set of nodes.
  • setLogLevel( ‘info’ | ‘debug’ | ‘output’ ): 设置 Mininet 的默认输出电平;建议使用“info”,因为它提供了有用的信息。

可以在mininet/examples中找到其他示例代码。

注意:Mininet()和构造函数

Mininet()是创建并返回网络对象的构造函数。Mininet它需要一些配置参数,特别是:

  • topo:拓扑对象(不是构造函数或模板 - 实际对象!)
  • host:用于在拓扑中创建Host元素的构造函数
  • switch:用于在拓扑中创建Switch元素的构造函数
  • controller:用于在拓扑中创建Controller元素的构造函数
  • link: 用于在拓扑中创建 Link s 的构造函数

构造函数可以使用functools.partial生成:

1
2
3
from functools import partial

net = Mininet( topo=SingleSwitchTopo(), link=partial(TCLink,delay='30ms', bw=100) ... )

您还可以创建自己的构造函数或子类。请记住,为拓扑中的每个网络元素调用构造函数。

Mininet 允许传入一个controller 对象或对象列表。

脚本输出

运行此脚本应该会产生类似于以下内容的结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ sudo python simpletest.py
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4 
*** Adding switches:
s1 
*** Adding links:
(h1, s1) (h2, s1) (h3, s1) (h4, s1) 
*** Configuring hosts
h1 h2 h3 h4 
*** Starting controller
c0 
*** Starting 1 switches
s1 ...
Dumping host connections
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
h3 h3-eth0:s1-eth3
h4 h4-eth0:s1-eth4
Testing network connectivity
*** Ping: testing ping reachability
h1 -> h2 h3 h4 
h2 -> h1 h3 h4 
h3 -> h1 h2 h4 
h4 -> h1 h2 h3 
*** Results: 0% dropped (12/12 received)
*** Stopping 1 controllers
c0 
*** Stopping 4 links
....
*** Stopping 1 switches
s1 
*** Stopping 4 hosts
h1 h2 h3 h4 
*** Done

设置性能参数

除了基本的行为网络之外,Mininet 还通过CPULimitedHost和TCLink 类提供性能限制和隔离功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/python

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

class SingleSwitchTopo( Topo ):
    "Single switch connected to n hosts."
    def build( self, n=2 ):
	switch = self.addSwitch( 's1' )
	for h in range(n):
	    # Each host gets 50%/n of system CPU
	    host = self.addHost( 'h%s' % (h + 1),
		                 cpu=.5/n )
	    # 10 Mbps, 5ms delay, 2% loss, 1000 packet queue
	    self.addLink( host, switch, bw=10, delay='5ms', loss=2,
                          max_queue_size=1000, use_htb=True )

def perfTest():
    "Create network and run simple performance test"
    topo = SingleSwitchTopo( n=4 )
    net = Mininet( topo=topo,
	           host=CPULimitedHost, link=TCLink )
    net.start()
    print( "Dumping host connections" )
    dumpNodeConnections( net.hosts )
    print( "Testing network connectivity" )
    net.pingAll()
    print( "Testing bandwidth between h1 and h4" )
    h1, h4 = net.get( 'h1', 'h4' )
    net.iperf( (h1, h4) )
    net.stop()

if __name__ == '__main__':
    setLogLevel( 'info' )
    perfTest()

重要方法和参数:

self.addHost(name, cpu=f):这允许您指定将分配给虚拟主机的整个系统 CPU 资源的一部分。

self.addLink( node1, node2, bw=10, delay=‘5ms’, max_queue_size=1000, loss=10, use_htb=True):添加具有带宽、延迟和丢失特性的双向链接,使用分层令牌桶速率限制器和 netem 延迟/丢失模拟器,队列最大为 1000 个数据包。bw参数是Mbit为单位的数字表示;delay表示为带有单位的字符串(例如“5ms”、“100us”、“1s”);loss以百分比表示(介于 0 和 100 之间);max_queue_size以数据包表示。

使用 Python 字典来复用相同的参数,例如:

1
2
3
linkopts = dict(bw=10, delay='5ms', loss=10, max_queue_size=1000, use_htb=True)
# (or you can use brace syntax: linkopts = {'bw':10, 'delay':'5ms', ... } )
self.addLink(node1, node2, **linkopts)

net.get():按名称检索节点(主机或交换机)对象。可以通过节点对象向主机发送命令(例如使用 host.cmd())并获取其输出。

另外,可以简单地使用中括号(例如net[‘h1’])按名称检索给定的节点。

在主机中运行程序

cmd()方法在主机中运行命令并获取输出。

1
2
3
h1 = net.get('h1')	
result = h1.cmd('ifconfig')
print( result )

在后台运行命令一段时间,然后停止该命令,并将其输出保存到一个文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from time import sleep
...
print( "Starting test..." )
h1.cmd('while true; do date; sleep 1; done > /tmp/date.out &')
sleep(10)
print( "Stopping test" )
h1.cmd('kill %while')
print( "Reading output" )
f = open('/tmp/date.out')
lineno = 1
for line in f.readlines():
    print( "%d: %s" % ( lineno, line.strip() ) )
    lineno += 1
f.close()

使用 shell 的输出重定向功能将输出发送到/tmp/date.out,使用 shell 的后台执行功能在后台&运行命令,并使用作业控制kill %while关闭在后台运行的程序。后台运行的作业不能保证在 Mininet 退出时停止(无论是有意还是由于错误)。定期使用 ps 命令来确保没有僵尸作业。

拥有一个 shell 进程可以让您轻松执行其他任务。例如,可以使用以下命令找出后台命令的 PID

1
pid = int( h1.cmd('echo $!') )

然后你可以使用 wait 等待特定进程完成执行,例如:

1
h1.cmd('wait', pid)

这仅适用于 UNIX 命令,不适用于内置于 bash shell 本身(并且没有单独的 pid!)的命令(例如while,cd)。

这些命令中的每一个都在前台执行(没有&)而不是后台 (&),因为要在python中获得输出。

除了使用 shell 的等待机制之外,Mininet 本身还允许使用以下命令启动前台命令sendCmd(),然后等待它在稍后的某个时间完成waitOutput():

1
2
3
4
5
6
for h in hosts:
    h.sendCmd('sleep 20')

results = {}
for h in hosts:
    results[h.name] = h.waitOutput()

如果将输出发送到文件,可能希望在测试运行时以交互方式监视该文件的内容。 examples/multipoll.py 示例提供了一个monitorFiles()函数,实现了一种可能的机制来监视多个输出文件。这简化了交互式监视来自多个主机的输出的测试的实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def monitorTest( N=3, seconds=3 ):
    "Run pings and monitor multiple hosts"
    topo = SingleSwitchTopo( N )
    net = Mininet( topo )
    net.start()
    hosts = net.hosts
    print( "Starting test..." )
    server = hosts[ 0 ]
    outfiles, errfiles = {}, {}
    for h in hosts:
        # Create and/or erase output files
        outfiles[ h ] = '/tmp/%s.out' % h.name
        errfiles[ h ] = '/tmp/%s.err' % h.name
        h.cmd( 'echo >', outfiles[ h ] )
        h.cmd( 'echo >', errfiles[ h ] )
        # Start pings
        h.cmdPrint('ping', server.IP(),
                   '>', outfiles[ h ],
                   '2>', errfiles[ h ],
                   '&' )
    print( "Monitoring output for", seconds, "seconds" )
    for h, line in monitorFiles( outfiles, seconds, timeoutms=500 ):
        if h:
            print( '%s: %s' % ( h.name, line ) )
    for h in hosts:
        h.cmd('kill %ping')
    net.stop()

这相当于通过monitorFiles监听outfiles列表中的文件,然后统一输出到bash中

新:popen()/pexec()界面

除了cmd()/sendCmd()的基于 shell 的机制之外,Mininet 现在还支持返回标准 Python Popen()对象的基于管道的接口(有关详细信息,请参阅 Python 的 subprocess 模块。)该机制较新且没有经过充分测试cmd(),但您可能会发现在后台运行多个进程并监视它们的输出很方便。提供了一个pmonitor()函数,Popen()可以更轻松地监视多个对象。

examples/popenpoll.py中的代码使用popen()接口和pmonitor()辅助函数实现与上述类似的功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def pmonitorTest( N=3, seconds=10 ):
    "Run pings and monitor multiple hosts using pmonitor"
    topo = SingleSwitchTopo( N )
    net = Mininet( topo )
    net.start()
    hosts = net.hosts
    print( "Starting test..." )
    server = hosts[ 0 ]
    popens = {}
    for h in hosts:
        popens[ h ] = h.popen('ping', server.IP() )
        print( "Monitoring output for", seconds, "seconds" )
        endTime = time() + seconds
        for h, line in pmonitor( popens, timeoutms=500 ):
            if h:
                print( '%s: %s' % ( h.name, line ), )
                if time() >= endTime:
                    for p in popens.values():
                        p.send_signal( SIGINT )
        net.stop()

请注意,此实现略有不同,因为它将时间管理从pmonitor()辅助函数中提取出来,但这使得ping能够在它被中断后捕获它的输出。

当然,您不必使用pmonitor()- 您可以使用 Popen.communicate()(只要您没有太多文件描述符)或select.poll()以及任何其他有效的机制。

重要:共享文件系统!

要记住的一件事是,默认情况下 Mininet 主机共享底层服务器的根文件系统。

共享根文件系统还意味着不需要在 Mininet 主机之间复制数据,因为可以直接访问。

然而,一个副作用是主机共享 Mininet 服务器的 /etc目录。、如果您需要对程序进行特定配置(例如httpd),那么您可能需要为每个 Mininet 主机创建不同的配置文件,并将它们指定为您正在运行的程序的启动选项。

另一个副作用是,尝试在多个主机上的同一目录中创建同一文件,会发生文件冲突。

如果需要每个主机的私有目录,将它们指定为Host的选项,例如:

1
h = Host( 'h1', privateDirs=[ '/some/directory' ] )

主机配置方法

Mininet hosts 提供了一些方便的网络配置方法:

  1. IP(): 返回主机或特定接口的 IP 地址。
  2. MAC():返回主机或特定接口的 MAC 地址。
  3. setARP(): 将静态 ARP 条目添加到主机的 ARP 缓存中。
  4. setIP():设置主机或特定接口的 IP 地址。
  5. setMAC():设置主机或特定接口的MAC地址

例如:

1
print( "Host", h1.name, "has IP address", h1.IP(), "and MAC address", h1.MAC() )

在任何情况下,如果您不提供特定接口(例如h1-eth0 或接口对象),该方法将使用主机的默认接口。

在 Mininet 中命名

主机被称为h1..hN,交换机被称为s1..sN。属于节点的接口以节点名称开头命名,例如h1-eth0是主机h1的默认接口,s1-eth1是交换机s1的第一个数据端口。主机接口仅在主机内部可见,交换机数据端口在“root”命名空间中可见。因此,很容易检查交换机接口,但检查主机接口有点棘手(主要使用host.cmd())。

命令行界面

Mininet 包括一个可以在网络上调用的命令行界面 (CLI),并提供各种有用的命令,以及显示 xterm 窗口和在网络中的各个节点上运行命令的能力。可以通过将网络对象传递给CLI()构造函数来在网络上调用 CLI:

1
2
3
4
5
6
7
8
from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet
from mininet.cli import CLI

net = Mininet(SingleSwitchTopo(2))
net.start()
CLI(net)
net.stop()

启动 CLI 可用于调试网络,因为它允许您查看网络拓扑(使用net命令)、测试连接性(使用pingall命令)以及向各个主机发送命令。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
*** Starting CLI:
mininet> net
c0
s1 lo:  s1-eth1:h1-eth0 s1-eth2:h2-eth0
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
mininet> pingall
*** Ping: testing ping reachability
h1 -> h2
h2 -> h1
*** Results: 0% dropped (0/2 lost)
mininet> h1 ip link show
746: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
	link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
749: h1-eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
	link/ether d6:13:2d:6f:98:95 brd ff:ff:ff:ff:ff:ff

mn使用–custom文件自定义

建议直接写python文件,详见官网

其他示例

Mininet 脚本的其他示例可以在mininet/examples.

这些示例旨在提供教育意义,因为它们展示了可以使用 Mininet API 的不同方式。我们鼓励您尝试阅读Python 代码并自己运行示例。如果它们由于某种原因不起作用,看看您是否能找出原因!

consoles.py可以构建的有趣演示。

miniedit.py 对于 Mininet 的简单实验来说是一个特别有用的 GUI。

了解 Mininet API

Mininet 的 API 构建在三个主要级别:

  • 低级 API:低级 API 由基本节点和链接类(例如Host、Switch和Link及其子类)组成,它们实际上可以单独实例化并用于创建网络,但有点笨拙。
  • 中级API:中级API 添加Mininet对象作为节点和链接的容器。它提供了多种方法(例如addHost()、addSwitch()和addLink())用于向网络添加节点和链接,以及网络配置、启动和关闭(特别是start()和stop()。)
  • 高级 API:高级 API 添加拓扑模板抽象Topo类,它提供创建可重用的参数化拓扑模板的能力。这些模板可以传递给mn命令(通过–custom选项)并从命令行使用。

了解每个 API 级别很有价值。一般来说,当你想直接控制节点和交换机时,你会使用低级 API。当您想要启动或停止网络时,通常使用中级 API(尤其是Mininet类)。

当您开始考虑创建完整的网络时,通常会希望选择中级 API(例如Mininet.add*())或高级 API ( Topo.add*()) 来创建您的网络。

低级 API:节点和链接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
h1 = Host( 'h1' )
h2 = Host( 'h2' )
s1 = OVSSwitch( 's1', inNamespace=False )
c0 = Controller( 'c0', inNamespace=False )
Link( h1, s1 )
Link( h2, s1 )
h1.setIP( '10.1/8' )
h2.setIP( '10.2/8' )
c0.start()
s1.start( [ c0 ] )
print( h1.cmd( 'ping -c1', h2.IP() ) )
s1.stop()
c0.stop()

中级 API:网络对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
net = Mininet()
h1 = net.addHost( 'h1' )
h2 = net.addHost( 'h2' )
s1 = net.addSwitch( 's1' )
c0 = net.addController( 'c0' )
net.addLink( h1, s1 )
net.addLink( h2, s1 )
net.start()
print( h1.cmd( 'ping -c1', h2.IP() ) )
CLI( net )
net.stop()

高级 API:拓扑模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class SingleSwitchTopo( Topo ):
    "Single Switch Topology"
    def build( self, count=1 ):
        hosts = [ self.addHost( 'h%d' % i )
                  for i in range( 1, count + 1 ) ]
        s1 = self.addSwitch( 's1' )
        for h in hosts: 
            self.addLink( h, s1 )

net = Mininet( topo=SingleSwitchTopo( 3 ) )
net.start()
CLI( net ) 
net.stop()

如您所见,中级 API 对于本示例实际上是最简单和最简洁的,不需要创建拓扑类。低级和中级 API 灵活而强大,但与高级TopoAPI 及其拓扑模板相比,重用起来可能不太方便。

另请注意,在 2.2.0 之前的 Mininet 版本中,高层Topo不支持节点之间的多链接,但低层 API 支持。目前Topo也不关心哪些坏过压保护由哪些控制器控制(您可以使用自定义Switch子类来执行此操作,如上所述。)使用中级和低级 API,您可以根据需要手动启动交换机,将适当的控制器列表传递给每个交换机。

Mininet API 文档

Mininet 包含每个模块和 API 调用的 Python 文档字符串。这些可以从 Python 的常规help()机制访问。例如,

1
2
3
4
5
6
>>> from mininet.node import Host
>>> help(Host.IP)
Help on method IP in module mininet.node:

IP(self, intf=None) unbound mininet.node.Host method
    Return IP address of a node or specific interface.

同样的文档也可以在 Mininet 网站上找到。

测试性能

这些是推荐的工具。

  1. 带宽 ( bwm-ng, ethstats)
  2. 延迟(使用ping)
  3. 队列(使用tc包含在monitor.py)
  4. TCPCWND统计信息(tcp_probe,也许我们应该将其添加到monitor.py)
  5. CPU 使用率(全局:top或每个容器cpuacct)

OpenFlow 和自定义路由

Mininet 最强大和最有用的功能之一是它使用 软件定义网络。

使用OpenFlow协议和相关工具,可以对交换机进行编程,以对进入它们的数据包执行几乎任何您想要的操作。

OpenFlow 使像 Mininet 这样的仿真器变得更加有用,因为网络系统设计(包括使用 OpenFlow 的自定义数据包转发)可以轻松地转移到硬件 OpenFlow 交换机以进行线速操作。

Mininet OpenFlow 教程

OpenFlow控制器

如果你想使用你自己的控制器,你可以很容易地创建一个自定义的子类Controller()并将它传递到 Mininet 中。可以在mininet.controller.NOX()中看到一个示例,它使用一组作为选项传入的模块调用 NOX classic。

下面是创建和使用自定义 POXController子类的简单示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python                                                                                      
                                                                                                       
from mininet.net import Mininet                                                                        
from mininet.node import Controller                                                                    
from mininet.topo import SingleSwitchTopo                                                              
from mininet.log import setLogLevel                                                                    

import os

class POXBridge( Controller ):
    "Custom Controller class to invoke POX forwarding.l2_learning" 
    def start( self ):
        "Start POX learning switch"
        self.pox = '%s/pox/pox.py' % os.environ[ 'HOME' ]
        self.cmd( self.pox, 'forwarding.l2_learning &' )
    def stop( self ):
        "Stop POX"
        self.cmd( 'kill %' + self.pox )

controllers = { 'poxbridge': POXBridge }

if __name__ == '__main__':
    setLogLevel( 'info' )
    net = Mininet( topo=SingleSwitchTopo( 2 ), controller=POXBridge )
    net.start()
    net.pingAll()
    net.stop()

外部 OpenFlow 控制器

自定义Controller()子类是自动启动和关闭控制器的最方便的方法。创建start()和stop()方法很容易,这样 Mininet 将根据需要自动启动和停止您的控制器。

可以使用RemoteController将 Mininet 连接到已经在其他地方运行的现有控制器,例如 LAN 上的某个地方、另一个 VM 中或笔记本电脑上。

1
2
from functools import partial
net = Mininet( topo=topo, controller=partial( RemoteController, ip='127.0.0.1', port=6633 ) )

或者

1
net = Mininet( topo=topo, controller=lambda name: RemoteController( name, ip='127.0.0.1' ) )

甚至

1
2
net = Mininet( topo=topo, controller=None)
net.addController( 'c0', controller=RemoteController, ip='127.0.0.1', port=6633 )

请注意,在这种情况下, controller(如host和switch)是一个构造函数,而不是一个对象(但请参阅下面的附加信息!)您可以使用partial或lambda在线创建自定义构造函数,或者您可以传入自己的函数(必须采用name参数并返回控制器对象)或类(例如RemoteController的子类)

您还可以创建多个控制器并创建一个自定义Switch()子类,根据需要连接到不同的控制器:

1
2
3
4
5
6
7
8
c0 = Controller( 'c0' )  # local controller
c1 = RemoteController( 'c1', ip='127.0.0.2' )  # external controller
cmap = { 's1': c0, 's2': c1, 's3': c1 }

class MultiSwitch( OVSSwitch ):
    "Custom Switch() subclass that connects to different controllers"
    def start( self, controllers ):
        return OVSSwitch.start( self, [ cmap[ self.name ] ] )

通过传入控制器对象调用 API

在 Mininet 2.2.0 及更高版本中,您可以选择传入Controller 对象而不是构造函数(甚至是对象列表)。添加此选项是因为尽管 API 明确指定需要构造函数,但人们仍在这样做.

这使您可以执行以下操作:

1
net = Mininet( topo, controller=RemoteController( 'c0', ip='127.0.0.1' ) )

多路径路由

重要的是要记住,以太网桥(也称为学习交换机)会淹没在其 MAC 表中丢失的数据包。它们还会像 ARP 和 DHCP 请求一样泛滥广播。这意味着,如果您的网络中有环路或多条路径,它无法将默认ovs-controller与充当学习交换机/以太网桥的controller控制器|NOX的pyswitch|POX的l2_learning一起使用。

更新(和更复杂)的 OpenFlow 控制器确实支持多路径路由 - 请查阅控制器的文档以确定是否需要任何特殊配置。

如果您正在构建类似胖树的拓扑结构,您可能希望查看 RipLPOX,这是一个使用 POX 实现的基本数据中心控制器。您可以将其用作您自己的自定义多路径路由的起点。

为了方便起见,您可能还希望实现一个自定义Controller()子类来调用 RipLPOX。

更新 Mininet

两种方式

  1. 使用指向 Mininet 源代码树的符号链接更新(使更新 Mininet 的 python 代码变得容易)。
1
2
3
4
5
cd ~/mininet
    git checkout master # assuming you want to update to the current master branch
sudo make develop # this only needs to be done initially and when mnexec.c changes
git fetch
git pull --rebase
  1. 将 Mininet 源代码复制到 /usr/lib/python…(允许您删除或移动 Mininet 源代码树):
1
2
3
4
5
cd ~/mininet
    git checkout master # assuming you want to update to the current master branch
git fetch
git pull --rebase
sudo make install

学习Python

详见Python教程

它是如何工作的

Mininet 背后的魔力是 Linux 中内置的一组功能,允许将单个系统拆分为一堆较小的“容器”,每个容器具有固定份额的处理能力,并结合允许链接准确的虚拟链接代码延迟和速度。

在内部,Mininet 采用 Linux 内核中的轻量级虚拟化功能,包括进程组、CPU 带宽隔离和网络命名空间,并将它们与链接调度程序和虚拟以太网链接相结合。与使用完整虚拟机的仿真器相比,这些功能产生的系统启动速度更快,可扩展到更多主机。

Mininet 网络由以下组件组成:

  1. 孤立的主机。Mininet 中的模拟主机是一组移动到网络命名空间中的用户级进程 - 网络状态的容器。网络名称空间为进程组提供接口、端口和路由表(例如 ARP 和 IP)的独占所有权。例如,两个网络命名空间中的两个 Web 服务器可以在一个系统中共存,它们都监听端口 80 上的私有 eth0 接口。Mininet 使用 CPU 带宽限制来限制每个进程组可用的 CPU 比例。
  2. 模拟链接。每个链路的数据速率由 Linux 流量控制 ( tc) 强制执行,它有许多数据包调度程序来将流量调整为配置的速率。每个模拟主机都有自己的虚拟以太网接口(使用 ip link add/set 创建和安装)。虚拟以太网(或veth)对,就像连接两个虚拟接口或虚拟交换机端口的电线一样;通过一个接口发送的数据包被传送到另一个接口,每个接口对所有系统和应用程序软件来说都是一个功能齐全的以太网端口。
  3. 仿真交换机。Mininet 通常使用在内核模式下运行的默认 Linux 网桥或 Open vSwitch 来跨接口交换数据包。交换机和路由器可以在内核中运行(为了速度)或在用户空间中运行(因此我们可以轻松修改它们)。
 |