package com.sequenceiq.cloudbreak.converter.spi; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.google.common.collect.Maps; import com.sequenceiq.cloudbreak.cloud.model.CloudInstance; import com.sequenceiq.cloudbreak.cloud.model.CloudStack; import com.sequenceiq.cloudbreak.cloud.model.Group; import com.sequenceiq.cloudbreak.cloud.model.Image; import com.sequenceiq.cloudbreak.cloud.model.InstanceStatus; import com.sequenceiq.cloudbreak.cloud.model.InstanceTemplate; import com.sequenceiq.cloudbreak.cloud.model.Network; import com.sequenceiq.cloudbreak.cloud.model.PortDefinition; import com.sequenceiq.cloudbreak.cloud.model.Security; import com.sequenceiq.cloudbreak.cloud.model.SecurityRule; import com.sequenceiq.cloudbreak.cloud.model.StackTemplate; import com.sequenceiq.cloudbreak.cloud.model.Subnet; import com.sequenceiq.cloudbreak.cloud.model.Volume; import com.sequenceiq.cloudbreak.core.CloudbreakImageNotFoundException; import com.sequenceiq.cloudbreak.domain.InstanceGroup; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.domain.Template; import com.sequenceiq.cloudbreak.domain.json.Json; import com.sequenceiq.cloudbreak.repository.SecurityRuleRepository; import com.sequenceiq.cloudbreak.service.ComponentConfigProvider; import com.sequenceiq.cloudbreak.service.image.ImageService; import com.sequenceiq.cloudbreak.service.stack.connector.VolumeUtils; @Component public class StackToCloudStackConverter { private static final Logger LOGGER = LoggerFactory.getLogger(StackToCloudStackConverter.class); @Inject private SecurityRuleRepository securityRuleRepository; @Inject private ImageService imageService; @Inject private ComponentConfigProvider componentConfigProvider; public CloudStack convert(Stack stack) { return convert(stack, Collections.emptySet()); } public CloudStack convertForDownscale(Stack stack, Set<String> deleteRequestedInstances) { return convert(stack, deleteRequestedInstances); } public CloudStack convertForTermination(Stack stack, String instanceId) { return convert(stack, Collections.singleton(instanceId)); } public CloudStack convert(Stack stack, Set<String> deleteRequestedInstances) { Image image = null; List<Group> instanceGroups = buildInstanceGroups(stack.getInstanceGroupsAsList(), deleteRequestedInstances); try { image = imageService.getImage(stack.getId()); } catch (CloudbreakImageNotFoundException e) { LOGGER.info(e.getMessage()); } Network network = buildNetwork(stack); StackTemplate stackTemplate = componentConfigProvider.getStackTemplate(stack.getId()); String template = null; if (stackTemplate != null) { template = stackTemplate.getTemplate(); } return new CloudStack(instanceGroups, network, image, stack.getParameters(), getUserDefinedTags(stack), template); } public Map<String, String> getUserDefinedTags(Stack stack) { Map<String, String> result = Maps.newHashMap(); try { if (stack.getTags() != null) { Map<String, String> userDefined = (Map<String, String>) stack.getTags().get(Map.class).get("userDefined"); if (userDefined != null) { result = userDefined; } } } catch (IOException e) { LOGGER.warn("Exception during converting user defined tags.", e); } finally { return result; } } public List<Group> buildInstanceGroups(List<InstanceGroup> instanceGroups, Set<String> deleteRequests) { // sort by name to avoid shuffling the different instance groups Collections.sort(instanceGroups); List<Group> groups = new ArrayList<>(); long privateId = getFirstValidPrivateId(instanceGroups); for (InstanceGroup instanceGroup : instanceGroups) { if (instanceGroup.getTemplate() != null) { CloudInstance skeleton = null; List<CloudInstance> instances = new ArrayList<>(); Template template = instanceGroup.getTemplate(); int desiredNodeCount = instanceGroup.getNodeCount(); // existing instances for (InstanceMetaData metaData : instanceGroup.getInstanceMetaData()) { InstanceStatus status = getInstanceStatus(metaData, deleteRequests); instances.add(buildInstance(metaData.getInstanceId(), template, instanceGroup.getGroupName(), metaData.getPrivateId(), metaData.getDiscoveryName(), status)); } // new instances int existingNodesSize = instances.size(); if (existingNodesSize < desiredNodeCount) { for (long i = 0; i < desiredNodeCount - existingNodesSize; i++) { instances.add(buildInstance(null, template, instanceGroup.getGroupName(), privateId++, null, InstanceStatus.CREATE_REQUESTED)); } } if (existingNodesSize == desiredNodeCount && desiredNodeCount == 0) { skeleton = buildInstance(null, template, instanceGroup.getGroupName(), 0L, null, InstanceStatus.CREATE_REQUESTED); } Json attributes = instanceGroup.getAttributes(); Map<String, Object> fields = attributes == null ? Collections.emptyMap() : attributes.getMap(); groups.add(new Group(instanceGroup.getGroupName(), instanceGroup.getInstanceGroupType(), instances, buildSecurity(instanceGroup), skeleton, fields)); } } return groups; } private Security buildSecurity(InstanceGroup ig) { List<SecurityRule> rules = new ArrayList<>(); if (ig.getSecurityGroup() == null) { return new Security(rules, null); } Long id = ig.getSecurityGroup().getId(); List<com.sequenceiq.cloudbreak.domain.SecurityRule> securityRules = securityRuleRepository.findAllBySecurityGroupId(id); for (com.sequenceiq.cloudbreak.domain.SecurityRule securityRule : securityRules) { List<PortDefinition> portDefinitions = new ArrayList<>(); for (String actualPort : securityRule.getPorts()) { String[] segments = actualPort.split("-"); if (segments.length > 1) { portDefinitions.add(new PortDefinition(segments[0], segments[1])); } else { portDefinitions.add(new PortDefinition(segments[0], segments[0])); } } rules.add(new SecurityRule(securityRule.getCidr(), portDefinitions.toArray(new PortDefinition[portDefinitions.size()]), securityRule.getProtocol())); } return new Security(rules, ig.getSecurityGroup().getSecurityGroupId()); } public List<CloudInstance> buildInstances(Stack stack) { List<Group> groups = buildInstanceGroups(stack.getInstanceGroupsAsList(), Collections.emptySet()); List<CloudInstance> cloudInstances = new ArrayList<>(); for (Group group : groups) { cloudInstances.addAll(group.getInstances()); } return cloudInstances; } public CloudInstance buildInstance(String id, Template template, String name, Long privateId, String hostName, InstanceStatus status) { InstanceTemplate instanceTemplate = buildInstanceTemplate(template, name, privateId, status); Map<String, Object> params = new HashMap<>(); if (hostName != null) { params.put(CloudInstance.DISCOVERY_NAME, hostName); } return new CloudInstance(id, instanceTemplate, params); } public InstanceTemplate buildInstanceTemplate(Template template, String name, Long privateId, InstanceStatus status) { Json attributes = template.getAttributes(); Map<String, Object> fields = attributes == null ? Collections.emptyMap() : attributes.getMap(); List<Volume> volumes = new ArrayList<>(); for (int i = 0; i < template.getVolumeCount(); i++) { Volume volume = new Volume(VolumeUtils.VOLUME_PREFIX + (i + 1), template.getVolumeType(), template.getVolumeSize()); volumes.add(volume); } return new InstanceTemplate(template.getInstanceType(), name, privateId, volumes, status, fields); } private Network buildNetwork(Stack stack) { com.sequenceiq.cloudbreak.domain.Network stackNetwork = stack.getNetwork(); Network result = null; if (stackNetwork != null) { Subnet subnet = new Subnet(stackNetwork.getSubnetCIDR()); Json attributes = stackNetwork.getAttributes(); Map<String, Object> params = attributes == null ? Collections.emptyMap() : attributes.getMap(); result = new Network(subnet, params); } return result; } private Long getFirstValidPrivateId(List<InstanceGroup> instanceGroups) { LOGGER.info("Get first valid PrivateId of instanceGroups"); long highest = 0; for (InstanceGroup instanceGroup : instanceGroups) { LOGGER.info("Checking of instanceGroup: {}", instanceGroup.getGroupName()); for (InstanceMetaData metaData : instanceGroup.getAllInstanceMetaData()) { Long privateId = metaData.getPrivateId(); LOGGER.info("InstanceMetaData metaData: privateId: {}, instanceGroupName: {}, instanceId: {}, status: {}", privateId, metaData.getInstanceGroupName(), metaData.getInstanceId(), metaData.getInstanceStatus()); if (privateId == null) { continue; } if (privateId > highest) { highest = privateId; } } } LOGGER.info("highest privateId: {}", highest); return highest == 0 ? 0 : highest + 1; } private InstanceStatus getInstanceStatus(InstanceMetaData metaData, Set<String> deleteRequests) { return deleteRequests.contains(metaData.getInstanceId()) ? InstanceStatus.DELETE_REQUESTED : metaData.getInstanceStatus() == com.sequenceiq.cloudbreak.api.model.InstanceStatus.REQUESTED ? InstanceStatus.CREATE_REQUESTED : InstanceStatus.CREATED; } }