blob: 72c98a56903977fa5e3b5f686057e7170f45fc89 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package main
import (
"csbench/domain"
"csbench/network"
"csbench/vm"
"csbench/volume"
"flag"
"fmt"
"io"
"math"
"math/rand"
"os"
"strings"
"time"
"csbench/apirunner"
"csbench/config"
log "github.com/sirupsen/logrus"
"github.com/apache/cloudstack-go/v2/cloudstack"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/montanaflynn/stats"
"github.com/sourcegraph/conc/pool"
)
var (
profiles = make(map[int]*config.Profile)
)
type Result struct {
Success bool
Duration float64
}
func init() {
logFile, err := os.OpenFile("csmetrics.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("Failed to create log file: %v", err)
}
mw := io.MultiWriter(os.Stdout, logFile)
log.SetOutput(mw)
}
func readConfigurations(configFile string) map[int]*config.Profile {
profiles, err := config.ReadProfiles(configFile)
if err != nil {
log.Fatal("Error reading profiles:", err)
}
return profiles
}
func logConfigurationDetails(profiles map[int]*config.Profile) {
apiURL := config.URL
iterations := config.Iterations
page := config.Page
pagesize := config.PageSize
host := config.Host
userProfileNames := make([]string, 0, len(profiles))
for _, profile := range profiles {
userProfileNames = append(userProfileNames, profile.Name)
}
fmt.Printf("\n\n\033[1;34mBenchmarking the CloudStack environment [%s] with the following configuration\033[0m\n\n", apiURL)
fmt.Printf("Management server : %s\n", host)
fmt.Printf("Roles : %s\n", strings.Join(userProfileNames, ","))
fmt.Printf("Iterations : %d\n", iterations)
fmt.Printf("Page : %d\n", page)
fmt.Printf("PageSize : %d\n\n", pagesize)
log.Infof("Found %d profiles in the configuration: ", len(profiles))
log.Infof("Management server : %s", host)
}
func logReport() {
fmt.Printf("\n\n\nLog file : csmetrics.log\n")
fmt.Printf("Reports directory per API : report/%s/\n", config.Host)
fmt.Printf("Number of APIs : %d\n", apirunner.APIscount)
fmt.Printf("Successful APIs : %d\n", apirunner.SuccessAPIs)
fmt.Printf("Failed APIs : %d\n", apirunner.FailedAPIs)
fmt.Printf("Time in seconds per API: %.2f (avg)\n", apirunner.TotalTime/float64(apirunner.APIscount))
fmt.Printf("\n\n\033[1;34m--------------------------------------------------------------------------------\033[0m\n" +
" Done with benchmarking\n" +
"\033[1;34m--------------------------------------------------------------------------------\033[0m\n\n")
}
func getSamples(results []*Result) (stats.Float64Data, stats.Float64Data, stats.Float64Data) {
var allExecutionsSample stats.Float64Data
var successfulExecutionSample stats.Float64Data
var failedExecutionSample stats.Float64Data
for _, result := range results {
duration := math.Round(result.Duration*1000) / 1000
allExecutionsSample = append(allExecutionsSample, duration)
if result.Success {
successfulExecutionSample = append(successfulExecutionSample, duration)
} else {
failedExecutionSample = append(failedExecutionSample, duration)
}
}
return allExecutionsSample, successfulExecutionSample, failedExecutionSample
}
func getRowFromSample(key string, sample stats.Float64Data) table.Row {
min, _ := sample.Min()
min = math.Round(min*1000) / 1000
max, _ := sample.Max()
max = math.Round(max*1000) / 1000
mean, _ := sample.Mean()
mean = math.Round(mean*1000) / 1000
median, _ := sample.Median()
median = math.Round(median*1000) / 1000
percentile90, _ := sample.Percentile(90)
percentile90 = math.Round(percentile90*1000) / 1000
percentile95, _ := sample.Percentile(95)
percentile95 = math.Round(percentile95*1000) / 1000
percentile99, _ := sample.Percentile(99)
percentile99 = math.Round(percentile99*1000) / 1000
return table.Row{key, len(sample), min, max, mean, median, percentile90, percentile95, percentile99}
}
/*
This function will generate a report with the following details:
1. Total Number of executions
2. Number of successful executions
3. Number of failed exections
4. Different statistics like min, max, avg, median, 90th percentile, 95th percentile, 99th percentile for above 3
Output format:
1. CSV
2. TSV
3. Table
*/
func generateReport(results map[string][]*Result, format string, outputFile string) {
fmt.Println("Generating report")
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Type", "Count", "Min", "Max", "Avg", "Median", "90th percentile", "95th percentile", "99th percentile"})
for key, result := range results {
allExecutionsSample, successfulExecutionSample, failedExecutionSample := getSamples(result)
t.AppendRow(getRowFromSample(fmt.Sprintf("%s - All", key), allExecutionsSample))
if failedExecutionSample.Len() != 0 {
t.AppendRow(getRowFromSample(fmt.Sprintf("%s - Successful", key), successfulExecutionSample))
t.AppendRow(getRowFromSample(fmt.Sprintf("%s - Failed", key), failedExecutionSample))
}
}
if outputFile != "" {
f, err := os.Create(outputFile)
if err != nil {
log.Error("Error creating file: ", err)
}
defer f.Close()
t.SetOutputMirror(f)
}
switch format {
case "csv":
t.RenderCSV()
case "tsv":
t.RenderTSV()
case "table":
t.Render()
}
}
func main() {
dbprofile := flag.Int("dbprofile", 0, "DB profile number")
create := flag.Bool("create", false, "Create resources. Specify at least one of the following options:\n\t"+
"-domain - Create subdomains and accounts\n\t"+
"-limits - Update limits to -1 for subdomains and accounts\n\t"+
"-network - Create shared network in all subdomains\n\t"+
"-vm - Deploy VMs in all networks in the subdomains\n\t"+
"-volume - Create and attach Volumes to VMs")
benchmark := flag.Bool("benchmark", false, "Benchmark list APIs")
domainFlag := flag.Bool("domain", false, "Works with -create & -teardown\n\t"+
"-create - Create subdomains and accounts\n\t"+
"-teardown - Delete all subdomains and accounts")
limitsFlag := flag.Bool("limits", false, "Update limits to -1 for subdomains and accounts")
networkFlag := flag.Bool("network", false, "Works with -create & -teardown\n\t"+
"-create - Create shared network in all subdomains\n\t"+
"-teardown - Delete all networks in the subdomains")
vmFlag := flag.Bool("vm", false, "Works with -create & -teardown\n\t"+
"-create - Deploy VMs in all networks in the subdomains\n\t"+
"-teardown - Delete all VMs in the subdomains")
volumeFlag := flag.Bool("volume", false, "Works with -create & -teardown\n\t"+
"-create - Create and attach Volumes to VMs\n\t"+
"-teardown - Delete all volumes in the subdomains")
vmAction := flag.String("vmaction", "", "Action to perform on VMs. Options:\n\t"+
"start - start all VMs\n\t"+
"stop - stop all VMs\n\t"+
"reboot - reboot all running VMs\n\t"+
"toggle - stop running VMs and start stopped VMs\n\t"+
"random - Randomly toggle VMs")
tearDown := flag.Bool("teardown", false, "Tear down resources. Specify at least one of the following options:\n\t"+
"-domain - Delete all subdomains and accounts\n\t"+
"-network - Delete all networks in the subdomains\n\t"+
"-vm - Delete all VMs in the subdomains\n\t"+
"-volume - Delete all volumes in the subdomains")
workers := flag.Int("workers", 10, "Number of workers to use while creating resources")
format := flag.String("format", "table", "Format of the report (csv, tsv, table). Valid only for create")
outputFile := flag.String("output", "", "Path to output file. Valid only for create")
configFile := flag.String("config", "config/config", "Path to config file")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Options:\n")
flag.PrintDefaults()
}
flag.Parse()
if !(*create || *benchmark || *tearDown || *vmAction != "") {
log.Fatal("Please provide one of the following options: -create, -benchmark, -vmaction, -teardown")
}
if *create && *tearDown && *vmAction == "" {
log.Fatal("Please provide only one of the following options: -create, -teardown, -vmaction")
}
if *vmAction != "" && !(*vmAction == "start" || *vmAction == "stop" || *vmAction == "reboot" || *vmAction == "toggle" || *vmAction == "random") {
log.Fatal("Invalid VM action. Please provide one of the following: start, stop, reboot, toggle, random")
}
if *create && !(*domainFlag || *limitsFlag || *networkFlag || *vmFlag || *volumeFlag) {
log.Fatal("Please provide one of the following options with create: -domain, -limits, -network, -vm, -volume")
}
if *tearDown && !(*domainFlag || *networkFlag || *vmFlag || *volumeFlag) {
log.Fatal("Please provide one of the following options with teardown: -domain, -network, -vm, -volume")
}
switch *format {
case "csv", "tsv", "table":
// valid format, continue
default:
log.Fatal("Invalid format. Please provide one of the following: csv, tsv, table")
}
if *dbprofile < 0 {
log.Fatal("Invalid DB profile number. Please provide a positive integer.")
}
profiles = readConfigurations(*configFile)
apiURL := config.URL
iterations := config.Iterations
page := config.Page
pagesize := config.PageSize
if *create {
results := createResources(domainFlag, limitsFlag, networkFlag, vmFlag, volumeFlag, workers)
generateReport(results, *format, *outputFile)
}
if *vmAction != "" {
results := executeVMAction(vmAction, workers)
generateReport(results, *format, *outputFile)
}
if *tearDown {
results := tearDownEnv(domainFlag, networkFlag, vmFlag, volumeFlag, workers)
generateReport(results, *format, *outputFile)
}
if *benchmark {
log.Infof("\nStarted benchmarking the CloudStack environment [%s]", apiURL)
logConfigurationDetails(profiles)
for i, profile := range profiles {
userProfileName := profile.Name
log.Infof("Using profile %d.%s for benchmarking", i, userProfileName)
fmt.Printf("\n\033[1;34m============================================================\033[0m\n")
fmt.Printf(" Profile: [%s]\n", userProfileName)
fmt.Printf("\033[1;34m============================================================\033[0m\n")
apirunner.RunAPIs(userProfileName, apiURL, profile.ApiKey, profile.SecretKey, profile.Expires, profile.SignatureVersion, iterations, page, pagesize, *dbprofile)
}
logReport()
log.Infof("Done with benchmarking the CloudStack environment [%s]", apiURL)
}
}
func executeVMAction(vmAction *string, workers *int) map[string][]*Result {
parentDomainId := config.ParentDomainId
var cs *cloudstack.CloudStackClient
workerPool := pool.NewWithResults[map[string]*Result]().WithMaxGoroutines(*workers)
for _, profile := range profiles {
if profile.Name == "admin" {
cs = cloudstack.NewAsyncClient(config.URL, profile.ApiKey, profile.SecretKey, false)
cs.Timeout(time.Duration(300 * time.Second))
}
}
if cs == nil {
log.Fatal("Failed to find admin profile")
}
log.Infof("Fetching all VMs in subdomains for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, config.ParentDomainId)
var allVMs []*cloudstack.VirtualMachine
for _, dmn := range domains {
vms, err := vm.ListVMs(cs, dmn.Id)
if err != nil {
log.Warn("Error listing VMs: ", err)
continue
}
allVMs = append(allVMs, vms...)
}
progressMarker := int(math.Max(float64(len(allVMs))/10.0, 5))
start := time.Now()
for i, virtualMachine := range allVMs {
virtualMachine := virtualMachine
if (i+1)%progressMarker == 0 {
log.Infof("Executed %d VMs", i+1)
}
if *vmAction == "random" && rand.Intn(100) < 50 {
continue
}
workerPool.Go(func() map[string]*Result {
taskStart := time.Now()
result := false
action := "skipped"
switch virtualMachine.State {
case "Running":
if *vmAction == "stop" || *vmAction == "toggle" || *vmAction == "random" {
err := vm.StopVM(cs, virtualMachine.Id)
result = err == nil
action = "stop"
} else if *vmAction == "reboot" {
err := vm.RebootVM(cs, virtualMachine.Id)
result = err == nil
action = "reboot"
}
case "Stopped":
if *vmAction == "start" || *vmAction == "toggle" || *vmAction == "random" {
err := vm.StartVM(cs, virtualMachine.Id)
result = err == nil
action = "start"
} else if *vmAction == "reboot" {
result = false
action = "stop"
}
}
return map[string]*Result{
action: {
Success: result,
Duration: time.Since(taskStart).Seconds(),
},
}
})
}
res := workerPool.Wait()
log.Infof("Executed %s on %d VMs in %.2f seconds", *vmAction, len(allVMs), time.Since(start).Seconds())
var results = make(map[string][]*Result)
for _, result := range res {
for key, value := range result {
key = "vmaction-" + key
if results[key] == nil {
results[key] = make([]*Result, 0)
}
results[key] = append(results[key], value)
}
}
return results
}
func createResources(domainFlag, limitsFlag, networkFlag, vmFlag, volumeFlag *bool, workers *int) map[string][]*Result {
apiURL := config.URL
for _, profile := range profiles {
if profile.Name == "admin" {
numNetworksPerDomain := config.NumNetworks
numVmsPerNetwork := config.NumVms
numVolumesPerVM := config.NumVolumes
cs := cloudstack.NewAsyncClient(apiURL, profile.ApiKey, profile.SecretKey, false)
var results = make(map[string][]*Result)
if *domainFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
if config.NumDomains > 0 {
results["domain"] = createDomains(workerPool, cs, config.ParentDomainId, config.NumDomains)
} else {
log.Warn("Number of domains (numdomains) is less than 1. Skipping domain creation")
}
}
if *limitsFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
results["limits"] = updateLimits(workerPool, cs, config.ParentDomainId)
}
if *networkFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
if numNetworksPerDomain > 0 {
results["network"] = createNetwork(workerPool, cs, config.ParentDomainId, numNetworksPerDomain)
} else {
log.Warn("Number of networks per domain (numnetworks) is less than 1. Skipping network creation")
}
}
if *vmFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
if numVmsPerNetwork > 0 {
results["vm"] = createVms(workerPool, cs, config.ParentDomainId, numVmsPerNetwork)
} else {
log.Warn("Number of VMs per network (numvms) is less than 1. Skipping VM creation")
}
}
if *volumeFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
if numVolumesPerVM > 0 {
results["volume"] = createVolumes(workerPool, cs, config.ParentDomainId, numVolumesPerVM)
} else {
log.Warn("Number of volumes per VM (numvolumes) is less than 1. Skipping volume creation")
}
}
return results
}
}
return nil
}
func createDomains(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string, count int) []*Result {
progressMarker := int(math.Max(float64(count)/10.0, 5))
start := time.Now()
log.Infof("Creating %d domains", count)
for i := 0; i < count; i++ {
if (i+1)%progressMarker == 0 {
log.Infof("Created %d domains", i+1)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
dmn, err := domain.CreateDomain(cs, parentDomainId)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
_, err = domain.CreateAccount(cs, dmn.Id)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Created %d domains in %.2f seconds", count, time.Since(start).Seconds())
return res
}
func updateLimits(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string) []*Result {
log.Infof("Fetching subdomains for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
accounts := make([]*cloudstack.Account, 0)
for _, dmn := range domains {
accounts = append(accounts, domain.ListAccounts(cs, dmn.Id)...)
}
progressMarker := int(math.Max(float64(len(accounts))/10.0, 5))
start := time.Now()
log.Infof("Updating limits for %d accounts", len(accounts))
for i, account := range accounts {
if (i+1)%progressMarker == 0 {
log.Infof("Updated limits for %d accounts", i+1)
}
account := account
workerPool.Go(func() *Result {
taskStart := time.Now()
resp := domain.UpdateLimits(cs, account)
return &Result{
Success: resp,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Updated limits for %d accounts in %.2f seconds", len(accounts), time.Since(start).Seconds())
return res
}
func createNetwork(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string, numNetworkPerDomain int) []*Result {
log.Infof("Fetching subdomains for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
progressMarker := int(math.Max(float64(len(domains)*numNetworkPerDomain)/10.0, 5))
counter := 0
start := time.Now()
log.Infof("Creating %d networks", len(domains)*numNetworkPerDomain)
for _, dmn := range domains {
for j := 1; j <= numNetworkPerDomain; j++ {
networkIdx := counter
dmn := dmn
counter++
if counter%progressMarker == 0 {
log.Infof("Created %d networks", counter)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
_, err := network.CreateNetwork(cs, dmn.Id, networkIdx)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
}
res := workerPool.Wait()
log.Infof("Created %d networks in %.2f seconds", len(domains)*numNetworkPerDomain, time.Since(start).Seconds())
return res
}
func createVms(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string, numVmPerNetwork int) []*Result {
log.Infof("Fetching subdomains & accounts for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
var accounts []*cloudstack.Account
for i := 0; i < len(domains); i++ {
account := domain.ListAccounts(cs, domains[i].Id)
accounts = append(accounts, account...)
}
domainIdAccountMapping := make(map[string]*cloudstack.Account)
for _, account := range accounts {
domainIdAccountMapping[account.Domainid] = account
}
log.Infof("Fetching networks for subdomains in domain %s", parentDomainId)
var allNetworks []*cloudstack.Network
for _, domain := range domains {
network, _ := network.ListNetworks(cs, domain.Id)
allNetworks = append(allNetworks, network...)
}
progressMarker := int(math.Max(float64(len(allNetworks)*numVmPerNetwork)/10.0, 5))
counter := 0
start := time.Now()
log.Infof("Creating %d VMs", len(allNetworks)*numVmPerNetwork)
for _, network := range allNetworks {
network := network
for j := 1; j <= numVmPerNetwork; j++ {
counter++
if counter%progressMarker == 0 {
log.Infof("Created %d VMs", counter)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
_, err := vm.DeployVm(cs, network.Domainid, network.Id, domainIdAccountMapping[network.Domainid].Name)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
}
res := workerPool.Wait()
log.Infof("Created %d VMs in %.2f seconds", len(allNetworks)*numVmPerNetwork, time.Since(start).Seconds())
return res
}
func createVolumes(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string, numVolumesPerVM int) []*Result {
log.Infof("Fetching all VMs in subdomains for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
var allVMs []*cloudstack.VirtualMachine
for _, dmn := range domains {
vms, err := vm.ListVMs(cs, dmn.Id)
if err != nil {
log.Warn("Error listing VMs: ", err)
continue
}
allVMs = append(allVMs, vms...)
}
progressMarker := int(math.Max(float64(len(allVMs)*numVolumesPerVM)/10.0, 5))
start := time.Now()
log.Infof("Creating %d volumes", len(allVMs)*numVolumesPerVM)
unsuitableVmCount := 0
counter := 0
for _, vm := range allVMs {
vm := vm
if vm.State != "Running" && vm.State != "Stopped" {
unsuitableVmCount++
continue
}
for j := 1; j <= numVolumesPerVM; j++ {
counter++
if counter%progressMarker == 0 {
log.Infof("Created %d volumes", counter)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
vol, err := volume.CreateVolume(cs, vm.Domainid, vm.Account)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
_, err = volume.AttachVolume(cs, vol.Id, vm.Id)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
}
if unsuitableVmCount > 0 {
log.Warnf("Found %d VMs in unsuitable state", unsuitableVmCount)
}
res := workerPool.Wait()
log.Infof("Created %d volumes in %.2f seconds", counter, time.Since(start).Seconds())
return res
}
func tearDownEnv(domainFlag, networkFlag, vmFlag, volumeFlag *bool, workers *int) map[string][]*Result {
apiURL := config.URL
for _, profile := range profiles {
userProfileName := profile.Name
if userProfileName == "admin" {
cs := cloudstack.NewAsyncClient(apiURL, profile.ApiKey, profile.SecretKey, false)
var results = make(map[string][]*Result)
if *vmFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
results["vm-destroy"] = destroyVms(workerPool, cs, config.ParentDomainId)
}
if *volumeFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
results["volume-delete"] = deleteVolumes(workerPool, cs, config.ParentDomainId)
}
if *networkFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
results["network-delete"] = deleteNetworks(workerPool, cs, config.ParentDomainId)
}
if *domainFlag {
workerPool := pool.NewWithResults[*Result]().WithMaxGoroutines(*workers)
results["domain-delete"] = deleteDomains(workerPool, cs, config.ParentDomainId)
}
return results
}
}
return nil
}
func destroyVms(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string) []*Result {
log.Infof("Fetching subdomains & accounts for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
var allVMs []*cloudstack.VirtualMachine
for _, dmn := range domains {
vms, err := vm.ListVMs(cs, dmn.Id)
if err != nil {
log.Warn("Error listing VMs: ", err)
continue
}
allVMs = append(allVMs, vms...)
}
progressMarker := int(math.Max(float64(len(allVMs))/10.0, 5))
start := time.Now()
log.Infof("Destroying %d VMs", len(allVMs))
for i, virtualMachine := range allVMs {
virtualMachine := virtualMachine
if i%progressMarker == 0 {
log.Infof("Destroyed %d VMs", i+1)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
err := vm.DestroyVm(cs, virtualMachine.Id)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Destroyed %d VMs in %.2f seconds", len(allVMs), time.Since(start).Seconds())
return res
}
func deleteNetworks(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string) []*Result {
log.Infof("Fetching subdomains & accounts for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
log.Infof("Fetching networks for subdomains in domain %s", parentDomainId)
var allNetworks []*cloudstack.Network
for _, domain := range domains {
network, _ := network.ListNetworks(cs, domain.Id)
allNetworks = append(allNetworks, network...)
}
progressMarker := int(math.Max(float64(len(allNetworks))/10.0, 5))
start := time.Now()
log.Infof("Deleting %d networks", len(allNetworks))
for i, net := range allNetworks {
net := net
if (i+1)%progressMarker == 0 {
log.Infof("Deleted %d networks", i+1)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
resp, err := network.DeleteNetwork(cs, net.Id)
if err != nil || !resp {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Deleted %d networks in %.2f seconds", len(allNetworks), time.Since(start).Seconds())
return res
}
func deleteVolumes(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string) []*Result {
log.Infof("Fetching subdomains & accounts for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
log.Infof("Fetching volumes for subdomains in domain %s", parentDomainId)
var allVolumes []*cloudstack.Volume
for _, domain := range domains {
volumes, _ := volume.ListVolumes(cs, domain.Id)
allVolumes = append(allVolumes, volumes...)
}
progressMarker := int(math.Max(float64(len(allVolumes))/10.0, 5))
start := time.Now()
log.Infof("Deleting %d volumes", len(allVolumes))
for i, vol := range allVolumes {
vol := vol
if (i+1)%progressMarker == 0 {
log.Infof("Deleted %d volumes", i+1)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
_, err := volume.DestroyVolume(cs, vol.Id)
if err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Deleted %d volumes in %.2f seconds", len(allVolumes), time.Since(start).Seconds())
return res
}
func deleteDomains(workerPool *pool.ResultPool[*Result], cs *cloudstack.CloudStackClient, parentDomainId string) []*Result {
log.Infof("Fetching subdomains for domain %s", parentDomainId)
domains := domain.ListSubDomains(cs, parentDomainId)
progressMarker := int(math.Max(float64(len(domains))/10.0, 5))
start := time.Now()
log.Infof("Deleting %d domains", len(domains))
for i, dmn := range domains {
dmn := dmn
if (i+1)%progressMarker == 0 {
log.Infof("Deleted %d domains", i+1)
}
workerPool.Go(func() *Result {
taskStart := time.Now()
resp, err := domain.DeleteDomain(cs, dmn.Id)
if !resp || err != nil {
return &Result{
Success: false,
Duration: time.Since(taskStart).Seconds(),
}
}
return &Result{
Success: true,
Duration: time.Since(taskStart).Seconds(),
}
})
}
res := workerPool.Wait()
log.Infof("Deleted %d domains in %.2f seconds", len(domains), time.Since(start).Seconds())
return res
}