package org.ovirt.engine.core.bll;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.BackendService;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.ServerCpu;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.utils.CpuVendor;
import org.ovirt.engine.core.compat.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class CpuFlagsManagerHandler implements BackendService {
private static final Logger log = LoggerFactory.getLogger(CpuFlagsManagerHandler.class);
private static Map<Version, CpuFlagsManager> managersDictionary = new HashMap<>();
@PostConstruct
public void initDictionaries() {
log.info("Start initializing dictionaries");
managersDictionary.clear();
for (Version ver : Config.<HashSet<Version>> getValue(ConfigValues.SupportedClusterLevels)) {
managersDictionary.put(ver, new CpuFlagsManager(ver));
}
log.info("Finished initializing dictionaries");
}
public String getCpuId(String name, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.getVDSVerbDataByCpuName(name) : null;
}
public String getCpuNameByCpuId(String name, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.getCpuNameByCpuId(name) : null;
}
public ArchitectureType getArchitectureByCpuName(String name, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.getArchitectureByCpuName(name) : null;
}
public List<ServerCpu> allServerCpuList(Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.getAllServerCpuList() : new ArrayList<>();
}
/**
* Returns missing CPU flags if any, or null if the server match the cluster
* CPU flags
*
* @return list of missing CPU flags
*/
public List<String> missingServerCpuFlags(String clusterCpuName, String serverFlags, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.missingServerCpuFlags(clusterCpuName, serverFlags) : null;
}
public boolean checkIfCpusSameManufacture(String cpuName1, String cpuName2, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.checkIfCpusSameManufacture(cpuName1, cpuName2) : false;
}
public boolean checkIfCpusExist(String cpuName, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.checkIfCpusExist(cpuName) : false;
}
/**
* Finds max server cpu by server cpu flags only
*/
public ServerCpu findMaxServerCpuByFlags(String flags, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.findMaxServerCpuByFlags(flags) : null;
}
public Version getLatestDictionaryVersion() {
return Collections.max(managersDictionary.keySet());
}
public List<ServerCpu> getSupportedServerCpuList(Version ver, String maxCpuName) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
return cpuFlagsManager != null ? cpuFlagsManager.getSupportedServerCpuList(maxCpuName) : new ArrayList<>();
}
private static class CpuFlagsManager {
private List<ServerCpu> intelCpuList;
private List<ServerCpu> amdCpuList;
private List<ServerCpu> ibmCpuList;
private List<ServerCpu> allCpuList = new ArrayList<>();
private Map<String, ServerCpu> intelCpuByNameDictionary = new HashMap<>();
private Map<String, ServerCpu> amdCpuByNameDictionary = new HashMap<>();
private Map<String, ServerCpu> ibmCpuByNameDictionary = new HashMap<>();
private Map<String, ServerCpu> intelCpuByVdsNameDictionary = new HashMap<>();
private Map<String, ServerCpu> amdCpuByVdsNameDictionary = new HashMap<>();
private Map<String, ServerCpu> ibmCpuByVdsNameDictionary = new HashMap<>();
public CpuFlagsManager(Version ver) {
initDictionaries(ver);
}
public ArchitectureType getArchitectureByCpuName(String cpuName) {
ServerCpu cpu = getServerCpuByName(cpuName);
if (cpu != null) {
return cpu.getArchitecture();
}
return ArchitectureType.undefined;
}
public ServerCpu getServerCpuByName(String cpuName) {
ServerCpu result = null;
if (cpuName != null) {
result = intelCpuByNameDictionary.get(cpuName);
if (result == null) {
result = amdCpuByNameDictionary.get(cpuName);
}
if (result == null) {
result = ibmCpuByNameDictionary.get(cpuName);
}
}
return result;
}
@SuppressWarnings("synthetic-access")
public void initDictionaries(Version ver) {
// init dictionaries
intelCpuByNameDictionary.clear();
amdCpuByNameDictionary.clear();
ibmCpuByNameDictionary.clear();
allCpuList.clear();
String[] cpus = Config.<String> getValue(ConfigValues.ServerCPUList, ver.toString()).split("[;]", -1);
for (String cpu : cpus) {
if (!StringUtils.isEmpty(cpu)) {
// [0]-level, [1]-name, [2]-flags, [3]-verb, [4]-arch
final String[] info = cpu.split("[:]", -1);
if (info.length == 5) {
// if no flags at all create new list instead of split
HashSet<String> flgs =
StringUtils.isEmpty(info[2]) ? new HashSet<>()
: new HashSet<>(Arrays.asList(info[2].split("[,]", -1)));
String arch = info[4].trim();
ArchitectureType archType = ArchitectureType.valueOf(arch);
String levelString = info[0].trim();
int level = 0;
if (StringUtils.isNotEmpty(levelString)) {
level = Integer.parseInt(levelString);
}
ServerCpu sc = new ServerCpu(info[1], level, flgs, info[3], archType);
if (sc.getFlags().contains(CpuVendor.INTEL.getFlag())) {
intelCpuByNameDictionary.put(sc.getCpuName(), sc);
intelCpuByVdsNameDictionary.put(sc.getVdsVerbData(), sc);
} else if (sc.getFlags().contains(CpuVendor.AMD.getFlag())) {
amdCpuByNameDictionary.put(sc.getCpuName(), sc);
amdCpuByVdsNameDictionary.put(sc.getVdsVerbData(), sc);
} else if (sc.getFlags().contains(CpuVendor.IBM.getFlag())) {
ibmCpuByNameDictionary.put(sc.getCpuName(), sc);
ibmCpuByVdsNameDictionary.put(sc.getVdsVerbData(), sc);
}
allCpuList.add(sc);
} else {
log.error("Error getting info for CPU '{}', not in expected format.", cpu);
}
}
}
intelCpuList = new ArrayList<>(intelCpuByNameDictionary.values());
amdCpuList = new ArrayList<>(amdCpuByNameDictionary.values());
ibmCpuList = new ArrayList<>(ibmCpuByNameDictionary.values());
Comparator<ServerCpu> cpuComparator = Comparator.comparingInt(ServerCpu::getLevel);
// Sort by the highest cpu level so the highest cpu match will be
// selected first
Collections.sort(intelCpuList, cpuComparator);
Collections.sort(amdCpuList, cpuComparator);
Collections.sort(ibmCpuList, cpuComparator);
}
public String getVDSVerbDataByCpuName(String name) {
String result = null;
ServerCpu sc = null;
if (name != null) {
if ((sc = intelCpuByNameDictionary.get(name)) != null
|| (sc = amdCpuByNameDictionary.get(name)) != null
|| (sc = ibmCpuByNameDictionary.get(name)) != null) {
result = sc.getVdsVerbData();
}
}
return result;
}
public String getCpuNameByCpuId(String vdsName) {
String result = null;
ServerCpu sc = null;
if (vdsName != null) {
if ((sc = intelCpuByVdsNameDictionary.get(vdsName)) != null
|| (sc = amdCpuByVdsNameDictionary.get(vdsName)) != null
|| (sc = ibmCpuByVdsNameDictionary.get(vdsName)) != null) {
result = sc.getCpuName();
}
}
return result;
}
public List<ServerCpu> getAllServerCpuList() {
return allCpuList;
}
/**
* Returns missing CPU flags if any, or null if the server match the
* cluster CPU flags
*
* @return list of missing CPU flags
*/
public List<String> missingServerCpuFlags(String clusterCpuName, String serverFlags) {
ServerCpu clusterCpu = null;
List<String> missingFlags = null;
HashSet<String> lstServerflags =
StringUtils.isEmpty(serverFlags) ? new HashSet<>()
: new HashSet<>(Arrays.asList(serverFlags.split("[,]", -1)));
// first find cluster cpu
if (clusterCpuName != null
&& ((clusterCpu = intelCpuByNameDictionary.get(clusterCpuName)) != null
|| (clusterCpu = amdCpuByNameDictionary.get(clusterCpuName)) != null
|| (clusterCpu = ibmCpuByNameDictionary.get(clusterCpuName)) != null
)) {
for (String flag : clusterCpu.getFlags()) {
if (!lstServerflags.contains(flag)) {
if (missingFlags == null) {
missingFlags = new ArrayList<>();
}
missingFlags.add(flag);
}
}
}
return missingFlags;
}
/**
* Return true if given flag list contains all flags of given ServerCpu
* object's flags.
*/
private boolean checkIfFlagsContainsCpuFlags(ServerCpu clusterCpu, Set<String> lstServerflags) {
return CollectionUtils.intersection(clusterCpu.getFlags(), lstServerflags).size() == clusterCpu.getFlags()
.size();
}
/**
* This method returns true if the given cpus are from the same
* manufacturer (intel or amd)
*/
public boolean checkIfCpusSameManufacture(String cpuName1, String cpuName2) {
if (cpuName1 == null || cpuName2 == null) {
return false;
}
if (intelCpuByNameDictionary.containsKey(cpuName1)) {
return intelCpuByNameDictionary.containsKey(cpuName2);
}
if (amdCpuByNameDictionary.containsKey(cpuName1)) {
return amdCpuByNameDictionary.containsKey(cpuName2);
}
if (ibmCpuByNameDictionary.containsKey(cpuName1)) {
return ibmCpuByNameDictionary.containsKey(cpuName2);
}
return false;
}
public boolean checkIfCpusExist(String cpuName) {
return cpuName != null
&& (intelCpuByNameDictionary.containsKey(cpuName)
|| amdCpuByNameDictionary.containsKey(cpuName)
|| ibmCpuByNameDictionary.containsKey(cpuName));
}
/**
* Finds max server cpu by server cpu flags only
*/
public ServerCpu findMaxServerCpuByFlags(String flags) {
ServerCpu result = null;
HashSet<String> lstFlags = StringUtils.isEmpty(flags) ? new HashSet<>()
: new HashSet<>(Arrays.asList(flags.split("[,]", -1)));
if (lstFlags.contains(CpuVendor.INTEL.getFlag())) {
for (int i = intelCpuList.size() - 1; i >= 0; i--) {
if (checkIfFlagsContainsCpuFlags(intelCpuList.get(i), lstFlags)) {
result = intelCpuList.get(i);
break;
}
}
} else if (lstFlags.contains(CpuVendor.AMD.getFlag())) {
for (int i = amdCpuList.size() - 1; i >= 0; i--) {
if (checkIfFlagsContainsCpuFlags(amdCpuList.get(i), lstFlags)) {
result = amdCpuList.get(i);
break;
}
}
} else if (lstFlags.contains(CpuVendor.IBM.getFlag())) {
for (int i = ibmCpuList.size() - 1; i >= 0; i--) {
if (checkIfFlagsContainsCpuFlags(ibmCpuList.get(i), lstFlags)) {
result = ibmCpuList.get(i);
break;
}
}
}
return result;
}
/**
* Returns a list with all CPU's which are with a lower CPU level than the given CPU.
*
* @return list of supported CPUs.
*/
public List<ServerCpu> getSupportedServerCpuList(String maxCpuName) {
List<ServerCpu> supportedCpus = new ArrayList<>();
if (intelCpuByNameDictionary.containsKey(maxCpuName)) {
ServerCpu selected = intelCpuByNameDictionary.get(maxCpuName);
int selectedCpuIndex = intelCpuList.indexOf(selected);
for (int i = 0; i <= selectedCpuIndex; i++) { // list is sorted by level
supportedCpus.add(intelCpuList.get(i));
}
} else if (ibmCpuByNameDictionary.containsKey(maxCpuName)) {
ServerCpu selected = ibmCpuByNameDictionary.get(maxCpuName);
int selectedCpuIndex =ibmCpuList.indexOf(selected);
for (int i = 0; i <= selectedCpuIndex; i++) {
supportedCpus.add(ibmCpuList.get(i));
}
} else if (amdCpuByNameDictionary.containsKey(maxCpuName)) {
ServerCpu selected = amdCpuByNameDictionary.get(maxCpuName);
int selectedCpuIndex =amdCpuList.indexOf(selected);
for (int i = 0; i <= selectedCpuIndex; i++) {
supportedCpus.add(amdCpuList.get(i));
}
}
return supportedCpus;
}
}
public int compareCpuLevels(String cpuName1, String cpuName2, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
ServerCpu server1 = null;
ServerCpu server2 = null;
if (cpuFlagsManager != null) {
server1 = cpuFlagsManager.getServerCpuByName(cpuName1);
server2 = cpuFlagsManager.getServerCpuByName(cpuName2);
}
int server1Level = (server1 != null) ? server1.getLevel() : 0;
int server2Level = (server2 != null) ? server2.getLevel() : 0;
return server1Level - server2Level;
}
public boolean isCpuUpdatable(String cpuName, Version ver) {
final CpuFlagsManager cpuFlagsManager = managersDictionary.get(ver);
ServerCpu server = null;
if (cpuFlagsManager != null) {
server = cpuFlagsManager.getServerCpuByName(cpuName);
}
int serverLevel = (server != null) ? server.getLevel() : 0;
return serverLevel != 0;
}
}