| /* |
| 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 |
| } |
| |
| // ApplyPropertyChange converts the response of a call to WaitForUpdates |
| // and applies it to the given managed object. |
| func ApplyPropertyChange(obj Reference, changes []types.PropertyChange) { |
| t := typeInfoForType(obj.Reference().Type) |
| v := reflect.ValueOf(obj) |
| |
| for _, p := range changes { |
| rv, ok := t.props[p.Name] |
| if !ok { |
| continue |
| } |
| |
| assignValue(v, rv, reflect.ValueOf(p.Val)) |
| } |
| } |
| |
| // 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) |
| } |