1/*
2Copyright (c) 2015 VMware, Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package property
18
19import (
20	"context"
21	"errors"
22
23	"github.com/vmware/govmomi/vim25"
24	"github.com/vmware/govmomi/vim25/methods"
25	"github.com/vmware/govmomi/vim25/mo"
26	"github.com/vmware/govmomi/vim25/soap"
27	"github.com/vmware/govmomi/vim25/types"
28)
29
30// Collector models the PropertyCollector managed object.
31//
32// For more information, see:
33// http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvmodl.query.PropertyCollector.html
34//
35type Collector struct {
36	roundTripper soap.RoundTripper
37	reference    types.ManagedObjectReference
38}
39
40// DefaultCollector returns the session's default property collector.
41func DefaultCollector(c *vim25.Client) *Collector {
42	p := Collector{
43		roundTripper: c,
44		reference:    c.ServiceContent.PropertyCollector,
45	}
46
47	return &p
48}
49
50func (p Collector) Reference() types.ManagedObjectReference {
51	return p.reference
52}
53
54// Create creates a new session-specific Collector that can be used to
55// retrieve property updates independent of any other Collector.
56func (p *Collector) Create(ctx context.Context) (*Collector, error) {
57	req := types.CreatePropertyCollector{
58		This: p.Reference(),
59	}
60
61	res, err := methods.CreatePropertyCollector(ctx, p.roundTripper, &req)
62	if err != nil {
63		return nil, err
64	}
65
66	newp := Collector{
67		roundTripper: p.roundTripper,
68		reference:    res.Returnval,
69	}
70
71	return &newp, nil
72}
73
74// Destroy destroys this Collector.
75func (p *Collector) Destroy(ctx context.Context) error {
76	req := types.DestroyPropertyCollector{
77		This: p.Reference(),
78	}
79
80	_, err := methods.DestroyPropertyCollector(ctx, p.roundTripper, &req)
81	if err != nil {
82		return err
83	}
84
85	p.reference = types.ManagedObjectReference{}
86	return nil
87}
88
89func (p *Collector) CreateFilter(ctx context.Context, req types.CreateFilter) error {
90	req.This = p.Reference()
91
92	_, err := methods.CreateFilter(ctx, p.roundTripper, &req)
93	if err != nil {
94		return err
95	}
96
97	return nil
98}
99
100func (p *Collector) WaitForUpdates(ctx context.Context, v string) (*types.UpdateSet, error) {
101	req := types.WaitForUpdatesEx{
102		This:    p.Reference(),
103		Version: v,
104	}
105
106	res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
107	if err != nil {
108		return nil, err
109	}
110
111	return res.Returnval, nil
112}
113
114func (p *Collector) CancelWaitForUpdates(ctx context.Context) error {
115	req := &types.CancelWaitForUpdates{This: p.Reference()}
116	_, err := methods.CancelWaitForUpdates(ctx, p.roundTripper, req)
117	return err
118}
119
120func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrieveProperties) (*types.RetrievePropertiesResponse, error) {
121	req.This = p.Reference()
122	return methods.RetrieveProperties(ctx, p.roundTripper, &req)
123}
124
125// Retrieve loads properties for a slice of managed objects. The dst argument
126// must be a pointer to a []interface{}, which is populated with the instances
127// of the specified managed objects, with the relevant properties filled in. If
128// the properties slice is nil, all properties are loaded.
129// Note that pointer types are optional fields that may be left as a nil value.
130// The caller should check such fields for a nil value before dereferencing.
131func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}) error {
132	if len(objs) == 0 {
133		return errors.New("object references is empty")
134	}
135
136	kinds := make(map[string]bool)
137
138	var propSet []types.PropertySpec
139	var objectSet []types.ObjectSpec
140
141	for _, obj := range objs {
142		if _, ok := kinds[obj.Type]; !ok {
143			spec := types.PropertySpec{
144				Type: obj.Type,
145			}
146			if ps == nil {
147				spec.All = types.NewBool(true)
148			} else {
149				spec.PathSet = ps
150			}
151			propSet = append(propSet, spec)
152			kinds[obj.Type] = true
153		}
154
155		objectSpec := types.ObjectSpec{
156			Obj:  obj,
157			Skip: types.NewBool(false),
158		}
159
160		objectSet = append(objectSet, objectSpec)
161	}
162
163	req := types.RetrieveProperties{
164		SpecSet: []types.PropertyFilterSpec{
165			{
166				ObjectSet: objectSet,
167				PropSet:   propSet,
168			},
169		},
170	}
171
172	res, err := p.RetrieveProperties(ctx, req)
173	if err != nil {
174		return err
175	}
176
177	if d, ok := dst.(*[]types.ObjectContent); ok {
178		*d = res.Returnval
179		return nil
180	}
181
182	return mo.LoadRetrievePropertiesResponse(res, dst)
183}
184
185// RetrieveWithFilter populates dst as Retrieve does, but only for entities matching the given filter.
186func (p *Collector) RetrieveWithFilter(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}, filter Filter) error {
187	if len(filter) == 0 {
188		return p.Retrieve(ctx, objs, ps, dst)
189	}
190
191	var content []types.ObjectContent
192
193	err := p.Retrieve(ctx, objs, filter.Keys(), &content)
194	if err != nil {
195		return err
196	}
197
198	objs = filter.MatchObjectContent(content)
199
200	if len(objs) == 0 {
201		return nil
202	}
203
204	return p.Retrieve(ctx, objs, ps, dst)
205}
206
207// RetrieveOne calls Retrieve with a single managed object reference via Collector.Retrieve().
208func (p *Collector) RetrieveOne(ctx context.Context, obj types.ManagedObjectReference, ps []string, dst interface{}) error {
209	var objs = []types.ManagedObjectReference{obj}
210	return p.Retrieve(ctx, objs, ps, dst)
211}
212