Update vmware/govmomi vendor: add vapi package

Zones implementation for vSphere cloud provider needs dependencies
which are not included in current vmware/govmomi vendor. So this
update added "vapi" package to support zones.
This commit is contained in:
jiatongw
2018-08-03 13:24:51 -07:00
parent 99abd4bc79
commit 5c44fd871f
41 changed files with 2046 additions and 283 deletions

View File

@@ -106,11 +106,10 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
switch types.ConfigSpecOperation(member.Operation) {
case types.ConfigSpecOperationAdd:
if FindReference(host.Network, s.Self) != nil {
if FindReference(s.Summary.HostMember, member.Host) != nil {
return nil, &types.AlreadyExists{Name: host.Name}
}
Map.AppendReference(host, &host.Network, s.Self)
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
@@ -129,8 +128,7 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
Map.RemoveReference(host, &host.Network, s.Self)
RemoveReference(&s.Summary.HostMember, s.Self)
RemoveReference(&s.Summary.HostMember, member.Host)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}

View File

@@ -161,6 +161,12 @@ var EventInfo = []types.EventDescriptionEventDetail{
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is starting",
},
{
Key: "VmStoppingEvent",
Description: "VM stopping",
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is stopping",
},
{
Key: "VmSuspendingEvent",
Description: "VM being suspended",

View File

@@ -157,15 +157,18 @@ func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFau
event.CreatedTime = time.Now()
event.UserName = ctx.Session.UserName
m.page = m.page.Next()
m.page = m.page.Prev()
m.page.Value = req.EventToPost
m.formatMessage(req.EventToPost)
for _, c := range m.collectors {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Next()
c.page.Value = event
}
ctx.WithLock(c, func() {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Prev()
c.page.Value = req.EventToPost
Map.Update(c, []types.PropertyChange{{Name: "latestPage", Val: c.GetLatestPage()}})
}
})
}
return &methods.PostEventBody{

View File

@@ -29,12 +29,10 @@ func (ds *Datastore) stat() error {
return err
}
bsize := uint64(stat.Bsize) / 512
info.FreeSpace = int64(stat.Bfree*bsize) >> 1
info.FreeSpace = int64(stat.Bfree * uint64(stat.Bsize))
ds.Summary.FreeSpace = info.FreeSpace
ds.Summary.Capacity = int64(stat.Blocks*bsize) >> 1
ds.Summary.Capacity = int64(stat.Blocks * uint64(stat.Bsize))
return nil
}

View File

@@ -17,11 +17,14 @@ limitations under the License.
package simulator
import (
"context"
"errors"
"log"
"path"
"reflect"
"strings"
"sync"
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
@@ -32,6 +35,11 @@ import (
type PropertyCollector struct {
mo.PropertyCollector
nopLocker
updates []types.ObjectUpdate
mu sync.Mutex
cancel context.CancelFunc
}
func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
@@ -72,6 +80,10 @@ func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, b
obj = o.Get()
}
return getManagedObject(obj), true
}
func getManagedObject(obj mo.Reference) reflect.Value {
rval := reflect.ValueOf(obj).Elem()
rtype := rval.Type()
@@ -82,26 +94,21 @@ func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, b
// for the case where the type has a field of the same name, for example:
// mo.ResourcePool.ResourcePool
for {
if path.Base(rtype.PkgPath()) != "mo" {
if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 {
log.Printf("%#v does not have an embedded mo type", ref)
return reflect.Value{}, false
}
rval = rval.Field(0)
rtype = rval.Type()
} else {
if path.Base(rtype.PkgPath()) == "mo" {
break
}
if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 {
log.Panicf("%#v does not have an embedded mo type", obj.Reference())
}
rval = rval.Field(0)
rtype = rval.Type()
}
return rval, true
return rval
}
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
if rval.Kind() == reflect.Ptr {
rval = rval.Elem()
}
// wrapValue converts slice types to the appropriate ArrayOf type used in property collector responses.
func wrapValue(rval reflect.Value, rtype reflect.Type) interface{} {
pval := rval.Interface()
if rval.Kind() == reflect.Slice {
@@ -128,7 +135,7 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
Long: v,
}
default:
kind := f.Type.Elem().Name()
kind := rtype.Elem().Name()
// Remove govmomi interface prefix name
if strings.HasPrefix(kind, "Base") {
kind = kind[4:]
@@ -143,6 +150,14 @@ func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{}
return pval
}
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
if rval.Kind() == reflect.Ptr {
rval = rval.Elem()
}
return wrapValue(rval, f.Type)
}
func fieldValue(rval reflect.Value, p string) (interface{}, error) {
var value interface{}
fields := strings.Split(p, ".")
@@ -401,7 +416,9 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
// Select object references
for _, spec := range r.SpecSet {
for _, o := range spec.ObjectSet {
rval, ok := getObject(ctx, o.Obj)
var rval reflect.Value
ok := false
ctx.WithLock(o.Obj, func() { rval, ok = getObject(ctx, o.Obj) })
if !ok {
if isFalse(spec.ReportMissingObjectsInResults) {
return nil, &types.ManagedObjectNotFound{Obj: o.Obj}
@@ -420,7 +437,7 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
}
for _, ref := range refs {
rr.collect(ctx, ref)
ctx.WithLock(ref, func() { rr.collect(ctx, ref) })
}
return rr.RetrieveResult, nil
@@ -429,7 +446,10 @@ func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx
func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault {
body := &methods.CreateFilterBody{}
filter := &PropertyFilter{pc: pc}
filter := &PropertyFilter{
pc: pc,
refs: make(map[types.ManagedObjectReference]struct{}),
}
filter.PartialUpdates = c.PartialUpdates
filter.Spec = c.Spec
@@ -455,14 +475,17 @@ func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.Crea
}
func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault {
pc.CancelWaitForUpdates(&types.CancelWaitForUpdates{This: c.This})
body := &methods.DestroyPropertyCollectorBody{}
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref})
filter.DestroyPropertyFilter(ctx, &types.DestroyPropertyFilter{This: ref})
}
ctx.Session.Remove(c.This)
ctx.Map.Remove(c.This)
body.Res = &types.DestroyPropertyCollectorResponse{}
@@ -519,24 +542,46 @@ func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveP
}
func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault {
pc.mu.Lock()
if pc.cancel != nil {
pc.cancel()
}
pc.mu.Unlock()
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)}
}
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
body := &methods.WaitForUpdatesExBody{}
func (pc *PropertyCollector) update(u types.ObjectUpdate) {
pc.mu.Lock()
pc.updates = append(pc.updates, u)
pc.mu.Unlock()
}
// At the moment we need to support Task completion. Handlers can simply set the Task
// state before returning and the non-incremental update is enough for the client.
// We can wait for incremental updates to simulate timeouts, etc.
if r.Version != "" {
body.Fault_ = Fault("incremental updates not supported yet", &types.NotSupported{})
return body
}
func (pc *PropertyCollector) PutObject(o mo.Reference) {
pc.update(types.ObjectUpdate{
Obj: o.Reference(),
Kind: types.ObjectUpdateKindEnter,
ChangeSet: nil,
})
}
update := &types.UpdateSet{
Version: "-",
}
func (pc *PropertyCollector) UpdateObject(o mo.Reference, changes []types.PropertyChange) {
pc.update(types.ObjectUpdate{
Obj: o.Reference(),
Kind: types.ObjectUpdateKindModify,
ChangeSet: changes,
})
}
func (pc *PropertyCollector) RemoveObject(ref types.ManagedObjectReference) {
pc.update(types.ObjectUpdate{
Obj: ref,
Kind: types.ObjectUpdateKindLeave,
ChangeSet: nil,
})
}
func (pc *PropertyCollector) apply(ctx *Context, update *types.UpdateSet) types.BaseMethodFault {
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
@@ -545,8 +590,7 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
return fault
}
fu := types.PropertyFilterUpdate{
@@ -554,6 +598,10 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
}
for _, o := range res.Objects {
if _, ok := filter.refs[o.Obj]; ok {
continue
}
filter.refs[o.Obj] = struct{}{}
ou := types.ObjectUpdate{
Obj: o.Obj,
Kind: types.ObjectUpdateKindEnter,
@@ -570,14 +618,122 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
fu.ObjectSet = append(fu.ObjectSet, ou)
}
update.FilterSet = append(update.FilterSet, fu)
if len(fu.ObjectSet) != 0 {
update.FilterSet = append(update.FilterSet, fu)
}
}
return nil
}
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
wait, cancel := context.WithCancel(context.Background())
if r.Options != nil {
if max := r.Options.MaxWaitSeconds; max != nil {
wait, cancel = context.WithTimeout(context.Background(), time.Second*time.Duration(*max))
}
}
pc.mu.Lock()
pc.cancel = cancel
pc.mu.Unlock()
body := &methods.WaitForUpdatesExBody{}
set := &types.UpdateSet{
Version: r.Version,
}
body.Res = &types.WaitForUpdatesExResponse{
Returnval: update,
Returnval: set,
}
return body
apply := func() bool {
if fault := pc.apply(ctx, set); fault != nil {
body.Fault_ = Fault("", fault)
body.Res = nil
return false
}
return true
}
if r.Version == "" {
apply() // Collect current state
set.Version = "-" // Next request with Version set will wait via loop below
ctx.Map.AddHandler(pc) // Listen for create, update, delete of managed objects
return body
}
ticker := time.NewTicker(250 * time.Millisecond) // allow for updates to accumulate
defer ticker.Stop()
// Start the wait loop, returning on one of:
// - Client calls CancelWaitForUpdates
// - MaxWaitSeconds was specified and has been exceeded
// - We have updates to send to the client
for {
select {
case <-wait.Done():
body.Res.Returnval = nil
switch wait.Err() {
case context.Canceled:
log.Printf("%s: WaitForUpdates canceled", pc.Self)
body.Fault_ = Fault("", new(types.RequestCanceled)) // CancelWaitForUpdates was called
body.Res = nil
case context.DeadlineExceeded:
log.Printf("%s: WaitForUpdates MaxWaitSeconds exceeded", pc.Self)
}
return body
case <-ticker.C:
pc.mu.Lock()
updates := pc.updates
pc.updates = nil // clear updates collected by the managed object CRUD listeners
pc.mu.Unlock()
if len(updates) == 0 {
continue
}
log.Printf("%s: applying %d updates to %d filters", pc.Self, len(updates), len(pc.Filter))
for _, f := range pc.Filter {
filter := ctx.Session.Get(f).(*PropertyFilter)
fu := types.PropertyFilterUpdate{Filter: f}
for _, update := range updates {
switch update.Kind {
case types.ObjectUpdateKindEnter: // Create
if !apply() {
return body
}
case types.ObjectUpdateKindModify: // Update
log.Printf("%s has %d changes", update.Obj, len(update.ChangeSet))
if !apply() { // An update may apply to collector traversal specs
return body
}
if _, ok := filter.refs[update.Obj]; ok {
// This object has already been applied by the filter,
// now check if the property spec applies for this update.
update = filter.apply(ctx, update)
if len(update.ChangeSet) != 0 {
fu.ObjectSet = append(fu.ObjectSet, update)
}
}
case types.ObjectUpdateKindLeave: // Delete
if _, ok := filter.refs[update.Obj]; !ok {
continue
}
delete(filter.refs, update.Obj)
fu.ObjectSet = append(fu.ObjectSet, update)
}
}
if len(fu.ObjectSet) != 0 {
set.FilterSet = append(set.FilterSet, fu)
}
}
if len(set.FilterSet) != 0 {
return body
}
}
}
}
// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment.

View File

@@ -17,6 +17,9 @@ limitations under the License.
package simulator
import (
"reflect"
"strings"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
@@ -26,17 +29,71 @@ import (
type PropertyFilter struct {
mo.PropertyFilter
pc *PropertyCollector
pc *PropertyCollector
refs map[types.ManagedObjectReference]struct{}
}
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
func (f *PropertyFilter) DestroyPropertyFilter(ctx *Context, c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{}
RemoveReference(&f.pc.Filter, c.This)
Map.Remove(c.This)
ctx.Session.Remove(c.This)
body.Res = &types.DestroyPropertyFilterResponse{}
return body
}
// matches returns true if the change matches one of the filter Spec.PropSet
func (f *PropertyFilter) matches(ctx *Context, ref types.ManagedObjectReference, change *types.PropertyChange) bool {
for _, p := range f.Spec.PropSet {
if p.Type != ref.Type {
continue
}
if isTrue(p.All) {
return true
}
for _, name := range p.PathSet {
if name == change.Name {
return true
}
// strings.HasPrefix("runtime.powerState", "runtime") == parent field matches
if strings.HasPrefix(change.Name, name) {
if obj := ctx.Map.Get(ref); obj != nil { // object may have since been deleted
change.Name = name
change.Val, _ = fieldValue(reflect.ValueOf(obj), name)
}
return true
}
}
}
return false
}
// apply the PropertyFilter.Spec to the given ObjectUpdate
func (f *PropertyFilter) apply(ctx *Context, change types.ObjectUpdate) types.ObjectUpdate {
parents := make(map[string]bool)
set := change.ChangeSet
change.ChangeSet = nil
for i, p := range set {
if f.matches(ctx, change.Obj, &p) {
if p.Name != set[i].Name {
// update matches a parent field from the spec.
if parents[p.Name] {
continue // only return 1 instance of the parent
}
parents[p.Name] = true
}
change.ChangeSet = append(change.ChangeSet, p)
}
}
return change
}

View File

@@ -23,6 +23,7 @@ import (
"reflect"
"strings"
"sync"
"sync/atomic"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
@@ -46,10 +47,11 @@ var refValueMap = map[string]string{
// Map is the default Registry instance.
var Map = NewRegistry()
// RegisterObject interface supports callbacks when objects are added and removed from the Registry
// RegisterObject interface supports callbacks when objects are created, updated and deleted from the Registry
type RegisterObject interface {
mo.Reference
PutObject(mo.Reference)
UpdateObject(mo.Reference, []types.PropertyChange)
RemoveObject(types.ManagedObjectReference)
}
@@ -59,7 +61,7 @@ type Registry struct {
objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
counter int64
Namespace string
Path string
@@ -112,8 +114,8 @@ func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference
}
if ref.Value == "" {
r.counter++
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), r.counter)
n := atomic.AddInt64(&r.counter, 1)
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), n)
}
return ref
@@ -126,7 +128,9 @@ func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectRefere
// AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) {
r.m.Lock()
r.handlers[h.Reference()] = h
r.m.Unlock()
}
// NewEntity sets Entity().Self with a new, unique Value.
@@ -173,10 +177,23 @@ func (r *Registry) Any(kind string) mo.Entity {
return nil
}
// applyHandlers calls the given func for each r.handlers
func (r *Registry) applyHandlers(f func(o RegisterObject)) {
r.m.Lock()
handlers := make([]RegisterObject, 0, len(r.handlers))
for _, handler := range r.handlers {
handlers = append(handlers, handler)
}
r.m.Unlock()
for i := range handlers {
f(handlers[i])
}
}
// Put adds a new object to Registry, generating a ManagedObjectReference if not already set.
func (r *Registry) Put(item mo.Reference) mo.Reference {
r.m.Lock()
defer r.m.Unlock()
ref := item.Reference()
if ref.Type == "" || ref.Value == "" {
@@ -192,25 +209,50 @@ func (r *Registry) Put(item mo.Reference) mo.Reference {
r.objects[ref] = item
for _, h := range r.handlers {
h.PutObject(item)
}
r.m.Unlock()
r.applyHandlers(func(o RegisterObject) {
o.PutObject(item)
})
return item
}
// Remove removes an object from the Registry.
func (r *Registry) Remove(item types.ManagedObjectReference) {
r.applyHandlers(func(o RegisterObject) {
o.RemoveObject(item)
})
r.m.Lock()
defer r.m.Unlock()
for _, h := range r.handlers {
h.RemoveObject(item)
}
delete(r.objects, item)
delete(r.handlers, item)
delete(r.locks, item)
r.m.Unlock()
}
// Update dispatches object property changes to RegisterObject handlers,
// such as any PropertyCollector instances with in-progress WaitForUpdates calls.
// The changes are also applied to the given object via mo.ApplyPropertyChange,
// so there is no need to set object fields directly.
func (r *Registry) Update(obj mo.Reference, changes []types.PropertyChange) {
for i := range changes {
if changes[i].Op == "" {
changes[i].Op = types.PropertyChangeOpAssign
}
if changes[i].Val != nil {
rval := reflect.ValueOf(changes[i].Val)
changes[i].Val = wrapValue(rval, rval.Type())
}
}
val := getManagedObject(obj).Addr().Interface().(mo.Reference)
mo.ApplyPropertyChange(val, changes)
r.applyHandlers(func(o RegisterObject) {
o.UpdateObject(val, changes)
})
}
// getEntityParent traverses up the inventory and returns the first object of type kind.
@@ -417,11 +459,23 @@ func (r *Registry) MarshalJSON() ([]byte, error) {
}
func (r *Registry) locker(obj mo.Reference) sync.Locker {
var ref types.ManagedObjectReference
switch x := obj.(type) {
case types.ManagedObjectReference:
ref = x
obj = r.Get(ref) // to check for sync.Locker
case *types.ManagedObjectReference:
ref = *x
obj = r.Get(ref) // to check for sync.Locker
default:
ref = obj.Reference()
}
if mu, ok := obj.(sync.Locker); ok {
return mu
}
ref := obj.Reference()
r.m.Lock()
mu, ok := r.locks[ref]
if !ok {
@@ -444,3 +498,9 @@ func (r *Registry) WithLock(obj mo.Reference, f func()) {
}
f()
}
// nopLocker can be embedded to opt-out of auto-locking (see Registry.WithLock)
type nopLocker struct{}
func (*nopLocker) Lock() {}
func (*nopLocker) Unlock() {}

View File

@@ -57,15 +57,18 @@ func (s *SearchIndex) FindByDatastorePath(r *types.FindByDatastorePath) soap.Has
func (s *SearchIndex) FindByInventoryPath(req *types.FindByInventoryPath) soap.HasFault {
body := &methods.FindByInventoryPathBody{Res: new(types.FindByInventoryPathResponse)}
path := strings.Split(req.InventoryPath, "/")
if len(path) <= 1 {
split := func(c rune) bool {
return c == '/'
}
path := strings.FieldsFunc(req.InventoryPath, split)
if len(path) < 1 {
return body
}
root := Map.content().RootFolder
o := &root
for _, name := range path[1:] {
for _, name := range path {
f := s.FindChild(&types.FindChild{Entity: *o, Name: name})
o = f.(*methods.FindChildBody).Res.Returnval
@@ -132,9 +135,16 @@ func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault {
if !ok {
continue
}
if vm.Config.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
if req.InstanceUuid != nil && *req.InstanceUuid {
if vm.Config.InstanceUuid == req.Uuid {
body.Res.Returnval = &ref
break
}
} else {
if vm.Config.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
}
}
}
} else {

View File

@@ -138,6 +138,12 @@ func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
session := ctx.Session
delete(s.sessions, session.Key)
for ref, obj := range ctx.Session.Registry.objects {
if _, ok := obj.(RegisterObject); ok {
ctx.Map.Remove(ref) // Remove RegisterObject handlers
}
}
ctx.postEvent(&types.UserLogoutSessionEvent{
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,

View File

@@ -390,7 +390,7 @@ func (s *Service) findDatastore(query url.Values) (*Datastore, error) {
ctx := context.Background()
finder := find.NewFinder(s.client, false)
dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcName"))
dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcPath"))
if err != nil {
return nil, err
}

View File

@@ -55,18 +55,19 @@ func CreateTask(e mo.Reference, name string, run func(*Task) (types.AnyType, typ
Execute: run,
}
Map.Put(task)
task.Self = Map.newReference(task)
task.Info.Key = task.Self.Value
task.Info.Task = task.Self
task.Info.Name = ucFirst(name)
task.Info.DescriptionId = fmt.Sprintf("%s.%s", ref.Type, id)
task.Info.Entity = &ref
task.Info.EntityName = ref.Value
task.Info.Reason = &types.TaskReasonUser{UserName: "vcsim"} // TODO: Context.Session.User
task.Info.QueueTime = time.Now()
task.Info.State = types.TaskInfoStateQueued
Map.Put(task)
return task
}
@@ -78,25 +79,31 @@ type TaskRunner interface {
func (t *Task) Run() types.ManagedObjectReference {
now := time.Now()
t.Info.StartTime = &now
t.Info.State = types.TaskInfoStateRunning
Map.Update(t, []types.PropertyChange{
{Name: "info.startTime", Val: now},
{Name: "info.state", Val: types.TaskInfoStateRunning},
})
res, err := t.Execute(t)
now = time.Now()
t.Info.CompleteTime = &now
state := types.TaskInfoStateSuccess
var fault interface{}
if err != nil {
t.Info.State = types.TaskInfoStateError
t.Info.Error = &types.LocalizedMethodFault{
state = types.TaskInfoStateError
fault = types.LocalizedMethodFault{
Fault: err,
LocalizedMessage: fmt.Sprintf("%T", err),
}
} else {
t.Info.Result = res
t.Info.State = types.TaskInfoStateSuccess
}
now = time.Now()
Map.Update(t, []types.PropertyChange{
{Name: "info.completeTime", Val: now},
{Name: "info.state", Val: state},
{Name: "info.result", Val: res},
{Name: "info.error", Val: fault},
})
return t.Self
}

View File

@@ -17,6 +17,8 @@ limitations under the License.
package simulator
import (
"sync"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
@@ -26,6 +28,7 @@ var recentTaskMax = 200 // the VC limit
type TaskManager struct {
mo.TaskManager
sync.Mutex
}
func NewTaskManager(ref types.ManagedObjectReference) object.Reference {
@@ -41,12 +44,16 @@ func (m *TaskManager) PutObject(obj mo.Reference) {
return
}
m.RecentTask = append(m.RecentTask, ref)
if len(m.RecentTask) > recentTaskMax {
m.RecentTask = m.RecentTask[1:]
m.Lock()
recent := append(m.RecentTask, ref)
if len(recent) > recentTaskMax {
recent = recent[1:]
}
Map.Update(m, []types.PropertyChange{{Name: "recentTask", Val: recent}})
m.Unlock()
}
func (m *TaskManager) RemoveObject(_ types.ManagedObjectReference) {
}
func (*TaskManager) RemoveObject(types.ManagedObjectReference) {}
func (*TaskManager) UpdateObject(mo.Reference, []types.PropertyChange) {}

View File

@@ -137,7 +137,8 @@ type ContainerView struct {
types map[string]bool
}
func (v *ContainerView) DestroyView(c *types.DestroyView) soap.HasFault {
func (v *ContainerView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
ctx.Session.Remove(c.This)
return destroyView(c.This)
}
@@ -192,3 +193,83 @@ func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectRefer
}
})
}
func (m *ViewManager) CreateListView(ctx *Context, req *types.CreateListView) soap.HasFault {
body := new(methods.CreateListViewBody)
list := new(ListView)
if err := list.add(req.Obj); err != nil {
body.Fault_ = Fault("", err)
return body
}
ctx.Session.Put(list)
body.Res = &types.CreateListViewResponse{
Returnval: list.Self,
}
return body
}
type ListView struct {
mo.ListView
}
func (v *ListView) update() {
Map.Update(v, []types.PropertyChange{{Name: "view", Val: v.View}})
}
func (v *ListView) add(refs []types.ManagedObjectReference) *types.ManagedObjectNotFound {
for _, ref := range refs {
obj := Map.Get(ref)
if obj == nil {
return &types.ManagedObjectNotFound{Obj: ref}
}
v.View = append(v.View, ref)
}
return nil
}
func (v *ListView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
ctx.Session.Remove(c.This)
return destroyView(c.This)
}
func (v *ListView) ModifyListView(req *types.ModifyListView) soap.HasFault {
body := new(methods.ModifyListViewBody)
for _, ref := range req.Remove {
RemoveReference(&v.View, ref)
}
if err := v.add(req.Add); err != nil {
body.Fault_ = Fault("", err)
return body
}
body.Res = new(types.ModifyListViewResponse)
if len(req.Remove) != 0 || len(req.Add) != 0 {
v.update()
}
return body
}
func (v *ListView) ResetListView(req *types.ResetListView) soap.HasFault {
body := new(methods.ResetListViewBody)
v.View = nil
if err := v.add(req.Obj); err != nil {
body.Fault_ = Fault("", err)
return body
}
body.Res = new(types.ResetListViewResponse)
v.update()
return body
}

View File

@@ -210,3 +210,10 @@ func (m *VirtualDiskManager) QueryVirtualDiskUuid(req *types.QueryVirtualDiskUui
return body
}
func (m *VirtualDiskManager) SetVirtualDiskUuid(req *types.SetVirtualDiskUuid) soap.HasFault {
body := new(methods.SetVirtualDiskUuidBody)
// TODO: validate uuid format and persist
body.Res = new(types.SetVirtualDiskUuidResponse)
return body
}

View File

@@ -444,6 +444,13 @@ func numberToString(n int64, sep rune) string {
return buf.String()
}
func getDiskSize(disk *types.VirtualDisk) int64 {
if disk.CapacityInBytes == 0 {
return disk.CapacityInKB * 1024
}
return disk.CapacityInBytes
}
func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault {
device := spec.Device
d := device.GetVirtualDevice()
@@ -518,9 +525,16 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
p, _ := parseDatastorePath(info.FileName)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
ds := Map.FindByName(p.Datastore, host.Datastore).Reference()
info.Datastore = &ds
entity := Map.FindByName(p.Datastore, host.Datastore)
ref := entity.Reference()
info.Datastore = &ref
ds := entity.(*Datastore)
// XXX: compare disk size and free space until windows stat is supported
ds.Summary.FreeSpace -= getDiskSize(x)
ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
}
}
@@ -556,6 +570,15 @@ func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *t
switch b := device.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
file = b.GetVirtualDeviceFileBackingInfo().FileName
p, _ := parseDatastorePath(file)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
ds := Map.FindByName(p.Datastore, host.Datastore).(*Datastore)
ds.Summary.FreeSpace += getDiskSize(device)
ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
}
if file != "" {
@@ -686,15 +709,9 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
}
}
c.VirtualMachine.Runtime.PowerState = c.state
c.VirtualMachine.Summary.Runtime.PowerState = c.state
bt := &c.VirtualMachine.Summary.Runtime.BootTime
var boot types.AnyType
if c.state == types.VirtualMachinePowerStatePoweredOn {
now := time.Now()
*bt = &now
} else {
*bt = nil
boot = time.Now()
}
event := c.event()
@@ -705,9 +722,23 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
&types.VmPoweredOnEvent{VmEvent: event},
)
case types.VirtualMachinePowerStatePoweredOff:
c.ctx.postEvent(&types.VmPoweredOffEvent{VmEvent: event})
c.ctx.postEvent(
&types.VmStoppingEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
case types.VirtualMachinePowerStateSuspended:
c.ctx.postEvent(
&types.VmSuspendingEvent{VmEvent: event},
&types.VmSuspendedEvent{VmEvent: event},
)
}
Map.Update(c.VirtualMachine, []types.PropertyChange{
{Name: "runtime.powerState", Val: c.state},
{Name: "summary.runtime.powerState", Val: c.state},
{Name: "summary.runtime.bootTime", Val: boot},
})
return nil, nil
}
@@ -739,6 +770,37 @@ func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task)
}
}
func (vm *VirtualMachine) SuspendVMTask(ctx *Context, req *types.SuspendVM_Task) soap.HasFault {
runner := &powerVMTask{vm, types.VirtualMachinePowerStateSuspended, ctx}
task := CreateTask(runner.Reference(), "suspend", runner.Run)
return &methods.SuspendVM_TaskBody{
Res: &types.SuspendVM_TaskResponse{
Returnval: task.Run(),
},
}
}
func (vm *VirtualMachine) ResetVMTask(ctx *Context, req *types.ResetVM_Task) soap.HasFault {
task := CreateTask(vm, "reset", func(task *Task) (types.AnyType, types.BaseMethodFault) {
res := vm.PowerOffVMTask(ctx, &types.PowerOffVM_Task{This: vm.Self})
ctask := Map.Get(res.(*methods.PowerOffVM_TaskBody).Res.Returnval).(*Task)
if ctask.Info.Error != nil {
return nil, ctask.Info.Error.Fault
}
_ = vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{This: vm.Self})
return nil, nil
})
return &methods.ResetVM_TaskBody{
Res: &types.ResetVM_TaskResponse{
Returnval: task.Run(),
},
}
}
func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
err := vm.configure(&req.Spec)
@@ -771,6 +833,11 @@ func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soa
return nil, r.Fault().VimFault().(types.BaseMethodFault)
}
// Remove all devices
devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
spec, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
vm.configureDevices(&types.VirtualMachineConfigSpec{DeviceChange: spec})
// Delete VM files from the datastore (ignoring result for now)
m := Map.FileManager()
dc := Map.getEntityDatacenter(vm).Reference()
@@ -907,29 +974,36 @@ func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soa
func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFault {
task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
var changes []types.PropertyChange
if ref := req.Spec.Datastore; ref != nil {
ds := Map.Get(*ref).(*Datastore)
Map.RemoveReference(ds, &ds.Vm, *ref)
vm.Datastore = []types.ManagedObjectReference{*ref}
// TODO: migrate vm.Config.Files (and vm.Summary.Config.VmPathName)
changes = append(changes, types.PropertyChange{Name: "datastore", Val: []types.ManagedObjectReference{*ref}})
}
if ref := req.Spec.Pool; ref != nil {
pool := Map.Get(*ref).(*ResourcePool)
Map.RemoveReference(pool, &pool.Vm, *ref)
vm.ResourcePool = ref
changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: *ref})
}
if ref := req.Spec.Host; ref != nil {
host := Map.Get(*ref).(*HostSystem)
Map.RemoveReference(host, &host.Vm, *ref)
vm.Runtime.Host = ref
changes = append(changes,
types.PropertyChange{Name: "runtime.host", Val: *ref},
types.PropertyChange{Name: "summary.runtime.host", Val: *ref},
)
}
Map.Update(vm, changes)
return nil, nil
})
@@ -1032,7 +1106,7 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T
}
}
func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) soap.HasFault {
r := &methods.ShutdownGuestBody{}
// should be poweron
if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff {
@@ -1047,6 +1121,17 @@ func (vm *VirtualMachine) ShutdownGuest(c *types.ShutdownGuest) soap.HasFault {
vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
vm.Summary.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
event := vm.event()
ctx.postEvent(
&types.VmGuestShutdownEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
Map.Update(vm, []types.PropertyChange{
{Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
})
r.Res = new(types.ShutdownGuestResponse)
return r