Skip to content

Commit c9e14c9

Browse files
committed
Merge pull request #1572 from wido/virtio-rng
CLOUDSTACK-9395: Add Virtio RNG device to Instances when configuredBy adding a Random Number Generator device to Instances we can prevent entropy starvation inside guest. The default source is /dev/random on the host, but this can be configured to another source when present, for example a hardware RNG. When enabled it will add the following to the Instance's XML definition: <rng model='virtio'> <backend model='random'>/dev/random</backend> </rng> If the Instance has the proper support, which most modern distributions have, it will have a /dev/hwrng device which it can use for gathering entropy. * pr/1572: CLOUDSTACK-9395: Add Virtio RNG device to Instances when configured Signed-off-by: Rajani Karuturi <rajani.karuturi@accelerite.com>
2 parents 8d1f1a6 + 0beb41b commit c9e14c9

6 files changed

Lines changed: 207 additions & 1 deletion

File tree

agent/conf/agent.properties

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,22 @@ hypervisor.type=kvm
152152
# kvmclock.disable=false
153153
# Some newer linux kernels are incapable of reliably migrating vms with kvmclock
154154
# This is a workaround for the bug, admin can set this to true per-host
155+
#
156+
# vm.rng.enable=false
157+
# This enabled the VirtIO Random Number Generator device for guests.
158+
#
159+
# vm.rng.model=random
160+
# The model of VirtIO Random Number Generator (RNG) to present to the Guest.
161+
# Currently only 'random' is supported.
162+
#
163+
# vm.rng.path=/dev/random
164+
# Local Random Number Device Generator to use for VirtIO RNG for Guests.
165+
# This is usually /dev/random, but per platform this might be different
166+
#
167+
# vm.rng.rate.bytes=2048
168+
# The amount of bytes the Guest may request/obtain from the RNG in the period
169+
# specified below.
170+
#
171+
# vm.rng.rate.period=1000
172+
# The number of milliseconds in which the guest is allowed to obtain the bytes
173+
# specified above.

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import javax.ejb.Local;
4545
import javax.naming.ConfigurationException;
4646

47+
import com.google.common.base.Strings;
4748
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
4849
import org.apache.cloudstack.storage.to.VolumeObjectTO;
4950
import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
@@ -115,6 +116,8 @@
115116
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy;
116117
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef;
117118
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef;
119+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
120+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
118121
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtRequestWrapper;
119122
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtUtilitiesHelper;
120123
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
@@ -244,6 +247,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
244247
protected long _diskActivityCheckFileSizeMin = 10485760; // 10MB
245248
protected int _diskActivityCheckTimeoutSeconds = 120; // 120s
246249
protected long _diskActivityInactiveThresholdMilliseconds = 30000; // 30s
250+
protected boolean _rngEnable = false;
251+
protected RngBackendModel _rngBackendModel = RngBackendModel.RANDOM;
252+
protected String _rngPath = "/dev/random";
253+
protected int _rngRatePeriod = 1000;
254+
protected int _rngRateBytes = 2048;
247255

248256
private final Map <String, String> _pifs = new HashMap<String, String>();
249257
private final Map<String, VmStats> _vmStats = new ConcurrentHashMap<String, VmStats>();
@@ -803,6 +811,27 @@ public boolean configure(final String name, final Map<String, Object> params) th
803811
_noKvmClock = true;
804812
}
805813

814+
value = (String) params.get("vm.rng.enable");
815+
if (Boolean.parseBoolean(value)) {
816+
_rngEnable = true;
817+
818+
value = (String) params.get("vm.rng.model");
819+
if (!Strings.isNullOrEmpty(value)) {
820+
_rngBackendModel = RngBackendModel.valueOf(value.toUpperCase());
821+
}
822+
823+
value = (String) params.get("vm.rng.path");
824+
if (!Strings.isNullOrEmpty(value)) {
825+
_rngPath = value;
826+
}
827+
828+
value = (String) params.get("vm.rng.rate.bytes");
829+
_rngRateBytes = NumbersUtil.parseInt(value, new Integer(_rngRateBytes));
830+
831+
value = (String) params.get("vm.rng.rate.period");
832+
_rngRatePeriod = NumbersUtil.parseInt(value, new Integer(_rngRatePeriod));
833+
}
834+
806835
LibvirtConnection.initialize(_hypervisorURI);
807836
Connect conn = null;
808837
try {
@@ -1983,6 +2012,11 @@ So if getMinSpeed() returns null we fall back to getSpeed().
19832012
devices.addDevice(vserial);
19842013
}
19852014

2015+
if (_rngEnable) {
2016+
final RngDef rngDevice = new RngDef(_rngPath, _rngBackendModel, _rngRateBytes, _rngRatePeriod);
2017+
devices.addDevice(rngDevice);
2018+
}
2019+
19862020
final VideoDef videoCard = new VideoDef(_videoHw, _videoRam);
19872021
devices.addDevice(videoCard);
19882022

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import javax.xml.parsers.DocumentBuilderFactory;
2626
import javax.xml.parsers.ParserConfigurationException;
2727

28+
import com.google.common.base.Strings;
2829
import org.apache.log4j.Logger;
2930
import org.w3c.dom.Document;
3031
import org.w3c.dom.Element;
@@ -36,11 +37,14 @@
3637
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
3738
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
3839
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
40+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
41+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
3942

4043
public class LibvirtDomainXMLParser {
4144
private static final Logger s_logger = Logger.getLogger(LibvirtDomainXMLParser.class);
4245
private final List<InterfaceDef> interfaces = new ArrayList<InterfaceDef>();
4346
private final List<DiskDef> diskDefs = new ArrayList<DiskDef>();
47+
private final List<RngDef> rngDefs = new ArrayList<RngDef>();
4448
private Integer vncPort;
4549
private String desc;
4650

@@ -189,6 +193,25 @@ public boolean parseDomainXML(String domXML) {
189193
}
190194
}
191195

196+
NodeList rngs = devices.getElementsByTagName("rng");
197+
for (int i = 0; i < rngs.getLength(); i++) {
198+
RngDef def = null;
199+
Element rng = (Element)rngs.item(i);
200+
String backendModel = getAttrValue("backend", "model", rng);
201+
String path = getTagValue("backend", rng);
202+
String bytes = getAttrValue("rate", "bytes", rng);
203+
String period = getAttrValue("rate", "period", rng);
204+
205+
if (Strings.isNullOrEmpty(backendModel)) {
206+
def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period));
207+
} else {
208+
def = new RngDef(path, RngBackendModel.valueOf(backendModel.toUpperCase()),
209+
Integer.parseInt(bytes), Integer.parseInt(period));
210+
}
211+
212+
rngDefs.add(def);
213+
}
214+
192215
return true;
193216
} catch (ParserConfigurationException e) {
194217
s_logger.debug(e.toString());
@@ -234,6 +257,10 @@ public List<DiskDef> getDisks() {
234257
return diskDefs;
235258
}
236259

260+
public List<RngDef> getRngs() {
261+
return rngDefs;
262+
}
263+
237264
public String getDescription() {
238265
return desc;
239266
}

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,107 @@ public String toString() {
13661366
}
13671367
}
13681368

1369+
public static class RngDef {
1370+
enum RngModel {
1371+
VIRTIO("virtio");
1372+
String model;
1373+
1374+
RngModel(String model) {
1375+
this.model = model;
1376+
}
1377+
1378+
@Override
1379+
public String toString() {
1380+
return model;
1381+
}
1382+
}
1383+
1384+
enum RngBackendModel {
1385+
RANDOM("random"), EGD("egd");
1386+
String model;
1387+
1388+
RngBackendModel(String model) {
1389+
this.model = model;
1390+
}
1391+
1392+
@Override
1393+
public String toString() {
1394+
return model;
1395+
}
1396+
}
1397+
1398+
private String path = "/dev/random";
1399+
private RngModel rngModel = RngModel.VIRTIO;
1400+
private RngBackendModel rngBackendModel = RngBackendModel.RANDOM;
1401+
private int rngRateBytes = 2048;
1402+
private int rngRatePeriod = 1000;
1403+
1404+
public RngDef(String path) {
1405+
this.path = path;
1406+
}
1407+
1408+
public RngDef(String path, int rngRateBytes, int rngRatePeriod) {
1409+
this.path = path;
1410+
this.rngRateBytes = rngRateBytes;
1411+
this.rngRatePeriod = rngRatePeriod;
1412+
}
1413+
1414+
public RngDef(RngModel rngModel) {
1415+
this.rngModel = rngModel;
1416+
}
1417+
1418+
public RngDef(RngBackendModel rngBackendModel) {
1419+
this.rngBackendModel = rngBackendModel;
1420+
}
1421+
1422+
public RngDef(String path, RngBackendModel rngBackendModel) {
1423+
this.path = path;
1424+
this.rngBackendModel = rngBackendModel;
1425+
}
1426+
1427+
public RngDef(String path, RngBackendModel rngBackendModel, int rngRateBytes, int rngRatePeriod) {
1428+
this.path = path;
1429+
this.rngBackendModel = rngBackendModel;
1430+
this.rngRateBytes = rngRateBytes;
1431+
this.rngRatePeriod = rngRatePeriod;
1432+
}
1433+
1434+
public RngDef(String path, RngModel rngModel) {
1435+
this.path = path;
1436+
this.rngModel = rngModel;
1437+
}
1438+
1439+
public String getPath() {
1440+
return path;
1441+
}
1442+
1443+
public RngBackendModel getRngBackendModel() {
1444+
return rngBackendModel;
1445+
}
1446+
1447+
public RngModel getRngModel() {
1448+
return rngModel;
1449+
}
1450+
1451+
public int getRngRateBytes() {
1452+
return rngRateBytes;
1453+
}
1454+
1455+
public int getRngRatePeriod() {
1456+
return rngRatePeriod;
1457+
}
1458+
1459+
@Override
1460+
public String toString() {
1461+
StringBuilder rngBuilder = new StringBuilder();
1462+
rngBuilder.append("<rng model='" + rngModel + "'>\n");
1463+
rngBuilder.append("<rate period='" + rngRatePeriod + "' bytes='" + rngRateBytes + "' />\n");
1464+
rngBuilder.append("<backend model='" + rngBackendModel + "'>" + path + "</backend>");
1465+
rngBuilder.append("</rng>\n");
1466+
return rngBuilder.toString();
1467+
}
1468+
}
1469+
13691470
public void setHvsType(String hvs) {
13701471
_hvsType = hvs;
13711472
}

plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
2525
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
26+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
2627

2728
public class LibvirtDomainXMLParserTest extends TestCase {
2829

@@ -164,6 +165,10 @@ public void testDomainXMLParser() {
164165
"<alias name='balloon0'/>" +
165166
"<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>" +
166167
"</memballoon>" +
168+
"<rng model='virtio'>" +
169+
"<rate period='5000' bytes='4096' />" +
170+
"<backend model='random'>/dev/random</backend>" +
171+
"</rng>" +
167172
"</devices>" +
168173
"<seclabel type='none'/>" +
169174
"</domain>";
@@ -190,5 +195,11 @@ public void testDomainXMLParser() {
190195
assertEquals(ifModel, ifs.get(i).getModel());
191196
assertEquals(ifType, ifs.get(i).getNetType());
192197
}
198+
199+
List<RngDef> rngs = parser.getRngs();
200+
assertEquals("/dev/random", rngs.get(0).getPath());
201+
assertEquals(RngDef.RngBackendModel.RANDOM, rngs.get(0).getRngBackendModel());
202+
assertEquals(4096, rngs.get(0).getRngRateBytes());
203+
assertEquals(5000, rngs.get(0).getRngRatePeriod());
193204
}
194-
}
205+
}

plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,18 @@ public void testHypervEnlightDef() {
118118
assertTrue((hostOsVersion.first() == 6 && hostOsVersion.second() >= 5) || (hostOsVersion.first() >= 7));
119119
}
120120

121+
public void testChannelDef() {
122+
LibvirtVMDef.RngDef.RngBackendModel backendModel = LibvirtVMDef.RngDef.RngBackendModel.RANDOM;
123+
String path = "/dev/random";
124+
int period = 2000;
125+
int bytes = 2048;
126+
127+
LibvirtVMDef.RngDef def = new LibvirtVMDef.RngDef(path, backendModel, bytes, period);
128+
assertEquals(def.getPath(), path);
129+
assertEquals(def.getRngBackendModel(), backendModel);
130+
assertEquals(def.getRngModel(), LibvirtVMDef.RngDef.RngModel.VIRTIO);
131+
assertEquals(def.getRngRateBytes(), bytes);
132+
assertEquals(def.getRngRatePeriod(), period);
133+
}
134+
121135
}

0 commit comments

Comments
 (0)