package org.ovirt.engine.core.bll;
import java.util.List;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmNetworkInterface;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.errors.VdcBllErrors;
import org.ovirt.engine.core.compat.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.compat.LongCompat;
import org.ovirt.engine.core.compat.NumberStyles;
import org.ovirt.engine.core.compat.RefObject;
import org.ovirt.engine.core.compat.StringHelper;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase;
public class MacPoolManager {
private static final MacPoolManager _instance = new MacPoolManager();
static {
}
public static MacPoolManager getInstance() {
return _instance;
}
private final java.util.ArrayList<String> mAvailableMacs = new java.util.ArrayList<String>();
private final java.util.ArrayList<String> mAllocatedMacs = new java.util.ArrayList<String>();
private final java.util.ArrayList<String> mAllocatedCustomMacs = new java.util.ArrayList<String>();
private final Object mLocObj = new Object();
private boolean mInitialized = false;
public void initialize() {
synchronized (mLocObj) {
List<VM> vms = DbFacade.getInstance().getVmDAO().getAll();
String ranges = Config.<String> GetValue(ConfigValues.MacPoolRanges);
if (!StringHelper.EqOp(ranges, "")) {
try {
initRanges(ranges);
} catch (MacPoolExceededMaxException e) {
log.errorFormat("MAC Pool range exceeded maximum number of mac pool addressed. Please check Mac Pool configuration.");
}
}
for (VM vm : vms) {
List<VmNetworkInterface> interfaces = DbFacade.getInstance()
.getVmNetworkInterfaceDAO().getAllForVm(vm.getvm_guid());
for (VmNetworkInterface iface : interfaces) {
AddMac(iface.getMacAddress());
}
}
mInitialized = true;
}
}
private void initRanges(String ranges) {
String[] rangesArray = ranges.split("[,]", -1);
for (String range : rangesArray) {
String[] startendArray = range.split("[-]", -1);
if (startendArray.length == 2) {
if (!initRange(startendArray[0], startendArray[1])) {
log.errorFormat("Failed to initialize Mac Pool range. Please fix Mac Pool range: {0}", range);
}
} else {
log.errorFormat("Failed to initialize Mac Pool range. Please fix Mac Pool range: {0}", range);
}
}
if (mAvailableMacs.isEmpty()) {
throw new VdcBLLException(VdcBllErrors.MAC_POOL_INITIALIZATION_FAILED);
}
}
private String ParseRangePart(String start) {
StringBuilder builder = new StringBuilder();
for (String part : start.split("[:]", -1)) {
String tempPart = part.trim();
if (tempPart.length() == 1) {
builder.append('0');
} else if (tempPart.length() > 2) {
return null;
}
builder.append(tempPart);
}
return builder.toString();
}
private boolean initRange(String start, String end) {
String parsedRangeStart = ParseRangePart(start);
String parsedRangeEnd = ParseRangePart(end);
if (parsedRangeEnd == null || parsedRangeStart == null) {
return false;
}
long startNum = LongCompat.parseLong(ParseRangePart(start), NumberStyles.HexNumber);
long endNum = LongCompat.parseLong(ParseRangePart(end), NumberStyles.HexNumber);
if (startNum > endNum) {
// throw new
// VdcBLLException(VdcBllErrors.MAC_POOL_INITIALIZATION_FAILED);
return false;
}
for (long i = startNum; i <= endNum; i++) {
String value = String.format("%x", i);
if (value.length() > 12) {
// throw new
// VdcBLLException(VdcBllErrors.MAC_POOL_INITIALIZATION_FAILED);
return false;
} else if (value.length() < 12) {
value = StringHelper.padLeft(value, 12, '0');
}
StringBuilder builder = new StringBuilder();
for (int j = 0; j < value.length(); j += 2) {
builder.append(value.substring(j, j + 2));
builder.append(":");
}
value = builder.toString();
value = value.substring(0, value.length() - 1);
if (!mAvailableMacs.contains(value)) {
mAvailableMacs.add(value);
}
if (mAvailableMacs.size() > Config.<Integer> GetValue(ConfigValues.MaxMacsCountInPool)) {
throw new MacPoolExceededMaxException();
}
}
return true;
}
public void allocateNewMac(RefObject<String> mac) {
log.info("MacPoolManager::allocateNewMac entered");
synchronized (mLocObj) {
if (!mInitialized) {
throw new VdcBLLException(VdcBllErrors.MAC_POOL_NOT_INITIALIZED);
}
if (mAvailableMacs.isEmpty()) {
throw new VdcBLLException(VdcBllErrors.MAC_POOL_NO_MACS_LEFT);
}
java.util.Iterator<String> my = mAvailableMacs.iterator();
my.hasNext();
mac.argvalue = my.next();
CommitNewMac(mac.argvalue);
}
log.infoFormat("MacPoolManager::allocateNewMac allocated mac = '{0}", mac.argvalue);
}
private boolean CommitNewMac(String mac) {
mAvailableMacs.remove(mac);
mAllocatedMacs.add(mac);
if (mAvailableMacs.isEmpty()) {
AuditLogableBase logable = new AuditLogableBase();
AuditLogDirector.log(logable, AuditLogType.MAC_POOL_EMPTY);
return false;
}
return true;
}
public int getavailableMacsCount() {
return mAvailableMacs.size();
}
public void freeMac(String mac) {
log.infoFormat("MacPoolManager::freeMac(mac = '{0}') - entered", mac);
synchronized (mLocObj) {
if (!mInitialized) {
throw new VdcBLLException(VdcBllErrors.MAC_POOL_NOT_INITIALIZED);
}
if (mAllocatedCustomMacs.contains(mac)) {
mAllocatedCustomMacs.remove(mac);
} else if (mAllocatedMacs.contains(mac)) {
mAllocatedMacs.remove(mac);
mAvailableMacs.add(mac);
}
}
}
/**
* Add user define mac address Function return false if the mac is in use
*
* @param mac
* @return
*/
public boolean AddMac(String mac) {
boolean retVal = true;
synchronized (mLocObj) {
if (mAllocatedMacs.contains(mac)) {
retVal = false;
} else {
if (mAvailableMacs.contains(mac)) {
retVal = CommitNewMac(mac);
} else if (mAllocatedCustomMacs.contains(mac)) {
retVal = false;
} else {
mAllocatedCustomMacs.add(mac);
}
}
}
return retVal;
}
public boolean IsMacInUse(String mac) {
return mAllocatedMacs.contains(mac) || mAllocatedCustomMacs.contains(mac);
}
private static LogCompat log = LogFactoryCompat.getLog(MacPoolManager.class);
}