当我编写一个Java项目时,我发现不能使用libvirt正确创建域。它引发了一个异常“ qemu-system-x86_64:无法在`127.0.0.1:4101'上启动VNC服务器:无法绑定套接字:地址已在使用中”,但是我没有使用“ 4101”端口。
系统信息:
# system centos7
$ uname -a
Linux bogon 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ rpm -ql libvirt-java
/usr/share/doc/libvirt-java-0.4.9
/usr/share/doc/libvirt-java-0.4.9/AUTHORS
/usr/share/doc/libvirt-java-0.4.9/INSTALL
/usr/share/doc/libvirt-java-0.4.9/LICENCE
/usr/share/doc/libvirt-java-0.4.9/NEWS
/usr/share/doc/libvirt-java-0.4.9/README
/usr/share/java/libvirt-0.4.9.jar
/usr/share/java/libvirt.jar
$ java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
异常消息:
libvirt: QEMU Driver 错误 : 内部错误:process exited while connecting to monitor:
(process:5178): GLib-WARNING **: 08:32:41.803: gmem.c:489: custom memory allocation vtable not supported
2019-09-12T00:32:41.839117Z qemu-system-x86_64: Failed to start VNC server on `127.0.0.1:4101': Failed to bind socket: Address already in use
Exception in thread "main" org.libvirt.LibvirtException: 内部错误:process exited while connecting to monitor:
(process:5178): GLib-WARNING **: 08:32:41.803: gmem.c:489: custom memory allocation vtable not supported
2019-09-12T00:32:41.839117Z qemu-system-x86_64: Failed to start VNC server on `127.0.0.1:4101': Failed to bind socket: Address already in use
at org.libvirt.ErrorHandler.processError(Unknown Source)
at org.libvirt.Connect.processError(Unknown Source)
at org.libvirt.Connect.domainCreateXML(Unknown Source)
at open.main(open.java:20)
测试代码:
public class VirConnector {
static Connect connect;
public static Connect getConnection() {
try {
connect = new Connect("qemu:///system");
} catch ( LibvirtException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return connect;
}
}
public class VirtualManageService {
Connect connect = null;
//创建,加载镜像也是这个函数只是cfg不同而已
public boolean domainCreate(String cfg) throws LibvirtException {
//创建一个domain
boolean status = false;
try {
connect = VirConnector.getConnection();
connect.domainCreateXML(cfg,0);
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
public boolean domainActive(String name) throws LibvirtException {
//创建一个domain
boolean status = false;
try {
connect = VirConnector.getConnection();
Domain domain = connect.domainLookupByName(name);
int num = domain.isActive();
if (num == 0){
domain.create();
status = true;
}else {
status = true;
}
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//删除
public boolean domainDelete(String name) throws LibvirtException {
try {
connect = VirConnector.getConnection();
Domain domain = connect.domainLookupByName(name);
domain.destroy();
} finally {
}
return true;
}
//创建存储池
public boolean storagePoolCreate(String cfg) {
boolean status = false;
StoragePool storagePool = null;
try {
//创建存储池
connect = VirConnector.getConnection();
storagePool = connect.storagePoolDefineXML(cfg,0);
//激活
storagePool.create(0);
//设置自动启动
storagePool.setAutostart(0);
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//销毁存储池
public boolean storagePoolDelete(String name) throws LibvirtException {
boolean status = false;
StoragePool storagePool = null;
try {
//查找存储池
connect = VirConnector.getConnection();
storagePool = connect.storagePoolLookupByName(name);
//销毁对象
storagePool.destroy();
//清除定义
storagePool.undefine();
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//创建分卷
public boolean volumeCreate(String poolName,String cfg){
boolean status = false;
StoragePool storagePool = null;
StorageVol storageVol = null;
try {
//查找存储池
connect = VirConnector.getConnection();
storagePool = connect.storagePoolLookupByName(poolName);
//创建分卷
storagePool.storageVolCreateXML(cfg,0);
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//删除分卷
public boolean volumeDelete(String poolName,String volName){
boolean status = false;
StoragePool storagePool = null;
StorageVol storageVol = null;
try {
//查找存储池
connect = VirConnector.getConnection();
storagePool = connect.storagePoolLookupByName(poolName);
//查找分卷
storageVol = storagePool.storageVolLookupByName(volName);
//清空分卷数据
storageVol.wipe();
//删除分卷
storageVol.delete(0);
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//分卷迁移
public boolean volumeMove(String poolNameSrc,String volNameSrc,String poolNameDesc,String cfg) throws LibvirtException {
boolean status = false;
StoragePool storagePoolSrc = null;
StoragePool storagePoolDesc = null;
StorageVol storageVolSrc = null;
try {
//查找存储池
connect = VirConnector.getConnection();
storagePoolSrc = connect.storagePoolLookupByName(poolNameSrc);
//查找待迁移的分卷
storageVolSrc = storagePoolSrc.storageVolLookupByName(volNameSrc);
//查找目的存储池
if (poolNameDesc != poolNameSrc){
storagePoolDesc = connect.storagePoolLookupByName(poolNameDesc);
//将待迁移的分卷按照cfg的策略进行迁移
storagePoolDesc.storageVolCreateXMLFrom(cfg,storageVolSrc,0);
}else{
storagePoolSrc.storageVolCreateXMLFrom(cfg,storageVolSrc,0);
}
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//创建网络配置
public boolean networkCreate(String cfg){
boolean status = false;
Network network = null;
try {
connect = VirConnector.getConnection();
network = connect.networkDefineXML(cfg);
network.create();
network.getAutostart();
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
//删除一个网络配置
public boolean networkDelete(String name){
boolean status = false;
Network network = null;
try {
connect = VirConnector.getConnection();
network = connect.networkLookupByName(name);
network.destroy();
network.undefine();
status = true;
} catch (LibvirtException e) {
e.printStackTrace();
}
return status;
}
}
@Controller
public class VirController {
//老师
@RequestMapping("/openExpVir.do")
@ResponseBody
// public String openVirtualForTeacher(HttpServletRequest request) throws IOException, LibvirtException, DocumentException {
public String openVirtualForTeacher(String coursename, String teacherID) throws IOException, LibvirtException {
//获取当前用户及其课程的信息
int[] port = new int[2];
// HttpSession session = request.getSession();
String link = "null";
//账号就是teacherDomain的主键
// String teacherID = (String) session.getAttribute("teacherID");
// String courseName = (String) session.getAttribute("coursename");
String courseName = coursename;
InquireDao inquireDao = new InquireDao();
port = inquireDao.inquireTeacherDomain(teacherID, courseName);
if (port[0] != 0 & port[1] != 0) {
boolean active = new VirtualManageService().domainActive(teacherID+courseName);
//1.传递参数
String shell = "bash " + "/shell/run.sh " + port[0] + " " + port[1];
//2.启动脚本
Process process = Runtime.getRuntime().exec(shell);
//创建连接,写入数据库意味着websocket创建成功,镜像创建成功。故直接访问link
link = "http://localhost:" + port[1] + "/vnc_lite.html";
} else {
//等待创建镜像和创建websocket
//创建镜像
VirtualManageService virtualManageService = new VirtualManageService();
//对镜像进行克隆
String volNameDesc = teacherID + courseName;
//配置文件
String pathVolMove = "/config/vol/defaultMove.xml";
XmlAlterService xmlVolMove = new XmlAlterService(pathVolMove);
xmlVolMove.xmlAlterName(volNameDesc);
String volDescCfg = xmlVolMove.xmlAlterVolPath("/home/chenyiguang/images/" + volNameDesc);
boolean isMove = virtualManageService.volumeMove("mypool", "sparse.img", "mypool", volDescCfg);
if (isMove) {
//成功
} else {
//失败
}
//获取配置文件 源配置文件
String path = "/config/dom/defaultDomain.xml";
XmlAlterService xmlDol = new XmlAlterService(path);
xmlDol.xmlAlterName(teacherID + courseName);
//自动选择端口
port = virtualManageService.selectPortForTeacher();
xmlDol.xmlAlterDomPort(port[0]);
String domainCfg = xmlDol.xmlAlterDomPath("/home/chenyiguang/images/" + volNameDesc);
boolean status = virtualManageService.domainCreate(domainCfg);
if (status == true) {
//创建成功
} else {
//创建失败
}
//写入数据库
AddDao addDao = new AddDao();
addDao.addTeacherDomain(teacherID, courseName, port[0], port[1]);
//创建websockify
//1.传递参数
String shell = "bash " + "/shell/run.sh " + port[0] + " " + port[1];
//2.启动脚本
Process process = Runtime.getRuntime().exec(shell);
link = "http://localhost:" + port[1] + "/vnc_lite.html";
}
return link;
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<domain type="qemu">
<name>20050505云计算与大数据</name>
<memory unit="G">1</memory>
<vcpu>1</vcpu>
<os>
<type arch="x86_64" machine="pc">hvm</type>
<boot dev="hd"/>
</os>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type="file" device="disk">
<source file="/home/chenyiguang/images/20050505云计算与大数据"/>
<target dev="hda"/>
</disk>
<interface type="network">
<source network="default"/>
</interface>
<input type="mouse" bus="ps2"/>
<graphics type="vnc" port="10001" listen="127.0.0.1"/>
</devices>
</domain>
$ virt-xml-validate error.xml
error.xml validates
答案 0 :(得分:0)
它引发一个异常“ qemu-system-x86_64:无法启动 127.0.0.1:4101上的VNC服务器:绑定套接字失败:已经有地址 在使用中”,但我不使用“ 4101”端口。
这是一个稍微误导QEMU错误消息的情况。
您的XML配置正在请求端口10001:
<graphics type="vnc" port="10001" listen="127.0.0.1"/>
-vnc的QEMU命令行参数不需要端口号,而需要VNC显示号。要从端口号获取VNC显示号,必须减去5900。因此,您对端口10001的请求将变成QEMU命令行参数-vnc 127.0.0.1:4101
。
然后,QEMU无法绑定到显示4101 /端口10001,因为已经有其他内容在监听。
这里最简单的解决方案是根本不指定端口号,而是在XML配置中设置autoport="yes"
,libvirt会自动选择一个可用的端口。