/*- * ============LICENSE_START======================================================= * dcae-inventory * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * 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. * ============LICENSE_END========================================================= */ package io.swagger.api.impl; import org.onap.dcae.inventory.clients.DCAEControllerClient; import org.onap.dcae.inventory.clients.DatabusControllerClient; import org.onap.dcae.inventory.daos.DCAEServiceComponentsDAO; import org.onap.dcae.inventory.daos.DCAEServiceTransactionDAO; import org.onap.dcae.inventory.daos.DCAEServicesDAO; import org.onap.dcae.inventory.daos.InventoryDAOManager; import org.onap.dcae.inventory.dbthings.mappers.DCAEServiceObjectMapper; import org.onap.dcae.inventory.dbthings.models.DCAEServiceComponentObject; import org.onap.dcae.inventory.dbthings.models.DCAEServiceObject; import org.onap.dcae.inventory.exceptions.DCAEControllerClientException; import org.onap.dcae.inventory.exceptions.DatabusControllerClientException; import io.swagger.api.*; import io.swagger.model.*; import io.swagger.api.NotFoundException; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.skife.jdbi.v2.Handle; import org.skife.jdbi.v2.Query; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.*; import javax.ws.rs.core.Link; import java.util.*; @javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2016-04-18T20:16:22.119Z") public class DcaeServicesApiServiceImpl extends DcaeServicesApiService { private static final Logger LOG = LoggerFactory.getLogger(DcaeServicesApiServiceImpl.class); private static final int PAGINATION_PAGE_SIZE = 25; private static final String COMPONENT_SOURCE_DCAE_CONTROLLER = "DCAECONTROLLER"; private static final String COMPONENT_SOURCE_DATA_BUS_CONTROLLER = "DMAAPCONTROLLER"; private final DCAEControllerClient dcaeControllerClient; private final DatabusControllerClient databusControllerClient; public DcaeServicesApiServiceImpl(DCAEControllerClient dcaeControllerClient, DatabusControllerClient databusControllerClient) { this.dcaeControllerClient = dcaeControllerClient; this.databusControllerClient = databusControllerClient; } private DCAEService createDCAEService(DCAEServiceObject serviceObject, Collection componentObjects, UriInfo uriInfo) { // TODO: Complete links: come back and do links for vnf DCAEService service = new DCAEService(); service.setServiceId(serviceObject.getServiceId()); service.setSelfLink(DcaeServicesApi.buildLinkForGet(uriInfo, "self", serviceObject.getServiceId())); service.setTypeLink(DcaeServiceTypesApi.buildLinkForGet(uriInfo, "type", serviceObject.getTypeId())); service.setCreated(serviceObject.getCreated().toDate()); service.setModified(serviceObject.getModified().toDate()); service.setVnfId(serviceObject.getVnfId()); service.setVnfType(serviceObject.getVnfType()); service.setVnfLocation(serviceObject.getVnfLocation()); service.setDeploymentRef(serviceObject.getDeploymentRef()); List serviceComponents = new ArrayList<>(); for (DCAEServiceComponentObject sco : componentObjects) { DCAEServiceComponent component = new DCAEServiceComponent(); component.setComponentId(sco.getComponentId()); component.setComponentType(sco.getComponentType()); component.setComponentSource(sco.getComponentSource()); component.setShareable(sco.getShareable()); component.setCreated(sco.getCreated().toDate()); component.setModified(sco.getModified().toDate()); // TODO: When putting together the components fail. Should this be a 500 case? // For now, this is just logged as a warning. if (COMPONENT_SOURCE_DCAE_CONTROLLER.equalsIgnoreCase(sco.getComponentSource())) { if (this.dcaeControllerClient != null) { try { DCAEControllerClient.ServiceInstance serviceInstance = this.dcaeControllerClient.getServiceInstance(component.getComponentId()); component.setStatus(serviceInstance.getStatus()); // There's no specific location rather its inferred from the AIC tenant component.setLocation(this.dcaeControllerClient.getLocation(serviceInstance)); Link componentLink = Link.fromUri(this.dcaeControllerClient.constructResourceURI(sco.getComponentId())) .rel("component").title(component.getComponentId()).build(); component.setComponentLink(componentLink); } catch (DCAEControllerClientException e) { LOG.warn(String.format("%s, %s", e.getMessage(), sco.toString()), e); } } } else if (COMPONENT_SOURCE_DATA_BUS_CONTROLLER.equalsIgnoreCase(sco.getComponentSource())) { if (this.databusControllerClient != null) { try { if (this.databusControllerClient.isExists(sco.getComponentId())) { Link componentLink = Link.fromUri(this.databusControllerClient.constructResourceURI(sco.getComponentId())) .rel("component").title(component.getComponentId()).build(); component.setComponentLink(componentLink); } else { LOG.warn(String.format("Feed/topic does not exist: %s", sco.getComponentId())); } } catch (DatabusControllerClientException e) { LOG.warn(String.format("%s, %s", e.getMessage(), sco.toString()), e); } } } else { LOG.warn(String.format("Handling unknown component source: %s", sco.getComponentSource())); } serviceComponents.add(component); } service.components(serviceComponents); return service; } @Override public Response dcaeServicesGet(String typeId, String vnfId, String vnfType, String vnfLocation, String componentType, Boolean shareable, DateTime created, Integer offset, UriInfo uriInfo, SecurityContext securityContext) { List serviceObjects = new ArrayList<>(); DateTime createdCutoff = created == null ? DateTime.now(DateTimeZone.UTC) : created; LOG.info(String.format("Create time upper bound cutoff: %s", createdCutoff.toString())); // Offset is zero-based index offset = (offset == null) ? 0 : offset; LOG.info(String.format("Query offset: %d", offset)); try (Handle jdbiHandle = InventoryDAOManager.getInstance().getHandle()) { // WATCH! There is the use of "distinct" here. StringBuilder sb = new StringBuilder("select distinct ds.* from dcae_services ds"); sb.append(" join dcae_services_components_maps m on ds.service_id = m.service_id "); sb.append(" join dcae_service_components dsc on m.component_id = dsc.component_id"); List whereClauses = new ArrayList<>(); if (typeId != null) { whereClauses.add("ds.type_id = :typeId"); } if (vnfId != null) { whereClauses.add("ds.vnf_id = :vnfId"); } if (vnfType != null) { whereClauses.add("lower(ds.vnf_type) = lower(:vnfType)"); } if (vnfLocation != null) { whereClauses.add("ds.vnf_location = :vnfLocation"); } if (componentType != null) { whereClauses.add("dsc.component_type = :componentType"); } if (shareable != null) { whereClauses.add("dsc.shareable = :shareable"); } whereClauses.add("ds.created < :createdCutoff"); whereClauses.add("ds.status = :serviceStatus"); if (!whereClauses.isEmpty()) { sb.append(" where "); sb.append(String.join(" and ", whereClauses)); } // Sort by created timestamp - always descending. sb.append(" order by created desc"); Query query = jdbiHandle.createQuery(sb.toString()).map(new DCAEServiceObjectMapper()); if (typeId != null) { query.bind("typeId", typeId); } if (vnfId != null) { query.bind("vnfId", vnfId); } if (vnfType != null) { query.bind("vnfType", vnfType); } if (vnfLocation != null) { query.bind("vnfLocation", vnfLocation); } if (componentType != null) { query.bind("componentType", componentType); } if (shareable != null) { // NOTE: That the shareable field in the database is actually an integer. query.bind("shareable", shareable ? 1 : 0); } query.bind("createdCutoff", createdCutoff); // NOTE: This is hardcoded because service status is only used internally. query.bind("serviceStatus", DCAEServiceObject.DCAEServiceStatus.RUNNING); serviceObjects = query.list(); } // NOTE: Chose to do the pagination via in code here rather than in SQL using LIMIT and OFFSET constraints // because of the need for the global total number of result items. SQL approach would require two queries. // Going forward, I think the better approach is using SQL because the resultsets may become very large. // For now I think this approach is OK and actually we do less SQL querying. Integer endpoint = Math.min(offset + PAGINATION_PAGE_SIZE, serviceObjects.size()); List serviceObjectsSliced = serviceObjects.subList(offset, endpoint); DCAEServiceComponentsDAO componentsDAO = InventoryDAOManager.getInstance().getDCAEServiceComponentsDAO(); List services = new ArrayList<>(); for (DCAEServiceObject so : serviceObjectsSliced) { List components = componentsDAO.getByServiceId(so.getServiceId()); services.add(createDCAEService(so, components, uriInfo)); } InlineResponse2001 response = new InlineResponse2001(); response.setItems(services); response.setTotalCount(serviceObjects.size()); // TODO: Show the total count of items in this response i.e. local count? InlineResponse200Links navigationLinks = new InlineResponse200Links(); Integer offsetPrev = offset - PAGINATION_PAGE_SIZE; if (offsetPrev >= 0) { navigationLinks.setPreviousLink(DcaeServicesApi.buildLinkForGet(uriInfo, "prev", typeId, vnfId, vnfType, vnfLocation, componentType, shareable, created, offsetPrev)); } Integer offsetNext = offset + PAGINATION_PAGE_SIZE; if (offsetNext < serviceObjects.size()) { navigationLinks.setNextLink(DcaeServicesApi.buildLinkForGet(uriInfo, "next", typeId, vnfId, vnfType, vnfLocation, componentType, shareable, created, offsetNext)); } response.setLinks(navigationLinks); return Response.ok().entity(response).build(); } @Override public Response dcaeServicesServiceIdGet(String serviceId, UriInfo uriInfo, SecurityContext securityContext) throws NotFoundException { DCAEServicesDAO servicesDAO = InventoryDAOManager.getInstance().getDCAEServicesDAO(); DCAEServiceComponentsDAO componentsDAO = InventoryDAOManager.getInstance().getDCAEServiceComponentsDAO(); DCAEServiceObject serviceObject = servicesDAO.getByServiceId(DCAEServiceObject.DCAEServiceStatus.RUNNING, serviceId); if (serviceObject == null) { throw new NotFoundException(1, String.format("DCAEService not found: %s", serviceId)); } List componentObjects = componentsDAO.getByServiceId(serviceId); DCAEService service = createDCAEService(serviceObject, componentObjects, uriInfo); return Response.ok().entity(service).build(); } @Override public Response dcaeServicesServiceIdPut(String serviceId, DCAEServiceRequest request, UriInfo uriInfo, SecurityContext securityContext) { // Check to make sure that the DCAE service type exists if (InventoryDAOManager.getInstance().getDCAEServiceTypesDAO().getByTypeIdActiveOnly(request.getTypeId()) == null) { String errorMessage = String.format("DCAE service type does not exist: %s", request.getTypeId()); ApiResponseMessage message = new ApiResponseMessage(ApiResponseMessage.ERROR, errorMessage); return Response.status(422).entity(message).build(); } // TODO: Check DCAE service components against source services i.e. DCAE controller and data bus controller // Possibly refuse to process if that check fails. DCAEServicesDAO servicesDAO = InventoryDAOManager.getInstance().getDCAEServicesDAO(); DCAEServiceComponentsDAO componentsDAO = InventoryDAOManager.getInstance().getDCAEServiceComponentsDAO(); // NOTE: 1607 is using Postgres v9.3 which does NOT have the upgrade to the INSERT operation that allows for UPSERTs // Challenge here is make this entire PUT operation atomic. // TODO: 1607 we are actually using v9.5 which has the UPSERT. Migrate this code to use the UPSERT. // Watch! We have to query for services regardless of status because we need to account for "removed" instances // that get resurrected. final DCAEServiceObject serviceObjectFromStore = servicesDAO.getByServiceId(serviceId); final Map componentObjectsFromStore = new HashMap<>(); for (DCAEServiceComponentObject componentObject : componentsDAO.getByServiceId(serviceId)) { componentObjectsFromStore.put(componentObject.getComponentId(), componentObject); } DateTime modified = DateTime.now(DateTimeZone.UTC); DCAEServiceTransactionDAO.DCAEServiceTransactionContext transactionContext = new DCAEServiceTransactionDAO.DCAEServiceTransactionContext(serviceId, modified); // 1) Insert/update for DCAEServiceObject DCAEServiceObject serviceObjectToSendBack = serviceObjectFromStore; if (serviceObjectFromStore == null) { serviceObjectToSendBack = new DCAEServiceObject(serviceId, request); serviceObjectToSendBack.setModified(modified); transactionContext.setServiceObjectToInsert(serviceObjectToSendBack); } else { LOG.info(String.format("DCAEServiceObject already exists - updating: %s, %s", serviceObjectFromStore.getCreated().toString(), serviceObjectFromStore.getModified().toString())); serviceObjectToSendBack = new DCAEServiceObject(serviceObjectFromStore, request); serviceObjectToSendBack.setModified(modified); transactionContext.setServiceObjectToUpdate(serviceObjectToSendBack); } // 2) Insert/update DCAEServiceComponentObjects. Components exist independent of the associated DCAE service. Map componentObjectsToSendBack = new HashMap<>(); for (DCAEServiceComponentRequest requestComponent : request.getComponents()) { // Have to query the database rather than checking the result of getting by service id because of the // independence of components and services. A component may already exist even though from a service // perspective it is seen as "new". final DCAEServiceComponentObject coExisting = componentsDAO.getByComponentId(requestComponent.getComponentId()); DCAEServiceComponentObject coToSendBack = null; if (coExisting == null) { // Add new component coToSendBack = new DCAEServiceComponentObject(requestComponent); coToSendBack.setModified(modified); transactionContext.addComponentObjectToInsert(coToSendBack); } else { // TODO: Check if the mutable fields have changed before doing the update. // Update existing component coToSendBack = new DCAEServiceComponentObject(coExisting, requestComponent); coToSendBack.setModified(modified); transactionContext.addComponentObjectToUpdate(coToSendBack); } if (coToSendBack != null) { componentObjectsToSendBack.put(coToSendBack.getComponentId(), coToSendBack); } } // 3) Update relationships: add ones that don't exist, delete ones that do exist but no longer should not // Add relationships that didn't exist before for (String componentId : componentObjectsToSendBack.keySet()) { if (!componentObjectsFromStore.containsKey(componentId)) { transactionContext.addMappingsToInsert(componentId); } } // Remove relationships that have been removed for (String componentId : componentObjectsFromStore.keySet()) { if (!componentObjectsToSendBack.containsKey(componentId)) { transactionContext.addMappingsToDelete(componentId); } } DCAEServiceTransactionDAO transactionDAO = InventoryDAOManager.getInstance().getDCAEServiceTransactionDAO(); transactionDAO.insert(transactionContext); DCAEService service = createDCAEService(serviceObjectToSendBack, componentObjectsToSendBack.values(), uriInfo); return Response.ok().entity(service).build(); } @Override public Response dcaeServicesServiceIdDelete(String serviceId, SecurityContext securityContext) throws NotFoundException { DCAEServicesDAO servicesDAO = InventoryDAOManager.getInstance().getDCAEServicesDAO(); if (servicesDAO.getByServiceId(DCAEServiceObject.DCAEServiceStatus.RUNNING, serviceId) == null) { throw new NotFoundException(ApiResponseMessage.ERROR, String.format("DCAE service not found: %s", serviceId)); } servicesDAO.updateStatusByServiceId(DateTime.now(DateTimeZone.UTC), DCAEServiceObject.DCAEServiceStatus.REMOVED, serviceId); return Response.ok().build(); } }