1/*
2Copyright (c) 2017 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 simulator
18
19import (
20	"fmt"
21	"strings"
22
23	"github.com/vmware/govmomi/simulator/esx"
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
30type ResourcePool struct {
31	mo.ResourcePool
32}
33
34func NewResourcePool() *ResourcePool {
35	pool := &ResourcePool{
36		ResourcePool: esx.ResourcePool,
37	}
38
39	if Map.IsVPX() {
40		pool.DisabledMethod = nil // Enable VApp methods for VC
41	}
42
43	return pool
44}
45
46func allResourceFieldsSet(info *types.ResourceAllocationInfo) bool {
47	return info.Reservation != nil &&
48		info.Limit != nil &&
49		info.ExpandableReservation != nil &&
50		info.Shares != nil
51}
52
53func allResourceFieldsValid(info *types.ResourceAllocationInfo) bool {
54	if info.Reservation != nil {
55		if *info.Reservation < 0 {
56			return false
57		}
58	}
59
60	if info.Limit != nil {
61		if *info.Limit < -1 {
62			return false
63		}
64	}
65
66	if info.Shares != nil {
67		if info.Shares.Level == types.SharesLevelCustom {
68			if info.Shares.Shares < 0 {
69				return false
70			}
71		}
72	}
73
74	if info.OverheadLimit != nil {
75		return false
76	}
77
78	return true
79}
80
81func (p *ResourcePool) createChild(name string, spec types.ResourceConfigSpec) (*ResourcePool, *soap.Fault) {
82	if e := Map.FindByName(name, p.ResourcePool.ResourcePool); e != nil {
83		return nil, Fault("", &types.DuplicateName{
84			Name:   e.Entity().Name,
85			Object: e.Reference(),
86		})
87	}
88
89	if !(allResourceFieldsSet(&spec.CpuAllocation) && allResourceFieldsValid(&spec.CpuAllocation)) {
90		return nil, Fault("", &types.InvalidArgument{
91			InvalidProperty: "spec.cpuAllocation",
92		})
93	}
94
95	if !(allResourceFieldsSet(&spec.MemoryAllocation) && allResourceFieldsValid(&spec.MemoryAllocation)) {
96		return nil, Fault("", &types.InvalidArgument{
97			InvalidProperty: "spec.memoryAllocation",
98		})
99	}
100
101	child := NewResourcePool()
102
103	child.Name = name
104	child.Owner = p.Owner
105	child.Summary.GetResourcePoolSummary().Name = name
106	child.Config.CpuAllocation = spec.CpuAllocation
107	child.Config.MemoryAllocation = spec.MemoryAllocation
108	child.Config.Entity = spec.Entity
109
110	return child, nil
111}
112
113func (p *ResourcePool) CreateResourcePool(c *types.CreateResourcePool) soap.HasFault {
114	body := &methods.CreateResourcePoolBody{}
115
116	child, err := p.createChild(c.Name, c.Spec)
117	if err != nil {
118		body.Fault_ = err
119		return body
120	}
121
122	Map.PutEntity(p, Map.NewEntity(child))
123
124	p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
125
126	body.Res = &types.CreateResourcePoolResponse{
127		Returnval: child.Reference(),
128	}
129
130	return body
131}
132
133func updateResourceAllocation(kind string, src, dst *types.ResourceAllocationInfo) types.BaseMethodFault {
134	if !allResourceFieldsValid(src) {
135		return &types.InvalidArgument{
136			InvalidProperty: fmt.Sprintf("spec.%sAllocation", kind),
137		}
138	}
139
140	if src.Reservation != nil {
141		dst.Reservation = src.Reservation
142	}
143
144	if src.Limit != nil {
145		dst.Limit = src.Limit
146	}
147
148	if src.Shares != nil {
149		dst.Shares = src.Shares
150	}
151
152	return nil
153}
154
155func (p *ResourcePool) UpdateConfig(c *types.UpdateConfig) soap.HasFault {
156	body := &methods.UpdateConfigBody{}
157
158	if c.Name != "" {
159		if e := Map.FindByName(c.Name, p.ResourcePool.ResourcePool); e != nil {
160			body.Fault_ = Fault("", &types.DuplicateName{
161				Name:   e.Entity().Name,
162				Object: e.Reference(),
163			})
164			return body
165		}
166
167		p.Name = c.Name
168	}
169
170	spec := c.Config
171
172	if spec != nil {
173		if err := updateResourceAllocation("memory", &spec.MemoryAllocation, &p.Config.MemoryAllocation); err != nil {
174			body.Fault_ = Fault("", err)
175			return body
176		}
177
178		if err := updateResourceAllocation("cpu", &spec.CpuAllocation, &p.Config.CpuAllocation); err != nil {
179			body.Fault_ = Fault("", err)
180			return body
181		}
182	}
183
184	body.Res = &types.UpdateConfigResponse{}
185
186	return body
187}
188
189type VirtualApp struct {
190	mo.VirtualApp
191}
192
193func NewVAppConfigSpec() types.VAppConfigSpec {
194	spec := types.VAppConfigSpec{
195		Annotation: "vcsim",
196		VmConfigSpec: types.VmConfigSpec{
197			Product: []types.VAppProductSpec{
198				{
199					Info: &types.VAppProductInfo{
200						Name:      "vcsim",
201						Vendor:    "VMware",
202						VendorUrl: "http://www.vmware.com/",
203						Version:   "0.1",
204					},
205					ArrayUpdateSpec: types.ArrayUpdateSpec{
206						Operation: types.ArrayUpdateOperationAdd,
207					},
208				},
209			},
210		},
211	}
212
213	return spec
214}
215
216func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault {
217	body := &methods.CreateVAppBody{}
218
219	pool, err := p.createChild(req.Name, req.ResSpec)
220	if err != nil {
221		body.Fault_ = err
222		return body
223	}
224
225	child := &VirtualApp{}
226	child.ResourcePool = pool.ResourcePool
227	child.Self.Type = "VirtualApp"
228	child.ParentFolder = req.VmFolder
229
230	if child.ParentFolder == nil {
231		folder := Map.getEntityDatacenter(p).VmFolder
232		child.ParentFolder = &folder
233	}
234
235	child.VAppConfig = &types.VAppConfigInfo{
236		VmConfigInfo: types.VmConfigInfo{},
237		Annotation:   req.ConfigSpec.Annotation,
238	}
239
240	for _, product := range req.ConfigSpec.Product {
241		child.VAppConfig.Product = append(child.VAppConfig.Product, *product.Info)
242	}
243
244	Map.PutEntity(p, Map.NewEntity(child))
245
246	p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
247
248	body.Res = &types.CreateVAppResponse{
249		Returnval: child.Reference(),
250	}
251
252	return body
253}
254
255func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Task) soap.HasFault {
256	ctx.Caller = &a.Self
257	body := &methods.CreateChildVM_TaskBody{}
258
259	folder := Map.Get(*a.ParentFolder).(*Folder)
260
261	res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
262		This:   folder.Self,
263		Config: req.Config,
264		Host:   req.Host,
265		Pool:   req.This,
266	})
267
268	body.Res = &types.CreateChildVM_TaskResponse{
269		Returnval: res.(*methods.CreateVM_TaskBody).Res.Returnval,
270	}
271
272	return body
273}
274
275func (a *VirtualApp) DestroyTask(req *types.Destroy_Task) soap.HasFault {
276	return (&ResourcePool{ResourcePool: a.ResourcePool}).DestroyTask(req)
277}
278
279func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
280	task := CreateTask(p, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
281		if strings.HasSuffix(p.Parent.Type, "ComputeResource") {
282			// Can't destroy the root pool
283			return nil, &types.InvalidArgument{}
284		}
285
286		pp := Map.Get(*p.Parent).(*ResourcePool)
287
288		parent := &pp.ResourcePool
289		// Remove child reference from rp
290		Map.RemoveReference(parent, &parent.ResourcePool, req.This)
291
292		// The grandchildren become children of the parent (rp)
293		Map.AppendReference(parent, &parent.ResourcePool, p.ResourcePool.ResourcePool...)
294
295		// And VMs move to the parent
296		vms := p.ResourcePool.Vm
297		for _, ref := range vms {
298			vm := Map.Get(ref).(*VirtualMachine)
299			Map.WithLock(vm, func() { vm.ResourcePool = &parent.Self })
300		}
301
302		Map.AppendReference(parent, &parent.Vm, vms...)
303
304		Map.Remove(req.This)
305
306		return nil, nil
307	})
308
309	return &methods.Destroy_TaskBody{
310		Res: &types.Destroy_TaskResponse{
311			Returnval: task.Run(),
312		},
313	}
314}
315