Skip to content

Commit

Permalink
vcsim: add TaskManager.CreateTask support
Browse files Browse the repository at this point in the history
  • Loading branch information
dougm committed Mar 18, 2024
1 parent d9af2a2 commit 9c59e9c
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 4 deletions.
100 changes: 100 additions & 0 deletions govc/task/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Copyright (c) 2024-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.
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 task

import (
"context"
"flag"
"fmt"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/session"
"github.com/vmware/govmomi/task"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)

type create struct {
*flags.ClientFlag

obj string
}

func init() {
cli.Register("task.create", &create{}, true)
}

func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) {
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
cmd.ClientFlag.Register(ctx, f)

f.StringVar(&cmd.obj, "o", "", "ManagedObject with which Task will be associated")
}

func (cmd *create) Description() string {
return `Create task of type ID.
ID must be one of:
govc extension.info -json | jq -r '.extensions[].taskList | select(. != null) | .[].taskID'
Examples:
govc task.create $ID`
}

func (cmd *create) Usage() string {
return "ID"
}

func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error {
c, err := cmd.Client()
if err != nil {
return err
}

if f.NArg() != 1 {
return flag.ErrHelp
}

m := task.NewManager(c)

req := types.CreateTask{
This: m.Reference(),
Obj: c.ServiceContent.RootFolder,
TaskTypeId: f.Arg(0),
Cancelable: true,
}

if cmd.obj != "" {
req.Obj.FromString(cmd.obj)
}

s, err := session.NewManager(c).UserSession(ctx)
if err != nil {
return err
}
req.InitiatedBy = s.UserName

res, err := methods.CreateTask(ctx, c, &req)
if err != nil {
return err
}

fmt.Println(res.Returnval.Task)

return nil
}
114 changes: 114 additions & 0 deletions govc/task/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
Copyright (c) 2024-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.
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 task

import (
"context"
"flag"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)

type set struct {
*flags.ClientFlag

desc types.LocalizableMessage
state string
err string
progress int
}

func init() {
cli.Register("task.set", &set{}, true)
}

func (cmd *set) Register(ctx context.Context, f *flag.FlagSet) {
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
cmd.ClientFlag.Register(ctx, f)

f.StringVar(&cmd.desc.Key, "d", "", "Task description key")
f.StringVar(&cmd.desc.Message, "m", "", "Task description message")
f.StringVar(&cmd.state, "s", "", "Task state")
f.StringVar(&cmd.err, "e", "", "Task error")
f.IntVar(&cmd.progress, "p", 0, "Task progress")
}

func (cmd *set) Description() string {
return `Set task state.
Examples:
id=$(govc task.create com.vmware.govmomi.simulator.test)
govc task.set $id -s error`
}

func (cmd *set) Usage() string {
return "ID"
}

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

c, err := cmd.Client()
if err != nil {
return err
}

ref := types.ManagedObjectReference{Type: "Task"}
if !ref.FromString(f.Arg(0)) {
ref.Value = f.Arg(0)
}

task := object.NewTask(c, ref)

var fault *types.LocalizedMethodFault

if cmd.err != "" {
fault = &types.LocalizedMethodFault{
Fault: &types.SystemError{Reason: cmd.err},
LocalizedMessage: cmd.err,
}
cmd.state = string(types.TaskInfoStateError)
}

if cmd.state != "" {
err := task.SetState(ctx, types.TaskInfoState(cmd.state), nil, fault)
if err != nil {
return err
}
}

if cmd.progress != 0 {
err := task.UpdateProgress(ctx, cmd.progress)
if err != nil {
return err
}
}

if cmd.desc.Key != "" {
err := task.SetDescription(ctx, cmd.desc)
if err != nil {
return err
}
}

return nil
}
33 changes: 33 additions & 0 deletions govc/test/tasks.bats
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,36 @@ load test_helper
run govc tasks 'host/*'
assert_success
}

@test "task.create" {
vcsim_env

export GOVC_SHOW_UNRELEASED=true

run govc task.create enoent
assert_failure

id=$(govc extension.info -json | jq -r '.extensions[].taskList | select(. != null) | .[].taskID' | head -1)
assert_equal com.vmware.govmomi.simulator.test "$id"

run govc task.create "$id"
assert_success
task="$output"

run govc task.set -s running "$task"
assert_success

govc tasks | grep "$id"

run govc task.set -d "$id.init" -m "task init" "$task"
assert_success

run govc task.set -p 42 "$task"
assert_success

run govc task.set -s success "$task"
assert_success

run govc task.set -s running "$task"
assert_failure
}
94 changes: 92 additions & 2 deletions simulator/task.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2017 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
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,
Expand All @@ -22,7 +22,9 @@ import (
"strings"
"time"

"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)

Expand Down Expand Up @@ -181,3 +183,91 @@ func (t *Task) Wait() {
time.Sleep(10 * time.Millisecond)
}
}

func (t *Task) isDone() bool {
return t.Info.State == types.TaskInfoStateError || t.Info.State == types.TaskInfoStateSuccess
}

func (t *Task) SetTaskState(ctx *Context, req *types.SetTaskState) soap.HasFault {
body := new(methods.SetTaskStateBody)

if t.isDone() {
body.Fault_ = Fault("", new(types.InvalidState))
return body
}

changes := []types.PropertyChange{
{Name: "info.state", Val: req.State},
}

switch req.State {
case types.TaskInfoStateRunning:
changes = append(changes, types.PropertyChange{Name: "info.startTime", Val: time.Now()})
case types.TaskInfoStateError, types.TaskInfoStateSuccess:
changes = append(changes, types.PropertyChange{Name: "info.completeTime", Val: time.Now()})

if req.Fault != nil {
changes = append(changes, types.PropertyChange{Name: "info.error", Val: req.Fault})
}
if req.Result != nil {
changes = append(changes, types.PropertyChange{Name: "info.result", Val: req.Result})
}
}

ctx.Map.Update(t, changes)

body.Res = new(types.SetTaskStateResponse)
return body
}

func (t *Task) SetTaskDescription(ctx *Context, req *types.SetTaskDescription) soap.HasFault {
body := new(methods.SetTaskDescriptionBody)

if t.isDone() {
body.Fault_ = Fault("", new(types.InvalidState))
return body
}

ctx.Map.Update(t, []types.PropertyChange{{Name: "info.description", Val: req.Description}})

body.Res = new(types.SetTaskDescriptionResponse)
return body
}

func (t *Task) UpdateProgress(ctx *Context, req *types.UpdateProgress) soap.HasFault {
body := new(methods.UpdateProgressBody)

if t.Info.State != types.TaskInfoStateRunning {
body.Fault_ = Fault("", new(types.InvalidState))
return body
}

ctx.Map.Update(t, []types.PropertyChange{{Name: "info.progress", Val: req.PercentDone}})

body.Res = new(types.UpdateProgressResponse)
return body
}

func (t *Task) CancelTask(ctx *Context, req *types.CancelTask) soap.HasFault {
body := new(methods.CancelTaskBody)

if t.isDone() {
body.Fault_ = Fault("", new(types.InvalidState))
return body
}

changes := []types.PropertyChange{
{Name: "info.canceled", Val: true},
{Name: "info.completeTime", Val: time.Now()},
{Name: "info.state", Val: types.TaskInfoStateError},
{Name: "info.error", Val: &types.LocalizedMethodFault{
Fault: &types.RequestCanceled{},
LocalizedMessage: "The task was canceled by a user",
}},
}

ctx.Map.Update(t, changes)

body.Res = new(types.CancelTaskResponse)
return body
}
Loading

0 comments on commit 9c59e9c

Please sign in to comment.