retrieve.go 7.4 KB

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