diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java b/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java index 112ab61e50b..347ff87ac9d 100755 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtComputingResource.java @@ -30,12 +30,15 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; +import java.text.DateFormat; import java.text.MessageFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -154,6 +157,7 @@ import com.cloud.agent.resource.computing.KVMHABase.NfsStoragePool; import com.cloud.agent.resource.computing.LibvirtVMDef.ConsoleDef; import com.cloud.agent.resource.computing.LibvirtVMDef.DevicesDef; import com.cloud.agent.resource.computing.LibvirtVMDef.DiskDef; +import com.cloud.agent.resource.computing.LibvirtVMDef.DiskDef.diskProtocol; import com.cloud.agent.resource.computing.LibvirtVMDef.FeaturesDef; import com.cloud.agent.resource.computing.LibvirtVMDef.GraphicDef; import com.cloud.agent.resource.computing.LibvirtVMDef.GuestDef; @@ -1298,6 +1302,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool(cmd .getPool().getUuid()); + + if (primaryPool.getType() == StoragePoolType.RBD) { + s_logger.debug("Snapshots are not supported on RBD volumes"); + return new ManageSnapshotAnswer(cmd, false, + "Snapshots are not supported on RBD volumes"); + } + KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(cmd .getVolumePath()); if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING @@ -1644,16 +1655,43 @@ public class LibvirtComputingResource extends ServerResourceBase implements + templateInstallFolder; _storage.mkdirs(tmpltPath); - Script command = new Script(_createTmplPath, _cmdsTimeout, s_logger); - command.add("-f", disk.getPath()); - command.add("-t", tmpltPath); - command.add("-n", cmd.getUniqueName() + ".qcow2"); + if (primary.getType() != StoragePoolType.RBD) { + Script command = new Script(_createTmplPath, _cmdsTimeout, s_logger); + command.add("-f", disk.getPath()); + command.add("-t", tmpltPath); + command.add("-n", cmd.getUniqueName() + ".qcow2"); - String result = command.execute(); + String result = command.execute(); - if (result != null) { - s_logger.debug("failed to create template: " + result); - return new CreatePrivateTemplateAnswer(cmd, false, result); + if (result != null) { + s_logger.debug("failed to create template: " + result); + return new CreatePrivateTemplateAnswer(cmd, false, result); + } + } else { + s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName()); + Script.runSimpleBashScript("qemu-img convert" + + " -f raw -O qcow2 " + + KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), + primary.getSourcePort(), + primary.getAuthUserName(), + primary.getAuthSecret(), + disk.getPath()) + + " " + tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); + File templateProp = new File(tmpltPath + "/template.properties"); + if (!templateProp.exists()) { + templateProp.createNewFile(); + } + + String templateContent = "filename=" + cmd.getUniqueName() + ".qcow2" + System.getProperty("line.separator"); + + DateFormat dateFormat = new SimpleDateFormat("MM_dd_yyyy"); + Date date = new Date(); + templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator"); + + FileOutputStream templFo = new FileOutputStream(templateProp); + templFo.write(templateContent.getBytes()); + templFo.flush(); + templFo.close(); } Map params = new HashMap(); @@ -1756,8 +1794,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements protected Answer execute(ModifyStoragePoolCommand cmd) { KVMStoragePool storagepool = _storagePoolMgr.createStoragePool(cmd - .getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool() - .getPath(), cmd.getPool().getType()); + .getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), + cmd.getPool().getPath(), cmd.getPool().getUserInfo(), cmd.getPool().getType()); if (storagepool == null) { return new Answer(cmd, false, " Failed to create storage pool"); } @@ -2624,10 +2662,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements } else { int devId = (int) volume.getDeviceId(); - if (volume.getType() == Volume.Type.DATADISK) { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, - DiskDef.diskBus.VIRTIO, - DiskDef.diskFmtType.QCOW2); + if (pool.getType() == StoragePoolType.RBD) { + /* + For RBD pools we use the secret mechanism in libvirt. + We store the secret under the UUID of the pool, that's why + we pass the pool's UUID as the authSecret + */ + disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(), + pool.getAuthUserName(), pool.getUuid(), + devId, diskBusType, diskProtocol.RBD); + } else if (volume.getType() == Volume.Type.DATADISK) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, + DiskDef.diskBus.VIRTIO, + DiskDef.diskFmtType.QCOW2); } else { disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.diskFmtType.QCOW2); @@ -2982,8 +3029,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements try { KVMStoragePool localStoragePool = _storagePoolMgr - .createStoragePool(_localStorageUUID, "localhost", - _localStoragePath, StoragePoolType.Filesystem); + .createStoragePool(_localStorageUUID, "localhost", -1, + _localStoragePath, "", StoragePoolType.Filesystem); com.cloud.agent.api.StoragePoolInfo pi = new com.cloud.agent.api.StoragePoolInfo( localStoragePool.getUuid(), cmd.getPrivateIpAddress(), _localStoragePath, _localStoragePath, @@ -4083,5 +4130,4 @@ public class LibvirtComputingResource extends ServerResourceBase implements return new Answer(cmd, success, ""); } - } diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtSecretDef.java b/agent/src/com/cloud/agent/resource/computing/LibvirtSecretDef.java new file mode 100644 index 00000000000..f7e10c38ddf --- /dev/null +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtSecretDef.java @@ -0,0 +1,106 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.resource.computing; + +public class LibvirtSecretDef { + + public enum usage { + VOLUME("volume"), CEPH("ceph"); + String _usage; + + usage(String usage) { + _usage = usage; + } + + @Override + public String toString() { + return _usage; + } + } + + private usage _usage; + private boolean _ephemeral; + private boolean _private; + private String _uuid; + private String _description; + private String _cephName; + private String _volumeVolume; + + public LibvirtSecretDef (usage usage, String uuid) { + _usage = usage; + _uuid = uuid; + } + + public LibvirtSecretDef (usage usage, String uuid, String description) { + _usage = usage; + _uuid = uuid; + _description = description; + } + + public boolean getEphemeral() { + return _ephemeral; + } + + public boolean getPrivate() { + return _private; + } + + public String getUuid() { + return _uuid; + } + + public String getDescription() { + return _description; + } + + public String getVolumeVolume() { + return _volumeVolume; + } + + public String getCephName() { + return _cephName; + } + + public void setVolumeVolume(String volume) { + _volumeVolume = volume; + } + + public void setCephName(String name) { + _cephName = name; + } + + @Override + public String toString() { + StringBuilder secretBuilder = new StringBuilder(); + secretBuilder.append("\n"); + secretBuilder.append("" + _uuid + "\n"); + if (_description != null) { + secretBuilder.append("" + _description + "\n"); + } + secretBuilder.append("\n"); + if (_usage == _usage.VOLUME) { + secretBuilder.append("" + _volumeVolume + "\n"); + } + if (_usage == _usage.CEPH) { + secretBuilder.append("" + _cephName + "\n"); + } + secretBuilder.append("\n"); + secretBuilder.append("\n"); + return secretBuilder.toString(); + } + +} diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolDef.java b/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolDef.java index 582cd2e2e6d..9c285284563 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolDef.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolDef.java @@ -18,7 +18,7 @@ package com.cloud.agent.resource.computing; public class LibvirtStoragePoolDef { public enum poolType { - ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir"); + ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir"), RBD("rbd"); String _poolType; poolType(String poolType) { @@ -31,12 +31,41 @@ public class LibvirtStoragePoolDef { } } + public enum authType { + CHAP("chap"), CEPH("ceph"); + String _authType; + + authType(String authType) { + _authType = authType; + } + + @Override + public String toString() { + return _authType; + } + } + private poolType _poolType; private String _poolName; private String _uuid; private String _sourceHost; + private int _sourcePort; private String _sourceDir; private String _targetPath; + private String _authUsername; + private authType _authType; + private String _secretUuid; + + public LibvirtStoragePoolDef(poolType type, String poolName, String uuid, + String host, int port, String dir, String targetPath) { + _poolType = type; + _poolName = poolName; + _uuid = uuid; + _sourceHost = host; + _sourcePort = port; + _sourceDir = dir; + _targetPath = targetPath; + } public LibvirtStoragePoolDef(poolType type, String poolName, String uuid, String host, String dir, String targetPath) { @@ -48,6 +77,20 @@ public class LibvirtStoragePoolDef { _targetPath = targetPath; } + public LibvirtStoragePoolDef(poolType type, String poolName, String uuid, + String sourceHost, int sourcePort, String dir, String authUsername, + authType authType, String secretUuid) { + _poolType = type; + _poolName = poolName; + _uuid = uuid; + _sourceHost = sourceHost; + _sourcePort = sourcePort; + _sourceDir = dir; + _authUsername = authUsername; + _authType = authType; + _secretUuid = secretUuid; + } + public String getPoolName() { return _poolName; } @@ -60,6 +103,10 @@ public class LibvirtStoragePoolDef { return _sourceHost; } + public int getSourcePort() { + return _sourcePort; + } + public String getSourceDir() { return _sourceDir; } @@ -68,6 +115,18 @@ public class LibvirtStoragePoolDef { return _targetPath; } + public String getAuthUserName() { + return _authUsername; + } + + public String getSecretUUID() { + return _secretUuid; + } + + public authType getAuthType() { + return _authType; + } + @Override public String toString() { StringBuilder storagePoolBuilder = new StringBuilder(); @@ -81,9 +140,22 @@ public class LibvirtStoragePoolDef { storagePoolBuilder.append("\n"); storagePoolBuilder.append("\n"); } - storagePoolBuilder.append("\n"); - storagePoolBuilder.append("" + _targetPath + "\n"); - storagePoolBuilder.append("\n"); + if (_poolType == poolType.RBD) { + storagePoolBuilder.append("\n"); + storagePoolBuilder.append("\n"); + storagePoolBuilder.append("" + _sourceDir + "\n"); + if (_authUsername != null) { + storagePoolBuilder.append("\n"); + storagePoolBuilder.append("\n"); + storagePoolBuilder.append("\n"); + } + storagePoolBuilder.append("\n"); + } + if (_poolType != poolType.RBD) { + storagePoolBuilder.append("\n"); + storagePoolBuilder.append("" + _targetPath + "\n"); + storagePoolBuilder.append("\n"); + } storagePoolBuilder.append("\n"); return storagePoolBuilder.toString(); } diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolXMLParser.java b/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolXMLParser.java index 5c45d76e82d..cff4c2b74aa 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolXMLParser.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtStoragePoolXMLParser.java @@ -51,15 +51,34 @@ public class LibvirtStoragePoolXMLParser { Element source = (Element) rootElement.getElementsByTagName( "source").item(0); String host = getAttrValue("host", "name", source); - String path = getAttrValue("dir", "path", source); - Element target = (Element) rootElement.getElementsByTagName( - "target").item(0); - String targetPath = getTagValue("path", target); + if (type.equalsIgnoreCase("rbd")) { + int port = Integer.parseInt(getAttrValue("host", "port", source)); + String pool = getTagValue("name", source); - return new LibvirtStoragePoolDef( - LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()), - poolName, uuid, host, path, targetPath); + Element auth = (Element) source.getElementsByTagName( + "auth").item(0); + + if (auth != null) { + String authUsername = auth.getAttribute("username"); + String authType = auth.getAttribute("type"); + return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()), + poolName, uuid, host, port, pool, authUsername, LibvirtStoragePoolDef.authType.valueOf(authType.toUpperCase()), uuid); + } else { + return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()), + poolName, uuid, host, port, pool, ""); + } + } else { + String path = getAttrValue("dir", "path", source); + + Element target = (Element) rootElement.getElementsByTagName( + "target").item(0); + String targetPath = getTagValue("path", target); + + return new LibvirtStoragePoolDef( + LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()), + poolName, uuid, host, path, targetPath); + } } catch (ParserConfigurationException e) { s_logger.debug(e.toString()); } catch (SAXException e) { diff --git a/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java b/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java index 8fd7815bf69..3b07bcd16b7 100644 --- a/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java +++ b/agent/src/com/cloud/agent/resource/computing/LibvirtVMDef.java @@ -338,7 +338,7 @@ public class LibvirtVMDef { } enum diskType { - FILE("file"), BLOCK("block"), DIRECTROY("dir"); + FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network"); String _diskType; diskType(String type) { @@ -351,6 +351,20 @@ public class LibvirtVMDef { } } + enum diskProtocol { + RBD("rbd"), SHEEPDOG("sheepdog"); + String _diskProtocol; + + diskProtocol(String protocol) { + _diskProtocol = protocol; + } + + @Override + public String toString() { + return _diskProtocol; + } + } + enum diskBus { IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML( "uml"), FDC("fdc"); @@ -382,7 +396,12 @@ public class LibvirtVMDef { private deviceType _deviceType; /* floppy, disk, cdrom */ private diskType _diskType; + private diskProtocol _diskProtocol; private String _sourcePath; + private String _sourceHost; + private int _sourcePort; + private String _authUserName; + private String _authSecretUUID; private String _diskLabel; private diskBus _bus; private diskFmtType _diskFmtType; /* qcow2, raw etc. */ @@ -461,6 +480,38 @@ public class LibvirtVMDef { _bus = bus; } + public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort, + String authUserName, String authSecretUUID, + int devId, diskBus bus, diskProtocol protocol) { + _diskType = diskType.NETWORK; + _deviceType = deviceType.DISK; + _diskFmtType = diskFmtType.RAW; + _sourcePath = diskName; + _sourceHost = sourceHost; + _sourcePort = sourcePort; + _authUserName = authUserName; + _authSecretUUID = authSecretUUID; + _diskLabel = getDevLabel(devId, bus); + _bus = bus; + _diskProtocol = protocol; + } + + public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort, + String authUserName, String authSecretUUID, + String diskLabel, diskBus bus, diskProtocol protocol) { + _diskType = diskType.NETWORK; + _deviceType = deviceType.DISK; + _diskFmtType = diskFmtType.RAW; + _sourcePath = diskName; + _sourceHost = sourceHost; + _sourcePort = sourcePort; + _authUserName = authUserName; + _authSecretUUID = authSecretUUID; + _diskLabel = diskLabel; + _bus = bus; + _diskProtocol = protocol; + } + public void setReadonly() { _readonly = true; } @@ -527,6 +578,18 @@ public class LibvirtVMDef { diskBuilder.append(" dev='" + _sourcePath + "'"); } diskBuilder.append("/>\n"); + } else if (_diskType == diskType.NETWORK) { + diskBuilder.append("\n"); + diskBuilder.append("\n"); + diskBuilder.append("\n"); + if (_authUserName != null) { + diskBuilder.append("\n"); + diskBuilder.append("\n"); + diskBuilder.append("\n"); + } } diskBuilder.append(" result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, "/bin/ping" + args); + if(result.first()) + return new Answer(cmd); + } catch (Exception e) { + s_logger.error("Unable to execute ping command on DomR (" + controlIp + "), domR may not be ready yet. failure due to " + + VmwareHelper.getExceptionMessage(e), e); + } + return new Answer(cmd,false,"PingTestCommand failed"); } protected Answer execute(CheckOnHostCommand cmd) { diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index eb82c75d823..e70ea4de308 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -17,10 +17,12 @@ package com.cloud.deploy; import java.util.ArrayList; +import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeSet; import javax.ejb.Local; import javax.naming.ConfigurationException; @@ -55,6 +57,7 @@ import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.resource.ResourceState; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.StoragePoolVO; @@ -98,6 +101,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Inject protected StoragePoolDao _storagePoolDao; @Inject protected CapacityDao _capacityDao; @Inject protected AccountManager _accountMgr; + @Inject protected StorageManager _storageMgr; @Inject(adapter=StoragePoolAllocator.class) protected Adapters _storagePoolAllocators; @@ -638,25 +642,56 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM"); boolean hostCanAccessPool = false; + boolean haveEnoughSpace = false; Map storage = new HashMap(); + TreeSet volumesOrderBySizeDesc = new TreeSet(new Comparator() { + @Override + public int compare(Volume v1, Volume v2) { + if(v1.getSize() < v2.getSize()) + return 1; + else + return -1; + } + }); + volumesOrderBySizeDesc.addAll(suitableVolumeStoragePools.keySet()); + boolean multipleVolume = volumesOrderBySizeDesc.size() > 1; for(Host potentialHost : suitableHosts){ - for(Volume vol : suitableVolumeStoragePools.keySet()){ + Map> volumeAllocationMap = new HashMap>(); + for(Volume vol : volumesOrderBySizeDesc){ + haveEnoughSpace = false; s_logger.debug("Checking if host: "+potentialHost.getId() +" can access any suitable storage pool for volume: "+ vol.getVolumeType()); List volumePoolList = suitableVolumeStoragePools.get(vol); hostCanAccessPool = false; for(StoragePool potentialSPool : volumePoolList){ if(hostCanAccessSPool(potentialHost, potentialSPool)){ - storage.put(vol, potentialSPool); hostCanAccessPool = true; + if(multipleVolume){ + List requestVolumes = null; + if(volumeAllocationMap.containsKey(potentialSPool)) + requestVolumes = volumeAllocationMap.get(potentialSPool); + else + requestVolumes = new ArrayList(); + requestVolumes.add(vol); + + if(!_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool)) + continue; + volumeAllocationMap.put(potentialSPool,requestVolumes); + } + storage.put(vol, potentialSPool); + haveEnoughSpace = true; break; } } if(!hostCanAccessPool){ break; } + if(!haveEnoughSpace) { + s_logger.warn("insufficient capacity to allocate all volumes"); + break; + } } - if(hostCanAccessPool){ - s_logger.debug("Found a potential host " + "id: "+potentialHost.getId() + " name: " +potentialHost.getName()+ " and associated storage pools for this VM"); + if(hostCanAccessPool && haveEnoughSpace){ + s_logger.debug("Found a potential host " + "id: "+potentialHost.getId() + " name: " +potentialHost.getName() + " and associated storage pools for this VM"); return new Pair>(potentialHost, storage); } } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 666e2691844..b45443e467c 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -331,9 +331,11 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } @Override + @ActionEvent(eventType = EventTypes.EVENT_ENABLE_STATIC_NAT, eventDescription = "enabling static nat") public boolean enableStaticNat(long ipId, long vmId, boolean isSystemVm) throws NetworkRuleConflictException, ResourceUnavailableException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); + UserContext.current().setEventDetails("Ip Id: " + ipId); IPAddressVO ipAddress = _ipAddressDao.findById(ipId); if (ipAddress == null) { @@ -1052,6 +1054,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } @Override + @ActionEvent(eventType = EventTypes.EVENT_DISABLE_STATIC_NAT, eventDescription = "disabling static nat", async=true) public boolean disableStaticNat(long ipId) throws ResourceUnavailableException, NetworkRuleConflictException, InsufficientAddressCapacityException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java index 67ad97c204b..59a02210d86 100755 --- a/server/src/com/cloud/storage/StorageManager.java +++ b/server/src/com/cloud/storage/StorageManager.java @@ -235,4 +235,6 @@ public interface StorageManager extends StorageService, Manager { String getSupportedImageFormatForCluster(Long clusterId); HypervisorType getHypervisorTypeFromFormat(ImageFormat format); -} + + boolean storagePoolHasEnoughSpace(List volume, StoragePool pool); +} diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index d94bada2866..a2ea98d872b 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -80,6 +80,7 @@ import com.cloud.api.commands.UpdateStoragePoolCmd; import com.cloud.api.commands.UploadVolumeCmd; import com.cloud.async.AsyncJobManager; import com.cloud.capacity.Capacity; +import com.cloud.capacity.CapacityManager; import com.cloud.capacity.CapacityState; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; @@ -133,6 +134,7 @@ import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; import com.cloud.server.ManagementServer; import com.cloud.server.ResourceTag.TaggedResourceType; +import com.cloud.server.StatsCollector; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.Storage.ImageFormat; @@ -274,6 +276,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag @Inject protected CapacityDao _capacityDao; @Inject + protected CapacityManager _capacityMgr; + @Inject protected DiskOfferingDao _diskOfferingDao; @Inject protected AccountDao _accountDao; @@ -352,6 +356,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag private StateMachine2 _volStateMachine; private int _customDiskOfferingMinSize = 1; private int _customDiskOfferingMaxSize = 1024; + private double _storageUsedThreshold = 1.0d; + private double _storageAllocatedThreshold = 1.0d; + protected BigDecimal _storageOverprovisioningFactor = new BigDecimal(1); public boolean share(VMInstanceVO vm, List vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException { @@ -955,6 +962,19 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag String time = configs.get("storage.cleanup.interval"); _storageCleanupInterval = NumbersUtil.parseInt(time, 86400); + String storageUsedThreshold = configDao.getValue(Config.StorageCapacityDisableThreshold.key()); + if (storageUsedThreshold != null) { + _storageUsedThreshold = Double.parseDouble(storageUsedThreshold); + } + + String storageAllocatedThreshold = configDao.getValue(Config.StorageAllocatedCapacityDisableThreshold.key()); + if (storageAllocatedThreshold != null) { + _storageAllocatedThreshold = Double.parseDouble(storageAllocatedThreshold); + } + + String globalStorageOverprovisioningFactor = configs.get("storage.overprovisioning.factor"); + _storageOverprovisioningFactor = new BigDecimal(NumbersUtil.parseFloat(globalStorageOverprovisioningFactor, 2.0f)); + s_logger.info("Storage cleanup enabled: " + _storageCleanupEnabled + ", interval: " + _storageCleanupInterval + ", template cleanup enabled: " + _templateCleanupEnabled); String workers = configs.get("expunge.workers"); @@ -1257,10 +1277,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (uriPath == null) { throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path"); } - } else if (uri.getScheme().equalsIgnoreCase("clvm")) { + } else if (uri.getScheme().equalsIgnoreCase("rbd")) { String uriPath = uri.getPath(); if (uriPath == null) { - throw new InvalidParameterValueException("host or path is null, should be clvm://localhost/path"); + throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool"); } } } catch (URISyntaxException e) { @@ -1283,6 +1303,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag String scheme = uri.getScheme(); String storageHost = uri.getHost(); String hostPath = uri.getPath(); + String userInfo = uri.getUserInfo(); int port = uri.getPort(); StoragePoolVO pool = null; if (s_logger.isDebugEnabled()) { @@ -1303,6 +1324,11 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag pool = new StoragePoolVO(StoragePoolType.Filesystem, "localhost", 0, hostPath); } else if (scheme.equalsIgnoreCase("sharedMountPoint")) { pool = new StoragePoolVO(StoragePoolType.SharedMountPoint, storageHost, 0, hostPath); + } else if (scheme.equalsIgnoreCase("rbd")) { + if (port == -1) { + port = 6789; + } + pool = new StoragePoolVO(StoragePoolType.RBD, storageHost, port, hostPath.replaceFirst("/", ""), userInfo); } else if (scheme.equalsIgnoreCase("PreSetup")) { pool = new StoragePoolVO(StoragePoolType.PreSetup, storageHost, 0, hostPath); } else if (scheme.equalsIgnoreCase("iscsi")) { @@ -1601,7 +1627,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.debug("creating pool " + pool.getName() + " on host " + hostId); if (pool.getPoolType() != StoragePoolType.NetworkFilesystem && pool.getPoolType() != StoragePoolType.Filesystem && pool.getPoolType() != StoragePoolType.IscsiLUN && pool.getPoolType() != StoragePoolType.Iscsi && pool.getPoolType() != StoragePoolType.VMFS && pool.getPoolType() != StoragePoolType.SharedMountPoint - && pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2) { + && pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2 && pool.getPoolType() != StoragePoolType.RBD) { s_logger.warn(" Doesn't support storage pool type " + pool.getPoolType()); return false; } @@ -3906,5 +3932,81 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag return HypervisorType.None; } } + + private boolean checkUsagedSpace(StoragePool pool){ + StatsCollector sc = StatsCollector.getInstance(); + if (sc != null) { + long totalSize = pool.getCapacityBytes(); + StorageStats stats = sc.getStoragePoolStats(pool.getId()); + if(stats == null){ + stats = sc.getStorageStats(pool.getId()); + } + if (stats != null) { + double usedPercentage = ((double)stats.getByteUsed() / (double)totalSize); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Checking pool " + pool.getId() + " for storage, totalSize: " + pool.getCapacityBytes() + ", usedBytes: " + stats.getByteUsed() + ", usedPct: " + usedPercentage + ", disable threshold: " + _storageUsedThreshold); + } + if (usedPercentage >= _storageUsedThreshold) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Insufficient space on pool: " + pool.getId() + " since its usage percentage: " +usedPercentage + " has crossed the pool.storage.capacity.disablethreshold: " + _storageUsedThreshold); + } + return false; + } + } + return true; + } + return false; + } + + @Override + public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool) { + if(volumes == null || volumes.isEmpty()) + return false; + + if(!checkUsagedSpace(pool)) + return false; + + // allocated space includes template of specified volume + StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); + long allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); + long totalAskingSize = 0; + for (Volume volume : volumes) { + if(volume.getTemplateId()!=null){ + VMTemplateVO tmpl = _templateDao.findById(volume.getTemplateId()); + if (tmpl.getFormat() != ImageFormat.ISO){ + allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, tmpl); + } + } + if(volume.getState() != Volume.State.Ready) + totalAskingSize = totalAskingSize + volume.getSize(); + } + + long totalOverProvCapacity; + if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) { + totalOverProvCapacity = _storageOverprovisioningFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue();// All this for the inaccuracy of floats for big number multiplication. + }else { + totalOverProvCapacity = pool.getCapacityBytes(); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Checking pool: " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + _storageAllocatedThreshold); + } + + double usedPercentage = (allocatedSizeWithtemplate + totalAskingSize) / (double)(totalOverProvCapacity); + if (usedPercentage > _storageAllocatedThreshold){ + if (s_logger.isDebugEnabled()) { + s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + " since its allocated percentage: " +usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + _storageAllocatedThreshold + ", skipping this pool"); + } + return false; + } + + if (totalOverProvCapacity < (allocatedSizeWithtemplate + totalAskingSize)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + ", not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + totalAskingSize); + } + return false; + } + return true; + } } diff --git a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java index 0a0f66ebc42..87cb065e285 100755 --- a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java @@ -17,6 +17,7 @@ package com.cloud.storage.allocator; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; @@ -27,7 +28,6 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.capacity.CapacityManager; -import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; @@ -41,13 +41,11 @@ import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; -import com.cloud.storage.StorageStats; -import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; -import com.cloud.storage.VMTemplateSwiftVO; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; import com.cloud.storage.Volume.Type; import com.cloud.storage.dao.StoragePoolDao; import com.cloud.storage.dao.StoragePoolHostDao; @@ -58,7 +56,6 @@ import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.swift.SwiftManager; import com.cloud.template.TemplateManager; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.Inject; import com.cloud.vm.DiskProfile; @@ -83,8 +80,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement long _extraBytesPerVolume = 0; Random _rand; boolean _dontMatter; - double _storageUsedThreshold = 1.0d; - double _storageAllocatedThreshold = 1.0d; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -97,17 +92,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement _extraBytesPerVolume = 0; - - String storageUsedThreshold = _configDao.getValue(Config.StorageCapacityDisableThreshold.key()); - if (storageUsedThreshold != null) { - _storageUsedThreshold = Double.parseDouble(storageUsedThreshold); - } - - String storageAllocatedThreshold = _configDao.getValue(Config.StorageAllocatedCapacityDisableThreshold.key()); - if (storageAllocatedThreshold != null) { - _storageAllocatedThreshold = Double.parseDouble(storageAllocatedThreshold); - } - _rand = new Random(System.currentTimeMillis()); _dontMatter = Boolean.parseBoolean(configs.get("storage.overwrite.provisioning")); @@ -192,60 +176,15 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return false; } - // check the used size against the total size, skip this host if it's greater than the configured - // capacity check "storage.capacity.threshold" - if (sc != null) { - long totalSize = pool.getCapacityBytes(); - StorageStats stats = sc.getStoragePoolStats(pool.getId()); - if(stats == null){ - stats = sc.getStorageStats(pool.getId()); - } - if (stats != null) { - double usedPercentage = ((double)stats.getByteUsed() / (double)totalSize); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Attempting to look for pool " + pool.getId() + " for storage, totalSize: " + pool.getCapacityBytes() + ", usedBytes: " + stats.getByteUsed() + ", usedPct: " + usedPercentage + ", disable threshold: " + _storageUsedThreshold); - } - if (usedPercentage >= _storageUsedThreshold) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage since its usage percentage: " +usedPercentage + " has crossed the pool.storage.capacity.disablethreshold: " + _storageUsedThreshold + ", skipping this pool"); - } - return false; - } - } - } - - long totalAllocatedSize = _capacityMgr.getAllocatedPoolCapacity(pool, null); - long askingSize = dskCh.getSize(); - - long totalOverProvCapacity; - if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) { - totalOverProvCapacity = _storageOverprovisioningFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue();// All this for the inaccuracy of floats for big number multiplication. - }else { - totalOverProvCapacity = pool.getCapacityBytes(); - } - if (s_logger.isDebugEnabled()) { - s_logger.debug("Attempting to look for pool " + pool.getId() + " for storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + totalAllocatedSize + ", askingSize : " + askingSize + ", allocated disable threshold: " + _storageAllocatedThreshold); - } - - double usedPercentage = (totalAllocatedSize + askingSize) / (double)(totalOverProvCapacity); - if (usedPercentage > _storageAllocatedThreshold){ - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage since its allocated percentage: " +usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + _storageAllocatedThreshold + ", skipping this pool"); - } - return false; - } - - if (totalOverProvCapacity < (totalAllocatedSize + askingSize)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage, not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + totalAllocatedSize + ", askingSize : " + askingSize); - } - - return false; - } - - return true; + // check capacity + Volume volume = _volumeDao.findById(dskCh.getVolumeId()); + List requestVolumes = new ArrayList(); + requestVolumes.add(volume); + return _storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool); } + + @Override public String chooseStorageIp(VirtualMachine vm, Host host, Host storage) { diff --git a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java index 18c8cdeb466..006931d4814 100644 --- a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java @@ -31,18 +31,25 @@ import org.apache.log4j.Logger; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.server.StatsCollector; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.user.Account; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.utils.component.Inject; @Local(value=StoragePoolAllocator.class) public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(FirstFitStoragePoolAllocator.class); protected String _allocationAlgorithm = "random"; + + @Inject + DiskOfferingDao _diskOfferingDao; @Override public boolean allocatorIsCorrectType(DiskProfile dskCh) { @@ -97,10 +104,16 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator { s_logger.debug("FirstFitStoragePoolAllocator has " + pools.size() + " pools to check for allocation"); } + DiskOfferingVO diskOffering = _diskOfferingDao.findById(dskCh.getDiskOfferingId()); for (StoragePoolVO pool: pools) { if(suitablePools.size() == returnUpTo){ break; } + if (diskOffering.getSystemUse() && pool.getPoolType() == StoragePoolType.RBD) { + s_logger.debug("Skipping RBD pool " + pool.getName() + " as a suitable pool. RBD is not supported for System VM's"); + continue; + } + if (checkPool(avoid, pool, dskCh, template, null, sc, plan)) { suitablePools.add(pool); } diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 50dcf384205..6e3f9c14116 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -79,6 +79,7 @@ import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.SnapshotScheduleVO; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolVO; @@ -298,6 +299,13 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma } } StoragePoolVO srcPool = _storagePoolDao.findById(volume.getPoolId()); + + // RBD volumes do not support snapshotting in the way CloudStack does it. + // For now we leave the snapshot feature disabled for RBD volumes + if (srcPool.getPoolType() == StoragePoolType.RBD) { + throw new CloudRuntimeException("RBD volumes do not support snapshotting"); + } + ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshotId, volume.getPath(), srcPool, preSnapshotPath, snapshot.getName(), vmName); ManageSnapshotAnswer answer = (ManageSnapshotAnswer) sendToPool(volume, cmd); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index a15222aa203..ae5f58160a4 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1477,6 +1477,7 @@ CREATE TABLE `cloud`.`storage_pool` ( `available_bytes` bigint unsigned, `capacity_bytes` bigint unsigned, `host_address` varchar(255) NOT NULL COMMENT 'FQDN or IP of storage server', + `user_info` varchar(255) NULL COMMENT 'Authorization information for the storage pool. Used by network filesystems', `path` varchar(255) NOT NULL COMMENT 'Filesystem path that is shared', `created` datetime COMMENT 'date the pool created', `removed` datetime COMMENT 'date removed if not null', diff --git a/setup/db/db/schema-303to40.sql b/setup/db/db/schema-303to40.sql new file mode 100644 index 00000000000..87dd1d3c789 --- /dev/null +++ b/setup/db/db/schema-303to40.sql @@ -0,0 +1,22 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade from 3.0.3 to 4.0.0; +--; + +ALTER TABLE `storage_pool` ADD `user_info` VARCHAR( 255 ) NULL COMMENT 'Authorization information for the storage pool. Used by network filesystems' AFTER `host_address`; diff --git a/ui/index.jsp b/ui/index.jsp index 1ee8f0bc1b9..94fbdde88bd 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -2046,6 +2046,7 @@ dictionary = { 'label.SR.name ': '', 'label.SharedMountPoint': '', 'label.clvm': '', +'label.rbd': '', 'label.volgroup': '', 'label.VMFS.datastore': '', 'label.network.device': '', diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 12d3498350c..c4cf58c86e5 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -475,6 +475,31 @@ function SharedMountPointURL(server, path) { return url; } +function rbdURL(monitor, pool, id, secret) { + var url; + + /* + Replace the + and / symbols by - and _ to have URL-safe base64 going to the API + It's hacky, but otherwise we'll confuse java.net.URI which splits the incoming URI + */ + secret = str.replace("+", "-"); + secret = str.replace("/", "_"); + + if (id != null && secret != null) { + monitor = id + ":" + secret + "@" + monitor; + } + + if(pool.substring(0,1) != "/") + pool = "/" + pool; + + if(monitor.indexOf("://")==-1) + url = "rbd://" + monitor + pool; + else + url = monitor + pool; + + return url; +} + function clvmURL(vgname) { var url; if(vgname.indexOf("://")==-1) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 712c122f68a..59d2d8703d9 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -7494,6 +7494,7 @@ var items = []; items.push({id: "nfs", description: "nfs"}); items.push({id: "SharedMountPoint", description: "SharedMountPoint"}); + items.push({id: "rbd", description: "RBD"}); args.response.success({data: items}); } else if(selectedClusterObj.hypervisortype == "XenServer") { @@ -7677,6 +7678,27 @@ $form.find('.form-item[rel=vCenterDataCenter]').hide(); $form.find('.form-item[rel=vCenterDataStore]').hide(); } + else if(protocol == "rbd") { + $form.find('.form-item[rel=rbdmonitor]').css('display', 'inline-block'); + $form.find('.form-item[rel=rbdmonitor]').find(".name").find("label").text("RADOS Monitor:"); + + $form.find('.form-item[rel=rbdpool]').css('display', 'inline-block'); + $form.find('.form-item[rel=rbdpool]').find(".name").find("label").text("RADOS Pool:"); + + $form.find('.form-item[rel=rbdid]').css('display', 'inline-block'); + $form.find('.form-item[rel=rbdid]').find(".name").find("label").text("RADOS User:"); + + $form.find('.form-item[rel=rbdsecret]').css('display', 'inline-block'); + $form.find('.form-item[rel=rbdsecret]').find(".name").find("label").text("RADOS Secret:"); + + $form.find('.form-item[rel=server]').hide(); + $form.find('.form-item[rel=iqn]').hide(); + $form.find('.form-item[rel=lun]').hide(); + $form.find('.form-item[rel=volumegroup]').hide(); + $form.find('.form-item[rel=path]').hide(); + $form.find('.form-item[rel=vCenterDataCenter]').hide(); + $form.find('.form-item[rel=vCenterDataStore]').hide(); + } else { //$dialogAddPool.find("#add_pool_server_container").show(); $form.find('.form-item[rel=server]').css('display', 'inline-block'); @@ -7744,6 +7766,28 @@ validation: { required: true }, isHidden: true }, + + // RBD + rbdmonitor: { + label: 'label.rbd.monitor', + validation: { required: true }, + isHidden: true + }, + rbdpool: { + label: 'label.rbd.pool', + validation: { required: true }, + isHidden: true + }, + rbdid: { + label: 'label.rbd.id', + validation: { required: false }, + isHidden: true + }, + rbdsecret: { + label: 'label.rbd.secret', + validation: { required: false }, + isHidden: true + }, //always appear (begin) storageTags: { @@ -7803,6 +7847,14 @@ vg = "/" + vg; url = clvmURL(vg); } + else if (args.data.protocol == "rbd") { + var rbdmonitor = args.data.rbdmonitor; + var rbdpool = args.data.rbdpool; + var rbdid = args.data.rbdid; + var rbdsecret = args.data.rbdsecret; + + url = rbdURL(rbdmonitor, rbdpool, rbdid, rbdsecret); + } else if (args.data.protocol == "vmfs") { //var path = trim($thisDialog.find("#add_pool_vmfs_dc").val()); var path = args.data.vCenterDataCenter;