github.com/vmware/govmomi@v0.37.2/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  }