package com.sequenceiq.cloudbreak.cloud.azure; import static com.sequenceiq.cloudbreak.cloud.model.CustomImage.customImage; import static com.sequenceiq.cloudbreak.cloud.model.DiskType.diskType; import static com.sequenceiq.cloudbreak.cloud.model.Orchestrator.orchestrator; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.sequenceiq.cloudbreak.api.model.ArmAttachedStorageOption; import com.sequenceiq.cloudbreak.cloud.PlatformParameters; import com.sequenceiq.cloudbreak.cloud.PlatformParametersConsts; import com.sequenceiq.cloudbreak.cloud.model.TagSpecification; import com.sequenceiq.cloudbreak.cloud.model.AvailabilityZone; import com.sequenceiq.cloudbreak.cloud.model.AvailabilityZones; import com.sequenceiq.cloudbreak.cloud.model.ConfigSpecification; import com.sequenceiq.cloudbreak.cloud.model.CustomImage; import com.sequenceiq.cloudbreak.cloud.model.DiskType; import com.sequenceiq.cloudbreak.cloud.model.DiskTypes; import com.sequenceiq.cloudbreak.cloud.model.PlatformImage; import com.sequenceiq.cloudbreak.cloud.model.PlatformOrchestrator; import com.sequenceiq.cloudbreak.cloud.model.Region; import com.sequenceiq.cloudbreak.cloud.model.RegionSpecification; import com.sequenceiq.cloudbreak.cloud.model.Regions; import com.sequenceiq.cloudbreak.cloud.model.RegionsSpecification; import com.sequenceiq.cloudbreak.cloud.model.ScriptParams; import com.sequenceiq.cloudbreak.cloud.model.StackParamValidation; import com.sequenceiq.cloudbreak.cloud.model.StringTypesCompare; import com.sequenceiq.cloudbreak.cloud.model.VmSpecification; import com.sequenceiq.cloudbreak.cloud.model.VmType; import com.sequenceiq.cloudbreak.cloud.model.VmTypeMeta; import com.sequenceiq.cloudbreak.cloud.model.VmTypes; import com.sequenceiq.cloudbreak.cloud.model.VmsSpecification; import com.sequenceiq.cloudbreak.cloud.model.VolumeParameterConfig; import com.sequenceiq.cloudbreak.cloud.model.VolumeParameterType; import com.sequenceiq.cloudbreak.cloud.model.ZoneVmSpecification; import com.sequenceiq.cloudbreak.cloud.model.ZoneVmSpecifications; import com.sequenceiq.cloudbreak.cloud.service.CloudbreakResourceReaderService; import com.sequenceiq.cloudbreak.common.type.OrchestratorConstants; import com.sequenceiq.cloudbreak.util.FileReaderUtils; import com.sequenceiq.cloudbreak.util.JsonUtil; @Service public class AzurePlatformParameters implements PlatformParameters { private static final Logger LOGGER = LoggerFactory.getLogger(AzurePlatformParameters.class); private static final int START_LABEL = 98; private static final int DEFAULT_REGION_TYPE_POSITION = 4; private static final ScriptParams SCRIPT_PARAMS = new ScriptParams("sd", START_LABEL); @Value("${cb.arm.vm.parameter.definition.path:}") private String armVmParameterDefinitionPath; @Value("${cb.arm.zone.parameter.definition.path:}") private String armZoneParameterDefinitionPath; @Inject private CloudbreakResourceReaderService cloudbreakResourceReaderService; @Inject private Environment environment; @Inject @Qualifier("AzureTagSpecification") private TagSpecification tagSpecification; private Map<Region, List<AvailabilityZone>> regions = new HashMap<>(); private List<VmType> vmTypes = new ArrayList<>(); private Map<AvailabilityZone, List<VmType>> vmTypesForZones = new HashMap<>(); private Map<AvailabilityZone, VmType> defaultVmTypes = new HashMap<>(); private Region defaultRegion; private VmType defaultVmType; @PostConstruct public void init() { this.regions = readRegions(resourceDefinition("zone")); readVmTypes(); this.defaultRegion = nthElement(this.regions.keySet(), DEFAULT_REGION_TYPE_POSITION); this.defaultVmType = defaultVmTypes.get(regions.get(defaultRegion).get(0)); } private void readVmTypes() { Map<String, VmType> vmTypeMap = new HashMap<>(); String vm = getDefinition(armVmParameterDefinitionPath, "vm"); String zoneVms = getDefinition(armVmParameterDefinitionPath, "zone-vm"); try { VmsSpecification oVms = JsonUtil.readValue(vm, VmsSpecification.class); for (VmSpecification vmSpecification : oVms.getItems()) { VmTypeMeta.VmTypeMetaBuilder builder = VmTypeMeta.VmTypeMetaBuilder.builder() .withCpuAndMemory(vmSpecification.getMetaSpecification().getProperties().getCpu(), vmSpecification.getMetaSpecification().getProperties().getMemory()); for (ConfigSpecification configSpecification : vmSpecification.getMetaSpecification().getConfigSpecification()) { if (configSpecification.getVolumeParameterType().equals(VolumeParameterType.AUTO_ATTACHED.name())) { builder.withAutoAttachedConfig(volumeParameterConfig(configSpecification)); } else if (configSpecification.getVolumeParameterType().equals(VolumeParameterType.EPHEMERAL.name())) { builder.withEphemeralConfig(volumeParameterConfig(configSpecification)); } else if (configSpecification.getVolumeParameterType().equals(VolumeParameterType.MAGNETIC.name())) { builder.withMagneticConfig(volumeParameterConfig(configSpecification)); } else if (configSpecification.getVolumeParameterType().equals(VolumeParameterType.SSD.name())) { builder.withSsdConfig(volumeParameterConfig(configSpecification)); } } VmTypeMeta vmTypeMeta = builder.create(); VmType vmType = VmType.vmTypeWithMeta(vmSpecification.getValue(), vmTypeMeta, true); vmTypes.add(vmType); vmTypeMap.put(vmType.value(), vmType); } ZoneVmSpecifications zoneVmSpecifications = JsonUtil.readValue(zoneVms, ZoneVmSpecifications.class); Map<String, List<AvailabilityZone>> azmap = regions.entrySet().stream().collect(Collectors.toMap(av -> av.getKey().value(), av -> av.getValue())); for (ZoneVmSpecification zvs : zoneVmSpecifications.getItems()) { List<VmType> regionVmTypes = zvs.getVmTypes().stream().filter(vmTypeName -> vmTypeMap.containsKey(vmTypeName)) .map(vmTypeName -> vmTypeMap.get(vmTypeName)).collect(Collectors.toList()); List<AvailabilityZone> azs = azmap.get(zvs.getZone()); if (azs != null) { azs.forEach(zone -> vmTypesForZones.put(zone, regionVmTypes)); azs.forEach(zone -> defaultVmTypes.put(zone, vmTypeMap.get(zvs.getDefaultVmType()))); } } } catch (IOException e) { LOGGER.error("Cannot initialize platform parameters for arm", e); } Collections.sort(vmTypes, new StringTypesCompare()); } private VolumeParameterConfig volumeParameterConfig(ConfigSpecification configSpecification) { return new VolumeParameterConfig( VolumeParameterType.valueOf(configSpecification.getVolumeParameterType()), Integer.valueOf(configSpecification.getMinimumSize()), Integer.valueOf(configSpecification.getMaximumSize()), Integer.valueOf(configSpecification.getMinimumNumber()), configSpecification.getMaximumNumberWithLimit()); } private Map<Region, List<AvailabilityZone>> readRegions() { Map<Region, List<AvailabilityZone>> regions = new HashMap<>(); String zone = getDefinition(armZoneParameterDefinitionPath, "zone"); try { RegionsSpecification oRegions = JsonUtil.readValue(zone, RegionsSpecification.class); for (RegionSpecification regionSpecification : oRegions.getItems()) { List<AvailabilityZone> av = new ArrayList<>(); for (String s : regionSpecification.getZones()) { av.add(AvailabilityZone.availabilityZone(s)); } Collections.sort(av, new StringTypesCompare()); regions.put(Region.region(regionSpecification.getName()), av); } } catch (IOException e) { return regions; } return sortMap(regions); } private String getDefinition(String parameter, String type) { if (Strings.isNullOrEmpty(parameter)) { return resourceDefinition(type); } else { return FileReaderUtils.readFileFromClasspathQuietly(parameter); } } @Override public ScriptParams scriptParams() { return SCRIPT_PARAMS; } @Override public DiskTypes diskTypes() { return new DiskTypes(getDiskTypes(), defaultDiskType(), diskMappings()); } @Override public Regions regions() { return new Regions(regions.keySet(), defaultRegion); } @Override public AvailabilityZones availabilityZones() { return new AvailabilityZones(regions); } private Collection<DiskType> getDiskTypes() { Collection<DiskType> disks = Lists.newArrayList(); for (AzureDiskType diskType : AzureDiskType.values()) { disks.add(diskType(diskType.value())); } return disks; } private Map<String, VolumeParameterType> diskMappings() { Map<String, VolumeParameterType> map = new HashMap<>(); map.put(AzureDiskType.GEO_REDUNDANT.value(), VolumeParameterType.MAGNETIC); map.put(AzureDiskType.LOCALLY_REDUNDANT.value(), VolumeParameterType.MAGNETIC); map.put(AzureDiskType.PREMIUM_LOCALLY_REDUNDANT.value(), VolumeParameterType.MAGNETIC); return map; } private DiskType defaultDiskType() { return diskType(AzureDiskType.LOCALLY_REDUNDANT.value()); } @Override public String resourceDefinition(String resource) { return FileReaderUtils.readFileFromClasspathQuietly("definitions/azure-" + resource + ".json"); } @Override public List<StackParamValidation> additionalStackParameters() { List<StackParamValidation> additionalStackParameterValidations = Lists.newArrayList(); additionalStackParameterValidations.add(new StackParamValidation(PlatformParametersConsts.TTL, false, String.class, Optional.absent())); additionalStackParameterValidations.add(new StackParamValidation("diskPerStorage", false, String.class, Optional.absent())); additionalStackParameterValidations.add(new StackParamValidation("persistentStorage", false, String.class, Optional.of("^[a-z0-9]{0,24}$"))); additionalStackParameterValidations.add(new StackParamValidation("attachedStorageOption", false, ArmAttachedStorageOption.class, Optional.absent())); return additionalStackParameterValidations; } @Override public VmTypes vmTypes(Boolean extended) { return new VmTypes(vmTypes, defaultVmType); } @Override public Map<AvailabilityZone, VmTypes> vmTypesPerAvailabilityZones(Boolean extended) { Map<AvailabilityZone, VmTypes> result = new HashMap<>(); for (Map.Entry<Region, List<AvailabilityZone>> zones : regions.entrySet()) { for (AvailabilityZone zone : zones.getValue()) { result.put(zone, new VmTypes(vmTypesForZones.get(zone), defaultVmTypes.get(zone))); } } return result; } @Override public PlatformOrchestrator orchestratorParams() { return new PlatformOrchestrator(Collections.singletonList(orchestrator(OrchestratorConstants.SALT)), orchestrator(OrchestratorConstants.SALT)); } @Override public PlatformImage images() { List<CustomImage> customImages = new ArrayList<>(); for (Map.Entry<Region, List<AvailabilityZone>> regionListEntry : regions.entrySet()) { String property = environment.getProperty("azure." + regionListEntry.getKey().value()); customImages.add(customImage(regionListEntry.getKey().value(), property)); } return new PlatformImage(customImages, imageRegex()); } @Override public String imageRegex() { return "^https://.*[.]vhd$"; } @Override public TagSpecification tagSpecification() { return tagSpecification; } }