/* Copyright (c) 2014-2015 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 mo import ( "context" "reflect" "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" ) func ignoreMissingProperty(ref types.ManagedObjectReference, p types.MissingProperty) bool { switch ref.Type { case "VirtualMachine": switch p.Path { case "environmentBrowser": // See https://github.com/vmware/govmomi/pull/242 return true case "alarmActionsEnabled": // Seen with vApp child VM return true } } return false } // ObjectContentToType loads an ObjectContent value into the value it // represents. If the ObjectContent value has a non-empty 'MissingSet' field, // it returns the first fault it finds there as error. If the 'MissingSet' // field is empty, it returns a pointer to a reflect.Value. It handles contain // nested properties, such as 'guest.ipAddress' or 'config.hardware'. func ObjectContentToType(o types.ObjectContent) (interface{}, error) { // Expect no properties in the missing set for _, p := range o.MissingSet { if ignoreMissingProperty(o.Obj, p) { continue } return nil, soap.WrapVimFault(p.Fault.Fault) } ti := typeInfoForType(o.Obj.Type) v, err := ti.LoadFromObjectContent(o) if err != nil { return nil, err } return v.Elem().Interface(), nil } // LoadRetrievePropertiesResponse converts the response of a call to // RetrieveProperties to one or more managed objects. func LoadRetrievePropertiesResponse(res *types.RetrievePropertiesResponse, dst interface{}) error { rt := reflect.TypeOf(dst) if rt == nil || rt.Kind() != reflect.Ptr { panic("need pointer") } rv := reflect.ValueOf(dst).Elem() if !rv.CanSet() { panic("cannot set dst") } isSlice := false switch rt.Elem().Kind() { case reflect.Struct: case reflect.Slice: isSlice = true default: panic("unexpected type") } if isSlice { for _, p := range res.Returnval { v, err := ObjectContentToType(p) if err != nil { return err } vt := reflect.TypeOf(v) if !rv.Type().AssignableTo(vt) { // For example: dst is []ManagedEntity, res is []HostSystem if field, ok := vt.FieldByName(rt.Elem().Elem().Name()); ok && field.Anonymous { rv.Set(reflect.Append(rv, reflect.ValueOf(v).FieldByIndex(field.Index))) continue } } rv.Set(reflect.Append(rv, reflect.ValueOf(v))) } } else { switch len(res.Returnval) { case 0: case 1: v, err := ObjectContentToType(res.Returnval[0]) if err != nil { return err } vt := reflect.TypeOf(v) if !rv.Type().AssignableTo(vt) { // For example: dst is ComputeResource, res is ClusterComputeResource if field, ok := vt.FieldByName(rt.Elem().Name()); ok && field.Anonymous { rv.Set(reflect.ValueOf(v).FieldByIndex(field.Index)) return nil } } rv.Set(reflect.ValueOf(v)) default: // If dst is not a slice, expect to receive 0 or 1 results panic("more than 1 result") } } return nil } // RetrievePropertiesForRequest calls the RetrieveProperties method with the // specified request and decodes the response struct into the value pointed to // by dst. func RetrievePropertiesForRequest(ctx context.Context, r soap.RoundTripper, req types.RetrieveProperties, dst interface{}) error { res, err := methods.RetrieveProperties(ctx, r, &req) if err != nil { return err } return LoadRetrievePropertiesResponse(res, dst) } // RetrieveProperties retrieves the properties of the managed object specified // as obj and decodes the response struct into the value pointed to by dst. func RetrieveProperties(ctx context.Context, r soap.RoundTripper, pc, obj types.ManagedObjectReference, dst interface{}) error { req := types.RetrieveProperties{ This: pc, SpecSet: []types.PropertyFilterSpec{ { ObjectSet: []types.ObjectSpec{ { Obj: obj, Skip: types.NewBool(false), }, }, PropSet: []types.PropertySpec{ { All: types.NewBool(true), Type: obj.Type, }, }, }, }, } return RetrievePropertiesForRequest(ctx, r, req, dst) }