use of com.cloud.hypervisor.vmware.util.VmwareContext in project cloudstack by apache.
the class VmwareStorageProcessor method cleanUpDatastore.
/**
* 1) Possibly expand the datastore.
* 2) Possibly consolidate all relevant VMDK files into one VMDK file.
* 3) Possibly move the VMDK file to the root folder (may already be there).
* 4) If the VMDK file wasn't already in the root folder, then delete the folder the VMDK file was in.
* 5) Possibly rename the VMDK file (this will lead to there being a delta file with the new name and the
* original file with the original name).
*
* Note: If the underlying VMDK file was for a root disk, the 'vmdk' parameter's value might look, for example,
* like "i-2-32-VM/ROOT-32.vmdk".
*
* Note: If the underlying VMDK file was for a data disk, the 'vmdk' parameter's value might look, for example,
* like "-iqn.2010-01.com.solidfire:4nhe.data-32.79-0.vmdk".
*
* Returns the (potentially new) name of the VMDK file.
*/
private String cleanUpDatastore(Command cmd, HostDatastoreSystemMO hostDatastoreSystem, DatastoreMO dsMo, Map<String, String> details) throws Exception {
s_logger.debug(String.format("Executing clean up in DataStore: [%s].", dsMo.getName()));
boolean expandDatastore = Boolean.parseBoolean(details.get(DiskTO.EXPAND_DATASTORE));
// reserve value in use is set to the minimum for the cloned volume), but that's fine.
if (expandDatastore) {
expandDatastore(hostDatastoreSystem, dsMo);
}
String vmdk = details.get(DiskTO.VMDK);
String fullVmdkPath = new DatastoreFile(dsMo.getName(), vmdk).getPath();
VmwareContext context = hostService.getServiceContext(null);
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null);
DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter());
String vmName = getVmName(vmdk);
// If vmName is not null, then move all VMDK files out of this folder to the root folder and then delete the folder named vmName.
if (vmName != null) {
String workerVmName = hostService.getWorkerName(context, cmd, 0, dsMo);
VirtualMachineMO vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVmName, null);
if (vmMo == null) {
throw new Exception("Unable to create a worker VM for volume creation");
}
vmMo.attachDisk(new String[] { fullVmdkPath }, dsMo.getMor());
List<String> backingFiles = new ArrayList<>(1);
List<VirtualDisk> virtualDisks = vmMo.getVirtualDisks();
VirtualDisk virtualDisk = virtualDisks.get(0);
VirtualDeviceBackingInfo virtualDeviceBackingInfo = virtualDisk.getBacking();
while (virtualDeviceBackingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo backingInfo = (VirtualDiskFlatVer2BackingInfo) virtualDeviceBackingInfo;
backingFiles.add(backingInfo.getFileName());
virtualDeviceBackingInfo = backingInfo.getParent();
}
vmMo.detachAllDisksAndDestroy();
VmwareStorageLayoutHelper.moveVolumeToRootFolder(dcMo, backingFiles);
vmdk = new DatastoreFile(vmdk).getFileName();
// Delete the folder the VMDK file was in.
DatastoreFile folderToDelete = new DatastoreFile(dsMo.getName(), vmName);
dsMo.deleteFolder(folderToDelete.getPath(), dcMo.getMor());
}
return vmdk;
}
use of com.cloud.hypervisor.vmware.util.VmwareContext in project cloudstack by apache.
the class VmwareStorageProcessor method copyVolumeFromPrimaryToSecondary.
@Override
public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) {
VolumeObjectTO srcVolume = (VolumeObjectTO) cmd.getSrcTO();
VolumeObjectTO destVolume = (VolumeObjectTO) cmd.getDestTO();
String vmName = srcVolume.getVmName();
VmwareContext context = hostService.getServiceContext(cmd);
try {
DataStoreTO primaryStorage = srcVolume.getDataStore();
NfsTO destStore = (NfsTO) destVolume.getDataStore();
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
Pair<String, String> result;
result = copyVolumeToSecStorage(hostService, hyperHost, cmd, vmName, primaryStorage.getUuid(), srcVolume.getPath(), destVolume.getPath(), destStore.getUrl(), hostService.getWorkerName(context, cmd, 0, null));
VolumeObjectTO newVolume = new VolumeObjectTO();
newVolume.setPath(result.first() + File.separator + result.second());
return new CopyCmdAnswer(newVolume);
} catch (Throwable e) {
return new CopyCmdAnswer(hostService.createLogMessageException(e, cmd));
}
}
use of com.cloud.hypervisor.vmware.util.VmwareContext in project cloudstack by apache.
the class HypervisorHostHelper method readOVF.
public static List<Pair<String, Boolean>> readOVF(VmwareHypervisorHost host, String ovfFilePath, DatastoreMO dsMo) throws Exception {
List<Pair<String, Boolean>> ovfVolumeInfos = new ArrayList<Pair<String, Boolean>>();
List<String> files = new ArrayList<String>();
ManagedObjectReference morRp = host.getHyperHostOwnerResourcePool();
assert (morRp != null);
ManagedObjectReference morHost = host.getMor();
String importEntityName = UUID.randomUUID().toString();
OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams();
importSpecParams.setHostSystem(morHost);
importSpecParams.setLocale("US");
importSpecParams.setEntityName(importEntityName);
importSpecParams.setDeploymentOption("");
String ovfDescriptor = removeOVFNetwork(HttpNfcLeaseMO.readOvfContent(ovfFilePath));
VmwareContext context = host.getContext();
OvfCreateImportSpecResult ovfImportResult = context.getService().createImportSpec(context.getServiceContent().getOvfManager(), ovfDescriptor, morRp, dsMo.getMor(), importSpecParams);
if (ovfImportResult == null) {
String msg = "createImportSpec() failed. ovfFilePath: " + ovfFilePath;
s_logger.error(msg);
throw new Exception(msg);
}
if (!ovfImportResult.getError().isEmpty()) {
for (LocalizedMethodFault fault : ovfImportResult.getError()) {
s_logger.error("createImportSpec error: " + fault.getLocalizedMessage());
}
throw new CloudException("Failed to create an import spec from " + ovfFilePath + ". Check log for details.");
}
if (!ovfImportResult.getWarning().isEmpty()) {
for (LocalizedMethodFault fault : ovfImportResult.getError()) {
s_logger.warn("createImportSpec warning: " + fault.getLocalizedMessage());
}
}
VirtualMachineImportSpec importSpec = (VirtualMachineImportSpec) ovfImportResult.getImportSpec();
if (importSpec == null) {
String msg = "createImportSpec() failed to create import specification for OVF template at " + ovfFilePath;
s_logger.error(msg);
throw new Exception(msg);
}
File ovfFile = new File(ovfFilePath);
for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) {
String absFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath();
files.add(absFile);
}
int osDiskSeqNumber = 0;
VirtualMachineConfigSpec config = importSpec.getConfigSpec();
String paramVal = getOVFParamValue(config);
if (paramVal != null && !paramVal.isEmpty()) {
try {
osDiskSeqNumber = getOsDiskFromOvfConf(config, paramVal);
} catch (Exception e) {
osDiskSeqNumber = 0;
}
}
int diskCount = 0;
int deviceCount = 0;
List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange();
for (VirtualDeviceConfigSpec deviceSpec : deviceConfigList) {
Boolean osDisk = false;
VirtualDevice device = deviceSpec.getDevice();
if (device instanceof VirtualDisk) {
if ((osDiskSeqNumber == 0 && diskCount == 0) || osDiskSeqNumber == deviceCount) {
osDisk = true;
}
Pair<String, Boolean> ovfVolumeInfo = new Pair<String, Boolean>(files.get(diskCount), osDisk);
ovfVolumeInfos.add(ovfVolumeInfo);
diskCount++;
}
deviceCount++;
}
return ovfVolumeInfos;
}
use of com.cloud.hypervisor.vmware.util.VmwareContext in project cloudstack by apache.
the class HypervisorHostHelper method prepareNetwork.
/**
* Prepares network (for non-standard virtual switch) for the VM NIC based on the parameters.
* Can create a new portgroup or update an existing.
* @return Pair of network's ManagedObjectReference and name
* @throws Exception
*/
public static Pair<ManagedObjectReference, String> prepareNetwork(String physicalNetwork, String namePrefix, HostMO hostMo, String vlanId, String secondaryvlanId, Integer networkRateMbps, Integer networkRateMulticastMbps, long timeOutMs, VirtualSwitchType vSwitchType, int numPorts, String gateway, boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType, Map<String, String> vsmCredentials, Map<NetworkOffering.Detail, String> details) throws Exception {
ManagedObjectReference morNetwork = null;
VmwareContext context = hostMo.getContext();
ManagedObjectReference dcMor = hostMo.getHyperHostDatacenter();
DatacenterMO dataCenterMo = new DatacenterMO(context, dcMor);
DistributedVirtualSwitchMO dvSwitchMo = null;
ManagedObjectReference morEthernetPortProfile = null;
String ethPortProfileName = null;
ManagedObjectReference morDvSwitch = null;
String dvSwitchName = null;
boolean bWaitPortGroupReady = false;
boolean createGCTag = false;
String vcApiVersion;
String minVcApiVersionSupportingAutoExpand;
boolean autoExpandSupported;
String networkName;
Integer vid = null;
// secondary pvlan id
Integer spvlanid = null;
/**
* This is the list of BroadcastDomainTypes we can actually
* prepare networks for in this function.
*/
BroadcastDomainType[] supportedBroadcastTypes = new BroadcastDomainType[] { BroadcastDomainType.Lswitch, BroadcastDomainType.LinkLocal, BroadcastDomainType.Native, BroadcastDomainType.Pvlan, BroadcastDomainType.Storage, BroadcastDomainType.UnDecided, BroadcastDomainType.Vlan };
if (!Arrays.asList(supportedBroadcastTypes).contains(broadcastDomainType)) {
throw new InvalidParameterException("BroadcastDomainType " + broadcastDomainType + " it not supported on a VMWare hypervisor at this time.");
}
if (broadcastDomainType == BroadcastDomainType.Lswitch) {
if (vSwitchType == VirtualSwitchType.NexusDistributedVirtualSwitch) {
throw new InvalidParameterException("Nexus Distributed Virtualswitch is not supported with BroadcastDomainType " + broadcastDomainType);
}
/**
* Nicira NVP requires all vms to be connected to a single port-group.
* A unique vlan needs to be set per port. This vlan is specific to
* this implementation and has no reference to other vlans in CS
*/
// FIXME Should be set via a configuration item in CS
networkName = "br-int";
// No doubt about this, depending on vid=null to avoid lots of code below
vid = null;
} else {
if (vlanId != null) {
vlanId = vlanId.replace("vlan://", "");
}
networkName = composeCloudNetworkName(namePrefix, vlanId, secondaryvlanId, networkRateMbps, physicalNetwork);
if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) && !StringUtils.containsAny(vlanId, ",-")) {
createGCTag = true;
vid = Integer.parseInt(vlanId);
}
if (vlanId != null && StringUtils.containsAny(vlanId, ",-")) {
createGCTag = true;
}
if (secondaryvlanId != null) {
spvlanid = Integer.parseInt(secondaryvlanId);
}
}
if (vSwitchType == VirtualSwitchType.VMwareDistributedVirtualSwitch) {
vcApiVersion = getVcenterApiVersion(context);
minVcApiVersionSupportingAutoExpand = "5.0";
autoExpandSupported = isFeatureSupportedInVcenterApiVersion(vcApiVersion, minVcApiVersionSupportingAutoExpand);
dvSwitchName = physicalNetwork;
// and switch types.
if (dvSwitchName == null) {
s_logger.warn("Detected null dvSwitch. Defaulting to dvSwitch0");
dvSwitchName = "dvSwitch0";
}
morDvSwitch = dataCenterMo.getDvSwitchMor(dvSwitchName);
if (morDvSwitch == null) {
String msg = "Unable to find distributed vSwitch " + dvSwitchName;
s_logger.error(msg);
throw new Exception(msg);
}
dvSwitchMo = new DistributedVirtualSwitchMO(context, morDvSwitch);
String dvSwitchVersion = dvSwitchMo.getDVSProductVersion(morDvSwitch);
s_logger.debug(String.format("Found distributed vSwitch: %s with product version: %s", dvSwitchName, dvSwitchVersion));
if (broadcastDomainType == BroadcastDomainType.Lswitch) {
if (!dataCenterMo.hasDvPortGroup(networkName)) {
throw new InvalidParameterException("NVP integration port-group " + networkName + " does not exist on the DVS " + dvSwitchName);
}
bWaitPortGroupReady = false;
} else {
boolean dvSwitchSupportNewPolicies = (isFeatureSupportedInVcenterApiVersion(vcApiVersion, MINIMUM_VCENTER_API_VERSION_WITH_DVS_NEW_POLICIES_SUPPORT) && isVersionEqualOrHigher(dvSwitchVersion, MINIMUM_DVS_VERSION_WITH_NEW_POLICIES_SUPPORT));
DVSTrafficShapingPolicy shapingPolicy = getDVSShapingPolicy(networkRateMbps);
DVSSecurityPolicy secPolicy = createDVSSecurityPolicy(details);
DVSMacManagementPolicy macManagementPolicy = createDVSMacManagementPolicy(details);
// First, if both vlan id and pvlan id are provided, we need to
// reconfigure the DVSwitch to have a tuple <vlan id, pvlan id> of
// type isolated.
String pvlanType = MapUtils.isNotEmpty(details) ? details.get(NetworkOffering.Detail.pvlanType) : null;
if (vid != null && spvlanid != null) {
setupPVlanPair(dvSwitchMo, morDvSwitch, vid, spvlanid, pvlanType);
}
VMwareDVSPortgroupPolicy portGroupPolicy = null;
// Next, create the port group. For this, we need to create a VLAN spec.
createPortGroup(physicalNetwork, networkName, vlanId, vid, spvlanid, dataCenterMo, shapingPolicy, secPolicy, macManagementPolicy, portGroupPolicy, dvSwitchMo, numPorts, autoExpandSupported, dvSwitchSupportNewPolicies);
bWaitPortGroupReady = true;
}
} else if (vSwitchType == VirtualSwitchType.NexusDistributedVirtualSwitch) {
ethPortProfileName = physicalNetwork;
// and switch types.
if (ethPortProfileName == null) {
s_logger.warn("Detected null ethrenet port profile. Defaulting to epp0.");
ethPortProfileName = "epp0";
}
morEthernetPortProfile = dataCenterMo.getDvPortGroupMor(ethPortProfileName);
if (morEthernetPortProfile == null) {
String msg = "Unable to find Ethernet port profile " + ethPortProfileName;
s_logger.error(msg);
throw new Exception(msg);
} else {
s_logger.info("Found Ethernet port profile " + ethPortProfileName);
}
long averageBandwidth = 0L;
if (networkRateMbps != null && networkRateMbps.intValue() > 0) {
averageBandwidth = networkRateMbps.intValue() * 1024L * 1024L;
}
// We chose 50% higher allocation than average bandwidth.
// TODO(sateesh): Optionally let user specify the peak coefficient
long peakBandwidth = (long) (averageBandwidth * 1.5);
// TODO(sateesh): Optionally let user specify the burst coefficient
long burstSize = 5 * averageBandwidth / 8;
if (vsmCredentials != null) {
s_logger.info("Stocking credentials of Nexus VSM");
context.registerStockObject("vsmcredentials", vsmCredentials);
}
if (!dataCenterMo.hasDvPortGroup(networkName)) {
s_logger.info("Port profile " + networkName + " not found.");
createPortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize, gateway, configureVServiceInNexus);
bWaitPortGroupReady = true;
} else {
s_logger.info("Port profile " + networkName + " found.");
updatePortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize);
}
}
// Wait for dvPortGroup on vCenter
if (bWaitPortGroupReady)
morNetwork = waitForDvPortGroupReady(dataCenterMo, networkName, timeOutMs);
else
morNetwork = dataCenterMo.getDvPortGroupMor(networkName);
if (morNetwork == null) {
String msg = "Failed to create guest network " + networkName;
s_logger.error(msg);
throw new Exception(msg);
}
if (createGCTag) {
NetworkMO networkMo = new NetworkMO(hostMo.getContext(), morNetwork);
networkMo.setCustomFieldValue(CustomFieldConstants.CLOUD_GC_DVP, "true");
s_logger.debug("Added custom field : " + CustomFieldConstants.CLOUD_GC_DVP);
}
return new Pair<ManagedObjectReference, String>(morNetwork, networkName);
}
use of com.cloud.hypervisor.vmware.util.VmwareContext in project cloudstack by apache.
the class HypervisorHostHelper method importVmFromOVF.
/**
* deploys a new VM from a ovf spec. It ignores network, defaults locale to 'US'
* @throws Exception shoud be a VmwareResourceException
*/
public static void importVmFromOVF(VmwareHypervisorHost host, String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, ManagedObjectReference morRp, ManagedObjectReference morHost, String configurationId) throws CloudRuntimeException, IOException {
assert (morRp != null);
OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams();
importSpecParams.setHostSystem(morHost);
importSpecParams.setLocale("US");
importSpecParams.setEntityName(vmName);
String deploymentOption = StringUtils.isNotBlank(configurationId) ? configurationId : "";
importSpecParams.setDeploymentOption(deploymentOption);
// diskOption: thin, thick, etc
importSpecParams.setDiskProvisioning(diskOption);
String ovfDescriptor = removeOVFNetwork(HttpNfcLeaseMO.readOvfContent(ovfFilePath));
VmwareContext context = host.getContext();
OvfCreateImportSpecResult ovfImportResult = null;
try {
ovfImportResult = context.getService().createImportSpec(context.getServiceContent().getOvfManager(), ovfDescriptor, morRp, dsMo.getMor(), importSpecParams);
} catch (ConcurrentAccessFaultMsg | FileFaultFaultMsg | InvalidDatastoreFaultMsg | InvalidStateFaultMsg | RuntimeFaultFaultMsg | TaskInProgressFaultMsg | VmConfigFaultFaultMsg error) {
throw new CloudRuntimeException("ImportSpec creation failed", error);
}
if (ovfImportResult == null) {
String msg = "createImportSpec() failed. ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + ", diskOption: " + diskOption;
s_logger.error(msg);
throw new CloudRuntimeException(msg);
}
if (!ovfImportResult.getError().isEmpty()) {
for (LocalizedMethodFault fault : ovfImportResult.getError()) {
s_logger.error("createImportSpec error: " + fault.getLocalizedMessage());
}
throw new CloudRuntimeException("Failed to create an import spec from " + ovfFilePath + ". Check log for details.");
}
if (!ovfImportResult.getWarning().isEmpty()) {
for (LocalizedMethodFault fault : ovfImportResult.getError()) {
s_logger.warn("createImportSpec warning: " + fault.getLocalizedMessage());
}
}
DatacenterMO dcMo = null;
try {
dcMo = new DatacenterMO(context, host.getHyperHostDatacenter());
} catch (Exception e) {
throw new CloudRuntimeException(String.format("no datacenter for host '%s' available in context", context.getServerAddress()), e);
}
ManagedObjectReference folderMO = null;
try {
folderMO = dcMo.getVmFolder();
} catch (Exception e) {
throw new CloudRuntimeException("no management handle for VmFolder", e);
}
ManagedObjectReference morLease = null;
try {
morLease = context.getService().importVApp(morRp, ovfImportResult.getImportSpec(), folderMO, morHost);
} catch (DuplicateNameFaultMsg | FileFaultFaultMsg | InsufficientResourcesFaultFaultMsg | InvalidDatastoreFaultMsg | InvalidNameFaultMsg | OutOfBoundsFaultMsg | RuntimeFaultFaultMsg | VmConfigFaultFaultMsg fault) {
throw new CloudRuntimeException("import vApp failed", fault);
}
if (morLease == null) {
String msg = "importVApp() failed. ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + ", diskOption: " + diskOption;
s_logger.error(msg);
throw new CloudRuntimeException(msg);
}
boolean importSuccess = true;
final HttpNfcLeaseMO leaseMo = new HttpNfcLeaseMO(context, morLease);
HttpNfcLeaseState state = null;
try {
state = leaseMo.waitState(new HttpNfcLeaseState[] { HttpNfcLeaseState.READY, HttpNfcLeaseState.ERROR });
} catch (Exception e) {
throw new CloudRuntimeException("exception while waiting for leaseMO", e);
}
try {
if (state == HttpNfcLeaseState.READY) {
final long totalBytes = HttpNfcLeaseMO.calcTotalBytes(ovfImportResult);
File ovfFile = new File(ovfFilePath);
HttpNfcLeaseInfo httpNfcLeaseInfo = null;
try {
httpNfcLeaseInfo = leaseMo.getLeaseInfo();
} catch (Exception e) {
throw new CloudRuntimeException("error waiting for lease info", e);
}
List<HttpNfcLeaseDeviceUrl> deviceUrls = httpNfcLeaseInfo.getDeviceUrl();
long bytesAlreadyWritten = 0;
final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter();
try {
for (HttpNfcLeaseDeviceUrl deviceUrl : deviceUrls) {
String deviceKey = deviceUrl.getImportKey();
for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) {
if (deviceKey.equals(ovfFileItem.getDeviceId())) {
String absoluteFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath();
s_logger.info("Uploading file: " + absoluteFile);
File f = new File(absoluteFile);
if (f.exists()) {
String urlToPost = deviceUrl.getUrl();
urlToPost = resolveHostNameInUrl(dcMo, urlToPost);
context.uploadVmdkFile(ovfFileItem.isCreate() ? "PUT" : "POST", urlToPost, absoluteFile, bytesAlreadyWritten, new ActionDelegate<Long>() {
@Override
public void action(Long param) {
progressReporter.reportProgress((int) (param * 100 / totalBytes));
}
});
bytesAlreadyWritten += ovfFileItem.getSize();
}
}
}
}
} catch (Exception e) {
String erroMsg = "File upload task failed to complete due to: " + e.getMessage();
s_logger.error(erroMsg);
// Set flag to cleanup the stale template left due to failed import operation, if any
importSuccess = false;
throw new CloudRuntimeException(erroMsg, e);
} catch (Throwable th) {
String errorMsg = "throwable caught during file upload task: " + th.getMessage();
s_logger.error(errorMsg);
// Set flag to cleanup the stale template left due to failed import operation, if any
importSuccess = false;
throw new CloudRuntimeException(errorMsg, th);
} finally {
progressReporter.close();
}
if (bytesAlreadyWritten == totalBytes) {
try {
leaseMo.updateLeaseProgress(100);
} catch (Exception e) {
throw new CloudRuntimeException("error while waiting for lease update", e);
}
}
} else if (state == HttpNfcLeaseState.ERROR) {
LocalizedMethodFault error = null;
try {
error = leaseMo.getLeaseError();
} catch (Exception e) {
throw new CloudRuntimeException("error getting lease error", e);
}
MethodFault fault = error.getFault();
String erroMsg = "Object creation on vCenter failed due to: Exception: " + fault.getClass().getName() + ", message: " + error.getLocalizedMessage();
s_logger.error(erroMsg);
throw new CloudRuntimeException(erroMsg);
}
} finally {
try {
if (!importSuccess) {
s_logger.error("Aborting the lease on " + vmName + " after import operation failed.");
leaseMo.abortLease();
} else {
leaseMo.completeLease();
}
} catch (Exception e) {
throw new CloudRuntimeException("error completing lease", e);
}
}
}
Aggregations