com.vmware.photon.controller.apife.backends.ProjectSqlBackend.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.photon.controller.apife.backends.ProjectSqlBackend.java

Source

/*
 * Copyright 2015 VMware, Inc. 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.
 */

package com.vmware.photon.controller.apife.backends;

import com.vmware.photon.controller.api.Operation;
import com.vmware.photon.controller.api.Project;
import com.vmware.photon.controller.api.ProjectCreateSpec;
import com.vmware.photon.controller.api.ProjectTicket;
import com.vmware.photon.controller.api.QuotaLineItem;
import com.vmware.photon.controller.api.ResourceTicketReservation;
import com.vmware.photon.controller.api.SecurityGroup;
import com.vmware.photon.controller.api.common.db.Transactional;
import com.vmware.photon.controller.api.common.entities.base.TagEntity;
import com.vmware.photon.controller.api.common.exceptions.external.ExternalException;
import com.vmware.photon.controller.apife.db.dao.ProjectDao;
import com.vmware.photon.controller.apife.entities.ProjectEntity;
import com.vmware.photon.controller.apife.entities.QuotaLineItemEntity;
import com.vmware.photon.controller.apife.entities.ResourceTicketEntity;
import com.vmware.photon.controller.apife.entities.SecurityGroupEntity;
import com.vmware.photon.controller.apife.entities.StepEntity;
import com.vmware.photon.controller.apife.entities.TaskEntity;
import com.vmware.photon.controller.apife.entities.TenantEntity;
import com.vmware.photon.controller.apife.exceptions.external.ContainerNotEmptyException;
import com.vmware.photon.controller.apife.exceptions.external.NameTakenException;
import com.vmware.photon.controller.apife.exceptions.external.ProjectNotFoundException;
import com.vmware.photon.controller.apife.exceptions.external.ResourceTicketNameNotFoundException;
import com.vmware.photon.controller.apife.exceptions.external.ResourceTicketNotFoundException;
import com.vmware.photon.controller.apife.exceptions.external.SecurityGroupsAlreadyInheritedException;
import com.vmware.photon.controller.apife.utils.SecurityGroupUtils;

import com.google.common.base.Optional;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * ProjectSqlBackend is performing project operations (create etc.) as instructed by API calls.
 */
@Singleton
public class ProjectSqlBackend implements ProjectBackend {

    private static final Logger logger = LoggerFactory.getLogger(ProjectSqlBackend.class);

    private final ProjectDao projectDao;
    private final TenantBackend tenantBackend;
    private final TombstoneBackend tombstoneBackend;
    private final TaskBackend taskBackend;
    private final VmBackend vmBackend;
    private final DiskBackend diskBackend;
    private final ResourceTicketBackend resourceTicketBackend;

    @Inject
    public ProjectSqlBackend(ProjectDao projectDao, TenantBackend tenantBackend, TombstoneBackend tombstoneBackend,
            TaskBackend taskBackend, VmBackend vmBackend, DiskBackend diskBackend,
            ResourceTicketBackend resourceTicketBackend) {
        this.projectDao = projectDao;
        this.tenantBackend = tenantBackend;
        this.tombstoneBackend = tombstoneBackend;
        this.taskBackend = taskBackend;
        this.vmBackend = vmBackend;
        this.diskBackend = diskBackend;
        this.resourceTicketBackend = resourceTicketBackend;
    }

    @Transactional
    public List<Project> filter(String tenantId, Optional<String> name) throws ExternalException {
        TenantEntity tenant = tenantBackend.findById(tenantId);
        List<ProjectEntity> projects = new ArrayList<>();

        if (name.isPresent()) {
            Optional<ProjectEntity> project = projectDao.findByName(name.get(), tenantId);

            if (project.isPresent()) {
                projects.add(project.get());
            }
        } else {
            projects = projectDao.findAll(tenantId);
        }

        List<Project> result = new ArrayList<>();

        for (ProjectEntity projectEntity : projects) {
            Project project = toApiListRepresentation(projectEntity);
            result.add(project);
        }

        return result;
    }

    @Transactional
    public Project getApiRepresentation(String id) throws ExternalException {
        ProjectEntity projectEntity = findById(id);
        return toApiRepresentation(projectEntity);
    }

    @Transactional
    public TaskEntity createProject(String tenantId, ProjectCreateSpec project) throws ExternalException {
        ProjectEntity projectEntity = create(tenantId, project);
        return taskBackend.createCompletedTask(projectEntity, Operation.CREATE_PROJECT);
    }

    @Transactional
    public TaskEntity deleteProject(String projectId) throws ExternalException {
        ProjectEntity projectEntity = findById(projectId);
        delete(projectEntity.getId());
        return taskBackend.createCompletedTask(projectEntity, Operation.DELETE_PROJECT);
    }

    @Transactional
    public ProjectEntity findById(String id) throws ProjectNotFoundException {
        Optional<ProjectEntity> project = projectDao.findById(id);

        if (project.isPresent()) {
            return project.get();
        }

        throw new ProjectNotFoundException(id);
    }

    @Override
    @Transactional
    public TaskEntity setSecurityGroups(String projectId, List<String> securityGroups) throws ExternalException {

        logger.info("Updating the security groups of project {} to {}", projectId, securityGroups.toString());

        ProjectEntity projectEntity = findById(projectId);

        List<SecurityGroup> currSecurityGroups = SecurityGroupUtils
                .toApiRepresentation(projectEntity.getSecurityGroups());
        Pair<List<SecurityGroup>, List<String>> result = SecurityGroupUtils
                .mergeSelfSecurityGroups(currSecurityGroups, securityGroups);

        replaceSecurityGroups(projectId, result.getLeft());

        TaskEntity taskEntity = taskBackend.createCompletedTask(projectEntity,
                Operation.SET_PROJECT_SECURITY_GROUPS);
        StepEntity stepEntity = taskBackend.getStepBackend().createCompletedStep(taskEntity, projectEntity,
                Operation.SET_PROJECT_SECURITY_GROUPS);

        if (!result.getRight().isEmpty()) {
            stepEntity.addWarning(new SecurityGroupsAlreadyInheritedException(result.getRight()));
        }

        return taskEntity;
    }

    @Transactional
    @Override
    public void replaceSecurityGroups(String id, List<SecurityGroup> securityGroups) throws ExternalException {

        logger.info("Setting the security groups of project {} to {}", id, securityGroups.toString());

        ProjectEntity entity = findById(id);
        entity.setSecurityGroups(SecurityGroupUtils.fromApiRepresentation(securityGroups));

        projectDao.update(entity);
    }

    private ProjectTicket getProjectTicket(String resourceTicketId) throws ResourceTicketNotFoundException {
        ResourceTicketEntity parent = resourceTicketBackend.findById(resourceTicketId);
        ProjectTicket ticket = new ProjectTicket();
        // null for tenant resource tickets
        if (parent != null) {
            ticket.setTenantTicketId(parent.getId());
            ticket.setTenantTicketName(parent.getName());
        }

        List<QuotaLineItem> limits = new ArrayList<>();
        List<QuotaLineItem> usage = new ArrayList<>();

        for (QuotaLineItemEntity qli : parent.getLimits()) {
            limits.add(new QuotaLineItem(qli.getKey(), qli.getValue(), qli.getUnit()));
        }

        for (QuotaLineItemEntity qli : parent.getUsage()) {
            usage.add(new QuotaLineItem(qli.getKey(), qli.getValue(), qli.getUnit()));
        }

        ticket.setLimits(limits);
        ticket.setUsage(usage);

        return ticket;
    }

    /**
     * This method requires the session and transaction to be setup before being invoked.
     *
     * @param projectEntity
     * @return Project
     */
    private Project toApiRepresentation(ProjectEntity projectEntity) throws ExternalException {
        Project project = new Project();

        project.setId(projectEntity.getId());
        project.setName(projectEntity.getName());
        project.setTenantId(projectEntity.getTenantId());

        Set<String> tags = new HashSet<>();

        for (TagEntity tag : projectEntity.getTags()) {
            tags.add(tag.getValue());
        }

        project.setTags(tags);

        ProjectTicket ticket = getProjectTicket(projectEntity.getResourceTicketId());
        project.setResourceTicket(ticket);
        project.setSecurityGroups(SecurityGroupUtils.toApiRepresentation(projectEntity.getSecurityGroups()));

        return project;
    }

    /**
     * This method requires the session and transaction to be setup before being invoked.
     *
     * @param projectEntity
     * @return
     */
    private Project toApiListRepresentation(ProjectEntity projectEntity) throws ResourceTicketNotFoundException {
        Project project = new Project();

        project.setId(projectEntity.getId());
        project.setName(projectEntity.getName());

        Set<String> tags = new HashSet<>();

        for (TagEntity tag : projectEntity.getTags()) {
            tags.add(tag.getValue());
        }

        project.setTags(tags);

        ProjectTicket ticket = getProjectTicket(projectEntity.getResourceTicketId());
        project.setResourceTicket(ticket);
        project.setSecurityGroups(SecurityGroupUtils.toApiRepresentation(projectEntity.getSecurityGroups()));

        return project;
    }

    private ResourceTicketEntity createProjectTicket(ResourceTicketEntity tenantTicket,
            ResourceTicketReservation reservation) throws ExternalException {

        Double subdividePercentage = reservation.getSubdividePercentage();

        if (subdividePercentage != null) {
            return resourceTicketBackend.subdivide(tenantTicket.getId(), subdividePercentage);
        }

        List<QuotaLineItemEntity> limits = new ArrayList<>();

        for (QuotaLineItem qli : reservation.getLimits()) {
            limits.add(new QuotaLineItemEntity(qli.getKey(), qli.getValue(), qli.getUnit()));
        }

        return resourceTicketBackend.subdivide(tenantTicket.getId(), limits);
    }

    private ProjectEntity create(String tenantId, ProjectCreateSpec projectCreateSpec) throws ExternalException {
        // TODO(olegs): lock tenant ticket before consuming quota
        // TODO(pankaj): https://www.pivotaltracker.com/story/show/95663220

        ResourceTicketReservation reservation = projectCreateSpec.getResourceTicket();
        TenantEntity tenant = tenantBackend.findById(tenantId);

        if (projectDao.findByName(projectCreateSpec.getName(), tenant.getId()).isPresent()) {
            throw new NameTakenException(ProjectEntity.KIND, projectCreateSpec.getName());
        }

        ResourceTicketEntity tenantTicket = resourceTicketBackend.findByName(tenantId, reservation.getName());

        if (tenantTicket == null) {
            throw new ResourceTicketNameNotFoundException(tenant.getName(), reservation.getName());
        }

        ResourceTicketEntity projectTicket = createProjectTicket(tenantTicket, reservation);
        ProjectEntity projectEntity = new ProjectEntity();
        projectEntity.setName(projectCreateSpec.getName());
        projectEntity.setTenantId(tenant.getId());
        projectEntity.setResourceTicketId(projectTicket.getId());
        List<SecurityGroup> selfSecurityGroups = new ArrayList<>();
        if (projectCreateSpec.getSecurityGroups() != null) {
            selfSecurityGroups = projectCreateSpec.getSecurityGroups().stream()
                    .map(sg -> new SecurityGroup(sg, false)).collect(Collectors.toList());
        }
        List<String> tenantSecurityGroups = getTenantSecurityGroupNames(tenant.getSecurityGroups());
        projectEntity.setSecurityGroups(SecurityGroupUtils.fromApiRepresentation(
                SecurityGroupUtils.mergeParentSecurityGroups(selfSecurityGroups, tenantSecurityGroups).getLeft()));
        projectDao.create(projectEntity);

        return projectEntity;
    }

    private void delete(String projectId) throws ExternalException {
        // TODO(olegs): lock tenant ticket before returning quota
        // TODO(pankaj): https://www.pivotaltracker.com/story/show/95663220

        ProjectEntity project = projectDao.findById(projectId).get();

        if (!vmBackend.filterByProject(projectId).isEmpty()) {
            throw new ContainerNotEmptyException(project,
                    String.format("Project '%s' VM list is non-empty", project.getName()));
        }

        if (!diskBackend.filter(projectId, Optional.<String>absent()).isEmpty()) {
            throw new ContainerNotEmptyException(project,
                    String.format("Project '%s' persistent disk list is non-empty", project.getName()));
        }

        // todo(markl): make sure disks are properly linked to project
        // todo(markl): https://www.pivotaltracker.com/story/show/49873767

        String resourceTicketId = project.getResourceTicketId();
        ResourceTicketEntity projectTicket = resourceTicketBackend.findById(resourceTicketId);

        resourceTicketBackend.returnQuota(projectTicket);

        tombstoneBackend.create(project.getKind(), project.getId());
        projectDao.delete(project);
        resourceTicketBackend.delete(projectTicket.getId());
    }

    private List<String> getTenantSecurityGroupNames(List<SecurityGroupEntity> tenantSecurityGroups) {
        List<String> tenantSecurityGroupsNames = new ArrayList<>();
        for (SecurityGroupEntity sg : tenantSecurityGroups) {
            tenantSecurityGroupsNames.add(sg.getName());
        }
        return tenantSecurityGroupsNames;
    }
}