Skip to content

Commit d1c38e1

Browse files
jeongda-youngDajeong-Park
authored andcommitted
vhba api
1 parent a8ef164 commit d1c38e1

File tree

2 files changed

+284
-9
lines changed

2 files changed

+284
-9
lines changed

core/src/main/java/com/cloud/resource/ServerResourceBase.java

Lines changed: 283 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222
import com.cloud.agent.IAgentControl;
2323
import com.cloud.agent.api.Answer;
2424
import com.cloud.agent.api.Command;
25+
import com.cloud.agent.api.CreateVhbaDeviceCommand;
2526
import com.cloud.agent.api.ListHostDeviceAnswer;
2627
import com.cloud.agent.api.ListHostHbaDeviceAnswer;
2728
import com.cloud.agent.api.ListHostLunDeviceAnswer;
2829
import com.cloud.agent.api.ListHostUsbDeviceAnswer;
30+
import com.cloud.agent.api.ListVhbaDevicesCommand;
2931
import com.cloud.agent.api.StartupCommand;
3032
import com.cloud.agent.api.UpdateHostHbaDeviceAnswer;
3133
import com.cloud.agent.api.UpdateHostLunDeviceAnswer;
3234
import com.cloud.agent.api.UpdateHostUsbDeviceAnswer;
35+
import com.cloud.agent.api.UpdateHostVhbaDeviceCommand;
3336
import com.cloud.utils.net.NetUtils;
3437
import com.cloud.utils.script.OutputInterpreter;
3538
import com.cloud.utils.script.Script;
@@ -56,15 +59,8 @@
5659
import org.apache.commons.lang3.StringUtils;
5760
import org.apache.logging.log4j.LogManager;
5861
import org.apache.logging.log4j.Logger;
59-
import org.json.JSONArray;
60-
import org.json.JSONObject;
6162
import javax.naming.ConfigurationException;
6263

63-
64-
// import org.apache.cloudstack.storage.command.browser.ListRbdObjectsAnswer;
65-
66-
// import com.cloud.agent.api.ListHostLunDeviceCommand;
67-
6864
public abstract class ServerResourceBase implements ServerResource {
6965
protected Logger logger = LogManager.getLogger(getClass());
7066
protected String name;
@@ -300,6 +296,8 @@ private boolean hasPartitionRecursive(JSONObject device) {
300296
protected Answer listHostHbaDevices(Command command) {
301297
List<String> hostDevicesText = new ArrayList<>();
302298
List<String> hostDevicesNames = new ArrayList<>();
299+
300+
// 물리 HBA 장치 조회
303301
Script listCommand = new Script("/bin/bash");
304302
listCommand.add("-c");
305303
listCommand.add("lspci | grep -i 'scsi\\|sas\\|fibre\\|raid\\|hba'");
@@ -311,10 +309,238 @@ protected Answer listHostHbaDevices(Command command) {
311309
String[] parts = line.split(" ", 2);
312310
if (parts.length >= 2) {
313311
hostDevicesNames.add(parts[0].trim());
314-
hostDevicesText.add(parts[1].trim());
312+
hostDevicesText.add("Physical HBA: " + parts[1].trim());
313+
}
314+
}
315+
}
316+
317+
// vHBA 장치 조회 (KVM 환경)
318+
try {
319+
Script vhbaCommand = new Script("/bin/bash");
320+
vhbaCommand.add("-c");
321+
vhbaCommand.add("virsh nodedev-list | grep vhba");
322+
OutputInterpreter.AllLinesParser vhbaParser = new OutputInterpreter.AllLinesParser();
323+
String vhbaResult = vhbaCommand.execute(vhbaParser);
324+
325+
if (vhbaResult == null && vhbaParser.getLines() != null) {
326+
String[] vhbaLines = vhbaParser.getLines().split("\\n");
327+
for (String vhbaLine : vhbaLines) {
328+
String vhbaName = vhbaLine.trim();
329+
if (!vhbaName.isEmpty()) {
330+
// vHBA 상세 정보 조회
331+
Script vhbaInfoCommand = new Script("/bin/bash");
332+
vhbaInfoCommand.add("-c");
333+
vhbaInfoCommand.add("virsh nodedev-dumpxml " + vhbaName);
334+
OutputInterpreter.AllLinesParser vhbaInfoParser = new OutputInterpreter.AllLinesParser();
335+
String vhbaInfoResult = vhbaInfoCommand.execute(vhbaInfoParser);
336+
337+
String vhbaDescription = "Virtual HBA Device";
338+
if (vhbaInfoResult == null && vhbaInfoParser.getLines() != null) {
339+
String[] infoLines = vhbaInfoParser.getLines().split("\\n");
340+
for (String infoLine : infoLines) {
341+
if (infoLine.contains("<name>")) {
342+
String name = infoLine.replaceAll("<[^>]*>", "").trim();
343+
vhbaDescription = "Virtual HBA: " + name;
344+
break;
345+
}
346+
}
347+
}
348+
349+
hostDevicesNames.add(vhbaName);
350+
hostDevicesText.add(vhbaDescription);
351+
}
315352
}
316353
}
354+
} catch (Exception e) {
355+
logger.debug("vHBA 조회 중 오류 발생 (일반적인 상황): " + e.getMessage());
317356
}
357+
358+
return new ListHostHbaDeviceAnswer(true, hostDevicesNames, hostDevicesText);
359+
}
360+
361+
protected Answer createHostVHbaDevices(Command command) {
362+
try {
363+
CreateVhbaDeviceCommand cmd = (CreateVhbaDeviceCommand) command;
364+
String parentHbaName = cmd.getParentHbaName();
365+
String wwnn = cmd.getWwnn();
366+
String wwpn = cmd.getWwpn();
367+
String vhbaName = cmd.getVhbaName();
368+
String xmlContent = cmd.getXmlContent();
369+
370+
// Vue에서 받은 XML을 임시 파일로 저장
371+
String xmlFilePath = "/tmp/" + vhbaName + ".xml";
372+
try (FileWriter writer = new FileWriter(xmlFilePath)) {
373+
writer.write(xmlContent);
374+
}
375+
376+
// virsh nodedev-create 명령 실행
377+
Script createCommand = new Script("/bin/bash");
378+
createCommand.add("-c");
379+
createCommand.add("virsh nodedev-create " + xmlFilePath);
380+
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
381+
String result = createCommand.execute(parser);
382+
383+
if (result != null) {
384+
logger.error("vHBA 생성 실패: " + result);
385+
return new com.cloud.agent.api.CreateVhbaDeviceAnswer(false, vhbaName, null);
386+
}
387+
388+
// 생성된 디바이스 이름 추출
389+
String createdDeviceName = null;
390+
if (parser.getLines() != null) {
391+
String[] lines = parser.getLines().split("\\n");
392+
for (String line : lines) {
393+
if (line.contains("created from")) {
394+
String[] parts = line.split(" ");
395+
if (parts.length >= 2) {
396+
createdDeviceName = parts[1];
397+
}
398+
break;
399+
}
400+
}
401+
}
402+
403+
// 임시 XML 파일 삭제
404+
// new File(xmlFilePath).delete();
405+
406+
logger.info("vHBA 디바이스 생성 성공: " + vhbaName + " -> " + createdDeviceName);
407+
return new com.cloud.agent.api.CreateVhbaDeviceAnswer(true, vhbaName, createdDeviceName);
408+
409+
} catch (Exception e) {
410+
logger.error("vHBA 디바이스 생성 중 오류: " + e.getMessage(), e);
411+
return new com.cloud.agent.api.CreateVhbaDeviceAnswer(false, null, null);
412+
}
413+
}
414+
415+
protected Answer listHostVHbaDevices(Command command) {
416+
try {
417+
ListVhbaDevicesCommand cmd = (ListVhbaDevicesCommand) command;
418+
String keyword = cmd.getKeyword();
419+
List<ListVhbaDevicesCommand.VhbaDeviceInfo> vhbaDevices = new ArrayList<>();
420+
421+
// vHBA 장치 조회 (KVM 환경)
422+
Script vhbaCommand = new Script("/bin/bash");
423+
vhbaCommand.add("-c");
424+
vhbaCommand.add("virsh nodedev-list | grep vhba");
425+
OutputInterpreter.AllLinesParser vhbaParser = new OutputInterpreter.AllLinesParser();
426+
String vhbaResult = vhbaCommand.execute(vhbaParser);
427+
428+
if (vhbaResult == null && vhbaParser.getLines() != null) {
429+
String[] vhbaLines = vhbaParser.getLines().split("\\n");
430+
for (String vhbaLine : vhbaLines) {
431+
String vhbaName = vhbaLine.trim();
432+
if (!vhbaName.isEmpty()) {
433+
// 키워드 필터링
434+
if (keyword != null && !keyword.isEmpty() && !vhbaName.contains(keyword)) {
435+
continue;
436+
}
437+
438+
// vHBA 상세 정보 조회
439+
Script vhbaInfoCommand = new Script("/bin/bash");
440+
vhbaInfoCommand.add("-c");
441+
vhbaInfoCommand.add("virsh nodedev-dumpxml " + vhbaName);
442+
OutputInterpreter.AllLinesParser vhbaInfoParser = new OutputInterpreter.AllLinesParser();
443+
String vhbaInfoResult = vhbaInfoCommand.execute(vhbaInfoParser);
444+
445+
String parentHbaName = "";
446+
String wwnn = "";
447+
String wwpn = "";
448+
String description = "Virtual HBA Device";
449+
String status = "Active";
450+
451+
if (vhbaInfoResult == null && vhbaInfoParser.getLines() != null) {
452+
String[] infoLines = vhbaInfoParser.getLines().split("\\n");
453+
for (String infoLine : infoLines) {
454+
if (infoLine.contains("<parent>")) {
455+
parentHbaName = infoLine.replaceAll("<[^>]*>", "").trim();
456+
} else if (infoLine.contains("wwnn=")) {
457+
wwnn = infoLine.replaceAll(".*wwnn='([^']*)'.*", "$1").trim();
458+
} else if (infoLine.contains("wwpn=")) {
459+
wwpn = infoLine.replaceAll(".*wwpn='([^']*)'.*", "$1").trim();
460+
} else if (infoLine.contains("<name>")) {
461+
String name = infoLine.replaceAll("<[^>]*>", "").trim();
462+
description = "Virtual HBA: " + name;
463+
}
464+
}
465+
}
466+
467+
// vHBA 상태 확인
468+
Script statusCommand = new Script("/bin/bash");
469+
statusCommand.add("-c");
470+
statusCommand.add("virsh nodedev-info " + vhbaName + " | grep State");
471+
OutputInterpreter.AllLinesParser statusParser = new OutputInterpreter.AllLinesParser();
472+
String statusResult = statusCommand.execute(statusParser);
473+
474+
if (statusResult == null && statusParser.getLines() != null) {
475+
String statusLine = statusParser.getLines().trim();
476+
if (statusLine.contains("State:")) {
477+
status = statusLine.replaceAll(".*State:\\s*([^\\s]+).*", "$1").trim();
478+
}
479+
}
480+
481+
com.cloud.agent.api.ListVhbaDevicesCommand.VhbaDeviceInfo vhbaInfo =
482+
new com.cloud.agent.api.ListVhbaDevicesCommand.VhbaDeviceInfo(
483+
vhbaName, parentHbaName, wwnn, wwpn, description, status
484+
);
485+
vhbaDevices.add(vhbaInfo);
486+
}
487+
}
488+
}
489+
490+
logger.info("vHBA 디바이스 조회 완료: " + vhbaDevices.size() + "개 발견");
491+
return new com.cloud.agent.api.ListVhbaDevicesAnswer(true, vhbaDevices);
492+
493+
} catch (Exception e) {
494+
logger.error("vHBA 디바이스 조회 중 오류: " + e.getMessage(), e);
495+
return new com.cloud.agent.api.ListVhbaDevicesAnswer(false, new ArrayList<>());
496+
}
497+
}
498+
499+
protected Answer listVhbaCapableHbas(Command command) {
500+
List<String> hostDevicesText = new ArrayList<>();
501+
List<String> hostDevicesNames = new ArrayList<>();
502+
503+
try {
504+
// vHBA를 지원하는 HBA 조회
505+
Script vportsCommand = new Script("/bin/bash");
506+
vportsCommand.add("-c");
507+
vportsCommand.add("virsh nodedev-list --cap vports");
508+
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
509+
String result = vportsCommand.execute(parser);
510+
511+
if (result == null && parser.getLines() != null) {
512+
String[] lines = parser.getLines().split("\\n");
513+
for (String line : lines) {
514+
String hbaName = line.trim();
515+
if (!hbaName.isEmpty()) {
516+
// HBA 상세 정보 조회
517+
Script hbaInfoCommand = new Script("/bin/bash");
518+
hbaInfoCommand.add("-c");
519+
hbaInfoCommand.add("virsh nodedev-dumpxml " + hbaName);
520+
OutputInterpreter.AllLinesParser hbaInfoParser = new OutputInterpreter.AllLinesParser();
521+
String hbaInfoResult = hbaInfoCommand.execute(hbaInfoParser);
522+
523+
String hbaDescription = "vHBA Capable HBA: " + hbaName;
524+
if (hbaInfoResult == null && hbaInfoParser.getLines() != null) {
525+
String[] infoLines = hbaInfoParser.getLines().split("\\n");
526+
for (String infoLine : infoLines) {
527+
if (infoLine.contains("<max_vports>")) {
528+
String maxVports = infoLine.replaceAll("<[^>]*>", "").trim();
529+
hbaDescription = "vHBA Capable HBA: " + hbaName + " (Max vPorts: " + maxVports + ")";
530+
break;
531+
}
532+
}
533+
}
534+
535+
hostDevicesNames.add(hbaName);
536+
hostDevicesText.add(hbaDescription);
537+
}
538+
}
539+
}
540+
} catch (Exception e) {
541+
logger.debug("vHBA 지원 HBA 조회 중 오류 발생: " + e.getMessage());
542+
}
543+
318544
return new ListHostHbaDeviceAnswer(true, hostDevicesNames, hostDevicesText);
319545
}
320546

@@ -771,4 +997,53 @@ protected Answer updateHostHbaDevices(Command command, String vmName, String xml
771997
return new UpdateHostHbaDeviceAnswer(false, vmName, xmlConfig, isAttach);
772998
}
773999
}
1000+
1001+
protected Answer updateHostVHbaDevices(Command command, String vmName, String xmlConfig, boolean isAttach) {
1002+
try {
1003+
UpdateHostVhbaDeviceCommand cmd = (UpdateHostVhbaDeviceCommand) command;
1004+
String vhbaName = cmd.getVhbaName();
1005+
1006+
String vhbaXmlPath = String.format("/tmp/vhba_device_%s.xml", vmName);
1007+
try {
1008+
// XML 파일이 없을 경우에만 생성
1009+
File xmlFile = new File(vhbaXmlPath);
1010+
if (!xmlFile.exists()) {
1011+
try (PrintWriter writer = new PrintWriter(vhbaXmlPath)) {
1012+
writer.write(xmlConfig);
1013+
}
1014+
logger.info("Generated XML file: {} for VM: {}", vhbaXmlPath, vmName);
1015+
}
1016+
1017+
Script virshCmd = new Script("virsh");
1018+
if (isAttach) {
1019+
virshCmd.add("attach-device", vmName, vhbaXmlPath);
1020+
} else {
1021+
virshCmd.add("detach-device", vmName, vhbaXmlPath);
1022+
logger.info("Executing detach command for VM: {} with XML: {}", vmName, xmlConfig);
1023+
}
1024+
1025+
logger.info("isAttach value: {}", isAttach);
1026+
1027+
String result = virshCmd.execute();
1028+
1029+
if (result != null) {
1030+
String action = isAttach ? "attach" : "detach";
1031+
logger.error("Failed to {} vHBA device: {}", action, result);
1032+
return new com.cloud.agent.api.UpdateHostVhbaDeviceAnswer(false, vhbaName, vmName, xmlConfig, isAttach);
1033+
}
1034+
1035+
String action = isAttach ? "attached to" : "detached from";
1036+
logger.info("Successfully {} vHBA device for VM {}", action, vmName);
1037+
return new com.cloud.agent.api.UpdateHostVhbaDeviceAnswer(true, vhbaName, vmName, xmlConfig, isAttach);
1038+
1039+
} catch (Exception e) {
1040+
String action = isAttach ? "attaching" : "detaching";
1041+
logger.error("Error {} vHBA device: {}", action, e.getMessage(), e);
1042+
return new com.cloud.agent.api.UpdateHostVhbaDeviceAnswer(false, vhbaName, vmName, xmlConfig, isAttach);
1043+
}
1044+
} catch (Exception e) {
1045+
logger.error("Error in updateHostVhbaDevices: " + e.getMessage(), e);
1046+
return new com.cloud.agent.api.UpdateHostVhbaDeviceAnswer(false, null, null, null, false);
1047+
}
1048+
}
7741049
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtlistHostUsbDevicesCommandWrapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import com.cloud.resource.ResourceWrapper;
2727

2828
@ResourceWrapper(handles = ListHostUsbDeviceCommand.class)
29-
public final class LibvirtlistHostUsbDevicesCommandWrapper
29+
public final class LibvirtListHostUsbDevicesCommandWrapper
3030
extends CommandWrapper<ListHostUsbDeviceCommand, Answer, LibvirtComputingResource> {
3131
@Override
3232
public Answer execute(final ListHostUsbDeviceCommand command,

0 commit comments

Comments
 (0)