package org.ovirt.engine.core.bll.scheduling.external; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import javax.inject.Singleton; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.utils.Pair; 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.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton public class ExternalSchedulerBrokerImpl implements ExternalSchedulerBroker { private static final String DISCOVER = "discover"; private static final String FILTER = "runFilters"; private static final String SCORE = "runCostFunctions"; private static final String BALANCE = "runLoadBalancing"; private static final Object[] EMPTY = new Object[] {}; private static final Logger log = LoggerFactory.getLogger(ExternalSchedulerBrokerImpl.class); private XmlRpcClientConfigImpl config = null; public ExternalSchedulerBrokerImpl() { String extSchedUrl = Config.getValue(ConfigValues.ExternalSchedulerServiceURL); config = new XmlRpcClientConfigImpl(); config.setEnabledForExtensions(true); config.setConnectionTimeout(Config.getValue(ConfigValues.ExternalSchedulerConnectionTimeout)); config.setReplyTimeout(Config.getValue(ConfigValues.ExternalSchedulerResponseTimeout)); try { config.setServerURL(new URL(extSchedUrl)); } catch (MalformedURLException e) { log.error("External scheduler got bad url: {}", e.getMessage()); log.debug("Exception", e); } } @Override public Optional<ExternalSchedulerDiscoveryResult> runDiscover() { try { XmlRpcClient client = new XmlRpcClient(); client.setConfig(config); Object result = client.execute(DISCOVER, EMPTY); return parseDiscoverResults(result); } catch (XmlRpcException e) { log.error("Error communicating with the external scheduler while discovering: {}", e.getMessage()); log.debug("Exception", e); return Optional.empty(); } } private Optional<ExternalSchedulerDiscoveryResult> parseDiscoverResults(Object result) { ExternalSchedulerDiscoveryResult retValue = new ExternalSchedulerDiscoveryResult(); if (!retValue.populate(result)) { return Optional.empty(); } return Optional.of(retValue); } @Override public List<Guid> runFilters(List<String> filterNames, List<Guid> hostIDs, Guid vmID, Map<String, String> propertiesMap) { try { // Do not call the scheduler when there is no operation requested from it if (filterNames.isEmpty()) { return hostIDs; } XmlRpcClient client = new XmlRpcClient(); client.setConfig(config); Object xmlRpcStruct = client.execute(FILTER, createFilterArgs(filterNames, hostIDs, vmID, propertiesMap)); return ExternalSchedulerBrokerObjectBuilder.getFilteringResult(xmlRpcStruct).getHosts(); } catch (XmlRpcException e) { log.error("Error communicating with the external scheduler while filtering: {}", e.getMessage()); log.debug("Exception", e); auditLogFailedToConnect(); return hostIDs; } } private void auditLogFailedToConnect() { AuditLogable loggable = new AuditLogableImpl(); new AuditLogDirector().log(loggable, AuditLogType.FAILED_TO_CONNECT_TO_SCHEDULER_PROXY); } private Object[] createFilterArgs(List<String> filterNames, List<Guid> hostIDs, Guid vmID, Map<String, String> propertiesMap) { Object[] sentObject = new Object[4]; // filters name sentObject[0] = filterNames; // hosts ids String[] arr = new String[hostIDs.size()]; for (int i = 0; i < arr.length; i++) { arr[i] = hostIDs.get(i).toString(); } sentObject[1] = arr; // vm id sentObject[2] = vmID.toString(); // additional args sentObject[3] = propertiesMap; return sentObject; } @Override public List<WeightResultEntry> runScores(List<Pair<String, Integer>> scoreNameAndWeight, List<Guid> hostIDs, Guid vmID, Map<String, String> propertiesMap) { try { // Do not call the scheduler when there is no operation requested from it if (scoreNameAndWeight.isEmpty()) { return Collections.emptyList(); } XmlRpcClient client = new XmlRpcClient(); client.setConfig(config); Object result = client.execute(SCORE, createScoreArgs(scoreNameAndWeight, hostIDs, vmID, propertiesMap)); return ExternalSchedulerBrokerObjectBuilder.getScoreResult(result).getHosts(); } catch (XmlRpcException e) { log.error("Error communicating with the external scheduler while running weight modules: {}", e.getMessage()); log.debug("Exception", e); auditLogFailedToConnect(); return Collections.emptyList(); } } private Object[] createScoreArgs(List<Pair<String, Integer>> scoreNameAndWeight, List<Guid> hostIDs, Guid vmID, Map<String, String> propertiesMap) { Object[] sentObject = new Object[4]; Object[] pairs = new Object[scoreNameAndWeight.size()]; for (int i = 0; i < pairs.length; i++) { pairs[i] = new Object[] { scoreNameAndWeight.get(i).getFirst(), scoreNameAndWeight.get(i).getSecond() }; } // score name + weight pairs sentObject[0] = pairs; // hosts ids String[] arr = new String[hostIDs.size()]; for (int i = 0; i < arr.length; i++) { arr[i] = hostIDs.get(i).toString(); } sentObject[1] = arr; // vm id sentObject[2] = vmID.toString(); // additional args sentObject[3] = propertiesMap; return sentObject; } @Override public Optional<BalanceResult> runBalance(String balanceName, List<Guid> hostIDs, Map<String, String> propertiesMap) { try { XmlRpcClient client = new XmlRpcClient(); client.setConfig(config); Object result = client.execute(BALANCE, createBalanceArgs(balanceName, hostIDs, propertiesMap)); return Optional.of(ExternalSchedulerBrokerObjectBuilder.getBalanceResult(result)); } catch (XmlRpcException e) { log.error("Error communicating with the external scheduler while balancing: {}", e.getMessage()); log.debug("Exception", e); auditLogFailedToConnect(); return Optional.empty(); } } private Object[] createBalanceArgs(String balanceName, List<Guid> hostIDs, Map<String, String> propertiesMap) { Object[] sentObject = new Object[3]; // balance name sentObject[0] = balanceName; // hosts ids String[] arr = new String[hostIDs.size()]; for (int i = 0; i < arr.length; i++) { arr[i] = hostIDs.get(i).toString(); } sentObject[1] = arr; // additional args sentObject[2] = propertiesMap; return sentObject; } }