Commit 1f84edba authored by wutu's avatar wutu

定义网络拓扑的数据结构(采用二维数组进行描述),实现了基本的网络创建接口。未进行测试!

parent 1adf8cee
......@@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import top.ninwoo.edgecenter.entity.ClusterConfig;
import top.ninwoo.edgecenter.entity.ContainerInfo;
import top.ninwoo.edgecenter.entity.NetworkTopology;
import top.ninwoo.edgecenter.service.ClusterService;
import top.ninwoo.utils.entity.DockerContainer;
import top.ninwoo.utils.entity.NetworkInfo;
......@@ -152,4 +153,29 @@ public class IndexController {
edgeInfo.put("mem", clusterService.mem());
return edgeInfo;
}
@RequestMapping(value = "/createTopo", method = RequestMethod.POST)
public NetworkTopology createTopo(long topologyId, String[] appNames, String topology) {
// 二维数组作为一个字符串传递进来,需要进行一个解析
NetworkTopology topo = new NetworkTopology();
topo.setAppNames(appNames);
topo.setTopologyId(111);
topo.setTopology(parseString(topology));
return topo;
}
private int[][] parseString(String topology) {
String[] lines = topology.split(";");
int x = lines.length;
int y = lines[0].split(",").length;
int[][] topo = new int[x][y];
for (int i = 0; i < x; i++) {
String[] integers = lines[i].split(",");
for (int j = 0; j < y; j++) {
topo[i][j] = Integer.parseInt(integers[j]);
}
}
return topo;
}
}
......@@ -18,4 +18,5 @@ public class ClusterConfig {
private Date createTime;
private String owner;
private List<ContainerDescription> dockers;
private NetworkTopology topology;
}
package top.ninwoo.edgecenter.entity;
import lombok.Data;
/**
* @Author joliu
* @Description 描述网络拓扑, 这个数据结构最终要存储到数据库中
* @Date Create in 下午9:30 2019/11/6
*/
@Data
public class NetworkTopology {
private long topologyId;
private String[] appNames;
private int[][] topology;
/* public NetworkTopology(long topologyId, String[] appNames) {
this.topologyId = topologyId;
this.appNames = appNames;
if(appNames == null || appNames.length < 1) {
throw new RuntimeException("容器的数组不能为空");
}
topology = new int[appNames.length][appNames.length];
}*/
}
package top.ninwoo.edgecenter.service;
/**
* @Author joliu
* @Description 用于分配ip的服务接口
* @Date Create in 下午4:36 2019/11/7
*/
public interface IpService {
/**
* 分配IP
* @param networkSegment IP的网段
* @param containerId 容器ID
* @return Ipv4的地址
*/
String assignIpString(String networkSegment, String containerId);
}
package top.ninwoo.edgecenter.service;
import top.ninwoo.edgecenter.entity.ClusterConfig;
/**
* @Author joliu
* @Description Topo服务
* @Date Create in 上午10:55 2019/11/7
*/
public interface TopologyService {
/**
* 初始化网络拓扑
* 将集群中的网络拓扑构建
* @param clusterConfig
*/
void initTopo(ClusterConfig clusterConfig);
}
package top.ninwoo.edgecenter.service.impl;
import org.springframework.stereotype.Service;
import top.ninwoo.edgecenter.service.IpService;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author joliu
* @Description
* @Date Create in 下午8:20 2019/11/7
*/
@Service
public class IpServiceImpl implements IpService {
ConcurrentHashMap<String, AtomicInteger> countMap = new ConcurrentHashMap<>();
ConcurrentHashMap<String, String> ipMap = new ConcurrentHashMap<>();
// TODO: 这个应该结合数据库,由云端提供服务,暂时提供一个测试版本
@Override
public String assignIpString(String networkSegment, String containerId) {
// networkSegment 10.100.1.0/24
// 在分配IP前还需要校验下这个网段是否是空闲的网段
AtomicInteger atomicInteger;
if(!countMap.containsKey(networkSegment)) {
countMap.put(networkSegment, new AtomicInteger(2));
}
atomicInteger = countMap.get(networkSegment);
String ip = networkSegment.substring(0, networkSegment.length() - 4) + atomicInteger.getAndIncrement()
+ networkSegment.substring(networkSegment.length()-3);
ipMap.put(containerId, ip);
return ip;
}
}
package top.ninwoo.edgecenter.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import top.ninwoo.edgecenter.entity.ClusterConfig;
import top.ninwoo.edgecenter.entity.NetworkTopology;
import top.ninwoo.edgecenter.service.ClusterService;
import top.ninwoo.edgecenter.service.IpService;
import top.ninwoo.edgecenter.service.TopologyService;
import top.ninwoo.utils.service.OVSService;
import top.ninwoo.utils.service.OvsDockerService;
import java.util.Set;
/**
* @Author joliu
* @Description 这里描述的物理连接的拓扑关系
* @Date Create in 上午11:16 2019/11/7
*/
public class TopologyServiceImpl implements TopologyService {
private final static Logger LOG = LoggerFactory.getLogger(TopologyServiceImpl.class);
@Autowired
OVSService ovsService;
@Autowired
OvsDockerService ovsDockerService;
// 这个服务接口的具体功能应该下沉
@Autowired
ClusterService clusterService;
@Autowired
IpService ipService;
@Override
public void initTopo(ClusterConfig clusterConfig) {
// 获取topology
NetworkTopology topology = clusterConfig.getTopology();
if(topology == null) {
LOG.warn("集群[" + clusterConfig.getId() + "]未设置网络");
return;
}
// 校验信息
String[] cNames = topology.getAppNames();
int[][] topo = topology.getTopology();
if(cNames == null || cNames.length <= 1) {
return;
}
// 这里声明:cNames中以br:开头的名称为ovs虚拟交换机
for (int i = 1; i < cNames.length; i++) {
for (int j = 0; j < i; j++) {
// 判断每一个节点的连接状态
if(topo[i][j] == 1) {
// 如果存在连接,则创建连接
addLink(clusterConfig.getId(), cNames[i], cNames[j]);
}
}
}
}
public void addLink(long clusterId, String appName1, String appName2) {
int flag = 0;
// 校验 参数是否合法
// step1: 判断appNames中是否存在ovs虚拟交换机
if(appName1.startsWith("br:")) {
// flag计数器加1
flag++;
// 创建对应的虚拟交换机
// 在创建前需要检查,是否已经创建过对应的交换机
if(!ovsService.isBridge(appName1)) {
LOG.info("Create Ovs Bridge!");
ovsService.addBridge(appName1.substring(3));
}
LOG.info("Ovs Bridge[" + appName1 + "] is normal!");
}
if(appName2.startsWith("br:")) {
flag++;
// 创建对应的虚拟交换机
if(!ovsService.isBridge(appName2)) {
LOG.info("Create Ovs Bridge!");
ovsService.addBridge(appName2.substring(3));
}
LOG.info("Ovs Bridge[" + appName2 + "] is normal!");
}
// 如果两个都是虚拟交换机,则需要创建交换机连接
if(flag == 2) {
// 校验是否是远端的交换机
// 远端交换机输入样例:br:remote:testBr:192.168.31.111
String remoteIp1 = "";
if(appName1.contains("remote")) {
remoteIp1 = appName1.split(":")[3];
}
String remoteIp2 = "";
if(appName2.contains("remote")) {
remoteIp2 = appName2.split(":")[3];
}
// 两个都是本地交换机
if("".equals(remoteIp1) && "".equals(remoteIp2)) {
// 创建两个虚拟交换机
ovsService.addBridge(appName1);
ovsService.addBridge(appName2);
// 创建两个交换机之间的连接
ovsService.linkOvs(appName1, appName2);
}
// 如果两个都是远程交换机
else if(!"".equals(remoteIp1) && !"".equals(remoteIp2)) {
// do nothing
}
// 如果其中之一是本地交换机,找到本地交换机,设置远端ip
else {
if("".equals(remoteIp1)) {
// appName2是远程的
ovsService.setVxlan(appName1, remoteIp2);
} else if("".equals(remoteIp2)) {
// appName1是远程的
ovsService.setVxlan(appName2, remoteIp1);
}
}
}
// 如果其中一个是虚拟交换机,则创建虚拟交换机到容器的连接
else if(flag == 1) {
String ovsName = appName1.startsWith("br:") ? appName1.substring(3) : appName2.substring(3);
String containerName = appName1.startsWith("br:") ? appName2 : appName2;
// 使用ovsDocker接口创建连接
// 获取全部的APP容器id
Set<String> cids = clusterService.getContainerIdsByClusterId(clusterId, containerName);
cids.forEach( cid -> {
// ip分配服务
// TODO: 网段应该是和网络拓扑绑定到一起,需要重新进行设计
String ip = ipService.assignIpString("10.10.1.0/24", cid);
// ovsDockerService.addPort(ovsName, "eth1", containerName, "ip");
ovsDockerService.addPort(ovsName, "eth1", cid, ip);
});
}
// TODO: 如果两个都是容器,这里可以配置逻辑连接
else {
throw new RuntimeException("Physical topology does not support c2c link.");
}
}
}
......@@ -12,4 +12,8 @@ public interface OVSService {
boolean delBridge(String bridgeName);
void addBridge(String bridgeName);
boolean linkOvs(String br1, String br2);
boolean setVxlan(String bridgeName, String remoteIp);
}
package top.ninwoo.utils.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
......@@ -25,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@Service
public class OVSServiceImpl implements OVSService, InitializingBean {
private final static Logger LOG = LoggerFactory.getLogger(OVSServiceImpl.class);
@Autowired
private OvsUtils ovsUtils;
......@@ -115,7 +118,7 @@ public class OVSServiceImpl implements OVSService, InitializingBean {
ovsUtils.addBridge(bridgeName);
}
public void addPortToBridge(String bridgeName, int port) {
public void addPortToBridge(String bridgeName, String port) {
if(!isInstall) {
throw new RuntimeException("Ovs未安装!");
}
......@@ -143,4 +146,45 @@ public class OVSServiceImpl implements OVSService, InitializingBean {
ovsUtils.addBridge(bridgeName);
}
/**
* 连接两个虚拟交换机
* @param br1
* @param br2
* @return
*/
@Override
public boolean linkOvs(String br1, String br2) {
// 判断两个Ovs网桥是否存在
if(!ovsBridges.containsKey(br1) || !ovsBridges.containsKey(br2)) {
LOG.error("ovs bridge[" + br1, ", " + br2 + "]不存在");
return false;
}
// 先创建虚拟网卡对
String[] vethPair = ovsUtils.createVethPair(br1, br2);
// 将虚拟网卡对的两端分别接入到ovs上
boolean b = ovsUtils.addBridgePort(br1, vethPair[0]);
boolean b1 = ovsUtils.addBridgePort(br2, vethPair[1]);
// 启动端口
boolean b2 = ovsUtils.enableLinuxPort(vethPair);
return b && b1 && b2;
}
/**
* 设置VxLan
* @param bridgeName
* @param remoteIp
* @return
*/
@Override
public boolean setVxlan(String bridgeName, String remoteIp) {
// TODO: 最好再校验一下RemoteIp是否合法
if(!isBridge(bridgeName)) {
LOG.warn("bridge[" + bridgeName + "] 不存在");
return false;
}
return ovsUtils.setVxlan(bridgeName, remoteIp);
}
}
......@@ -57,7 +57,7 @@ public interface OvsUtils {
* @param bridgeName
* @param port
*/
boolean addBridgePort(String bridgeName, int port);
boolean addBridgePort(String bridgeName, String port);
/**
* 删除指定的端口
......@@ -65,4 +65,12 @@ public interface OvsUtils {
* @param port
*/
boolean delBridgePort(String bridgeName, int port);
String[] createVethPair(String br1, String br2);
boolean enableLinuxPort(String port);
boolean enableLinuxPort(String[] ports);
boolean setVxlan(String bridgeName, String remoteIp);
}
......@@ -231,5 +231,5 @@ public class IptablesUtilsImpl implements IptablesUtils {
//return dockerUtils.execInDocker(containerId, cmd.split(" "));
}
// 差一个转发的实现
// TODO: 差一个转发的实现
}
......@@ -168,8 +168,14 @@ public class OvsUtilsImpl implements OvsUtils {
* @return
*/
@Override
public boolean addBridgePort(String bridgeName, int port) {
// TODO: 待实现
public boolean addBridgePort(String bridgeName, String port) {
String cmd = "echo 'Vudo3423' | sudo -S ovs-vsctl add-port " + bridgeName + " " + port;
String res = linuxCtlUtils.runCmd(cmd);
if(!"".equals(res)) {
LOG.warn( "AddBridgePort:" + "[" + bridgeName + "," + port + "]" + res);
return false;
}
return true;
}
......@@ -183,4 +189,73 @@ public class OvsUtilsImpl implements OvsUtils {
}
return true;
}
/**
* 创建一对虚拟网卡,输入的两个网桥名字作为创建网桥的辅助工具
* @param br1
* @param br2
* @return
*/
@Override
public String[] createVethPair(String br1, String br2) {
String veth1 = br1 + "_" + br2;
String veth2 = br2 + "_" + br1;
String cmd = "echo 'Vudo3423' | sudo -S ip link add " + veth1 + " type veth peer name " + veth2;
String res = linuxCtlUtils.runCmd(cmd);
if(!"".equals(res)) {
LOG.warn("createVethPair:[" + br1 + "," + br2 + "]:" + res);
}
return new String[]{veth1, veth2};
}
/**
* 启动虚拟网卡
* @param port
* @return
*/
@Override
public boolean enableLinuxPort(String port) {
String cmd = "echo 'Vudo3423' | sudo -S ifconfig " + port + " up";
String res = linuxCtlUtils.runCmd(cmd);
if(!"".equals(res)) {
LOG.warn("EnbaleLinuxPort:" + "[" + port + "]:" + port);
return false;
}
return true;
}
/**
* 启动网卡
* @param ports
* @return
*/
@Override
public boolean enableLinuxPort(String[] ports) {
boolean result = true;
for (String port : ports) {
if (!enableLinuxPort(port)) {
result = false;
}
}
return result;
}
/**
* 设置vxlan
* @param bridgeName
* @param remoteIp
* @return
*/
@Override
public boolean setVxlan(String bridgeName, String remoteIp) {
String cmd = "echo 'Vudo3423' | sudo -S ovs-vsctl add-port " + bridgeName
+ " vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=" + remoteIp;
String res = linuxCtlUtils.runCmd(cmd);
if(!"".equals(res)) {
LOG.warn("setVxlan:" + "[" + bridgeName + "]:" + res);
return false;
}
return true;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment