主函数
main.py
# 导入必要的库
import random
from mininet.link import TCLink
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.node import RemoteController
from mininet.util import dumpNodeConnections
import F5
import state
import torch
import torch.nn as nn
import torch.optim as optim
import torch.multiprocessing as mp
import gym
# 定义环境和模型
class Environment:
def __init__(self, topology):
self.topology = topology
self.controller = RemoteController()
self.net = Mininet(topo=self.topology, controller=self.controller, host=CPULimitedHost, link=TCLink)
self.net.start()
self.mapping_relation = {}
for link in self.net.links:
self.mapping_relation[link.intf1.name] = link.intf2.name
self.current_state = 0.0
# for switch in net.switches:
# # 获取交换机的名称
# switch_name = switch.name
#
# # 获取交换机与之相连的链路的接口名称
# for intf in switch.intfList():
# intf_name = intf.namenet
#
# # 获取链路的带宽利用率
# bandwidth_utilization = get_bandwidth_utilization(switch_name, intf_name)
# link_bw.append(bandwidth_utilization)
# print(f"交换机 {switch_name} 的链路 {intf_name} 的带宽利用率为:{bandwidth_utilization}")
# state.append(link_bw)
# print(state)
# 创建A3C算法Agent
def reset(self):
# 初始化环境状态
# 初始化与智能体相连的链路的带宽利用率
# link_bandwidth_utilization = [random.uniform(0, 1) for _ in range(4)]
# (负载均衡程度)表示状态
self.current_state = state.init(self.net)
return self.current_state
def reward(self,state):
# 以交换机上各条链路的带宽利用率的方差的倒数作为奖励值
# 以字典保存奖励值
switch_reward = {}
for switch_name,switch_state in state.items():
sum_bw = sum(switch_state.values()) # 求交换机上各条链路的带宽利用率的和
ave_bw = sum_bw / len(switch_state) # 求带宽利用率的平均值
sum = 0
for bw in state.values():
sum += (bw - ave_bw) ** 2
variance_bw = sum / len(switch_state) # 求带宽利用率的方差
recirocal = 1 / (variance_bw + 1) # 求方差的倒数
switch_reward[switch_name] = recirocal
return switch_reward
def step(self, action):
# 执行动作并返回新的状态、奖励和是否完成的标志
new_state = self.reset()
reward = self.reward(new_state)
done = 1
return new_state, reward, done
class Model:
def __init__(self, state_dim, action_dim):
super(A3CNet, self).__init__()
# 定义网络结构,例如使用多层全连接层
self.fc1 = nn.Linear(state_dim, 64) # 输入层到隐藏层的全连接层,输入维度为state_dim,隐藏层维度为64
self.fc2 = nn.Linear(64, action_dim) # 隐藏层到输出层的全连接层,隐藏层维度为64,输出维度为action_dim
def forward(self, x):
x = torch.relu(self.fc1(x)) # 使用ReLU激活函数处理输入层到隐藏层的输出
x = self.fc2(x) # 输出隐藏层到输出层的结果,不使用激活函数
return x
# 定义A3C算法的Actor和Critic模型
# 定义A3C算法的Agent
def create_flow_entry(in_port, out_port):
# 生成一个简单的流表项字典
flow_entry = {
"match": {
"in_port": in_port
},
"actions": [
{
"type": "OUTPUT",
"port": out_port
}
]
}
return flow_entry
class Agent:
def __init__(self, state_shape, action_shape):
self.state_shape = state_shape
self.action_shape = action_shape
self.actor = Model(state_shape, action_shape)
self.critic = Model(state_shape, 1)
def choose_action(self, state):
# 基于当前状态选择动作
# 选择带宽利用率最低的链路
for switch, switch_bw in state.items():
min_utilization = float('inf') # 初始化为无穷大
chosen_link = None
for interface,utilization in switch_bw.items(): # 遍历交换机的状态
if utilization < min_utilization: # 获取最小利用率
min_utilization = utilization
chosen_link = (switch, interface)
# 下发流表项,将流量转发到选择的链路
if chosen_link:
switch, interface = chosen_link
# 下发流表项,将流量从某个输入端口(interface)转发到某个输出端口(out_port)
out_port = env.mapping_relation[interface]
flow_entry = create_flow_entry(in_port=interface, out_port=out_port)
env.controller.install_flow_entry(flow_entry)
# return action
def train(self, state, action, reward, next_state, done):
# 训练Actor和Critic模型
...
# 定义训练过程
def train_a3c(env, agent, num_episodes):
for episode in range(num_episodes):
state = env.reset() # 初始化环境状态,获得当前状态
done = False # 完成标志
total_reward = 0 # 奖励总值
while not done:
# 选择动作并执行
action = agent.choose_action(state,env.controller)
next_state, reward, done = env.step(action)
total_reward += reward
# 训练智能体Agent
agent.train(state, action, reward, next_state, done)
state = next_state
# 输出每个episode的总奖励
print(f"Episode {episode}, Total Reward: {total_reward}")
# 主函数
if __name__ == "__main__":
# 创建四叉胖树拓扑结构网络,并初始化环境
topology = F5.MyTopo()
# net = Mininet(topo=topology, host=CPULimitedHost, link=TCLink)
# topology = F5.MyTopo() # 创建四叉胖树拓扑结构网络
env = Environment(topology) # 初始化网络环境
state_shape = 2 # 确定状态空间的形状,与交换机相连的链路数量相对应
action_shape = 2 # 确定动作空间的形状
agent = Agent(state_shape, action_shape)
# 训练Agent
num_episodes = 10 # 确定训练的轮次
train_a3c(env, agent, num_episodes)
# print(env.mapping_relation)
状态文件
state.py
import re
import subprocess
import time
from cv2 import setLogLevel
from mininet.net import Mininet
from mininet.link import Link, TCLink
from mininet.node import CPULimitedHost
import F5
def get_bandwidth_utilization(net,switch, interface):
# 假设这里使用iperf或其他方法来获取链路的带宽利用率
# 返回链路的带宽利用率
# 运行iperf客户端并与目标服务器建立连接
server_cmd = f"iperf -s > iperf_server.txt 2>&1 &"
server_IP = net[switch].IP()
cmd = f"iperf -c {server_IP} -t 5" # 设置测试时间为5秒
# 启动iperf服务器
subprocess.Popen(server_cmd, shell=True)
time.sleep(1)
output = subprocess.check_output(cmd, shell=True, text=True)
# 解析iperf输出,获取传输速率(单位为Mbits/sec)
m = re.search(r'\d+\.\d+\s+Gbits/sec', output)
if m:
transfer_rate = float(m.group().split()[0])
link_bandwidth = 100.0 # 假设链路带宽为100 Mbps
bandwidth_utilization = transfer_rate / link_bandwidth
return bandwidth_utilization
else:
return None
return bandwidth_utilization
def init(net):
# setLogLevel('info')
# topo = F5.MyTopo()
# net = Mininet(topo=topo, host=CPULimitedHost, link=TCLink)
net.start()
state = {} # 状态
# 获取所有交换机的状态
for switch in net.switches:
# 获取交换机的名称
switch_name = switch.name
switch_state = {} # 交换机对应的状态
# 获取交换机与之相连的链路的接口名称
for intf in switch.intfList():
intf_name = intf.name
# 获取链路的带宽利用率
bandwidth_utilization = get_bandwidth_utilization(net,switch_name, intf_name)
switch_state[intf_name] = bandwidth_utilization
# link_bw.append(bandwidth_utilization)
# print(f"交换机 {switch_name} 的链路 {intf_name} 的带宽利用率为:{bandwidth_utilization}")
state[switch_name] = switch_state
print(state)
return state
# net.stop()
拓扑结构
F5.py
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import RemoteController
from mininet.link import TCLink
from mininet.util import dumpNodeConnections
class MyTopo(Topo):
def __init__(self):
super(MyTopo, self).__init__()
# Marking the number of switch for per level
k = 4
pod = k
L1 = (pod // 2) ** 2
L2 = pod * pod // 2
L3 = L2
# Starting create the switch
c = [] # core switch
a = [] # aggregate switch
e = [] # edge switch
# notice: switch label is a special data structure
for i in range(L1):
c_sw = self.addSwitch('c{}'.format(i + 1)) # label from 1 to n,not start with 0
c.append(c_sw)
for i in range(L2):
a_sw = self.addSwitch('a{}'.format(L1 + i + 1))
a.append(a_sw)
for i in range(L3):
e_sw = self.addSwitch('e{}'.format(L1 + L2 + i + 1))
e.append(e_sw)
# 建立映射关系
# Starting create the link between switchs
# first the first level and second level link
for i in range(L1):
c_sw = c[i]
start = i % (pod // 2)
for j in range(pod):
self.addLink(c_sw, a[start + j * (pod // 2)])
# second the second level and third level link
for i in range(L2):
group = i // (pod // 2)
for j in range(pod // 2):
self.addLink(a[i], e[group * (pod // 2) + j])
# Starting create the host and create link between switches and hosts
for i in range(L3):
for j in range(2):
hs = self.addHost('h{}'.format(i * 2 + j + 1))
self.addLink(e[i], hs)
topos = {"mytopo": (lambda: MyTopo())}