github.com/vmware/govmomi@v0.51.0/vim25/mo/retrieve.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package mo 6 7 import ( 8 "context" 9 "reflect" 10 11 "github.com/vmware/govmomi/vim25/methods" 12 "github.com/vmware/govmomi/vim25/soap" 13 "github.com/vmware/govmomi/vim25/types" 14 ) 15 16 func ignoreMissingProperty(ref types.ManagedObjectReference, p types.MissingProperty) bool { 17 switch ref.Type { 18 case "VirtualMachine": 19 switch p.Path { 20 case "environmentBrowser": 21 // See https://github.com/vmware/govmomi/pull/242 22 return true 23 case "alarmActionsEnabled": 24 // Seen with vApp child VM 25 return true 26 } 27 case "ResourcePool": 28 switch p.Path { 29 case "resourceConfigSpecDetailed": 30 return true 31 } 32 } 33 34 return false 35 } 36 37 // ObjectContentToType loads an ObjectContent value into the value it 38 // represents. If the ObjectContent value has a non-empty 'MissingSet' field, 39 // it returns the first fault it finds there as error. If the 'MissingSet' 40 // field is empty, it returns a pointer to a reflect.Value. It handles contain 41 // nested properties, such as 'guest.ipAddress' or 'config.hardware'. 42 func ObjectContentToType(o types.ObjectContent, ptr ...bool) (any, error) { 43 // Expect no properties in the missing set 44 for _, p := range o.MissingSet { 45 if ignoreMissingProperty(o.Obj, p) { 46 continue 47 } 48 49 return nil, soap.WrapVimFault(p.Fault.Fault) 50 } 51 52 ti := typeInfoForType(o.Obj.Type) 53 v, err := ti.LoadFromObjectContent(o) 54 if err != nil { 55 return nil, err 56 } 57 58 if len(ptr) == 1 && ptr[0] { 59 return v.Interface(), nil 60 } 61 return v.Elem().Interface(), nil 62 } 63 64 // ApplyPropertyChange converts the response of a call to WaitForUpdates 65 // and applies it to the given managed object. 66 func ApplyPropertyChange(obj Reference, changes []types.PropertyChange) { 67 t := typeInfoForType(obj.Reference().Type) 68 v := reflect.ValueOf(obj) 69 70 for _, p := range changes { 71 var field Field 72 if !field.FromString(p.Name) { 73 panic(p.Name + ": invalid property path") 74 } 75 76 rv, ok := t.props[field.Path] 77 if !ok { 78 panic(field.Path + ": property not found") 79 } 80 81 if field.Key == nil { // Key is only used for notifications 82 assignValue(v, rv, reflect.ValueOf(p.Val)) 83 } 84 } 85 } 86 87 // LoadObjectContent converts the response of a call to 88 // RetrieveProperties{Ex} to one or more managed objects. 89 func LoadObjectContent(content []types.ObjectContent, dst any) error { 90 rt := reflect.TypeOf(dst) 91 if rt == nil || rt.Kind() != reflect.Ptr { 92 panic("need pointer") 93 } 94 95 rv := reflect.ValueOf(dst).Elem() 96 if !rv.CanSet() { 97 panic("cannot set dst") 98 } 99 100 isSlice := false 101 switch rt.Elem().Kind() { 102 case reflect.Struct: 103 case reflect.Slice: 104 isSlice = true 105 default: 106 panic("unexpected type") 107 } 108 109 if isSlice { 110 for _, p := range content { 111 v, err := ObjectContentToType(p) 112 if err != nil { 113 return err 114 } 115 116 vt := reflect.TypeOf(v) 117 118 if !rv.Type().AssignableTo(vt) { 119 // For example: dst is []ManagedEntity, res is []HostSystem 120 if field, ok := vt.FieldByName(rt.Elem().Elem().Name()); ok && field.Anonymous { 121 rv.Set(reflect.Append(rv, reflect.ValueOf(v).FieldByIndex(field.Index))) 122 continue 123 } 124 } 125 126 rv.Set(reflect.Append(rv, reflect.ValueOf(v))) 127 } 128 } else { 129 switch len(content) { 130 case 0: 131 case 1: 132 v, err := ObjectContentToType(content[0]) 133 if err != nil { 134 return err 135 } 136 137 vt := reflect.TypeOf(v) 138 139 if !rv.Type().AssignableTo(vt) { 140 // For example: dst is ComputeResource, res is ClusterComputeResource 141 if field, ok := vt.FieldByName(rt.Elem().Name()); ok && field.Anonymous { 142 rv.Set(reflect.ValueOf(v).FieldByIndex(field.Index)) 143 return nil 144 } 145 } 146 147 rv.Set(reflect.ValueOf(v)) 148 default: 149 // If dst is not a slice, expect to receive 0 or 1 results 150 panic("more than 1 result") 151 } 152 } 153 154 return nil 155 } 156 157 // RetrievePropertiesEx wraps RetrievePropertiesEx and ContinueRetrievePropertiesEx to collect properties in batches. 158 func RetrievePropertiesEx(ctx context.Context, r soap.RoundTripper, req types.RetrievePropertiesEx) ([]types.ObjectContent, error) { 159 rx, err := methods.RetrievePropertiesEx(ctx, r, &req) 160 if err != nil { 161 return nil, err 162 } 163 164 if rx.Returnval == nil { 165 return nil, nil 166 } 167 168 objects := rx.Returnval.Objects 169 token := rx.Returnval.Token 170 171 for token != "" { 172 cx, err := methods.ContinueRetrievePropertiesEx(ctx, r, &types.ContinueRetrievePropertiesEx{ 173 This: req.This, 174 Token: token, 175 }) 176 if err != nil { 177 return nil, err 178 } 179 180 token = cx.Returnval.Token 181 objects = append(objects, cx.Returnval.Objects...) 182 } 183 184 return objects, nil 185 } 186 187 // RetrievePropertiesForRequest calls the RetrieveProperties method with the 188 // specified request and decodes the response struct into the value pointed to 189 // by dst. 190 func RetrievePropertiesForRequest(ctx context.Context, r soap.RoundTripper, req types.RetrieveProperties, dst any) error { 191 objects, err := RetrievePropertiesEx(ctx, r, types.RetrievePropertiesEx{ 192 This: req.This, 193 SpecSet: req.SpecSet, 194 }) 195 if err != nil { 196 return err 197 } 198 199 return LoadObjectContent(objects, dst) 200 } 201 202 // RetrieveProperties retrieves the properties of the managed object specified 203 // as obj and decodes the response struct into the value pointed to by dst. 204 func RetrieveProperties(ctx context.Context, r soap.RoundTripper, pc, obj types.ManagedObjectReference, dst any) error { 205 req := types.RetrieveProperties{ 206 This: pc, 207 SpecSet: []types.PropertyFilterSpec{ 208 { 209 ObjectSet: []types.ObjectSpec{ 210 { 211 Obj: obj, 212 Skip: types.NewBool(false), 213 }, 214 }, 215 PropSet: []types.PropertySpec{ 216 { 217 All: types.NewBool(true), 218 Type: obj.Type, 219 }, 220 }, 221 }, 222 }, 223 } 224 225 return RetrievePropertiesForRequest(ctx, r, req, dst) 226 } 227 228 var morType = reflect.TypeOf((*types.ManagedObjectReference)(nil)).Elem() 229 230 // References returns all non-nil moref field values in the given struct. 231 // Only Anonymous struct fields are followed by default. The optional follow 232 // param will follow any struct fields when true. 233 func References(s any, follow ...bool) []types.ManagedObjectReference { 234 var refs []types.ManagedObjectReference 235 rval := reflect.ValueOf(s) 236 rtype := rval.Type() 237 238 if rval.Kind() == reflect.Ptr { 239 rval = rval.Elem() 240 rtype = rval.Type() 241 } 242 243 for i := 0; i < rval.NumField(); i++ { 244 val := rval.Field(i) 245 finfo := rtype.Field(i) 246 247 if finfo.Anonymous { 248 refs = append(refs, References(val.Interface(), follow...)...) 249 continue 250 } 251 if finfo.Name == "Self" { 252 continue 253 } 254 255 ftype := val.Type() 256 257 if ftype.Kind() == reflect.Slice { 258 if ftype.Elem() == morType { 259 s := val.Interface().([]types.ManagedObjectReference) 260 for i := range s { 261 refs = append(refs, s[i]) 262 } 263 } 264 continue 265 } 266 267 if ftype.Kind() == reflect.Ptr { 268 if val.IsNil() { 269 continue 270 } 271 val = val.Elem() 272 ftype = val.Type() 273 } 274 275 if ftype == morType { 276 refs = append(refs, val.Interface().(types.ManagedObjectReference)) 277 continue 278 } 279 280 if len(follow) != 0 && follow[0] { 281 if ftype.Kind() == reflect.Struct && val.CanSet() { 282 refs = append(refs, References(val.Interface(), follow...)...) 283 } 284 } 285 } 286 287 return refs 288 }