in cmd/copy.go [1742:1880]
func (cca *CookedCopyCmdArgs) ReportProgressOrExit(lcm common.LifecycleMgr) (totalKnownCount uint32) {
// fetch a job status
var summary common.ListJobSummaryResponse
Rpc(common.ERpcCmd.ListJobSummary(), &cca.jobID, &summary)
glcmSwapOnce.Do(func() {
Rpc(common.ERpcCmd.GetJobLCMWrapper(), &cca.jobID, &glcm)
})
summary.IsCleanupJob = cca.isCleanupJob // only FE knows this, so we can only set it here
cleanupStatusString := fmt.Sprintf("Cleanup %v/%v", summary.TransfersCompleted, summary.TotalTransfers)
jobDone := summary.JobStatus.IsJobDone()
totalKnownCount = summary.TotalTransfers
// if json is not desired, and job is done, then we generate a special end message to conclude the job
duration := time.Since(cca.jobStartTime) // report the total run time of the job
var computeThroughput = func() float64 {
// compute the average throughput for the last time interval
bytesInMb := float64(float64(summary.BytesOverWire-cca.intervalBytesTransferred) / float64(base10Mega))
timeElapsed := time.Since(cca.intervalStartTime).Seconds()
// reset the interval timer and byte count
cca.intervalStartTime = time.Now()
cca.intervalBytesTransferred = summary.BytesOverWire
return common.Iff(timeElapsed != 0, bytesInMb/timeElapsed, 0) * 8
}
glcm.Progress(func(format common.OutputFormat) string {
if format == common.EOutputFormat.Json() {
jsonOutput, err := json.Marshal(summary)
common.PanicIfErr(err)
return string(jsonOutput)
} else {
// abbreviated output for cleanup jobs
if cca.isCleanupJob {
return cleanupStatusString
}
// if json is not needed, then we generate a message that goes nicely on the same line
// display a scanning keyword if the job is not completely ordered
var scanningString = " (scanning...)"
if summary.CompleteJobOrdered {
scanningString = ""
}
throughput := computeThroughput()
throughputString := fmt.Sprintf("2-sec Throughput (Mb/s): %v", jobsAdmin.ToFixed(throughput, 4))
if throughput == 0 {
// As there would be case when no bits sent from local, e.g. service side copy, when throughput = 0, hide it.
throughputString = ""
}
// indicate whether constrained by disk or not
isBenchmark := cca.FromTo.From() == common.ELocation.Benchmark()
perfString, diskString := getPerfDisplayText(summary.PerfStrings, summary.PerfConstraint, duration, isBenchmark)
return fmt.Sprintf("%.1f %%, %v Done, %v Failed, %v Pending, %v Skipped, %v Total%s, %s%s%s",
summary.PercentComplete,
summary.TransfersCompleted,
summary.TransfersFailed,
summary.TotalTransfers-(summary.TransfersCompleted+summary.TransfersFailed+summary.TransfersSkipped),
summary.TransfersSkipped, summary.TotalTransfers, scanningString, perfString, throughputString, diskString)
}
})
if jobDone {
exitCode := cca.getSuccessExitCode()
if summary.TransfersFailed > 0 || summary.JobStatus == common.EJobStatus.Cancelled() || summary.JobStatus == common.EJobStatus.Cancelling() {
exitCode = common.EExitCode.Error()
}
builder := func(format common.OutputFormat) string {
if format == common.EOutputFormat.Json() {
jsonOutput, err := json.Marshal(summary)
common.PanicIfErr(err)
return string(jsonOutput)
} else {
screenStats, logStats := formatExtraStats(cca.FromTo, summary.AverageIOPS, summary.AverageE2EMilliseconds, summary.NetworkErrorPercentage, summary.ServerBusyPercentage)
output := fmt.Sprintf(
`
Job %s summary
Elapsed Time (Minutes): %v
Number of File Transfers: %v
Number of Folder Property Transfers: %v
Number of Symlink Transfers: %v
Total Number of Transfers: %v
Number of File Transfers Completed: %v
Number of Folder Transfers Completed: %v
Number of File Transfers Failed: %v
Number of Folder Transfers Failed: %v
Number of File Transfers Skipped: %v
Number of Folder Transfers Skipped: %v
Total Number of Bytes Transferred: %v
Final Job Status: %v%s%s
`,
summary.JobID.String(),
jobsAdmin.ToFixed(duration.Minutes(), 4),
summary.FileTransfers,
summary.FolderPropertyTransfers,
summary.SymlinkTransfers,
summary.TotalTransfers,
summary.TransfersCompleted-summary.FoldersCompleted,
summary.FoldersCompleted,
summary.TransfersFailed-summary.FoldersFailed,
summary.FoldersFailed,
summary.TransfersSkipped-summary.FoldersSkipped,
summary.FoldersSkipped,
summary.TotalBytesTransferred,
summary.JobStatus,
screenStats,
formatPerfAdvice(summary.PerformanceAdvice))
// abbreviated output for cleanup jobs
if cca.isCleanupJob {
output = fmt.Sprintf("%s: %s)", cleanupStatusString, summary.JobStatus)
}
// log to job log
jobMan, exists := jobsAdmin.JobsAdmin.JobMgr(summary.JobID)
if exists {
// Passing this as LogError ensures the stats are always logged.
jobMan.Log(common.LogError, logStats+"\n"+output)
}
return output
}
}
if cca.hasFollowup() {
lcm.Exit(builder, common.EExitCode.NoExit()) // leave the app running to process the followup
cca.launchFollowup(exitCode)
lcm.SurrenderControl() // the followup job will run on its own goroutines
} else {
lcm.Exit(builder, exitCode)
}
}
return
}