/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource; import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.api.mapper.DbObjectMapper; import com.emc.storageos.api.mapper.functions.MapCustomConfig; import com.emc.storageos.api.service.impl.response.BulkList; import com.emc.storageos.api.service.impl.response.RestLinkFactory; import com.emc.storageos.api.service.impl.response.SearchedResRepList; import com.emc.storageos.customconfigcontroller.CustomConfigConstraint; import com.emc.storageos.customconfigcontroller.CustomConfigType; import com.emc.storageos.customconfigcontroller.DataSourceVariable; import com.emc.storageos.customconfigcontroller.impl.CustomConfigHandler; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.PrefixConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.CustomConfig; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.model.BulkIdParam; import com.emc.storageos.model.ResourceTypeEnum; import com.emc.storageos.model.RestLinkRep; import com.emc.storageos.model.customconfig.CustomConfigBulkRep; import com.emc.storageos.model.customconfig.CustomConfigCreateParam; import com.emc.storageos.model.customconfig.CustomConfigList; import com.emc.storageos.model.customconfig.CustomConfigPreviewParam; import com.emc.storageos.model.customconfig.CustomConfigPreviewRep; import com.emc.storageos.model.customconfig.CustomConfigRestRep; import com.emc.storageos.model.customconfig.CustomConfigRuleList; import com.emc.storageos.model.customconfig.CustomConfigTypeList; import com.emc.storageos.model.customconfig.CustomConfigTypeRep; import com.emc.storageos.model.customconfig.CustomConfigUpdateParam; import com.emc.storageos.model.customconfig.CustomConfigVariableList; import com.emc.storageos.model.customconfig.PreviewVariableParam; import com.emc.storageos.model.customconfig.RelatedConfigTypeRep; import com.emc.storageos.model.customconfig.ScopeParam; import com.emc.storageos.model.customconfig.ScopeParamList; import com.emc.storageos.model.customconfig.ConfigTypeScopeParam; import com.emc.storageos.model.customconfig.VariableParam; import com.emc.storageos.model.search.SearchResultResourceRep; import com.emc.storageos.model.search.SearchResults; import com.emc.storageos.security.authentication.RequestProcessingUtils; 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; /** * APIs to view, create, modify and remove configs */ @Path("/config/controller") @DefaultPermissions(readRoles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }, writeRoles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public class CustomConfigService extends ResourceService { private static final Logger log = LoggerFactory.getLogger(CustomConfigService.class); // Constants for Events private static final String EVENT_SERVICE_TYPE = "Config"; private static final String CONFIG_TYPE = "config_type"; private static final String SYSTEM_DEFAULT = "system_default"; private static final String SCOPE = "scope"; private static final String VALUE = "value"; private static final String SCOPE_DELIMETER = ","; private static final String NAME = "name"; @Autowired private CustomConfigHandler customConfigHandler; @Override public String getServiceType() { return EVENT_SERVICE_TYPE; } /** * List configs. * * @brief List of configs * @return A reference to a CustomConfigList. */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }) public CustomConfigList getCustomConfigs() { CustomConfigList configList = new CustomConfigList(); List<URI> ids = _dbClient.queryByType(CustomConfig.class, true); Iterator<CustomConfig> iter = _dbClient.queryIterativeObjects(CustomConfig.class, ids); while (iter.hasNext()) { configList.getCustomConfigs().add(toNamedRelatedResource(iter.next())); } return configList; } /** * Get config details * * @param id the URN of a ViPRconfig. * * @brief Show config * @return A reference to a CustomConfigRestRep */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}") @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }) public CustomConfigRestRep getCustomConfig(@PathParam("id") URI id) { ArgValidator.checkFieldUriType(id, CustomConfig.class, "id"); CustomConfig config = queryResource(id); return DbObjectMapper.map(config); } /** * Retrieve configs based on input ids. * * * @param param POST data containing the id list. * * @brief List data of configs * @return list of representations. */ @POST @Path("/bulk") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigBulkRep getBulkResources(BulkIdParam param) { List<URI> ids = param.getIds(); Iterator<CustomConfig> _dbIterator = _dbClient.queryIterativeObjects(CustomConfig.class, ids); return new CustomConfigBulkRep(BulkList.wrapping(_dbIterator, MapCustomConfig.getInstance())); } /** * @brief List all instances of config * Retrieve all ids of config * * @prereq none * * @return list of ids. */ @GET @Path("/bulk") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public BulkIdParam getBulkIds() { BulkIdParam ret = new BulkIdParam(); ret.setIds(_dbClient.queryByType(CustomConfig.class, true)); return ret; } /** * Deactivates the config. * When a config is deleted it will move to a "marked for deletion" state. * * @prereq none * @param id the URN of a ViPR config * @brief Deactivate config * @return No data returned in response body */ @POST @Path("/{id}/deactivate") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public Response deactivateCustomConfig(@PathParam("id") URI id) { CustomConfig customConfig = getCustomConfigById(id, true); ArgValidator.checkReference(CustomConfig.class, id, checkForDelete(customConfig)); if (customConfig.getSystemDefault()) { // system default could not be deleted throw APIException.badRequests.systemDefaultConfigCouldNotBeModifiedOrDeactivated(customConfig.getId()); } customConfig.setRegistered(false); _dbClient.markForDeletion(customConfig); auditOp(OperationTypeEnum.DELETE_CONFIG, true, null, id.toString(), customConfig.getLabel(), customConfig.getScope()); return Response.ok().build(); } /** * Creates config. * * @param createParam create parameters * @brief Create config * @return CustomConfigRestRep */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigRestRep createCustomConfig(CustomConfigCreateParam createParam) { String configType = createParam.getConfigType(); String theVal = createParam.getValue(); ArgValidator.checkFieldNotEmpty(configType, CONFIG_TYPE); ArgValidator.checkFieldNotEmpty(theVal, VALUE); ScopeParam scopeParam = createParam.getScope(); ArgValidator.checkFieldNotNull(scopeParam, SCOPE); StringMap scopeMap = new StringMap(); scopeMap.put(scopeParam.getType(), scopeParam.getValue()); customConfigHandler.validate(configType, scopeMap, theVal, true); String label = CustomConfigHandler.constructConfigName(configType, scopeMap); CustomConfig config = new CustomConfig(); config.setId(URIUtil.createId(CustomConfig.class)); config.setConfigType(configType); config.setScope(scopeMap); config.setLabel(label); config.setValue(theVal); config.setRegistered(createParam.getRegistered()); config.setSystemDefault(false); _dbClient.createObject(config); auditOp(OperationTypeEnum.CREATE_CONFIG, true, null, config.getId().toString(), config.getLabel(), config.getScope()); return DbObjectMapper.map(config); } /** * Modify a config. * * @param id URN of the config * @param param create parameters * @brief Modify config * @return NamedRelatedResourceRep */ @PUT @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}") @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigRestRep updateCustomConfig(@PathParam("id") URI id, CustomConfigUpdateParam param) { CustomConfig config = getCustomConfigById(id, true); if (config.getSystemDefault()) { // system default could not be modified throw APIException.badRequests.systemDefaultConfigCouldNotBeModifiedOrDeactivated(config.getId()); } customConfigHandler.validate(config.getConfigType(), config.getScope(), param.getValue(), false); if (param.getValue() != null && !param.getValue().isEmpty()) { config.setValue(param.getValue()); } _dbClient.updateAndReindexObject(config); auditOp(OperationTypeEnum.UPDATE_CONFIG, true, null, config.getId().toString(), config.getLabel(), config.getScope()); return DbObjectMapper.map(config); } /** * Register a config. * * @param id URN of the config * @brief Register config * @return NamedRelatedResourceRep */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/register") @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigRestRep registerCustomConfig(@PathParam("id") URI id) { CustomConfig config = getCustomConfigById(id, true); if (config.getRegistered()) { return DbObjectMapper.map(config); } config.setRegistered(true); _dbClient.updateAndReindexObject(config); auditOp(OperationTypeEnum.REGISTER_CONFIG, true, null, config.getId().toString(), config.getLabel(), config.getScope()); return DbObjectMapper.map(config); } /** * Deregister a config. * * @param id URN of the config * @brief Deregister config * @return NamedRelatedResourceRep */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/deregister") @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigRestRep deregisterCustomConfig(@PathParam("id") URI id) { CustomConfig config = getCustomConfigById(id, true); if (config.getSystemDefault()) { throw APIException.badRequests.systemDefaultConfigCouldNotBeModifiedOrDeactivated(config.getId()); } config.setRegistered(false); _dbClient.updateAndReindexObject(config); auditOp(OperationTypeEnum.DEREGISTER_CONFIG, true, null, config.getId().toString(), config.getLabel(), config.getScope()); return DbObjectMapper.map(config); } /** * Get a config preview value. * * @param param create parameters * @brief Get config preview value * @return preview value */ @POST @Path("/preview") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public CustomConfigPreviewRep getCustomConfigPreviewValue(CustomConfigPreviewParam param) { String configType = param.getConfigType(); String theVal = param.getValue(); ArgValidator.checkFieldNotEmpty(configType, CONFIG_TYPE); ArgValidator.checkFieldNotEmpty(theVal, VALUE); ScopeParam scopeParm = param.getScope(); StringMap scope = null; if (scopeParm != null) { scope = new StringMap(); scope.put(scopeParm.getType(), scopeParm.getValue()); } List<PreviewVariableParam> variables = param.getPreviewVariables(); Map<String, String> variableValues = null; if (variables != null && !variables.isEmpty()) { variableValues = new HashMap<String, String>(); for (PreviewVariableParam variable : variables) { variableValues.put(variable.getVariableName(), variable.getValue()); } } String result = customConfigHandler.getCustomConfigPreviewValue(configType, theVal, scope, variableValues); CustomConfigPreviewRep preview = new CustomConfigPreviewRep(result); return preview; } /** * List config types. * * @brief List of config types * @return The list of config types. */ @GET @Path("/types") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }) public CustomConfigTypeList getCustomConfigTypes() { List<CustomConfigType> items = customConfigHandler.getCustomConfigTypes(); List<RelatedConfigTypeRep> types = new ArrayList<RelatedConfigTypeRep>(); for (CustomConfigType item : items) { RelatedConfigTypeRep type = new RelatedConfigTypeRep(); // build config type Link String service = ResourceTypeEnum.CONFIG_TYPE.getService(); StringBuilder build = (new StringBuilder(service)). append('/').append(item.getName()); type.setConfigName(item.getName()); try { type.setSelfLink(new RestLinkRep("self", new URI(build.toString()))); } catch (URISyntaxException e) { // it should not happen } types.add(type); } return new CustomConfigTypeList(types); } /** * Show config type. * * @brief Show config type details * @return The config type data. */ @GET @Path("/types/{config_name}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }) public CustomConfigTypeRep getCustomConfigType(@PathParam("config_name") String configName) { CustomConfigType item = customConfigHandler.getCustomConfigType(configName); CustomConfigTypeRep result = new CustomConfigTypeRep(); if (item == null) { log.info("No config type found for :", configName); throw APIException.badRequests.invalidConfigType(configName); } result.setConfigName(configName); result.setType(item.getType()); result.setConfigType(item.getConfigType()); Map<DataSourceVariable, Boolean> dataSources = item.getDataSourceVariables(); if (dataSources != null && !dataSources.isEmpty()) { List<VariableParam> variables = new ArrayList<VariableParam>(); for (Map.Entry<DataSourceVariable, Boolean> entry : dataSources.entrySet()) { DataSourceVariable datasource = entry.getKey(); VariableParam variable = new VariableParam(); variable.setName(datasource.getDisplayName()); variable.setSampleValue(datasource.getSample()); variable.setIsRecommended(entry.getValue()); variables.add(variable); } CustomConfigVariableList variableList = new CustomConfigVariableList(variables); result.setVariables(variableList); } Map<String, String> scopes = item.getScope(); if (scopes != null && !scopes.isEmpty()) { List<ConfigTypeScopeParam> scopeParms = new ArrayList<ConfigTypeScopeParam>(); for (Map.Entry<String, String> entry : scopes.entrySet()) { String type = entry.getKey(); String value = entry.getValue(); List<String> values = new ArrayList<String>(); if (value.contains(SCOPE_DELIMETER)) { values = java.util.Arrays.asList(value.split(SCOPE_DELIMETER)); } else { values.add(value); } ConfigTypeScopeParam scopeparm = new ConfigTypeScopeParam(type, values); scopeParms.add(scopeparm); } ScopeParamList scopeList = new ScopeParamList(scopeParms); result.setScopes(scopeList); } // get rules List<CustomConfigConstraint> constraints = item.getConstraints(); if (constraints != null && !constraints.isEmpty()) { List<String> rules = new ArrayList<String>(); for (CustomConfigConstraint constraint : constraints) { rules.add(constraint.getName()); } CustomConfigRuleList ruleList = new CustomConfigRuleList(rules); result.setRules(ruleList); } return result; } /** * Search configs * <p> * Users could search configs by name, or config_name, or scope or system_default flag. e.g. /search?name=; * /search?config_name=SanZoneName; /search?config_name=SanZoneName&&scope=systemType.mds * * @brief Search configs * @return search result */ @GET @Path("/search") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public SearchResults search() { Map<String, List<String>> parameters = uriInfo.getQueryParameters(); // remove non-search related common parameters parameters.remove(RequestProcessingUtils.REQUESTING_COOKIES); SearchedResRepList resRepList = null; SearchResults result = new SearchResults(); String name = null; if (parameters.containsKey(NAME)) { name = parameters.get(NAME).get(0); ArgValidator.checkFieldNotEmpty(name, NAME); resRepList = new SearchedResRepList(getResourceType()); _dbClient.queryByConstraint( PrefixConstraint.Factory.getLabelPrefixConstraint(CustomConfig.class, name), resRepList); String systemDefault = null; if (parameters.containsKey(SYSTEM_DEFAULT)) { systemDefault = parameters.get(SYSTEM_DEFAULT).get(0); List<SearchResultResourceRep> searchResultList = new ArrayList<SearchResultResourceRep>(); Iterator<SearchResultResourceRep> it = resRepList.iterator(); while (it.hasNext()) { SearchResultResourceRep rp = it.next(); URI id = rp.getId(); CustomConfig config = queryResource(id); if (systemDefault.equals(config.getSystemDefault().toString())) { RestLinkRep selfLink = new RestLinkRep("self", RestLinkFactory.newLink(getResourceType(), id)); SearchResultResourceRep searchResult = new SearchResultResourceRep(id, selfLink, config.getLabel()); searchResultList.add(searchResult); } } result.setResource(searchResultList); } else { result.setResource(resRepList); } } else if (parameters.containsKey(CONFIG_TYPE)) { String configName = parameters.get(CONFIG_TYPE).get(0); // Validate the user passed a value for the config type. ArgValidator.checkFieldNotEmpty(configName, CONFIG_TYPE); StringMap scopeMap = null; if (parameters.containsKey(SCOPE)) { String scope = parameters.get(SCOPE).get(0); scopeMap = new StringMap(); if (scope.contains(".")) { String[] scopeSplits = scope.split("\\."); scopeMap.put(scopeSplits[0], scopeSplits[1]); } else { throw APIException.badRequests.invalidScopeFomart(scope); } } String systemDefault = null; if (parameters.containsKey(SYSTEM_DEFAULT)) { systemDefault = parameters.get(SYSTEM_DEFAULT).get(0); } List<SearchResultResourceRep> searchResultList = new ArrayList<SearchResultResourceRep>(); List<CustomConfig> configList = getCustomConfig(configName, scopeMap); for (CustomConfig config : configList) { if (config.getInactive()) { continue; } if (systemDefault != null && !systemDefault.equals(config.getSystemDefault().toString())) { continue; } RestLinkRep selfLink = new RestLinkRep("self", RestLinkFactory.newLink(getResourceType(), config.getId())); SearchResultResourceRep searchResult = new SearchResultResourceRep(config.getId(), selfLink, config.getLabel()); searchResultList.add(searchResult); } result.setResource(searchResultList); } else if (parameters.containsKey(SYSTEM_DEFAULT)) { // search parameters only contains system_default List<SearchResultResourceRep> searchResultList = new ArrayList<SearchResultResourceRep>(); String systemDefault = parameters.get(SYSTEM_DEFAULT).get(0); List<URI> ids = _dbClient.queryByType(CustomConfig.class, true); Iterator<CustomConfig> iter = _dbClient.queryIterativeObjects(CustomConfig.class, ids); while (iter.hasNext()) { CustomConfig config = iter.next(); if (systemDefault.equals(config.getSystemDefault().toString())) { RestLinkRep selfLink = new RestLinkRep("self", RestLinkFactory.newLink(getResourceType(), config.getId())); SearchResultResourceRep searchResult = new SearchResultResourceRep(config.getId(), selfLink, config.getLabel()); searchResultList.add(searchResult); } } result.setResource(searchResultList); } return result; } protected CustomConfig queryResource(URI id) { CustomConfig config = getCustomConfigById(id, false); return config; } protected ResourceTypeEnum getResourceType() { return ResourceTypeEnum.CUSTOM_CONFIG; } /** * Get CustomConfig object from id * * @param id the URN of a ViPR CustomConfig * @return */ private CustomConfig getCustomConfigById(URI id, boolean checkInactive) { if (id == null) { return null; } CustomConfig ret = _permissionsHelper.getObjectById(id, CustomConfig.class); ArgValidator.checkEntity(ret, id, isIdEmbeddedInURL(id), checkInactive); return ret; } /** * Get config instance matching config type and scope * * @param configType config type e.g. SanZoneName * @param scope * @return CustomConfig instance */ private List<CustomConfig> getCustomConfig(String configType, StringMap scope) { List<CustomConfig> configList = new ArrayList<CustomConfig>(); URIQueryResultList results = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory.getCustomConfigByConfigType(configType), results); while (results.iterator().hasNext()) { CustomConfig tmpConfig = _dbClient.queryObject(CustomConfig.class, results.iterator().next()); if (scope == null || scope.isEmpty()) { configList.add(tmpConfig); continue; } else { Map<String, String> tmpscope = tmpConfig.getScope(); if (tmpscope != null && scope != null && tmpscope.equals(scope)) { configList.add(tmpConfig); log.debug("Found the custom config {} for {}", configType, scope); break; } } } return configList; } }