/**
* Copyright (C) 2008 - Abiquo Holdings S.L. All rights reserved.
*
* Please see /opt/abiquo/tomcat/webapps/legal/ on Abiquo server
* or contact contact@abiquo.com for licensing information.
*/
package com.abiquo.hypervisor.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.abiquo.hypervisor.model.DHCPRule;
import com.abiquo.hypervisor.model.DiskDescription;
import com.abiquo.hypervisor.model.DiskStandard;
import com.abiquo.hypervisor.model.DiskStateful;
import com.abiquo.hypervisor.model.SecondaryDiskStandard;
import com.abiquo.hypervisor.model.SecondaryDiskStateful;
import com.abiquo.hypervisor.model.VirtualMachineDefinition;
import com.abiquo.hypervisor.model.VirtualMachineDefinition.AntiaffinityRule;
import com.abiquo.hypervisor.model.VirtualMachineDefinition.NetworkConfiguration;
import com.abiquo.hypervisor.model.VirtualMachineDefinition.PrimaryDisk;
import com.abiquo.hypervisor.model.VirtualNIC;
import com.abiquo.hypervisor.util.ReconfigureUtils.ReconfigureNIC.NICOp;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
/**
* Utility functions to detect section changes between {@link VirtualMachineDefinition} instances.
*
* @author eruiz
*/
public class ReconfigureUtils
{
public static boolean isRamChanged(final VirtualMachineDefinition oldDefinition,
final VirtualMachineDefinition newDefinition)
{
int oldRam = oldDefinition.getHardwareConfiguration().getRamInMb();
int newRam = newDefinition.getHardwareConfiguration().getRamInMb();
return oldRam != newRam;
}
public static boolean isNumberOfVirtualCpuChanged(final VirtualMachineDefinition oldDefinition,
final VirtualMachineDefinition newDefinition)
{
int oldNumber = oldDefinition.getHardwareConfiguration().getNumVirtualCpus();
int newNumber = newDefinition.getHardwareConfiguration().getNumVirtualCpus();
Integer oldNumberCps = oldDefinition.getHardwareConfiguration().getCoresPerSocket();
Integer newNumberCps = newDefinition.getHardwareConfiguration().getCoresPerSocket();
return oldNumber != newNumber || !Objects.equal(oldNumberCps, newNumberCps);
}
public static boolean isRemoteDesktopEnabledChanged(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition)
{
boolean oldEnabled = oldDefinition.getNetworkConfiguration().getRdEnabled();
boolean newEnabled = newDefinition.getNetworkConfiguration().getRdEnabled();
return oldEnabled != newEnabled;
}
public static boolean isRemoteDesktopPortChanged(final VirtualMachineDefinition oldDefinition,
final VirtualMachineDefinition newDefinition)
{
int oldPort = oldDefinition.getNetworkConfiguration().getRdPort();
int newPort = newDefinition.getNetworkConfiguration().getRdPort();
return oldPort != newPort;
}
public static boolean isPasswordChanged(final VirtualMachineDefinition oldDefinition,
final VirtualMachineDefinition newDefinition)
{
String oldPw = oldDefinition.getNetworkConfiguration().getRdPassword();
String newPw = newDefinition.getNetworkConfiguration().getRdPassword();
return oldPw != newPw;
}
public static boolean isKeyMapChanged(final VirtualMachineDefinition oldDefinition,
final VirtualMachineDefinition newDefinition)
{
String oldKeyMap = oldDefinition.getNetworkConfiguration().getKeyMap();
String newKeyMap = newDefinition.getNetworkConfiguration().getKeyMap();
return oldKeyMap != newKeyMap;
}
/**
* @return true if the primary {@link DiskStandard} have changed its capacity
*/
public static boolean isPrimaryDiskStandardCapacityChanged(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition)
{
if (oldDefinition.getPrimaryDisk().isStateful()
|| newDefinition.getPrimaryDisk().isStateful())
{
return false;
}
long oldCapacity = oldDefinition.getPrimaryDisk().getDiskStandard().getCapacityInBytes();
long newCapacity = newDefinition.getPrimaryDisk().getDiskStandard().getCapacityInBytes();
return oldCapacity != newCapacity;
}
public static List<ReconfigureStatefulDisk> getReconfigureStatefulDisks(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition)
{
return getReconfigureStatefulDisks(oldDefinition, newDefinition, false);
}
public static List<ReconfigureStatefulDisk> getReconfigureStatefulDisks(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition,
final boolean detectMovesbySequence)
{
List<ReconfigureStatefulDisk> disks = new ArrayList<ReconfigureStatefulDisk>();
List<SecondaryDiskStateful> oldDisks = oldDefinition.getSecondaryDisks().getStatefulDisks();
List<SecondaryDiskStateful> newDisks = newDefinition.getSecondaryDisks().getStatefulDisks();
for (SecondaryDiskStateful newDisk : newDisks)
{
SecondaryDiskStateful oldDisk = isPersistentDiskContained(newDisk, oldDisks);
if (oldDisk == null)
{
disks.add(new ReconfigureStatefulDisk(newDisk, DiskOp.ATTACH));
}
else if (!detectMovesbySequence && isChangedBusOrUnitNumber(oldDisk, newDisk)
|| detectMovesbySequence && oldDisk.getSequence() != newDisk.getSequence())
{
disks.add(new ReconfigureStatefulDisk(newDisk, DiskOp.MOVE_UNIT_NUMBER));
}
}
for (SecondaryDiskStateful oldDisk : oldDisks)
{
SecondaryDiskStateful newDisk = isPersistentDiskContained(oldDisk, newDisks);
if (newDisk == null)
{
disks.add(new ReconfigureStatefulDisk(oldDisk, DiskOp.DETACH));
}
}
// Detach operations must be processed first to avoid collisions.
// Move unit number operations may require a detach, so make them also before the attach
// ones.
Collections.sort(disks, ReconfigureDisk.SORT_BY_OP);
return disks;
}
public static List<ReconfigureStandardDisk> getReconfigureStandardDisks(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition)
{
return getReconfigureStandardDisks(oldDefinition, newDefinition, false);
}
public static List<ReconfigureStandardDisk> getReconfigureStandardDisks(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition,
final boolean detectMovesbySequence)
{
List<ReconfigureStandardDisk> disks = new ArrayList<ReconfigureStandardDisk>();
List<SecondaryDiskStandard> oldDisks = oldDefinition.getSecondaryDisks().getStandardDisks();
List<SecondaryDiskStandard> newDisks = newDefinition.getSecondaryDisks().getStandardDisks();
for (SecondaryDiskStandard newDisk : newDisks)
{
SecondaryDiskStandard oldDisk = isStandardDiskContained(newDisk, oldDisks);
if (oldDisk == null)
{
disks.add(new ReconfigureStandardDisk(newDisk, DiskOp.ATTACH));
}
else if (!detectMovesbySequence && isChangedBusOrUnitNumber(oldDisk, newDisk)
|| detectMovesbySequence && oldDisk.getSequence() != newDisk.getSequence())
{
disks.add(new ReconfigureStandardDisk(newDisk, DiskOp.MOVE_UNIT_NUMBER));
}
else if (oldDisk.getCapacityInBytes() != newDisk.getCapacityInBytes())
{
final long oldCapacity = oldDisk.getCapacityInBytes();
final long newCapacity = newDisk.getCapacityInBytes();
if (oldCapacity < newCapacity)
{
disks.add(new ReconfigureStandardDisk(newDisk, DiskOp.INCREASE_SIZE));
}
else
{
disks.add(new ReconfigureStandardDisk(newDisk, DiskOp.DECREASE_SIZE));
}
}
}
for (SecondaryDiskStandard oldDisk : oldDisks)
{
SecondaryDiskStandard newDisk = isStandardDiskContained(oldDisk, newDisks);
if (newDisk == null)
{
disks.add(new ReconfigureStandardDisk(oldDisk, DiskOp.DETACH));
}
}
// Detach operations must be processed first to avoid collisions.
// Move unit number operations may require a detach, so make them also before the attach
// ones.
Collections.sort(disks, ReconfigureDisk.SORT_BY_OP);
return disks;
}
private static boolean isChangedBusOrUnitNumber(final DiskDescription oldDisk,
final DiskDescription newDisk)
{
return oldDisk.getBusNumber() != null && oldDisk.getBusNumber() != newDisk.getBusNumber() || //
oldDisk.getUnitNumber() != null && oldDisk.getUnitNumber() != newDisk.getUnitNumber();
}
public static List<ReconfigureNIC> getReconfigureNICs(
final VirtualMachineDefinition oldDefinition, final VirtualMachineDefinition newDefinition)
{
List<ReconfigureNIC> nics = new ArrayList<ReconfigureNIC>();
List<VirtualNIC> oldnics = oldDefinition.getNetworkConfiguration().getVirtualNICs();
List<VirtualNIC> newnics = newDefinition.getNetworkConfiguration().getVirtualNICs();
for (VirtualNIC oldnic : oldnics)
{
if (!isVirtualNICContained(oldnic, newnics))
{
nics.add(new ReconfigureNIC(oldnic, NICOp.REMOVE));
}
}
for (VirtualNIC newnic : newnics)
{
if (!isVirtualNICContained(newnic, oldnics))
{
nics.add(new ReconfigureNIC(newnic, NICOp.ADD));
}
}
return nics;
}
public static boolean refersToSameVirtualMachine(final VirtualMachineDefinition one,
final VirtualMachineDefinition other)
{
return one.getName().equals(other.getName()) && haveSamePrimaryDisk(one, other);
}
private static boolean haveSamePrimaryDisk(final VirtualMachineDefinition one,
final VirtualMachineDefinition other)
{
PrimaryDisk oldPrimary = one.getPrimaryDisk();
PrimaryDisk newPrimary = other.getPrimaryDisk();
boolean sameDisk = false;
if (oldPrimary.isStandard() && newPrimary.isStandard())
{
sameDisk = sameDiskStandard(oldPrimary.getDiskStandard(), newPrimary.getDiskStandard());
}
else if (oldPrimary.isStateful() && newPrimary.isStateful())
{
sameDisk = sameDiskStateful(oldPrimary.getDiskStateful(), newPrimary.getDiskStateful());
}
return sameDisk;
}
private static SecondaryDiskStateful isPersistentDiskContained(
final SecondaryDiskStateful disk, final List<SecondaryDiskStateful> disks)
{
for (SecondaryDiskStateful i : disks)
{
if (sameAuxiliaryDisk(disk, i))
{
return i;
}
}
return null;
}
/**
* Returns the old representation of the provided disk.
*
* @return null if not contained (shoudl deatach the disk)
*/
private static SecondaryDiskStandard isStandardDiskContained(final SecondaryDiskStandard disk,
final List<SecondaryDiskStandard> disks)
{
for (SecondaryDiskStandard i : disks)
{
if (sameAuxiliaryDisk(disk, i))
{
return i;
}
}
return null;
}
private static boolean isVirtualNICContained(final VirtualNIC vnic, final List<VirtualNIC> vnics)
{
for (VirtualNIC i : vnics)
{
if (sameVirtualNIC(vnic, i))
{
return true;
}
}
return false;
}
private static boolean sameDiskDescription(final DiskDescription one,
final DiskDescription other)
{
boolean equals = true;
equals &= one.getFormat() == other.getFormat();
// Destination datastore in auxiliary disks is only used in ESX
if (one.getDestinationDatastore() != null)
{
equals &= one.getDestinationDatastore().equals(other.getDestinationDatastore());
}
return equals;
}
private static boolean sameDiskStandard(final DiskStandard one, final DiskStandard other)
{
boolean equals = sameDiskDescription(one, other);
// Since resize hd is allowed, we have to check if new size is great or equal than old
// value.
equals &= one.getCapacityInBytes() <= other.getCapacityInBytes();
equals &= StringUtils.equals(one.getPath(), other.getPath());
equals &= StringUtils.equals(one.getRepository(), other.getRepository());
return equals;
}
private static boolean sameDiskStateful(final DiskStateful one, final DiskStateful other)
{
boolean equals = sameDiskDescription(one, other);
// We have to check if new size is equal than old value.
equals &= one.getCapacityInBytes() == other.getCapacityInBytes();
equals &= StringUtils.equals(one.getLocation(), other.getLocation());
return equals;
}
public static boolean sameAuxiliaryDisk(final SecondaryDiskStateful one,
final SecondaryDiskStateful other)
{
return sameDiskStateful(one, other);
}
public static boolean sameAuxiliaryDisk(final SecondaryDiskStandard one,
final SecondaryDiskStandard other)
{
return one.getDiskManagementId() == other.getDiskManagementId();
}
public static boolean sameAttachmentSlot(final DiskDescription one, final DiskDescription other)
{
return one.getDiskControllerType().equals(other.getDiskControllerType())
&& one.getBusNumber().equals(other.getBusNumber())
&& one.getUnitNumber().equals(other.getUnitNumber());
}
private static boolean sameVirtualNIC(final VirtualNIC one, final VirtualNIC other)
{
boolean equals = sameDhcpRule(one, other);
equals &= StringUtils.equals(one.getVSwitchName(), other.getVSwitchName());
equals &= StringUtils.equals(one.getNetworkName(), other.getNetworkName());
equals &= StringUtils.equals(one.getForwardMode(), other.getForwardMode());
equals &= one.getVlanTag() == other.getVlanTag();
equals &= one.getSequence() == other.getSequence();
return equals;
}
public static boolean isDhcpServerChanged(final NetworkConfiguration current,
final NetworkConfiguration newDef)
{
if (current == null && newDef == null)
{
return false;
}
if (current == null ^ newDef == null)
{
return true;
}
boolean sameAddress = current.getDhcpAddress().equals(newDef.getDhcpAddress());
boolean samePort = current.getDhcpPort() == newDef.getDhcpPort();
return !(sameAddress && samePort);
}
public static boolean isDhcpv6ServerChanged(final NetworkConfiguration current,
final NetworkConfiguration newDef)
{
if (current == null && newDef == null)
{
return false;
}
if (current == null ^ newDef == null)
{
return true;
}
boolean sameAddress = current.getDhcpv6Address().equals(newDef.getDhcpv6Address());
boolean samePort = current.getDhcpv6Port() == newDef.getDhcpv6Port();
return !(sameAddress && samePort);
}
public static List<DHCPRule> getRemovedDhcpRules(final NetworkConfiguration oldDefinition,
final NetworkConfiguration newDefinition)
{
List<DHCPRule> removed = new ArrayList<DHCPRule>();
List<DHCPRule> oldRules = new LinkedList<DHCPRule>();
List<DHCPRule> newRules = new LinkedList<DHCPRule>();
if (oldDefinition != null)
{
oldRules.addAll(oldDefinition.getVirtualNICs());
}
if (newDefinition != null)
{
newRules.addAll(newDefinition.getVirtualNICs());
}
for (DHCPRule oldDisk : oldRules)
{
if (!isDhcpRuleContained(oldDisk, newRules))
{
removed.add(oldDisk);
}
}
return removed;
}
public static List<DHCPRule> getAddedDhcpRules(final NetworkConfiguration oldDefinition,
final NetworkConfiguration newDefinition)
{
List<DHCPRule> added = new ArrayList<DHCPRule>();
List<DHCPRule> oldRules = new LinkedList<DHCPRule>();
List<DHCPRule> newRules = new LinkedList<DHCPRule>();
if (oldDefinition != null)
{
oldRules.addAll(oldDefinition.getVirtualNICs());
}
if (newDefinition != null)
{
newRules.addAll(newDefinition.getVirtualNICs());
}
for (DHCPRule newRule : newRules)
{
if (!isDhcpRuleContained(newRule, oldRules))
{
added.add(newRule);
}
}
return added;
}
private static boolean isDhcpRuleContained(final DHCPRule rule, final List<DHCPRule> otherrules)
{
for (DHCPRule i : otherrules)
{
if (sameDhcpRule(rule, i))
{
return true;
}
}
return false;
}
private static boolean sameDhcpRule(final DHCPRule one, final DHCPRule other)
{
boolean equals = true;
equals &= StringUtils.equals(one.getIp(), other.getIp());
equals &= StringUtils.equals(one.getMacAddress(), other.getMacAddress());
equals &= StringUtils.equals(one.getLeaseName(), other.getLeaseName());
equals &= StringUtils.equals(one.getGateway(), other.getGateway());
equals &= StringUtils.equals(one.getMask(), other.getMask());
equals &= StringUtils.equals(one.getPrimaryDNS(), other.getPrimaryDNS());
equals &= StringUtils.equals(one.getSecondaryDNS(), other.getSecondaryDNS());
equals &= StringUtils.equals(one.getSufixDNS(), other.getSufixDNS());
equals &= one.isConfigureGateway() == other.isConfigureGateway();
equals &= one.isIpv6() == other.isIpv6();
return equals;
}
public static boolean anitaffinityRuleChanged(final VirtualMachineDefinition vm,
final VirtualMachineDefinition vm2)
{
return Objects.equal(vm.getAntiaffinityRule(), vm2.getAntiaffinityRule());
}
public static Optional<AntiaffinityRule> antiaffinityRuleToAdd(
final VirtualMachineDefinition oldvm, final VirtualMachineDefinition newvm)
{
if (newvm.getAntiaffinityRule() != null
&& (oldvm.getAntiaffinityRule() == null || !Objects.equal(oldvm.getAntiaffinityRule()
.getName(), newvm.getAntiaffinityRule().getName())))
{
return Optional.fromNullable(newvm.getAntiaffinityRule());
}
else
{
return Optional.absent();
}
}
public static Optional<AntiaffinityRule> antiaffinityRuleToDelete(
final VirtualMachineDefinition oldvm, final VirtualMachineDefinition newvm)
{
if (oldvm.getAntiaffinityRule() != null
&& (newvm.getAntiaffinityRule() == null || !Objects.equal(oldvm.getAntiaffinityRule()
.getName(), newvm.getAntiaffinityRule().getName())))
{
return Optional.fromNullable(oldvm.getAntiaffinityRule());
}
else
{
return Optional.absent();
}
}
public static enum DiskOp
{
DETACH, MOVE_UNIT_NUMBER, ATTACH, INCREASE_SIZE, DECREASE_SIZE;
};
public static class ReconfigureDisk<T extends DiskDescription>
{
public T disk;
public DiskOp op;
public ReconfigureDisk(final T disk, final DiskOp op)
{
this.op = op;
this.disk = disk;
}
public static Comparator<ReconfigureDisk< ? >> SORT_BY_OP =
new Comparator<ReconfigureDisk< ? >>()
{
@Override
public int compare(final ReconfigureDisk< ? > op1, final ReconfigureDisk< ? > op2)
{
return op1.op.compareTo(op2.op);
}
};
}
public static class ReconfigureStatefulDisk extends ReconfigureDisk<SecondaryDiskStateful>
{
public ReconfigureStatefulDisk(final SecondaryDiskStateful disk, final DiskOp op)
{
super(disk, op);
}
}
public static class ReconfigureStandardDisk extends ReconfigureDisk<SecondaryDiskStandard>
{
public ReconfigureStandardDisk(final SecondaryDiskStandard disk, final DiskOp op)
{
super(disk, op);
}
}
public static class ReconfigureNIC
{
public static enum NICOp
{
ADD, REMOVE
};
public VirtualNIC nic;
public NICOp op;
public ReconfigureNIC(final VirtualNIC nic, final NICOp op)
{
this.nic = nic;
this.op = op;
}
}
}