/*
* Copyright (c) 2010-2016 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ovirt.engine.api.restapi.resource;
import static java.util.stream.Collectors.joining;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.ovirt.engine.api.model.Action;
import org.ovirt.engine.api.model.Api;
import org.ovirt.engine.api.model.ApiSummary;
import org.ovirt.engine.api.model.ApiSummaryItem;
import org.ovirt.engine.api.model.BaseResource;
import org.ovirt.engine.api.model.DetailedLink;
import org.ovirt.engine.api.model.ProductInfo;
import org.ovirt.engine.api.model.Rsdl;
import org.ovirt.engine.api.model.SpecialObjects;
import org.ovirt.engine.api.model.Tag;
import org.ovirt.engine.api.model.Template;
import org.ovirt.engine.api.model.Version;
import org.ovirt.engine.api.resource.AffinityLabelsResource;
import org.ovirt.engine.api.resource.BookmarksResource;
import org.ovirt.engine.api.resource.ClusterLevelsResource;
import org.ovirt.engine.api.resource.ClustersResource;
import org.ovirt.engine.api.resource.CpuProfilesResource;
import org.ovirt.engine.api.resource.DataCentersResource;
import org.ovirt.engine.api.resource.DiskProfilesResource;
import org.ovirt.engine.api.resource.DisksResource;
import org.ovirt.engine.api.resource.EventsResource;
import org.ovirt.engine.api.resource.ExternalVmImportsResource;
import org.ovirt.engine.api.resource.HostsResource;
import org.ovirt.engine.api.resource.IconsResource;
import org.ovirt.engine.api.resource.ImageTransfersResource;
import org.ovirt.engine.api.resource.InstanceTypesResource;
import org.ovirt.engine.api.resource.JobsResource;
import org.ovirt.engine.api.resource.MacPoolsResource;
import org.ovirt.engine.api.resource.NetworkFiltersResource;
import org.ovirt.engine.api.resource.NetworksResource;
import org.ovirt.engine.api.resource.OperatingSystemsResource;
import org.ovirt.engine.api.resource.RolesResource;
import org.ovirt.engine.api.resource.SchedulingPoliciesResource;
import org.ovirt.engine.api.resource.SchedulingPolicyUnitsResource;
import org.ovirt.engine.api.resource.StorageDomainsResource;
import org.ovirt.engine.api.resource.StorageServerConnectionsResource;
import org.ovirt.engine.api.resource.SystemPermissionsResource;
import org.ovirt.engine.api.resource.SystemResource;
import org.ovirt.engine.api.resource.TagsResource;
import org.ovirt.engine.api.resource.TemplatesResource;
import org.ovirt.engine.api.resource.VmPoolsResource;
import org.ovirt.engine.api.resource.VmsResource;
import org.ovirt.engine.api.resource.VnicProfilesResource;
import org.ovirt.engine.api.resource.aaa.DomainsResource;
import org.ovirt.engine.api.resource.aaa.GroupsResource;
import org.ovirt.engine.api.resource.aaa.UsersResource;
import org.ovirt.engine.api.resource.externalhostproviders.EngineKatelloErrataResource;
import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostProvidersResource;
import org.ovirt.engine.api.resource.openstack.OpenstackImageProvidersResource;
import org.ovirt.engine.api.resource.openstack.OpenstackNetworkProvidersResource;
import org.ovirt.engine.api.resource.openstack.OpenstackVolumeProvidersResource;
import org.ovirt.engine.api.restapi.invocation.Current;
import org.ovirt.engine.api.restapi.logging.MessageBundle;
import org.ovirt.engine.api.restapi.logging.Messages;
import org.ovirt.engine.api.restapi.resource.aaa.BackendDomainsResource;
import org.ovirt.engine.api.restapi.resource.aaa.BackendGroupsResource;
import org.ovirt.engine.api.restapi.resource.aaa.BackendUsersResource;
import org.ovirt.engine.api.restapi.resource.externalhostproviders.BackendEngineKatelloErrataResource;
import org.ovirt.engine.api.restapi.resource.externalhostproviders.BackendExternalHostProvidersResource;
import org.ovirt.engine.api.restapi.resource.openstack.BackendOpenStackImageProvidersResource;
import org.ovirt.engine.api.restapi.resource.openstack.BackendOpenStackNetworkProvidersResource;
import org.ovirt.engine.api.restapi.resource.openstack.BackendOpenStackVolumeProvidersResource;
import org.ovirt.engine.api.restapi.rsdl.RsdlLoader;
import org.ovirt.engine.api.restapi.types.DateMapper;
import org.ovirt.engine.api.restapi.types.MappingLocator;
import org.ovirt.engine.api.restapi.types.VersionMapper;
import org.ovirt.engine.api.restapi.util.ParametersHelper;
import org.ovirt.engine.api.utils.ApiRootLinksCreator;
import org.ovirt.engine.api.utils.LinkCreator;
import org.ovirt.engine.core.branding.BrandingManager;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.constants.QueryConstants;
import org.ovirt.engine.core.common.mode.ApplicationMode;
import org.ovirt.engine.core.common.queries.ConfigurationValues;
import org.ovirt.engine.core.common.queries.GetSystemStatisticsQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BackendApiResource
extends AbstractBackendActionableResource<Api, Object>
implements SystemResource {
private static volatile BackendApiResource instance;
public static BackendApiResource getInstance() {
if (instance == null) {
synchronized (BackendApiResource.class) {
if (instance == null) {
BackendApiResource tmp = new BackendApiResource();
tmp.init();
instance = tmp;
}
}
}
return instance;
}
private static final Logger log = LoggerFactory.getLogger(BackendApiResource.class);
private static final String API_SCHEMA = "api.xsd";
private static final String RSDL_CONSTRAINT_PARAMETER = "rsdl";
private static final String SCHEMA_CONSTRAINT_PARAMETER = "schema";
private static final String SCHEMA_NAME = "ovirt-engine-api-schema.xsd";
ApplicationMode appMode = ApplicationMode.AllModes;
public BackendApiResource() {
super(Guid.Empty.toString(), Api.class, Object.class);
}
private void init() {
// Create and populate the message bundle:
messageBundle = new MessageBundle();
messageBundle.setPath(Messages.class.getName());
messageBundle.populate();
// Create and populate the mapping locator:
mappingLocator = new MappingLocator();
mappingLocator.populate();
}
private Collection<DetailedLink> getLinks() {
return ApiRootLinksCreator.getLinks(getAbsolutePath());
}
private Collection<DetailedLink> getGlusterLinks() {
return ApiRootLinksCreator.getGlusterLinks(getAbsolutePath());
}
private String getAbsolutePath(String... segments) {
Current current = getCurrent();
return current.getAbsolutePath(segments);
}
private Template createBlankTemplate() {
Template template = new Template();
String id = "00000000-0000-0000-0000-000000000000";
template.setId(id);
template.setHref(getAbsolutePath( "templates", id));
return template;
}
private Tag createRootTag() {
Tag tag = new Tag();
String id = "00000000-0000-0000-0000-000000000000";
tag.setId(id);
tag.setHref(getAbsolutePath("tags", id));
return tag;
}
private Api getApi() {
Api api = new Api();
api.setTime(DateMapper.map(new Date(), null));
for (DetailedLink detailedLink : getLinks()) {
//add thin link
api.getLinks().add(LinkCreator.createLink(detailedLink.getHref(), detailedLink.getRel()));
//when required - add extra link for search
if (detailedLink.isSetLinkCapabilities() && detailedLink.getLinkCapabilities().isSetSearchable() && detailedLink.getLinkCapabilities().isSearchable()) {
api.getLinks().add(LinkCreator.createLink(detailedLink.getHref(), detailedLink.getRel(), detailedLink.getRequest().getUrl().getParametersSets()));
}
//add special links
SpecialObjects specialObjects = new SpecialObjects();
specialObjects.setBlankTemplate(createBlankTemplate());
specialObjects.setRootTag(createRootTag());
api.setSpecialObjects(specialObjects);
}
return api;
}
private Api getGlusterApi() {
Api api = new Api();
api.setTime(DateMapper.map(new Date(), null));
for (DetailedLink detailedLink : getGlusterLinks()) {
// add thin link
api.getLinks().add(LinkCreator.createLink(detailedLink.getHref(), detailedLink.getRel()));
// when required - add extra link for search
if (detailedLink.isSetLinkCapabilities() && detailedLink.getLinkCapabilities().isSetSearchable()
&& detailedLink.getLinkCapabilities().isSearchable()) {
api.getLinks().add(LinkCreator.createLink(detailedLink.getHref(),
detailedLink.getRel(),
detailedLink.getRequest().getUrl().getParametersSets()));
}
// add special links
SpecialObjects specialObjects = new SpecialObjects();
specialObjects.setRootTag(createRootTag());
api.setSpecialObjects(specialObjects);
}
return api;
}
private void addHeader(BaseResource response, Response.ResponseBuilder responseBuilder) {
// Concatenate links in a single header with a comma-separated value, which is the canonical form according
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2.
String root = getCurrent().getRoot();
String links = response.getLinks().stream()
.map(link -> String.format("<%s>; rel=%s", root + link.getHref(), link.getRel()))
.sorted()
.collect(joining(","));
responseBuilder.header("Link", links);
}
private Response.ResponseBuilder getResponseBuilder(BaseResource response) {
Response.ResponseBuilder responseBuilder = Response.ok();
if (response instanceof Api) {
addHeader(response, responseBuilder);
}
return responseBuilder;
}
@Override
public Response head() {
appMode = getCurrent().getApplicationMode();
Api api;
if(appMode == ApplicationMode.GlusterOnly) {
api = getGlusterApi();
}
else {
api = getApi();
}
return getResponseBuilder(api).build();
}
@Override
public Response get() {
appMode = getCurrent().getApplicationMode();
if (ParametersHelper.getParameter(httpHeaders, uriInfo, RSDL_CONSTRAINT_PARAMETER) != null) {
try {
Rsdl rsdl = addSystemVersion(getRSDL());
return Response.ok().entity(rsdl).build();
} catch (Exception e) {
throw new WebApplicationException(e, Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
}
}
else if (ParametersHelper.getParameter(httpHeaders, uriInfo, SCHEMA_CONSTRAINT_PARAMETER) != null) {
return getSchema();
}
else {
BaseResource response;
if (appMode == ApplicationMode.GlusterOnly) {
response = addGlusterSummary(addSystemVersion(getGlusterApi()));
}
else {
response = addSummary(addSystemVersion(getApi()));
}
return getResponseBuilder(response).entity(response).build();
}
}
private Response getSchema() {
byte[] buffer = new byte[4096];
String version = getCurrent().getVersion();
String resourcePath = String.format("/v%s/%s", version, API_SCHEMA);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath);){
int count;
while ((count = is.read(buffer)) != -1) {
baos.write(buffer, 0, count);
}
baos.flush();
return Response.ok(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition", "attachment; filename = " + SCHEMA_NAME)
.build();
} catch (IOException e) {
log.error("Loading api.xsd file failed.", e);
return Response.serverError().build();
}
}
private Rsdl addSystemVersion(Rsdl rsdl) {
rsdl.setVersion(getVersion());
return rsdl;
}
private Version getVersion() {
VdcQueryReturnValue result = runQuery(VdcQueryType.GetProductVersion, new VdcQueryParametersBase());
return VersionMapper.map((org.ovirt.engine.core.compat.Version) result.getReturnValue());
}
public Rsdl getRSDL() throws ClassNotFoundException, IOException {
return RsdlLoader.loadRsdl(Rsdl.class);
}
private Api addSystemVersion(Api api) {
String productVersion = getConfigurationValueDefault(ConfigurationValues.ProductRPMVersion);
if (productVersion != null) {
BrandingManager obrand = BrandingManager.getInstance();
ProductInfo productInfo = new ProductInfo();
productInfo.setName(obrand.getMessage("obrand.backend.product"));
productInfo.setVendor(obrand.getMessage("obrand.backend.vendor"));
Version version = getVersion();
version.setFullVersion(productVersion);
productInfo.setVersion(version);
api.setProductInfo(productInfo);
}
return api;
}
private Map<String, Integer> getSystemStatistics() {
VdcQueryReturnValue result = runQuery(
VdcQueryType.GetSystemStatistics,
new GetSystemStatisticsQueryParameters(-1)
);
if (result.getSucceeded()) {
return asStatisticsMap(result.getReturnValue());
}
return Collections.emptyMap();
}
@SuppressWarnings("unchecked")
private HashMap<String, Integer> asStatisticsMap(Object result) {
return (HashMap<String, Integer>)result;
}
private Api addSummary(Api api) {
Map<String, Integer> stats = getSystemStatistics();
ApiSummary summary = new ApiSummary();
summary.setVms(
makeSummaryItem(
stats,
QueryConstants.SYSTEM_STATS_TOTAL_VMS_FIELD,
QueryConstants.SYSTEM_STATS_ACTIVE_VMS_FIELD
)
);
summary.setHosts(
makeSummaryItem(
stats,
QueryConstants.SYSTEM_STATS_TOTAL_HOSTS_FIELD,
QueryConstants.SYSTEM_STATS_ACTIVE_HOSTS_FIELD
)
);
summary.setUsers(
makeSummaryItem(
stats,
QueryConstants.SYSTEM_STATS_TOTAL_USERS_FIELD,
QueryConstants.SYSTEM_STATS_ACTIVE_USERS_FIELD
)
);
summary.setStorageDomains(
makeSummaryItem(
stats,
QueryConstants.SYSTEM_STATS_TOTAL_STORAGE_DOMAINS_FIELD,
QueryConstants.SYSTEM_STATS_ACTIVE_STORAGE_DOMAINS_FIELD
)
);
api.setSummary(summary);
return api;
}
private Api addGlusterSummary(Api api) {
Map<String, Integer> stats = getSystemStatistics();
ApiSummary summary = new ApiSummary();
summary.setHosts(
makeSummaryItem(
stats,
"total_vds",
"active_vds"
)
);
summary.setUsers(
makeSummaryItem(
stats,
"total_users",
"active_users"
)
);
api.setSummary(summary);
return api;
}
private ApiSummaryItem makeSummaryItem(Map<String, Integer> values, String totalKey, String activeKey) {
Integer totalValue = values.get(totalKey);
Integer activeValue = values.get(activeKey);
if (totalValue == null && activeValue == null) {
return null;
}
ApiSummaryItem item = new ApiSummaryItem();
if (totalValue != null) {
item.setTotal(totalValue);
}
if (activeValue != null) {
item.setActive(activeValue);
}
return item;
}
@Override
public Response reloadConfigurations(Action action) {
return doAction(VdcActionType.ReloadConfigurations,
new VdcActionParametersBase(),
action);
}
@Override
public BookmarksResource getBookmarksResource() {
return inject(new BackendBookmarksResource());
}
@Override
public ClustersResource getClustersResource() {
return inject(new BackendClustersResource());
}
@Override
public CpuProfilesResource getCpuProfilesResource() {
return inject(new BackendCpuProfilesResource());
}
@Override
public DataCentersResource getDataCentersResource() {
return inject(new BackendDataCentersResource());
}
@Override
public DiskProfilesResource getDiskProfilesResource() {
return inject(new BackendDiskProfilesResource());
}
@Override
public DisksResource getDisksResource() {
return inject(new BackendDisksResource());
}
@Override
public DomainsResource getDomainsResource() {
return inject(new BackendDomainsResource());
}
@Override
public EventsResource getEventsResource() {
return inject(new BackendEventsResource());
}
@Override
public ExternalHostProvidersResource getExternalHostProvidersResource() {
return inject(new BackendExternalHostProvidersResource());
}
@Override
public ExternalVmImportsResource getExternalVmImportsResource() {
return inject(new BackendExternalVmImportsResource());
}
@Override
public GroupsResource getGroupsResource() {
return inject(new BackendGroupsResource());
}
@Override
public HostsResource getHostsResource() {
return inject(new BackendHostsResource());
}
@Override
public IconsResource getIconsResource() {
return inject(new BackendIconsResource());
}
@Override
public InstanceTypesResource getInstanceTypesResource() {
return inject(new BackendInstanceTypesResource());
}
@Override
public JobsResource getJobsResource() {
return inject(new BackendJobsResource());
}
@Override
public MacPoolsResource getMacPoolsResource() {
return inject(new BackendMacPoolsResource());
}
@Override
public NetworkFiltersResource getNetworkFiltersResource() {
return inject(new BackendNetworkFiltersResource());
}
@Override
public NetworksResource getNetworksResource() {
return inject(new BackendNetworksResource());
}
@Override
public OpenstackImageProvidersResource getOpenstackImageProvidersResource() {
return inject(new BackendOpenStackImageProvidersResource());
}
@Override
public OpenstackNetworkProvidersResource getOpenstackNetworkProvidersResource() {
return inject(new BackendOpenStackNetworkProvidersResource());
}
@Override
public OpenstackVolumeProvidersResource getOpenstackVolumeProvidersResource() {
return inject(new BackendOpenStackVolumeProvidersResource());
}
@Override
public OperatingSystemsResource getOperatingSystemsResource() {
return inject(new BackendOperatingSystemsResource());
}
@Override
public RolesResource getRolesResource() {
return inject(new BackendRolesResource());
}
@Override
public SchedulingPoliciesResource getSchedulingPoliciesResource() {
return inject(new BackendSchedulingPoliciesResource());
}
@Override
public SchedulingPolicyUnitsResource getSchedulingPolicyUnitsResource() {
return inject(new BackendSchedulingPolicyUnitsResource());
}
@Override
public StorageDomainsResource getStorageDomainsResource() {
return inject(new BackendStorageDomainsResource());
}
@Override
public StorageServerConnectionsResource getStorageConnectionsResource() {
return inject(new BackendStorageServerConnectionsResource());
}
@Override
public EngineKatelloErrataResource getKatelloErrataResource() {
return inject(new BackendEngineKatelloErrataResource());
}
@Override
public SystemPermissionsResource getPermissionsResource() {
return inject(new BackendSystemPermissionsResource());
}
@Override
public TagsResource getTagsResource() {
return inject(new BackendTagsResource());
}
@Override
public TemplatesResource getTemplatesResource() {
return inject(new BackendTemplatesResource());
}
@Override
public UsersResource getUsersResource() {
return inject(new BackendUsersResource());
}
@Override
public VmPoolsResource getVmPoolsResource() {
return inject(new BackendVmPoolsResource());
}
@Override
public VmsResource getVmsResource() {
return inject(new BackendVmsResource());
}
@Override
public VnicProfilesResource getVnicProfilesResource() {
return inject(new BackendVnicProfilesResource());
}
@Override
public ImageTransfersResource getImageTransfersResource() {
return inject(new BackendImageTransfersResource());
}
@Override
public AffinityLabelsResource getAffinityLabelsResource() {
return inject(new BackendAffinityLabelsResource());
}
@Override
public ClusterLevelsResource getClusterLevelsResource() {
return inject(new BackendClusterLevelsResource());
}
}