Skip to content

Commit

Permalink
vcsim: add TaskHistoryCollector support
Browse files Browse the repository at this point in the history
refactor portions of 'EventHistoryCollector' to 'HistoryCollector' for use with 'TaskHistoryCollector'

govc: add history flags to 'tasks' command

Closes vmware#2567
  • Loading branch information
dougm committed Mar 18, 2024
1 parent d62c0ac commit a75f9ef
Show file tree
Hide file tree
Showing 9 changed files with 788 additions and 177 deletions.
139 changes: 126 additions & 13 deletions govc/task/recent.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2017-2023 VMware, Inc. All Rights Reserved.
Copyright (c) 2017-2024 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -22,11 +22,15 @@ import (
"fmt"
"io"
"strings"
"time"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/task"
"github.com/vmware/govmomi/view"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
Expand All @@ -37,6 +41,12 @@ type recent struct {
max int
follow bool
long bool

state flags.StringList
begin time.Duration
end time.Duration

plain bool
}

func init() {
Expand All @@ -50,6 +60,9 @@ func (cmd *recent) Register(ctx context.Context, f *flag.FlagSet) {
f.IntVar(&cmd.max, "n", 25, "Output the last N tasks")
f.BoolVar(&cmd.follow, "f", false, "Follow recent task updates")
f.BoolVar(&cmd.long, "l", false, "Use long task description")
f.Var(&cmd.state, "s", "Task states")
f.DurationVar(&cmd.begin, "b", 0, "Begin time of task history")
f.DurationVar(&cmd.end, "e", 0, "End time of task history")
}

func (cmd *recent) Description() string {
Expand All @@ -64,7 +77,10 @@ By default, all recent tasks are included (via TaskManager), but can be limited
to a specific inventory object.
Examples:
govc tasks
govc tasks # tasks completed within the past 10 minutes
govc tasks -b 2h # tasks completed within the past 2 hours
govc tasks -s queued -s running # incompleted tasks
govc tasks -s error -s success # completed tasks
govc tasks -f
govc tasks -f /dc1/host/cluster1`
}
Expand Down Expand Up @@ -101,7 +117,101 @@ func taskName(info *types.TaskInfo) string {
}
}

type history struct {
*task.HistoryCollector

cmd *recent
}

func (h *history) Collect(ctx context.Context, f func([]types.TaskInfo)) error {
for {
tasks, err := h.ReadNextTasks(ctx, 10)
if err != nil {
return err
}

if len(tasks) == 0 {
if h.cmd.follow {
// TODO: this only follows new events.
// need to watch TaskHistoryCollector.LatestPage for updates to existing Tasks
time.Sleep(time.Second)
continue
}
break
}

f(tasks)
}
return nil
}

type collector interface {
Collect(context.Context, func([]types.TaskInfo)) error
Destroy(context.Context) error
}

func (cmd *recent) newCollector(ctx context.Context, c *vim25.Client, ref *types.ManagedObjectReference) (collector, error) {
if cmd.begin == 0 && cmd.end == 0 {
if ref == nil {
ref = c.ServiceContent.TaskManager
}

v, err := view.NewManager(c).CreateTaskView(ctx, ref)
if err != nil {
return nil, err
}

v.Follow = cmd.follow && cmd.plain
return v, nil
}

m := task.NewManager(c)

if ref == nil {
ref = &c.ServiceContent.RootFolder
}

now, err := methods.GetCurrentTime(ctx, c) // vCenter server time (UTC)
if err != nil {
return nil, err
}

if cmd.begin == 0 {
cmd.begin = 10 * time.Minute
}

filter := types.TaskFilterSpec{
Entity: &types.TaskFilterSpecByEntity{
Entity: *ref,
Recursion: types.TaskFilterSpecRecursionOptionAll,
},
Time: &types.TaskFilterSpecByTime{
TimeType: types.TaskFilterSpecTimeOptionStartedTime,
BeginTime: types.NewTime(now.Add(cmd.begin * -1)),
},
}

for _, state := range cmd.state {
filter.State = append(filter.State, types.TaskInfoState(state))
}

if cmd.end != 0 {
filter.Time.EndTime = types.NewTime(now.Add(cmd.end * -1))
}

collector, err := m.CreateCollectorForTasks(ctx, filter)
if err != nil {
return nil, err
}

return &history{collector, cmd}, nil
}

func (cmd *recent) Run(ctx context.Context, f *flag.FlagSet) error {
if f.NArg() > 1 {
return flag.ErrHelp
}

c, err := cmd.Client()
if err != nil {
return err
Expand Down Expand Up @@ -134,31 +244,33 @@ func (cmd *recent) Run(ctx context.Context, f *flag.FlagSet) error {
}
}

watch := *m
var watch *types.ManagedObjectReference

if f.NArg() == 1 {
refs, merr := cmd.ManagedObjects(ctx, f.Args())
if merr != nil {
return merr
}
watch = refs[0]
if len(refs) != 1 {
return fmt.Errorf("%s matches %d objects", f.Arg(0), len(refs))
}
watch = &refs[0]
}

v, err := view.NewManager(c).CreateTaskView(ctx, &watch)
// writes dump/json/xml once even if follow is specified, otherwise syntax error occurs
cmd.plain = !(cmd.Dump || cmd.JSON || cmd.XML)

v, err := cmd.newCollector(ctx, c, watch)
if err != nil {
return nil
return err
}

defer func() {
_ = v.Destroy(context.Background())
}()

// writes dump/json/xml once even if follow is specified, otherwise syntax error occurs
writesPlain := !(cmd.Dump || cmd.JSON || cmd.XML)
v.Follow = cmd.follow && writesPlain

res := &taskResult{name: tn}
if writesPlain {
if cmd.plain {
res.WriteHeader(cmd.Out)
}

Expand Down Expand Up @@ -207,17 +319,18 @@ func (t *taskResult) Write(w io.Writer) error {
user = strings.TrimPrefix(user, "com.vmware.") // e.g. com.vmware.vsan.health
}

queued := info.QueueTime.Format(stamp)
queued := "-"
start := "-"
end := start

if info.StartTime != nil {
start = info.StartTime.Format(stamp)
queued = info.StartTime.Sub(info.QueueTime).Round(time.Millisecond).String()
}

msg := fmt.Sprintf("%2d%% %s", info.Progress, info.Task)

if info.CompleteTime != nil {
if info.CompleteTime != nil && info.StartTime != nil {
msg = info.CompleteTime.Sub(*info.StartTime).String()

if info.State == types.TaskInfoStateError {
Expand Down
2 changes: 1 addition & 1 deletion govc/test/object.bats
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ EOF

n=$(ls "$dir"/*.xml | wc -l)
rm -rf "$dir"
assert_equal 6 "$n"
assert_equal 10 "$n"
}

@test "tree" {
Expand Down
21 changes: 18 additions & 3 deletions govc/test/tasks.bats
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@ load test_helper

run govc tasks
assert_success
}

@test "tasks host" {
vcsim_env
run govc tasks vm/DC0_H0_VM0
assert_success

run govc tasks vm/DC0_H0_VM0 vm/DC0_H0_VM1
assert_failure # > 1 arg

run govc tasks 'host/*'
assert_failure # matches 2 objects

run govc tasks -b 1h
assert_success
}

@test "tasks esx" {
vcsim_env -esx

run govc tasks
assert_success

run govc tasks -b 1h
assert_failure # TaskHistoryCollector not supported on ESX
}
4 changes: 2 additions & 2 deletions govc/test/vcsim.bats
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ EOF

# compile + run examples against vcsim
for main in ../../examples/*/main.go ; do
# TODO: #2567 #2476
if [[ $main =~ task|alarm ]]; then
# TODO: #2476
if [[ $main =~ alarm ]]; then
continue
fi
run go run "$main" -insecure -url "$GOVC_URL"
Expand Down
Loading

0 comments on commit a75f9ef

Please sign in to comment.