package org.openstack.atlas.api.resources; import org.apache.abdera.model.Feed; import org.openstack.atlas.api.helpers.PaginationHelper; import org.openstack.atlas.api.helpers.ResponseFactory; import org.openstack.atlas.api.mapper.DomainToRestModel; import org.openstack.atlas.api.mapper.UsageMapper; import org.openstack.atlas.api.repository.ValidatorRepository; import org.openstack.atlas.api.resources.providers.CommonDependencyProvider; import org.openstack.atlas.api.validation.context.HttpRequestType; import org.openstack.atlas.api.validation.results.ValidatorResult; import org.openstack.atlas.docs.loadbalancers.api.v1.AccountBilling; import org.openstack.atlas.docs.loadbalancers.api.v1.LimitTypes; import org.openstack.atlas.docs.loadbalancers.api.v1.Limits; import org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancer; import org.openstack.atlas.service.domain.entities.AccountLimitType; import org.openstack.atlas.service.domain.entities.LimitType; import org.openstack.atlas.service.domain.exceptions.BadRequestException; import org.openstack.atlas.service.domain.operations.Operation; import org.openstack.atlas.service.domain.pojos.LbQueryStatus; import org.openstack.atlas.util.common.exceptions.ConverterException; import org.w3.atom.Link; import javax.ws.rs.*; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import java.util.*; import static javax.ws.rs.core.MediaType.*; import static org.openstack.atlas.api.atom.FeedType.PARENT_FEED; import static org.openstack.atlas.service.domain.operations.Operation.CREATE_LOADBALANCER; import static org.openstack.atlas.service.domain.util.Constants.NUM_DAYS_OF_USAGE; import static org.openstack.atlas.util.converters.DateTimeConverters.isoTocal; public class LoadBalancersResource extends CommonDependencyProvider { private LoadBalancerResource loadBalancerResource; private ProtocolsResource protocolsResource; private AlgorithmsResource algorithmsResource; private StubResource stubResource; private BounceResource bounceResource; private Integer accountId; private HttpHeaders requestHeaders; private AllowedDomainsResource allowedDomainsResource; @GET @Produces({APPLICATION_XML, APPLICATION_JSON, APPLICATION_ATOM_XML}) public Response retrieveLoadBalancers(@QueryParam("status") String status, @QueryParam("offset") Integer offset, @QueryParam("limit") Integer limit, @QueryParam("marker") Integer marker, @QueryParam("page") Integer page, @QueryParam("changes-since") String changedSince, @QueryParam("nodeaddress") String nodeAddress) { if (requestHeaders.getRequestHeader("Accept").get(0).equals(APPLICATION_ATOM_XML)) { return getFeedResponse(page); } List<org.openstack.atlas.service.domain.entities.LoadBalancer> domainLbs; org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancers dataModelLbs = new org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancers(); Calendar changedCal = null; LbQueryStatus qs = null; try { if (nodeAddress != null) { domainLbs = loadBalancerService.getLoadBalancersWithNode(nodeAddress, accountId); for (org.openstack.atlas.service.domain.entities.LoadBalancer domainLb : domainLbs) { dataModelLbs.getLoadBalancers().add(dozerMapper.map(domainLb, org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancer.class, "LB_NAME_ID_STATUS")); } } else { if (status != null) { qs = LbQueryStatus.INCLUDE; } else { qs = LbQueryStatus.EXCLUDE; status = "DELETED"; } if (changedSince != null) { changedCal = isoTocal(changedSince); } if (limit == null || limit < 0 || limit > 100) { limit = 100; } domainLbs = loadBalancerService.getLoadbalancersGeneric(accountId, status, qs, changedCal, offset, limit, marker); for (org.openstack.atlas.service.domain.entities.LoadBalancer domainLb : domainLbs) { dataModelLbs.getLoadBalancers().add(dozerMapper.map(domainLb, org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancer.class, "SIMPLE_LB")); } } return Response.status(200).entity(dataModelLbs).build(); } catch (Exception e) { return ResponseFactory.getErrorResponse(e, null, null); } } @POST @Consumes({APPLICATION_XML, APPLICATION_JSON}) public Response createLoadBalancer(LoadBalancer loadBalancer) { ValidatorResult result = ValidatorRepository.getValidatorFor(LoadBalancer.class).validate(loadBalancer, HttpRequestType.POST); if (!result.passedValidation()) { return getValidationFaultResponse(result); } try { List<String> errors = verifyNodeDomains(loadBalancer.getNodes()); if (errors.size() > 0) { return getValidationFaultResponse(errors); } org.openstack.atlas.service.domain.entities.LoadBalancer domainLb = dozerMapper.map(loadBalancer, org.openstack.atlas.service.domain.entities.LoadBalancer.class); domainLb.setAccountId(accountId); if (requestHeaders != null) { domainLb.setUserName(requestHeaders.getRequestHeader("X-PP-User").get(0)); } virtualIpService.addAccountRecord(accountId); org.openstack.atlas.service.domain.entities.LoadBalancer returnLb = loadBalancerService.create(domainLb); asyncService.callAsyncLoadBalancingOperation(CREATE_LOADBALANCER, returnLb); return Response.status(Response.Status.ACCEPTED).entity(dozerMapper.map(returnLb, LoadBalancer.class)).build(); } catch (Exception e) { return ResponseFactory.getErrorResponse(e, null, null); } } @GET @Path("usage") public Response retrieveAccountBilling(@QueryParam("startTime") String startTimeParam, @QueryParam("endTime") String endTimeParam) { org.openstack.atlas.service.domain.pojos.AccountBilling daccountBilling; AccountBilling raccountBilling; Calendar startTime; Calendar endTime; final String badRequestMessage = "Date parameters must follow ISO-8601 format"; if (endTimeParam == null) { endTime = Calendar.getInstance(); // Default to right now } else { try { endTime = isoTocal(endTimeParam); } catch (ConverterException ex) { return ResponseFactory.getResponseWithStatus(Response.Status.BAD_REQUEST, badRequestMessage); } } if (startTimeParam == null) { startTime = (Calendar) endTime.clone(); startTime.add(Calendar.DAY_OF_MONTH, -NUM_DAYS_OF_USAGE); // default to NUM_DAYS_OF_USAGE days ago } else { try { startTime = isoTocal(startTimeParam); } catch (ConverterException ex) { return ResponseFactory.getResponseWithStatus(Response.Status.BAD_REQUEST, badRequestMessage); } } try { daccountBilling = loadBalancerService.getAccountBilling(accountId, startTime, endTime); raccountBilling = UsageMapper.toDataModelAccountBilling(daccountBilling); return Response.status(200).entity(raccountBilling).build(); } catch (Exception ex) { return ResponseFactory.getErrorResponse(ex, null, null); } } @GET @Path("billable") public Response retrieveBillableLoadBalancers(@QueryParam("startTime") String startTimeParam, @QueryParam("endTime") String endTimeParam, @QueryParam("offset") Integer offset, @QueryParam("limit") Integer limit) { Calendar startTime; Calendar endTime; List<org.openstack.atlas.service.domain.entities.LoadBalancer> domainLbs; org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancers dataModelLbs = new org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancers(); if (startTimeParam == null || endTimeParam == null) { final String badRequestMessage = "'startTime' and 'endTime' query parameters are required"; return ResponseFactory.getResponseWithStatus(Response.Status.BAD_REQUEST, badRequestMessage); } else { try { startTime = isoTocal(startTimeParam); endTime = isoTocal(endTimeParam); } catch (ConverterException ex) { final String badRequestMessage = "Date parameters must follow ISO-8601 format"; return ResponseFactory.getResponseWithStatus(Response.Status.BAD_REQUEST, badRequestMessage); } } try { limit = PaginationHelper.determinePageLimit(limit); offset = PaginationHelper.determinePageOffset(offset); domainLbs = loadBalancerService.getLoadBalancersWithUsage(accountId, startTime, endTime, offset, limit); for (org.openstack.atlas.service.domain.entities.LoadBalancer domainLb : domainLbs) { dataModelLbs.getLoadBalancers().add(dozerMapper.map(domainLb, org.openstack.atlas.docs.loadbalancers.api.v1.LoadBalancer.class, "SIMPLE_LB")); } if (dataModelLbs.getLoadBalancers().size() > limit) { String relativeUri = String.format("/%d/loadbalancers/billable?startTime=%s&endTime=%s&offset=%d&limit=%d", accountId, startTimeParam, endTimeParam, PaginationHelper.calculateNextOffset(offset, limit), limit); Link nextLink = PaginationHelper.createLink(PaginationHelper.NEXT, relativeUri); dataModelLbs.getLinks().add(nextLink); dataModelLbs.getLoadBalancers().remove(limit.intValue()); // Remove limit+1 item } if (offset > 0) { String relativeUri = String.format("/%d/loadbalancers/billable?startTime=%s&endTime=%s&offset=%d&limit=%d", accountId, startTimeParam, endTimeParam, PaginationHelper.calculatePreviousOffset(offset, limit), limit); Link nextLink = PaginationHelper.createLink(PaginationHelper.PREVIOUS, relativeUri); dataModelLbs.getLinks().add(nextLink); } return Response.status(200).entity(dataModelLbs).build(); } catch (Exception ex) { return ResponseFactory.getErrorResponse(ex, null, null); } } @DELETE @Produces({APPLICATION_XML, APPLICATION_JSON, APPLICATION_ATOM_XML}) public Response deleteLoadbalancer(@QueryParam("id") Set<Integer> loadBalancerIds) { try { if (loadBalancerIds.isEmpty()) { BadRequestException badRequestException = new BadRequestException("Must supply one or more id's to process this request."); return ResponseFactory.getErrorResponse(badRequestException, null, null); } Integer limit = accountLimitService.getLimit(accountId, AccountLimitType.BATCH_DELETE_LIMIT); if (loadBalancerIds.size() > limit) { BadRequestException badRequestException = new BadRequestException(String.format("Currently, the limit of accepted parameters is: %s :please supply a valid parameter list.", limit)); return ResponseFactory.getErrorResponse(badRequestException, null, null); } List<org.openstack.atlas.service.domain.entities.LoadBalancer> verifiedLbs = loadBalancerService.prepareForDelete(accountId, new ArrayList<Integer>(loadBalancerIds)); for (org.openstack.atlas.service.domain.entities.LoadBalancer dblb : verifiedLbs) { if (requestHeaders != null) { if (requestHeaders.getRequestHeader("X-PP-User") != null && requestHeaders.getRequestHeader("X-PP-User").size() > 0) { dblb.setUserName(requestHeaders.getRequestHeader("X-PP-User").get(0)); } } //TODO: Use this till we update other methods to use the dataContainer asyncService.callAsyncLoadBalancingOperation(Operation.DELETE_LOADBALANCER, dblb); } return Response.status(202).build(); } catch (Exception e) { return ResponseFactory.getErrorResponse(e, null, null); } } @Path("{id: [-+]?[0-9][0-9]*}") public LoadBalancerResource retrieveLoadBalancerResource(@PathParam("id") int id) { loadBalancerResource.setId(id); loadBalancerResource.setAccountId(accountId); loadBalancerResource.setRequestHeaders(requestHeaders); return loadBalancerResource; } @Path("protocols") public ProtocolsResource retrieveProtocolsResource() { return protocolsResource; } @Path("algorithms") public AlgorithmsResource retrieveAlgorithmsResource() { return algorithmsResource; } @Path("stub") public StubResource retrieveStubResource() { return stubResource; } @Path("bounce") public BounceResource retrieveBounceResource() { return bounceResource; } @GET @Path("limittypes") public Response getAllLimitTypes() { List<LimitType> allLimites = accountLimitService.getAllLimitTypes(); LimitTypes rLimitTypes = DomainToRestModel.LimitTypeList2LimitType(allLimites); return Response.status(200).entity(rLimitTypes).build(); } @GET @Path("absolutelimits") public Response getAllLimitsForAccount() { Map<String, Integer> accountLimits = accountLimitService.getAllLimitsForAccount(accountId); Limits rLimits = DomainToRestModel.AccountLimitMap2Limits(accountLimits); return Response.status(200).entity(rLimits).build(); } @Path("alloweddomains") public AllowedDomainsResource retrieveAllowedDomainsResource() { return allowedDomainsResource; } private Response getFeedResponse(Integer page) { Map<String, Object> feedAttributes = new HashMap<String, Object>(); feedAttributes.put("feedType", PARENT_FEED); feedAttributes.put("accountId", accountId); feedAttributes.put("page", page); Feed feed = atomFeedAdapter.getFeed(feedAttributes); if (feed.getEntries().isEmpty()) { try { lbRepository.getByAccountId(accountId, null); } catch (Exception e) { return ResponseFactory.getErrorResponse(e, null, null); } } return Response.status(200).entity(feed).build(); } public void setProtocolsResource(ProtocolsResource protocolsResource) { this.protocolsResource = protocolsResource; } public void setAlgorithmsResource(AlgorithmsResource algorithmsResource) { this.algorithmsResource = algorithmsResource; } public void setLoadBalancerResource(LoadBalancerResource loadBalancerResource) { this.loadBalancerResource = loadBalancerResource; } public void setStubResource(StubResource stubResource) { this.stubResource = stubResource; } public void setBounceResource(BounceResource bounceResource) { this.bounceResource = bounceResource; } public void setAccountId(Integer accountId) { this.accountId = accountId; } public void setRequestHeaders(HttpHeaders requestHeaders) { this.requestHeaders = requestHeaders; } public void setAllowedDomainsResource(AllowedDomainsResource allowedDomainsResource) { this.allowedDomainsResource = allowedDomainsResource; } }