func()

in src/go/cmd/vmscaler/vmscaler/vmscaler.go [284:417]


func (v *VMScaler) createPlan(firstRun bool) (*Plan, error) {
	log.Debug.Printf("[VMScaler.createPlan")
	defer log.Debug.Printf("VMScaler.createPlan]")

	var vmssMap map[string]compute.VirtualMachineScaleSet
	vmssMap = make(map[string]compute.VirtualMachineScaleSet)
	var vmssAtCapacity []string
	var vmssToSeal []string
	var vmssToIncrease []string
	var newVMSSNames []string
	var vmssToDelete []string

	currentCapacity := int64(0)
	increasedCapacity := int64(0)

	// read all VMSS resources in the resource group
	vmssList, err := v.vmssClient.ListVMSS(v.ResourceGroup)
	if err != nil {
		return nil, err
	}

	// get the current capacity of all VMSS
	for _, element := range vmssList {
		currentCapacity += *element.Sku.Capacity
	}

	// on first run, we set date on all unsealed VMSS.  This ensures
	// no pre-mature sealing
	if firstRun {
		for _, element := range vmssList {
			if _, ok := element.Tags[SEALED_TAG_KEY]; ok {
				log.Debug.Printf("vmss '%s' is sealed", *element.Name)
				continue
			}
			vmssMap[*element.Name] = element
			vmssAtCapacity = append(vmssAtCapacity, *element.Name)
		}
		plan := &Plan{
			VmssMap:           vmssMap,
			CurrentCapacity:   currentCapacity,
			IncreasedCapacity: increasedCapacity,
			VmssAtCapacity:    vmssAtCapacity,
			VMSSToSeal:        vmssToSeal,
			VMSSToIncrease:    vmssToIncrease,
			VMSSToDelete:      vmssToDelete,
			NewVMSSNames:      newVMSSNames,
		}

		plan.Print(v)

		return plan, nil
	}

	for _, element := range vmssList {
		vmssMap[*element.Name] = element

		if !v.vmssOpManager.IsComplete(*element.Name) {
			log.Info.Printf("skipping vmss '%s', operation still pending", *element.Name)
			continue
		}

		// delete 0 capacity VMSS
		if *element.Sku.Capacity == 0 {
			log.Info.Printf("need to delete %s", *element.Name)
			vmssToDelete = append(vmssToDelete, *element.Name)
			continue
		}

		// skip operations on sealed VMSS
		if _, ok := element.Tags[SEALED_TAG_KEY]; ok {
			log.Debug.Printf("vmss '%s' is sealed", *element.Name)
			continue
		}

		// check VMSS at capacity
		if *element.Sku.Capacity >= v.VMsPerVMSS {
			vmssAtCapacity = append(vmssAtCapacity, *element.Name)
			continue
		}

		// check if VMSS needs to be sealed
		if val, ok := element.Tags[LAST_TIME_AT_CAPACITY_TAG_KEY]; ok {
			var lastTimeAtCapacity time.Time
			if err := lastTimeAtCapacity.UnmarshalText([]byte(*val)); err != nil {
				log.Error.Printf("vmss '%s' has a bad tag", *element.Name)
			} else if time.Since(lastTimeAtCapacity) > timeToSeal {
				vmssToSeal = append(vmssToSeal, *element.Name)
				continue
			}
		}

		// check if VMSS can be increased
		if *element.VirtualMachineScaleSetProperties.ProvisioningState == string(computesvc.ProvisioningStateSucceeded) {
			if (currentCapacity + increasedCapacity) < v.TotalNodes {
				vmssToIncrease = append(vmssToIncrease, *element.Name)
				increasedCapacity += (v.VMsPerVMSS - *element.Sku.Capacity)
			} else {
				// we have enough nodes, but we should update the timestamp, so we do not seal
				vmssAtCapacity = append(vmssAtCapacity, *element.Name)
			}
			continue
		}
	}

	remainingVMs := v.TotalNodes - (currentCapacity + increasedCapacity)
	if remainingVMs > 0 {
		// creating the new VMSS
		newVmssCount := (remainingVMs / v.VMsPerVMSS)
		if (remainingVMs % v.VMsPerVMSS) > 0 {
			newVmssCount++
		}
		for i := 0; int64(len(newVMSSNames)) < newVmssCount; i++ {
			vmssName := fmt.Sprintf("%s%d", VMSS_PREFIX, i)
			if _, ok := vmssMap[vmssName]; !ok {
				newVMSSNames = append(newVMSSNames, vmssName)
			}
		}
	}

	plan := &Plan{
		VmssMap:           vmssMap,
		CurrentCapacity:   currentCapacity,
		IncreasedCapacity: increasedCapacity,
		VmssAtCapacity:    vmssAtCapacity,
		VMSSToSeal:        vmssToSeal,
		VMSSToIncrease:    vmssToIncrease,
		VMSSToDelete:      vmssToDelete,
		NewVMSSNames:      newVMSSNames,
	}

	plan.Print(v)

	return plan, nil
}