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