package io.subutai.core.environment.rest.ui; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Collectors; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import io.subutai.bazaar.share.quota.ContainerQuota; import io.subutai.bazaar.share.quota.ContainerSize; import io.subutai.common.environment.BazaarEnvironment; import io.subutai.common.environment.ContainerDto; import io.subutai.common.environment.ContainerHostNotFoundException; import io.subutai.common.environment.ContainerQuotaDto; import io.subutai.common.environment.Environment; import io.subutai.common.environment.EnvironmentCreationRef; import io.subutai.common.environment.EnvironmentDto; import io.subutai.common.environment.EnvironmentModificationException; import io.subutai.common.environment.EnvironmentNotFoundException; import io.subutai.common.environment.Node; import io.subutai.common.environment.PeerTemplatesDownloadProgress; import io.subutai.common.environment.Topology; import io.subutai.common.gson.required.RequiredDeserializer; import io.subutai.common.metric.ResourceHostMetric; import io.subutai.common.metric.ResourceHostMetrics; import io.subutai.common.peer.ContainerHost; import io.subutai.common.peer.EnvironmentContainerHost; import io.subutai.common.peer.EnvironmentId; import io.subutai.common.peer.LocalPeer; import io.subutai.common.peer.Peer; import io.subutai.common.peer.PeerException; import io.subutai.common.peer.ResourceHost; import io.subutai.common.protocol.Template; import io.subutai.common.settings.Common; import io.subutai.common.util.JsonUtil; import io.subutai.common.util.ServiceLocator; import io.subutai.common.util.StringUtil; import io.subutai.core.environment.api.EnvironmentManager; import io.subutai.core.environment.api.SecureEnvironmentManager; import io.subutai.core.environment.api.ShareDto.ShareDto; import io.subutai.core.environment.api.exception.EnvironmentCreationException; import io.subutai.core.environment.rest.ui.entity.ChangedContainerDto; import io.subutai.core.environment.rest.ui.entity.NodeSchemaDto; import io.subutai.core.environment.rest.ui.entity.PeerDto; import io.subutai.core.environment.rest.ui.entity.ResourceHostDto; import io.subutai.core.identity.api.IdentityManager; import io.subutai.core.identity.api.model.User; import io.subutai.core.peer.api.PeerManager; import io.subutai.core.template.api.TemplateManager; import static io.subutai.common.util.JsonUtil.mapper; public class RestServiceImpl implements RestService { private static final Logger LOG = LoggerFactory.getLogger( RestServiceImpl.class ); private static final String ERROR_KEY = "ERROR"; private final EnvironmentManager environmentManager; private final PeerManager peerManager; private final TemplateManager templateManager; private final SecureEnvironmentManager secureEnvironmentManager; private Gson gson = RequiredDeserializer.createValidatingGson(); private IdentityManager identityManager = ServiceLocator.lookup( IdentityManager.class ); public RestServiceImpl( final EnvironmentManager environmentManager, final PeerManager peerManager, final TemplateManager templateManager, final SecureEnvironmentManager secureEnvironmentManager ) { Preconditions.checkNotNull( environmentManager ); Preconditions.checkNotNull( peerManager ); Preconditions.checkNotNull( templateManager ); this.environmentManager = environmentManager; this.peerManager = peerManager; this.templateManager = templateManager; this.secureEnvironmentManager = secureEnvironmentManager; } /** Templates *************************************************** */ @Override public Response getVerifiedTemplate( final String templateName ) { try { Template template = templateManager.getVerifiedTemplateByName( templateName ); if ( template != null ) { return Response.ok( gson.toJson( template ) ).build(); } else { return Response.status( Response.Status.NOT_FOUND ).build(); } } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() == null ? "Internal error" : e.getMessage() ) ).build(); } } @Override public Response createTemplate( final String environmentId, final String containerId, final String templateName, final String version, final boolean privateTemplate ) { try { environmentManager.createTemplate( environmentId, containerId, templateName, version, privateTemplate ); return Response.ok().build(); } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } /** Environments **************************************************** */ @Override public Response build( final String name, final String topologyJson ) { Map<String, String> envCreationRef = Maps.newHashMap(); try { //disallow bazaar users to use this operation filterBazaarUser(); } catch ( AccessControlException e ) { return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation, please visit Bazaar to perform" + " this operation." ) ).build(); } try { Preconditions.checkArgument( !StringUtils.isBlank( name ), "Invalid environment name" ); Preconditions.checkArgument( !StringUtils.isBlank( topologyJson ), "Invalid environment topology" ); checkName( name ); List<NodeSchemaDto> nodes = parseNodes( topologyJson ); Topology topology = new Topology( name ); distribute( nodes ); for ( NodeSchemaDto dto : nodes ) { topology.addNodePlacement( dto.getPeerId(), new Node( dto.getName(), dto.getName(), dto.getQuota().getContainerQuota(), dto.getPeerId(), dto.getHostId(), dto.getTemplateId() ) ); } EnvironmentCreationRef ref = environmentManager.createEnvironment( topology, true ); envCreationRef.put( "trackerId", ref.getTrackerId() ); envCreationRef.put( "environmentId", ref.getEnvironmentId() ); } catch ( Exception e ) { if ( e.getClass() == AccessControlException.class ) { LOG.error( e.getMessage() ); return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation" ) ).build(); } return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() == null ? "Internal error" : e.getMessage() ) ).build(); } return Response.ok( JsonUtil.toJson( envCreationRef ) ).build(); } private void distribute( final List<NodeSchemaDto> nodes ) { //fetch online peers with connected resource hosts Map<String, PeerDto> peersNHosts = getPeersNConnectedRHs(); //filter peers that have ALL resource hosts connected for ( Map.Entry<String, PeerDto> peerEntry : peersNHosts.entrySet() ) { String peerId = peerEntry.getKey(); PeerDto peerDto = peerEntry.getValue(); if ( peerDto.getRhCount() > peerDto.getResourceHosts().size() ) { LOG.warn( "Peer {} has disconnected resource hosts, skipping it", peerDto.getName() ); peersNHosts.remove( peerId ); } } //collect all available resource hosts List<ResourceHostDto> resourceHosts = Lists.newArrayList(); for ( PeerDto peerDto : peersNHosts.values() ) { resourceHosts.addAll( peerDto.getResourceHosts() ); } //check if we have resource hosts to use if ( resourceHosts.size() == 0 ) { throw new IllegalStateException( "Not enough connected resource hosts. All resource hosts of selected peers must be connected" ); } //distribute nodes over resource hosts (round-robin) Iterator<ResourceHostDto> rhIterator = Iterables.cycle( resourceHosts ).iterator(); for ( NodeSchemaDto node : nodes ) { ResourceHostDto rh = rhIterator.next(); node.setPeerId( rh.getPeerId() ); node.setHostId( rh.getId() ); } } private List<NodeSchemaDto> parseNodes( final String nodes ) throws java.io.IOException { TypeFactory typeFactory = mapper.getTypeFactory(); CollectionType arrayType = typeFactory.constructCollectionType( ArrayList.class, NodeSchemaDto.class ); return mapper.readValue( nodes, arrayType ); } @Override public Response buildAdvanced( final String name, final String topologyJson ) { Map<String, String> envCreationRef = Maps.newHashMap(); try { //disallow bazaar users to use this operation filterBazaarUser(); } catch ( AccessControlException e ) { return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation, please visit Bazaar to perform" + " this operation." ) ).build(); } try { checkName( name ); List<NodeSchemaDto> schemaDto = parseNodes( topologyJson ); final List<Node> nodes = getNodes( schemaDto ); Topology topology = new Topology( name ); topology.addAllNodePlacement( nodes ); EnvironmentCreationRef ref = environmentManager.createEnvironment( topology, true ); envCreationRef.put( "trackerId", ref.getTrackerId() ); envCreationRef.put( "environmentId", ref.getEnvironmentId() ); } catch ( Exception e ) { if ( e.getClass() == AccessControlException.class ) { LOG.error( e.getMessage() ); return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation" ) ).build(); } return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } return Response.ok( JsonUtil.toJson( envCreationRef ) ).build(); } @Override public Response modify( final String environmentId, final String topologyJson, final String removedContainers, final String quotaContainers ) { String trackerId; try { //disallow bazaar users to use this operation filterBazaarUser(); } catch ( AccessControlException e ) { return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation, please visit Bazaar to perform" + " this operation." ) ).build(); } try { String name = environmentManager.loadEnvironment( environmentId ).getName(); Topology topology = new Topology( name ); List<NodeSchemaDto> nodes = parseNodes( topologyJson ); distribute( nodes ); for ( NodeSchemaDto dto : nodes ) { topology.addNodePlacement( dto.getPeerId(), new Node( dto.getName(), dto.getName(), dto.getQuota().getContainerQuota(), dto.getPeerId(), dto.getHostId(), dto.getTemplateId() ) ); } Set<String> removedContainersList = JsonUtil.fromJson( removedContainers, new TypeToken<Set<String>>() { }.getType() ); Map<String, ContainerQuota> changedContainersFiltered = getChangedContainers( quotaContainers ); EnvironmentCreationRef ref = environmentManager .modifyEnvironment( environmentId, topology, removedContainersList, changedContainersFiltered, true ); trackerId = ref.getTrackerId(); } catch ( Exception e ) { if ( e.getClass() == AccessControlException.class ) { LOG.error( e.getMessage() ); return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation" ) ).build(); } return Response.status( Response.Status.BAD_REQUEST ).entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ) .build(); } return Response.ok( JsonUtil.toJson( trackerId ) ).build(); } @Override public Response modifyAdvanced( final String environmentId, final String topologyJson, final String removedContainers, final String quotaContainers ) { String trackerId; try { //disallow bazaar users to use this operation filterBazaarUser(); } catch ( AccessControlException e ) { return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation, please visit Bazaar to perform" + " this operation." ) ).build(); } try { String name = environmentManager.loadEnvironment( environmentId ).getName(); List<NodeSchemaDto> schemaDto = parseNodes( topologyJson ); Topology topology = new Topology( name ); final List<Node> nodes = getNodes( schemaDto ); topology.addAllNodePlacement( nodes ); Set<String> containersToRemove = JsonUtil.fromJson( removedContainers, new TypeToken<Set<String>>() { }.getType() ); Map<String, ContainerQuota> changedContainersFiltered = getChangedContainers( quotaContainers ); EnvironmentCreationRef ref = environmentManager .modifyEnvironment( environmentId, topology, containersToRemove, changedContainersFiltered, true ); trackerId = ref.getTrackerId(); } catch ( Exception e ) { if ( e.getClass() == AccessControlException.class ) { LOG.error( e.getMessage() ); return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation" ) ).build(); } return Response.status( Response.Status.BAD_REQUEST ).entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ) .build(); } return Response.ok( JsonUtil.toJson( trackerId ) ).build(); } private List<Node> getNodes( final List<NodeSchemaDto> schemaDto ) { List<Node> result = new ArrayList<>(); for ( NodeSchemaDto dto : schemaDto ) { ContainerQuota quota = dto.getQuota().getContainerQuota(); if ( quota.getContainerSize() != ContainerSize.CUSTOM ) { quota = ContainerSize.getDefaultContainerQuota( quota.getContainerSize() ); } Node node = new Node( dto.getName(), dto.getName(), quota, dto.getPeerId(), dto.getHostId(), dto.getTemplateId() ); result.add( node ); } return result; } private Map<String, ContainerQuota> getChangedContainers( final String quotaContainers ) throws java.io.IOException { Map<String, ContainerQuota> changedContainersFiltered = new HashMap<>(); TypeFactory typeFactory = mapper.getTypeFactory(); CollectionType arrayType = typeFactory.constructCollectionType( ArrayList.class, ChangedContainerDto.class ); List<ChangedContainerDto> changedContainers = mapper.readValue( quotaContainers, arrayType ); for ( ChangedContainerDto cont : changedContainers ) { ContainerQuotaDto containerQuotaDto = cont.getQuota(); ContainerSize containerSize = containerQuotaDto.getContainerSize(); ContainerQuota defaultQuota = ContainerSize.getDefaultContainerQuota( containerSize ); if ( containerSize == ContainerSize.CUSTOM ) { defaultQuota = containerQuotaDto.getContainerQuota(); } changedContainersFiltered.put( cont.getHostId(), defaultQuota ); } return changedContainersFiltered; } @Override public Response destroyEnvironment( final String environmentId ) { try { environmentManager.cancelEnvironmentWorkflow( environmentId ); environmentManager.destroyEnvironment( environmentId, false ); } catch ( EnvironmentNotFoundException e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, "Environment is already destroyed" ) ) .build(); } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } return Response.ok().build(); } @Override public Response getSshKeys( final String environmentId ) { try { Environment environment = environmentManager.loadEnvironment( environmentId ); return Response.ok( JsonUtil.toJson( environment.getSshKeys() ) ).build(); } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } /** Environments SSH keys **************************************************** */ @Override public Response addSshKey( final String environmentId, final String key ) { if ( StringUtils.isBlank( environmentId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid environment id" ) ).build(); } else if ( StringUtils.isBlank( key ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid ssh key" ) ).build(); } try { byte[] bytesEncoded = Base64.decodeBase64( key.getBytes() ); environmentManager.addSshKey( environmentId, new String( bytesEncoded ).trim(), false ); } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } return Response.ok().build(); } @Override public Response removeSshKey( final String environmentId, final String key ) { if ( StringUtils.isBlank( environmentId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid environment id" ) ).build(); } else if ( StringUtils.isBlank( key ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid ssh key" ) ).build(); } try { byte[] bytesEncoded = Base64.decodeBase64( key.getBytes() ); environmentManager.removeSshKey( environmentId, new String( bytesEncoded ).trim(), false ); } catch ( Exception e ) { return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } return Response.ok().build(); } @Override public Response setContainerName( String environmentId, String containerId, String name ) { try { Environment environment = findEnvironmentByContainerId( containerId ); Preconditions.checkNotNull( environment, "Environment not found" ); ContainerHost containerHost = environment.getContainerHostById( containerId ); environmentManager.changeContainerHostname( containerHost.getContainerId(), name, false ); } catch ( Exception e ) { return Response.serverError().entity( e.getMessage() ).build(); } return Response.ok().build(); } /** Containers **************************************************** */ @Override public Response destroyContainer( final String containerId ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); environmentManager.destroyContainer( environment.getId(), containerHost.getId(), false ); return Response.ok().build(); } catch ( ContainerHostNotFoundException | EnvironmentNotFoundException | EnvironmentModificationException e ) { LOG.error( "Error destroying container #destroyContainer", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ) .entity( JsonUtil.toJson( ERROR_KEY, "Container not found" ) ).build(); } @Override public Response getContainerState( final String containerId ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); return Response.ok().entity( JsonUtil.toJson( "STATE", containerHost.getState() ) ).build(); } catch ( ContainerHostNotFoundException e ) { LOG.error( "Error getting container state", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response startContainer( final String containerId ) { try { //disallow bazaar users to use this operation filterBazaarUser(); } catch ( AccessControlException e ) { return Response.status( Response.Status.FORBIDDEN ). entity( JsonUtil.GSON.toJson( "You don't have permission to perform this operation, please visit Bazaar to perform" + " this operation." ) ).build(); } if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); containerHost.start(); return Response.ok().build(); } catch ( PeerException e ) { LOG.error( "Exception starting container host", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response stopContainer( final String containerId ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); containerHost.stop(); return Response.ok().build(); } catch ( PeerException e ) { LOG.error( "Exception stopping container host", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response listContainerSnapshots( final String containerId ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); return Response.ok( JsonUtil.toJson( containerHost.listSnapshots().getSnapshots() ) ).build(); } catch ( PeerException e ) { LOG.error( "Exception listing container snapshots", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response removeContainerSnapshot( final String containerId, final String partition, final String label ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } if ( StringUtils.isBlank( partition ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid partition" ) ).build(); } if ( StringUtils.isBlank( label ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid label" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); containerHost.removeSnapshot( partition, label ); return Response.ok().build(); } catch ( PeerException e ) { LOG.error( "Exception removing container snapshot", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response rollbackContainerSnapshot( final String containerId, final String partition, final String label ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } if ( StringUtils.isBlank( partition ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid partition" ) ).build(); } if ( StringUtils.isBlank( label ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid label" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); containerHost.rollbackToSnapshot( partition, label, true ); return Response.ok().build(); } catch ( PeerException e ) { LOG.error( "Exception rolling back to container snapshot", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } @Override public Response addContainerSnapshot( final String containerId, final String partition, final String label, final boolean stopContainer ) { if ( StringUtils.isBlank( containerId ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid container id" ) ).build(); } if ( StringUtils.isBlank( partition ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid partition" ) ).build(); } if ( StringUtils.isBlank( label ) ) { return Response.status( Response.Status.BAD_REQUEST ) .entity( JsonUtil.toJson( ERROR_KEY, "Invalid label" ) ).build(); } Environment environment = findEnvironmentByContainerId( containerId ); if ( environment != null ) { try { ContainerHost containerHost = environment.getContainerHostById( containerId ); containerHost.addSnapshot( partition, label, stopContainer ); return Response.ok().build(); } catch ( PeerException e ) { LOG.error( "Exception adding container snapshot", e ); return Response.serverError().entity( JsonUtil.toJson( ERROR_KEY, e.getMessage() ) ).build(); } } return Response.status( Response.Status.NOT_FOUND ).build(); } /** Container types **************************************************** */ @Override public Response listContainerTypes() { return Response.ok().entity( gson.toJson( ContainerSize.values() ) ).build(); } @Override public Response listContainerTypesInfo() { try { return Response.ok().entity( gson.toJson( ContainerSize.getContainerSizesDescription() ) ).build(); } catch ( Exception e ) { return Response.serverError().entity( gson.toJson( e.getMessage() ) ).build(); } } private CompletionService<Boolean> getCompletionService( Executor executor ) { return new ExecutorCompletionService<>( executor ); } /** Peers **************************************************** */ @Override public Response getPeers() { return Response.ok().entity( JsonUtil.toJson( getPeersNConnectedRHs() ) ).build(); } private Map<String, PeerDto> getPeersNConnectedRHs() { List<Peer> peers = peerManager.getPeers(); ExecutorService taskExecutor = Executors.newFixedThreadPool( Math.min( Common.MAX_EXECUTOR_SIZE, peers.size() ) ); CompletionService<Boolean> taskCompletionService = getCompletionService( taskExecutor ); Map<String, PeerDto> peerHostMap = Maps.newHashMap(); try { for ( Peer peer : peers ) { taskCompletionService.submit( () -> { boolean isOnline = peer.isOnline(); PeerDto peerDto = new PeerDto( peer.getId(), peer.getName(), isOnline, peer.isLocal() ); if ( isOnline ) { ResourceHostMetrics metrics = peer.getResourceHostMetrics(); Collection<ResourceHostMetric> collection = metrics.getResources(); peerDto.setRhCount( metrics.getResourceHostCount() ); for ( ResourceHostMetric metric : collection ) { peerDto.addResourceHostDto( new ResourceHostDto( metric.getHostInfo().getId(), metric.getHostName(), metric.getCpuModel(), metric.getUsedCpu().toString(), metric.getTotalRam().toString(), metric.getAvailableRam().toString(), metric.getTotalSpace().toString(), metric.getAvailableSpace().toString(), metric.isManagement(), metric.getPeerId() ) ); } } peerHostMap.put( peer.getId(), peerDto ); return true; } ); } taskExecutor.shutdown(); for ( int i = 0; i < peers.size(); i++ ) { try { Future<Boolean> future = taskCompletionService.take(); future.get(); } catch ( ExecutionException | InterruptedException e ) { //ignored } } } catch ( Exception e ) { LOG.error( "Resource hosts are empty", e ); } return peerHostMap; } @Override public Response getResourceHosts() { List<ResourceHostDto> resourceHostDtos = Lists.newArrayList(); try { LocalPeer localPeer = peerManager.getLocalPeer(); for ( ResourceHost resourceHost : localPeer.getResourceHosts() ) { resourceHostDtos.add( new ResourceHostDto( resourceHost.getId(), resourceHost.getHostname(), resourceHost.getInstanceType(), resourceHost.isManagementHost(), resourceHost.getArch() ) ); } } catch ( Exception e ) { LOG.error( "Resource hosts are empty", e ); } return Response.ok().entity( JsonUtil.toJson( resourceHostDtos ) ).build(); } /** Tags **************************************************** */ @Override public Response addTags( final String environmentId, final String containerId, final String tagsJson ) { try { Environment environment = environmentManager.loadEnvironment( environmentId ); EnvironmentContainerHost containerHost = environment.getContainerHostById( containerId ); Set<String> tags = JsonUtil.fromJson( tagsJson, new TypeToken<Set<String>>() { }.getType() ); for ( String tag : tags ) { containerHost = containerHost.addTag( tag ); } } catch ( Exception e ) { return Response.status( Response.Status.BAD_REQUEST ).entity( JsonUtil.toJson( e ) ).build(); } return Response.ok().build(); } @Override public Response removeTag( final String environmentId, final String containerId, final String tag ) { try { Environment environment = environmentManager.loadEnvironment( environmentId ); environment.getContainerHostById( containerId ).removeTag( tag ); } catch ( Exception e ) { return Response.status( Response.Status.BAD_REQUEST ).entity( JsonUtil.toJson( e ) ).build(); } return Response.ok().build(); } /** Additional *************************************** */ @Override public Response setupContainerSsh( final String environmentId, final String containerId ) { try { return Response.ok( JsonUtil .toJson( environmentManager.setupSshTunnelForContainer( containerId, environmentId ) ) ).build(); } catch ( Exception e ) { return Response.status( Response.Status.BAD_REQUEST ).entity( e ).build(); } } @Override public Response getSharedUsers( final String objectId ) { try { List<ShareDto> sharedUsers = secureEnvironmentManager.getSharedUsers( objectId ); return Response.ok( JsonUtil.toJson( sharedUsers ) ).build(); } catch ( Exception e ) { return Response.status( Response.Status.INTERNAL_SERVER_ERROR ).entity( e.getMessage() ).build(); } } @Override public Response share( final String users, final String environmentId ) { ShareDto[] shareDto = gson.fromJson( users, ShareDto[].class ); secureEnvironmentManager.shareEnvironment( shareDto, environmentId ); return Response.ok().build(); } private Environment findEnvironmentByContainerId( String containerId ) { for ( Environment environment : environmentManager.getEnvironments() ) { for ( ContainerHost containerHost : environment.getContainerHosts() ) { if ( containerHost.getId().equals( containerId ) ) { return environment; } } } return null; } @Override public Response getDownloadProgress( String environmentId ) { try { List<Peer> peers = Lists.newArrayList( environmentManager.loadEnvironment( environmentId ).getPeers() ); List<PeerTemplatesDownloadProgress> result = peers.stream().map( p -> { try { return p.getTemplateDownloadProgress( new EnvironmentId( environmentId ) ); } catch ( Exception e ) { return new PeerTemplatesDownloadProgress( "NONE" ); } } ).sorted( Comparator.comparing( PeerTemplatesDownloadProgress::getPeerId ) ) .collect( Collectors.toList() ); if ( result.stream().filter( s -> !s.getTemplatesDownloadProgresses().isEmpty() ).count() == 0 ) { return Response.ok().build(); } return Response.ok( JsonUtil.toJson( result ) ).build(); } catch ( Exception e ) { return Response.serverError().entity( e.getMessage() ).build(); } } @Override public Response listEnvironments() { Set<Environment> environments = environmentManager.getEnvironments(); Set<EnvironmentDto> environmentDtos = Sets.newHashSet(); for ( Environment environment : environments ) { try { String dataSource = ( environment instanceof BazaarEnvironment || String.format( "Of %s", Common.BAZAAR_ID ) .equals( environment.getName() ) ) ? Common.BAZAAR_ID : Common.SUBUTAI_ID; EnvironmentDto environmentDto = new EnvironmentDto( environment.getId(), environment.getName(), environment.getStatus(), convertContainersToContainerJson( environment.getContainerHosts(), dataSource ), dataSource, environmentManager.getEnvironmentOwnerName( environment ) ); environmentDtos.add( environmentDto ); } catch ( Exception e ) { LOG.error( "Error JSON-ifying environment {}: {}", environment.getId(), e.getMessage() ); } } return Response.ok( JsonUtil.toJson( removeXss( environmentDtos ) ) ).build(); } @Override public Response listTenantEnvironments() { Set<EnvironmentDto> tenantEnvs = environmentManager.getTenantEnvironments(); return Response.ok( JsonUtil.toJson( removeXss( tenantEnvs ) ) ).build(); } private Set<EnvironmentDto> removeXss( final Set<EnvironmentDto> environmentDtos ) { for ( EnvironmentDto environmentDto : environmentDtos ) { environmentDto.setName( StringUtil.removeHtml( environmentDto.getName() ) ); for ( ContainerDto containerDto : environmentDto.getContainers() ) { containerDto.setContainerName( StringUtil.removeHtml( containerDto.getContainerName() ) ); containerDto.setHostname( StringUtil.removeHtml( containerDto.getHostname() ) ); containerDto.setTemplateName( StringUtil.removeHtml( containerDto.getTemplateName() ) ); } } return environmentDtos; } /** AUX **************************************************** */ private Set<ContainerDto> convertContainersToContainerJson( Set<EnvironmentContainerHost> containerHosts, String datasource ) { Set<ContainerDto> containerDtos = Sets.newHashSet(); for ( EnvironmentContainerHost containerHost : containerHosts ) { ContainerDto containerDto = new ContainerDto( containerHost.getId(), containerHost.getEnvironmentId().getId(), containerHost.getHostname(), containerHost.getIp(), containerHost.getTemplateName(), containerHost.getContainerSize(), containerHost.getArch().toString(), containerHost.getTags(), containerHost.getPeerId(), containerHost.getResourceHostId().getId(), containerHost.isLocal(), datasource, containerHost.getState(), containerHost.getTemplateId(), containerHost.getContainerName(), containerHost.getResourceHostId().getId() ); try { ContainerQuota containerQuota = containerHost.getQuota(); if ( containerQuota != null ) { containerDto.setQuota( new ContainerQuotaDto( containerQuota ) ); } } catch ( Exception e ) { LOG.error( "Error getting container quota: {}", e.getMessage() ); } containerDtos.add( containerDto ); } return containerDtos; } private void checkName( final String name ) throws EnvironmentCreationException { if ( environmentManager.getEnvironments().stream().filter( e -> e.getName().equalsIgnoreCase( name.trim() ) ) .count() > 0 ) { throw new EnvironmentCreationException( "Duplicated environment name" ); } if ( name.trim().length() > 50 ) { throw new EnvironmentCreationException( "Environment name is too long, it should be 50 chars max" ); } } /** * Filter if active user isbazaar user. */ private void filterBazaarUser() throws AccessControlException { User user = identityManager.getActiveUser(); if ( user.isBazaarUser() ) { throw new AccessControlException( "You don't have permission to perform this operation" ); } } }