X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/91ef36eeea827569ec3745696e233aad0f8063aa..0348478eba20e59c3305dd5eda702d9192b45058:/lib/costanalyzer/costanalyzer.go diff --git a/lib/costanalyzer/costanalyzer.go b/lib/costanalyzer/costanalyzer.go index d81ade607c..37e655e53a 100644 --- a/lib/costanalyzer/costanalyzer.go +++ b/lib/costanalyzer/costanalyzer.go @@ -35,6 +35,7 @@ type nodeInfo struct { // Modern ProviderType string Price float64 + Preemptible bool } type arrayFlags []string @@ -90,6 +91,12 @@ Usage: was fulfilled. This program uses the cost data stored at the time of the execution of the container, stored in the 'node.json' file in its log collection. + - if a container was run on a preemptible ("spot") instance, the cost data + reported by this program may be wildly inaccurate, because it does not have + access to the spot pricing in effect for the node then the container ran. The + UUID report file that is generated when the '-output' option is specified has + a column that indicates the preemptible state of the instance that ran the + container. In order to get the data for the uuids supplied, the ARVADOS_API_HOST and ARVADOS_API_TOKEN environment variables must be set. @@ -120,7 +127,7 @@ Options: if len(uuids) < 1 { flags.Usage() - err = fmt.Errorf("Error: no uuid(s) provided") + err = fmt.Errorf("error: no uuid(s) provided") exitCode = 2 return } @@ -181,7 +188,7 @@ func addContainerLine(logger *logrus.Logger, node nodeInfo, cr arvados.Container size = node.ProviderType } cost = delta.Seconds() / 3600 * price - csv += size + "," + strconv.FormatFloat(price, 'f', 8, 64) + "," + strconv.FormatFloat(cost, 'f', 8, 64) + "\n" + csv += size + "," + fmt.Sprintf("%+v", node.Preemptible) + "," + strconv.FormatFloat(price, 'f', 8, 64) + "," + strconv.FormatFloat(cost, 'f', 8, 64) + "\n" return } @@ -285,7 +292,7 @@ func loadObject(logger *logrus.Logger, ac *arvados.Client, path string, uuid str func getNode(arv *arvadosclient.ArvadosClient, ac *arvados.Client, kc *keepclient.KeepClient, cr arvados.ContainerRequest) (node nodeInfo, err error) { if cr.LogUUID == "" { - err = errors.New("No log collection") + err = errors.New("no log collection") return } @@ -369,7 +376,7 @@ func generateCrCsv(logger *logrus.Logger, uuid string, arv *arvadosclient.Arvado cost = make(map[string]float64) - csv := "CR UUID,CR name,Container UUID,State,Started At,Finished At,Duration in seconds,Compute node type,Hourly node cost,Total cost\n" + csv := "CR UUID,CR name,Container UUID,State,Started At,Finished At,Duration in seconds,Compute node type,Preemptible,Hourly node cost,Total cost\n" var tmpCsv string var tmpTotalCost float64 var totalCost float64 @@ -399,7 +406,7 @@ func generateCrCsv(logger *logrus.Logger, uuid string, arv *arvadosclient.Arvado return nil, fmt.Errorf("error loading cr object %s: %s", uuid, err) } var container arvados.Container - err = loadObject(logger, ac, uuid, cr.ContainerUUID, cache, &container) + err = loadObject(logger, ac, crUUID, cr.ContainerUUID, cache, &container) if err != nil { return nil, fmt.Errorf("error loading container object %s: %s", cr.ContainerUUID, err) } @@ -428,7 +435,7 @@ func generateCrCsv(logger *logrus.Logger, uuid string, arv *arvadosclient.Arvado if err != nil { return nil, fmt.Errorf("error querying container_requests: %s", err.Error()) } - logger.Infof("Collecting child containers for container request %s", uuid) + logger.Infof("Collecting child containers for container request %s", crUUID) for _, cr2 := range childCrs.Items { logger.Info(".") node, err := getNode(arv, ac, kc, cr2) @@ -437,7 +444,7 @@ func generateCrCsv(logger *logrus.Logger, uuid string, arv *arvadosclient.Arvado } logger.Debug("\nChild container: " + cr2.ContainerUUID + "\n") var c2 arvados.Container - err = loadObject(logger, ac, uuid, cr2.ContainerUUID, cache, &c2) + err = loadObject(logger, ac, cr.UUID, cr2.ContainerUUID, cache, &c2) if err != nil { return nil, fmt.Errorf("error loading object %s: %s", cr2.ContainerUUID, err) } @@ -452,7 +459,7 @@ func generateCrCsv(logger *logrus.Logger, uuid string, arv *arvadosclient.Arvado if resultsDir != "" { // Write the resulting CSV file - fName := resultsDir + "/" + uuid + ".csv" + fName := resultsDir + "/" + crUUID + ".csv" err = ioutil.WriteFile(fName, []byte(csv), 0644) if err != nil { return nil, fmt.Errorf("error writing file with path %s: %s", fName, err.Error()) @@ -509,7 +516,7 @@ func costanalyzer(prog string, args []string, loader *config.Loader, logger *log var crCsv map[string]float64 crCsv, err = generateCrCsv(logger, uuid, arv, ac, kc, resultsDir, cache) if err != nil { - err = fmt.Errorf("Error generating CSV for uuid %s: %s", uuid, err.Error()) + err = fmt.Errorf("error generating CSV for uuid %s: %s", uuid, err.Error()) exitcode = 2 return } @@ -521,9 +528,9 @@ func costanalyzer(prog string, args []string, loader *config.Loader, logger *log // It is identified by the user uuid. As such, cost analysis for the // "Home" project is not supported by this program. Skip this uuid, but // keep going. - logger.Errorf("Cost analysis is not supported for the 'Home' project: %s", uuid) + logger.Errorf("cost analysis is not supported for the 'Home' project: %s", uuid) } else { - logger.Errorf("This argument does not look like a uuid: %s\n", uuid) + logger.Errorf("this argument does not look like a uuid: %s\n", uuid) exitcode = 3 return } @@ -554,7 +561,7 @@ func costanalyzer(prog string, args []string, loader *config.Loader, logger *log aFile := resultsDir + "/" + time.Now().Format("2006-01-02-15-04-05") + "-aggregate-costaccounting.csv" err = ioutil.WriteFile(aFile, []byte(csv), 0644) if err != nil { - err = fmt.Errorf("Error writing file with path %s: %s", aFile, err.Error()) + err = fmt.Errorf("error writing file with path %s: %s", aFile, err.Error()) exitcode = 1 return }