package jit.edu.paas.service.impl; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.plugins.Page; import com.baomidou.mybatisplus.service.impl.ServiceImpl; import com.google.common.collect.ImmutableMap; import com.spotify.docker.client.DockerClient; import com.spotify.docker.client.exceptions.DockerRequestException; import com.spotify.docker.client.exceptions.NotFoundException; import com.spotify.docker.client.messages.AttachedNetwork; import com.spotify.docker.client.messages.ContainerInfo; import com.spotify.docker.client.messages.Network; import com.spotify.docker.client.messages.NetworkConfig; import jit.edu.paas.commons.util.*; import jit.edu.paas.domain.entity.ContainerNetwork; import jit.edu.paas.domain.entity.SysNetwork; import jit.edu.paas.domain.enums.ResultEnum; import jit.edu.paas.domain.enums.RoleEnum; import jit.edu.paas.domain.enums.SysLogTypeEnum; import jit.edu.paas.domain.vo.ContainerNetworkVO; import jit.edu.paas.domain.vo.ResultVO; import jit.edu.paas.exception.CustomException; import jit.edu.paas.mapper.ContainerNetworkMapper; import jit.edu.paas.mapper.SysNetworkMapper; import jit.edu.paas.mapper.UserContainerMapper; import jit.edu.paas.service.SysLogService; import jit.edu.paas.service.SysLoginService; import jit.edu.paas.service.SysNetworkService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; import java.util.*; /** * <p> * 系统网络表 服务实现类 * </p> * * @author jitwxs * @since 2018-07-14 */ @Slf4j @Service public class SysNetworkServiceImpl extends ServiceImpl<SysNetworkMapper, SysNetwork> implements SysNetworkService { @Autowired private SysNetworkMapper networkMapper; @Autowired private UserContainerMapper containerMapper; @Autowired private ContainerNetworkMapper containerNetworkMapper; @Autowired private SysLoginService loginService; @Autowired private SysLogService sysLogService; @Autowired private DockerClient dockerClient; @Override public SysNetwork getById(String id) { return networkMapper.selectById(id); } @Override public boolean hasExistName(String name) { List<SysNetwork> list = networkMapper.selectList(new EntityWrapper<SysNetwork>().eq("name", name)); return CollectionUtils.getListFirst(list) != null; } @Override public boolean hasExistName(String name, String userId) { List<SysNetwork> list = networkMapper.selectList(new EntityWrapper<SysNetwork>() .eq("name", name) .eq("user_id", userId)); return CollectionUtils.getListFirst(list) != null; } @Override public Page<SysNetwork> listAllNetwork(Page<SysNetwork> page, Boolean hasPublic) { List<SysNetwork> list = networkMapper.listAllNetwork(page, hasPublic); return page.setRecords(list); } @Override public Page<SysNetwork> listSelfNetwork(Page<SysNetwork> page, String userId) { List<SysNetwork> list = networkMapper.listSelfNetwork(page, userId); return page.setRecords(list); } @Override public Page<SysNetwork> listSelfAndPublicNetwork(Page<SysNetwork> page, String uid) { List<SysNetwork> list = networkMapper.listSelfAndPublicNetwork(page, uid); return page.setRecords(list); } @Transactional(rollbackFor = CustomException.class) @Override public ResultVO createPublicNetwork(String name, String driver, Map<String, String> labels, HttpServletRequest request) { // 参数判断 if(StringUtils.isBlank(name, driver)) { return ResultVOUtils.error(ResultEnum.PARAM_ERROR); } // 名称只能由数字和字母组成 if(StringUtils.isNotAlphaOrNumeric(name)) { return ResultVOUtils.error(ResultEnum.NETWORK_NAME_ILLEGAL); } // 名称判断 if(hasExistName(name)) { return ResultVOUtils.error(ResultEnum.NETWORK_NAME_EXIST); } // host driver 判断 if("host".equals(driver) && hasExistHostDriver()) { return ResultVOUtils.error(ResultEnum.NETWORK_HOST_EXIST); } try { NetworkConfig.Builder builder = NetworkConfig.builder(); builder.name(name); builder.driver(driver); builder.checkDuplicate(true); builder.attachable(true); if(labels != null) { builder.labels(labels); } NetworkConfig config = builder.build(); dockerClient.createNetwork(config); // 保存数据库 List<Network> networks = dockerClient.listNetworks(DockerClient.ListNetworksParam.byNetworkName(name)); if(networks == null || networks.size() == 0) { return ResultVOUtils.error(ResultEnum.PUBLIC_NETWORK_CREATE_ERROR); } SysNetwork sysNetwork = network2SysNetwork(networks.get(0)); networkMapper.insert(sysNetwork); // 保存日志 sysLogService.saveLog(request, SysLogTypeEnum.CREATE_PUBLIC_NETWORK); return ResultVOUtils.success(); } catch (NotFoundException fe){ fe.printStackTrace(); return ResultVOUtils.error(ResultEnum.CREATE_NETWORK_ERROR_BY_DRIVER); } catch (Exception e) { log.error("创建公共网络出现错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.createPublicNetwork()", HttpClientUtils.getStackTraceAsString(e)); // 保存日志 sysLogService.saveLog(request, SysLogTypeEnum.CREATE_PUBLIC_NETWORK_ERROR, e); return ResultVOUtils.error(ResultEnum.PUBLIC_NETWORK_CREATE_ERROR); } } @Override public ResultVO createUserNetwork(String name, String driver, Map<String, String> labels, String uid) { // 参数判断 if(StringUtils.isBlank(name, driver)) { return ResultVOUtils.error(ResultEnum.PARAM_ERROR); } // 名称只能由数字和字母组成 if(StringUtils.isNotAlphaOrNumeric(name)) { return ResultVOUtils.error(ResultEnum.NETWORK_NAME_ILLEGAL); } // 名称判断 if(hasExistName(name, uid)) { return ResultVOUtils.error(ResultEnum.NETWORK_NAME_EXIST); } // host driver 判断 if("host".equals(driver) && hasExistHostDriver()) { return ResultVOUtils.error(ResultEnum.NETWORK_HOST_EXIST); } try { String fullName =uid + "-" + name; NetworkConfig.Builder builder = NetworkConfig.builder(); builder.name(fullName); builder.driver(driver); builder.checkDuplicate(true); builder.attachable(true); if(labels != null) { builder.labels(labels); } NetworkConfig config = builder.build(); dockerClient.createNetwork(config); // 保存数据库 List<Network> networks = dockerClient.listNetworks(DockerClient.ListNetworksParam.byNetworkName(name)); if(networks == null || networks.size() == 0) { return ResultVOUtils.error(ResultEnum.USER_NETWORK_CREATE_ERROR); } SysNetwork sysNetwork = network2SysNetwork(networks.get(0)); networkMapper.insert(sysNetwork); return ResultVOUtils.success(); } catch (Exception e) { log.error("创建个人网络出现错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.createUserNetwork()", HttpClientUtils.getStackTraceAsString(e)); return ResultVOUtils.error(ResultEnum.USER_NETWORK_CREATE_ERROR); } } @Override public ResultVO hasPermission(String networkId, String userId) { SysNetwork network = getById(networkId); if(network == null) { return ResultVOUtils.error(ResultEnum.NETWORK_NOT_EXIST); } // 公共网络均能访问 if(network.getHasPublic()) { return ResultVOUtils.success(); } String roleName = loginService.getRoleName(userId); if(RoleEnum.ROLE_USER.getMessage().equals(roleName)) { // 普通用户无法访问他人网络 if(!userId.equals(network.getUserId())) { return ResultVOUtils.error(ResultEnum.PERMISSION_ERROR); } } return ResultVOUtils.success(); } @Override public ResultVO connectNetwork(String networkId, String containerId, String userId) { // 鉴权 ResultVO resultVO = hasPermission(networkId, userId); if(ResultEnum.OK.getCode() != resultVO.getCode()) { return resultVO; } // 校验容器所属 if(!containerMapper.hasBelongSb(containerId, userId)) { return ResultVOUtils.error(ResultEnum.NETWORK_CONNECT_REFUSED); } try { if (dockerClient.inspectNetwork(networkId) == null || networkMapper.selectById(networkId) == null) { sync(); return ResultVOUtils.error(ResultEnum.NETWORK_NOT_EXIST); } containerNetworkMapper.insert(new ContainerNetwork(containerId, networkId)); dockerClient.connectToNetwork(containerId, networkId); return ResultVOUtils.success(); } catch (DockerRequestException requestException){ return ResultVOUtils.error(ResultEnum.DOCKER_EXCEPTION.getCode(), HttpClientUtils.getErrorMessage(requestException.getMessage())); }catch (Exception e) { log.error("连接网络出错错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.connectNetwork()", HttpClientUtils.getStackTraceAsString(e)); return ResultVOUtils.error(ResultEnum.CONNECT_NETWORK_ERROR); } } @Override public ResultVO disConnectNetwork(String networkId, String containerId, String userId) { // 鉴权 ResultVO resultVO = hasPermission(networkId, userId); if(ResultEnum.OK.getCode() != resultVO.getCode()) { return resultVO; } // 校验容器所属 if(!containerMapper.hasBelongSb(containerId, userId)) { return ResultVOUtils.error(ResultEnum.NETWORK_CONNECT_REFUSED); } try { dockerClient.disconnectFromNetwork(containerId, networkId); containerNetworkMapper.delete(new EntityWrapper<ContainerNetwork>().eq("container_id",containerId).and().eq("network_id",networkId)); return ResultVOUtils.success(); } catch (DockerRequestException requestException){ return ResultVOUtils.error( ResultEnum.SERVICE_CREATE_ERROR.getCode(), HttpClientUtils.getErrorMessage(requestException.getMessage())); } catch (Exception e) { log.error("取消连接网络出错错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.disConnectNetwork()", HttpClientUtils.getStackTraceAsString(e)); return ResultVOUtils.error(ResultEnum.DIS_CONNECT_NETWORK_ERROR); } } @Override public ResultVO deleteCheck(String id, String userId) { SysNetwork network = getById(id); if(network == null) { return ResultVOUtils.error(ResultEnum.NETWORK_NOT_EXIST); } // Bridge网络无法删除 if("bridge".equals(network.getName().toLowerCase())) { return ResultVOUtils.error(ResultEnum.DELETE_NETWORK_ERROR_BY_BRIDGE); } if (containerNetworkMapper.selectList(new EntityWrapper<ContainerNetwork>().eq("network_id", id)) != null) { return ResultVOUtils.error(ResultEnum.DELETE_NETWORK_ERROR_BY_USED); } String roleName = loginService.getRoleName(userId); if(RoleEnum.ROLE_USER.getMessage().equals(roleName)) { // 普通用户无法删除公共网络 if(network.getHasPublic()) { return ResultVOUtils.error(ResultEnum.PERMISSION_ERROR); } // 普通用户无法删除他人网络 if(!userId.equals(network.getUserId())) { return ResultVOUtils.error(ResultEnum.PERMISSION_ERROR); } } return ResultVOUtils.success(); } @Transactional(rollbackFor = CustomException.class) @Override public ResultVO deleteNetwork(String networkId, String userId, HttpServletRequest request) { ResultVO resultVO = deleteCheck(networkId, userId); if(ResultEnum.OK.getCode() != resultVO.getCode()) { return resultVO; } try { dockerClient.removeNetwork(networkId); networkMapper.deleteById(networkId); return ResultVOUtils.success(); } catch (DockerRequestException requestException){ return ResultVOUtils.error( ResultEnum.SERVICE_CREATE_ERROR.getCode(), HttpClientUtils.getErrorMessage(requestException.getMessage())); } catch (Exception e) { log.error("删除网络错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.deleteNetwork()", HttpClientUtils.getStackTraceAsString(e)); // 写入日志 sysLogService.saveLog(request, SysLogTypeEnum.DELETE_NETWORK_ERROR, e); return ResultVOUtils.error(ResultEnum.DELETE_NETWORK_ERROR); } } @Transactional(rollbackFor = CustomException.class) @Override public ResultVO sync() { try { // 1、查询本地和数据库网络列表 List<Network> localNetworks = dockerClient.listNetworks(); List<SysNetwork> dbNetworks = networkMapper.selectList(new EntityWrapper<>()); int addCount = 0, deleteCount = 0, errorCount = 0; boolean[] dbFlag = new boolean[dbNetworks.size()]; Arrays.fill(dbFlag, false); // 2、计算新增 for (int i = 0; i < localNetworks.size(); i++) { Network network = localNetworks.get(i); String id = network.id(); // 寻找新增记录 boolean flag = false; for (int j = 0; j < dbNetworks.size(); j++) { if (dbFlag[j]) { continue; } if (id.equals(dbNetworks.get(j).getId())) { flag = true; dbFlag[j] = true; } } // 新增记录 if (!flag) { try { SysNetwork sysNetwork = network2SysNetwork(network); networkMapper.insert(sysNetwork); addCount++; } catch (Exception e) { errorCount++; log.error("同步网络时新增记录出现错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.sync()", HttpClientUtils.getStackTraceAsString(e)); } } } // 3、计算删除 for (int i = 0; i < dbNetworks.size(); i++) { if (!dbFlag[i]) { try { networkMapper.deleteById(dbNetworks.get(i).getId()); deleteCount++; } catch (Exception e) { errorCount++; log.error("同步网络时删除记录出现错误,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.sync()", HttpClientUtils.getStackTraceAsString(e)); } } } // 4、准备结果 Map<String, Integer> map = new HashMap<>(16); map.put("add", addCount); map.put("delete", deleteCount); map.put("error", errorCount); return ResultVOUtils.success(map); } catch (DockerRequestException requestException){ return ResultVOUtils.error( ResultEnum.SERVICE_CREATE_ERROR.getCode(), HttpClientUtils.getErrorMessage(requestException.getMessage())); } catch (Exception e) { log.error("读取网络信息失败,错误位置:{},错误栈:{}", "SysNetworkServiceImpl.sync()", HttpClientUtils.getStackTraceAsString(e)); return ResultVOUtils.error(ResultEnum.DOCKER_EXCEPTION); } } @Override public ResultVO listByContainerId(String containerId) { List<ContainerNetwork> list = containerNetworkMapper.selectList( new EntityWrapper<ContainerNetwork>().eq("container_id", containerId)); List<ContainerNetworkVO> res = new ArrayList<>(); for(ContainerNetwork containerNetwork : list) { ContainerNetworkVO vo = new ContainerNetworkVO(); BeanUtils.copyProperties(containerNetwork, vo); // 设置网络 vo.setNetwork(networkMapper.selectById(containerNetwork.getNetworkId())); res.add(vo); } return ResultVOUtils.success(res); } @Transactional(rollbackFor = CustomException.class) @Override public ResultVO syncByContainerId(String containerId) { try { List<ContainerNetwork> dbList = containerNetworkMapper.selectList(new EntityWrapper<ContainerNetwork>().eq("container_id", containerId)); boolean[] dbFlag = new boolean[dbList.size()]; Arrays.fill(dbFlag, false); ContainerInfo containerInfo = dockerClient.inspectContainer(containerId); ImmutableMap<String, AttachedNetwork> networkImmutableMap = containerInfo.networkSettings().networks(); int addCount = 0, deleteCount = 0; if(networkImmutableMap != null && networkImmutableMap.size() > 0) { boolean flag = false; for(AttachedNetwork attachedNetwork : networkImmutableMap.values()) { String networkId = attachedNetwork.networkId(); if(StringUtils.isNotBlank(networkId)) { // 判断数据库中是否有该条记录 for(int i=0; i<dbList.size(); i++) { if(dbFlag[i]) { continue; } if(hasExist(containerId, networkId)) { dbFlag[i] = true; flag = true; break; } } // 保存新纪录 if(!flag) { ContainerNetwork containerNetwork = new ContainerNetwork(containerId, networkId); containerNetworkMapper.insert(containerNetwork); addCount++; } } } // 删除失效记录 for(int i=0; i< dbList.size(); i++) { if(!dbFlag[i]) { containerNetworkMapper.deleteById(dbList.get(i).getId()); deleteCount++; } } } Map<String, Integer> map = new HashMap<>(16); map.put("add", addCount); map.put("delete", deleteCount); return ResultVOUtils.success(map); } catch (Exception e) { return ResultVOUtils.error(ResultEnum.CONTAINER_NETWORK_SYNC_ERROR); } } /** * 判断是否存在driver为host的网络 * host网络只允许存在一个 * @author jitwxs * @since 2018/7/14 17:56 */ @Override public boolean hasExistHostDriver() { List<SysNetwork> sysNetworks = networkMapper.selectList(new EntityWrapper<SysNetwork>().eq("driver", "host")); return CollectionUtils.getListFirst(sysNetworks) != null; } /** * Network --> SysNetwork * @author jitwxs * @since 2018/7/14 16:26 */ private SysNetwork network2SysNetwork(Network network) { SysNetwork sysNetwork = new SysNetwork(); sysNetwork.setId(network.id()); sysNetwork.setScope(network.scope()); sysNetwork.setDriver(network.driver()); sysNetwork.setHasInternal(network.internal()); sysNetwork.setHasIpv6(network.enableIPv6()); sysNetwork.setLabels(JsonUtils.objectToJson(network.labels())); // 个人网络完整name格式:userId-name String networkName = network.name(); if(networkName.contains("-")) { int i = networkName.indexOf("-"); String userId = networkName.substring(0, i); String name = networkName.substring(i+1); sysNetwork.setName(name); sysNetwork.setUserId(userId); sysNetwork.setHasPublic(false); } else { sysNetwork.setName(networkName); sysNetwork.setHasPublic(true); } return sysNetwork; } private boolean hasExist(String containerId, String networkId) { List<ContainerNetwork> list = containerNetworkMapper.selectList(new EntityWrapper<ContainerNetwork>() .eq("container_id", containerId) .eq("network_id", networkId)); return CollectionUtils.isListNotEmpty(list); } }