package org.ovirt.engine.core.bll.scheduling.external;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.scheduling.PolicyUnit;
import org.ovirt.engine.core.common.scheduling.PolicyUnitType;
import org.ovirt.engine.core.common.utils.customprop.SimpleCustomPropertiesUtil;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogable;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableImpl;
import org.ovirt.engine.core.dao.scheduling.PolicyUnitDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ExternalSchedulerDiscovery {
private static final Logger log = LoggerFactory.getLogger(ExternalSchedulerDiscovery.class);
@Inject
private PolicyUnitDao policyUnitDao;
@Inject
ExternalSchedulerBroker broker;
protected ExternalSchedulerDiscovery() {}
/**
* Discover external schedulers and process its policy units. This operation may take time and is recommended to run
* in a different thread. If we found new policy units we save them to db and expose them for usage.
*
* @return {@code true} if new policies where found and saved to db, {@code false} otherwise.
*/
public boolean discover() {
boolean dbUpdated;
Optional<ExternalSchedulerDiscoveryResult> discoveryResult = broker.runDiscover();
if (discoveryResult.isPresent()) {
updateDB(discoveryResult.get());
log.debug("PolicyUnits updated for external broker.");
dbUpdated = true;
} else {
AuditLogable loggable = new AuditLogableImpl();
new AuditLogDirector().log(loggable, AuditLogType.FAILED_TO_CONNECT_TO_SCHEDULER_PROXY);
log.warn("Discovery returned empty result when talking to broker. Disabling external units");
List<PolicyUnit> failingPolicyUnits = policyUnitDao.getAll().stream()
.collect(Collectors.toList());
markExternalPoliciesAsDisabled(failingPolicyUnits);
dbUpdated = true;
}
return dbUpdated;
}
private void updateDB(ExternalSchedulerDiscoveryResult discoveryResult) {
List<PolicyUnit> allPolicyUnits = policyUnitDao.getAll();
Set<PolicyUnit> foundInBoth = new HashSet<>();
for (ExternalSchedulerDiscoveryUnit unit : discoveryResult.getFilters()) {
PolicyUnit found = compareToDB(allPolicyUnits, unit, PolicyUnitType.FILTER);
foundInBoth.add(found);
}
for (ExternalSchedulerDiscoveryUnit unit : discoveryResult.getScores()) {
PolicyUnit found = compareToDB(allPolicyUnits, unit, PolicyUnitType.WEIGHT);
foundInBoth.add(found);
}
for (ExternalSchedulerDiscoveryUnit unit : discoveryResult.getBalance()) {
PolicyUnit found = compareToDB(allPolicyUnits, unit, PolicyUnitType.LOAD_BALANCING);
foundInBoth.add(found);
}
// found in the db for the current broker, but not found in discovery, mark as such
markExternalPoliciesAsDisabled(allPolicyUnits.stream()
.filter(unit -> !foundInBoth.contains(unit))
.collect(Collectors.toList()));
}
private void markExternalPoliciesAsDisabled(List<PolicyUnit> units) {
for (PolicyUnit policyUnit : units) {
if (!policyUnit.isInternal()) {
policyUnit.setEnabled(false);
policyUnitDao.update(policyUnit);
}
}
}
public void markAllExternalPoliciesAsDisabled(){
markExternalPoliciesAsDisabled(policyUnitDao.getAll());
}
private PolicyUnit compareToDB(List<PolicyUnit> dbEntries,
ExternalSchedulerDiscoveryUnit discoveryUnit,
PolicyUnitType type) {
for (PolicyUnit policyUnit : dbEntries) {
if (policyUnit.isInternal()) {
continue;
}
if (policyUnit.getPolicyUnitType() != type) {
continue;
}
if (!policyUnit.getName().equals(discoveryUnit.getName())) {
continue;
}
Map<String, String> discoveryPropMap =
StringUtils.isEmpty(discoveryUnit.getRegex()) ? new LinkedHashMap<>() :
SimpleCustomPropertiesUtil.getInstance().convertProperties(discoveryUnit.getRegex());
if (!discoveryPropMap.equals(policyUnit.getParameterRegExMap()) ||
!discoveryUnit.getDescription().equals(policyUnit.getDescription()) ||
!policyUnit.isEnabled()) {
sendToDb(discoveryUnit, policyUnit, type);
}
return policyUnit;
}
return sendToDb(discoveryUnit, null, type);
}
private PolicyUnit sendToDb(ExternalSchedulerDiscoveryUnit discovery,
/* Nullable */PolicyUnit policyUnit, PolicyUnitType type) {
PolicyUnit policy = createFromDiscoveryUnit(discovery, type);
if (policyUnit != null) {
log.warn("Policy unit {} already reported by broker.", policyUnit.getName());
}
if (policyUnit != null && policyUnit.getId() != null) {
policy.setId(policyUnit.getId());
policyUnitDao.update(policy);
} else {
policy.setId(Guid.newGuid());
policyUnitDao.save(policy);
}
return policy;
}
private PolicyUnit createFromDiscoveryUnit(ExternalSchedulerDiscoveryUnit discoveryUnit, PolicyUnitType type) {
PolicyUnit policy = new PolicyUnit();
policy.setInternal(false);
policy.setName(discoveryUnit.getName());
policy.setPolicyUnitType(type);
policy.setDescription(discoveryUnit.getDescription());
if (!StringUtils.isEmpty(discoveryUnit.getRegex())) {
policy.setParameterRegExMap(SimpleCustomPropertiesUtil.getInstance()
.convertProperties(discoveryUnit.getRegex()));
} else {
policy.setParameterRegExMap(new LinkedHashMap<>());
}
return policy;
}
}