func (n *assigner) getJobStaticIpsToReuse(previousInput bftinput.Input, jobName string, networkName string) []string {
	staticIps := []string{}

	previousJob, found := previousInput.FindJobByName(jobName)
	if !found {
		return staticIps
	}

	for _, jobNetwork := range previousJob.Networks {
		if jobNetwork.Name == networkName {
			for _, ip := range jobNetwork.StaticIps {
				staticIps = append(staticIps, ip)
			}
		}
	}

	if len(staticIps) == 0 {
		return staticIps
	}

	shuffledStaticIPsIdsx := rand.Perm(len(staticIps))
	ipsToReuse := rand.Intn(len(staticIps))

	shuffledStaticIps := []string{}

	for i := 0; i < ipsToReuse; i++ {
		shuffledStaticIps = append(shuffledStaticIps, staticIps[shuffledStaticIPsIdsx[i]])
	}

	return shuffledStaticIps
}
func (s *stemcellComparator) jobStemcellChanged(job bftinput.Job, currentInput bftinput.Input, mostRecentInput bftinput.Input) bool {
	prevJob, found := mostRecentInput.FindJobByName(job.Name)
	if !found {
		return false
	}

	var currentStemcell bftinput.StemcellConfig
	if job.Stemcell != "" {
		currentStemcell = s.findStemcellByAlias(job.Stemcell, currentInput)
	} else {
		currentStemcell = s.findResourcePoolStemcell(job.ResourcePool, currentInput)
	}

	if prevJob.Stemcell != "" {
		prevStemcell := s.findStemcellByAlias(prevJob.Stemcell, mostRecentInput)
		if prevStemcell.Version != currentStemcell.Version {
			s.logger.Debug("stemcell_comparator", "Stemcell versions don't match. Previous input: %#v, new input: %#v", mostRecentInput, currentInput)
			return true
		}
	} else {
		prevStemcell := s.findResourcePoolStemcell(prevJob.ResourcePool, mostRecentInput)
		if prevStemcell.Version != currentStemcell.Version {
			s.logger.Debug("stemcell_comparator", "Stemcell versions don't match. Previous input: %#v, new input: %#v", mostRecentInput, currentInput)
			return true
		}
	}

	return false
}
func (g *inputGenerator) generateInputWithJobNames(jobNames []string) bftinput.Input {
	input := bftinput.Input{
		Jobs: []bftinput.Job{},
	}
	for _, jobName := range jobNames {
		input.Jobs = append(input.Jobs, bftinput.Job{
			Name:      jobName,
			Instances: g.parameters.Instances[rand.Intn(len(g.parameters.Instances))],
		})
	}

	return input
}
func (f *fixedMigratedFrom) Apply(input bftinput.Input, previousInput bftinput.Input) bftinput.Input {
	for foundJobIdx, job := range input.Jobs {
		previousJob, found := previousInput.FindJobByName(job.Name)
		if found {
			if len(previousJob.AvailabilityZones) == 0 && len(job.AvailabilityZones) > 0 {
				staticIPs := f.sameStaticIps(job, previousJob, input)
				for _, ip := range staticIPs {
					f.assignMigratedFromBasedOnIp(ip, &input.Jobs[foundJobIdx])
				}
			}
		}
	}

	return input
}
func (s *FakeStemcell) Apply(input bftinput.Input, previousInput bftinput.Input) bftinput.Input {
	input.Stemcells = []bftinput.StemcellConfig{
		{Name: "fake-stemcell"},
	}

	return input
}
func (g *inputGenerator) fuzzInput(input bftinput.Input, previousInput bftinput.Input) bftinput.Input {
	input.CloudConfig = previousInput.CloudConfig
	input.Stemcells = previousInput.Stemcells

	input = g.parameterProvider.Get("availability_zone").Apply(input, previousInput)
	input = g.parameterProvider.Get("vm_type").Apply(input, previousInput)
	input = g.parameterProvider.Get("stemcell").Apply(input, previousInput)
	input = g.parameterProvider.Get("persistent_disk").Apply(input, previousInput)
	input = g.parameterProvider.Get("network").Apply(input, previousInput)
	input = g.parameterProvider.Get("template").Apply(input, previousInput)
	input = g.parameterProvider.Get("compilation").Apply(input, previousInput)
	input = g.parameterProvider.Get("update").Apply(input, previousInput)
	input = g.parameterProvider.Get("cloud_properties").Apply(input, previousInput)
	input = g.parameterProvider.Get("fixed_migrated_from").Apply(input, previousInput)

	return input
}
func (s *stemcell) Apply(input bftinput.Input, previousInput bftinput.Input) bftinput.Input {
	input.Stemcells = nil

	var stemcellConfig bftinput.StemcellConfig

	if s.definition == "os" {
		stemcellConfig = bftinput.StemcellConfig{
			OS: "toronto-os",
		}
	} else {
		stemcellConfig = bftinput.StemcellConfig{
			Name: "ubuntu-stemcell",
		}
	}

	usedStemcells := map[string]bool{}

	if len(input.CloudConfig.VmTypes) > 0 {
		for _, vmType := range input.CloudConfig.VmTypes {
			stemcellConfig.Version = s.stemcellVersions[rand.Intn(len(s.stemcellVersions))]
			stemcellConfig.Alias = fmt.Sprintf("stemcell-%s", stemcellConfig.Version)

			if usedStemcells[stemcellConfig.Alias] != true {
				input.Stemcells = append(input.Stemcells, stemcellConfig)
			}
			usedStemcells[stemcellConfig.Alias] = true

			for j := range input.Jobs {
				if input.Jobs[j].VmType == vmType.Name {
					input.Jobs[j].Stemcell = stemcellConfig.Alias
				}
			}
		}
	} else {
		for r, _ := range input.CloudConfig.ResourcePools {
			stemcellConfig.Version = s.stemcellVersions[rand.Intn(len(s.stemcellVersions))]
			input.CloudConfig.ResourcePools[r].Stemcell = stemcellConfig
		}

		for j := range input.Jobs {
			input.Jobs[j].Stemcell = ""
		}
	}

	return input
}
func (a *analyzer) isMigratingFromAzsToNoAzsAndReusingStaticIps(previousInput bftinput.Input, currentInput bftinput.Input) bool {
	for _, job := range currentInput.Jobs {
		previousJob, found := previousInput.FindJobByName(job.Name)
		if found && (len(previousJob.AvailabilityZones) > 0 && len(job.AvailabilityZones) == 0) {
			for _, network := range job.Networks {
				previousNetwork, networkFound := previousJob.FindNetworkByName(network.Name)
				if networkFound {
					for _, currentIP := range network.StaticIps {
						for _, prevIP := range previousNetwork.StaticIps {
							if prevIP == currentIP {
								return true
							}
						}
					}
				}
			}
		}
	}

	return false
}
func (g *inputGenerator) createInputFromPrevious(previousInput bftinput.Input) bftinput.Input {
	input := bftinput.Input{}

	for _, job := range previousInput.Jobs {
		job.Instances = g.parameters.Instances[rand.Intn(len(g.parameters.Instances))]
		job.MigratedFrom = nil

		input.Jobs = append(input.Jobs, job)
	}

	input.Jobs = g.randomizeJobs(input.Jobs)

	for j := range input.Jobs {
		migratedFromCount := g.parameters.MigratedFromCount[rand.Intn(len(g.parameters.MigratedFromCount))]
		for i := 0; i < migratedFromCount; i++ {
			migratedFromName := g.nameGenerator.Generate(10)
			input.Jobs[j].MigratedFrom = append(input.Jobs[j].MigratedFrom, bftinput.MigratedFromConfig{Name: migratedFromName})
		}
	}

	return input
}
func (f *fixedMigratedFrom) sameStaticIps(job bftinput.Job, previousJob bftinput.Job, input bftinput.Input) []staticIPInfo {
	ips := []staticIPInfo{}
	for _, network := range job.Networks {
		previousNetwork, networkFound := previousJob.FindNetworkByName(network.Name)
		if networkFound {
			for _, currentIP := range network.StaticIps {
				for _, prevIP := range previousNetwork.StaticIps {
					if prevIP == currentIP {
						cloudNetwork, cloudNetworkFound := input.FindNetworkByName(network.Name)
						if cloudNetworkFound {
							ip := staticIPInfo{
								IP:      currentIP,
								Network: cloudNetwork,
							}
							ips = append(ips, ip)
						}
					}
				}
			}
		}
	}
	return ips
}
func (n *nothingChangedComparator) nothingChanged(job bftinput.Job, currentInput bftinput.Input, previousInputs []bftinput.Input) bool {
	mostRecentInput := previousInputs[len(previousInputs)-1]

	prevJob, found := mostRecentInput.FindJobByName(job.Name)
	if !found {
		return false
	}

	if len(previousInputs) > 1 {
		inputBeforePrevious := previousInputs[len(previousInputs)-2]
		jobBeforePrevious, found := inputBeforePrevious.FindJobByName(job.Name)
		if found && jobBeforePrevious.HasPersistentDisk() && !prevJob.HasPersistentDisk() {
			return false
		}

		for _, migratedFromConfig := range prevJob.MigratedFrom {
			jobBeforePrevious, found := inputBeforePrevious.FindJobByName(migratedFromConfig.Name)
			if found && jobBeforePrevious.HasPersistentDisk() && !prevJob.HasPersistentDisk() {
				return false
			}
		}
	}

	if !prevJob.IsEqual(job) {
		return false
	}

	for _, azName := range job.AvailabilityZones {
		currentAz, _ := currentInput.FindAzByName(azName)
		prevAz, _ := mostRecentInput.FindAzByName(azName)
		if !currentAz.IsEqual(prevAz) {
			return false
		}
	}

	if job.PersistentDiskPool != "" {
		currentPersistentDiskPool, _ := currentInput.FindDiskPoolByName(job.PersistentDiskPool)
		prevPersistentDiskPool, _ := mostRecentInput.FindDiskPoolByName(job.PersistentDiskPool)
		if !currentPersistentDiskPool.IsEqual(prevPersistentDiskPool) {
			return false
		}
	}

	if job.PersistentDiskType != "" {
		currentPersistentDiskType, _ := currentInput.FindDiskTypeByName(job.PersistentDiskType)
		prevPersistentDiskType, _ := mostRecentInput.FindDiskTypeByName(job.PersistentDiskType)
		if !currentPersistentDiskType.IsEqual(prevPersistentDiskType) {
			return false
		}
	}

	if job.ResourcePool != "" {
		currentResourcePool, _ := currentInput.FindResourcePoolByName(job.ResourcePool)
		prevResourcePool, _ := mostRecentInput.FindResourcePoolByName(job.ResourcePool)
		if !currentResourcePool.IsEqual(prevResourcePool) {
			return false
		}
	}

	if job.VmType != "" {
		currentVmType, _ := currentInput.FindVmTypeByName(job.VmType)
		prevVmType, _ := mostRecentInput.FindVmTypeByName(job.VmType)
		if !currentVmType.IsEqual(prevVmType) {
			return false
		}
	}

	if job.Stemcell != "" {
		currentStemcell, _ := currentInput.FindStemcellByName(job.Stemcell)
		prevStemcell, _ := mostRecentInput.FindStemcellByName(job.Stemcell)
		if !currentStemcell.IsEqual(prevStemcell) {
			return false
		}
	}

	for _, jobNetwork := range job.Networks {
		currentNetwork, _ := currentInput.FindNetworkByName(jobNetwork.Name)
		prevNetwork, _ := mostRecentInput.FindNetworkByName(jobNetwork.Name)
		if !currentNetwork.IsEqual(prevNetwork) {
			return false
		}
	}

	return true
}
func (c *cloudProperties) Apply(input bftinput.Input, previousInput bftinput.Input) bftinput.Input {
	for i, subject := range input.CloudConfig.AvailabilityZones {
		prevSubject, found := previousInput.FindAzByName(subject.Name)
		input.CloudConfig.AvailabilityZones[i].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
	}

	for i, subject := range input.CloudConfig.VmTypes {
		prevSubject, found := previousInput.FindVmTypeByName(subject.Name)
		input.CloudConfig.VmTypes[i].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
	}

	for i, subject := range input.CloudConfig.PersistentDiskPools {
		prevSubject, found := previousInput.FindDiskPoolByName(subject.Name)
		input.CloudConfig.PersistentDiskPools[i].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
	}

	for i, subject := range input.CloudConfig.PersistentDiskTypes {
		prevSubject, found := previousInput.FindDiskPoolByName(subject.Name)
		input.CloudConfig.PersistentDiskTypes[i].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
	}

	for i, subject := range input.CloudConfig.ResourcePools {
		prevSubject, found := previousInput.FindResourcePoolByName(subject.Name)
		input.CloudConfig.ResourcePools[i].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
	}

	// we can't really detect when a previous input has used cloud properties since we could
	// validly used 0 properties
	input.CloudConfig.Compilation.CloudProperties = c.FuzzCloudProperties(true, previousInput.CloudConfig.Compilation.CloudProperties)

	for i, network := range input.CloudConfig.Networks {
		if network.Subnets != nil {
			for s, subject := range network.Subnets {
				if subject.IpPool != nil {
					// manual
					prevSubject, found := previousInput.FindSubnetByIpRange(subject.IpPool.IpRange)
					input.CloudConfig.Networks[i].Subnets[s].CloudProperties = c.FuzzCloudProperties(found, prevSubject.CloudProperties)
				} else {
					// dynamic
					input.CloudConfig.Networks[i].Subnets[s].CloudProperties = c.FuzzCloudProperties(false, map[string]string{})
				}
			}
		} else {
			// vip
			input.CloudConfig.Networks[i].CloudProperties = c.FuzzCloudProperties(false, map[string]string{})
		}
	}

	return input
}