Skip to content

Commit f3510c2

Browse files
syedskattoju4
authored andcommitted
ISCI Session cleanup for KVM managed storage
1 parent 7c7efe7 commit f3510c2

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
5050
import com.cloud.resource.RequestWrapper;
51+
import com.cloud.hypervisor.kvm.storage.IscsiStorageCleanupMonitor;
5152
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
5253
import org.apache.cloudstack.storage.to.TemplateObjectTO;
5354
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@@ -1086,6 +1087,10 @@ public boolean configure(final String name, final Map<String, Object> params) th
10861087
storageProcessor.configure(name, params);
10871088
storageHandler = new StorageSubsystemCommandHandlerBase(storageProcessor);
10881089

1090+
IscsiStorageCleanupMonitor isciCleanupMonitor = new IscsiStorageCleanupMonitor();
1091+
final Thread cleanupMonitor = new Thread(isciCleanupMonitor);
1092+
cleanupMonitor.start();
1093+
10891094
return true;
10901095
}
10911096

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package com.cloud.hypervisor.kvm.storage;
2+
3+
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
4+
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
5+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
6+
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
7+
import org.apache.log4j.Logger;
8+
import org.libvirt.Connect;
9+
import org.libvirt.Domain;
10+
import org.libvirt.LibvirtException;
11+
12+
import java.io.File;
13+
import java.nio.file.Files;
14+
import java.nio.file.Paths;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
public class IscsiStorageCleanupMonitor implements Runnable{
20+
private static final Logger s_logger = Logger.getLogger(IscsiStorageCleanupMonitor.class);
21+
private static final int CLEANUP_INTERVAL_SEC = 60; // check every X seconds
22+
private static final String ISCSI_PATH_PREFIX = "/dev/disk/by-path";
23+
private static final String KEYWORD_ISCSI = "iscsi";
24+
private static final String KEYWORD_IQN = "iqn";
25+
26+
private IscsiAdmStorageAdaptor iscsiStorageAdaptor;
27+
28+
private Map<String, Boolean> diskStatusMap;
29+
30+
public IscsiStorageCleanupMonitor() {
31+
diskStatusMap = new HashMap<>();
32+
s_logger.debug("Initialize cleanup thread");
33+
iscsiStorageAdaptor = new IscsiAdmStorageAdaptor();
34+
}
35+
36+
37+
private class Monitor extends ManagedContextRunnable {
38+
39+
@Override
40+
protected void runInContext() {
41+
Connect conn = null;
42+
try {
43+
conn = LibvirtConnection.getConnection();
44+
45+
//populate all the iscsi disks currently attached to this host
46+
diskStatusMap.clear();
47+
File[] iscsiVolumes = new File(ISCSI_PATH_PREFIX).listFiles();
48+
49+
if (iscsiVolumes == null || iscsiVolumes.length == 0) {
50+
s_logger.debug("No iscsi sessions found for cleanup");
51+
return;
52+
}
53+
54+
for( File v : iscsiVolumes) {
55+
if (isIscsiDisk(v.getAbsolutePath())) {
56+
s_logger.debug("found iscsi disk by cleanup thread, marking inactive:" + v.getAbsolutePath());
57+
diskStatusMap.put(v.getAbsolutePath(), false);
58+
}
59+
}
60+
61+
// check if they belong to any VM
62+
int[] domains = conn.listDomains();
63+
s_logger.debug(String.format(" ********* FOUND %d DOMAINS ************", domains.length));
64+
for (int domId : domains) {
65+
Domain dm = conn.domainLookupByID(domId);
66+
final String domXml = dm.getXMLDesc(0);
67+
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
68+
parser.parseDomainXML(domXml);
69+
List<LibvirtVMDef.DiskDef> disks = parser.getDisks();
70+
71+
//check the volume map. If an entry exists change the status to True
72+
for (final LibvirtVMDef.DiskDef disk : disks) {
73+
if (diskStatusMap.containsKey(disk.getDiskPath())) {
74+
diskStatusMap.put(disk.getDiskPath(), true);
75+
s_logger.debug("active disk found by cleanup thread" + disk.getDiskPath());
76+
}
77+
}
78+
}
79+
80+
// the ones where the state is false, they are stale. They may be
81+
// removed we go through each volume which is false, check iscsiadm,
82+
// if the volume still exisits, logout of that volume and remove it from the map
83+
84+
// XXX: It is possible that someone had manually added an iSCSI volume.
85+
// we would not be able to detect that
86+
for (String diskPath : diskStatusMap.keySet()) {
87+
if (!diskStatusMap.get(diskPath)) {
88+
if (Files.exists(Paths.get(diskPath))) {
89+
try {
90+
s_logger.info("Cleaning up disk " + diskPath);
91+
iscsiStorageAdaptor.disconnectPhysicalDiskByPath(diskPath);
92+
} catch (Exception e) {
93+
s_logger.warn("[ignored] Error cleaning up " + diskPath, e);
94+
}
95+
}
96+
diskStatusMap.remove(diskPath);
97+
}
98+
}
99+
100+
} catch (LibvirtException e) {
101+
s_logger.warn("[ignored] Error tryong to cleanup ", e);
102+
}
103+
}
104+
105+
private boolean isIscsiDisk(String path) {
106+
return path.startsWith(ISCSI_PATH_PREFIX) && path.contains(KEYWORD_ISCSI) && path.contains(KEYWORD_IQN);
107+
}
108+
}
109+
110+
@Override
111+
public void run() {
112+
while(true) {
113+
try {
114+
Thread.sleep(CLEANUP_INTERVAL_SEC * 1000);
115+
} catch (InterruptedException e) {
116+
s_logger.debug("[ignored] interupted between heartbeats.");
117+
}
118+
119+
Thread monitorThread = new Thread(new Monitor());
120+
monitorThread.start();
121+
try {
122+
monitorThread.join();
123+
} catch (InterruptedException e) {
124+
s_logger.debug("[ignored] interupted joining monitor.");
125+
}
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)