/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
import static com.emc.storageos.api.mapper.ComputeMapper.map;
import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource;
import java.net.URI;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.emc.storageos.api.mapper.ComputeMapper;
import com.emc.storageos.api.service.impl.resource.utils.ComputeSystemUtils;
import com.emc.storageos.api.service.impl.response.BulkList;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.ComputeElement;
import com.emc.storageos.db.client.model.ComputeVirtualPool;
import com.emc.storageos.db.client.model.Cluster;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.DiscoveredDataObject.RegistrationStatus;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.model.BulkIdParam;
import com.emc.storageos.model.ResourceTypeEnum;
import com.emc.storageos.model.compute.ComputeElementBulkRep;
import com.emc.storageos.model.compute.ComputeElementList;
import com.emc.storageos.model.compute.ComputeElementRestRep;
import com.emc.storageos.security.authorization.CheckPermission;
import com.emc.storageos.security.authorization.DefaultPermissions;
import com.emc.storageos.security.authorization.Role;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.BadRequestException;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager;
import com.emc.storageos.volumecontroller.impl.monitoring.cim.enums.RecordType;
import com.google.common.base.Function;
@Path("/vdc/compute-elements")
@DefaultPermissions(readRoles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR },
writeRoles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public class ComputeElementService extends TaskResourceService {
private static final String EVENT_SERVICE_TYPE = "ComputeElement";
private static final String EVENT_SERVICE_SOURCE = "ComputeElementService";
@Autowired
private RecordableEventManager _evtMgr;
@Autowired
private ComputeVirtualPoolService computeVirtualPoolService;
@Override
public String getServiceType() {
return EVENT_SERVICE_TYPE;
}
private static final Logger _log = LoggerFactory.getLogger(ComputeElementService.class);
@Override
protected URI getTenantOwner(URI id) {
return null;
}
@Override
protected ComputeElement queryResource(URI id) {
ArgValidator.checkUri(id);
ComputeElement ce = _dbClient.queryObject(ComputeElement.class, id);
ArgValidator.checkEntity(ce, id, isIdEmbeddedInURL(id));
return ce;
}
@Override
protected ResourceTypeEnum getResourceType() {
return ResourceTypeEnum.COMPUTE_ELEMENT;
}
/**
* Gets the compute element with the passed id from the database.
*
* @param id the URN of a ViPR compute element.
*
* @return A reference to the registered compute element.
*
* @throws BadRequestException When the compute element is not registered.
*/
protected ComputeElement queryRegisteredResource(URI id) {
ArgValidator.checkUri(id);
ComputeElement ce = _dbClient.queryObject(ComputeElement.class, id);
ArgValidator.checkEntityNotNull(ce, id, isIdEmbeddedInURL(id));
if (!RegistrationStatus.REGISTERED.toString().equalsIgnoreCase(ce.getRegistrationStatus())) {
throw APIException.badRequests.resourceNotRegistered(ComputeElement.class.getSimpleName(), id);
}
return ce;
}
/**
* Gets the ids and self links for all compute elements.
*
* @brief List compute elements
* @return A ComputeElementList reference specifying the ids and self links for
* the compute elements.
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public ComputeElementList getComputeElements() {
ComputeElementList computeElements = new ComputeElementList();
List<URI> ids = _dbClient.queryByType(ComputeElement.class, true);
for (URI id : ids) {
ComputeElement computeElement = _dbClient.queryObject(ComputeElement.class, id);
if (computeElement != null && !computeElement.getInactive()) {
computeElements.getComputeElements().add(toNamedRelatedResource(computeElement));
}
}
return computeElements;
}
/**
* Gets the data for a compute element.
*
* @param id the URN of a ViPR compute element.
*
* @brief Show compute element
* @return A ComputeElementRestRep reference specifying the data for the
* compute element with the passed id.
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public ComputeElementRestRep getComputeElement(@PathParam("id") URI id) {
ArgValidator.checkFieldUriType(id, ComputeElement.class, "id");
ComputeElement ce = queryResource(id);
ArgValidator.checkEntity(ce, id, isIdEmbeddedInURL(id));
Host associatedHost = getAssociatedHost(ce, _dbClient);
Cluster cluster = null;
if (associatedHost!=null && !NullColumnValueGetter.isNullURI(associatedHost.getCluster())){
cluster = _dbClient.queryObject(Cluster.class, associatedHost.getCluster());
}
return ComputeMapper.map(ce, associatedHost, cluster);
}
/**
* Allows the user to deregister a registered compute element so that it is no
* longer used by the system. This simply sets the registration_status of
* the compute element to UNREGISTERED.
*
* @param id the URN of a ViPR compute element to deregister.
*
* @brief Unregister compute element
* @return Status indicating success or failure.
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/deregister")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public ComputeElementRestRep deregisterComputeElement(@PathParam("id") URI id) {
ArgValidator.checkFieldUriType(id, ComputeElement.class, "id");
ComputeElement ce = queryResource(id);
URIQueryResultList uris = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getHostComputeElementConstraint(ce.getId()), uris);
List<Host> hosts = _dbClient.queryObject(Host.class, uris, true);
if (!hosts.isEmpty()) {
throw APIException.badRequests.unableToDeregisterProvisionedComputeElement(ce.getLabel(), hosts.get(0).getHostName());
}
if (RegistrationStatus.REGISTERED.toString().equalsIgnoreCase(ce.getRegistrationStatus())) {
ce.setRegistrationStatus(RegistrationStatus.UNREGISTERED.toString());
_dbClient.persistObject(ce);
// Remove the element being deregistered from all CVPs it is part of.
URIQueryResultList cvpList = new URIQueryResultList();
_log.debug("Looking for CVPs this blade is in");
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getMatchedComputeElementComputeVirtualPoolConstraint(id), cvpList);
Iterator<URI> cvpListItr = cvpList.iterator();
while (cvpListItr.hasNext()) {
ComputeVirtualPool cvp = _dbClient.queryObject(ComputeVirtualPool.class, cvpListItr.next());
_log.debug("Found cvp:" + cvp.getLabel() + "containing compute element being deregistered");
StringSet currentElements = new StringSet();
if (cvp.getMatchedComputeElements() != null) {
currentElements.addAll(cvp.getMatchedComputeElements());
currentElements.remove(ce.getId().toString());
}
cvp.setMatchedComputeElements(currentElements);
_dbClient.updateAndReindexObject(cvp);
_log.debug("Removed ce from cvp");
}
// Record the compute element deregister event.
// recordComputeElementEvent(OperationTypeEnum.DEREGISTER_COMPUTE_ELEMENT,
// COMPUTE_ELEMENT_DEREGISTERED_DESCRIPTION, ce.getId());
recordAndAudit(ce, OperationTypeEnum.DEREGISTER_COMPUTE_ELEMENT, true, null);
}
return ComputeMapper.map(ce,null, null);
}
/**
* Manually register the discovered compute element with the passed id on the
* registered compute system with the passed id.
*
* @param computeElementId The id of the compute element.
*
* @brief Register compute system compute element
* @return A reference to a ComputeElementRestRep specifying the data for the
* registered compute element.
*/
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
@Path("/{id}/register")
public ComputeElementRestRep registerComputeElement(@PathParam("id") URI id) {
ArgValidator.checkFieldUriType(id, ComputeElement.class, "id");
ComputeElement ce = _dbClient.queryObject(ComputeElement.class, id);
ArgValidator.checkEntity(ce, id, isIdEmbeddedInURL(id));
if (ce == null) {
throw APIException.badRequests.computeElementNotFound(id);
}
if (ce.getComputeSystem() == null) {
throw APIException.badRequests.computeElementNotBelongingToSystem(id, null);
} else {
ComputeSystemUtils.queryRegisteredSystem(ce.getComputeSystem(), _dbClient, isIdEmbeddedInURL(ce.getComputeSystem()));
}
// if not registered, registered it. Otherwise, dont do anything
if (RegistrationStatus.UNREGISTERED.toString().equalsIgnoreCase(ce.getRegistrationStatus())) {
registerComputeElement(ce);
List<URI> cvpIds = _dbClient.queryByType(ComputeVirtualPool.class, true);
Iterator<ComputeVirtualPool> iter = _dbClient.queryIterativeObjects(ComputeVirtualPool.class, cvpIds);
while (iter.hasNext()) {
ComputeVirtualPool cvp = iter.next();
if (cvp.getUseMatchedElements()) {
_log.debug("Compute pool " + cvp.getLabel() + " configured to use dynamic matching -- refresh matched elements");
computeVirtualPoolService.getMatchingCEsforCVPAttributes(cvp);
_dbClient.updateAndReindexObject(cvp);
}
}
}
return map(ce,null,null);
}
private void registerComputeElement(ComputeElement ce) {
ce.setRegistrationStatus(RegistrationStatus.REGISTERED.toString());
_dbClient.updateAndReindexObject(ce);
recordAndAudit(ce, OperationTypeEnum.REGISTER_COMPUTE_ELEMENT, true, null);
}
/**
* Retrieves resource representations based on input ids.
*
* @param param POST data containing the id list.
* @brief List data of compute element resources
* @return list of representations.
*
* @throws DatabaseException When an error occurs querying the database.
*/
@POST
@Path("/bulk")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public ComputeElementBulkRep getBulkResources(BulkIdParam param) {
return (ComputeElementBulkRep) super.getBulkResources(param);
}
@Override
public ComputeElementBulkRep queryBulkResourceReps(List<URI> ids) {
Iterator<ComputeElement> _dbIterator =
_dbClient.queryIterativeObjects(getResourceClass(), ids);
return new ComputeElementBulkRep(BulkList.wrapping(_dbIterator, new Function<ComputeElement, ComputeElementRestRep>() {
@Override
public ComputeElementRestRep apply(ComputeElement ce) {
Host associatedHost = getAssociatedHost(ce, _dbClient);
Cluster cluster = null;
if (associatedHost!=null && !NullColumnValueGetter.isNullURI(associatedHost.getCluster())){
cluster = _dbClient.queryObject(Cluster.class, associatedHost.getCluster());
}
ComputeElementRestRep restRep = ComputeMapper.map(ce, associatedHost, cluster);
return restRep;
}
}));
}
private Host getAssociatedHost(ComputeElement ce, DbClient dbClient) {
Host associatedHost = null;
URIQueryResultList uris = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getHostComputeElementConstraint(ce.getId()), uris);
List<Host> hosts = _dbClient.queryObject(Host.class, uris, true);
// we expect to find just one host that uses this CE
if (hosts!=null && !hosts.isEmpty()){
associatedHost = hosts.get(0);
}
return associatedHost;
}
@SuppressWarnings("unchecked")
@Override
public Class<ComputeElement> getResourceClass() {
return ComputeElement.class;
}
/**
* Record ViPR Event for the completed operations
*
* @param computeElement
* @param type
* @param description
*/
private void recordComputeEvent(ComputeElement computeElement, OperationTypeEnum typeEnum, boolean status) {
RecordableBourneEvent event = new RecordableBourneEvent(
/* String */typeEnum.getEvType(status),
/* tenant id */null,
/* user id ?? */URI.create("ViPR-User"),
/* project ID */null,
/* CoS */null,
/* service */EVENT_SERVICE_TYPE,
/* resource id */computeElement.getId(),
/* description */typeEnum.getDescription(),
/* timestamp */System.currentTimeMillis(),
/* extensions */null,
/* native guid */computeElement.getNativeGuid(),
/* record type */RecordType.Event.name(),
/* Event Source */EVENT_SERVICE_SOURCE,
/* Operational Status codes */"",
/* Operational Status Descriptions */"");
try {
_evtMgr.recordEvents(event);
} catch (Exception ex) {
_log.error("Failed to record event. Event description: {}. Error: {}.",
typeEnum.getDescription(), ex);
}
}
private void recordAndAudit(ComputeElement ce, OperationTypeEnum typeEnum, boolean status, String operationalStage) {
recordComputeEvent(ce, typeEnum, status);
auditOp(typeEnum, status, operationalStage,
ce.getId().toString(), ce.getLabel(), ce.getNativeGuid(), ce.getUuid(), ce.getOriginalUuid());
}
}