package org.ovirt.engine.core.bll.host.provider.foreman;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.DeserializationConfig.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.ovirt.engine.core.bll.host.provider.ContentHostProvider;
import org.ovirt.engine.core.bll.host.provider.HostProviderProxy;
import org.ovirt.engine.core.bll.provider.BaseProviderProxy;
import org.ovirt.engine.core.common.businessentities.ErrataData;
import org.ovirt.engine.core.common.businessentities.Erratum;
import org.ovirt.engine.core.common.businessentities.ExternalComputeResource;
import org.ovirt.engine.core.common.businessentities.ExternalDiscoveredHost;
import org.ovirt.engine.core.common.businessentities.ExternalHostGroup;
import org.ovirt.engine.core.common.businessentities.Provider;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.queries.ErrataFilter;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.uutils.crypto.CryptMD5;
public class ForemanHostProviderProxy extends BaseProviderProxy implements HostProviderProxy {
private ObjectMapper objectMapper = new ObjectMapper();
private static final String API_ENTRY_POINT = "/api/v2";
private static final String JSON_FORMAT = "format=json";
private static final String API_VERSION_ENTRY_POINT = API_ENTRY_POINT + "/status";
private static final String HOSTS_ENTRY_POINT = API_ENTRY_POINT + "/hosts";
private static final String ALL_HOSTS_QUERY = HOSTS_ENTRY_POINT + "?" + JSON_FORMAT;
private static final String SEARCH_SECTION_FORMAT = "search=%1$s";
static final String SEARCH_QUERY_FORMAT = "?" + SEARCH_SECTION_FORMAT + "&" + JSON_FORMAT;
private static final String HOST_GROUPS_ENTRY_POINT = API_ENTRY_POINT + "/hostgroups";
private static final String HOST_GROUPS_QUERY = HOST_GROUPS_ENTRY_POINT + "?" + JSON_FORMAT;
private static final String COMPUTE_RESOURCES_HOSTS_ENTRY_POINT = API_ENTRY_POINT
+ "/compute_resources?search=" + URLEncoder.encode("oVirt|RHEV");
private static final String DISCOVERED_HOSTS = "/discovered_hosts";
private static final String DISCOVERED_HOSTS_ENTRY_POINT = API_ENTRY_POINT + DISCOVERED_HOSTS;
private static final Version KATELLO_V3_VERSION = new Version("1.11");
public ForemanHostProviderProxy(Provider<?> hostProvider) {
super(hostProvider);
objectMapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
byte[] runHttpGetMethod(String relativeUrl) {
return runHttpMethod(
HttpMethodType.GET,
"application/json; charset=utf-8",
null,
createConnection(relativeUrl));
}
private List<VDS> runHostListMethod(String relativeUrl) {
try {
ForemanHostWrapper fhw = objectMapper.readValue(runHttpGetMethod(relativeUrl), ForemanHostWrapper.class);
return mapHosts(Arrays.asList(fhw.getResults()));
} catch (IOException e) {
return null;
}
}
private List<ExternalDiscoveredHost> runDiscoveredHostListMethod(String relativeUrl) {
try {
ForemanDiscoveredHostWrapper fdw =
objectMapper.readValue(runHttpGetMethod(relativeUrl), ForemanDiscoveredHostWrapper.class);
return mapDiscoveredHosts(Arrays.asList(fdw.getResults()));
} catch (IOException e) {
return null;
}
}
private List<ExternalHostGroup> runHostGroupListMethod(String relativeUrl) {
try {
ForemanHostGroupWrapper fhgw =
objectMapper.readValue(runHttpGetMethod(relativeUrl), ForemanHostGroupWrapper.class);
return mapHostGroups(Arrays.asList(fhgw.getResults()));
} catch (IOException e) {
return null;
}
}
private List<ExternalComputeResource> runComputeResourceMethod(String relativeUrl) {
try {
ForemanComputerResourceWrapper fcrw =
objectMapper.readValue(runHttpGetMethod(relativeUrl), ForemanComputerResourceWrapper.class);
return mapComputeResource(Arrays.asList(fcrw.getResults()));
} catch (IOException e) {
return null;
}
}
// Mapping
private List<ExternalComputeResource> mapComputeResource(List<ForemanComputerResource> foremanCrs) {
List<ExternalComputeResource> crs = new ArrayList<>(foremanCrs.size());
for (ForemanComputerResource cr : foremanCrs) {
ExternalComputeResource computeResource = new ExternalComputeResource();
computeResource.setName(cr.getName());
computeResource.setUrl(cr.getUrl());
computeResource.setId(cr.getId());
computeResource.setProvider(cr.getProvider());
computeResource.setUser(cr.getUser());
crs.add(computeResource);
}
return crs;
}
private List<ExternalDiscoveredHost> mapDiscoveredHosts(List<ForemanDiscoveredHost> foremanHosts) {
List<ExternalDiscoveredHost> hosts = new ArrayList<>(foremanHosts.size());
for (ForemanDiscoveredHost host : foremanHosts) {
ExternalDiscoveredHost dhost = new ExternalDiscoveredHost();
dhost.setName(host.getName());
dhost.setIp(host.getIp());
dhost.setMac(host.getMac());
dhost.setLastReport(host.getLastReport());
dhost.setSubnetName(host.getSubnetName());
hosts.add(dhost);
}
return hosts;
}
private List<VDS> mapHosts(List<ForemanHost> foremanHosts) {
List<VDS> hosts = new ArrayList<>(foremanHosts.size());
for (ForemanHost foremanHost : foremanHosts) {
VDS host = new VDS();
host.setVdsName(foremanHost.getName());
host.setHostName(foremanHost.getName());
hosts.add(host);
}
return hosts;
}
private List<ExternalHostGroup> mapHostGroups(List<ForemanHostGroup> foremanHostGroups) {
Map<Integer, ExternalHostGroup> hostGroups = new HashMap<>();
for (ForemanHostGroup hostGroup : foremanHostGroups) {
ExternalHostGroup hostgroup = new ExternalHostGroup();
hostgroup.setHostgroupId(hostGroup.getId());
hostgroup.setName(hostGroup.getName());
hostgroup.setOperatingsystemId(hostGroup.getOperatingSystemId());
hostgroup.setEnvironmentId(hostGroup.getEnvironmentId());
hostgroup.setDomainId(hostGroup.getDomainId());
hostgroup.setSubnetId(hostGroup.getSubnetId());
hostgroup.setParameters(hostGroup.getParameters());
hostgroup.setMediumId(hostGroup.getMediumId());
hostgroup.setArchitectureId(hostGroup.getArchitectureId());
hostgroup.setPtableId(hostGroup.getPtableId());
hostgroup.setOperatingsystemName(hostGroup.getOperatingSystemName());
hostgroup.setDomainName(hostGroup.getDomainName());
hostgroup.setSubnetName(hostGroup.getSubnetName());
hostgroup.setArchitectureName(hostGroup.getArchitectureName());
hostgroup.setAncestry(hostGroup.getAncestry());
hostgroup.setEnvironmentName(hostGroup.getEnvironmentName());
hostgroup.setPtableName(hostGroup.getPtableName());
hostgroup.setMediumName(hostGroup.getMediumName());
hostGroups.put(hostGroup.getId(), hostgroup);
}
List<ExternalHostGroup> ret = new ArrayList<>(foremanHostGroups.size());
for (ForemanHostGroup hostGroup : foremanHostGroups) {
if (hostGroup.getAncestry() != null) {
String[] ancestries = hostGroup.getAncestry().split("/");
if (hostGroup.getMediumName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String medName = hg.getMediumName();
if (medName != null) {
int medId = hg.getMediumId();
hostGroups.get(hostGroup.getId()).setMediumName(medName);
hostGroups.get(hostGroup.getId()).setMediumId(medId);
break;
}
}
}
if (hostGroup.getEnvironmentName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String envName = hg.getEnvironmentName();
if (envName != null) {
int envId = hg.getEnvironmentId();
hostGroups.get(hostGroup.getId()).setEnvironmentName(envName);
hostGroups.get(hostGroup.getId()).setEnvironmentId(envId);
break;
}
}
}
if (hostGroup.getPtableName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String ptableName = hg.getPtableName();
if (ptableName != null) {
int ptableId = hg.getPtableId();
hostGroups.get(hostGroup.getId()).setPtableName(ptableName);
hostGroups.get(hostGroup.getId()).setPtableId(ptableId);
break;
}
}
}
if (hostGroup.getArchitectureName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String archName = hg.getArchitectureName();
if (archName != null) {
int archId = hg.getArchitectureId();
hostGroups.get(hostGroup.getId()).setArchitectureName(archName);
hostGroups.get(hostGroup.getId()).setArchitectureId(archId);
break;
}
}
}
if (hostGroup.getOperatingSystemName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String osName = hg.getOperatingsystemName();
if (osName != null) {
int osId = hg.getOperatingsystemId();
hostGroups.get(hostGroup.getId()).setOperatingsystemName(osName);
hostGroups.get(hostGroup.getId()).setOperatingsystemId(osId);
break;
}
}
}
if (hostGroup.getDomainName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String domainName = hg.getDomainName();
if (domainName != null) {
int domainId = hg.getDomainId();
hostGroups.get(hostGroup.getId()).setDomainName(domainName);
hostGroups.get(hostGroup.getId()).setDomainId(domainId);
break;
}
}
}
if (hostGroup.getSubnetName() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
String subnetName = hg.getSubnetName();
if (subnetName != null) {
int subnetId = hg.getSubnetId();
hostGroups.get(hostGroup.getId()).setSubnetName(subnetName);
hostGroups.get(hostGroup.getId()).setSubnetId(subnetId);
break;
}
}
}
if (hostGroup.getParameters() == null) {
for (int i = ancestries.length - 1; i >= 0; i--) {
ExternalHostGroup hg = hostGroups.get(Integer.parseInt(ancestries[i]));
Map<String, String> parameters = hg.getParameters();
if (parameters != null) {
hostGroups.get(hostGroup.getId()).setParameters(parameters);
break;
}
}
}
}
ret.add(hostGroups.get(hostGroup.getId()));
}
return ret;
}
@Override
public List<VDS> getAll() {
return runHostListMethod(ALL_HOSTS_QUERY);
}
@Override
public List<VDS> getFiltered(String filter) {
return runHostListMethod(HOSTS_ENTRY_POINT + String.format(SEARCH_QUERY_FORMAT, filter));
}
@Override
public List<ExternalDiscoveredHost> getDiscoveredHosts() {
return runDiscoveredHostListMethod(DISCOVERED_HOSTS_ENTRY_POINT);
}
@Override
public List<ExternalHostGroup> getHostGroups() {
return runHostGroupListMethod(HOST_GROUPS_QUERY);
}
@Override
public List<ExternalComputeResource> getComputeResources() {
return runComputeResourceMethod(COMPUTE_RESOURCES_HOSTS_ENTRY_POINT);
}
@Override
public void provisionHost(VDS host,
ExternalHostGroup hg,
ExternalComputeResource computeResource,
String mac,
String discoverName,
String rootPassword,
String ip) {
final String entityBody = "{\n" +
" \"discovered_host\": {\n" +
" \"name\": \"" + host.getName() + "\",\n" +
" \"hostgroup_id\": \"" + hg.getHostgroupId() + "\",\n" +
" \"environment_id\": \"" + hg.getEnvironmentId() + "\",\n" +
" \"mac\": \"" + mac + "\",\n" +
" \"domain_id\": \"" + hg.getDomainId() + "\",\n" +
" \"subnet_id\": \"" + hg.getSubnetId() + "\",\n" +
" \"ip\": \"" + ip + "\",\n" +
" \"architecture_id\": \"" + hg.getArchitectureId() + "\",\n" +
" \"operatingsystem_id\": \"" + hg.getOperatingsystemId() + "\",\n" +
" \"medium_id\": \"" + hg.getMediumId() + "\",\n" +
" \"ptable_id\": \"" + hg.getPtableId() + "\",\n" +
" \"root_pass\": \"" + rootPassword + "\",\n" +
" \"host_parameters_attributes\": [\n" +
" {\n" +
" \"name\": \"host_ovirt_id\",\n" +
" \"value\": \"" + host.getId() + "\",\n" +
" \"_destroy\": \"false\"\n" +
" },\n" +
" {\n" +
" \"name\": \"compute_resource_id\",\n" +
" \"value\": \"" + computeResource.getId() + "\",\n" +
" \"_destroy\": \"false\"\n" +
" },\n" +
" {\n" +
" \"name\": \"pass\",\n" +
" \"value\": \"" + CryptMD5.crypt(rootPassword) + "\",\n" +
" \"_destroy\": \"false\"\n" +
" },\n" +
" {\n" +
" \"name\": \"management\",\n" +
" \"value\": \"" + computeResource.getUrl().replaceAll("(http://|/api|/ovirt-engine)", "") + "\",\n" +
" \"_destroy\": \"false\"\n" +
" }\n" +
" ]\n" +
" }\n" +
"}";
runHttpMethod(
HttpMethodType.PUT,
"application/json; charset=utf-8",
entityBody,
createConnection(DISCOVERED_HOSTS_ENTRY_POINT + "/" + discoverName)
);
}
@Override
protected void afterReadResponse(HttpURLConnection connection, byte[] response) throws Exception {
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK
&& connection.getResponseCode() != HttpURLConnection.HTTP_MOVED_TEMP) {
ForemanErrorWrapper ferr = objectMapper.readValue(response, ForemanErrorWrapper.class);
String err = StringUtils.join(ferr.getForemanError().getFullMessages(), ", ");
throw new EngineException(EngineError.PROVIDER_FAILURE, err);
}
}
@Override
public void testConnection() {
runHttpGetMethod(API_ENTRY_POINT);
// validate permissions to discovered host and host group.
getDiscoveredHosts();
getHostGroups();
}
@Override
public ErrataData getErrataForHost(String hostName, ErrataFilter errataFilter) {
return getContentHostProvider().getErrataForHost(hostName, errataFilter);
}
@Override
public Erratum getErratumForHost(String hostName, String erratumId) {
return getContentHostProvider().getErratumForHost(hostName, erratumId);
}
@Override
public boolean isContentHostExist(String hostName) {
return getContentHostProvider().isContentHostExist(hostName);
}
private ContentHostProvider getContentHostProvider() {
Version foremanVersion = getForemanVersion();
if (foremanVersion != null && foremanVersion.greaterOrEquals(KATELLO_V3_VERSION)) {
return new KatelloV30Provider(this);
} else {
return new KatelloV21Provider(this);
}
}
private Version getForemanVersion() {
try {
ReportedForemanStatus status =
objectMapper.readValue(runHttpGetMethod(API_VERSION_ENTRY_POINT), ReportedForemanStatus.class);
return new Version(status.getVersion());
} catch (IOException e) {
log.warn(
"Unable to detect Foreman version for provider {}. Using older version to connect to the provider",
getProvider().getName());
return null;
}
}
}