博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Qt浅谈之四十六QemuQuestAgent的应用
阅读量:4179 次
发布时间:2019-05-26

本文共 21533 字,大约阅读时间需要 71 分钟。

一、简介

        qemu-ga是在虚拟机中安装的一个agent,宿主机host通过通道(unix socket)与虚拟机vm内部的agent进行通信,这样宿主机就有了一种从外部控制/获取虚拟机的手段。比如:host可以向vm下发执行修改hostname的指令,或者获取vm内所有进程信息的指令。
        qemu-ga时刻监听这个unix socket,一旦发现有指令发送来,分析该指令,并执行,通过unix socket返回执行结果,传输的是json字符串。

二、详解

1、centos6下使用qemu-ga

(1)虚拟机连接本机及网络,然后在虚拟机中安装qemu-ga(windows是qemu-ga.exe)
yum install qemu-guest-agent
(2)修改安装后的qemu-ga配置文件
#修改/etc/sysconfig/qemu-ga文件将 # Enable fsfreeze hook. See the --fsfreeze-hook option in "qemu-ga --help".FSFREEZE_HOOK_ENABLE=0改为# Enable fsfreeze hook. See the --fsfreeze-hook option in "qemu-ga --help".FSFREEZE_HOOK_ENABLE=1
#修改/etc/sysconfig/qemu-ga,注释掉BLACKLIST_RPC这一行,将所有功能开放将BLACKLIST_RPC="guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush"改为#BLACKLIST_RPC="guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush"
(3)将虚拟机关机,在虚拟机配置文件libvirt.xml中的<devices>下面添加下述配置,并重新启动虚拟机
(4)测试是否正常
#得到虚拟机对应的domain id[root@node-12 ~]# virsh list Id    名称                         状态---------------------------------------------------- 90    instance-0000209f              running #使用命令进行测试[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-info"}'{"return":{"version":"0.12.1","supported_commands":[{"enabled":true,"name":"guest-set-vcpus"},{"enabled":true}...
(5)freeze文件系统的方法
#直接用virsh命令,freeze文件系统[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-freeze"}'{"return":1} #freeze后,可以查询当前虚拟机文件系统的状态,表明是frozen[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-status"}'{"return":"frozen"} #thaw(解封)文件系统[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-thaw"}'{"return":1} #thaw后,文件系统为解封状态[root@node-12 ~]# virsh qemu-agent-command 90 '{"execute":"guest-fsfreeze-status"}'{"return":"thawed"}

2、python使用qemu-ga(类实现)

from commands import getstatusoutputimport loggerimport os, sys, statimport json  import base64  import crypt  import stringimport random  import reimport socket########################Configure############################FILE_OPEN_READ="""{"execute":"guest-file-open", "arguments":{"path":"%s","mode":"r"}}"""  FILE_OPEN_WRITE="""{"execute":"guest-file-open", "arguments":{"path":"%s","mode":"%s"}}"""  FILE_READ="""{"execute":"guest-file-read", "arguments":{"handle":%s,"count":%d}}"""FILE_WRITE="""{"execute":"guest-file-write", "arguments":{"handle":%s,"buf-b64":"%s"}}"""   FILE_CLOSE="""{"execute":"guest-file-close", "arguments":{"handle":%s}}""" class QemuQuestAgent:    def __init__(self):        self.__socketFileName = ""        self.__totalSize = 0        self.__currentSize = 0    def setSocketFile(self, filename):        self.socketFileName = filename        if not os.path.exists(self.socketFileName):            logger.error("%s do not exist!", self.socketFileName)            return False        return True            def resetPassWord(self, newPassword):        passwordFile = "/etc/shadow"        content = self._QemuQuestAgent__guestFileRead(passwordFile)        if not content.strip():            return False         content = base64.standard_b64decode(content)        user_array = re.split("\n",content)          for iter,line in enumerate(user_array):              info = line.split(":")              if info[0] == "root":                  info[1] = self._QemuQuestAgent__generationPwd(newPassword)                  user_array[iter] = ":".join(info)                  break        content = base64.standard_b64encode("\n".join(user_array))        write_count = self._QemuQuestAgent__guestFileWrite(passwordFile, content, "w+")        if write_count > 0:            logger.info("change password successfully!")            return True        else:            return False              def putFileToVM(self, fileName, vmFilePathName):        if not os.path.exists(fileName):            logger.error("%s do not exist" % (fileName))            return False        if vmFilePathName[-1] == "/":            vmFilePathName += fileName.split("/")[-1]        filestats = os.stat(fileName)        self.__totalSize = filestats[stat.ST_SIZE]        if self.__totalSize <= 0:            logger.error("%s is Empty!" % (fileName))            return False        fd = open(fileName, "r")        self.__currentSize = 0        total = 0        while True:            content = fd.read(4096)            total += 4096;            self.__currentSize += len(content)   #<=4096            content = base64.standard_b64encode(content)                  write_count = self._QemuQuestAgent__guestFileWrite(vmFilePathName, content, "a+")            if write_count <= 0:                fd.close()                return False            if total >= self.__totalSize:                break        fd.close()        return True        def processWrite(self):        return (self.__currentSize, self.__totalSize)            def __getResult(self, command, fileName = ""):        resultStr = ""        if fileName.strip():            self.socketFileName = fileName        if not os.path.exists(self.socketFileName):            return None;               sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)        server_address = self.socketFileName        try:            sockfd.connect(server_address)        except socket.error, msg:            logger.error("guestagent:%s,%s", socket.error, msg)            return None        try:            sockfd.send(command)            resultStr = sockfd.recv(10240)        finally:            sockfd.close()        return None if not resultStr else json.loads(resultStr);                def __generationPwd(self, pwd):        salt=''.join(random.choice(string.ascii_letters + string.digits + "./") for _ in range(16))          return crypt.crypt(pwd, "$6$%s" % salt)        def __guestFileRead(self, path):        file_handle = -1        content = self._QemuQuestAgent__getResult(FILE_OPEN_READ % (path))        if not content:           return ""        file_handle = content["return"]        if file_handle == -1:            return ""        file_content = self._QemuQuestAgent__getResult(FILE_READ % (file_handle,102400))["return"]["buf-b64"]        self._QemuQuestAgent__getResult(FILE_CLOSE % file_handle)         if not file_content:            return ""        else:            return file_content    def __guestFileWrite(self, path, content, mode):          file_handle = -1          content = self._QemuQuestAgent__getResult(FILE_OPEN_WRITE % (path, mode))        if not content:           return -1        file_handle = content["return"]        if file_handle == -1:            return -2        write_count = self._QemuQuestAgent__getResult(FILE_WRITE % (file_handle,content))["return"]["count"]        self._QemuQuestAgent__getResult(FILE_CLOSE % file_handle)          return write_count ########################test############################def testagent():    instance = QemuQuestAgent()    if instance.setSocketFile("/var/lib/libvirt/qemu/test.agent"):        if instance.resetPassWord("abc123"):            return True        if instance.putFileToVM("test.py", "/root/test.py"):            return True    return Falseif __name__ == '__main__':      testagent()

3、Qt使用qemu-ga

(1)qemuquestagent.h
#ifndef QEMUQUESTAGENT_H#define QEMUQUESTAGENT_H#include 
#include
#include
#include
/*********************** *
*
*
*
* * qemu-ga --daemonize -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 * **********************//*************QemuQuestAgent*****************/class HostConnectVM;class ResetPassWord;class FileManager;class QemuQuestAgent{public: QemuQuestAgent(); ~QemuQuestAgent(); void getQGACommandAll(); void resetPassWord(); void fileOperation();private: HostConnectVM *hostVM; ResetPassWord *passWord; FileManager *fileManager; QString socketFileName;};/*************ResetPassWord*****************/class ResetPassWord{public: ResetPassWord(HostConnectVM *host); ~ResetPassWord(); int checkOldPassWord(const QString &password); int setNewPassWord(const QString &password);private: QString passwdFileRead(); bool passwdFileWrite(const QString &password); bool openFile(QString mode); void closeFile(); QString generationNewPassWord(const QString &password);private: int fileHandle; QString filePasswd; HostConnectVM *pwHost; QStringList lineContent;};/*************FileManager*****************/class FileManager{public: FileManager(HostConnectVM *host); ~FileManager(); int getFile(const QString &filePathName, const QString &localPath = ""); int putFile(const QString &localPathName, const QString &clientPathName); bool getFileContent(const QString &filename, const QString &localPathName); bool putFileContent(const QString &localName, const QString &clientName);private: int openFile(const QString &filename, const QString &mode);private: HostConnectVM *fmHost;};#endif // QEMUQUESTAGENT_H
(2)qemuquestagent.cpp
#include 
#include
#include "qemuquestagent.h"#include "questagentcomponent.h"#include "defineconstants.h"#include "jsonparse.h"#include "base64.h"/*************QemuQuestAgent*****************/QemuQuestAgent::QemuQuestAgent(){ socketFileName = "/var/lib/libvirt/qemu/test.agent"; hostVM = new HostConnectVM(socketFileName); passWord = new ResetPassWord(hostVM); fileManager = new FileManager(hostVM);}QemuQuestAgent::~QemuQuestAgent(){ if (hostVM) { delete hostVM; hostVM = NULL; } if (passWord) { delete passWord; passWord = NULL; } if (fileManager) { delete fileManager; fileManager = NULL; }}void QemuQuestAgent::getQGACommandAll(){ QString info = hostVM->getResult(GUEST_INFO); qDebug() << __FUNCTION__<<__LINE__ << info; if (info == "error2") { qDebug() << "can not conncet to the client of VM"; }}void QemuQuestAgent::resetPassWord(){ QString oldPassWord = "abc123"; QString newPassWord = "abc124"; int flag = passWord->checkOldPassWord(oldPassWord); if (flag == 1) { qDebug() << "old password is wrong"; } else if (flag == 0){ qDebug() << "password is right"; if (passWord->setNewPassWord(newPassWord) == 0) { qDebug() << "set newpassword succeed"; } else { qDebug() << "set newpassword failed"; } } else { //qDebug() << __LINE__ << "the client of VM is off"; }}void QemuQuestAgent::fileOperation(){ qDebug() << "---fileOperation---"; QString file = "/tmp/test1K.iso"; fileManager->getFile(file, "/tmp/abc/"); //fileManager->putFile("/tmp/abc/test1M.iso", "/tmp/");}/*************ResetPassWord*****************/ResetPassWord::ResetPassWord(HostConnectVM *host) : fileHandle(-1){ filePasswd = "/etc/shadow"; pwHost = host;}ResetPassWord::~ResetPassWord(){}int ResetPassWord::checkOldPassWord(const QString &password){ if (openFile("r") == false) { return -1; } QString fileContent = passwdFileRead(); closeFile(); lineContent.clear(); lineContent = fileContent.split("\n"); for (int index = 0; index < lineContent.size(); ++index) { QStringList fields = lineContent.at(index).split(":"); if (fields.at(0) == "root") { QStringList passwdList = fields.at(1).trimmed().split("$"); QString saltBuff = QString("$%1$%2").arg(passwdList[1]).arg(passwdList[2]); char *shadowPwd = crypt(password.toStdString().data(), saltBuff.toStdString().data()); if (!strcmp(shadowPwd, fields.at(1).trimmed().toStdString().data())) { //compare old and new password return 0; } else { return 1; } } } return false;}int ResetPassWord::setNewPassWord(const QString &password){ for (int index = 0; index < lineContent.size(); ++index) { QStringList fields = lineContent.at(index).split(":"); if (fields.at(0) == "root") { fields[1] = generationNewPassWord(password); lineContent[index] = fields.join(":"); break; } } QString newFileContent = ""; if (lineContent.size() > 0) { newFileContent = lineContent.join("\n"); } if (openFile("r+") == false && newFileContent.isEmpty()) { return -1; } if (passwdFileWrite(newFileContent) == false) { return 1; } lineContent.clear(); closeFile(); return 0;}QString ResetPassWord::passwdFileRead(){ QString resultStr = pwHost->getResult(FILE_READ.arg(fileHandle).arg(1400)); //qDebug() << "**********" << resultStr<<"****"; QMap
resultMap = JsonParse::jsonParseEnter(resultStr); // {"return": {"buf-b64": "aGVsbG8gd29ybGQhCg==", "count": 13, "eof": true}} if (resultMap.count() != 3) { qDebug() << "JsonParse failed!" <
getResult(FILE_WRITE.arg(fileHandle).arg(QString::fromStdString(outResult))); QMap
resultMap = JsonParse::jsonParseEnter(resultStr); // {"return": {"count": 13, "eof": false}} qDebug() << "&&&" << resultMap.count() << resultMap["eof"] << resultMap["count"] << QString::fromStdString(outResult) << outResult.size(); if (resultMap.count() == 2 && resultMap["eof"] == "false") { qDebug() << "change password successed"; return true; } else { return false; }}bool ResetPassWord::openFile(QString mode){ QString resultStr = pwHost->getResult(FILE_OPEN.arg(filePasswd).arg(mode)); if (resultStr == "error2") { qDebug() << "can not conncet to the client of VM"; return false; } //qDebug() << "aoyang---------------" << resultStr; QMap
resultMap = JsonParse::jsonParseEnter(resultStr); fileHandle = resultMap.begin().value().toInt(); if (fileHandle < 0) { return false; } return true;}void ResetPassWord::closeFile(){ pwHost->getResult(FILE_FLUSH.arg(fileHandle)); pwHost->getResult(FILE_CLOSE.arg(fileHandle));}QString ResetPassWord::generationNewPassWord(const QString &password){ char seqCode[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; QString verifyCode = "$6$"; QTime time= QTime::currentTime(); qsrand(time.msec()+time.second()*1000); for(int i = 0; i < 16; i++) { int num = qrand() % strlen(seqCode); verifyCode += seqCode[num]; } QString shadowPwd = crypt(password.toStdString().data(), verifyCode.toStdString().data()); return shadowPwd;}/*************FileManager*****************/FileManager::FileManager(HostConnectVM *host){ fmHost = host;}FileManager::~FileManager(){}int FileManager::getFile(const QString &filePathName, const QString &localPath){ if (filePathName.isEmpty()) { return -1; } QFileInfo info(filePathName); QString locaFilename = localPath; if (localPath.isEmpty()) { locaFilename = QDir::currentPath(); locaFilename += "/"; locaFilename += info.fileName(); } else if (localPath.right(1) == "/"){ //path locaFilename += info.fileName(); } QFileInfo loaclInfo(locaFilename); QString dirName = loaclInfo.absolutePath(); QDir dir(dirName); if (!dir.exists()) { dir.mkpath(dirName); } //write content to file //qDebug() << "locaFilename=" << locaFilename << "filePathName=" <
getResult(FILE_READ.arg(handle).arg(1400)); //qDebug() << "--------" << resultStr<<"---------"; QMap
resultMap = JsonParse::jsonParseEnter(resultStr); // {"return": {"buf-b64": "aGVsbG8gd29ybGQhCg==", "count": 13, "eof": false}} if (resultMap.count() != 3) { qDebug() << "JsonParse failed!" <
<< QString::fromStdString(outResult); out << QString::fromStdString(outResult); file.flush(); if (resultMap["eof"] == "true") { file.close(); fmHost->getResult(FILE_CLOSE.arg(handle)); return true; } } } return false;}int FileManager::putFile(const QString &localPathName, const QString &clientPathName){ QFileInfo localFile(localPathName); if (!localFile.isFile()) { qDebug() << "file is not existed"; return -1; } //long fileSize = localFile.size(); QString clientFileName = clientPathName; if (clientPathName.right(1) == "/") { clientFileName += localFile.fileName(); } putFileContent(localPathName, clientFileName);}bool FileManager::putFileContent(const QString &localName, const QString &clientName){ QFile file(localName); long fileSize = file.size(); if (fileSize <= 0) { return false; } if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } int handle = openFile(clientName, "w"); if (handle < 0) { return false; } int total = 0; do { string fileContent = file.read(10240).data(); total += 10240; string outResult = ""; Base64TransCode::Base64Encode(fileContent, &outResult); QString resultStr = fmHost->getResult(FILE_WRITE.arg(handle).arg(QString::fromStdString(outResult))); fmHost->getResult(FILE_FLUSH.arg(handle)); QMap
resultMap = JsonParse::jsonParseEnter(resultStr); // {"return": {"count": 13, "eof": false}} //qDebug() << "&&&" << resultMap.count() << resultMap["eof"] << resultMap["count"]<< outResult.size(); if (resultMap.count() != 2 || resultMap["count"].toInt() <= 0) { qDebug() << "write file error"; fmHost->getResult(FILE_CLOSE.arg(handle)); return false; } }while(total < fileSize); fmHost->getResult(FILE_CLOSE.arg(handle));}int FileManager::openFile(const QString &filename, const QString &mode){ QString resultStr = fmHost->getResult(FILE_OPEN.arg(filename).arg(mode)); qDebug() << FILE_OPEN.arg(filename).arg(mode); if (resultStr == "error2") { qDebug() << "can not conncet to the client of VM"; return -2; } QMap
resultMap = JsonParse::jsonParseEnter(resultStr); int fileHandle = resultMap.begin().value().toInt(); if (fileHandle <= 0) { qDebug() << "open file failed!"; return -1; } return fileHandle;}
(3)questagentcomponent.cpp
socket通讯,发送命令返回结果
#include 
/* See NOTES */#include
#include
#include
#include
#include "questagentcomponent.h"HostConnectVM::HostConnectVM(const QString &file){ socketFile = file;}HostConnectVM::~HostConnectVM(){}QString HostConnectVM::getResult(QString command, QString file){ if (!file.isEmpty()) { socketFile = file; } QString resultStr = ""; /* create a socket */ struct sockaddr_un address; address.sun_family = AF_UNIX; strcpy(address.sun_path, socketFile.toStdString().data()); sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd == -1) { qDebug() << "create socket failed"; resultStr = "error1"; return resultStr; } /* connect to the server */ int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address)); if(result == -1) { qDebug() << "connect socket failed"; resultStr = "error2"; return resultStr; } if((result = write(sockfd, command.toStdString().data(), command.length())) != command.length()) { qDebug() << "write socket failed"; resultStr = "error3"; return resultStr; } char buff[10240] = {0}; if ((result = read(sockfd, buff, 10240)) == -1) { qDebug() << "read socket failed"; resultStr = "error4"; return resultStr; } resultStr = buff; resultStr = resultStr.trimmed(); close(sockfd); return resultStr;}
(4)jsonparse.cpp
传输数据格式json的封装和解析
#include "jsonparse.h"JsonParse::JsonParse(){}JsonParse::~JsonParse(){}QMap
JsonParse::jsonParseEnter(const QString &str){ QMap
map; map.clear(); if (str.trimmed().isEmpty()) { return map; } QScriptEngine engine; QScriptValue source = engine.evaluate("value="+str); QScriptValue valueReturn = source.property("return"); if (valueReturn.isObject()) { QScriptValueIterator it(valueReturn); while(it.hasNext()) { it.next(); map.insert(it.name(), it.value().toString()); } } else { map.insert("return", valueReturn.toString()); }#ifndef PRINT_DEBUG QMap
::const_iterator iter = map.constBegin(); for(;iter != map.constEnd(); iter++) { qDebug() << "---key=" << iter.key() << ", value=" << iter.value(); }#endif}
(5)base64.cpp
        base64将字符串以MIME BASE64编码,此种编码是为了使二进制数据可以通过非纯8-bit的传输层传输,可以让中文字或者图片也能在网络上顺利传输,例如电子邮件的主体。在BASE64编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共64个基本字符,不包含其它特殊的字符,因而才取名BASE64。编码后的字符串比原来的字符串长度再加1/3左右。
#include 
#include "base64.h"bool Base64TransCode::Base64Encode(const string& input, string* output){ string temp; temp.resize(modp_b64_encode_len(input.size())); // makes room for null byte // null terminates result since result is base64 text! int input_size = static_cast
(input.size()); int output_size= modp_b64_encode(&(temp[0]), input.data(), input_size); if (output_size < 0) return false; temp.resize(output_size); // strips off null byte output->swap(temp); return true;}bool Base64TransCode::Base64Decode(const string& input, string* output){ string temp; temp.resize(modp_b64_decode_len(input.size())); // does not null terminate result since result is binary data! int input_size = static_cast
(input.size()); int output_size = modp_b64_decode(&(temp[0]), input.data(), input_size); if (output_size < 0) return false; temp.resize(output_size); output->swap(temp); return true;}
(6)main.cpp
#include 
#include "qemuquestagent.h"int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); QemuQuestAgent w; //w.resetPassWord(); w.getQGACommandAll(); //w.fileOperation(); return a.exec();}

三、总结

(1)参考:、、。
(2)若有问题或建议,请留言,在此感谢!

转载地址:http://gylai.baihongyu.com/

你可能感兴趣的文章
mysql的通用查询日志和慢查询日志
查看>>
IDEA中设置Run Dashboard(Services)
查看>>
Mysql5.7免安装安装教程 win10
查看>>
SpringBoot属性注入的几种方式
查看>>
Idea 解决SVN冲突
查看>>
Sptingboot AOP实现多数据源切换(Hive Impala oracle)
查看>>
dynamic-datasource动态多数据源整合hive impala
查看>>
Mybatis+impala插入超过510个字符串的字段报:HIVE_PARAMETER_QUERY_DATA_TYPE_ERR_NON_SUPPORT_DATA_TYPE
查看>>
SpringBoot项目启动完成自动打开网址
查看>>
记录一下把mapper.xml文件放在java的坑
查看>>
反射的使用
查看>>
使用Stream排序分组
查看>>
linux安装mysql 5.7.23二进制 安装jdk tomcat
查看>>
mysql总结 windows 版本
查看>>
POI 导出工具类
查看>>
HTTP请求工具类
查看>>
Ngnix+tomcat 集群以及session共享
查看>>
Nginx配置多个项目放在不同的tomcat中,共享同一个端口
查看>>
mysql的JDBC连接工具类
查看>>
ORACLE的JDBC连接工具类
查看>>