VMware vSphere Integrated Containers provider (#206)

* Add Virtual Kubelet provider for VIC

Initial virtual kubelet provider for VMware VIC.  This provider currently
handles creating and starting of a pod VM via the VIC portlayer and persona
server.  Image store handling via the VIC persona server.  This provider
currently requires the feature/wolfpack branch of VIC.

* Added pod stop and delete.  Also added node capacity.

Added the ability to stop and delete pod VMs via VIC.  Also retrieve
node capacity information from the VCH.

* Cleanup and readme file

Some file clean up and added a Readme.md markdown file for the VIC
provider.

* Cleaned up errors, added function comments, moved operation code

1. Cleaned up error handling.  Set standard for creating errors.
2. Added method prototype comments for all interface functions.
3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder.

* Add mocking code and unit tests for podcache, podcreator, and podstarter

Used the unit test framework used in VIC to handle assertions in the provider's
unit test.  Mocking code generated using OSS project mockery, which is compatible
with the testify assertion framework.

* Vendored packages for the VIC provider

Requires feature/wolfpack branch of VIC and a few specific commit sha of
projects used within VIC.

* Implementation of POD Stopper and Deleter unit tests (#4)

* Updated files for initial PR
This commit is contained in:
Loc Nguyen
2018-06-04 15:41:32 -07:00
committed by Ria Bhatia
parent 98a111e8b7
commit 513cebe7b7
6296 changed files with 1123685 additions and 8 deletions

View File

@@ -0,0 +1,258 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type AuthorizationManager struct {
mo.AuthorizationManager
permissions map[types.ManagedObjectReference][]types.Permission
privileges map[string]struct{}
system []string
nextID int32
}
func NewAuthorizationManager(ref types.ManagedObjectReference) object.Reference {
m := &AuthorizationManager{}
m.Self = ref
m.RoleList = make([]types.AuthorizationRole, len(esx.RoleList))
copy(m.RoleList, esx.RoleList)
m.permissions = make(map[types.ManagedObjectReference][]types.Permission)
l := object.AuthorizationRoleList(m.RoleList)
m.system = l.ByName("ReadOnly").Privilege
admin := l.ByName("Admin")
m.privileges = make(map[string]struct{}, len(admin.Privilege))
for _, id := range admin.Privilege {
m.privileges[id] = struct{}{}
}
root := Map.content().RootFolder
for _, u := range DefaultUserGroup {
m.permissions[root] = append(m.permissions[root], types.Permission{
Entity: &root,
Principal: u.Principal,
Group: u.Group,
RoleId: admin.RoleId,
Propagate: true,
})
}
return m
}
func (m *AuthorizationManager) RetrieveEntityPermissions(req *types.RetrieveEntityPermissions) soap.HasFault {
e := Map.Get(req.Entity).(mo.Entity)
p := m.permissions[e.Reference()]
if req.Inherited {
for {
parent := e.Entity().Parent
if parent == nil {
break
}
e = Map.Get(parent.Reference()).(mo.Entity)
p = append(p, m.permissions[e.Reference()]...)
}
}
return &methods.RetrieveEntityPermissionsBody{
Res: &types.RetrieveEntityPermissionsResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) RetrieveAllPermissions(req *types.RetrieveAllPermissions) soap.HasFault {
var p []types.Permission
for _, v := range m.permissions {
p = append(p, v...)
}
return &methods.RetrieveAllPermissionsBody{
Res: &types.RetrieveAllPermissionsResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) RemoveEntityPermission(req *types.RemoveEntityPermission) soap.HasFault {
var p []types.Permission
for _, v := range m.permissions[req.Entity] {
if v.Group == req.IsGroup && v.Principal == req.User {
continue
}
p = append(p, v)
}
m.permissions[req.Entity] = p
return &methods.RemoveEntityPermissionBody{
Res: &types.RemoveEntityPermissionResponse{},
}
}
func (m *AuthorizationManager) SetEntityPermissions(req *types.SetEntityPermissions) soap.HasFault {
m.permissions[req.Entity] = req.Permission
return &methods.SetEntityPermissionsBody{
Res: &types.SetEntityPermissionsResponse{},
}
}
func (m *AuthorizationManager) RetrieveRolePermissions(req *types.RetrieveRolePermissions) soap.HasFault {
var p []types.Permission
for _, set := range m.permissions {
for _, v := range set {
if v.RoleId == req.RoleId {
p = append(p, v)
}
}
}
return &methods.RetrieveRolePermissionsBody{
Res: &types.RetrieveRolePermissionsResponse{
Returnval: p,
},
}
}
func (m *AuthorizationManager) AddAuthorizationRole(req *types.AddAuthorizationRole) soap.HasFault {
body := &methods.AddAuthorizationRoleBody{}
for _, role := range m.RoleList {
if role.Name == req.Name {
body.Fault_ = Fault("", &types.AlreadyExists{})
return body
}
}
ids, err := m.privIDs(req.PrivIds)
if err != nil {
body.Fault_ = err
return body
}
m.RoleList = append(m.RoleList, types.AuthorizationRole{
Info: &types.Description{
Label: req.Name,
Summary: req.Name,
},
RoleId: m.nextID,
Privilege: ids,
Name: req.Name,
System: false,
})
m.nextID++
body.Res = &types.AddAuthorizationRoleResponse{}
return body
}
func (m *AuthorizationManager) UpdateAuthorizationRole(req *types.UpdateAuthorizationRole) soap.HasFault {
body := &methods.UpdateAuthorizationRoleBody{}
for _, role := range m.RoleList {
if role.Name == req.NewName && role.RoleId != req.RoleId {
body.Fault_ = Fault("", &types.AlreadyExists{})
return body
}
}
for i, role := range m.RoleList {
if role.RoleId == req.RoleId {
if len(req.PrivIds) != 0 {
ids, err := m.privIDs(req.PrivIds)
if err != nil {
body.Fault_ = err
return body
}
m.RoleList[i].Privilege = ids
}
m.RoleList[i].Name = req.NewName
body.Res = &types.UpdateAuthorizationRoleResponse{}
return body
}
}
body.Fault_ = Fault("", &types.NotFound{})
return body
}
func (m *AuthorizationManager) RemoveAuthorizationRole(req *types.RemoveAuthorizationRole) soap.HasFault {
body := &methods.RemoveAuthorizationRoleBody{}
for i, role := range m.RoleList {
if role.RoleId == req.RoleId {
m.RoleList = append(m.RoleList[:i], m.RoleList[i+1:]...)
body.Res = &types.RemoveAuthorizationRoleResponse{}
return body
}
}
body.Fault_ = Fault("", &types.NotFound{})
return body
}
func (m *AuthorizationManager) privIDs(ids []string) ([]string, *soap.Fault) {
system := make(map[string]struct{}, len(m.system))
for _, id := range ids {
if _, ok := m.privileges[id]; !ok {
return nil, Fault("", &types.InvalidArgument{InvalidProperty: "privIds"})
}
if strings.HasPrefix(id, "System.") {
system[id] = struct{}{}
}
}
for _, id := range m.system {
if _, ok := system[id]; ok {
continue
}
ids = append(ids, id)
}
return ids, nil
}

View File

@@ -0,0 +1,37 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/types"
)
func TestAuthorizationManager(t *testing.T) {
for i := 0; i < 2; i++ {
model := VPX()
_ = New(NewServiceInstance(model.ServiceContent, model.RootFolder)) // 2nd pass panics w/o copying RoleList
authz := Map.Get(*vpx.ServiceContent.AuthorizationManager).(*AuthorizationManager)
authz.RemoveAuthorizationRole(&types.RemoveAuthorizationRole{
RoleId: -2, // ReadOnly
})
}
}

View File

@@ -0,0 +1,322 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"sync/atomic"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type ClusterComputeResource struct {
mo.ClusterComputeResource
ruleKey int32
}
type addHost struct {
*ClusterComputeResource
req *types.AddHost_Task
}
func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
spec := add.req.Spec
if spec.HostName == "" {
return nil, &types.NoHost{}
}
host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name
if add.req.AsConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
} else {
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
}
cr := add.ClusterComputeResource
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
cr.Host = append(cr.Host, host.Reference())
addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
return host.Reference(), nil
}
func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFault {
return &methods.AddHost_TaskBody{
Res: &types.AddHost_TaskResponse{
Returnval: NewTask(&addHost{c, add}).Run(),
},
}
}
func (c *ClusterComputeResource) updateRules(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.RulesSpec {
var i int
exists := false
match := func(info types.BaseClusterRuleInfo) bool {
return info.GetClusterRuleInfo().Name == spec.Info.GetClusterRuleInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(rule types.BaseClusterRuleInfo) bool {
return rule.GetClusterRuleInfo().Key == spec.ArrayUpdateSpec.RemoveKey.(int32)
}
}
for i = range cfg.Rule {
if match(cfg.Rule[i].GetClusterRuleInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
info := spec.Info.GetClusterRuleInfo()
info.Key = atomic.AddInt32(&c.ruleKey, 1)
info.RuleUuid = uuid.New().String()
cfg.Rule = append(cfg.Rule, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Rule = append(cfg.Rule[:i], cfg.Rule[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateGroups(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.GroupSpec {
var i int
exists := false
match := func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.Info.GetClusterGroupInfo().Name
}
if spec.Operation == types.ArrayUpdateOperationRemove {
match = func(info types.BaseClusterGroupInfo) bool {
return info.GetClusterGroupInfo().Name == spec.ArrayUpdateSpec.RemoveKey.(string)
}
}
for i = range cfg.Group {
if match(cfg.Group[i].GetClusterGroupInfo()) {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group, spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group[i] = spec.Info
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.Group = append(cfg.Group[:i], cfg.Group[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDAS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DasVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DasVmConfig {
if cfg.DasVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
src := spec.Info.DasSettings
if src == nil {
return new(types.InvalidArgument)
}
dst := cfg.DasVmConfig[i].DasSettings
if src.RestartPriority != "" {
dst.RestartPriority = src.RestartPriority
}
if src.RestartPriorityTimeout != 0 {
dst.RestartPriorityTimeout = src.RestartPriorityTimeout
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DasVmConfig = append(cfg.DasVmConfig[:i], cfg.DasVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) updateOverridesDRS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
for _, spec := range cspec.DrsVmConfigSpec {
var i int
var key types.ManagedObjectReference
exists := false
if spec.Operation == types.ArrayUpdateOperationRemove {
key = spec.RemoveKey.(types.ManagedObjectReference)
} else {
key = spec.Info.Key
}
for i = range cfg.DrsVmConfig {
if cfg.DrsVmConfig[i].Key == key {
exists = true
break
}
}
switch spec.Operation {
case types.ArrayUpdateOperationAdd:
if exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig, *spec.Info)
case types.ArrayUpdateOperationEdit:
if !exists {
return new(types.InvalidArgument)
}
if spec.Info.Enabled != nil {
cfg.DrsVmConfig[i].Enabled = spec.Info.Enabled
}
if spec.Info.Behavior != "" {
cfg.DrsVmConfig[i].Behavior = spec.Info.Behavior
}
case types.ArrayUpdateOperationRemove:
if !exists {
return new(types.InvalidArgument)
}
cfg.DrsVmConfig = append(cfg.DrsVmConfig[:i], cfg.DrsVmConfig[i+1:]...)
}
}
return nil
}
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.ReconfigureComputeResource_Task) soap.HasFault {
task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
if !ok {
return nil, new(types.InvalidArgument)
}
updates := []func(*types.ClusterConfigInfoEx, *types.ClusterConfigSpecEx) types.BaseMethodFault{
c.updateRules,
c.updateGroups,
c.updateOverridesDAS,
c.updateOverridesDRS,
}
for _, update := range updates {
if err := update(c.ConfigurationEx.(*types.ClusterConfigInfoEx), spec); err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.ReconfigureComputeResource_TaskBody{
Res: &types.ReconfigureComputeResource_TaskResponse{
Returnval: task.Run(),
},
}
}
func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
if e := Map.FindByName(name, f.ChildEntity); e != nil {
return nil, &types.DuplicateName{
Name: e.Entity().Name,
Object: e.Reference(),
}
}
cluster := &ClusterComputeResource{}
cluster.Name = name
cluster.Summary = &types.ClusterComputeResourceSummary{
UsageSummary: new(types.ClusterUsageSummary),
}
config := &types.ClusterConfigInfoEx{}
cluster.ConfigurationEx = config
config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
config.DrsConfig.Enabled = types.NewBool(true)
pool := NewResourcePool()
Map.PutEntity(cluster, Map.NewEntity(pool))
cluster.ResourcePool = &pool.Self
f.putChild(cluster)
pool.Owner = cluster.Self
return cluster, nil
}

View File

@@ -0,0 +1,112 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/types"
)
func TestClusterESX(t *testing.T) {
content := esx.ServiceContent
s := New(NewServiceInstance(content, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
dc := object.NewDatacenter(c.Client, esx.Datacenter.Reference())
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
_, err = folders.HostFolder.CreateCluster(ctx, "cluster1", types.ClusterConfigSpecEx{})
if err == nil {
t.Fatal("expected error")
}
}
func TestClusterVC(t *testing.T) {
content := vpx.ServiceContent
s := New(NewServiceInstance(content, vpx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
f := object.NewRootFolder(c.Client)
dc, err := f.CreateDatacenter(ctx, "foo")
if err != nil {
t.Error(err)
}
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
cluster, err := folders.HostFolder.CreateCluster(ctx, "cluster1", types.ClusterConfigSpecEx{})
if err != nil {
t.Fatal(err)
}
_, err = folders.HostFolder.CreateCluster(ctx, "cluster1", types.ClusterConfigSpecEx{})
if err == nil {
t.Error("expected DuplicateName error")
}
spec := types.HostConnectSpec{}
for _, fail := range []bool{true, false} {
task, err := cluster.AddHost(ctx, spec, true, nil, nil)
if err != nil {
t.Fatal(err)
}
_, err = task.WaitForResult(ctx, nil)
if fail {
if err == nil {
t.Error("expected error")
}
spec.HostName = "localhost"
} else {
if err != nil {
t.Error(err)
}
}
}
}

View File

@@ -0,0 +1,113 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type CustomFieldsManager struct {
mo.CustomFieldsManager
nextKey int32
}
func NewCustomFieldsManager(ref types.ManagedObjectReference) object.Reference {
m := &CustomFieldsManager{}
m.Self = ref
return m
}
func (c *CustomFieldsManager) find(key int32) (int, *types.CustomFieldDef) {
for i, field := range c.Field {
if field.Key == key {
return i, &c.Field[i]
}
}
return -1, nil
}
func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) soap.HasFault {
body := &methods.AddCustomFieldDefBody{}
def := types.CustomFieldDef{
Key: c.nextKey,
Name: req.Name,
ManagedObjectType: req.MoType,
Type: req.MoType,
FieldDefPrivileges: req.FieldDefPolicy,
FieldInstancePrivileges: req.FieldPolicy,
}
c.Field = append(c.Field, def)
c.nextKey++
body.Res = &types.AddCustomFieldDefResponse{
Returnval: def,
}
return body
}
func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldDef) soap.HasFault {
body := &methods.RemoveCustomFieldDefBody{}
i, field := c.find(req.Key)
if field == nil {
body.Fault_ = Fault("", &types.NotFound{})
return body
}
c.Field = append(c.Field[:i], c.Field[i+1:]...)
body.Res = &types.RemoveCustomFieldDefResponse{}
return body
}
func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldDef) soap.HasFault {
body := &methods.RenameCustomFieldDefBody{}
_, field := c.find(req.Key)
if field == nil {
body.Fault_ = Fault("", &types.NotFound{})
return body
}
field.Name = req.Name
body.Res = &types.RenameCustomFieldDefResponse{}
return body
}
func (c *CustomFieldsManager) SetField(req *types.SetField) soap.HasFault {
body := &methods.SetFieldBody{}
entity := Map.Get(req.Entity).(mo.Entity).Entity()
Map.WithLock(entity, func() {
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
})
})
body.Res = &types.SetFieldResponse{}
return body
}

View File

@@ -0,0 +1,123 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"reflect"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
func TestCustomFieldsManager(t *testing.T) {
s := New(NewServiceInstance(vpx.ServiceContent, vpx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
fieldsManager, err := object.GetCustomFieldsManager(c.Client)
if err != nil {
t.Fatal(err)
}
field, err := fieldsManager.Add(ctx, "field_name", "VirtualMachine", nil, nil)
if err != nil {
t.Fatal(err)
}
if field.Name != "field_name" && field.Type != "VirtualMachine" {
t.Fatal("field add result mismatched with the inserted")
}
fields, err := fieldsManager.Field(ctx)
if err != nil {
t.Fatal(err)
}
if len(fields) != 1 {
t.Fatalf("expect len(fields)=1; got %d", len(fields))
}
if !reflect.DeepEqual(&fields[0], field) {
t.Fatalf("expect fields[0]==field; got %+v,%+v", fields[0], field)
}
key, err := fieldsManager.FindKey(ctx, field.Name)
if err != nil {
t.Fatal(err)
}
if key != field.Key {
t.Fatalf("expect key == field.Key; got %d != %d", key, field.Key)
}
err = fieldsManager.Rename(ctx, key, "new_field_name")
if err != nil {
t.Fatal(err)
}
fields, err = fieldsManager.Field(ctx)
if err != nil {
t.Fatal(err)
}
if len(fields) != 1 {
t.Fatalf("expect len(fields)=1; got %d", len(fields))
}
if fields[0].Name != "new_field_name" {
t.Fatalf("expect field.name to be %s; got %s", "new_field_name", fields[0].Name)
}
folder := Map.content().RootFolder
err = fieldsManager.Set(ctx, folder, 1, "value")
if err != nil {
t.Fatal(err)
}
values := Map.Get(folder.Reference()).(mo.Entity).Entity().CustomValue
if len(values) != 1 {
t.Fatalf("expect CustomValue has 1 item; got %d", len(values))
}
fkey := values[0].GetCustomFieldValue().Key
if fkey != 1 {
t.Fatalf("expect value.Key to be 1; got %d", fkey)
}
value := values[0].(*types.CustomFieldStringValue).Value
if value != "value" {
t.Fatalf("expect value.Value to be %q; got %q", "value", value)
}
err = fieldsManager.Remove(ctx, field.Key)
if err != nil {
t.Fatal(err)
}
fields, err = fieldsManager.Field(ctx)
if err != nil {
t.Fatal(err)
}
if len(fields) != 0 {
t.Fatalf("expect fields to be empty; got %+v", fields)
}
}

View File

@@ -0,0 +1,160 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"strings"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type Datacenter struct {
mo.Datacenter
isESX bool
}
// NewDatacenter creates a Datacenter and its child folders.
func NewDatacenter(f *Folder) *Datacenter {
dc := &Datacenter{
isESX: f.Self == esx.RootFolder.Self,
}
if dc.isESX {
dc.Datacenter = esx.Datacenter
}
f.putChild(dc)
dc.createFolders()
return dc
}
// Create Datacenter Folders.
// Every Datacenter has 4 inventory Folders: Vm, Host, Datastore and Network.
// The ESX folder child types are limited to 1 type.
// The VC folders have additional child types, including nested folders.
func (dc *Datacenter) createFolders() {
folders := []struct {
ref *types.ManagedObjectReference
name string
types []string
}{
{&dc.VmFolder, "vm", []string{"VirtualMachine", "VirtualApp", "Folder"}},
{&dc.HostFolder, "host", []string{"ComputeResource", "Folder"}},
{&dc.DatastoreFolder, "datastore", []string{"Datastore", "StoragePod", "Folder"}},
{&dc.NetworkFolder, "network", []string{"Network", "DistributedVirtualSwitch", "Folder"}},
}
for _, f := range folders {
folder := &Folder{}
folder.Name = f.name
if dc.isESX {
folder.ChildType = f.types[:1]
folder.Self = *f.ref
Map.PutEntity(dc, folder)
} else {
folder.ChildType = f.types
e := Map.PutEntity(dc, folder)
// propagate the generated morefs to Datacenter
ref := e.Reference()
f.ref.Type = ref.Type
f.ref.Value = ref.Value
}
}
net := Map.Get(dc.NetworkFolder).(*Folder)
for _, ref := range esx.Datacenter.Network {
// Add VM Network by default to each Datacenter
network := &mo.Network{}
network.Self = ref
network.Name = strings.Split(ref.Value, "-")[1]
network.Entity().Name = network.Name
if !dc.isESX {
network.Self.Value = "" // we want a different moid per-DC
}
net.putChild(network)
}
}
func datacenterEventArgument(obj mo.Entity) *types.DatacenterEventArgument {
dc, ok := obj.(*Datacenter)
if !ok {
dc = Map.getEntityDatacenter(obj)
}
return &types.DatacenterEventArgument{
Datacenter: dc.Self,
EntityEventArgument: types.EntityEventArgument{Name: dc.Name},
}
}
func (dc *Datacenter) PowerOnMultiVMTask(ctx *Context, req *types.PowerOnMultiVM_Task) soap.HasFault {
task := CreateTask(dc, "powerOnMultiVM", func(_ *Task) (types.AnyType, types.BaseMethodFault) {
if dc.isESX {
return nil, new(types.NotImplemented)
}
for _, ref := range req.Vm {
vm := Map.Get(ref).(*VirtualMachine)
Map.WithLock(vm, func() {
vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{})
})
}
return nil, nil
})
return &methods.PowerOnMultiVM_TaskBody{
Res: &types.PowerOnMultiVM_TaskResponse{
Returnval: task.Run(),
},
}
}
func (d *Datacenter) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(d, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
folders := []types.ManagedObjectReference{
d.VmFolder,
d.HostFolder,
}
for _, ref := range folders {
if len(Map.Get(ref).(*Folder).ChildEntity) != 0 {
return nil, &types.ResourceInUse{}
}
}
Map.Get(*d.Parent).(*Folder).removeChild(d.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,85 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
func TestDatacenterCreateFolders(t *testing.T) {
// For this test we only want the RootFolder, 1 Datacenter and its child folders
models := []Model{
Model{
ServiceContent: esx.ServiceContent,
RootFolder: esx.RootFolder,
},
Model{
ServiceContent: vpx.ServiceContent,
RootFolder: vpx.RootFolder,
Datacenter: 1,
},
}
for _, model := range models {
_ = model.Create()
dc := Map.Any("Datacenter").(*Datacenter)
folders := []types.ManagedObjectReference{
dc.VmFolder,
dc.HostFolder,
dc.DatastoreFolder,
dc.NetworkFolder,
}
for _, ref := range folders {
if ref.Type == "" || ref.Value == "" {
t.Errorf("invalid moref=%#v", ref)
}
e := Map.Get(ref).(mo.Entity)
if e.Entity().Name == "" {
t.Error("empty name")
}
if *e.Entity().Parent != dc.Self {
t.Fail()
}
f, ok := e.(*Folder)
if !ok {
t.Fatalf("unexpected type (%T) for %#v", e, ref)
}
if Map.IsVPX() {
if len(f.ChildType) < 2 {
t.Fail()
}
} else {
if len(f.ChildType) != 1 {
t.Fail()
}
}
}
}
}

View File

@@ -0,0 +1,59 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type Datastore struct {
mo.Datastore
}
func parseDatastorePath(dsPath string) (*object.DatastorePath, types.BaseMethodFault) {
var p object.DatastorePath
if p.FromString(dsPath) {
return &p, nil
}
return nil, &types.InvalidDatastorePath{DatastorePath: dsPath}
}
func (ds *Datastore) RefreshDatastore(*types.RefreshDatastore) soap.HasFault {
r := &methods.RefreshDatastoreBody{}
err := ds.stat()
if err != nil {
r.Fault_ = Fault(err.Error(), &types.HostConfigFault{})
return r
}
info := ds.Info.GetDatastoreInfo()
now := time.Now()
info.Timestamp = &now
return r
}

View File

@@ -0,0 +1,484 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"net/http"
"os"
"path"
"strings"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func TestParseDatastorePath(t *testing.T) {
tests := []struct {
dsPath string
dsFile string
fail bool
}{
{"", "", true},
{"x", "", true},
{"[", "", true},
{"[nope", "", true},
{"[test]", "", false},
{"[test] foo", "foo", false},
{"[test] foo/foo.vmx", "foo/foo.vmx", false},
{"[test]foo bar/foo bar.vmx", "foo bar/foo bar.vmx", false},
}
for _, test := range tests {
p, err := parseDatastorePath(test.dsPath)
if test.fail {
if err == nil {
t.Errorf("expected error for: %s", test.dsPath)
}
} else {
if err != nil {
t.Errorf("unexpected error '%#v' for: %s", err, test.dsPath)
} else {
if test.dsFile != p.Path {
t.Errorf("dsFile=%s", p.Path)
}
if p.Datastore != "test" {
t.Errorf("ds=%s", p.Datastore)
}
}
}
}
}
func TestRefreshDatastore(t *testing.T) {
tests := []struct {
dir string
fail bool
}{
{".", false},
{"-", true},
}
for _, test := range tests {
ds := &Datastore{}
ds.Info = &types.LocalDatastoreInfo{
DatastoreInfo: types.DatastoreInfo{
Url: test.dir,
},
}
res := ds.RefreshDatastore(nil)
err := res.Fault()
if test.fail {
if err == nil {
t.Error("expected error")
}
} else {
if err != nil {
t.Error(err)
}
}
}
}
func TestDatastoreHTTP(t *testing.T) {
ctx := context.Background()
src := "datastore_test.go"
dst := "tmp.go"
for i, model := range []*Model{ESX(), VPX()} {
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
if i == 0 {
// Enable use of SessionManagerGenericServiceTicket.HostName in govmomi, disabled by default.
opts := s.URL.Query()
opts.Set("GOVMOMI_USE_SERVICE_TICKET_HOSTNAME", "true")
s.URL.RawQuery = opts.Encode()
}
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
ds, err := finder.DefaultDatastore(ctx)
if err != nil {
t.Fatal(err)
}
if i == 0 {
// Cover the service ticket path
// TODO: govmomi requires HostSystem.Config.VirtualNicManagerInfo
// ctx = ds.HostContext(ctx, object.NewHostSystem(c.Client, esx.HostSystem.Reference()))
}
dsPath := ds.Path
if !c.IsVC() {
dc = nil // test using the default
}
fm := object.NewFileManager(c.Client)
browser, err := ds.Browser(ctx)
if err != nil {
t.Fatal(err)
}
download := func(name string, fail bool) {
st, serr := ds.Stat(ctx, name)
_, _, err = ds.Download(ctx, name, nil)
if fail {
if err == nil {
t.Fatal("expected Download error")
}
if serr == nil {
t.Fatal("expected Stat error")
}
} else {
if err != nil {
t.Errorf("Download error: %s", err)
}
if serr != nil {
t.Errorf("Stat error: %s", serr)
}
p := st.GetFileInfo().Path
if p != name {
t.Errorf("path=%s", p)
}
}
}
upload := func(name string, fail bool, method string) {
f, err := os.Open(src)
if err != nil {
t.Fatal(err)
}
defer f.Close()
p := soap.DefaultUpload
p.Method = method
err = ds.Upload(ctx, f, name, &p)
if fail {
if err == nil {
t.Fatalf("%s %s: expected error", method, name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
}
rm := func(name string, fail bool) {
task, err := fm.DeleteDatastoreFile(ctx, dsPath(name), dc)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Fatalf("rm %s: expected error", name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
}
mv := func(src string, dst string, fail bool, force bool) {
task, err := fm.MoveDatastoreFile(ctx, dsPath(src), dc, dsPath(dst), dc, force)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Fatalf("mv %s %s: expected error", src, dst)
}
} else {
if err != nil {
t.Fatal(err)
}
}
}
mkdir := func(name string, fail bool, p bool) {
err := fm.MakeDirectory(ctx, dsPath(name), dc, p)
if fail {
if err == nil {
t.Fatalf("mkdir %s: expected error", name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
}
stat := func(name string, fail bool) {
_, err := ds.Stat(ctx, name)
if fail {
if err == nil {
t.Fatalf("stat %s: expected error", name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
}
ls := func(name string, fail bool) []types.BaseFileInfo {
spec := types.HostDatastoreBrowserSearchSpec{
MatchPattern: []string{"*"},
}
task, err := browser.SearchDatastore(ctx, dsPath(name), &spec)
if err != nil {
t.Fatal(err)
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
if fail {
if err == nil {
t.Fatalf("ls %s: expected error", name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
return nil
}
return info.Result.(types.HostDatastoreBrowserSearchResults).File
}
lsr := func(name string, fail bool, query ...types.BaseFileQuery) []types.HostDatastoreBrowserSearchResults {
spec := types.HostDatastoreBrowserSearchSpec{
MatchPattern: []string{"*"},
Query: query,
}
task, err := browser.SearchDatastoreSubFolders(ctx, dsPath(name), &spec)
if err != nil {
t.Fatal(err)
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
if fail {
if err == nil {
t.Fatalf("find %s: expected error", name)
}
} else {
if err != nil {
t.Fatal(err)
}
}
return nil
}
return info.Result.(types.ArrayOfHostDatastoreBrowserSearchResults).HostDatastoreBrowserSearchResults
}
// GET file does not exist = fail
download(dst, true)
stat(dst, true)
ls(dst, true)
lsr(dst, true)
// delete file does not exist = fail
rm(dst, true)
// PUT file = ok
upload(dst, false, "PUT")
stat(dst, false)
ls("", false)
lsr("", false)
// GET file exists = ok
download(dst, false)
// POST file exists = fail
upload(dst, true, "POST")
// delete existing file = ok
rm(dst, false)
stat(dst, true)
// GET file does not exist = fail
download(dst, true)
// POST file does not exist = ok
upload(dst, false, "POST")
// PATCH method not supported = fail
upload(dst+".patch", true, "PATCH")
// PUT path is directory = fail
upload("", true, "PUT")
// mkdir parent does not exist = fail
mkdir("foo/bar", true, false)
// mkdir -p parent does not exist = ok
mkdir("foo/bar", false, true)
// mkdir = ok
mkdir("foo/bar/baz", false, false)
target := path.Join("foo", dst)
// mv dst not exist = ok
mv(dst, target, false, false)
// POST file does not exist = ok
upload(dst, false, "POST")
// mv dst exists = fail
mv(dst, target, true, false)
// mv dst exists, force=true = ok
mv(dst, target, false, true)
// mv src does not exist = fail
mv(dst, target, true, true)
// ls -R = ok
res := lsr("foo", false)
count := func(s string) int {
n := 0
for _, dir := range res {
for _, f := range dir.File {
if strings.HasSuffix(f.GetFileInfo().Path, s) {
n++
}
}
}
return n
}
n := len(res) + count("")
if n != 6 {
t.Errorf("ls -R foo==%d", n)
}
// test FileQuery
res = lsr("", false)
all := count(".vmdk") // foo-flat.vmdk + foo.vmdk
res = lsr("", false, new(types.VmDiskFileQuery))
allq := count(".vmdk") // foo.vmdk only
if allq*2 != all {
t.Errorf("ls -R *.vmdk: %d vs %d", all, allq)
}
res = lsr("", false, new(types.VmLogFileQuery), new(types.VmConfigFileQuery))
all = count("")
if all != model.Count().Machine*2 {
t.Errorf("ls -R vmware.log+.vmx: %d", all)
}
invalid := []string{
"", //InvalidDatastorePath
"[nope]", // InvalidDatastore
}
// test FileType details
mkdir("exts", false, false)
stat("exts", false)
exts := []string{"img", "iso", "log", "nvram", "vmdk", "vmx"}
for _, ext := range exts {
name := dst + "." + ext
upload(name, false, "POST")
stat(name, false)
}
for _, p := range invalid {
dsPath = func(name string) string {
return p
}
mv(target, dst, true, false)
mkdir("sorry", true, false)
rm(target, true)
ls(target, true)
}
// cover the dst failure path
for _, p := range invalid {
dsPath = func(name string) string {
if name == dst {
return p
}
return ds.Path(name)
}
mv(target, dst, true, false)
}
dsPath = func(name string) string {
return ds.Path("enoent")
}
ls(target, true)
// cover the case where datacenter or datastore lookup fails
for _, q := range []string{"dcName=nope", "dsName=nope"} {
u := *s.URL
u.RawQuery = q
u.Path = path.Join(folderPrefix, dst)
r, err := http.Get(u.String())
if err != nil {
t.Fatal(err)
}
if r.StatusCode == http.StatusOK {
t.Error("expected failure")
}
}
}
}

22
vendor/github.com/vmware/govmomi/simulator/doc.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package simulator is a mock framework for the vSphere API.
See also: https://github.com/vmware/govmomi/blob/master/vcsim/README.md
*/
package simulator

195
vendor/github.com/vmware/govmomi/simulator/dvs.go generated vendored Normal file
View File

@@ -0,0 +1,195 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type DistributedVirtualSwitch struct {
mo.DistributedVirtualSwitch
}
func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Task) soap.HasFault {
task := CreateTask(s, "addDVPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
f := Map.getEntityParent(s, "Folder").(*Folder)
for _, spec := range c.Spec {
pg := &DistributedVirtualPortgroup{}
pg.Name = spec.Name
pg.Entity().Name = pg.Name
if obj := Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
return nil, &types.DuplicateName{
Name: pg.Name,
Object: obj.Reference(),
}
}
f.putChild(pg)
pg.Key = pg.Self.Value
pg.Config = types.DVPortgroupConfigInfo{
Key: pg.Key,
Name: pg.Name,
NumPorts: spec.NumPorts,
DistributedVirtualSwitch: &s.Self,
DefaultPortConfig: spec.DefaultPortConfig,
Description: spec.Description,
Type: spec.Type,
Policy: spec.Policy,
PortNameFormat: spec.PortNameFormat,
Scope: spec.Scope,
VendorSpecificConfig: spec.VendorSpecificConfig,
ConfigVersion: spec.ConfigVersion,
AutoExpand: spec.AutoExpand,
VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
}
if pg.Config.DefaultPortConfig == nil {
pg.Config.DefaultPortConfig = &types.VMwareDVSPortSetting{
Vlan: new(types.VmwareDistributedVirtualSwitchVlanIdSpec),
}
}
pg.PortKeys = []string{}
s.Portgroup = append(s.Portgroup, pg.Self)
s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
for _, h := range s.Summary.HostMember {
pg.Host = append(pg.Host, h)
host := Map.Get(h).(*HostSystem)
Map.AppendReference(host, &host.Network, pg.Reference())
}
}
return nil, nil
})
return &methods.AddDVPortgroup_TaskBody{
Res: &types.AddDVPortgroup_TaskResponse{
Returnval: task.Run(),
},
}
}
func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_Task) soap.HasFault {
task := CreateTask(s, "reconfigureDvs", func(t *Task) (types.AnyType, types.BaseMethodFault) {
spec := req.Spec.GetDVSConfigSpec()
for _, member := range spec.Host {
h := Map.Get(member.Host)
if h == nil {
return nil, &types.ManagedObjectNotFound{Obj: member.Host}
}
host := h.(*HostSystem)
switch types.ConfigSpecOperation(member.Operation) {
case types.ConfigSpecOperationAdd:
if FindReference(host.Network, s.Self) != nil {
return nil, &types.AlreadyExists{Name: host.Name}
}
Map.AppendReference(host, &host.Network, s.Self)
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
Map.AddReference(pg, &pg.Host, member.Host)
}
case types.ConfigSpecOperationRemove:
for _, ref := range host.Vm {
vm := Map.Get(ref).(*VirtualMachine)
if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
return nil, &types.ResourceInUse{
Type: pg.Type,
Name: pg.Value,
}
}
}
Map.RemoveReference(host, &host.Network, s.Self)
RemoveReference(&s.Summary.HostMember, s.Self)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}
}
return nil, nil
})
return &methods.ReconfigureDvs_TaskBody{
Res: &types.ReconfigureDvs_TaskResponse{
Returnval: task.Run(),
},
}
}
func (s *DistributedVirtualSwitch) FetchDVPorts(req *types.FetchDVPorts) soap.HasFault {
body := &methods.FetchDVPortsBody{}
body.Res = &types.FetchDVPortsResponse{
Returnval: s.dvPortgroups(req.Criteria),
}
return body
}
func (s *DistributedVirtualSwitch) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
f := Map.getEntityParent(s, "Folder").(*Folder)
f.removeChild(s.Reference())
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}
func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitchPortCriteria) []types.DistributedVirtualPort {
// TODO(agui): Filter is not implemented yet
var res []types.DistributedVirtualPort
for _, ref := range s.Portgroup {
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid,
Key: pg.Key,
Config: types.DVPortConfigInfo{
Setting: pg.Config.DefaultPortConfig,
},
})
for _, key := range pg.PortKeys {
res = append(res, types.DistributedVirtualPort{
DvsUuid: s.Uuid,
Key: key,
Config: types.DVPortConfigInfo{
Setting: pg.Config.DefaultPortConfig,
},
})
}
}
return res
}

154
vendor/github.com/vmware/govmomi/simulator/dvs_test.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"reflect"
"testing"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/task"
"github.com/vmware/govmomi/vim25/types"
)
func TestDVS(t *testing.T) {
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
c := m.Service.client
finder := find.NewFinder(c, false)
dc, _ := finder.DatacenterList(ctx, "*")
finder.SetDatacenter(dc[0])
folders, _ := dc[0].Folders(ctx)
hosts, _ := finder.HostSystemList(ctx, "*/*")
dvs0 := object.NewDistributedVirtualSwitch(c, Map.Any("DistributedVirtualSwitch").Reference())
var spec types.DVSCreateSpec
spec.ConfigSpec = &types.VMwareDVSConfigSpec{}
spec.ConfigSpec.GetDVSConfigSpec().Name = "DVS1"
dtask, err := folders.NetworkFolder.CreateDVS(ctx, spec)
if err != nil {
t.Fatal(err)
}
info, err := dtask.WaitForResult(ctx, nil)
if err != nil {
t.Fatal(err)
}
dvs := object.NewDistributedVirtualSwitch(c, info.Result.(types.ManagedObjectReference))
config := &types.DVSConfigSpec{}
for _, host := range hosts {
config.Host = append(config.Host, types.DistributedVirtualSwitchHostMemberConfigSpec{
Host: host.Reference(),
})
}
tests := []struct {
op types.ConfigSpecOperation
pg string
err types.BaseMethodFault
}{
{types.ConfigSpecOperationAdd, "", nil}, // Add == OK
{types.ConfigSpecOperationAdd, "", &types.AlreadyExists{}}, // Add == fail (AlreadyExists)
{types.ConfigSpecOperationEdit, "", &types.NotSupported{}}, // Edit == fail (NotSupported)
{types.ConfigSpecOperationRemove, "", nil}, // Remove == OK
{types.ConfigSpecOperationAdd, "", nil}, // Add == OK
{types.ConfigSpecOperationAdd, "DVPG0", nil}, // Add PG == OK
{types.ConfigSpecOperationRemove, "", &types.ResourceInUse{}}, // Remove dvs0 == fail (ResourceInUse)
{types.ConfigSpecOperationRemove, "", nil}, // Remove dvs1 == OK (no VMs attached)
{types.ConfigSpecOperationRemove, "", &types.ManagedObjectNotFound{}}, // Remove == fail (ManagedObjectNotFound)
}
for x, test := range tests {
dswitch := dvs
switch test.err.(type) {
case *types.ManagedObjectNotFound:
for i := range config.Host {
config.Host[i].Host.Value = "enoent"
}
case *types.ResourceInUse:
dswitch = dvs0
}
if test.pg == "" {
for i := range config.Host {
config.Host[i].Operation = string(test.op)
}
dtask, err = dswitch.Reconfigure(ctx, config)
} else {
switch test.op {
case types.ConfigSpecOperationAdd:
dtask, err = dswitch.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{{Name: test.pg}})
}
}
if err != nil {
t.Fatal(err)
}
err = dtask.Wait(ctx)
if test.err == nil {
if err != nil {
t.Fatalf("%d: %s", x, err)
}
continue
}
if err == nil {
t.Errorf("expected error in test %d", x)
}
if reflect.TypeOf(test.err) != reflect.TypeOf(err.(task.Error).Fault()) {
t.Errorf("expected %T fault in test %d", test.err, x)
}
}
ports, err := dvs.FetchDVPorts(ctx, nil)
if err != nil {
t.Fatal(err)
}
if len(ports) != 2 {
t.Fatalf("expected 2 ports in DVPorts; got %d", len(ports))
}
dtask, err = dvs.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = dtask.Wait(ctx)
if err != nil {
t.Fatal(err)
}
}

46
vendor/github.com/vmware/govmomi/simulator/entity.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func RenameTask(e mo.Entity, r *types.Rename_Task) soap.HasFault {
task := CreateTask(e, "rename", func(t *Task) (types.AnyType, types.BaseMethodFault) {
obj := Map.Get(r.This).(mo.Entity).Entity()
if parent, ok := Map.Get(*obj.Parent).(*Folder); ok {
if Map.FindByName(r.NewName, parent.ChildEntity) != nil {
return nil, &types.InvalidArgument{InvalidProperty: "name"}
}
}
obj.Name = r.NewName
return nil, nil
})
return &methods.Rename_TaskBody{
Res: &types.Rename_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,70 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
func TestRename(t *testing.T) {
m := VPX()
m.Datacenter = 2
m.Folder = 2
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
dc := Map.Any("Datacenter").(*Datacenter)
vmFolder := Map.Get(dc.VmFolder).(*Folder)
f1 := Map.Get(vmFolder.ChildEntity[0]).(*Folder) // "F1"
id := vmFolder.CreateFolder(&types.CreateFolder{
This: vmFolder.Reference(),
Name: "F2",
}).(*methods.CreateFolderBody).Res.Returnval
f2 := Map.Get(id).(*Folder) // "F2"
states := []types.TaskInfoState{types.TaskInfoStateError, types.TaskInfoStateSuccess}
name := f1.Name
for _, expect := range states {
id = f2.RenameTask(&types.Rename_Task{
This: f2.Reference(),
NewName: name,
}).(*methods.Rename_TaskBody).Res.Returnval
task := Map.Get(id).(*Task)
if task.Info.State != expect {
t.Errorf("state=%s", task.Info.State)
}
name = name + "-uniq"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// Datacenter is the default template for Datacenter properties.
// Capture method:
// govc datacenter.info -dump
var Datacenter = mo.Datacenter{
ManagedEntity: mo.ManagedEntity{
ExtensibleManagedObject: mo.ExtensibleManagedObject{
Self: types.ManagedObjectReference{Type: "Datacenter", Value: "ha-datacenter"},
Value: nil,
AvailableField: nil,
},
Parent: (*types.ManagedObjectReference)(nil),
CustomValue: nil,
OverallStatus: "",
ConfigStatus: "",
ConfigIssue: nil,
EffectiveRole: nil,
Permission: nil,
Name: "ha-datacenter",
DisabledMethod: nil,
RecentTask: nil,
DeclaredAlarmState: nil,
TriggeredAlarmState: nil,
AlarmActionsEnabled: (*bool)(nil),
Tag: nil,
},
VmFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-vm"},
HostFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-host"},
DatastoreFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-datastore"},
NetworkFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-network"},
Datastore: []types.ManagedObjectReference{
{Type: "Datastore", Value: "57089c25-85e3ccd4-17b6-000c29d0beb3"},
},
Network: []types.ManagedObjectReference{
{Type: "Network", Value: "HaNetwork-VM Network"},
},
Configuration: types.DatacenterConfigInfo{},
}

20
vendor/github.com/vmware/govmomi/simulator/esx/doc.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package esx contains SOAP responses from an ESX server, captured using `govc ... -dump`.
*/
package esx

View File

@@ -0,0 +1,236 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// EventInfo is the default template for the EventManager description.eventInfo property.
// Capture method:
// govc object.collect -s -dump EventManager:ha-eventmgr description.eventInfo
// The captured list has been manually pruned and FullFormat fields changed to use Go's template variable syntax.
var EventInfo = []types.EventDescriptionEventDetail{
{
Key: "UserLoginSessionEvent",
Description: "User login",
Category: "info",
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged in as {{.UserAgent}}",
},
{
Key: "UserLogoutSessionEvent",
Description: "User logout",
Category: "info",
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged out (login time: {{.LoginTime}}, number of API invocations: {{.CallCount}}, user agent: {{.UserAgent}})",
},
{
Key: "DatacenterCreatedEvent",
Description: "Datacenter created",
Category: "info",
FullFormat: "Created datacenter {{.Datacenter.Name}} in folder {{.Parent.Name}}",
},
{
Key: "DatastoreFileMovedEvent",
Description: "File or directory moved to datastore",
Category: "info",
FullFormat: "Move of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
},
{
Key: "DatastoreFileCopiedEvent",
Description: "File or directory copied to datastore",
Category: "info",
FullFormat: "Copy of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
},
{
Key: "DatastoreFileDeletedEvent",
Description: "File or directory deleted",
Category: "info",
FullFormat: "Deletion of file or directory {{.TargetFile}} from {{.Datastore.Name}} was initiated",
},
{
Key: "EnteringMaintenanceModeEvent",
Description: "Entering maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has started to enter maintenance mode",
},
{
Key: "EnteredMaintenanceModeEvent",
Description: "Entered maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has entered maintenance mode",
},
{
Key: "ExitMaintenanceModeEvent",
Description: "Exit maintenance mode",
Category: "info",
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has exited maintenance mode",
},
{
Key: "VmSuspendedEvent",
Description: "VM suspended",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is suspended",
},
{
Key: "VmMigratedEvent",
Description: "VM migrated",
Category: "info",
FullFormat: "Migration of virtual machine {{.Vm.Name}} from {{.SourceHost.Name}, {{.SourceDatastore.Name}} to {{.Host.Name}, {{.Ds.Name}} completed",
},
{
Key: "VmBeingMigratedEvent",
Description: "VM migrating",
Category: "info",
FullFormat: "Relocating {{.Vm.Name}} from {{.Host.Name}, {{.Ds.Name}} in {{.Datacenter.Name}} to {{.DestHost.Name}, {{.DestDatastore.Name}} in {{.DestDatacenter.Name}}",
},
{
Key: "VmMacAssignedEvent",
Description: "VM MAC assigned",
Category: "info",
FullFormat: "New MAC address ({{.Mac}}) assigned to adapter {{.Adapter}} for {{.Vm.Name}}",
},
{
Key: "VmRegisteredEvent",
Description: "VM registered",
Category: "info",
FullFormat: "Registered {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmReconfiguredEvent",
Description: "VM reconfigured",
Category: "info",
FullFormat: "Reconfigured {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmGuestRebootEvent",
Description: "Guest reboot",
Category: "info",
FullFormat: "Guest OS reboot for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmBeingClonedEvent",
Description: "VM being cloned",
Category: "info",
FullFormat: "Cloning {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} to {{.DestName}} on host {{.DestHost.Name}}",
},
{
Key: "VmClonedEvent",
Description: "VM cloned",
Category: "info",
FullFormat: "Clone of {{.SourceVm.Name}} completed",
},
{
Key: "VmBeingDeployedEvent",
Description: "Deploying VM",
Category: "info",
FullFormat: "Deploying {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} from template {{.SrcTemplate.Name}}",
},
{
Key: "VmDeployedEvent",
Description: "VM deployed",
Category: "info",
FullFormat: "Template {{.SrcTemplate.Name}} deployed on host {{.Host.Name}}",
},
{
Key: "VmInstanceUuidAssignedEvent",
Description: "Assign a new instance UUID",
Category: "info",
FullFormat: "Assign a new instance UUID ({{.InstanceUuid}}) to {{.Vm.Name}}",
},
{
Key: "VmPoweredOnEvent",
Description: "VM powered on",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered on",
},
{
Key: "VmStartingEvent",
Description: "VM starting",
Category: "info",
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is starting",
},
{
Key: "VmSuspendingEvent",
Description: "VM being suspended",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is being suspended",
},
{
Key: "VmResumingEvent",
Description: "VM resuming",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is resumed",
},
{
Key: "VmBeingCreatedEvent",
Description: "Creating VM",
Category: "info",
FullFormat: "Creating {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmCreatedEvent",
Description: "VM created",
Category: "info",
FullFormat: "Created virtual machine {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmRemovedEvent",
Description: "VM removed",
Category: "info",
FullFormat: "Removed {{.Vm.Name}} on {{.Host.Name}} from {{.Datacenter.Name}}",
},
{
Key: "VmResettingEvent",
Description: "VM resetting",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is reset",
},
{
Key: "VmGuestShutdownEvent",
Description: "Guest OS shut down",
Category: "info",
FullFormat: "Guest OS shut down for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmUuidAssignedEvent",
Description: "VM UUID assigned",
Category: "info",
FullFormat: "Assigned new BIOS UUID ({{.Uuid}}) to {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
{
Key: "VmPoweredOffEvent",
Description: "VM powered off",
Category: "info",
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered off",
},
{
Key: "VmRelocatedEvent",
Description: "VM relocated",
Category: "info",
FullFormat: "Completed the relocation of the virtual machine",
},
{
Key: "DrsVmMigratedEvent",
Description: "DRS VM migrated",
Category: "info",
FullFormat: "DRS migrated {{.Vm.Name}} from {{.SourceHost.Name}} to {{.Host.Name}} in cluster {{.ComputeResource.Name}} in {{.Datacenter.Name}}",
},
{
Key: "DrsVmPoweredOnEvent",
Description: "DRS VM powered on",
Category: "info",
FullFormat: "DRS powered On {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
},
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,864 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import (
"time"
"github.com/vmware/govmomi/vim25/types"
)
// HostHardwareInfo is the default template for the HostSystem hardware property.
// Capture method:
// govc object.collect -s -dump HostSystem:ha-host hardware
var HostHardwareInfo = &types.HostHardwareInfo{
SystemInfo: types.HostSystemInfo{
Vendor: "VMware, Inc.",
Model: "VMware Virtual Platform",
Uuid: "e88d4d56-9f1e-3ea1-71fa-13a8e1a7fd70",
OtherIdentifyingInfo: []types.HostSystemIdentificationInfo{
{
IdentifierValue: " No Asset Tag",
IdentifierType: &types.ElementDescription{
Description: types.Description{
Label: "Asset Tag",
Summary: "Asset tag of the system",
},
Key: "AssetTag",
},
},
{
IdentifierValue: "[MS_VM_CERT/SHA1/27d66596a61c48dd3dc7216fd715126e33f59ae7]",
IdentifierType: &types.ElementDescription{
Description: types.Description{
Label: "OEM specific string",
Summary: "OEM specific string",
},
Key: "OemSpecificString",
},
},
{
IdentifierValue: "Welcome to the Virtual Machine",
IdentifierType: &types.ElementDescription{
Description: types.Description{
Label: "OEM specific string",
Summary: "OEM specific string",
},
Key: "OemSpecificString",
},
},
{
IdentifierValue: "VMware-56 4d 8d e8 1e 9f a1 3e-71 fa 13 a8 e1 a7 fd 70",
IdentifierType: &types.ElementDescription{
Description: types.Description{
Label: "Service tag",
Summary: "Service tag of the system",
},
Key: "ServiceTag",
},
},
},
},
CpuPowerManagementInfo: &types.HostCpuPowerManagementInfo{
CurrentPolicy: "Balanced",
HardwareSupport: "",
},
CpuInfo: types.HostCpuInfo{
NumCpuPackages: 2,
NumCpuCores: 2,
NumCpuThreads: 2,
Hz: 3591345000,
},
CpuPkg: []types.HostCpuPackage{
{
Index: 0,
Vendor: "intel",
Hz: 3591345000,
BusHz: 115849838,
Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz",
ThreadId: []int16{0},
CpuFeature: []types.HostCpuIdInfo{
{
Level: 0,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
},
{
Level: 1,
Vendor: "",
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
Ebx: "0000:0000:0000:0001:0000:1000:0000:0000",
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
},
{
Level: -2147483648,
Vendor: "",
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
{
Level: -2147483647,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
},
{
Level: -2147483640,
Vendor: "",
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
},
},
{
Index: 1,
Vendor: "intel",
Hz: 3591345000,
BusHz: 115849838,
Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz",
ThreadId: []int16{1},
CpuFeature: []types.HostCpuIdInfo{
{
Level: 0,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
},
{
Level: 1,
Vendor: "",
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
Ebx: "0000:0010:0000:0001:0000:1000:0000:0000",
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
},
{
Level: -2147483648,
Vendor: "",
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
{
Level: -2147483647,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
},
{
Level: -2147483640,
Vendor: "",
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
},
},
},
MemorySize: 4294430720,
NumaInfo: &types.HostNumaInfo{
Type: "NUMA",
NumNodes: 1,
NumaNode: []types.HostNumaNode{
{
TypeId: 0x0,
CpuID: []int16{1, 0},
MemoryRangeBegin: 4294967296,
MemoryRangeLength: 1073741824,
},
},
},
SmcPresent: types.NewBool(false),
PciDevice: []types.HostPciDevice{
{
Id: "0000:00:00.0",
ClassId: 1536,
Bus: 0x0,
Slot: 0x0,
Function: 0x0,
VendorId: -32634,
SubVendorId: 5549,
VendorName: "Intel Corporation",
DeviceId: 29072,
SubDeviceId: 6518,
ParentBridge: "",
DeviceName: "Virtual Machine Chipset",
},
{
Id: "0000:00:01.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x1,
Function: 0x0,
VendorId: -32634,
SubVendorId: 0,
VendorName: "Intel Corporation",
DeviceId: 29073,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "440BX/ZX/DX - 82443BX/ZX/DX AGP bridge",
},
{
Id: "0000:00:07.0",
ClassId: 1537,
Bus: 0x0,
Slot: 0x7,
Function: 0x0,
VendorId: -32634,
SubVendorId: 5549,
VendorName: "Intel Corporation",
DeviceId: 28944,
SubDeviceId: 6518,
ParentBridge: "",
DeviceName: "Virtual Machine Chipset",
},
{
Id: "0000:00:07.1",
ClassId: 257,
Bus: 0x0,
Slot: 0x7,
Function: 0x1,
VendorId: -32634,
SubVendorId: 5549,
VendorName: "Intel Corporation",
DeviceId: 28945,
SubDeviceId: 6518,
ParentBridge: "",
DeviceName: "PIIX4 for 430TX/440BX/MX IDE Controller",
},
{
Id: "0000:00:07.3",
ClassId: 1664,
Bus: 0x0,
Slot: 0x7,
Function: 0x3,
VendorId: -32634,
SubVendorId: 5549,
VendorName: "Intel Corporation",
DeviceId: 28947,
SubDeviceId: 6518,
ParentBridge: "",
DeviceName: "Virtual Machine Chipset",
},
{
Id: "0000:00:07.7",
ClassId: 2176,
Bus: 0x0,
Slot: 0x7,
Function: 0x7,
VendorId: 5549,
SubVendorId: 5549,
VendorName: "VMware",
DeviceId: 1856,
SubDeviceId: 1856,
ParentBridge: "",
DeviceName: "Virtual Machine Communication Interface",
},
{
Id: "0000:00:0f.0",
ClassId: 768,
Bus: 0x0,
Slot: 0xf,
Function: 0x0,
VendorId: 5549,
SubVendorId: 5549,
VendorName: "VMware",
DeviceId: 1029,
SubDeviceId: 1029,
ParentBridge: "",
DeviceName: "SVGA II Adapter",
},
{
Id: "0000:00:11.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x11,
Function: 0x0,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1936,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI bridge",
},
{
Id: "0000:00:15.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x0,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.1",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x1,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.2",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x2,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.3",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x3,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.4",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x4,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.5",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x5,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.6",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x6,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:15.7",
ClassId: 1540,
Bus: 0x0,
Slot: 0x15,
Function: 0x7,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x0,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.1",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x1,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.2",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x2,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.3",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x3,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.4",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x4,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.5",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x5,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.6",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x6,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:16.7",
ClassId: 1540,
Bus: 0x0,
Slot: 0x16,
Function: 0x7,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x0,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.1",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x1,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.2",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x2,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.3",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x3,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.4",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x4,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.5",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x5,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.6",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x6,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:17.7",
ClassId: 1540,
Bus: 0x0,
Slot: 0x17,
Function: 0x7,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.0",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x0,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.1",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x1,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.2",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x2,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.3",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x3,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.4",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x4,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.5",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x5,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.6",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x6,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:00:18.7",
ClassId: 1540,
Bus: 0x0,
Slot: 0x18,
Function: 0x7,
VendorId: 5549,
SubVendorId: 0,
VendorName: "VMware",
DeviceId: 1952,
SubDeviceId: 0,
ParentBridge: "",
DeviceName: "PCI Express Root Port",
},
{
Id: "0000:03:00.0",
ClassId: 263,
Bus: 0x3,
Slot: 0x0,
Function: 0x0,
VendorId: 5549,
SubVendorId: 5549,
VendorName: "VMware",
DeviceId: 1984,
SubDeviceId: 1984,
ParentBridge: "0000:00:15.0",
DeviceName: "PVSCSI SCSI Controller",
},
{
Id: "0000:0b:00.0",
ClassId: 512,
Bus: 0xb,
Slot: 0x0,
Function: 0x0,
VendorId: 5549,
SubVendorId: 5549,
VendorName: "VMware Inc.",
DeviceId: 1968,
SubDeviceId: 1968,
ParentBridge: "0000:00:16.0",
DeviceName: "vmxnet3 Virtual Ethernet Controller",
},
{
Id: "0000:13:00.0",
ClassId: 512,
Bus: 0x13,
Slot: 0x0,
Function: 0x0,
VendorId: 5549,
SubVendorId: 5549,
VendorName: "VMware Inc.",
DeviceId: 1968,
SubDeviceId: 1968,
ParentBridge: "0000:00:17.0",
DeviceName: "vmxnet3 Virtual Ethernet Controller",
},
},
CpuFeature: []types.HostCpuIdInfo{
{
Level: 0,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
},
{
Level: 1,
Vendor: "",
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
Ebx: "0000:0000:0000:0001:0000:1000:0000:0000",
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
},
{
Level: -2147483648,
Vendor: "",
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
{
Level: -2147483647,
Vendor: "",
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
},
{
Level: -2147483640,
Vendor: "",
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
},
},
BiosInfo: &types.HostBIOSInfo{
BiosVersion: "6.00",
ReleaseDate: nil,
Vendor: "",
MajorRelease: 0,
MinorRelease: 0,
FirmwareMajorRelease: 0,
FirmwareMinorRelease: 0,
},
ReliableMemoryInfo: &types.HostReliableMemoryInfo{},
}
func init() {
date, _ := time.Parse("2006-01-02", "2015-07-02")
HostHardwareInfo.BiosInfo.ReleaseDate = &date
}

View File

@@ -0,0 +1,346 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// HostStorageDeviceInfo is the default template for the HostSystem config.storageDevice property.
// Capture method:
// govc object.collect -s -dump HostSystem:ha-host config.storageDevice
var HostStorageDeviceInfo = types.HostStorageDeviceInfo{
HostBusAdapter: []types.BaseHostHostBusAdapter{
&types.HostParallelScsiHba{
HostHostBusAdapter: types.HostHostBusAdapter{
Key: "key-vim.host.ParallelScsiHba-vmhba0",
Device: "vmhba0",
Bus: 3,
Status: "unknown",
Model: "PVSCSI SCSI Controller",
Driver: "pvscsi",
Pci: "0000:03:00.0",
},
},
&types.HostBlockHba{
HostHostBusAdapter: types.HostHostBusAdapter{
Key: "key-vim.host.BlockHba-vmhba1",
Device: "vmhba1",
Bus: 0,
Status: "unknown",
Model: "PIIX4 for 430TX/440BX/MX IDE Controller",
Driver: "vmkata",
Pci: "0000:00:07.1",
},
},
&types.HostBlockHba{
HostHostBusAdapter: types.HostHostBusAdapter{
Key: "key-vim.host.BlockHba-vmhba64",
Device: "vmhba64",
Bus: 0,
Status: "unknown",
Model: "PIIX4 for 430TX/440BX/MX IDE Controller",
Driver: "vmkata",
Pci: "0000:00:07.1",
},
},
},
ScsiLun: []types.BaseScsiLun{
&types.ScsiLun{
HostDevice: types.HostDevice{
DeviceName: "/vmfs/devices/cdrom/mpx.vmhba1:C0:T0:L0",
DeviceType: "cdrom",
},
Key: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
Uuid: "0005000000766d686261313a303a30",
Descriptor: []types.ScsiLunDescriptor{
{
Quality: "lowQuality",
Id: "mpx.vmhba1:C0:T0:L0",
},
{
Quality: "lowQuality",
Id: "vml.0005000000766d686261313a303a30",
},
{
Quality: "lowQuality",
Id: "0005000000766d686261313a303a30",
},
},
CanonicalName: "mpx.vmhba1:C0:T0:L0",
DisplayName: "Local NECVMWar CD-ROM (mpx.vmhba1:C0:T0:L0)",
LunType: "cdrom",
Vendor: "NECVMWar",
Model: "VMware IDE CDR00",
Revision: "1.00",
ScsiLevel: 5,
SerialNumber: "unavailable",
DurableName: (*types.ScsiLunDurableName)(nil),
AlternateName: []types.ScsiLunDurableName{
{
Namespace: "GENERIC_VPD",
NamespaceId: 0x5,
Data: []uint8{0x2d, 0x37, 0x39},
},
{
Namespace: "GENERIC_VPD",
NamespaceId: 0x5,
Data: []uint8{0x30},
},
},
StandardInquiry: []uint8{0x30},
QueueDepth: 0,
OperationalState: []string{"ok"},
Capabilities: &types.ScsiLunCapabilities{},
VStorageSupport: "vStorageUnsupported",
ProtocolEndpoint: types.NewBool(false),
},
&types.HostScsiDisk{
ScsiLun: types.ScsiLun{
HostDevice: types.HostDevice{
DeviceName: "/vmfs/devices/disks/mpx.vmhba0:C0:T0:L0",
DeviceType: "disk",
},
Key: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
Uuid: "0000000000766d686261303a303a30",
Descriptor: []types.ScsiLunDescriptor{
{
Quality: "lowQuality",
Id: "mpx.vmhba0:C0:T0:L0",
},
{
Quality: "lowQuality",
Id: "vml.0000000000766d686261303a303a30",
},
{
Quality: "lowQuality",
Id: "0000000000766d686261303a303a30",
},
},
CanonicalName: "mpx.vmhba0:C0:T0:L0",
DisplayName: "Local VMware, Disk (mpx.vmhba0:C0:T0:L0)",
LunType: "disk",
Vendor: "VMware, ",
Model: "VMware Virtual S",
Revision: "1.0 ",
ScsiLevel: 2,
SerialNumber: "unavailable",
DurableName: (*types.ScsiLunDurableName)(nil),
AlternateName: []types.ScsiLunDurableName{
{
Namespace: "GENERIC_VPD",
NamespaceId: 0x5,
Data: []uint8{0x2d, 0x37, 0x39},
},
{
Namespace: "GENERIC_VPD",
NamespaceId: 0x5,
Data: []uint8{0x30},
},
},
StandardInquiry: []uint8{0x30},
QueueDepth: 1024,
OperationalState: []string{"ok"},
Capabilities: &types.ScsiLunCapabilities{},
VStorageSupport: "vStorageUnsupported",
ProtocolEndpoint: types.NewBool(false),
},
Capacity: types.HostDiskDimensionsLba{
BlockSize: 512,
Block: 67108864,
},
DevicePath: "/vmfs/devices/disks/mpx.vmhba0:C0:T0:L0",
Ssd: types.NewBool(true),
LocalDisk: types.NewBool(true),
PhysicalLocation: nil,
EmulatedDIXDIFEnabled: types.NewBool(false),
VsanDiskInfo: (*types.VsanHostVsanDiskInfo)(nil),
ScsiDiskType: "native512",
},
},
ScsiTopology: &types.HostScsiTopology{
Adapter: []types.HostScsiTopologyInterface{
{
Key: "key-vim.host.ScsiTopology.Interface-vmhba0",
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
Target: []types.HostScsiTopologyTarget{
{
Key: "key-vim.host.ScsiTopology.Target-vmhba0:0:0",
Target: 0,
Lun: []types.HostScsiTopologyLun{
{
Key: "key-vim.host.ScsiTopology.Lun-0000000000766d686261303a303a30",
Lun: 0,
ScsiLun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
},
},
Transport: &types.HostParallelScsiTargetTransport{},
},
},
},
{
Key: "key-vim.host.ScsiTopology.Interface-vmhba1",
Adapter: "key-vim.host.BlockHba-vmhba1",
Target: []types.HostScsiTopologyTarget{
{
Key: "key-vim.host.ScsiTopology.Target-vmhba1:0:0",
Target: 0,
Lun: []types.HostScsiTopologyLun{
{
Key: "key-vim.host.ScsiTopology.Lun-0005000000766d686261313a303a30",
Lun: 0,
ScsiLun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
},
},
Transport: &types.HostBlockAdapterTargetTransport{},
},
},
},
{
Key: "key-vim.host.ScsiTopology.Interface-vmhba64",
Adapter: "key-vim.host.BlockHba-vmhba64",
Target: nil,
},
},
},
MultipathInfo: &types.HostMultipathInfo{
Lun: []types.HostMultipathInfoLogicalUnit{
{
Key: "key-vim.host.MultipathInfo.LogicalUnit-0005000000766d686261313a303a30",
Id: "0005000000766d686261313a303a30",
Lun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
Path: []types.HostMultipathInfoPath{
{
Key: "key-vim.host.MultipathInfo.Path-vmhba1:C0:T0:L0",
Name: "vmhba1:C0:T0:L0",
PathState: "active",
State: "active",
IsWorkingPath: types.NewBool(true),
Adapter: "key-vim.host.BlockHba-vmhba1",
Lun: "key-vim.host.MultipathInfo.LogicalUnit-0005000000766d686261313a303a30",
Transport: &types.HostBlockAdapterTargetTransport{},
},
},
Policy: &types.HostMultipathInfoFixedLogicalUnitPolicy{
HostMultipathInfoLogicalUnitPolicy: types.HostMultipathInfoLogicalUnitPolicy{
Policy: "VMW_PSP_FIXED",
},
Prefer: "vmhba1:C0:T0:L0",
},
StorageArrayTypePolicy: &types.HostMultipathInfoLogicalUnitStorageArrayTypePolicy{
Policy: "VMW_SATP_LOCAL",
},
},
{
Key: "key-vim.host.MultipathInfo.LogicalUnit-0000000000766d686261303a303a30",
Id: "0000000000766d686261303a303a30",
Lun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
Path: []types.HostMultipathInfoPath{
{
Key: "key-vim.host.MultipathInfo.Path-vmhba0:C0:T0:L0",
Name: "vmhba0:C0:T0:L0",
PathState: "active",
State: "active",
IsWorkingPath: types.NewBool(true),
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
Lun: "key-vim.host.MultipathInfo.LogicalUnit-0000000000766d686261303a303a30",
Transport: &types.HostParallelScsiTargetTransport{},
},
},
Policy: &types.HostMultipathInfoFixedLogicalUnitPolicy{
HostMultipathInfoLogicalUnitPolicy: types.HostMultipathInfoLogicalUnitPolicy{
Policy: "VMW_PSP_FIXED",
},
Prefer: "vmhba0:C0:T0:L0",
},
StorageArrayTypePolicy: &types.HostMultipathInfoLogicalUnitStorageArrayTypePolicy{
Policy: "VMW_SATP_LOCAL",
},
},
},
},
PlugStoreTopology: &types.HostPlugStoreTopology{
Adapter: []types.HostPlugStoreTopologyAdapter{
{
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba0",
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0"},
},
{
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba1",
Adapter: "key-vim.host.BlockHba-vmhba1",
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
},
{
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba64",
Adapter: "key-vim.host.BlockHba-vmhba64",
Path: nil,
},
},
Path: []types.HostPlugStoreTopologyPath{
{
Key: "key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0",
Name: "vmhba0:C0:T0:L0",
ChannelNumber: 0,
TargetNumber: 0,
LunNumber: 0,
Adapter: "key-vim.host.PlugStoreTopology.Adapter-vmhba0",
Target: "key-vim.host.PlugStoreTopology.Target-pscsi.0:0",
Device: "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30",
},
{
Key: "key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0",
Name: "vmhba1:C0:T0:L0",
ChannelNumber: 0,
TargetNumber: 0,
LunNumber: 0,
Adapter: "key-vim.host.PlugStoreTopology.Adapter-vmhba1",
Target: "key-vim.host.PlugStoreTopology.Target-ide.0:0",
Device: "key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30",
},
},
Target: []types.HostPlugStoreTopologyTarget{
{
Key: "key-vim.host.PlugStoreTopology.Target-pscsi.0:0",
Transport: &types.HostParallelScsiTargetTransport{},
},
{
Key: "key-vim.host.PlugStoreTopology.Target-ide.0:0",
Transport: &types.HostBlockAdapterTargetTransport{},
},
},
Device: []types.HostPlugStoreTopologyDevice{
{
Key: "key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30",
Lun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
},
{
Key: "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30",
Lun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0"},
},
},
Plugin: []types.HostPlugStoreTopologyPlugin{
{
Key: "key-vim.host.PlugStoreTopology.Plugin-NMP",
Name: "NMP",
Device: []string{"key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30", "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30"},
ClaimedPath: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0", "key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
},
},
},
SoftwareInternetScsiEnabled: false,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import (
"time"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// ResourcePool is the default template for ResourcePool properties.
// Capture method:
// govc pool.info "*" -dump
var ResourcePool = mo.ResourcePool{
ManagedEntity: mo.ManagedEntity{
ExtensibleManagedObject: mo.ExtensibleManagedObject{
Self: types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
Value: nil,
AvailableField: nil,
},
Parent: &types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"},
CustomValue: nil,
OverallStatus: "green",
ConfigStatus: "green",
ConfigIssue: nil,
EffectiveRole: []int32{-1},
Permission: nil,
Name: "Resources",
DisabledMethod: []string{"CreateVApp", "CreateChildVM_Task"},
RecentTask: nil,
DeclaredAlarmState: nil,
TriggeredAlarmState: nil,
AlarmActionsEnabled: (*bool)(nil),
Tag: nil,
},
Summary: &types.ResourcePoolSummary{
DynamicData: types.DynamicData{},
Name: "Resources",
Config: types.ResourceConfigSpec{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
ChangeVersion: "",
LastModified: (*time.Time)(nil),
CpuAllocation: types.ResourceAllocationInfo{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(4121),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(4121),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
Level: "custom",
},
OverheadLimit: nil,
},
MemoryAllocation: types.ResourceAllocationInfo{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(961),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(961),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
Level: "custom",
},
OverheadLimit: nil,
},
},
Runtime: types.ResourcePoolRuntimeInfo{
DynamicData: types.DynamicData{},
Memory: types.ResourcePoolResourceUsage{
DynamicData: types.DynamicData{},
ReservationUsed: 0,
ReservationUsedForVm: 0,
UnreservedForPool: 1007681536,
UnreservedForVm: 1007681536,
OverallUsage: 0,
MaxUsage: 1007681536,
},
Cpu: types.ResourcePoolResourceUsage{
DynamicData: types.DynamicData{},
ReservationUsed: 0,
ReservationUsedForVm: 0,
UnreservedForPool: 4121,
UnreservedForVm: 4121,
OverallUsage: 0,
MaxUsage: 4121,
},
OverallStatus: "green",
},
QuickStats: (*types.ResourcePoolQuickStats)(nil),
ConfiguredMemoryMB: 0,
},
Runtime: types.ResourcePoolRuntimeInfo{
DynamicData: types.DynamicData{},
Memory: types.ResourcePoolResourceUsage{
DynamicData: types.DynamicData{},
ReservationUsed: 0,
ReservationUsedForVm: 0,
UnreservedForPool: 1007681536,
UnreservedForVm: 1007681536,
OverallUsage: 0,
MaxUsage: 1007681536,
},
Cpu: types.ResourcePoolResourceUsage{
DynamicData: types.DynamicData{},
ReservationUsed: 0,
ReservationUsedForVm: 0,
UnreservedForPool: 4121,
UnreservedForVm: 4121,
OverallUsage: 0,
MaxUsage: 4121,
},
OverallStatus: "green",
},
Owner: types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"},
ResourcePool: nil,
Vm: nil,
Config: types.ResourceConfigSpec{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
ChangeVersion: "",
LastModified: (*time.Time)(nil),
CpuAllocation: types.ResourceAllocationInfo{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(4121),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(4121),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
Level: "custom",
},
OverheadLimit: nil,
},
MemoryAllocation: types.ResourceAllocationInfo{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(961),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(961),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
Level: "custom",
},
OverheadLimit: nil,
},
},
ChildConfiguration: nil,
}

View File

@@ -0,0 +1,76 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// RootFolder is the default template for the ServiceContent rootFolder property.
// Capture method:
// govc folder.info -dump /
var RootFolder = mo.Folder{
ManagedEntity: mo.ManagedEntity{
ExtensibleManagedObject: mo.ExtensibleManagedObject{
Self: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
Value: nil,
AvailableField: nil,
},
Parent: (*types.ManagedObjectReference)(nil),
CustomValue: nil,
OverallStatus: "green",
ConfigStatus: "green",
ConfigIssue: nil,
EffectiveRole: []int32{-1},
Permission: []types.Permission{
{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
Principal: "vpxuser",
Group: false,
RoleId: -1,
Propagate: true,
},
{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
Principal: "dcui",
Group: false,
RoleId: -1,
Propagate: true,
},
{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
Principal: "root",
Group: false,
RoleId: -1,
Propagate: true,
},
},
Name: "ha-folder-root",
DisabledMethod: nil,
RecentTask: nil,
DeclaredAlarmState: nil,
TriggeredAlarmState: nil,
AlarmActionsEnabled: (*bool)(nil),
Tag: nil,
},
ChildType: []string{"Datacenter"},
ChildEntity: nil,
}

View File

@@ -0,0 +1,86 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// ServiceContent is the default template for the ServiceInstance content property.
// Capture method:
// govc object.collect -s -dump - content
var ServiceContent = types.ServiceContent{
RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "ha-property-collector"},
ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"},
About: types.AboutInfo{
Name: "VMware ESXi",
FullName: "VMware ESXi 6.5.0 build-5969303",
Vendor: "VMware, Inc.",
Version: "6.5.0",
Build: "5969303",
LocaleVersion: "INTL",
LocaleBuild: "000",
OsType: "vmnix-x86",
ProductLineId: "embeddedEsx",
ApiType: "HostAgent",
ApiVersion: "6.5",
InstanceUuid: "",
LicenseProductName: "VMware ESX Server",
LicenseProductVersion: "6.0",
},
Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "HostAgentSettings"},
UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "ha-user-directory"},
SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "ha-sessionmgr"},
AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "ha-authmgr"},
ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ha-servicemanager"},
PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "ha-perfmgr"},
ScheduledTaskManager: (*types.ManagedObjectReference)(nil),
AlarmManager: (*types.ManagedObjectReference)(nil),
EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "ha-eventmgr"},
TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "ha-taskmgr"},
ExtensionManager: (*types.ManagedObjectReference)(nil),
CustomizationSpecManager: (*types.ManagedObjectReference)(nil),
CustomFieldsManager: (*types.ManagedObjectReference)(nil),
AccountManager: &types.ManagedObjectReference{Type: "HostLocalAccountManager", Value: "ha-localacctmgr"},
DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "ha-diagnosticmgr"},
LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "ha-license-manager"},
SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "ha-searchindex"},
FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "ha-nfc-file-manager"},
DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "ha-datastore-namespace-manager"},
VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "ha-vdiskmanager"},
VirtualizationManager: (*types.ManagedObjectReference)(nil),
SnmpSystem: (*types.ManagedObjectReference)(nil),
VmProvisioningChecker: (*types.ManagedObjectReference)(nil),
VmCompatibilityChecker: (*types.ManagedObjectReference)(nil),
OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "ha-ovf-manager"},
IpPoolManager: (*types.ManagedObjectReference)(nil),
DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "ha-dvsmanager"},
HostProfileManager: (*types.ManagedObjectReference)(nil),
ClusterProfileManager: (*types.ManagedObjectReference)(nil),
ComplianceManager: (*types.ManagedObjectReference)(nil),
LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "ha-l10n-manager"},
StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "ha-storage-resource-manager"},
GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "ha-guest-operations-manager"},
OverheadMemoryManager: (*types.ManagedObjectReference)(nil),
CertificateManager: (*types.ManagedObjectReference)(nil),
IoFilterManager: (*types.ManagedObjectReference)(nil),
VStorageObjectManager: &types.ManagedObjectReference{Type: "HostVStorageObjectManager", Value: "ha-vstorage-object-manager"},
HostSpecManager: (*types.ManagedObjectReference)(nil),
CryptoManager: &types.ManagedObjectReference{Type: "CryptoManager", Value: "ha-crypto-manager"},
HealthUpdateManager: (*types.ManagedObjectReference)(nil),
FailoverClusterConfigurator: (*types.ManagedObjectReference)(nil),
FailoverClusterManager: (*types.ManagedObjectReference)(nil),
}

View File

@@ -0,0 +1,30 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// Setting is captured from ESX's HostSystem.configManager.advancedOption
// Capture method:
// govc object.collect -s -dump $(govc object.collect -s HostSystem:ha-host configManager.advancedOption) setting
var Setting = []types.BaseOptionValue{
// This list is currently pruned to include a single option for testing
&types.OptionValue{
Key: "Config.HostAgent.log.level",
Value: "info",
},
}

View File

@@ -0,0 +1,242 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package esx
import "github.com/vmware/govmomi/vim25/types"
// VirtualDevice is the default set of VirtualDevice types created for a VirtualMachine
// Capture method:
// govc vm.create foo
// govc object.collect -s -dump vm/foo config.hardware.device
var VirtualDevice = []types.BaseVirtualDevice{
&types.VirtualIDEController{
VirtualController: types.VirtualController{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 200,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "IDE 0",
Summary: "IDE 0",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 0,
UnitNumber: (*int32)(nil),
},
BusNumber: 0,
Device: nil,
},
},
&types.VirtualIDEController{
VirtualController: types.VirtualController{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 201,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "IDE 1",
Summary: "IDE 1",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 0,
UnitNumber: (*int32)(nil),
},
BusNumber: 1,
Device: nil,
},
},
&types.VirtualPS2Controller{
VirtualController: types.VirtualController{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 300,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "PS2 controller 0",
Summary: "PS2 controller 0",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 0,
UnitNumber: (*int32)(nil),
},
BusNumber: 0,
Device: []int32{600, 700},
},
},
&types.VirtualPCIController{
VirtualController: types.VirtualController{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 100,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "PCI controller 0",
Summary: "PCI controller 0",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 0,
UnitNumber: (*int32)(nil),
},
BusNumber: 0,
Device: []int32{500, 12000},
},
},
&types.VirtualSIOController{
VirtualController: types.VirtualController{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 400,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "SIO controller 0",
Summary: "SIO controller 0",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 0,
UnitNumber: (*int32)(nil),
},
BusNumber: 0,
Device: nil,
},
},
&types.VirtualKeyboard{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 600,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "Keyboard ",
Summary: "Keyboard",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 300,
UnitNumber: types.NewInt32(0),
},
},
&types.VirtualPointingDevice{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 700,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "Pointing device",
Summary: "Pointing device; Device",
},
Backing: &types.VirtualPointingDeviceDeviceBackingInfo{
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{},
DeviceName: "",
UseAutoDetect: types.NewBool(false),
},
HostPointingDevice: "autodetect",
},
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 300,
UnitNumber: types.NewInt32(1),
},
},
&types.VirtualMachineVideoCard{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 500,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "Video card ",
Summary: "Video card",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 100,
UnitNumber: types.NewInt32(0),
},
VideoRamSizeInKB: 4096,
NumDisplays: 1,
UseAutoDetect: types.NewBool(false),
Enable3DSupport: types.NewBool(false),
Use3dRenderer: "automatic",
GraphicsMemorySizeInKB: 262144,
},
&types.VirtualMachineVMCIDevice{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 12000,
DeviceInfo: &types.Description{
DynamicData: types.DynamicData{},
Label: "VMCI device",
Summary: "Device on the virtual machine PCI bus that provides support for the virtual machine communication interface",
},
Backing: nil,
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
SlotInfo: nil,
ControllerKey: 100,
UnitNumber: types.NewInt32(17),
},
Id: -1,
AllowUnrestrictedCommunication: types.NewBool(false),
FilterEnable: types.NewBool(true),
FilterInfo: (*types.VirtualMachineVMCIDeviceFilterInfo)(nil),
},
}
// EthernetCard template for types.VirtualEthernetCard
var EthernetCard = types.VirtualE1000{
VirtualEthernetCard: types.VirtualEthernetCard{
VirtualDevice: types.VirtualDevice{
DynamicData: types.DynamicData{},
Key: 4000,
Backing: &types.VirtualEthernetCardNetworkBackingInfo{
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{},
DeviceName: "VM Network",
UseAutoDetect: types.NewBool(false),
},
Network: (*types.ManagedObjectReference)(nil),
InPassthroughMode: types.NewBool(false),
},
Connectable: &types.VirtualDeviceConnectInfo{
DynamicData: types.DynamicData{},
StartConnected: true,
AllowGuestControl: true,
Connected: false,
Status: "untried",
},
SlotInfo: &types.VirtualDevicePciBusSlotInfo{
VirtualDeviceBusSlotInfo: types.VirtualDeviceBusSlotInfo{},
PciSlotNumber: 32,
},
ControllerKey: 100,
UnitNumber: types.NewInt32(7),
},
AddressType: "generated",
MacAddress: "",
WakeOnLanEnabled: types.NewBool(true),
},
}

View File

@@ -0,0 +1,423 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"bytes"
"container/ring"
"log"
"reflect"
"text/template"
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
var (
maxPageSize = 1000
logEvents = false
)
type EventManager struct {
mo.EventManager
root types.ManagedObjectReference
page *ring.Ring
key int32
collectors map[types.ManagedObjectReference]*EventHistoryCollector
templates map[string]*template.Template
}
func NewEventManager(ref types.ManagedObjectReference) object.Reference {
return &EventManager{
EventManager: mo.EventManager{
Self: ref,
Description: types.EventDescription{
EventInfo: esx.EventInfo,
},
MaxCollector: 1000,
},
root: Map.content().RootFolder,
page: ring.New(maxPageSize),
collectors: make(map[types.ManagedObjectReference]*EventHistoryCollector),
templates: make(map[string]*template.Template),
}
}
func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
size, err := validatePageSize(req.Filter.MaxCount)
if err != nil {
return nil, err
}
if len(m.collectors) >= int(m.MaxCollector) {
return nil, Fault("Too many event collectors to create", new(types.InvalidState))
}
collector := &EventHistoryCollector{
m: m,
page: ring.New(size),
}
collector.Filter = req.Filter
collector.fillPage(size)
return collector, nil
}
func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
body := new(methods.CreateCollectorForEventsBody)
collector, err := m.createCollector(ctx, req)
if err != nil {
body.Fault_ = err
return body
}
ref := ctx.Session.Put(collector).Reference()
m.collectors[ref] = collector
body.Res = &types.CreateCollectorForEventsResponse{
Returnval: ref,
}
return body
}
func (m *EventManager) QueryEvents(ctx *Context, req *types.QueryEvents) soap.HasFault {
if Map.IsESX() {
return &methods.QueryEventsBody{
Fault_: Fault("", new(types.NotImplemented)),
}
}
body := new(methods.QueryEventsBody)
collector, err := m.createCollector(ctx, &types.CreateCollectorForEvents{Filter: req.Filter})
if err != nil {
body.Fault_ = err
return body
}
body.Res = &types.QueryEventsResponse{
Returnval: collector.GetLatestPage(),
}
return body
}
// formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field.
func (m *EventManager) formatMessage(event types.BaseEvent) {
id := reflect.ValueOf(event).Elem().Type().Name()
e := event.GetEvent()
t, ok := m.templates[id]
if !ok {
for _, info := range m.Description.EventInfo {
if info.Key == id {
t = template.Must(template.New(id).Parse(info.FullFormat))
m.templates[id] = t
break
}
}
}
var buf bytes.Buffer
if err := t.Execute(&buf, event); err != nil {
log.Print(err)
}
e.FullFormattedMessage = buf.String()
if logEvents {
log.Printf("[%s] %s", id, e.FullFormattedMessage)
}
}
func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFault {
m.key++
event := req.EventToPost.GetEvent()
event.Key = m.key
event.ChainId = event.Key
event.CreatedTime = time.Now()
event.UserName = ctx.Session.UserName
m.page = m.page.Next()
m.page.Value = req.EventToPost
m.formatMessage(req.EventToPost)
for _, c := range m.collectors {
if c.eventMatches(req.EventToPost) {
c.page = c.page.Next()
c.page.Value = event
}
}
return &methods.PostEventBody{
Res: new(types.PostEventResponse),
}
}
type EventHistoryCollector struct {
mo.EventHistoryCollector
m *EventManager
page *ring.Ring
}
// doEntityEventArgument calls f for each entity argument in the event.
// If f returns true, the iteration stops.
func doEntityEventArgument(event types.BaseEvent, f func(types.ManagedObjectReference, *types.EntityEventArgument) bool) bool {
e := event.GetEvent()
if arg := e.Vm; arg != nil {
if f(arg.Vm, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Host; arg != nil {
if f(arg.Host, &arg.EntityEventArgument) {
return true
}
}
if arg := e.ComputeResource; arg != nil {
if f(arg.ComputeResource, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Ds; arg != nil {
if f(arg.Datastore, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Net; arg != nil {
if f(arg.Network, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Dvs; arg != nil {
if f(arg.Dvs, &arg.EntityEventArgument) {
return true
}
}
if arg := e.Datacenter; arg != nil {
if f(arg.Datacenter, &arg.EntityEventArgument) {
return true
}
}
return false
}
// eventFilterSelf returns true if self is one of the entity arguments in the event.
func eventFilterSelf(event types.BaseEvent, self types.ManagedObjectReference) bool {
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
return self == ref
})
}
// eventFilterChildren returns true if a child of self is one of the entity arguments in the event.
func eventFilterChildren(event types.BaseEvent, self types.ManagedObjectReference) bool {
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
seen := false
var match func(types.ManagedObjectReference)
match = func(child types.ManagedObjectReference) {
if child == self {
seen = true
return
}
walk(child, match)
}
walk(ref, match)
return seen
})
}
// entityMatches returns true if the spec Entity filter matches the event.
func (c *EventHistoryCollector) entityMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
e := spec.Entity
if e == nil {
return true
}
isRootFolder := c.m.root == e.Entity
switch e.Recursion {
case types.EventFilterSpecRecursionOptionSelf:
return isRootFolder || eventFilterSelf(event, e.Entity)
case types.EventFilterSpecRecursionOptionChildren:
return eventFilterChildren(event, e.Entity)
case types.EventFilterSpecRecursionOptionAll:
if isRootFolder || eventFilterSelf(event, e.Entity) {
return true
}
return eventFilterChildren(event, e.Entity)
}
return false
}
// typeMatches returns true if one of the spec EventTypeId types matches the event.
func (c *EventHistoryCollector) typeMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
if len(spec.EventTypeId) == 0 {
return true
}
matches := func(name string) bool {
for _, id := range spec.EventTypeId {
if id == name {
return true
}
}
return false
}
kind := reflect.ValueOf(event).Elem().Type()
if matches(kind.Name()) {
return true // concrete type
}
field, ok := kind.FieldByNameFunc(matches)
if ok {
return field.Anonymous // base type (embedded field)
}
return false
}
// eventMatches returns true one of the filters matches the event.
func (c *EventHistoryCollector) eventMatches(event types.BaseEvent) bool {
spec := c.Filter.(types.EventFilterSpec)
if !c.typeMatches(event, &spec) {
return false
}
// TODO: spec.Time, spec.UserName, etc
return c.entityMatches(event, &spec)
}
// filePage copies the manager's latest events into the collector's page with Filter applied.
func (c *EventHistoryCollector) fillPage(size int) {
l := c.page.Len()
delta := size - l
if delta < 0 {
// Shrink ring size
c.page = c.page.Unlink(-delta)
return
}
matches := 0
mpage := c.m.page
page := c.page
if delta != 0 {
// Grow ring size
c.page = c.page.Link(ring.New(delta))
}
for i := 0; i < maxPageSize; i++ {
event, ok := mpage.Value.(types.BaseEvent)
mpage = mpage.Prev()
if !ok {
continue
}
if c.eventMatches(event) {
page.Value = event
page = page.Prev()
matches++
if matches == size {
break
}
}
}
}
func validatePageSize(count int32) (int, *soap.Fault) {
size := int(count)
if size == 0 {
size = 10 // defaultPageSize
} else if size < 0 || size > maxPageSize {
return -1, Fault("", &types.InvalidArgument{InvalidProperty: "maxCount"})
}
return size, nil
}
func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.SetCollectorPageSize) soap.HasFault {
body := new(methods.SetCollectorPageSizeBody)
size, err := validatePageSize(req.MaxCount)
if err != nil {
body.Fault_ = err
return body
}
ctx.WithLock(c.m, func() {
c.fillPage(size)
})
body.Res = new(types.SetCollectorPageSizeResponse)
return body
}
func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.DestroyCollector) soap.HasFault {
ctx.Session.Remove(req.This)
ctx.WithLock(c.m, func() {
delete(c.m.collectors, req.This)
})
return &methods.DestroyCollectorBody{
Res: new(types.DestroyCollectorResponse),
}
}
func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
var latestPage []types.BaseEvent
c.page.Do(func(val interface{}) {
if val == nil {
return
}
latestPage = append(latestPage, val.(types.BaseEvent))
})
return latestPage
}
func (c *EventHistoryCollector) Get() mo.Reference {
clone := *c
clone.LatestPage = clone.GetLatestPage()
return &clone
}

View File

@@ -0,0 +1,117 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/event"
"github.com/vmware/govmomi/vim25/types"
)
func TestEventManagerVPX(t *testing.T) {
logEvents = testing.Verbose()
ctx := context.Background()
m := VPX()
m.Datacenter = 2
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
e := event.NewManager(c.Client)
count := m.Count()
root := c.ServiceContent.RootFolder
vm := Map.Any("VirtualMachine").(*VirtualMachine)
host := Map.Get(vm.Runtime.Host.Reference()).(*HostSystem)
vmEvents := 6 // BeingCreated + InstanceUuid + Uuid + Created + Starting + PoweredOn
tests := []struct {
obj types.ManagedObjectReference
expect int
ids []string
}{
{root, -1 * count.Machine, nil},
{root, 1, []string{"SessionEvent"}}, // UserLoginSessionEvent
{vm.Reference(), 0, []string{"SessionEvent"}},
{root, count.Machine, []string{"VmCreatedEvent"}}, // concrete type
{root, count.Machine * vmEvents, []string{"VmEvent"}}, // base type
{vm.Reference(), 1, []string{"VmCreatedEvent"}},
{vm.Reference(), vmEvents, nil},
{host.Reference(), len(host.Vm), []string{"VmCreatedEvent"}},
{host.Reference(), len(host.Vm) * vmEvents, nil},
}
for i, test := range tests {
n := 0
filter := types.EventFilterSpec{
Entity: &types.EventFilterSpecByEntity{
Entity: test.obj,
Recursion: types.EventFilterSpecRecursionOptionAll,
},
EventTypeId: test.ids,
MaxCount: 100,
}
f := func(obj types.ManagedObjectReference, events []types.BaseEvent) error {
n += len(events)
qevents, qerr := e.QueryEvents(ctx, filter)
if qerr != nil {
t.Fatal(qerr)
}
if n != len(qevents) {
t.Errorf("%d vs %d", n, len(qevents))
}
return nil
}
err = e.Events(ctx, []types.ManagedObjectReference{test.obj}, filter.MaxCount, false, false, f, test.ids...)
if err != nil {
t.Fatalf("%d: %s", i, err)
}
if test.expect < 0 {
expect := test.expect * -1
if n < expect {
t.Errorf("%d: expected at least %d events, got: %d", i, expect, n)
}
continue
}
if test.expect != n {
t.Errorf("%d: expected %d events, got: %d", i, test.expect, n)
}
}
}

View File

@@ -0,0 +1,112 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator_test
import (
"context"
"fmt"
"log"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// BusyVM changes the behavior of simulator.VirtualMachine
type BusyVM struct {
*simulator.VirtualMachine
}
// Override simulator.VirtualMachine.PowerOffVMTask to inject faults
func (vm *BusyVM) PowerOffVMTask(req *types.PowerOffVM_Task) soap.HasFault {
task := simulator.CreateTask(req.This, "powerOff", func(*simulator.Task) (types.AnyType, types.BaseMethodFault) {
return nil, &types.TaskInProgress{}
})
return &methods.PowerOffVM_TaskBody{
Res: &types.PowerOffVM_TaskResponse{
Returnval: task.Run(),
},
}
}
// Add AcquireTicket method, not implemented by simulator.VirtualMachine
func (vm *BusyVM) AcquireTicket(req *types.AcquireTicket) soap.HasFault {
body := &methods.AcquireTicketBody{}
if req.TicketType != "mks" {
body.Fault_ = simulator.Fault("", &types.InvalidArgument{})
}
body.Res = &types.AcquireTicketResponse{
Returnval: types.VirtualMachineTicket{
Ticket: "welcome 2 the machine",
},
}
return body
}
// Example of extending the simulator to inject faults.
func Example() {
ctx := context.Background()
model := simulator.VPX()
defer model.Remove()
_ = model.Create()
s := model.Service.NewServer()
defer s.Close()
// NewClient connects to s.URL over https and invokes 2 SOAP methods (RetrieveServiceContent + Login)
c, _ := govmomi.NewClient(ctx, s.URL, true)
// Shortcut to choose any VM, rather than using the more verbose Finder or ContainerView.
obj := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
// Validate VM is powered on
if obj.Runtime.PowerState != "poweredOn" {
log.Fatal(obj.Runtime.PowerState)
}
// Wrap the existing vm object, using the same vm.Self (ManagedObjectReference) value as the Map key.
simulator.Map.Put(&BusyVM{obj})
vm := object.NewVirtualMachine(c.Client, obj.Reference())
// Start a PowerOff task using the SOAP client.
task, _ := vm.PowerOff(ctx)
// Wait for task completion, expecting failure.
err := task.Wait(ctx)
if err == nil {
log.Fatal("expected error")
}
// Invalid ticket type, expecting failure.
_, err = vm.AcquireTicket(ctx, "pks")
if err == nil {
log.Fatal("expected error")
}
mks, _ := vm.AcquireTicket(ctx, "mks")
fmt.Println(mks.Ticket, obj.Runtime.PowerState)
// Output: welcome 2 the machine poweredOn
}

View File

@@ -0,0 +1,92 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator_test
import (
"context"
"fmt"
"log"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/simulator"
)
// Example boilerplate for starting a simulator initialized with an ESX model.
func ExampleESX() {
ctx := context.Background()
// ESXi model + initial set of objects (VMs, network, datastore)
model := simulator.ESX()
defer model.Remove()
err := model.Create()
if err != nil {
log.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, _ := govmomi.NewClient(ctx, s.URL, true)
fmt.Printf("%s with %d host", c.Client.ServiceContent.About.ApiType, model.Count().Host)
// Output: HostAgent with 1 host
}
// Example for starting a simulator with empty inventory, similar to a fresh install of vCenter.
func ExampleModel() {
ctx := context.Background()
model := simulator.VPX()
model.Datacenter = 0 // No DC == no inventory
defer model.Remove()
err := model.Create()
if err != nil {
log.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, _ := govmomi.NewClient(ctx, s.URL, true)
fmt.Printf("%s with %d hosts", c.Client.ServiceContent.About.ApiType, model.Count().Host)
// Output: VirtualCenter with 0 hosts
}
// Example boilerplate for starting a simulator initialized with a vCenter model.
func ExampleVPX() {
ctx := context.Background()
// vCenter model + initial set of objects (cluster, hosts, VMs, network, datastore, etc)
model := simulator.VPX()
defer model.Remove()
err := model.Create()
if err != nil {
log.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, _ := govmomi.NewClient(ctx, s.URL, true)
fmt.Printf("%s with %d hosts", c.Client.ServiceContent.About.ApiType, model.Count().Host)
// Output: VirtualCenter with 4 hosts
}

View File

@@ -0,0 +1,252 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"io"
"os"
"path"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type FileManager struct {
mo.FileManager
}
func NewFileManager(ref types.ManagedObjectReference) object.Reference {
m := &FileManager{}
m.Self = ref
return m
}
func (f *FileManager) findDatastore(ref mo.Reference, name string) (*Datastore, types.BaseMethodFault) {
var refs []types.ManagedObjectReference
switch obj := ref.(type) {
case *Folder:
refs = obj.ChildEntity
case *StoragePod:
refs = obj.ChildEntity
}
for _, ref := range refs {
switch obj := Map.Get(ref).(type) {
case *Datastore:
if obj.Name == name {
return obj, nil
}
case *Folder, *StoragePod:
ds, _ := f.findDatastore(obj, name)
if ds != nil {
return ds, nil
}
}
}
return nil, &types.InvalidDatastore{Name: name}
}
func (f *FileManager) resolve(dc *types.ManagedObjectReference, name string) (string, types.BaseMethodFault) {
p, fault := parseDatastorePath(name)
if fault != nil {
return "", fault
}
if dc == nil {
if Map.IsESX() {
dc = &esx.Datacenter.Self
} else {
return "", &types.InvalidArgument{InvalidProperty: "dc"}
}
}
folder := Map.Get(*dc).(*Datacenter).DatastoreFolder
ds, fault := f.findDatastore(Map.Get(folder), p.Datastore)
if fault != nil {
return "", fault
}
dir := ds.Info.GetDatastoreInfo().Url
return path.Join(dir, p.Path), nil
}
func (f *FileManager) fault(name string, err error, fault types.BaseFileFault) types.BaseMethodFault {
switch {
case os.IsNotExist(err):
fault = new(types.FileNotFound)
case os.IsExist(err):
fault = new(types.FileAlreadyExists)
}
fault.GetFileFault().File = name
return fault.(types.BaseMethodFault)
}
func (f *FileManager) deleteDatastoreFile(req *types.DeleteDatastoreFile_Task) types.BaseMethodFault {
file, fault := f.resolve(req.Datacenter, req.Name)
if fault != nil {
return fault
}
_, err := os.Stat(file)
if err != nil {
if os.IsNotExist(err) {
return f.fault(file, err, new(types.CannotDeleteFile))
}
}
err = os.RemoveAll(file)
if err != nil {
return f.fault(file, err, new(types.CannotDeleteFile))
}
return nil
}
func (f *FileManager) DeleteDatastoreFileTask(req *types.DeleteDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "deleteDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.deleteDatastoreFile(req)
})
return &methods.DeleteDatastoreFile_TaskBody{
Res: &types.DeleteDatastoreFile_TaskResponse{
Returnval: task.Run(),
},
}
}
func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault {
body := &methods.MakeDirectoryBody{}
name, fault := f.resolve(req.Datacenter, req.Name)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
mkdir := os.Mkdir
if isTrue(req.CreateParentDirectories) {
mkdir = os.MkdirAll
}
err := mkdir(name, 0700)
if err != nil {
fault = f.fault(req.Name, err, new(types.CannotCreateFile))
body.Fault_ = Fault(err.Error(), fault)
return body
}
body.Res = new(types.MakeDirectoryResponse)
return body
}
func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types.BaseMethodFault {
src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
if fault != nil {
return fault
}
dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
if fault != nil {
return fault
}
if !isTrue(req.Force) {
_, err := os.Stat(dst)
if err == nil {
return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
}
}
err := os.Rename(src, dst)
if err != nil {
return f.fault(src, err, new(types.CannotAccessFile))
}
return nil
}
func (f *FileManager) MoveDatastoreFileTask(req *types.MoveDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "moveDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.moveDatastoreFile(req)
})
return &methods.MoveDatastoreFile_TaskBody{
Res: &types.MoveDatastoreFile_TaskResponse{
Returnval: task.Run(),
},
}
}
func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types.BaseMethodFault {
src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
if fault != nil {
return fault
}
dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
if fault != nil {
return fault
}
if !isTrue(req.Force) {
_, err := os.Stat(dst)
if err == nil {
return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
}
}
r, err := os.Open(src)
if err != nil {
return f.fault(dst, err, new(types.CannotAccessFile))
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return f.fault(dst, err, new(types.CannotCreateFile))
}
defer w.Close()
if _, err = io.Copy(w, r); err != nil {
return f.fault(dst, err, new(types.CannotCreateFile))
}
return nil
}
func (f *FileManager) CopyDatastoreFileTask(req *types.CopyDatastoreFile_Task) soap.HasFault {
task := CreateTask(f, "copyDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, f.copyDatastoreFile(req)
})
return &methods.CopyDatastoreFile_TaskBody{
Res: &types.CopyDatastoreFile_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,220 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"errors"
"fmt"
"reflect"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
)
func TestFinderVPX(t *testing.T) {
ctx := context.Background()
m := VPX()
m.Datacenter = 3
m.Folder = 2
m.Pool = 1
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
client, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(client.Client, false)
dc, _ := finder.Datacenter(ctx, "/F0/DC1")
finder.SetDatacenter(dc)
tests := []struct {
kind string
path string
expect int
}{
{"ManagedObjectList", "./*", 4}, // /F0/DC1/{vm,host,network,datastore}
{"ManagedObjectListChildren", ".", 4}, // ""
{"ManagedObjectList", "/", 1},
{"ManagedObjectList", "/*", m.Datacenter - m.Folder + m.Folder},
{"ManagedObjectList", "/DC0", 1},
{"ManagedObjectList", "/F[01]", 2},
{"ManagedObjectListChildren", "/*", m.Datacenter + 3},
{"ManagedObjectListChildren", "/*/*", 19},
{"ManagedObjectListChildren", "/*/*/*", 25},
{"FolderList", "/*", m.Folder},
{"DatacenterList", "/F0/*", 1},
{"DatacenterList", "/DC0", 1},
{"VirtualMachineList", "/DC0/vm/*", (m.Host + m.Cluster) * m.Machine},
{"VirtualMachineList", "F0/DC1_C0_RP0_VM0", 1},
{"VirtualMachineList", "./DC1_C0_RP0_VM0", 0},
{"VirtualMachineList", "DC1_C0_RP0_VM0", 1}, // find . -type VirtualMachine -name DC1_C0_RP0_VM0
{"VirtualAppList", "/DC0/vm/*", 0},
{"DatastoreList", "/DC0/datastore/*", m.Datastore},
{"DatastoreList", "./*", 0},
{"DatastoreList", "./F0/*", m.Datastore},
{"DatastoreList", "/F*/*/datastore/F*/*", m.Datastore * m.Folder},
{"DatastoreList", "/F0/DC1/datastore/F0/LocalDS_0", m.Datastore},
{"DatastoreList", "/F0/DC1/datastore/F0/*", m.Datastore},
{"DatastoreList", "/F1/DC2/datastore/F1/*", m.Datastore},
{"DatastoreList", "./LocalDS_0", 0},
{"DatastoreList", "LocalDS_0", 1}, // find . -type Datastore -name LocalDS_0
{"DatastoreClusterList", "/DC0/datastore/*", 0},
{"ComputeResourceList", "/DC0/host/*", m.Host + m.Cluster},
{"ClusterComputeResourceList", "/DC0/host/*", m.Cluster},
{"HostSystemList", "/DC0/host/*", m.Host + m.ClusterHost},
{"HostSystemList", "/F0/DC1/host/F0/*", m.Host + m.ClusterHost},
{"HostSystemList", "DC1_H0", 1}, // find . -type HostSystem -name DC1_H0
{"ComputeResourceList", "DC1_H0", 1}, // find . -type ComputeResource -name DC1_H0
{"ClusterComputeResourceList", "DC1_C0", 1}, // find . -type ClusterComputeResource -name DC1_H0
{"NetworkList", "/DC0/network/*", 3 + m.Portgroup}, // VM Network + DSwitch + DSwitch-Uplinks + m.Portgroup
{"NetworkList", "./*", 1},
{"NetworkList", "/F0/DC1/network/VM Network", 1},
{"NetworkList", "/F0/DC1/network/F0/*", 2 + m.Portgroup},
{"NetworkList", "./F0/DC1_DVPG0", 1},
{"NetworkList", "./F0/DC1_DVPG0", 1},
{"NetworkList", "DC1_DVPG0", 1}, // find . -type Network -name DC1_DVPG0
{"ResourcePoolList", "/F0/DC1/host/F0/*", m.Host + m.Cluster},
{"ResourcePoolList", "/F0/DC1/host/F0/*/*", m.Host + m.Cluster},
{"ResourcePoolList", "/DC0/host/*", m.Host + m.Cluster},
{"ResourcePoolList", "/DC0/host/*/*", m.Host + m.Cluster},
{"ResourcePoolList", "/DC0/host/DC0_H0/Resources", 1},
{"ResourcePoolList", "/F1/DC2/host/F1/DC2_C0/Resources", 1},
{"ResourcePoolList", "Resources", m.Host + m.Cluster}, // find . -type ResourcePool -name Resources
{"ResourcePoolList", "/F1/DC2/...", m.Pool + m.Host + 1}, // find $path -type ResourcePool
{"ResourcePoolList", "/F1/DC2/host/...", m.Pool + m.Host + 1}, // find $path -type ResourcePool
{"ResourcePoolList", "/F1/DC2/host/F1/...", m.Pool + m.Host + 1}, // find $path -type ResourcePool
{"ResourcePoolList", "/F1/DC2/host/F1/DC2_C0/...", m.Pool + 1}, // find $path -type ResourcePool
{"ResourcePoolList", "/F1/DC2/host/F1/DC2_C0/Resources/...", m.Pool}, // find $path -type ResourcePool
{"ResourcePoolList", "F0/DC1_C0", 1},
{"ResourcePoolList", "DC1_C0_RP1", 1}, // find . -type ResourcePool -name DC1_C0_RP1
{"", "", 0}, // unset Datacenter
{"DatacenterList", "*", m.Datacenter}, // find . -type Datacenter
{"DatacenterList", "./...", m.Datacenter}, // find . -type Datacenter
{"DatacenterList", "DC2", 1}, // find . -type Datacenter -name DC2
{"DatacenterList", "/*", m.Datacenter - m.Folder},
{"DatacenterList", "/*/*", m.Folder},
{"DatastoreList", "/F1/DC2/datastore/F1/LocalDS_0", 1},
{"VirtualMachineList", "DC1_C0_RP0_VM0", 0}, // TODO: recurse all Datacenters?
}
f := reflect.ValueOf(finder)
c := reflect.ValueOf(ctx)
for i, test := range tests {
if test.kind == "" {
finder.SetDatacenter(nil)
continue
}
err = nil
arg := []reflect.Value{c, reflect.ValueOf(test.path)}
res := f.MethodByName(test.kind).Call(arg)
rval := res[0]
rerr := res[1]
if rval.Len() != test.expect {
msg := fmt.Sprintf("expected %d, got %d", test.expect, rval.Len())
if !rerr.IsNil() {
msg += fmt.Sprintf(" (%s)", rerr.Interface())
}
err = errors.New(msg)
if !rval.IsNil() {
for j := 0; j < rval.Len(); j++ {
t.Logf("%s\n", rval.Index(j).Interface())
}
}
} else if !rerr.IsNil() {
if test.expect != 0 {
err = rerr.Interface().(error)
}
}
if err != nil {
t.Errorf("%d) %s(%s): %s", i, test.kind, test.path, err)
}
}
}
func TestFinderESX(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
client, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(client.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
f := reflect.ValueOf(finder)
c := reflect.ValueOf(ctx)
tests := []string{"Folder", "Datastore", "ComputeResource", "HostSystem", "Datastore", "Network"}
for _, test := range tests {
res := f.MethodByName("Default" + test).Call([]reflect.Value{c})
if !res[1].IsNil() {
t.Fatalf("%s: %s", test, res[1].Interface())
}
// test find by moref
ref := res[0].Interface().(object.Reference).Reference()
o, err := finder.Element(ctx, ref)
if err != nil {
t.Fatal(err)
}
if o.Object.Reference() != ref {
t.Errorf("%s", ref)
}
}
}

583
vendor/github.com/vmware/govmomi/simulator/folder.go generated vendored Normal file
View File

@@ -0,0 +1,583 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"math/rand"
"path"
"strings"
"github.com/google/uuid"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type Folder struct {
mo.Folder
}
func (f *Folder) eventArgument() types.FolderEventArgument {
return types.FolderEventArgument{
Folder: f.Self,
EntityEventArgument: types.EntityEventArgument{Name: f.Name},
}
}
// update references when objects are added/removed from a Folder
func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
ref := o.Reference()
if f.Parent == nil {
return // this is the root folder
}
switch ref.Type {
case "Datacenter", "Folder":
return // nothing to update
}
dc := Map.getEntityDatacenter(f)
switch ref.Type {
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
u(dc, &dc.Network, ref)
case "Datastore":
u(dc, &dc.Datastore, ref)
}
}
func networkSummary(n *mo.Network) *types.NetworkSummary {
return &types.NetworkSummary{
Network: &n.Self,
Name: n.Name,
Accessible: true,
}
}
func (f *Folder) putChild(o mo.Entity) {
Map.PutEntity(f, o)
f.ChildEntity = append(f.ChildEntity, o.Reference())
f.update(o, Map.AddReference)
switch e := o.(type) {
case *mo.Network:
e.Summary = networkSummary(e)
case *mo.OpaqueNetwork:
e.Summary = networkSummary(&e.Network)
case *DistributedVirtualPortgroup:
e.Summary = networkSummary(&e.Network)
}
}
func (f *Folder) removeChild(o mo.Reference) {
Map.Remove(o.Reference())
RemoveReference(&f.ChildEntity, o.Reference())
f.update(o, Map.RemoveReference)
}
func (f *Folder) hasChildType(kind string) bool {
for _, t := range f.ChildType {
if t == kind {
return true
}
}
return false
}
func (f *Folder) typeNotSupported() *soap.Fault {
return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{})
}
type addStandaloneHost struct {
*Folder
req *types.AddStandaloneHost_Task
}
func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host, err := CreateStandaloneHost(add.Folder, add.req.Spec)
if err != nil {
return nil, err
}
if add.req.AddConnected {
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
}
return host.Reference(), nil
}
func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.HasFault {
r := &methods.AddStandaloneHost_TaskBody{}
if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
r.Res = &types.AddStandaloneHost_TaskResponse{
Returnval: NewTask(&addStandaloneHost{f, a}).Run(),
}
} else {
r.Fault_ = f.typeNotSupported()
}
return r
}
func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault {
r := &methods.CreateFolderBody{}
if f.hasChildType("Folder") {
folder := &Folder{}
folder.Name = c.Name
folder.ChildType = f.ChildType
f.putChild(folder)
r.Res = &types.CreateFolderResponse{
Returnval: folder.Self,
}
} else {
r.Fault_ = f.typeNotSupported()
}
return r
}
// StoragePod aka "Datastore Cluster"
type StoragePod struct {
mo.StoragePod
}
func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
r := &methods.CreateStoragePodBody{}
if f.hasChildType("StoragePod") {
pod := &StoragePod{}
pod.Name = c.Name
pod.ChildType = []string{"Datastore"}
f.putChild(pod)
r.Res = &types.CreateStoragePodResponse{
Returnval: pod.Self,
}
} else {
r.Fault_ = f.typeNotSupported()
}
return r
}
func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
return (&Folder{Folder: p.Folder}).MoveIntoFolderTask(c)
}
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
r := &methods.CreateDatacenterBody{}
if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
dc := NewDatacenter(f)
dc.Name = c.Name
r.Res = &types.CreateDatacenterResponse{
Returnval: dc.Self,
}
ctx.postEvent(&types.DatacenterCreatedEvent{
DatacenterEvent: types.DatacenterEvent{
Event: types.Event{
Datacenter: datacenterEventArgument(dc),
},
},
Parent: f.eventArgument(),
})
} else {
r.Fault_ = f.typeNotSupported()
}
return r
}
func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
r := &methods.CreateClusterExBody{}
if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
cluster, err := CreateClusterComputeResource(f, c.Name, c.Spec)
if err != nil {
r.Fault_ = Fault("", err)
return r
}
r.Res = &types.CreateClusterExResponse{
Returnval: cluster.Self,
}
} else {
r.Fault_ = f.typeNotSupported()
}
return r
}
type createVM struct {
*Folder
ctx *Context
req *types.CreateVM_Task
register bool
}
func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
vm, err := NewVirtualMachine(c.Folder.Self, &c.req.Config)
if err != nil {
return nil, err
}
vm.ResourcePool = &c.req.Pool
if c.req.Host == nil {
var hosts []types.ManagedObjectReference
pool := Map.Get(c.req.Pool).(mo.Entity)
switch cr := Map.getEntityComputeResource(pool).(type) {
case *mo.ComputeResource:
hosts = cr.Host
case *ClusterComputeResource:
hosts = cr.Host
}
// Assuming for now that all hosts have access to the datastore
host := hosts[rand.Intn(len(hosts))]
vm.Runtime.Host = &host
} else {
vm.Runtime.Host = c.req.Host
}
vm.Guest = &types.GuestInfo{
ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
ToolsVersion: "0",
}
vm.Summary.Guest = &types.VirtualMachineGuestSummary{
ToolsStatus: vm.Guest.ToolsStatus,
}
vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName
vm.Summary.Runtime.Host = vm.Runtime.Host
err = vm.create(&c.req.Config, c.register)
if err != nil {
return nil, err
}
c.Folder.putChild(vm)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
Map.AppendReference(host, &host.Vm, vm.Self)
for i := range vm.Datastore {
ds := Map.Get(vm.Datastore[i]).(*Datastore)
Map.AppendReference(ds, &ds.Vm, vm.Self)
}
pool := Map.Get(*vm.ResourcePool)
// This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
c.ctx.WithLock(pool, func() {
switch rp := pool.(type) {
case *ResourcePool:
rp.Vm = append(rp.Vm, vm.Self)
case *VirtualApp:
rp.Vm = append(rp.Vm, vm.Self)
}
})
event := vm.event()
c.ctx.postEvent(
&types.VmBeingCreatedEvent{
VmEvent: event,
ConfigSpec: &c.req.Config,
},
&types.VmInstanceUuidAssignedEvent{
VmEvent: event,
InstanceUuid: vm.Config.InstanceUuid,
},
&types.VmUuidAssignedEvent{
VmEvent: event,
Uuid: vm.Config.Uuid,
},
&types.VmCreatedEvent{
VmEvent: event,
},
)
return vm.Reference(), nil
}
func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
return &methods.CreateVM_TaskBody{
Res: &types.CreateVM_TaskResponse{
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
},
}
}
type registerVM struct {
*Folder
ctx *Context
req *types.RegisterVM_Task
}
func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
host := c.req.Host
pool := c.req.Pool
if c.req.AsTemplate {
if host == nil {
return nil, &types.InvalidArgument{InvalidProperty: "host"}
} else if pool != nil {
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
}
pool = hostParent(&Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
} else {
if pool == nil {
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
}
}
if c.req.Path == "" {
return nil, &types.InvalidArgument{InvalidProperty: "path"}
}
s := Map.SearchIndex()
r := s.FindByDatastorePath(&types.FindByDatastorePath{
This: s.Reference(),
Path: c.req.Path,
Datacenter: Map.getEntityDatacenter(c.Folder).Reference(),
})
if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil {
return nil, &types.AlreadyExists{Name: ref.Value}
}
if c.req.Name == "" {
p, err := parseDatastorePath(c.req.Path)
if err != nil {
return nil, err
}
c.req.Name = path.Dir(p.Path)
}
create := NewTask(&createVM{
Folder: c.Folder,
register: true,
ctx: c.ctx,
req: &types.CreateVM_Task{
This: c.Folder.Reference(),
Config: types.VirtualMachineConfigSpec{
Name: c.req.Name,
Files: &types.VirtualMachineFileInfo{
VmPathName: c.req.Path,
},
},
Pool: *pool,
Host: host,
},
})
create.Run()
if create.Info.Error != nil {
return nil, create.Info.Error.Fault
}
return create.Info.Result, nil
}
func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
ctx.Caller = &f.Self
return &methods.RegisterVM_TaskBody{
Res: &types.RegisterVM_TaskResponse{
Returnval: NewTask(&registerVM{f, ctx, c}).Run(),
},
}
}
func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) {
for _, ref := range c.List {
obj := Map.Get(ref).(mo.Entity)
parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder)
if !ok || !f.hasChildType(ref.Type) {
return nil, &types.NotSupported{}
}
parent.removeChild(ref)
f.putChild(obj)
}
return nil, nil
})
return &methods.MoveIntoFolder_TaskBody{
Res: &types.MoveIntoFolder_TaskResponse{
Returnval: task.Run(),
},
}
}
func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) {
spec := req.Spec.ConfigSpec.GetDVSConfigSpec()
dvs := &DistributedVirtualSwitch{}
dvs.Name = spec.Name
dvs.Entity().Name = dvs.Name
if Map.FindByName(dvs.Name, f.ChildEntity) != nil {
return nil, &types.InvalidArgument{InvalidProperty: "name"}
}
dvs.Uuid = uuid.New().String()
f.putChild(dvs)
dvs.Summary = types.DVSSummary{
Name: dvs.Name,
Uuid: dvs.Uuid,
NumPorts: spec.NumStandalonePorts,
ProductInfo: req.Spec.ProductInfo,
Description: spec.Description,
}
configInfo := &types.VMwareDVSConfigInfo{
DVSConfigInfo: types.DVSConfigInfo{
Uuid: dvs.Uuid,
Name: spec.Name,
ConfigVersion: spec.ConfigVersion,
NumStandalonePorts: spec.NumStandalonePorts,
MaxPorts: spec.MaxPorts,
UplinkPortPolicy: spec.UplinkPortPolicy,
UplinkPortgroup: spec.UplinkPortgroup,
DefaultPortConfig: spec.DefaultPortConfig,
ExtensionKey: spec.ExtensionKey,
Description: spec.Description,
Policy: spec.Policy,
VendorSpecificConfig: spec.VendorSpecificConfig,
SwitchIpAddress: spec.SwitchIpAddress,
DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
},
}
if spec.Contact != nil {
configInfo.Contact = *spec.Contact
}
dvs.Config = configInfo
if dvs.Summary.ProductInfo == nil {
product := Map.content().About
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
Name: "DVS",
Vendor: product.Vendor,
Version: product.Version,
Build: product.Build,
ForwardingClass: "etherswitch",
}
}
dvs.AddDVPortgroupTask(&types.AddDVPortgroup_Task{
Spec: []types.DVPortgroupConfigSpec{{
Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
DefaultPortConfig: &types.VMwareDVSPortSetting{
Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
VlanId: []types.NumericRange{{Start: 0, End: 4094}},
},
},
}},
})
return dvs.Reference(), nil
})
return &methods.CreateDVS_TaskBody{
Res: &types.CreateDVS_TaskResponse{
Returnval: task.Run(),
},
}
}
func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
return RenameTask(f, r)
}
func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
type destroyer interface {
mo.Reference
DestroyTask(*types.Destroy_Task) soap.HasFault
}
task := CreateTask(f, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
// Attempt to destroy all children
for _, c := range f.ChildEntity {
obj, ok := Map.Get(c).(destroyer)
if !ok {
continue
}
var fault types.BaseMethodFault
Map.WithLock(obj, func() {
id := obj.DestroyTask(&types.Destroy_Task{
This: c,
}).(*methods.Destroy_TaskBody).Res.Returnval
t := Map.Get(id).(*Task)
if t.Info.Error != nil {
fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
}
})
if fault != nil {
return nil, fault
}
}
// Remove the folder itself
Map.Get(*f.Parent).(*Folder).removeChild(f.Self)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,562 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"reflect"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func addStandaloneHostTask(folder *object.Folder, spec types.HostConnectSpec) (*object.Task, error) {
// TODO: add govmomi wrapper
req := types.AddStandaloneHost_Task{
This: folder.Reference(),
Spec: spec,
AddConnected: true,
}
res, err := methods.AddStandaloneHost_Task(context.TODO(), folder.Client(), &req)
if err != nil {
return nil, err
}
task := object.NewTask(folder.Client(), res.Returnval)
return task, nil
}
func TestFolderESX(t *testing.T) {
content := esx.ServiceContent
s := New(NewServiceInstance(content, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
f := object.NewRootFolder(c.Client)
_, err = f.CreateFolder(ctx, "foo")
if err == nil {
t.Error("expected error")
}
_, err = f.CreateDatacenter(ctx, "foo")
if err == nil {
t.Error("expected error")
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, "")
if err != nil {
t.Fatal(err)
}
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
spec := types.HostConnectSpec{}
_, err = addStandaloneHostTask(folders.HostFolder, spec)
if err == nil {
t.Fatal("expected error")
}
_, err = folders.DatastoreFolder.CreateStoragePod(ctx, "pod")
if err == nil {
t.Fatal("expected error")
}
}
func TestFolderVC(t *testing.T) {
content := vpx.ServiceContent
s := New(NewServiceInstance(content, vpx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
f := object.NewRootFolder(c.Client)
ff, err := f.CreateFolder(ctx, "foo")
if err != nil {
t.Error(err)
}
dc, err := f.CreateDatacenter(ctx, "bar")
if err != nil {
t.Error(err)
}
for _, ref := range []object.Reference{ff, dc} {
o := Map.Get(ref.Reference())
if o == nil {
t.Fatalf("failed to find %#v", ref)
}
e := o.(mo.Entity).Entity()
if *e.Parent != f.Reference() {
t.Fail()
}
}
dc, err = ff.CreateDatacenter(ctx, "biz")
if err != nil {
t.Error(err)
}
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
_, err = folders.VmFolder.CreateStoragePod(ctx, "pod")
if err == nil {
t.Error("expected error")
}
_, err = folders.DatastoreFolder.CreateStoragePod(ctx, "pod")
if err != nil {
t.Error(err)
}
tests := []struct {
name string
state types.TaskInfoState
}{
{"", types.TaskInfoStateError},
{"foo.local", types.TaskInfoStateSuccess},
}
for _, test := range tests {
spec := types.HostConnectSpec{
HostName: test.name,
}
task, err := addStandaloneHostTask(folders.HostFolder, spec)
if err != nil {
t.Fatal(err)
}
res, err := task.WaitForResult(ctx, nil)
if test.state == types.TaskInfoStateError {
if err == nil {
t.Error("expected error")
}
if res.Result != nil {
t.Error("expected nil")
}
} else {
if err != nil {
t.Fatal(err)
}
ref, ok := res.Result.(types.ManagedObjectReference)
if !ok {
t.Errorf("expected moref, got type=%T", res.Result)
}
host := Map.Get(ref).(*HostSystem)
if host.Name != test.name {
t.Fail()
}
if ref == esx.HostSystem.Self {
t.Error("expected new host Self reference")
}
if *host.Summary.Host == esx.HostSystem.Self {
t.Error("expected new host summary Self reference")
}
pool := Map.Get(*host.Parent).(*mo.ComputeResource).ResourcePool
if *pool == esx.ResourcePool.Self {
t.Error("expected new pool Self reference")
}
}
if res.State != test.state {
t.Fatalf("%s", res.State)
}
}
}
func TestFolderFaults(t *testing.T) {
f := Folder{}
f.ChildType = []string{"VirtualMachine"}
if f.CreateFolder(nil).Fault() == nil {
t.Error("expected fault")
}
if f.CreateDatacenter(nil, nil).Fault() == nil {
t.Error("expected fault")
}
}
func TestRegisterVm(t *testing.T) {
ctx := context.Background()
for i, model := range []*Model{ESX(), VPX()} {
match := "*"
if i == 1 {
model.App = 1
match = "*APP*"
}
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
vmFolder := folders.VmFolder
vms, err := finder.VirtualMachineList(ctx, match)
if err != nil {
t.Fatal(err)
}
vm := Map.Get(vms[0].Reference()).(*VirtualMachine)
req := types.RegisterVM_Task{
This: vmFolder.Reference(),
AsTemplate: true,
}
steps := []struct {
e interface{}
f func()
}{
{
new(types.InvalidArgument), func() { req.AsTemplate = false },
},
{
new(types.InvalidArgument), func() { req.Pool = vm.ResourcePool },
},
{
new(types.InvalidArgument), func() { req.Path = "enoent" },
},
{
new(types.InvalidDatastorePath), func() { req.Path = vm.Config.Files.VmPathName + "-enoent" },
},
{
new(types.NotFound), func() { req.Path = vm.Config.Files.VmPathName },
},
{
new(types.AlreadyExists), func() { Map.Remove(vm.Reference()) },
},
{
nil, func() {},
},
}
for _, step := range steps {
res, err := methods.RegisterVM_Task(ctx, c.Client, &req)
if err != nil {
t.Fatal(err)
}
rt := Map.Get(res.Returnval).(*Task)
if step.e != nil {
fault := rt.Info.Error.Fault
if reflect.TypeOf(fault) != reflect.TypeOf(step.e) {
t.Errorf("%T != %T", fault, step.e)
}
} else {
if rt.Info.Error != nil {
t.Errorf("unexpected error: %#v", rt.Info.Error)
}
}
step.f()
}
nvm, err := finder.VirtualMachine(ctx, vm.Name)
if err != nil {
t.Fatal(err)
}
if nvm.Reference() == vm.Reference() {
t.Error("expected new moref")
}
_, _ = nvm.PowerOn(ctx)
steps = []struct {
e interface{}
f func()
}{
{
types.InvalidPowerState{}, func() { _, _ = nvm.PowerOff(ctx) },
},
{
nil, func() {},
},
{
types.ManagedObjectNotFound{}, func() {},
},
}
for _, step := range steps {
err = nvm.Unregister(ctx)
if step.e != nil {
fault := soap.ToSoapFault(err).VimFault()
if reflect.TypeOf(fault) != reflect.TypeOf(step.e) {
t.Errorf("%T != %T", fault, step.e)
}
} else {
if err != nil {
t.Errorf("unexpected error: %#v", err)
}
}
step.f()
}
}
}
func TestFolderMoveInto(t *testing.T) {
ctx := context.Background()
model := VPX()
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
ds, err := finder.DefaultDatastore(ctx)
if err != nil {
t.Fatal(err)
}
// Move Datastore into a vm folder should fail
task, err := folders.VmFolder.MoveInto(ctx, []types.ManagedObjectReference{ds.Reference()})
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err == nil {
t.Errorf("expected error")
}
// Move Datacenter into a sub folder should pass
f, err := object.NewRootFolder(c.Client).CreateFolder(ctx, "foo")
if err != nil {
t.Error(err)
}
task, _ = f.MoveInto(ctx, []types.ManagedObjectReference{dc.Reference()})
err = task.Wait(ctx)
if err != nil {
t.Error(err)
}
pod, err := folders.DatastoreFolder.CreateStoragePod(ctx, "pod")
if err != nil {
t.Error(err)
}
// Moving any type other than Datastore into a StoragePod should fail
task, _ = pod.MoveInto(ctx, []types.ManagedObjectReference{dc.Reference()})
err = task.Wait(ctx)
if err == nil {
t.Error("expected error")
}
// Move DS into a StoragePod
task, _ = pod.MoveInto(ctx, []types.ManagedObjectReference{ds.Reference()})
err = task.Wait(ctx)
if err != nil {
t.Error(err)
}
}
func TestFolderCreateDVS(t *testing.T) {
ctx := context.Background()
model := VPX()
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
var spec types.DVSCreateSpec
spec.ConfigSpec = &types.VMwareDVSConfigSpec{}
spec.ConfigSpec.GetDVSConfigSpec().Name = "foo"
task, err := folders.NetworkFolder.CreateDVS(ctx, spec)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Error(err)
}
net, err := finder.Network(ctx, "foo")
if err != nil {
t.Error(err)
}
dvs, ok := net.(*object.DistributedVirtualSwitch)
if !ok {
t.Fatalf("%T is not of type %T", net, dvs)
}
task, err = folders.NetworkFolder.CreateDVS(ctx, spec)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err == nil {
t.Error("expected error")
}
pspec := types.DVPortgroupConfigSpec{Name: "xnet"}
task, err = dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{pspec})
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Error(err)
}
net, err = finder.Network(ctx, "xnet")
if err != nil {
t.Error(err)
}
pg, ok := net.(*object.DistributedVirtualPortgroup)
if !ok {
t.Fatalf("%T is not of type %T", net, pg)
}
backing, err := net.EthernetCardBackingInfo(ctx)
if err != nil {
t.Fatal(err)
}
info, ok := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
if ok {
if info.Port.SwitchUuid == "" || info.Port.PortgroupKey == "" {
t.Errorf("invalid port: %#v", info.Port)
}
} else {
t.Fatalf("%T is not of type %T", net, info)
}
task, err = dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{pspec})
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err == nil {
t.Error("expected error")
}
}

171
vendor/github.com/vmware/govmomi/simulator/guest_id.go generated vendored Normal file
View File

@@ -0,0 +1,171 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import "github.com/vmware/govmomi/vim25/types"
// GuestID is the list of valid types.VirtualMachineGuestOsIdentifier
var GuestID = []types.VirtualMachineGuestOsIdentifier{
types.VirtualMachineGuestOsIdentifierDosGuest,
types.VirtualMachineGuestOsIdentifierWin31Guest,
types.VirtualMachineGuestOsIdentifierWin95Guest,
types.VirtualMachineGuestOsIdentifierWin98Guest,
types.VirtualMachineGuestOsIdentifierWinMeGuest,
types.VirtualMachineGuestOsIdentifierWinNTGuest,
types.VirtualMachineGuestOsIdentifierWin2000ProGuest,
types.VirtualMachineGuestOsIdentifierWin2000ServGuest,
types.VirtualMachineGuestOsIdentifierWin2000AdvServGuest,
types.VirtualMachineGuestOsIdentifierWinXPHomeGuest,
types.VirtualMachineGuestOsIdentifierWinXPProGuest,
types.VirtualMachineGuestOsIdentifierWinXPPro64Guest,
types.VirtualMachineGuestOsIdentifierWinNetWebGuest,
types.VirtualMachineGuestOsIdentifierWinNetStandardGuest,
types.VirtualMachineGuestOsIdentifierWinNetEnterpriseGuest,
types.VirtualMachineGuestOsIdentifierWinNetDatacenterGuest,
types.VirtualMachineGuestOsIdentifierWinNetBusinessGuest,
types.VirtualMachineGuestOsIdentifierWinNetStandard64Guest,
types.VirtualMachineGuestOsIdentifierWinNetEnterprise64Guest,
types.VirtualMachineGuestOsIdentifierWinLonghornGuest,
types.VirtualMachineGuestOsIdentifierWinLonghorn64Guest,
types.VirtualMachineGuestOsIdentifierWinNetDatacenter64Guest,
types.VirtualMachineGuestOsIdentifierWinVistaGuest,
types.VirtualMachineGuestOsIdentifierWinVista64Guest,
types.VirtualMachineGuestOsIdentifierWindows7Guest,
types.VirtualMachineGuestOsIdentifierWindows7_64Guest,
types.VirtualMachineGuestOsIdentifierWindows7Server64Guest,
types.VirtualMachineGuestOsIdentifierWindows8Guest,
types.VirtualMachineGuestOsIdentifierWindows8_64Guest,
types.VirtualMachineGuestOsIdentifierWindows8Server64Guest,
types.VirtualMachineGuestOsIdentifierWindows9Guest,
types.VirtualMachineGuestOsIdentifierWindows9_64Guest,
types.VirtualMachineGuestOsIdentifierWindows9Server64Guest,
types.VirtualMachineGuestOsIdentifierWindowsHyperVGuest,
types.VirtualMachineGuestOsIdentifierFreebsdGuest,
types.VirtualMachineGuestOsIdentifierFreebsd64Guest,
types.VirtualMachineGuestOsIdentifierRedhatGuest,
types.VirtualMachineGuestOsIdentifierRhel2Guest,
types.VirtualMachineGuestOsIdentifierRhel3Guest,
types.VirtualMachineGuestOsIdentifierRhel3_64Guest,
types.VirtualMachineGuestOsIdentifierRhel4Guest,
types.VirtualMachineGuestOsIdentifierRhel4_64Guest,
types.VirtualMachineGuestOsIdentifierRhel5Guest,
types.VirtualMachineGuestOsIdentifierRhel5_64Guest,
types.VirtualMachineGuestOsIdentifierRhel6Guest,
types.VirtualMachineGuestOsIdentifierRhel6_64Guest,
types.VirtualMachineGuestOsIdentifierRhel7Guest,
types.VirtualMachineGuestOsIdentifierRhel7_64Guest,
types.VirtualMachineGuestOsIdentifierCentosGuest,
types.VirtualMachineGuestOsIdentifierCentos64Guest,
types.VirtualMachineGuestOsIdentifierCentos6Guest,
types.VirtualMachineGuestOsIdentifierCentos6_64Guest,
types.VirtualMachineGuestOsIdentifierCentos7Guest,
types.VirtualMachineGuestOsIdentifierCentos7_64Guest,
types.VirtualMachineGuestOsIdentifierOracleLinuxGuest,
types.VirtualMachineGuestOsIdentifierOracleLinux64Guest,
types.VirtualMachineGuestOsIdentifierOracleLinux6Guest,
types.VirtualMachineGuestOsIdentifierOracleLinux6_64Guest,
types.VirtualMachineGuestOsIdentifierOracleLinux7Guest,
types.VirtualMachineGuestOsIdentifierOracleLinux7_64Guest,
types.VirtualMachineGuestOsIdentifierSuseGuest,
types.VirtualMachineGuestOsIdentifierSuse64Guest,
types.VirtualMachineGuestOsIdentifierSlesGuest,
types.VirtualMachineGuestOsIdentifierSles64Guest,
types.VirtualMachineGuestOsIdentifierSles10Guest,
types.VirtualMachineGuestOsIdentifierSles10_64Guest,
types.VirtualMachineGuestOsIdentifierSles11Guest,
types.VirtualMachineGuestOsIdentifierSles11_64Guest,
types.VirtualMachineGuestOsIdentifierSles12Guest,
types.VirtualMachineGuestOsIdentifierSles12_64Guest,
types.VirtualMachineGuestOsIdentifierNld9Guest,
types.VirtualMachineGuestOsIdentifierOesGuest,
types.VirtualMachineGuestOsIdentifierSjdsGuest,
types.VirtualMachineGuestOsIdentifierMandrakeGuest,
types.VirtualMachineGuestOsIdentifierMandrivaGuest,
types.VirtualMachineGuestOsIdentifierMandriva64Guest,
types.VirtualMachineGuestOsIdentifierTurboLinuxGuest,
types.VirtualMachineGuestOsIdentifierTurboLinux64Guest,
types.VirtualMachineGuestOsIdentifierUbuntuGuest,
types.VirtualMachineGuestOsIdentifierUbuntu64Guest,
types.VirtualMachineGuestOsIdentifierDebian4Guest,
types.VirtualMachineGuestOsIdentifierDebian4_64Guest,
types.VirtualMachineGuestOsIdentifierDebian5Guest,
types.VirtualMachineGuestOsIdentifierDebian5_64Guest,
types.VirtualMachineGuestOsIdentifierDebian6Guest,
types.VirtualMachineGuestOsIdentifierDebian6_64Guest,
types.VirtualMachineGuestOsIdentifierDebian7Guest,
types.VirtualMachineGuestOsIdentifierDebian7_64Guest,
types.VirtualMachineGuestOsIdentifierDebian8Guest,
types.VirtualMachineGuestOsIdentifierDebian8_64Guest,
types.VirtualMachineGuestOsIdentifierDebian9Guest,
types.VirtualMachineGuestOsIdentifierDebian9_64Guest,
types.VirtualMachineGuestOsIdentifierDebian10Guest,
types.VirtualMachineGuestOsIdentifierDebian10_64Guest,
types.VirtualMachineGuestOsIdentifierAsianux3Guest,
types.VirtualMachineGuestOsIdentifierAsianux3_64Guest,
types.VirtualMachineGuestOsIdentifierAsianux4Guest,
types.VirtualMachineGuestOsIdentifierAsianux4_64Guest,
types.VirtualMachineGuestOsIdentifierAsianux5_64Guest,
types.VirtualMachineGuestOsIdentifierAsianux7_64Guest,
types.VirtualMachineGuestOsIdentifierOpensuseGuest,
types.VirtualMachineGuestOsIdentifierOpensuse64Guest,
types.VirtualMachineGuestOsIdentifierFedoraGuest,
types.VirtualMachineGuestOsIdentifierFedora64Guest,
types.VirtualMachineGuestOsIdentifierCoreos64Guest,
types.VirtualMachineGuestOsIdentifierVmwarePhoton64Guest,
types.VirtualMachineGuestOsIdentifierOther24xLinuxGuest,
types.VirtualMachineGuestOsIdentifierOther26xLinuxGuest,
types.VirtualMachineGuestOsIdentifierOtherLinuxGuest,
types.VirtualMachineGuestOsIdentifierOther3xLinuxGuest,
types.VirtualMachineGuestOsIdentifierGenericLinuxGuest,
types.VirtualMachineGuestOsIdentifierOther24xLinux64Guest,
types.VirtualMachineGuestOsIdentifierOther26xLinux64Guest,
types.VirtualMachineGuestOsIdentifierOther3xLinux64Guest,
types.VirtualMachineGuestOsIdentifierOtherLinux64Guest,
types.VirtualMachineGuestOsIdentifierSolaris6Guest,
types.VirtualMachineGuestOsIdentifierSolaris7Guest,
types.VirtualMachineGuestOsIdentifierSolaris8Guest,
types.VirtualMachineGuestOsIdentifierSolaris9Guest,
types.VirtualMachineGuestOsIdentifierSolaris10Guest,
types.VirtualMachineGuestOsIdentifierSolaris10_64Guest,
types.VirtualMachineGuestOsIdentifierSolaris11_64Guest,
types.VirtualMachineGuestOsIdentifierOs2Guest,
types.VirtualMachineGuestOsIdentifierEComStationGuest,
types.VirtualMachineGuestOsIdentifierEComStation2Guest,
types.VirtualMachineGuestOsIdentifierNetware4Guest,
types.VirtualMachineGuestOsIdentifierNetware5Guest,
types.VirtualMachineGuestOsIdentifierNetware6Guest,
types.VirtualMachineGuestOsIdentifierOpenServer5Guest,
types.VirtualMachineGuestOsIdentifierOpenServer6Guest,
types.VirtualMachineGuestOsIdentifierUnixWare7Guest,
types.VirtualMachineGuestOsIdentifierDarwinGuest,
types.VirtualMachineGuestOsIdentifierDarwin64Guest,
types.VirtualMachineGuestOsIdentifierDarwin10Guest,
types.VirtualMachineGuestOsIdentifierDarwin10_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin11Guest,
types.VirtualMachineGuestOsIdentifierDarwin11_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin12_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin13_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin14_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin15_64Guest,
types.VirtualMachineGuestOsIdentifierDarwin16_64Guest,
types.VirtualMachineGuestOsIdentifierVmkernelGuest,
types.VirtualMachineGuestOsIdentifierVmkernel5Guest,
types.VirtualMachineGuestOsIdentifierVmkernel6Guest,
types.VirtualMachineGuestOsIdentifierVmkernel65Guest,
types.VirtualMachineGuestOsIdentifierOtherGuest,
types.VirtualMachineGuestOsIdentifierOtherGuest64,
}

35
vendor/github.com/vmware/govmomi/simulator/guest_id.sh generated vendored Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash -e
pushd "$(dirname "$0")" >/dev/null
{
cat <<EOF
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
// GuestID is the list of valid types.VirtualMachineGuestOsIdentifier
var GuestID = []types.VirtualMachineGuestOsIdentifier{
EOF
ids=($(grep 'VirtualMachineGuestOsIdentifier(' ../vim25/types/enum.go | grep = | awk '{print $1}'))
printf "types.%s,\n" "${ids[@]}"
echo "}"
} > guest_id.go
goimports -w guest_id.go

View File

@@ -0,0 +1,254 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"io/ioutil"
"log"
"os"
"path"
"strings"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type HostDatastoreBrowser struct {
mo.HostDatastoreBrowser
}
type searchDatastore struct {
*HostDatastoreBrowser
DatastorePath string
SearchSpec *types.HostDatastoreBrowserSearchSpec
res []types.HostDatastoreBrowserSearchResults
recurse bool
}
func (s *searchDatastore) addFile(file os.FileInfo, res *types.HostDatastoreBrowserSearchResults) {
details := s.SearchSpec.Details
if details == nil {
details = new(types.FileQueryFlags)
}
name := file.Name()
info := types.FileInfo{
Path: name,
}
var finfo types.BaseFileInfo = &info
if details.FileSize {
info.FileSize = file.Size()
}
if details.Modification {
mtime := file.ModTime()
info.Modification = &mtime
}
if isTrue(details.FileOwner) {
// Assume for now this process created all files in the datastore
user := os.Getenv("USER")
info.Owner = user
}
if file.IsDir() {
finfo = &types.FolderFileInfo{FileInfo: info}
} else if details.FileType {
switch path.Ext(name) {
case ".img":
finfo = &types.FloppyImageFileInfo{FileInfo: info}
case ".iso":
finfo = &types.IsoImageFileInfo{FileInfo: info}
case ".log":
finfo = &types.VmLogFileInfo{FileInfo: info}
case ".nvram":
finfo = &types.VmNvramFileInfo{FileInfo: info}
case ".vmdk":
// TODO: lookup device to set other fields
finfo = &types.VmDiskFileInfo{FileInfo: info}
case ".vmx":
finfo = &types.VmConfigFileInfo{FileInfo: info}
}
}
res.File = append(res.File, finfo)
}
func (s *searchDatastore) queryMatch(file os.FileInfo) bool {
if len(s.SearchSpec.Query) == 0 {
return true
}
name := file.Name()
ext := path.Ext(name)
for _, q := range s.SearchSpec.Query {
switch q.(type) {
case *types.FileQuery:
return true
case *types.FolderFileQuery:
if file.IsDir() {
return true
}
case *types.FloppyImageFileQuery:
if ext == ".img" {
return true
}
case *types.IsoImageFileQuery:
if ext == ".iso" {
return true
}
case *types.VmConfigFileQuery:
if ext == ".vmx" {
// TODO: check Filter and Details fields
return true
}
case *types.VmDiskFileQuery:
if ext == ".vmdk" {
if strings.HasSuffix(name, "-flat.vmdk") {
// only matches the descriptor, not the backing file(s)
return false
}
// TODO: check Filter and Details fields
return true
}
case *types.VmLogFileQuery:
if ext == ".log" {
return strings.HasPrefix(name, "vmware")
}
case *types.VmNvramFileQuery:
if ext == ".nvram" {
return true
}
case *types.VmSnapshotFileQuery:
if ext == ".vmsn" {
return true
}
}
}
return false
}
func (s *searchDatastore) search(ds *types.ManagedObjectReference, folder string, dir string) error {
files, err := ioutil.ReadDir(dir)
if err != nil {
log.Printf("search %s: %s", dir, err)
return err
}
res := types.HostDatastoreBrowserSearchResults{
Datastore: ds,
FolderPath: folder,
}
for _, file := range files {
name := file.Name()
if s.queryMatch(file) {
for _, m := range s.SearchSpec.MatchPattern {
if ok, _ := path.Match(m, name); ok {
s.addFile(file, &res)
break
}
}
}
if s.recurse && file.IsDir() {
_ = s.search(ds, path.Join(folder, name), path.Join(dir, name))
}
}
s.res = append(s.res, res)
return nil
}
func (s *searchDatastore) Run(Task *Task) (types.AnyType, types.BaseMethodFault) {
p, fault := parseDatastorePath(s.DatastorePath)
if fault != nil {
return nil, fault
}
ref := Map.FindByName(p.Datastore, s.Datastore)
if ref == nil {
return nil, &types.InvalidDatastore{Name: p.Datastore}
}
ds := ref.(*Datastore)
dir := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path)
err := s.search(&ds.Self, s.DatastorePath, dir)
if err != nil {
ff := types.FileFault{
File: p.Path,
}
if os.IsNotExist(err) {
return nil, &types.FileNotFound{FileFault: ff}
}
return nil, &types.InvalidArgument{InvalidProperty: p.Path}
}
if s.recurse {
return types.ArrayOfHostDatastoreBrowserSearchResults{
HostDatastoreBrowserSearchResults: s.res,
}, nil
}
return s.res[0], nil
}
func (b *HostDatastoreBrowser) SearchDatastoreTask(s *types.SearchDatastore_Task) soap.HasFault {
task := NewTask(&searchDatastore{
HostDatastoreBrowser: b,
DatastorePath: s.DatastorePath,
SearchSpec: s.SearchSpec,
})
return &methods.SearchDatastore_TaskBody{
Res: &types.SearchDatastore_TaskResponse{
Returnval: task.Run(),
},
}
}
func (b *HostDatastoreBrowser) SearchDatastoreSubFoldersTask(s *types.SearchDatastoreSubFolders_Task) soap.HasFault {
task := NewTask(&searchDatastore{
HostDatastoreBrowser: b,
DatastorePath: s.DatastorePath,
SearchSpec: s.SearchSpec,
recurse: true,
})
return &methods.SearchDatastoreSubFolders_TaskBody{
Res: &types.SearchDatastoreSubFolders_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,161 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"os"
"path"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type HostDatastoreSystem struct {
mo.HostDatastoreSystem
Host *mo.HostSystem
}
func (dss *HostDatastoreSystem) add(ds *Datastore) *soap.Fault {
info := ds.Info.GetDatastoreInfo()
info.Name = ds.Name
if e := Map.FindByName(ds.Name, dss.Datastore); e != nil {
return Fault(e.Reference().Value, &types.DuplicateName{
Name: ds.Name,
Object: e.Reference(),
})
}
fi, err := os.Stat(info.Url)
if err == nil && !fi.IsDir() {
err = os.ErrInvalid
}
if err != nil {
switch {
case os.IsNotExist(err):
return Fault(err.Error(), &types.NotFound{})
default:
return Fault(err.Error(), &types.HostConfigFault{})
}
}
folder := Map.getEntityFolder(dss.Host, "datastore")
ds.Self.Type = typeName(ds)
// Datastore is the only type where create methods do not include the parent (Folder in this case),
// but we need the moref to be unique per DC/datastoreFolder, but not per-HostSystem.
ds.Self.Value += "@" + folder.Self.Value
// TODO: name should be made unique in the case of Local ds type
ds.Summary.Datastore = &ds.Self
ds.Summary.Name = ds.Name
ds.Summary.Url = info.Url
dss.Datastore = append(dss.Datastore, ds.Self)
dss.Host.Datastore = dss.Datastore
parent := hostParent(dss.Host)
Map.AddReference(parent, &parent.Datastore, ds.Self)
browser := &HostDatastoreBrowser{}
browser.Datastore = dss.Datastore
ds.Browser = Map.Put(browser).Reference()
folder.putChild(ds)
return nil
}
func (dss *HostDatastoreSystem) CreateLocalDatastore(c *types.CreateLocalDatastore) soap.HasFault {
r := &methods.CreateLocalDatastoreBody{}
ds := &Datastore{}
ds.Name = c.Name
ds.Self.Value = c.Path
ds.Info = &types.LocalDatastoreInfo{
DatastoreInfo: types.DatastoreInfo{
Name: c.Name,
Url: c.Path,
},
Path: c.Path,
}
ds.Summary.Type = "local"
if err := dss.add(ds); err != nil {
r.Fault_ = err
return r
}
ds.Host = append(ds.Host, types.DatastoreHostMount{
Key: dss.Host.Reference(),
MountInfo: types.HostMountInfo{
AccessMode: string(types.HostMountModeReadWrite),
Mounted: types.NewBool(true),
Accessible: types.NewBool(true),
},
})
_ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self})
r.Res = &types.CreateLocalDatastoreResponse{
Returnval: ds.Self,
}
return r
}
func (dss *HostDatastoreSystem) CreateNasDatastore(c *types.CreateNasDatastore) soap.HasFault {
r := &methods.CreateNasDatastoreBody{}
ds := &Datastore{}
ds.Name = path.Base(c.Spec.LocalPath)
ds.Self.Value = c.Spec.RemoteHost + ":" + c.Spec.RemotePath
ds.Info = &types.NasDatastoreInfo{
DatastoreInfo: types.DatastoreInfo{
Url: c.Spec.LocalPath,
},
Nas: &types.HostNasVolume{
HostFileSystemVolume: types.HostFileSystemVolume{
Name: c.Spec.LocalPath,
Type: c.Spec.Type,
},
RemoteHost: c.Spec.RemoteHost,
RemotePath: c.Spec.RemotePath,
},
}
ds.Summary.Type = c.Spec.Type
if err := dss.add(ds); err != nil {
r.Fault_ = err
return r
}
_ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self})
r.Res = &types.CreateNasDatastoreResponse{
Returnval: ds.Self,
}
return r
}

View File

@@ -0,0 +1,100 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/types"
)
func TestHostDatastoreSystem(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
host := object.NewHostSystem(c.Client, esx.HostSystem.Reference())
dss, err := host.ConfigManager().DatastoreSystem(ctx)
if err != nil {
t.Error(err)
}
pwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
spec := types.HostNasVolumeSpec{
Type: string(types.HostFileSystemVolumeFileSystemTypeNFS),
RemoteHost: "localhost",
}
tests := []func(string) (*object.Datastore, error){
func(dir string) (*object.Datastore, error) {
spec.LocalPath = dir
spec.RemotePath = dir
return dss.CreateNasDatastore(ctx, spec)
},
func(dir string) (*object.Datastore, error) {
return dss.CreateLocalDatastore(ctx, filepath.Base(dir), dir)
},
}
for _, create := range tests {
for _, fail := range []bool{false, true} {
if fail {
_, err = create(pwd)
if err == nil {
t.Error("expected error")
}
// TODO: hds.Remove(ds)
pwd = filepath.Join(pwd, "esx")
} else {
_, err = create(pwd)
if err != nil {
t.Error(err)
}
}
}
}
for _, create := range tests {
for _, dir := range []string{"./enoent", "host_datastore_system.go"} {
_, err = create(dir)
if err == nil {
t.Error("expected error")
}
}
}
}

View File

@@ -0,0 +1,87 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type HostFirewallSystem struct {
mo.HostFirewallSystem
}
func NewHostFirewallSystem(_ *mo.HostSystem) *HostFirewallSystem {
info := esx.HostFirewallInfo
return &HostFirewallSystem{
HostFirewallSystem: mo.HostFirewallSystem{
FirewallInfo: &info,
},
}
}
func DisableRuleset(info *types.HostFirewallInfo, id string) bool {
for i := range info.Ruleset {
if info.Ruleset[i].Key == id {
info.Ruleset[i].Enabled = false
return true
}
}
return false
}
func (s *HostFirewallSystem) DisableRuleset(req *types.DisableRuleset) soap.HasFault {
body := &methods.DisableRulesetBody{}
if DisableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) {
body.Res = new(types.DisableRulesetResponse)
return body
}
body.Fault_ = Fault("", &types.NotFound{})
return body
}
func EnableRuleset(info *types.HostFirewallInfo, id string) bool {
for i := range info.Ruleset {
if info.Ruleset[i].Key == id {
info.Ruleset[i].Enabled = true
return true
}
}
return false
}
func (s *HostFirewallSystem) EnableRuleset(req *types.EnableRuleset) soap.HasFault {
body := &methods.EnableRulesetBody{}
if EnableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) {
body.Res = new(types.EnableRulesetResponse)
return body
}
body.Fault_ = Fault("", &types.NotFound{})
return body
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
)
func TestHostFirewallSystem(t *testing.T) {
ctx := context.Background()
m := ESX()
m.Datastore = 0
m.Machine = 0
err := m.Create()
if err != nil {
t.Fatal(err)
}
c := m.Service.client
host := object.NewHostSystem(c, esx.HostSystem.Reference())
hfs, _ := host.ConfigManager().FirewallSystem(ctx)
err = hfs.DisableRuleset(ctx, "enoent")
if err == nil {
t.Error("expected error")
}
err = hfs.EnableRuleset(ctx, "enoent")
if err == nil {
t.Error("expected error")
}
err = hfs.DisableRuleset(ctx, "sshServer")
if err != nil {
t.Error(err)
}
err = hfs.EnableRuleset(ctx, "sshServer")
if err != nil {
t.Error(err)
}
_, err = hfs.Info(ctx)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,80 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// As of vSphere API 5.1, local groups operations are deprecated, so it's not supported here.
type HostLocalAccountManager struct {
mo.HostLocalAccountManager
}
func NewHostLocalAccountManager(ref types.ManagedObjectReference) object.Reference {
m := &HostLocalAccountManager{}
m.Self = ref
return m
}
func (h *HostLocalAccountManager) CreateUser(req *types.CreateUser) soap.HasFault {
spec := req.User.GetHostAccountSpec()
userDirectory := Map.UserDirectory()
found := userDirectory.search(true, false, compareFunc(spec.Id, true))
if len(found) > 0 {
return &methods.CreateUserBody{
Fault_: Fault("", &types.AlreadyExists{}),
}
}
userDirectory.addUser(spec.Id)
return &methods.CreateUserBody{
Res: &types.CreateUserResponse{},
}
}
func (h *HostLocalAccountManager) RemoveUser(req *types.RemoveUser) soap.HasFault {
userDirectory := Map.UserDirectory()
found := userDirectory.search(true, false, compareFunc(req.UserName, true))
if len(found) == 0 {
return &methods.RemoveUserBody{
Fault_: Fault("", &types.UserNotFound{}),
}
}
userDirectory.removeUser(req.UserName)
return &methods.RemoveUserBody{
Res: &types.RemoveUserResponse{},
}
}
func (h *HostLocalAccountManager) UpdateUser(req *types.UpdateUser) soap.HasFault {
return &methods.CreateUserBody{
Res: &types.CreateUserResponse{},
}
}

View File

@@ -0,0 +1,92 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
func TestHostLocalAccountManager(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
ts := m.Service.NewServer()
defer ts.Close()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
ref := types.ManagedObjectReference{Type: "HostLocalAccountManager", Value: "ha-localacctmgr"}
createUserReq := &types.CreateUser{
This: ref,
User: &types.HostAccountSpec{
Id: "userid",
},
}
_, err = methods.CreateUser(ctx, c.Client, createUserReq)
if err != nil {
t.Fatal(err)
}
_, err = methods.CreateUser(ctx, c.Client, createUserReq)
if err == nil {
t.Fatal("expect err; got nil")
}
updateUserReq := &types.UpdateUser{
This: ref,
User: &types.HostAccountSpec{
Id: "userid",
},
}
_, err = methods.UpdateUser(ctx, c.Client, updateUserReq)
if err != nil {
t.Fatal(err)
}
removeUserReq := &types.RemoveUser{
This: ref,
UserName: "userid",
}
_, err = methods.RemoveUser(ctx, c.Client, removeUserReq)
if err != nil {
t.Fatal(err)
}
_, err = methods.RemoveUser(ctx, c.Client, removeUserReq)
if err == nil {
t.Fatal("expect err; got nil")
}
}

View File

@@ -0,0 +1,171 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type HostNetworkSystem struct {
mo.HostNetworkSystem
Host *mo.HostSystem
}
func NewHostNetworkSystem(host *mo.HostSystem) *HostNetworkSystem {
return &HostNetworkSystem{
Host: host,
HostNetworkSystem: mo.HostNetworkSystem{
NetworkInfo: &types.HostNetworkInfo{
Vswitch: []types.HostVirtualSwitch{
{
Name: "vSwitch0",
Portgroup: []string{"VM Network"},
},
},
},
},
}
}
func (s *HostNetworkSystem) folder() *Folder {
f := Map.getEntityDatacenter(s.Host).NetworkFolder
return Map.Get(f).(*Folder)
}
func (s *HostNetworkSystem) AddVirtualSwitch(c *types.AddVirtualSwitch) soap.HasFault {
r := &methods.AddVirtualSwitchBody{}
for _, vswitch := range s.NetworkInfo.Vswitch {
if vswitch.Name == c.VswitchName {
r.Fault_ = Fault("", &types.AlreadyExists{Name: c.VswitchName})
return r
}
}
s.NetworkInfo.Vswitch = append(s.NetworkInfo.Vswitch, types.HostVirtualSwitch{
Name: c.VswitchName,
})
r.Res = &types.AddVirtualSwitchResponse{}
return r
}
func (s *HostNetworkSystem) RemoveVirtualSwitch(c *types.RemoveVirtualSwitch) soap.HasFault {
r := &methods.RemoveVirtualSwitchBody{}
vs := s.NetworkInfo.Vswitch
for i, v := range vs {
if v.Name == c.VswitchName {
s.NetworkInfo.Vswitch = append(vs[:i], vs[i+1:]...)
r.Res = &types.RemoveVirtualSwitchResponse{}
return r
}
}
r.Fault_ = Fault("", &types.NotFound{})
return r
}
func (s *HostNetworkSystem) AddPortGroup(c *types.AddPortGroup) soap.HasFault {
var vswitch *types.HostVirtualSwitch
r := &methods.AddPortGroupBody{}
if c.Portgrp.Name == "" {
r.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "name"})
return r
}
for i := range s.NetworkInfo.Vswitch {
if s.NetworkInfo.Vswitch[i].Name == c.Portgrp.VswitchName {
vswitch = &s.NetworkInfo.Vswitch[i]
break
}
}
if vswitch == nil {
r.Fault_ = Fault("", &types.NotFound{})
return r
}
network := &mo.Network{}
network.Name = c.Portgrp.Name
network.Entity().Name = network.Name
folder := s.folder()
if obj := Map.FindByName(c.Portgrp.Name, folder.ChildEntity); obj != nil {
r.Fault_ = Fault("", &types.DuplicateName{
Name: c.Portgrp.Name,
Object: obj.Reference(),
})
return r
}
folder.putChild(network)
vswitch.Portgroup = append(vswitch.Portgroup, c.Portgrp.Name)
r.Res = &types.AddPortGroupResponse{}
return r
}
func (s *HostNetworkSystem) RemovePortGroup(c *types.RemovePortGroup) soap.HasFault {
var vswitch *types.HostVirtualSwitch
r := &methods.RemovePortGroupBody{}
for i, v := range s.NetworkInfo.Vswitch {
for j, pg := range v.Portgroup {
if pg == c.PgName {
vswitch = &s.NetworkInfo.Vswitch[i]
vswitch.Portgroup = append(vswitch.Portgroup[:j], vswitch.Portgroup[j+1:]...)
}
}
}
if vswitch == nil {
r.Fault_ = Fault("", &types.NotFound{})
return r
}
folder := s.folder()
e := Map.FindByName(c.PgName, folder.ChildEntity)
folder.removeChild(e.Reference())
r.Res = &types.RemovePortGroupResponse{}
return r
}
func (s *HostNetworkSystem) UpdateNetworkConfig(req *types.UpdateNetworkConfig) soap.HasFault {
s.NetworkConfig = &req.Config
return &methods.UpdateNetworkConfigBody{
Res: &types.UpdateNetworkConfigResponse{
Returnval: types.HostNetworkConfigResult{},
},
}
}

View File

@@ -0,0 +1,118 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/types"
)
func TestHostNetworkSystem(t *testing.T) {
ctx := context.Background()
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
host := object.NewHostSystem(s.client, esx.HostSystem.Reference())
ns, err := host.ConfigManager().NetworkSystem(ctx)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(s.client, false)
finder.SetDatacenter(object.NewDatacenter(s.client, esx.Datacenter.Reference()))
// created by default
_, err = finder.Network(ctx, "VM Network")
if err != nil {
t.Fatal(err)
}
// not created yet
_, err = finder.Network(ctx, "bridge")
if err == nil {
t.Fatal("expected error")
}
err = ns.AddVirtualSwitch(ctx, "vSwitch0", nil)
if err == nil {
t.Fatal("expected error") // DuplicateName
}
err = ns.AddVirtualSwitch(ctx, "vSwitch1", nil)
if err != nil {
t.Fatal(err)
}
spec := types.HostPortGroupSpec{}
err = ns.AddPortGroup(ctx, spec)
if err == nil {
t.Fatal("expected error") // InvalidArgument "name"
}
spec.Name = "bridge"
err = ns.AddPortGroup(ctx, spec)
if err == nil {
t.Fatal("expected error") // NotFound
}
spec.VswitchName = "vSwitch1"
err = ns.AddPortGroup(ctx, spec)
if err != nil {
t.Fatal(err)
}
_, err = finder.Network(ctx, "bridge")
if err != nil {
t.Fatal(err)
}
err = ns.AddPortGroup(ctx, spec)
if err == nil {
t.Error("expected error") // DuplicateName
}
err = ns.RemovePortGroup(ctx, "bridge")
if err != nil {
t.Fatal(err)
}
_, err = finder.Network(ctx, "bridge")
if err == nil {
t.Fatal("expected error")
}
err = ns.RemovePortGroup(ctx, "bridge")
if err == nil {
t.Error("expected error")
}
err = ns.RemoveVirtualSwitch(ctx, "vSwitch1")
if err != nil {
t.Fatal(err)
}
err = ns.RemoveVirtualSwitch(ctx, "vSwitch1")
if err == nil {
t.Error("expected error")
}
}

View File

@@ -0,0 +1,219 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type HostSystem struct {
mo.HostSystem
}
func NewHostSystem(host mo.HostSystem) *HostSystem {
now := time.Now()
hs := &HostSystem{
HostSystem: host,
}
hs.Name = hs.Summary.Config.Name
hs.Summary.Runtime = &hs.Runtime
hs.Summary.Runtime.BootTime = &now
id := uuid.New().String()
hardware := *host.Summary.Hardware
hs.Summary.Hardware = &hardware
hs.Summary.Hardware.Uuid = id
info := *esx.HostHardwareInfo
info.SystemInfo.Uuid = id
hs.Hardware = &info
config := []struct {
ref **types.ManagedObjectReference
obj mo.Reference
}{
{&hs.ConfigManager.DatastoreSystem, &HostDatastoreSystem{Host: &hs.HostSystem}},
{&hs.ConfigManager.NetworkSystem, NewHostNetworkSystem(&hs.HostSystem)},
{&hs.ConfigManager.AdvancedOption, NewOptionManager(nil, esx.Setting)},
{&hs.ConfigManager.FirewallSystem, NewHostFirewallSystem(&hs.HostSystem)},
}
for _, c := range config {
ref := Map.Put(c.obj).Reference()
*c.ref = &ref
}
return hs
}
func (h *HostSystem) eventArgument() *types.HostEventArgument {
return &types.HostEventArgument{
Host: h.Self,
EntityEventArgument: types.EntityEventArgument{Name: h.Name},
}
}
func (h *HostSystem) eventArgumentParent() *types.ComputeResourceEventArgument {
parent := hostParent(&h.HostSystem)
return &types.ComputeResourceEventArgument{
ComputeResource: parent.Self,
EntityEventArgument: types.EntityEventArgument{Name: parent.Name},
}
}
func hostParent(host *mo.HostSystem) *mo.ComputeResource {
switch parent := Map.Get(*host.Parent).(type) {
case *mo.ComputeResource:
return parent
case *ClusterComputeResource:
return &parent.ComputeResource
default:
return nil
}
}
func addComputeResource(s *types.ComputeResourceSummary, h *HostSystem) {
s.TotalCpu += h.Summary.Hardware.CpuMhz
s.TotalMemory += h.Summary.Hardware.MemorySize
s.NumCpuCores += h.Summary.Hardware.NumCpuCores
s.NumCpuThreads += h.Summary.Hardware.NumCpuThreads
s.EffectiveCpu += h.Summary.Hardware.CpuMhz
s.EffectiveMemory += h.Summary.Hardware.MemorySize
s.NumHosts++
s.NumEffectiveHosts++
s.OverallStatus = types.ManagedEntityStatusGreen
}
// CreateDefaultESX creates a standalone ESX
// Adds objects of type: Datacenter, Network, ComputeResource, ResourcePool and HostSystem
func CreateDefaultESX(f *Folder) {
dc := NewDatacenter(f)
host := NewHostSystem(esx.HostSystem)
summary := new(types.ComputeResourceSummary)
addComputeResource(summary, host)
cr := &mo.ComputeResource{Summary: summary}
cr.Self = *host.Parent
cr.Name = host.Name
cr.Host = append(cr.Host, host.Reference())
Map.PutEntity(cr, host)
pool := NewResourcePool()
cr.ResourcePool = &pool.Self
Map.PutEntity(cr, pool)
pool.Owner = cr.Self
Map.Get(dc.HostFolder).(*Folder).putChild(cr)
}
// CreateStandaloneHost uses esx.HostSystem as a template, applying the given spec
// and creating the ComputeResource parent and ResourcePool sibling.
func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, types.BaseMethodFault) {
if spec.HostName == "" {
return nil, &types.NoHost{}
}
pool := NewResourcePool()
host := NewHostSystem(esx.HostSystem)
host.Summary.Config.Name = spec.HostName
host.Name = host.Summary.Config.Name
host.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
summary := new(types.ComputeResourceSummary)
addComputeResource(summary, host)
cr := &mo.ComputeResource{
ConfigurationEx: &types.ComputeResourceConfigInfo{
VmSwapPlacement: string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory),
},
Summary: summary,
}
Map.PutEntity(cr, Map.NewEntity(host))
host.Summary.Host = &host.Self
Map.PutEntity(cr, Map.NewEntity(pool))
cr.Name = host.Name
cr.Host = append(cr.Host, host.Reference())
cr.ResourcePool = &pool.Self
f.putChild(cr)
pool.Owner = cr.Self
return host, nil
}
func (h *HostSystem) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(h, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if len(h.Vm) > 0 {
return nil, &types.ResourceInUse{}
}
f := Map.getEntityParent(h, "Folder").(*Folder)
f.removeChild(h.Reference())
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}
func (h *HostSystem) EnterMaintenanceModeTask(spec *types.EnterMaintenanceMode_Task) soap.HasFault {
task := CreateTask(h, "enterMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
h.Runtime.InMaintenanceMode = true
return nil, nil
})
return &methods.EnterMaintenanceMode_TaskBody{
Res: &types.EnterMaintenanceMode_TaskResponse{
Returnval: task.Run(),
},
}
}
func (h *HostSystem) ExitMaintenanceModeTask(spec *types.ExitMaintenanceMode_Task) soap.HasFault {
task := CreateTask(h, "exitMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
h.Runtime.InMaintenanceMode = false
return nil, nil
})
return &methods.ExitMaintenanceMode_TaskBody{
Res: &types.ExitMaintenanceMode_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,208 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
)
func TestDefaultESX(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
client, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, "")
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
host, err := finder.HostSystemOrDefault(ctx, "*")
if err != nil {
t.Fatal(err)
}
if host.Name() != esx.HostSystem.Summary.Config.Name {
t.Fail()
}
pool, err := finder.ResourcePoolOrDefault(ctx, "*")
if err != nil {
t.Fatal(err)
}
if pool.Name() != "Resources" {
t.Fail()
}
}
func TestMaintenanceMode(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c := m.Service.client
hs := Map.Get(esx.HostSystem.Reference()).(*HostSystem)
host := object.NewHostSystem(c, hs.Self)
task, err := host.EnterMaintenanceMode(ctx, 1, false, nil)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if hs.Runtime.InMaintenanceMode != true {
t.Fatal("expect InMaintenanceMode is true; got false")
}
task, err = host.ExitMaintenanceMode(ctx, 1)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if hs.Runtime.InMaintenanceMode != false {
t.Fatal("expect InMaintenanceMode is false; got true")
}
}
func TestNewHostSystem(t *testing.T) {
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
hs := NewHostSystem(esx.HostSystem)
if hs.Summary.Runtime != &hs.Runtime {
t.Fatal("expected hs.Summary.Runtime == &hs.Runtime; got !=")
}
}
func TestDestroyHostSystem(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c := m.Service.client
vm := Map.Any("VirtualMachine").(*VirtualMachine)
hs := Map.Get(*vm.Runtime.Host).(*HostSystem)
host := object.NewHostSystem(c, hs.Self)
vms := []*VirtualMachine{}
for _, vmref := range hs.Vm {
vms = append(vms, Map.Get(vmref).(*VirtualMachine))
}
task, err := host.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err == nil {
t.Fatal("expect err because host with vms cannot be destroyed")
}
for _, vmref := range hs.Vm {
vm := Map.Get(vmref).(*VirtualMachine)
vmo := object.NewVirtualMachine(c, vm.Self)
task, err := vmo.PowerOff(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
task, err = vmo.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
}
task, err = host.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
hs2 := Map.Get(esx.HostSystem.Reference())
if hs2 != nil {
t.Fatal("host should have been destroyed")
}
}

View File

@@ -0,0 +1,392 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
var ipPool = MustNewIpPool(&types.IpPool{
Id: 1,
Name: "ip-pool",
AvailableIpv4Addresses: 250,
AvailableIpv6Addresses: 250,
AllocatedIpv6Addresses: 0,
AllocatedIpv4Addresses: 0,
Ipv4Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "10.10.10.255",
Gateway: "10.10.10.1",
SubnetAddress: "10.10.10.0",
Range: "10.10.10.2#250",
},
Ipv6Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "2001:4860:0:2001::ff",
Gateway: "2001:4860:0:2001::1",
SubnetAddress: "2001:4860:0:2001::0",
Range: "2001:4860:0:2001::2#250",
},
})
// IpPoolManager implements a simple IP Pool manager in which all pools are shared
// across different datacenters.
type IpPoolManager struct {
mo.IpPoolManager
pools map[int32]*IpPool
nextPoolId int32
}
func NewIpPoolManager(ref types.ManagedObjectReference) *IpPoolManager {
m := &IpPoolManager{}
m.Self = ref
m.pools = map[int32]*IpPool{
1: ipPool,
}
m.nextPoolId = 2
return m
}
func (m *IpPoolManager) CreateIpPool(req *types.CreateIpPool) soap.HasFault {
body := &methods.CreateIpPoolBody{}
id := m.nextPoolId
var err error
m.pools[id], err = NewIpPool(&req.Pool)
if err != nil {
body.Fault_ = Fault("", &types.RuntimeFault{})
return body
}
m.nextPoolId++
body.Res = &types.CreateIpPoolResponse{
Returnval: id,
}
return body
}
func (m *IpPoolManager) DestroyIpPool(req *types.DestroyIpPool) soap.HasFault {
delete(m.pools, req.Id)
return &methods.DestroyIpPoolBody{
Res: &types.DestroyIpPoolResponse{},
}
}
func (m *IpPoolManager) QueryIpPools(req *types.QueryIpPools) soap.HasFault {
pools := []types.IpPool{}
for i := int32(1); i < m.nextPoolId; i++ {
if p, ok := m.pools[i]; ok {
pools = append(pools, *p.config)
}
}
return &methods.QueryIpPoolsBody{
Res: &types.QueryIpPoolsResponse{
Returnval: pools,
},
}
}
func (m *IpPoolManager) UpdateIpPool(req *types.UpdateIpPool) soap.HasFault {
body := &methods.UpdateIpPoolBody{}
var pool *IpPool
var err error
var ok bool
if pool, ok = m.pools[req.Pool.Id]; !ok {
body.Fault_ = Fault("", &types.NotFoundFault{})
return body
}
if pool.config.AllocatedIpv4Addresses+pool.config.AllocatedIpv6Addresses != 0 {
body.Fault_ = Fault("update a pool has been used is not supported", &types.RuntimeFault{})
return body
}
m.pools[req.Pool.Id], err = NewIpPool(&req.Pool)
if err != nil {
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
return body
}
body.Res = &types.UpdateIpPoolResponse{}
return body
}
func (m *IpPoolManager) AllocateIpv4Address(req *types.AllocateIpv4Address) soap.HasFault {
body := &methods.AllocateIpv4AddressBody{}
pool, ok := m.pools[req.PoolId]
if !ok {
body.Fault_ = Fault("", &types.InvalidArgument{})
return body
}
ip, err := pool.AllocateIPv4(req.AllocationId)
if err != nil {
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
return body
}
body.Res = &types.AllocateIpv4AddressResponse{
Returnval: ip,
}
return body
}
func (m *IpPoolManager) AllocateIpv6Address(req *types.AllocateIpv6Address) soap.HasFault {
body := &methods.AllocateIpv6AddressBody{}
pool, ok := m.pools[req.PoolId]
if !ok {
body.Fault_ = Fault("", &types.InvalidArgument{})
return body
}
ip, err := pool.AllocateIpv6(req.AllocationId)
if err != nil {
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
return body
}
body.Res = &types.AllocateIpv6AddressResponse{
Returnval: ip,
}
return body
}
func (m *IpPoolManager) ReleaseIpAllocation(req *types.ReleaseIpAllocation) soap.HasFault {
body := &methods.ReleaseIpAllocationBody{}
pool, ok := m.pools[req.PoolId]
if !ok {
body.Fault_ = Fault("", &types.InvalidArgument{})
return body
}
pool.ReleaseIpv4(req.AllocationId)
pool.ReleaseIpv6(req.AllocationId)
body.Res = &types.ReleaseIpAllocationResponse{}
return body
}
func (m *IpPoolManager) QueryIPAllocations(req *types.QueryIPAllocations) soap.HasFault {
body := &methods.QueryIPAllocationsBody{}
pool, ok := m.pools[req.PoolId]
if !ok {
body.Fault_ = Fault("", &types.InvalidArgument{})
return body
}
body.Res = &types.QueryIPAllocationsResponse{}
ipv4, ok := pool.ipv4Allocation[req.ExtensionKey]
if ok {
body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
IpAddress: ipv4,
AllocationId: req.ExtensionKey,
})
}
ipv6, ok := pool.ipv6Allocation[req.ExtensionKey]
if ok {
body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
IpAddress: ipv6,
AllocationId: req.ExtensionKey,
})
}
return body
}
var (
errNoIpAvailable = errors.New("no ip address available")
errInvalidAllocation = errors.New("allocation id not recognized")
)
type IpPool struct {
config *types.IpPool
ipv4Allocation map[string]string
ipv6Allocation map[string]string
ipv4Pool []string
ipv6Pool []string
}
func MustNewIpPool(config *types.IpPool) *IpPool {
pool, err := NewIpPool(config)
if err != nil {
panic(err)
}
return pool
}
func NewIpPool(config *types.IpPool) (*IpPool, error) {
pool := &IpPool{
config: config,
ipv4Allocation: make(map[string]string),
ipv6Allocation: make(map[string]string),
}
return pool, pool.init()
}
func (p *IpPool) init() error {
// IPv4 range
if p.config.Ipv4Config != nil {
ranges := strings.Split(p.config.Ipv4Config.Range, ",")
for _, r := range ranges {
sp := strings.Split(r, "#")
if len(sp) != 2 {
return fmt.Errorf("format of range should be ip#number; got %q", r)
}
ip := net.ParseIP(strings.TrimSpace(sp[0])).To4()
if ip == nil {
return fmt.Errorf("bad ip format: %q", sp[0])
}
length, err := strconv.Atoi(sp[1])
if err != nil {
return err
}
for i := 0; i < length; i++ {
p.ipv4Pool = append(p.ipv4Pool, net.IPv4(ip[0], ip[1], ip[2], ip[3]+byte(i)).String())
}
}
}
// IPv6 range
if p.config.Ipv6Config != nil {
ranges := strings.Split(p.config.Ipv6Config.Range, ",")
for _, r := range ranges {
sp := strings.Split(r, "#")
if len(sp) != 2 {
return fmt.Errorf("format of range should be ip#number; got %q", r)
}
ip := net.ParseIP(strings.TrimSpace(sp[0])).To16()
if ip == nil {
return fmt.Errorf("bad ip format: %q", sp[0])
}
length, err := strconv.Atoi(sp[1])
if err != nil {
return err
}
for i := 0; i < length; i++ {
var ipv6 [16]byte
copy(ipv6[:], ip)
ipv6[15] += byte(i)
p.ipv6Pool = append(p.ipv6Pool, net.IP(ipv6[:]).String())
}
}
}
return nil
}
func (p *IpPool) AllocateIPv4(allocation string) (string, error) {
if ip, ok := p.ipv4Allocation[allocation]; ok {
return ip, nil
}
l := len(p.ipv4Pool)
if l == 0 {
return "", errNoIpAvailable
}
ip := p.ipv4Pool[l-1]
p.config.AvailableIpv4Addresses--
p.config.AllocatedIpv4Addresses++
p.ipv4Pool = p.ipv4Pool[:l-1]
p.ipv4Allocation[allocation] = ip
return ip, nil
}
func (p *IpPool) ReleaseIpv4(allocation string) error {
ip, ok := p.ipv4Allocation[allocation]
if !ok {
return errInvalidAllocation
}
delete(p.ipv4Allocation, allocation)
p.config.AvailableIpv4Addresses++
p.config.AllocatedIpv4Addresses--
p.ipv4Pool = append(p.ipv4Pool, ip)
return nil
}
func (p *IpPool) AllocateIpv6(allocation string) (string, error) {
if ip, ok := p.ipv6Allocation[allocation]; ok {
return ip, nil
}
l := len(p.ipv6Pool)
if l == 0 {
return "", errNoIpAvailable
}
ip := p.ipv6Pool[l-1]
p.config.AvailableIpv6Addresses--
p.config.AllocatedIpv6Addresses++
p.ipv6Pool = p.ipv6Pool[:l-1]
p.ipv6Allocation[allocation] = ip
return ip, nil
}
func (p *IpPool) ReleaseIpv6(allocation string) error {
ip, ok := p.ipv6Allocation[allocation]
if !ok {
return errInvalidAllocation
}
delete(p.ipv6Allocation, allocation)
p.config.AvailableIpv6Addresses++
p.config.AllocatedIpv6Addresses--
p.ipv6Pool = append(p.ipv6Pool, ip)
return nil
}

View File

@@ -0,0 +1,362 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"fmt"
"net"
"reflect"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
func TestIpPoolv4(t *testing.T) {
tests := []struct {
ipRange string
length int32
}{
{"10.10.10.2#250", 250},
{"10.10.10.2#10, 10.10.10.20#20", 30},
{"10.10.10.2#10, 10.10.10.20#20, 10.10.10.50#20", 50},
}
for _, test := range tests {
var ipPool = MustNewIpPool(&types.IpPool{
Id: 1,
Name: "ip-pool",
AvailableIpv4Addresses: test.length,
AvailableIpv6Addresses: 0,
AllocatedIpv6Addresses: 0,
AllocatedIpv4Addresses: 0,
Ipv4Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "10.10.10.255",
Gateway: "10.10.10.1",
SubnetAddress: "10.10.10.0",
Range: test.ipRange,
},
})
if len(ipPool.ipv4Pool) != int(test.length) {
t.Fatalf("expect length to be %d; got %d", test.length, len(ipPool.ipv4Pool))
}
ip, err := ipPool.AllocateIPv4("alloc")
if err != nil {
t.Fatal(err)
}
ip2, err := ipPool.AllocateIPv4("alloc")
if err != nil {
t.Fatal(err)
}
if ip != ip2 {
t.Fatalf("same allocation key should allocate the same ip; got %s, %s", ip, ip2)
}
err = ipPool.ReleaseIpv4("bad-alloc")
if err == nil {
t.Fatal("expect error to release a bad allocation")
}
if len(ipPool.ipv4Pool) != int(test.length)-1 {
t.Fatalf("expect length to be %d; got %d", test.length-1, len(ipPool.ipv4Pool))
}
err = ipPool.ReleaseIpv4("alloc")
if err != nil {
t.Fatal(err)
}
if len(ipPool.ipv4Pool) != int(test.length) {
t.Fatalf("expect length to be %d; got %d", test.length, len(ipPool.ipv4Pool))
}
allocated := map[string]bool{}
for i := 0; i < int(test.length); i++ {
ip, err := ipPool.AllocateIPv4(fmt.Sprintf("alloc-%d", i))
if err != nil {
t.Fatal(err)
}
if _, ok := allocated[ip]; ok {
t.Fatalf("duplicated allocation of ip %q", ip)
}
allocated[ip] = true
}
_, err = ipPool.AllocateIPv4("last-allocation")
if err != errNoIpAvailable {
t.Fatalf("expect errNoIpAvailable; got %s", err)
}
}
}
func TestIpPoolv6(t *testing.T) {
tests := []struct {
ipRange string
length int32
}{
{"2001:4860:0:2001::2#250", 250},
}
for _, test := range tests {
var ipPool = MustNewIpPool(&types.IpPool{
Id: 1,
Name: "ip-pool",
AvailableIpv4Addresses: 0,
AvailableIpv6Addresses: test.length,
AllocatedIpv6Addresses: 0,
AllocatedIpv4Addresses: 0,
Ipv6Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "2001:4860:0:2001::ff",
Gateway: "2001:4860:0:2001::1",
SubnetAddress: "2001:4860:0:2001::0",
Range: test.ipRange,
},
})
if len(ipPool.ipv6Pool) != int(test.length) {
t.Fatalf("expect length to be %d; got %d", test.length, len(ipPool.ipv4Pool))
}
ip, err := ipPool.AllocateIpv6("alloc")
if err != nil {
t.Fatal(err)
}
ip2, err := ipPool.AllocateIpv6("alloc")
if err != nil {
t.Fatal(err)
}
if ip != ip2 {
t.Fatalf("same allocation key should allocate the same ip; got %s, %s", ip, ip2)
}
err = ipPool.ReleaseIpv6("bad-alloc")
if err == nil {
t.Fatal("expect error to release a bad allocation")
}
if len(ipPool.ipv6Pool) != int(test.length)-1 {
t.Fatalf("expect length to be %d; got %d", test.length-1, len(ipPool.ipv4Pool))
}
err = ipPool.ReleaseIpv6("alloc")
if err != nil {
t.Fatal(err)
}
if len(ipPool.ipv6Pool) != int(test.length) {
t.Fatalf("expect length to be %d; got %d", test.length, len(ipPool.ipv4Pool))
}
allocated := map[string]bool{}
for i := 0; i < int(test.length); i++ {
ip, err := ipPool.AllocateIpv6(fmt.Sprintf("alloc-%d", i))
if err != nil {
t.Fatal(err)
}
if _, ok := allocated[ip]; ok {
t.Fatalf("duplicated allocation of ip %q", ip)
}
allocated[ip] = true
}
_, err = ipPool.AllocateIpv6("last-allocation")
if err != errNoIpAvailable {
t.Fatalf("expect errNoIpAvailable; got %s", err)
}
}
}
func TestIpPoolManagerLifecycle(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
ref := types.ManagedObjectReference{Type: "IpPoolManager", Value: "IpPoolManager"}
var ipPool = &types.IpPool{
Name: "ip-pool",
AvailableIpv4Addresses: 250,
AvailableIpv6Addresses: 250,
AllocatedIpv4Addresses: 0,
AllocatedIpv6Addresses: 0,
Ipv4Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "10.10.10.255",
Gateway: "10.10.10.1",
SubnetAddress: "10.10.10.0",
Range: "10.10.10.2#250",
},
Ipv6Config: &types.IpPoolIpPoolConfigInfo{
Netmask: "2001:4860:0:2001::ff",
Gateway: "2001:4860:0:2001::1",
SubnetAddress: "2001:4860:0:2001::0",
Range: "2001:4860:0:2001::2#250",
},
}
createReq := &types.CreateIpPool{
This: ref,
Pool: *ipPool,
}
createResp, err := methods.CreateIpPool(ctx, c.Client, createReq)
if err != nil {
t.Fatal(err)
}
if createResp.Returnval != 2 {
t.Fatalf("expect pool id to be 2; got %d", createResp.Returnval)
}
ipPool.Id = 2
ipPool.Ipv4Config = &types.IpPoolIpPoolConfigInfo{
Netmask: "10.20.10.255",
Gateway: "10.20.10.1",
SubnetAddress: "10.20.10.0",
Range: "10.20.10.2#250",
}
updateReq := &types.UpdateIpPool{
This: ref,
Pool: *ipPool,
}
_, err = methods.UpdateIpPool(ctx, c.Client, updateReq)
if err != nil {
t.Fatal(err)
}
queryReq := &types.QueryIpPools{
This: ref,
}
queryResp, err := methods.QueryIpPools(ctx, c.Client, queryReq)
if err != nil {
t.Fatal(err)
}
if len(queryResp.Returnval) != 2 {
t.Fatalf("expect length of ip pools is 2; got %d", len(queryResp.Returnval))
}
if !reflect.DeepEqual(queryResp.Returnval[1].Ipv4Config, ipPool.Ipv4Config) {
t.Fatalf("expect query result equal to %+v; got %+v",
ipPool.Ipv4Config, queryResp.Returnval[1].Ipv4Config)
}
destroyReq := &types.DestroyIpPool{
This: ref,
Id: 2,
}
_, err = methods.DestroyIpPool(ctx, c.Client, destroyReq)
if err != nil {
t.Fatal(err)
}
queryResp, err = methods.QueryIpPools(ctx, c.Client, queryReq)
if err != nil {
t.Fatal(err)
}
if len(queryResp.Returnval) != 1 {
t.Fatalf("expect length of ip pools is 1 (1 deleted); got %d", len(queryResp.Returnval))
}
}
func TestIpPoolManagerAllocate(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
ref := types.ManagedObjectReference{Type: "IpPoolManager", Value: "IpPoolManager"}
// Allocate IPv4
allocateReq := &types.AllocateIpv4Address{
This: ref,
PoolId: 1,
AllocationId: "alloc",
}
allocateResp, err := methods.AllocateIpv4Address(ctx, c.Client, allocateReq)
if err != nil {
t.Fatal(err)
}
if net.ParseIP(allocateResp.Returnval) == nil {
t.Fatalf("%q is not IP address", allocateResp.Returnval)
}
releaseReq := &types.ReleaseIpAllocation{
This: ref,
PoolId: 1,
AllocationId: "alloc",
}
queryReq := &types.QueryIPAllocations{
This: ref,
PoolId: 1,
ExtensionKey: "alloc",
}
queryResp, err := methods.QueryIPAllocations(ctx, c.Client, queryReq)
if err != nil {
t.Fatal(err)
}
if len(queryResp.Returnval) != 1 {
t.Fatalf("expect length of result 1; got %s", queryResp.Returnval)
}
if queryResp.Returnval[0].IpAddress != allocateResp.Returnval {
t.Fatalf("expect same IP address; got %s, %s", queryResp.Returnval[0].IpAddress, allocateResp.Returnval)
}
_, err = methods.ReleaseIpAllocation(ctx, c.Client, releaseReq)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,156 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// EvalLicense is the default license
var EvalLicense = types.LicenseManagerLicenseInfo{
LicenseKey: "00000-00000-00000-00000-00000",
EditionKey: "eval",
Name: "Evaluation Mode",
Properties: []types.KeyAnyValue{
{
Key: "feature",
Value: types.KeyValue{
Key: "serialuri:2",
Value: "Remote virtual Serial Port Concentrator",
},
},
{
Key: "feature",
Value: types.KeyValue{
Key: "dvs",
Value: "vSphere Distributed Switch",
},
},
},
}
type LicenseManager struct {
mo.LicenseManager
}
func NewLicenseManager(ref types.ManagedObjectReference) object.Reference {
m := &LicenseManager{}
m.Self = ref
m.Licenses = []types.LicenseManagerLicenseInfo{EvalLicense}
if Map.IsVPX() {
am := Map.Put(&LicenseAssignmentManager{}).Reference()
m.LicenseAssignmentManager = &am
}
return m
}
func (m *LicenseManager) AddLicense(req *types.AddLicense) soap.HasFault {
body := &methods.AddLicenseBody{
Res: &types.AddLicenseResponse{},
}
for _, license := range m.Licenses {
if license.LicenseKey == req.LicenseKey {
body.Res.Returnval = licenseInfo(license.LicenseKey, license.Labels)
return body
}
}
m.Licenses = append(m.Licenses, types.LicenseManagerLicenseInfo{
LicenseKey: req.LicenseKey,
Labels: req.Labels,
})
body.Res.Returnval = licenseInfo(req.LicenseKey, req.Labels)
return body
}
func (m *LicenseManager) RemoveLicense(req *types.RemoveLicense) soap.HasFault {
body := &methods.RemoveLicenseBody{
Res: &types.RemoveLicenseResponse{},
}
for i, license := range m.Licenses {
if req.LicenseKey == license.LicenseKey {
m.Licenses = append(m.Licenses[:i], m.Licenses[i+1:]...)
return body
}
}
return body
}
type LicenseAssignmentManager struct {
mo.LicenseAssignmentManager
}
func (m *LicenseAssignmentManager) QueryAssignedLicenses(req *types.QueryAssignedLicenses) soap.HasFault {
body := &methods.QueryAssignedLicensesBody{
Res: &types.QueryAssignedLicensesResponse{},
}
// EntityId can be a HostSystem or the vCenter InstanceUuid
if req.EntityId != "" {
if req.EntityId != Map.content().About.InstanceUuid {
id := types.ManagedObjectReference{
Type: "HostSystem",
Value: req.EntityId,
}
if Map.Get(id) == nil {
return body
}
}
}
body.Res.Returnval = []types.LicenseAssignmentManagerLicenseAssignment{
{
EntityId: req.EntityId,
AssignedLicense: EvalLicense,
},
}
return body
}
func licenseInfo(key string, labels []types.KeyValue) types.LicenseManagerLicenseInfo {
info := EvalLicense
info.LicenseKey = key
info.Labels = labels
return info
}

View File

@@ -0,0 +1,206 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"reflect"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/license"
)
func TestLicenseManagerVPX(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
lm := license.NewManager(c.Client)
am, err := lm.AssignmentManager(ctx)
if err != nil {
t.Fatal(err)
}
la, err := am.QueryAssigned(ctx, "enoent")
if err != nil {
t.Fatal(err)
}
if len(la) != 0 {
t.Errorf("unexpected license")
}
finder := find.NewFinder(c.Client, false)
hosts, err := finder.HostSystemList(ctx, "/...")
if err != nil {
t.Fatal(err)
}
host := hosts[0].Reference().Value
vcid := c.Client.ServiceContent.About.InstanceUuid
for _, name := range []string{"", host, vcid} {
la, err = am.QueryAssigned(ctx, name)
if err != nil {
t.Fatal(err)
}
if len(la) != 1 {
t.Fatal("no licenses")
}
if !reflect.DeepEqual(la[0].AssignedLicense, EvalLicense) {
t.Fatal("invalid license")
}
}
}
func TestLicenseManagerESX(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
lm := license.NewManager(c.Client)
_, err = lm.AssignmentManager(ctx)
if err == nil {
t.Fatal("expected error")
}
la, err := lm.List(ctx)
if err != nil {
t.Fatal(err)
}
if len(la) != 1 {
t.Fatal("no licenses")
}
if !reflect.DeepEqual(la[0], EvalLicense) {
t.Fatal("invalid license")
}
}
func TestAddRemoveLicense(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
lm := license.NewManager(c.Client)
key := "00000-00000-00000-00000-11111"
labels := map[string]string{"key": "value"}
info, err := lm.Add(ctx, key, labels)
if err != nil {
t.Fatal(err)
}
if info.LicenseKey != key {
t.Fatalf("expect info.LicenseKey equal to %q; got %q", key, info.LicenseKey)
}
if len(info.Labels) != len(labels) {
t.Fatalf("expect len(info.Labels) eqaul to %d; got %d",
len(labels), len(info.Labels))
}
if info.Labels[0].Key != "key" || info.Labels[0].Value != "value" {
t.Fatalf("expect label to be {key:value}; got {%s:%s}",
info.Labels[0].Key, info.Labels[0].Value)
}
la, err := lm.List(ctx)
if err != nil {
t.Fatal(err)
}
if len(la) != 2 {
t.Fatal("no licenses")
}
if la[1].LicenseKey != key {
t.Fatalf("expect info.LicenseKey equal to %q; got %q",
key, la[1].LicenseKey)
}
if len(la[1].Labels) != len(labels) {
t.Fatalf("expect len(info.Labels) eqaul to %d; got %d",
len(labels), len(la[1].Labels))
}
if la[1].Labels[0].Key != "key" || la[1].Labels[0].Value != "value" {
t.Fatalf("expect label to be {key:value}; got {%s:%s}",
la[1].Labels[0].Key, la[1].Labels[0].Value)
}
err = lm.Remove(ctx, key)
if err != nil {
t.Fatal(err)
}
la, err = lm.List(ctx)
if err != nil {
t.Fatal(err)
}
if len(la) != 1 {
t.Fatal("no licenses")
}
}

489
vendor/github.com/vmware/govmomi/simulator/model.go generated vendored Normal file
View File

@@ -0,0 +1,489 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// Model is used to populate a Model with an initial set of managed entities.
// This is a simple helper for tests running against a simulator, to populate an inventory
// with commonly used models.
type Model struct {
Service *Service `json:"-"`
ServiceContent types.ServiceContent `json:"-"`
RootFolder mo.Folder `json:"-"`
// Autostart will power on Model created VMs when true
Autostart bool `json:"-"`
// Datacenter specifies the number of Datacenter entities to create
Datacenter int
// Portgroup specifies the number of DistributedVirtualPortgroup entities to create per Datacenter
Portgroup int
// Host specifies the number of standalone HostSystems entities to create per Datacenter
Host int `json:",omitempty"`
// Cluster specifies the number of ClusterComputeResource entities to create per Datacenter
Cluster int
// ClusterHost specifies the number of HostSystems entities to create within a Cluster
ClusterHost int `json:",omitempty"`
// Pool specifies the number of ResourcePool entities to create per Cluster
Pool int
// Datastore specifies the number of Datastore entities to create
// Each Datastore will have temporary local file storage and will be mounted
// on every HostSystem created by the ModelConfig
Datastore int
// Machine specifies the number of VirtualMachine entities to create per ResourcePool
Machine int
// Folder specifies the number of Datacenter to place within a Folder.
// This includes a folder for the Datacenter itself and its host, vm, network and datastore folders.
// All resources for the Datacenter are placed within these folders, rather than the top-level folders.
Folder int
// App specifies the number of VirtualApp to create per Cluster
App int
// Pod specifies the number of StoragePod to create per Cluster
Pod int
// total number of inventory objects, set by Count()
total int
dirs []string
}
// ESX is the default Model for a standalone ESX instance
func ESX() *Model {
return &Model{
ServiceContent: esx.ServiceContent,
RootFolder: esx.RootFolder,
Autostart: true,
Datastore: 1,
Machine: 2,
}
}
// VPX is the default Model for a vCenter instance
func VPX() *Model {
return &Model{
ServiceContent: vpx.ServiceContent,
RootFolder: vpx.RootFolder,
Autostart: true,
Datacenter: 1,
Portgroup: 1,
Host: 1,
Cluster: 1,
ClusterHost: 3,
Datastore: 1,
Machine: 2,
}
}
// Count returns a Model with total number of each existing type
func (m *Model) Count() Model {
count := Model{}
for ref, obj := range Map.objects {
if _, ok := obj.(mo.Entity); !ok {
continue
}
count.total++
switch ref.Type {
case "Datacenter":
count.Datacenter++
case "DistributedVirtualPortgroup":
count.Portgroup++
case "ClusterComputeResource":
count.Cluster++
case "Datastore":
count.Datastore++
case "HostSystem":
count.Host++
case "VirtualMachine":
count.Machine++
case "ResourcePool":
count.Pool++
case "VirtualApp":
count.App++
case "Folder":
count.Folder++
case "StoragePod":
count.Pod++
}
}
return count
}
func (*Model) fmtName(prefix string, num int) string {
return fmt.Sprintf("%s%d", prefix, num)
}
// Create populates the Model with the given ModelConfig
func (m *Model) Create() error {
m.Service = New(NewServiceInstance(m.ServiceContent, m.RootFolder))
ctx := context.Background()
client := m.Service.client
root := object.NewRootFolder(client)
// After all hosts are created, this var is used to mount the host datastores.
var hosts []*object.HostSystem
hostMap := make(map[string][]*object.HostSystem)
// We need to defer VM creation until after the datastores are created.
var vms []func() error
// 1 DVS per DC, added to all hosts
var dvs *object.DistributedVirtualSwitch
// 1 NIC per VM, backed by a DVPG if Model.Portgroup > 0
vmnet := esx.EthernetCard.Backing
// addHost adds a cluster host or a stanalone host.
addHost := func(name string, f func(types.HostConnectSpec) (*object.Task, error)) (*object.HostSystem, error) {
spec := types.HostConnectSpec{
HostName: name,
}
task, err := f(spec)
if err != nil {
return nil, err
}
info, err := task.WaitForResult(context.Background(), nil)
if err != nil {
return nil, err
}
host := object.NewHostSystem(client, info.Result.(types.ManagedObjectReference))
hosts = append(hosts, host)
if dvs != nil {
config := &types.DVSConfigSpec{
Host: []types.DistributedVirtualSwitchHostMemberConfigSpec{{
Operation: string(types.ConfigSpecOperationAdd),
Host: host.Reference(),
}},
}
_, _ = dvs.Reconfigure(ctx, config)
}
return host, nil
}
// addMachine returns a func to create a VM.
addMachine := func(prefix string, host *object.HostSystem, pool *object.ResourcePool, folders *object.DatacenterFolders) {
nic := esx.EthernetCard
nic.Backing = vmnet
ds := types.ManagedObjectReference{}
f := func() error {
for i := 0; i < m.Machine; i++ {
name := m.fmtName(prefix+"_VM", i)
config := types.VirtualMachineConfigSpec{
Name: name,
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
Files: &types.VirtualMachineFileInfo{
VmPathName: "[LocalDS_0]",
},
}
if pool == nil {
pool, _ = host.ResourcePool(ctx)
}
var devices object.VirtualDeviceList
scsi, _ := devices.CreateSCSIController("pvscsi")
ide, _ := devices.CreateIDEController()
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
disk.CapacityInKB = 1024
devices = append(devices, scsi, cdrom, disk, &nic)
config.DeviceChange, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
task, err := folders.VmFolder.CreateVM(ctx, config, pool, host)
if err != nil {
return err
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
return err
}
vm := object.NewVirtualMachine(client, info.Result.(types.ManagedObjectReference))
if m.Autostart {
_, _ = vm.PowerOn(ctx)
}
}
return nil
}
vms = append(vms, f)
}
nfolder := 0
for ndc := 0; ndc < m.Datacenter; ndc++ {
dcName := m.fmtName("DC", ndc)
folder := root
fName := m.fmtName("F", nfolder)
// If Datacenter > Folder, don't create folders for the first N DCs.
if nfolder < m.Folder && ndc >= (m.Datacenter-m.Folder) {
f, err := folder.CreateFolder(ctx, fName)
if err != nil {
return err
}
folder = f
}
dc, err := folder.CreateDatacenter(ctx, dcName)
if err != nil {
return err
}
folders, err := dc.Folders(ctx)
if err != nil {
return err
}
if m.Pod > 0 {
for pod := 0; pod < m.Pod; pod++ {
_, _ = folders.DatastoreFolder.CreateStoragePod(ctx, m.fmtName(dcName+"_POD", pod))
}
}
if folder != root {
// Create sub-folders and use them to create any resources that follow
subs := []**object.Folder{&folders.DatastoreFolder, &folders.HostFolder, &folders.NetworkFolder, &folders.VmFolder}
for _, sub := range subs {
f, err := (*sub).CreateFolder(ctx, fName)
if err != nil {
return err
}
*sub = f
}
nfolder++
}
if m.Portgroup > 0 {
var spec types.DVSCreateSpec
spec.ConfigSpec = &types.VMwareDVSConfigSpec{}
spec.ConfigSpec.GetDVSConfigSpec().Name = m.fmtName("DVS", 0)
task, err := folders.NetworkFolder.CreateDVS(ctx, spec)
if err != nil {
return err
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
return err
}
dvs = object.NewDistributedVirtualSwitch(client, info.Result.(types.ManagedObjectReference))
for npg := 0; npg < m.Portgroup; npg++ {
name := m.fmtName(dcName+"_DVPG", npg)
task, err = dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{{Name: name}})
if err != nil {
return err
}
err = task.Wait(ctx)
if err != nil {
return err
}
// Use the 1st DVPG for the VMs eth0 backing
if npg == 0 {
// AddPortgroup_Task does not return the moid, so we look it up by name
net := Map.Get(folders.NetworkFolder.Reference()).(*Folder)
pg := Map.FindByName(name, net.ChildEntity)
vmnet, _ = object.NewDistributedVirtualPortgroup(client, pg.Reference()).EthernetCardBackingInfo(ctx)
}
}
}
for nhost := 0; nhost < m.Host; nhost++ {
name := m.fmtName(dcName+"_H", nhost)
host, err := addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
return folders.HostFolder.AddStandaloneHost(ctx, spec, true, nil, nil)
})
if err != nil {
return err
}
addMachine(name, host, nil, folders)
}
for ncluster := 0; ncluster < m.Cluster; ncluster++ {
clusterName := m.fmtName(dcName+"_C", ncluster)
cluster, err := folders.HostFolder.CreateCluster(ctx, clusterName, types.ClusterConfigSpecEx{})
if err != nil {
return err
}
for nhost := 0; nhost < m.ClusterHost; nhost++ {
name := m.fmtName(clusterName+"_H", nhost)
_, err = addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
return cluster.AddHost(ctx, spec, true, nil, nil)
})
if err != nil {
return err
}
}
pool, err := cluster.ResourcePool(ctx)
if err != nil {
return err
}
prefix := clusterName + "_RP"
addMachine(prefix+"0", nil, pool, folders)
for npool := 1; npool <= m.Pool; npool++ {
spec := types.DefaultResourceConfigSpec()
_, err = pool.Create(ctx, m.fmtName(prefix, npool), spec)
if err != nil {
return err
}
}
prefix = clusterName + "_APP"
for napp := 0; napp < m.App; napp++ {
rspec := types.DefaultResourceConfigSpec()
vspec := NewVAppConfigSpec()
name := m.fmtName(prefix, napp)
vapp, err := pool.CreateVApp(ctx, name, rspec, vspec, nil)
if err != nil {
return err
}
addMachine(name, nil, vapp.ResourcePool, folders)
}
}
hostMap[dcName] = hosts
hosts = nil
}
if m.ServiceContent.RootFolder == esx.RootFolder.Reference() {
// ESX model
host := object.NewHostSystem(client, esx.HostSystem.Reference())
dc := object.NewDatacenter(client, esx.Datacenter.Reference())
folders, err := dc.Folders(ctx)
if err != nil {
return err
}
hostMap[dc.Reference().Value] = append(hosts, host)
addMachine(host.Reference().Value, host, nil, folders)
}
for dc, dchosts := range hostMap {
for i := 0; i < m.Datastore; i++ {
err := m.createLocalDatastore(dc, m.fmtName("LocalDS_", i), dchosts)
if err != nil {
return err
}
}
}
for _, createVM := range vms {
err := createVM()
if err != nil {
return err
}
}
return nil
}
func (m *Model) createLocalDatastore(dc string, name string, hosts []*object.HostSystem) error {
ctx := context.Background()
dir, err := ioutil.TempDir("", fmt.Sprintf("govcsim-%s-%s-", dc, name))
if err != nil {
return err
}
m.dirs = append(m.dirs, dir)
for _, host := range hosts {
dss, err := host.ConfigManager().DatastoreSystem(ctx)
if err != nil {
return err
}
_, err = dss.CreateLocalDatastore(ctx, name, dir)
if err != nil {
return err
}
}
return nil
}
// Remove cleans up items created by the Model, such as local datastore directories
func (m *Model) Remove() {
for _, dir := range m.dirs {
_ = os.RemoveAll(dir)
}
}

View File

@@ -0,0 +1,146 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/simulator/vpx"
)
func compareModel(t *testing.T, m *Model) {
count := m.Count()
hosts := (m.Host + (m.ClusterHost * m.Cluster)) * m.Datacenter
vms := ((m.Host + m.Cluster) * m.Datacenter) * m.Machine
// child pools + root pools
pools := (m.Pool * m.Cluster * m.Datacenter) + (m.Host+m.Cluster)*m.Datacenter
// root folder + Datacenter folders {host,vm,datastore,network} + top-level folders
folders := 1 + (4 * m.Datacenter) + (5 * m.Folder)
pgs := m.Portgroup
if pgs > 0 {
pgs++ // uplinks
}
tests := []struct {
expect int
actual int
kind string
}{
{m.Datacenter, count.Datacenter, "Datacenter"},
{m.Cluster * m.Datacenter, count.Cluster, "Cluster"},
{pgs * m.Datacenter, count.Portgroup, "Portgroup"},
{m.Datastore * m.Datacenter, count.Datastore, "Datastore"},
{hosts, count.Host, "Host"},
{vms, count.Machine, "VirtualMachine"},
{pools, count.Pool, "ResourcePool"},
{folders, count.Folder, "Folder"},
}
for _, test := range tests {
if test.expect != test.actual {
t.Errorf("expected %d %s, actual: %d", test.expect, test.kind, test.actual)
}
}
}
func TestModelESX(t *testing.T) {
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
// Set these for the compareModel math, and for m.Create to fail below
m.Datacenter = 1
m.Host = 1
compareModel(t, m)
err = m.Create()
if err == nil {
t.Error("expected error")
}
}
func TestModelVPX(t *testing.T) {
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
compareModel(t, m)
}
func TestModelNoSwitchVPX(t *testing.T) {
m := VPX()
m.Portgroup = 0 // disabled DVS creation
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
compareModel(t, m)
}
func TestModelCustomVPX(t *testing.T) {
m := &Model{
ServiceContent: vpx.ServiceContent,
RootFolder: vpx.RootFolder,
Datacenter: 2,
Cluster: 2,
Host: 2,
ClusterHost: 3,
Datastore: 1,
Machine: 3,
Pool: 2,
Portgroup: 2,
}
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
compareModel(t, m)
}
func TestModelWithFolders(t *testing.T) {
m := VPX()
m.Datacenter = 3
m.Folder = 2
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
compareModel(t, m)
}

View File

@@ -0,0 +1,98 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type OptionManager struct {
mo.OptionManager
}
func NewOptionManager(ref *types.ManagedObjectReference, setting []types.BaseOptionValue) object.Reference {
s := &OptionManager{}
if ref != nil {
s.Self = *ref
}
s.Setting = setting
return s
}
func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault {
body := &methods.QueryOptionsBody{}
res := &types.QueryOptionsResponse{}
for _, opt := range m.Setting {
if strings.HasPrefix(opt.GetOptionValue().Key, req.Name) {
res.Returnval = append(res.Returnval, opt)
}
}
if len(res.Returnval) == 0 {
body.Fault_ = Fault("", &types.InvalidName{Name: req.Name})
} else {
body.Res = res
}
return body
}
func (m *OptionManager) find(key string) *types.OptionValue {
for _, opt := range m.Setting {
setting := opt.GetOptionValue()
if setting.Key == key {
return setting
}
}
return nil
}
func (m *OptionManager) UpdateOptions(req *types.UpdateOptions) soap.HasFault {
body := new(methods.UpdateOptionsBody)
for _, change := range req.ChangedValue {
setting := change.GetOptionValue()
// We don't currently include the entire list of default settings for ESX and vCenter,
// this prefix is currently used to test the failure path.
// Real vCenter seems to only allow new options if Key has a "config." prefix.
// TODO: consider behaving the same, which would require including 2 long lists of options in vpx.Setting and esx.Setting
if strings.HasPrefix(setting.Key, "ENOENT.") {
body.Fault_ = Fault("", &types.InvalidName{Name: setting.Key})
return body
}
opt := m.find(setting.Key)
if opt != nil {
// This is an existing option.
opt.Value = setting.Value
continue
}
m.Setting = append(m.Setting, change)
}
body.Res = new(types.UpdateOptionsResponse)
return body
}

View File

@@ -0,0 +1,146 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/types"
)
func TestOptionManagerESX(t *testing.T) {
ctx := context.Background()
model := ESX()
model.Datastore = 0
model.Machine = 0
err := model.Create()
if err != nil {
t.Fatal(err)
}
c := model.Service.client
m := object.NewOptionManager(c, *c.ServiceContent.Setting)
_, err = m.Query(ctx, "config.vpxd.")
if err == nil {
t.Error("expected error")
}
host := object.NewHostSystem(c, esx.HostSystem.Reference())
m, err = host.ConfigManager().OptionManager(ctx)
if err != nil {
t.Fatal(err)
}
res, err := m.Query(ctx, "Config.HostAgent.")
if err != nil {
t.Error(err)
}
if len(res) == 0 {
t.Error("no results")
}
err = m.Update(ctx, []types.BaseOptionValue{&types.OptionValue{
Key: "Config.HostAgent.log.level",
Value: "verbose",
}})
if err != nil {
t.Error(err)
}
}
func TestOptionManagerVPX(t *testing.T) {
ctx := context.Background()
model := VPX()
model.Datastore = 0
model.Machine = 0
err := model.Create()
if err != nil {
t.Fatal(err)
}
c := model.Service.client
m := object.NewOptionManager(c, *c.ServiceContent.Setting)
_, err = m.Query(ctx, "enoent")
if err == nil {
t.Error("expected error")
}
res, err := m.Query(ctx, "event.")
if err != nil {
t.Error(err)
}
if len(res) == 0 {
t.Error("no results")
}
val := &types.OptionValue{
Key: "event.maxAge",
}
// Get the existing maxAge value
for _, r := range res {
opt := r.GetOptionValue()
if opt.Key == val.Key {
val.Value = opt.Value
}
}
// Increase maxAge * 2
val.Value = val.Value.(int32) * 2
err = m.Update(ctx, []types.BaseOptionValue{val})
if err != nil {
t.Error(err)
}
// Verify maxAge was updated
res, err = m.Query(ctx, val.Key)
if err != nil {
t.Error(err)
}
if res[0].GetOptionValue().Value != val.Value {
t.Errorf("%s was not updated", val.Key)
}
// Expected to throw InvalidName fault
err = m.Update(ctx, []types.BaseOptionValue{&types.OptionValue{
Key: "ENOENT.anything",
}})
if err == nil {
t.Error("expected error")
}
// Add a new option
err = m.Update(ctx, []types.BaseOptionValue{&types.OptionValue{
Key: "config.anything",
Value: "OK",
}})
if err != nil {
t.Error(err)
}
}

40
vendor/github.com/vmware/govmomi/simulator/os_unix.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
//+build !windows
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import "syscall"
func (ds *Datastore) stat() error {
info := ds.Info.GetDatastoreInfo()
var stat syscall.Statfs_t
err := syscall.Statfs(info.Url, &stat)
if err != nil {
return err
}
bsize := uint64(stat.Bsize) / 512
info.FreeSpace = int64(stat.Bfree*bsize) >> 1
ds.Summary.FreeSpace = info.FreeSpace
ds.Summary.Capacity = int64(stat.Blocks*bsize) >> 1
return nil
}

View File

@@ -0,0 +1,26 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import "os"
func (ds *Datastore) stat() error {
info := ds.Info.GetDatastoreInfo()
_, err := os.Stat(info.Url)
return err
}

View File

@@ -0,0 +1,35 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type PerformanceManager struct {
mo.PerformanceManager
}
func NewPerformanceManager(ref types.ManagedObjectReference) object.Reference {
m := &PerformanceManager{}
m.Self = ref
m.PerfCounter = esx.PerfCounter
return m
}

View File

@@ -0,0 +1,71 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type DistributedVirtualPortgroup struct {
mo.DistributedVirtualPortgroup
}
func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(req *types.ReconfigureDVPortgroup_Task) soap.HasFault {
task := CreateTask(s, "reconfigureDvPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
s.Config.DefaultPortConfig = req.Spec.DefaultPortConfig
s.Config.NumPorts = req.Spec.NumPorts
s.Config.AutoExpand = req.Spec.AutoExpand
s.Config.Type = req.Spec.Type
s.Config.Description = req.Spec.Description
s.Config.DynamicData = req.Spec.DynamicData
s.Config.Name = req.Spec.Name
s.Config.Policy = req.Spec.Policy
s.Config.PortNameFormat = req.Spec.PortNameFormat
s.Config.VmVnicNetworkResourcePoolKey = req.Spec.VmVnicNetworkResourcePoolKey
return nil, nil
})
return &methods.ReconfigureDVPortgroup_TaskBody{
Res: &types.ReconfigureDVPortgroup_TaskResponse{
Returnval: task.Run(),
},
}
}
func (s *DistributedVirtualPortgroup) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
Map.RemoveReference(vswitch, &vswitch.Portgroup, s.Reference())
Map.removeString(vswitch, &vswitch.Summary.PortgroupName, s.Name)
f := Map.getEntityParent(vswitch, "Folder").(*Folder)
f.removeChild(s.Reference())
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,93 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
func TestReconfigurePortgroup(t *testing.T) {
ctx := context.Background()
m := VPX()
err := m.Create()
if err != nil {
t.Fatal(err)
}
defer m.Remove()
c := m.Service.client
dvs := object.NewDistributedVirtualSwitch(c,
Map.Any("DistributedVirtualSwitch").Reference())
spec := []types.DVPortgroupConfigSpec{
types.DVPortgroupConfigSpec{
Name: "pg1",
NumPorts: 10,
},
}
task, err := dvs.AddPortgroup(ctx, spec)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
pg := object.NewDistributedVirtualPortgroup(c,
Map.Any("DistributedVirtualPortgroup").Reference())
pgspec := types.DVPortgroupConfigSpec{
NumPorts: 5,
Name: "pg1",
}
task, err = pg.Reconfigure(ctx, pgspec)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
pge := Map.Get(pg.Reference()).(*DistributedVirtualPortgroup)
if pge.Config.Name != "pg1" || pge.Config.NumPorts != 5 {
t.Fatalf("expect pg.Name==pg1 && pg.Config.NumPort==5; got %s,%d",
pge.Config.Name, pge.Config.NumPorts)
}
task, err = pg.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,580 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"errors"
"log"
"path"
"reflect"
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type PropertyCollector struct {
mo.PropertyCollector
}
func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
s := &PropertyCollector{}
s.Self = ref
return s
}
var errMissingField = errors.New("missing field")
var errEmptyField = errors.New("empty field")
func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, bool) {
var obj mo.Reference
if ctx.Session == nil {
// Even without permissions to access an object or specific fields, RetrieveProperties
// returns an ObjectContent response as long as the object exists. See retrieveResult.add()
obj = Map.Get(ref)
} else {
obj = ctx.Session.Get(ref)
}
if obj == nil {
return reflect.Value{}, false
}
if ctx.Session == nil && ref.Type == "SessionManager" {
// RetrieveProperties on SessionManager without a session always returns empty,
// rather than MissingSet + Fault.NotAuthenticated for each field.
obj = &mo.SessionManager{Self: ref}
}
// For objects that use internal types that differ from that of the vim25/mo field types.
// See EventHistoryCollector for example.
type get interface {
Get() mo.Reference
}
if o, ok := obj.(get); ok {
obj = o.Get()
}
rval := reflect.ValueOf(obj).Elem()
rtype := rval.Type()
// PropertyCollector is for Managed Object types only (package mo).
// If the registry object is not in the mo package, assume it is a wrapper
// type where the first field is an embedded mo type.
// We need to dig out the mo type for PropSet.All to work properly and
// for the case where the type has a field of the same name, for example:
// mo.ResourcePool.ResourcePool
for {
if path.Base(rtype.PkgPath()) != "mo" {
if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 {
log.Printf("%#v does not have an embedded mo type", ref)
return reflect.Value{}, false
}
rval = rval.Field(0)
rtype = rval.Type()
} else {
break
}
}
return rval, true
}
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
if rval.Kind() == reflect.Ptr {
rval = rval.Elem()
}
pval := rval.Interface()
if rval.Kind() == reflect.Slice {
// Convert slice to types.ArrayOf*
switch v := pval.(type) {
case []string:
pval = &types.ArrayOfString{
String: v,
}
case []int32:
pval = &types.ArrayOfInt{
Int: v,
}
default:
kind := f.Type.Elem().Name()
// Remove govmomi interface prefix name
if strings.HasPrefix(kind, "Base") {
kind = kind[4:]
}
akind, _ := typeFunc("ArrayOf" + kind)
a := reflect.New(akind)
a.Elem().FieldByName(kind).Set(rval)
pval = a.Interface()
}
}
return pval
}
func fieldValue(rval reflect.Value, p string) (interface{}, error) {
var value interface{}
fields := strings.Split(p, ".")
for i, name := range fields {
kind := rval.Type().Kind()
if kind == reflect.Interface {
if rval.IsNil() {
continue
}
rval = rval.Elem()
kind = rval.Type().Kind()
}
if kind == reflect.Ptr {
if rval.IsNil() {
continue
}
rval = rval.Elem()
}
x := ucFirst(name)
val := rval.FieldByName(x)
if !val.IsValid() {
return nil, errMissingField
}
if isEmpty(val) {
return nil, errEmptyField
}
if i == len(fields)-1 {
ftype, _ := rval.Type().FieldByName(x)
value = fieldValueInterface(ftype, val)
break
}
rval = val
}
return value, nil
}
func fieldRefs(f interface{}) []types.ManagedObjectReference {
switch fv := f.(type) {
case types.ManagedObjectReference:
return []types.ManagedObjectReference{fv}
case *types.ArrayOfManagedObjectReference:
return fv.ManagedObjectReference
case nil:
// empty field
}
return nil
}
func isEmpty(rval reflect.Value) bool {
switch rval.Kind() {
case reflect.Ptr:
return rval.IsNil()
case reflect.String:
return rval.Len() == 0
}
return false
}
func isTrue(v *bool) bool {
return v != nil && *v
}
func isFalse(v *bool) bool {
return v == nil || *v == false
}
func lcFirst(s string) string {
return strings.ToLower(s[:1]) + s[1:]
}
func ucFirst(s string) string {
return strings.ToUpper(s[:1]) + s[1:]
}
type retrieveResult struct {
*types.RetrieveResult
req *types.RetrievePropertiesEx
collected map[types.ManagedObjectReference]bool
specs map[string]*types.TraversalSpec
}
func (rr *retrieveResult) add(ctx *Context, name string, val types.AnyType, content *types.ObjectContent) {
if ctx.Session != nil {
content.PropSet = append(content.PropSet, types.DynamicProperty{
Name: name,
Val: val,
})
return
}
content.MissingSet = append(content.MissingSet, types.MissingProperty{
Path: name,
Fault: types.LocalizedMethodFault{Fault: &types.NotAuthenticated{
NoPermission: types.NoPermission{
Object: content.Obj,
PrivilegeId: "System.Read",
}},
},
})
}
func (rr *retrieveResult) collectAll(ctx *Context, rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) {
for i := 0; i < rval.NumField(); i++ {
val := rval.Field(i)
f := rtype.Field(i)
if isEmpty(val) || f.Name == "Self" {
continue
}
if f.Anonymous {
// recurse into embedded field
rr.collectAll(ctx, val, f.Type, content)
continue
}
rr.add(ctx, lcFirst(f.Name), fieldValueInterface(f, val), content)
}
}
func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields []string, content *types.ObjectContent) {
seen := make(map[string]bool)
for i := range content.PropSet {
seen[content.PropSet[i].Name] = true // mark any already collected via embedded field
}
for _, name := range fields {
if seen[name] {
// rvc 'ls' includes the "name" property twice, then fails with no error message or stack trace
// in RbVmomi::VIM::ObjectContent.to_hash_uncached when it sees the 2nd "name" property.
continue
}
seen[name] = true
val, err := fieldValue(rval, name)
if err == nil {
rr.add(ctx, name, val, content)
continue
}
switch err {
case errEmptyField:
// ok
case errMissingField:
content.MissingSet = append(content.MissingSet, types.MissingProperty{
Path: name,
Fault: types.LocalizedMethodFault{Fault: &types.InvalidProperty{
Name: name,
}},
})
}
}
}
func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference) {
if rr.collected[ref] {
return
}
content := types.ObjectContent{
Obj: ref,
}
rval, ok := getObject(ctx, ref)
if !ok {
// Possible if a test uses Map.Remove instead of Destroy_Task
log.Printf("object %s no longer exists", ref)
return
}
rtype := rval.Type()
for _, spec := range rr.req.SpecSet {
for _, p := range spec.PropSet {
if p.Type != ref.Type {
// e.g. ManagedEntity, ComputeResource
field, ok := rtype.FieldByName(p.Type)
if !(ok && field.Anonymous) {
continue
}
}
if isTrue(p.All) {
rr.collectAll(ctx, rval, rtype, &content)
continue
}
rr.collectFields(ctx, rval, p.PathSet, &content)
}
}
if len(content.PropSet) != 0 || len(content.MissingSet) != 0 {
rr.Objects = append(rr.Objects, content)
}
rr.collected[ref] = true
}
func (rr *retrieveResult) selectSet(ctx *Context, obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault {
for _, ss := range s {
ts, ok := ss.(*types.TraversalSpec)
if ok {
if ts.Name != "" {
rr.specs[ts.Name] = ts
}
}
}
for _, ss := range s {
ts, ok := ss.(*types.TraversalSpec)
if !ok {
ts = rr.specs[ss.GetSelectionSpec().Name]
if ts == nil {
return &types.InvalidArgument{InvalidProperty: "undefined TraversalSpec name"}
}
}
f, _ := fieldValue(obj, ts.Path)
for _, ref := range fieldRefs(f) {
if isFalse(ts.Skip) {
*refs = append(*refs, ref)
}
rval, ok := getObject(ctx, ref)
if ok {
if err := rr.selectSet(ctx, rval, ts.SelectSet, refs); err != nil {
return err
}
}
}
}
return nil
}
func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) {
var refs []types.ManagedObjectReference
rr := &retrieveResult{
RetrieveResult: &types.RetrieveResult{},
req: r,
collected: make(map[types.ManagedObjectReference]bool),
specs: make(map[string]*types.TraversalSpec),
}
// Select object references
for _, spec := range r.SpecSet {
for _, o := range spec.ObjectSet {
rval, ok := getObject(ctx, o.Obj)
if !ok {
if isFalse(spec.ReportMissingObjectsInResults) {
return nil, &types.ManagedObjectNotFound{Obj: o.Obj}
}
continue
}
if o.SelectSet == nil || isFalse(o.Skip) {
refs = append(refs, o.Obj)
}
if err := rr.selectSet(ctx, rval, o.SelectSet, &refs); err != nil {
return nil, err
}
}
}
for _, ref := range refs {
rr.collect(ctx, ref)
}
return rr.RetrieveResult, nil
}
func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault {
body := &methods.CreateFilterBody{}
filter := &PropertyFilter{pc: pc}
filter.PartialUpdates = c.PartialUpdates
filter.Spec = c.Spec
pc.Filter = append(pc.Filter, ctx.Session.Put(filter).Reference())
body.Res = &types.CreateFilterResponse{
Returnval: filter.Self,
}
return body
}
func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.CreatePropertyCollector) soap.HasFault {
body := &methods.CreatePropertyCollectorBody{}
cpc := &PropertyCollector{}
body.Res = &types.CreatePropertyCollectorResponse{
Returnval: ctx.Session.Put(cpc).Reference(),
}
return body
}
func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault {
body := &methods.DestroyPropertyCollectorBody{}
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
filter.DestroyPropertyFilter(&types.DestroyPropertyFilter{This: ref})
}
ctx.Session.Remove(c.This)
body.Res = &types.DestroyPropertyCollectorResponse{}
return body
}
func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.RetrievePropertiesEx) soap.HasFault {
body := &methods.RetrievePropertiesExBody{}
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
} else {
body.Res = &types.RetrievePropertiesExResponse{
Returnval: res,
}
}
return body
}
// RetrieveProperties is deprecated, but govmomi is still using it at the moment.
func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveProperties) soap.HasFault {
body := &methods.RetrievePropertiesBody{}
res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{
This: r.This,
SpecSet: r.SpecSet,
})
if res.Fault() != nil {
body.Fault_ = res.Fault()
} else {
body.Res = &types.RetrievePropertiesResponse{
Returnval: res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects,
}
}
return body
}
func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault {
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)}
}
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
body := &methods.WaitForUpdatesExBody{}
// At the moment we need to support Task completion. Handlers can simply set the Task
// state before returning and the non-incremental update is enough for the client.
// We can wait for incremental updates to simulate timeouts, etc.
if r.Version != "" {
body.Fault_ = Fault("incremental updates not supported yet", &types.NotSupported{})
return body
}
update := &types.UpdateSet{
Version: "-",
}
for _, ref := range pc.Filter {
filter := ctx.Session.Get(ref).(*PropertyFilter)
r := &types.RetrievePropertiesEx{}
r.SpecSet = append(r.SpecSet, filter.Spec)
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
fu := types.PropertyFilterUpdate{
Filter: ref,
}
for _, o := range res.Objects {
ou := types.ObjectUpdate{
Obj: o.Obj,
Kind: types.ObjectUpdateKindEnter,
}
for _, p := range o.PropSet {
ou.ChangeSet = append(ou.ChangeSet, types.PropertyChange{
Op: types.PropertyChangeOpAssign,
Name: p.Name,
Val: p.Val,
})
}
fu.ObjectSet = append(fu.ObjectSet, ou)
}
update.FilterSet = append(update.FilterSet, fu)
}
body.Res = &types.WaitForUpdatesExResponse{
Returnval: update,
}
return body
}
// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment.
func (pc *PropertyCollector) WaitForUpdates(ctx *Context, r *types.WaitForUpdates) soap.HasFault {
body := &methods.WaitForUpdatesBody{}
res := pc.WaitForUpdatesEx(ctx, &types.WaitForUpdatesEx{
This: r.This,
Version: r.Version,
})
if res.Fault() != nil {
body.Fault_ = res.Fault()
} else {
body.Res = &types.WaitForUpdatesResponse{
Returnval: *res.(*methods.WaitForUpdatesExBody).Res.Returnval,
}
}
return body
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type PropertyFilter struct {
mo.PropertyFilter
pc *PropertyCollector
}
func (f *PropertyFilter) DestroyPropertyFilter(c *types.DestroyPropertyFilter) soap.HasFault {
body := &methods.DestroyPropertyFilterBody{}
RemoveReference(&f.pc.Filter, c.This)
Map.Remove(c.This)
body.Res = &types.DestroyPropertyFilterResponse{}
return body
}

130
vendor/github.com/vmware/govmomi/simulator/race_test.go generated vendored Normal file
View File

@@ -0,0 +1,130 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25/types"
)
func TestRace(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
spec := types.VirtualMachineConfigSpec{
Name: fmt.Sprintf("race-test-%d", i),
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
Files: &types.VirtualMachineFileInfo{
VmPathName: "[LocalDS_0]",
},
}
wg.Add(1)
go func() {
defer wg.Done()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
pc := property.DefaultCollector(c.Client)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
f, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
pool, err := finder.ResourcePool(ctx, "DC0_C0/Resources")
if err != nil {
t.Fatal(err)
}
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
for j := 0; j < 2; j++ {
cspec := spec // copy spec and give it a unique name
cspec.Name += fmt.Sprintf("-%d", j)
wg.Add(1)
go func() {
defer wg.Done()
task, _ := f.VmFolder.CreateVM(ctx, cspec, pool, nil)
info, terr := task.WaitForResult(ctx, nil)
if terr != nil {
t.Error(terr)
}
go func() {
for _ = range ticker.C {
var content []types.ObjectContent
rerr := pc.RetrieveOne(ctx, info.Result.(types.ManagedObjectReference), nil, &content)
if rerr != nil {
t.Error(rerr)
}
}
}()
}()
}
vms, err := finder.VirtualMachineList(ctx, "*")
if err != nil {
t.Fatal(err)
}
for i := range vms {
vm := vms[i]
wg.Add(1)
go func() {
defer wg.Done()
task, _ := vm.PowerOff(ctx)
_ = task.Wait(ctx)
}()
}
}()
}
wg.Wait()
}

426
vendor/github.com/vmware/govmomi/simulator/registry.go generated vendored Normal file
View File

@@ -0,0 +1,426 @@
/*
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
"sync"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
// This is a map from a reference type name to a reference value name prefix.
// It's a convention that VirtualCenter follows. The map is not complete, but
// it should cover the most popular objects.
var refValueMap = map[string]string{
"DistributedVirtualPortgroup": "dvportgroup",
"EnvironmentBrowser": "envbrowser",
"HostSystem": "host",
"ResourcePool": "resgroup",
"VirtualMachine": "vm",
"VirtualMachineSnapshot": "snapshot",
"VmwareDistributedVirtualSwitch": "dvs",
"DistributedVirtualSwitch": "dvs",
}
// Map is the default Registry instance.
var Map = NewRegistry()
// RegisterObject interface supports callbacks when objects are added and removed from the Registry
type RegisterObject interface {
mo.Reference
PutObject(mo.Reference)
RemoveObject(types.ManagedObjectReference)
}
// Registry manages a map of mo.Reference objects
type Registry struct {
m sync.Mutex
objects map[types.ManagedObjectReference]mo.Reference
handlers map[types.ManagedObjectReference]RegisterObject
locks map[types.ManagedObjectReference]sync.Locker
counter int
}
// NewRegistry creates a new instances of Registry
func NewRegistry() *Registry {
r := &Registry{
objects: make(map[types.ManagedObjectReference]mo.Reference),
handlers: make(map[types.ManagedObjectReference]RegisterObject),
locks: make(map[types.ManagedObjectReference]sync.Locker),
}
return r
}
// typeName returns the type of the given object.
func typeName(item mo.Reference) string {
return reflect.TypeOf(item).Elem().Name()
}
// valuePrefix returns the value name prefix of a given object
func valuePrefix(typeName string) string {
if v, ok := refValueMap[typeName]; ok {
return v
}
return strings.ToLower(typeName)
}
// newReference returns a new MOR, where Type defaults to type of the given item
// and Value defaults to a unique id for the given type.
func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference {
ref := item.Reference()
if ref.Type == "" {
ref.Type = typeName(item)
}
if ref.Value == "" {
r.counter++
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), r.counter)
}
return ref
}
func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectReference) {
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
}
// AddHandler adds a RegisterObject handler to the Registry.
func (r *Registry) AddHandler(h RegisterObject) {
r.handlers[h.Reference()] = h
}
// NewEntity sets Entity().Self with a new, unique Value.
// Useful for creating object instances from templates.
func (r *Registry) NewEntity(item mo.Entity) mo.Entity {
e := item.Entity()
e.Self.Value = ""
e.Self = r.newReference(item)
return item
}
// PutEntity sets item.Parent to that of parent.Self before adding item to the Registry.
func (r *Registry) PutEntity(parent mo.Entity, item mo.Entity) mo.Entity {
e := item.Entity()
if parent != nil {
e.Parent = &parent.Entity().Self
}
r.Put(item)
return item
}
// Get returns the object for the given reference.
func (r *Registry) Get(ref types.ManagedObjectReference) mo.Reference {
r.m.Lock()
defer r.m.Unlock()
return r.objects[ref]
}
// Any returns the first instance of entity type specified by kind.
func (r *Registry) Any(kind string) mo.Entity {
r.m.Lock()
defer r.m.Unlock()
for ref, val := range r.objects {
if ref.Type == kind {
return val.(mo.Entity)
}
}
return nil
}
// Put adds a new object to Registry, generating a ManagedObjectReference if not already set.
func (r *Registry) Put(item mo.Reference) mo.Reference {
r.m.Lock()
defer r.m.Unlock()
ref := item.Reference()
if ref.Type == "" || ref.Value == "" {
ref = r.newReference(item)
r.setReference(item, ref)
}
if me, ok := item.(mo.Entity); ok {
me.Entity().ConfigStatus = types.ManagedEntityStatusGreen
me.Entity().OverallStatus = types.ManagedEntityStatusGreen
me.Entity().EffectiveRole = []int32{-1} // Admin
}
r.objects[ref] = item
for _, h := range r.handlers {
h.PutObject(item)
}
return item
}
// Remove removes an object from the Registry.
func (r *Registry) Remove(item types.ManagedObjectReference) {
r.m.Lock()
defer r.m.Unlock()
for _, h := range r.handlers {
h.RemoveObject(item)
}
delete(r.objects, item)
delete(r.handlers, item)
delete(r.locks, item)
}
// getEntityParent traverses up the inventory and returns the first object of type kind.
// If no object of type kind is found, the method will panic when it reaches the
// inventory root Folder where the Parent field is nil.
func (r *Registry) getEntityParent(item mo.Entity, kind string) mo.Entity {
for {
parent := item.Entity().Parent
item = r.Get(*parent).(mo.Entity)
if item.Reference().Type == kind {
return item
}
}
}
// getEntityDatacenter returns the Datacenter containing the given item
func (r *Registry) getEntityDatacenter(item mo.Entity) *Datacenter {
return r.getEntityParent(item, "Datacenter").(*Datacenter)
}
func (r *Registry) getEntityFolder(item mo.Entity, kind string) *Folder {
dc := Map.getEntityDatacenter(item)
var ref types.ManagedObjectReference
switch kind {
case "datastore":
ref = dc.DatastoreFolder
}
folder := r.Get(ref).(*Folder)
// If Model was created with Folder option, use that Folder; else use top-level folder
for _, child := range folder.ChildEntity {
if child.Type == "Folder" {
folder = Map.Get(child).(*Folder)
break
}
}
return folder
}
// getEntityComputeResource returns the ComputeResource parent for the given item.
// A ResourcePool for example may have N Parents of type ResourcePool, but the top
// most Parent pool is always a ComputeResource child.
func (r *Registry) getEntityComputeResource(item mo.Entity) mo.Entity {
for {
parent := item.Entity().Parent
item = r.Get(*parent).(mo.Entity)
switch item.Reference().Type {
case "ComputeResource":
return item
case "ClusterComputeResource":
return item
}
}
}
// FindByName returns the first mo.Entity of the given refs whose Name field is equal to the given name.
// If there is no match, nil is returned.
// This method is useful for cases where objects are required to have a unique name, such as Datastore with
// a HostStorageSystem or HostSystem within a ClusterComputeResource.
func (r *Registry) FindByName(name string, refs []types.ManagedObjectReference) mo.Entity {
for _, ref := range refs {
if e, ok := r.Get(ref).(mo.Entity); ok {
if name == e.Entity().Name {
return e
}
}
}
return nil
}
// FindReference returns the 1st match found in refs, or nil if not found.
func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedObjectReference) *types.ManagedObjectReference {
for _, ref := range refs {
for _, m := range match {
if ref == m {
return &ref
}
}
}
return nil
}
// AppendReference appends the given refs to field.
func (r *Registry) AppendReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
r.WithLock(obj, func() {
*field = append(*field, ref...)
})
}
// AddReference appends ref to field if not already in the given field.
func (r *Registry) AddReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
r.WithLock(obj, func() {
if FindReference(*field, ref) == nil {
*field = append(*field, ref)
}
})
}
// RemoveReference removes ref from the given field.
func RemoveReference(field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
for i, r := range *field {
if r == ref {
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
}
}
// RemoveReference removes ref from the given field.
func (r *Registry) RemoveReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
r.WithLock(obj, func() {
RemoveReference(field, ref)
})
}
func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
r.WithLock(obj, func() {
for i, name := range *field {
if name == val {
*field = append((*field)[:i], (*field)[i+1:]...)
break
}
}
})
}
func (r *Registry) content() types.ServiceContent {
return r.Get(methods.ServiceInstance).(*ServiceInstance).Content
}
// IsESX returns true if this Registry maps an ESX model
func (r *Registry) IsESX() bool {
return r.content().About.ApiType == "HostAgent"
}
// IsVPX returns true if this Registry maps a VPX model
func (r *Registry) IsVPX() bool {
return !r.IsESX()
}
// SearchIndex returns the SearchIndex singleton
func (r *Registry) SearchIndex() *SearchIndex {
return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex)
}
// EventManager returns the EventManager singleton
func (r *Registry) EventManager() *EventManager {
return r.Get(r.content().EventManager.Reference()).(*EventManager)
}
// FileManager returns the FileManager singleton
func (r *Registry) FileManager() *FileManager {
return r.Get(r.content().FileManager.Reference()).(*FileManager)
}
// VirtualDiskManager returns the VirtualDiskManager singleton
func (r *Registry) VirtualDiskManager() *VirtualDiskManager {
return r.Get(r.content().VirtualDiskManager.Reference()).(*VirtualDiskManager)
}
// ViewManager returns the ViewManager singleton
func (r *Registry) ViewManager() *ViewManager {
return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
}
// UserDirectory returns the UserDirectory singleton
func (r *Registry) UserDirectory() *UserDirectory {
return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
}
// SessionManager returns the SessionManager singleton
func (r *Registry) SessionManager() *SessionManager {
return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
}
func (r *Registry) MarshalJSON() ([]byte, error) {
r.m.Lock()
defer r.m.Unlock()
vars := struct {
Objects int
Locks int
}{
len(r.objects),
len(r.locks),
}
return json.Marshal(vars)
}
func (r *Registry) locker(obj mo.Reference) sync.Locker {
if mu, ok := obj.(sync.Locker); ok {
return mu
}
ref := obj.Reference()
r.m.Lock()
mu, ok := r.locks[ref]
if !ok {
mu = new(sync.Mutex)
r.locks[ref] = mu
}
r.m.Unlock()
return mu
}
var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
// WithLock holds a lock for the given object while then given function is run.
func (r *Registry) WithLock(obj mo.Reference, f func()) {
if enableLocker {
mu := r.locker(obj)
mu.Lock()
defer mu.Unlock()
}
f()
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"testing"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
func TestRegistry(t *testing.T) {
r := NewRegistry()
ref := types.ManagedObjectReference{Type: "Test", Value: "Test"}
f := &mo.Folder{}
f.Self = ref
r.PutEntity(nil, f)
e := r.Get(ref)
if e.Reference() != ref {
t.Fail()
}
r.Remove(ref)
if r.Get(ref) != nil {
t.Fail()
}
r.Put(e)
e = r.Get(ref)
if e.Reference() != ref {
t.Fail()
}
}
func TestRemoveReference(t *testing.T) {
var refs []types.ManagedObjectReference
for i := 0; i < 5; i++ {
refs = append(refs, types.ManagedObjectReference{Type: "any", Value: fmt.Sprintf("%d", i)})
}
n := len(refs)
RemoveReference(&refs, refs[2])
if len(refs) != n-1 {
t.Errorf("%d", len(refs))
}
}

View File

@@ -0,0 +1,314 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"strings"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type ResourcePool struct {
mo.ResourcePool
}
func NewResourcePool() *ResourcePool {
pool := &ResourcePool{
ResourcePool: esx.ResourcePool,
}
if Map.IsVPX() {
pool.DisabledMethod = nil // Enable VApp methods for VC
}
return pool
}
func allResourceFieldsSet(info *types.ResourceAllocationInfo) bool {
return info.Reservation != nil &&
info.Limit != nil &&
info.ExpandableReservation != nil &&
info.Shares != nil
}
func allResourceFieldsValid(info *types.ResourceAllocationInfo) bool {
if info.Reservation != nil {
if *info.Reservation < 0 {
return false
}
}
if info.Limit != nil {
if *info.Limit < -1 {
return false
}
}
if info.Shares != nil {
if info.Shares.Level == types.SharesLevelCustom {
if info.Shares.Shares < 0 {
return false
}
}
}
if info.OverheadLimit != nil {
return false
}
return true
}
func (p *ResourcePool) createChild(name string, spec types.ResourceConfigSpec) (*ResourcePool, *soap.Fault) {
if e := Map.FindByName(name, p.ResourcePool.ResourcePool); e != nil {
return nil, Fault("", &types.DuplicateName{
Name: e.Entity().Name,
Object: e.Reference(),
})
}
if !(allResourceFieldsSet(&spec.CpuAllocation) && allResourceFieldsValid(&spec.CpuAllocation)) {
return nil, Fault("", &types.InvalidArgument{
InvalidProperty: "spec.cpuAllocation",
})
}
if !(allResourceFieldsSet(&spec.MemoryAllocation) && allResourceFieldsValid(&spec.MemoryAllocation)) {
return nil, Fault("", &types.InvalidArgument{
InvalidProperty: "spec.memoryAllocation",
})
}
child := NewResourcePool()
child.Name = name
child.Owner = p.Owner
child.Summary.GetResourcePoolSummary().Name = name
child.Config.CpuAllocation = spec.CpuAllocation
child.Config.MemoryAllocation = spec.MemoryAllocation
child.Config.Entity = spec.Entity
return child, nil
}
func (p *ResourcePool) CreateResourcePool(c *types.CreateResourcePool) soap.HasFault {
body := &methods.CreateResourcePoolBody{}
child, err := p.createChild(c.Name, c.Spec)
if err != nil {
body.Fault_ = err
return body
}
Map.PutEntity(p, Map.NewEntity(child))
p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
body.Res = &types.CreateResourcePoolResponse{
Returnval: child.Reference(),
}
return body
}
func updateResourceAllocation(kind string, src, dst *types.ResourceAllocationInfo) types.BaseMethodFault {
if !allResourceFieldsValid(src) {
return &types.InvalidArgument{
InvalidProperty: fmt.Sprintf("spec.%sAllocation", kind),
}
}
if src.Reservation != nil {
dst.Reservation = src.Reservation
}
if src.Limit != nil {
dst.Limit = src.Limit
}
if src.Shares != nil {
dst.Shares = src.Shares
}
return nil
}
func (p *ResourcePool) UpdateConfig(c *types.UpdateConfig) soap.HasFault {
body := &methods.UpdateConfigBody{}
if c.Name != "" {
if e := Map.FindByName(c.Name, p.ResourcePool.ResourcePool); e != nil {
body.Fault_ = Fault("", &types.DuplicateName{
Name: e.Entity().Name,
Object: e.Reference(),
})
return body
}
p.Name = c.Name
}
spec := c.Config
if spec != nil {
if err := updateResourceAllocation("memory", &spec.MemoryAllocation, &p.Config.MemoryAllocation); err != nil {
body.Fault_ = Fault("", err)
return body
}
if err := updateResourceAllocation("cpu", &spec.CpuAllocation, &p.Config.CpuAllocation); err != nil {
body.Fault_ = Fault("", err)
return body
}
}
body.Res = &types.UpdateConfigResponse{}
return body
}
type VirtualApp struct {
mo.VirtualApp
}
func NewVAppConfigSpec() types.VAppConfigSpec {
spec := types.VAppConfigSpec{
Annotation: "vcsim",
VmConfigSpec: types.VmConfigSpec{
Product: []types.VAppProductSpec{
{
Info: &types.VAppProductInfo{
Name: "vcsim",
Vendor: "VMware",
VendorUrl: "http://www.vmware.com/",
Version: "0.1",
},
ArrayUpdateSpec: types.ArrayUpdateSpec{
Operation: types.ArrayUpdateOperationAdd,
},
},
},
},
}
return spec
}
func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault {
body := &methods.CreateVAppBody{}
pool, err := p.createChild(req.Name, req.ResSpec)
if err != nil {
body.Fault_ = err
return body
}
child := &VirtualApp{}
child.ResourcePool = pool.ResourcePool
child.Self.Type = "VirtualApp"
child.ParentFolder = req.VmFolder
if child.ParentFolder == nil {
folder := Map.getEntityDatacenter(p).VmFolder
child.ParentFolder = &folder
}
child.VAppConfig = &types.VAppConfigInfo{
VmConfigInfo: types.VmConfigInfo{},
Annotation: req.ConfigSpec.Annotation,
}
for _, product := range req.ConfigSpec.Product {
child.VAppConfig.Product = append(child.VAppConfig.Product, *product.Info)
}
Map.PutEntity(p, Map.NewEntity(child))
p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
body.Res = &types.CreateVAppResponse{
Returnval: child.Reference(),
}
return body
}
func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Task) soap.HasFault {
ctx.Caller = &a.Self
body := &methods.CreateChildVM_TaskBody{}
folder := Map.Get(*a.ParentFolder).(*Folder)
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
This: folder.Self,
Config: req.Config,
Host: req.Host,
Pool: req.This,
})
body.Res = &types.CreateChildVM_TaskResponse{
Returnval: res.(*methods.CreateVM_TaskBody).Res.Returnval,
}
return body
}
func (a *VirtualApp) DestroyTask(req *types.Destroy_Task) soap.HasFault {
return (&ResourcePool{ResourcePool: a.ResourcePool}).DestroyTask(req)
}
func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
task := CreateTask(p, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if strings.HasSuffix(p.Parent.Type, "ComputeResource") {
// Can't destroy the root pool
return nil, &types.InvalidArgument{}
}
pp := Map.Get(*p.Parent).(*ResourcePool)
parent := &pp.ResourcePool
// Remove child reference from rp
Map.RemoveReference(parent, &parent.ResourcePool, req.This)
// The grandchildren become children of the parent (rp)
Map.AppendReference(parent, &parent.ResourcePool, p.ResourcePool.ResourcePool...)
// And VMs move to the parent
vms := p.ResourcePool.Vm
for _, ref := range vms {
vm := Map.Get(ref).(*VirtualMachine)
Map.WithLock(vm, func() { vm.ResourcePool = &parent.Self })
}
Map.AppendReference(parent, &parent.Vm, vms...)
Map.Remove(req.This)
return nil, nil
})
return &methods.Destroy_TaskBody{
Res: &types.Destroy_TaskResponse{
Returnval: task.Run(),
},
}
}

View File

@@ -0,0 +1,323 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"reflect"
"testing"
"github.com/google/uuid"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func TestResourcePool(t *testing.T) {
ctx := context.Background()
m := &Model{
ServiceContent: esx.ServiceContent,
RootFolder: esx.RootFolder,
}
err := m.Create()
if err != nil {
t.Fatal(err)
}
c := m.Service.client
finder := find.NewFinder(c, false)
finder.SetDatacenter(object.NewDatacenter(c, esx.Datacenter.Reference()))
spec := types.DefaultResourceConfigSpec()
parent := object.NewResourcePool(c, esx.ResourcePool.Self)
spec.CpuAllocation.Reservation = nil
// missing required field (Reservation) for create
_, err = parent.Create(ctx, "fail", spec)
if err == nil {
t.Error("expected error")
}
spec = types.DefaultResourceConfigSpec()
// can't destroy a root pool
task, err := parent.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
if err = task.Wait(ctx); err == nil {
t.Fatal("expected error destroying a root pool")
}
// create a child pool
childName := uuid.New().String()
child, err := parent.Create(ctx, childName, spec)
if err != nil {
t.Fatal(err)
}
if child.Reference() == esx.ResourcePool.Self {
t.Error("expected new pool Self reference")
}
*spec.CpuAllocation.Reservation = -1
// invalid field value (Reservation) for update
err = child.UpdateConfig(ctx, "", &spec)
if err == nil {
t.Error("expected error")
}
// valid config update
*spec.CpuAllocation.Reservation = 10
err = child.UpdateConfig(ctx, "", &spec)
if err != nil {
t.Error(err)
}
var p mo.ResourcePool
err = child.Properties(ctx, child.Reference(), []string{"config.cpuAllocation"}, &p)
if err != nil {
t.Error(err)
}
if *p.Config.CpuAllocation.Reservation != 10 {
t.Error("config not updated")
}
// duplicate name
_, err = parent.Create(ctx, childName, spec)
if err == nil {
t.Error("expected error")
}
// create a grandchild pool
grandChildName := uuid.New().String()
_, err = child.Create(ctx, grandChildName, spec)
if err != nil {
t.Fatal(err)
}
// create sibling (of the grand child) pool
siblingName := uuid.New().String()
_, err = child.Create(ctx, siblingName, spec)
if err != nil {
t.Fatal(err)
}
// finder should return the 2 grand children
pools, err := finder.ResourcePoolList(ctx, "*/Resources/"+childName+"/*")
if err != nil {
t.Fatal(err)
}
if len(pools) != 2 {
t.Fatalf("len(pools) == %d", len(pools))
}
// destroy the child
task, err = child.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
// finder should error not found after destroying the child
_, err = finder.ResourcePoolList(ctx, "*/Resources/"+childName+"/*")
if err == nil {
t.Fatal("expected not found error")
}
// since the child was destroyed, grand child pools should now be children of the root pool
pools, err = finder.ResourcePoolList(ctx, "*/Resources/*")
if err != nil {
t.Fatal(err)
}
if len(pools) != 2 {
t.Fatalf("len(pools) == %d", len(pools))
}
}
func TestCreateVAppESX(t *testing.T) {
ctx := context.Background()
m := ESX()
m.Datastore = 0
m.Machine = 0
err := m.Create()
if err != nil {
t.Fatal(err)
}
c := m.Service.client
parent := object.NewResourcePool(c, esx.ResourcePool.Self)
rspec := types.DefaultResourceConfigSpec()
vspec := NewVAppConfigSpec()
_, err = parent.CreateVApp(ctx, "myapp", rspec, vspec, nil)
if err == nil {
t.Fatal("expected error")
}
fault := soap.ToSoapFault(err).Detail.Fault
if reflect.TypeOf(fault) != reflect.TypeOf(&types.MethodDisabled{}) {
t.Errorf("fault=%#v", fault)
}
}
func TestCreateVAppVPX(t *testing.T) {
ctx := context.Background()
m := VPX()
err := m.Create()
if err != nil {
t.Fatal(err)
}
defer m.Remove()
c := m.Service.client
parent := object.NewResourcePool(c, Map.Any("ResourcePool").Reference())
rspec := types.DefaultResourceConfigSpec()
vspec := NewVAppConfigSpec()
vapp, err := parent.CreateVApp(ctx, "myapp", rspec, vspec, nil)
if err != nil {
t.Fatal(err)
}
_, err = parent.CreateVApp(ctx, "myapp", rspec, vspec, nil)
if err == nil {
t.Error("expected error")
}
spec := types.VirtualMachineConfigSpec{
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
Files: &types.VirtualMachineFileInfo{
VmPathName: "[LocalDS_0]",
},
}
for _, fail := range []bool{true, false} {
task, cerr := vapp.CreateChildVM(ctx, spec, nil)
if cerr != nil {
t.Fatal(err)
}
cerr = task.Wait(ctx)
if fail {
if cerr == nil {
t.Error("expected error")
}
} else {
if cerr != nil {
t.Error(err)
}
}
spec.Name = "test"
}
si := object.NewSearchIndex(c)
vm, err := si.FindChild(ctx, vapp, spec.Name)
if err != nil {
t.Fatal(err)
}
if vm == nil {
t.Errorf("FindChild(%s)==nil", spec.Name)
}
task, err := vapp.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
}
func TestResourcePoolValidation(t *testing.T) {
tests := []func() bool{
func() bool {
return allResourceFieldsSet(&types.ResourceAllocationInfo{})
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Limit = nil
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Reservation = nil
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.ExpandableReservation = nil
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Shares = nil
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Reservation = types.NewInt64(-1)
return allResourceFieldsValid(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Limit = types.NewInt64(-100)
return allResourceFieldsValid(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
shares := spec.CpuAllocation.Shares
shares.Level = types.SharesLevelCustom
shares.Shares = -1
return allResourceFieldsValid(&spec.CpuAllocation)
},
}
for i, test := range tests {
ok := test()
if ok {
t.Errorf("%d: expected false", i)
}
}
}

View File

@@ -0,0 +1,155 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type SearchIndex struct {
mo.SearchIndex
}
func NewSearchIndex(ref types.ManagedObjectReference) object.Reference {
m := &SearchIndex{}
m.Self = ref
return m
}
func (s *SearchIndex) FindByDatastorePath(r *types.FindByDatastorePath) soap.HasFault {
res := &methods.FindByDatastorePathBody{Res: new(types.FindByDatastorePathResponse)}
for ref, obj := range Map.objects {
vm, ok := obj.(*VirtualMachine)
if !ok {
continue
}
if vm.Config.Files.VmPathName == r.Path {
res.Res.Returnval = &ref
break
}
}
return res
}
func (s *SearchIndex) FindByInventoryPath(req *types.FindByInventoryPath) soap.HasFault {
body := &methods.FindByInventoryPathBody{Res: new(types.FindByInventoryPathResponse)}
path := strings.Split(req.InventoryPath, "/")
if len(path) <= 1 {
return body
}
root := Map.content().RootFolder
o := &root
for _, name := range path[1:] {
f := s.FindChild(&types.FindChild{Entity: *o, Name: name})
o = f.(*methods.FindChildBody).Res.Returnval
if o == nil {
break
}
}
body.Res.Returnval = o
return body
}
func (s *SearchIndex) FindChild(req *types.FindChild) soap.HasFault {
body := &methods.FindChildBody{}
obj := Map.Get(req.Entity)
if obj == nil {
body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Entity})
return body
}
body.Res = new(types.FindChildResponse)
var children []types.ManagedObjectReference
switch e := obj.(type) {
case *Datacenter:
children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder}
case *Folder:
children = e.ChildEntity
case *mo.ComputeResource:
children = e.Host
children = append(children, *e.ResourcePool)
case *ClusterComputeResource:
children = e.Host
children = append(children, *e.ResourcePool)
case *ResourcePool:
children = e.ResourcePool.ResourcePool
children = append(children, e.Vm...)
case *VirtualApp:
children = e.ResourcePool.ResourcePool
children = append(children, e.Vm...)
}
match := Map.FindByName(req.Name, children)
if match != nil {
ref := match.Reference()
body.Res.Returnval = &ref
}
return body
}
func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault {
body := &methods.FindByUuidBody{Res: new(types.FindByUuidResponse)}
if req.VmSearch {
// Find Virtual Machine using UUID
for ref, obj := range Map.objects {
vm, ok := obj.(*VirtualMachine)
if !ok {
continue
}
if vm.Config.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
}
}
} else {
// Find Host System using UUID
for ref, obj := range Map.objects {
host, ok := obj.(*HostSystem)
if !ok {
continue
}
if host.Summary.Hardware.Uuid == req.Uuid {
body.Res.Returnval = &ref
break
}
}
}
return body
}

View File

@@ -0,0 +1,210 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"strings"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func TestSearchIndex(t *testing.T) {
ctx := context.Background()
for _, model := range []*Model{ESX(), VPX()} {
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
vms, err := finder.VirtualMachineList(ctx, "*")
if err != nil {
t.Fatal(err)
}
vm := Map.Get(vms[0].Reference()).(*VirtualMachine)
si := object.NewSearchIndex(c.Client)
ref, err := si.FindByDatastorePath(ctx, dc, vm.Config.Files.VmPathName)
if err != nil {
t.Fatal(err)
}
if ref.Reference() != vm.Reference() {
t.Errorf("moref mismatch %s != %s", ref, vm.Reference())
}
ref, err = si.FindByDatastorePath(ctx, dc, vm.Config.Files.VmPathName+"enoent")
if err != nil {
t.Fatal(err)
}
if ref != nil {
t.Errorf("ref=%s", ref)
}
ref, err = si.FindByUuid(ctx, dc, vm.Config.Uuid, true, nil)
if err != nil {
t.Fatal(err)
}
if ref.Reference() != vm.Reference() {
t.Errorf("moref mismatch %s != %s", ref, vm.Reference())
}
ref, err = si.FindByUuid(ctx, dc, vm.Config.Uuid, false, nil)
if err != nil {
t.Fatal(err)
}
if ref != nil {
t.Error("expected nil")
}
host := Map.Any("HostSystem").(*HostSystem)
ref, err = si.FindByUuid(ctx, dc, host.Summary.Hardware.Uuid, false, nil)
if err != nil {
t.Fatal(err)
}
if ref.Reference() != host.Reference() {
t.Errorf("moref mismatch %s != %s", ref, host.Reference())
}
}
}
func TestSearchIndexFindChild(t *testing.T) {
ctx := context.Background()
model := VPX()
model.Pool = 3
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
si := object.NewSearchIndex(c.Client)
tests := [][]string{
// Datacenter -> host Folder -> Cluster -> HostSystem
{"DC0", "host", "DC0_C0", "DC0_C0_H0"},
// Datacenter -> host Folder -> ComputeResource -> HostSystem
{"DC0", "host", "DC0_H0", "DC0_H0"},
// Datacenter -> host Folder -> Cluster -> ResourcePool -> ResourcePool
{"DC0", "host", "DC0_C0", "Resources", "DC0_C0_RP1"},
// Datacenter -> host Folder -> Cluster -> ResourcePool -> VirtualMachine
{"DC0", "host", "DC0_C0", "Resources", "DC0_C0_RP0_VM0"},
// Datacenter -> vm Folder -> VirtualMachine
{"DC0", "vm", "DC0_C0_RP0_VM0"},
}
root := c.ServiceContent.RootFolder
for _, path := range tests {
parent := root
ipath := []string{""}
for _, name := range path {
ref, err := si.FindChild(ctx, parent, name)
if err != nil {
t.Fatal(err)
}
if ref == nil {
t.Fatalf("failed to match %s using %s", name, parent)
}
parent = ref.Reference()
ipath = append(ipath, name)
iref, err := si.FindByInventoryPath(ctx, strings.Join(ipath, "/"))
if err != nil {
t.Fatal(err)
}
if iref.Reference() != ref.Reference() {
t.Errorf("%s != %s", iref, ref)
}
}
}
ref, err := si.FindChild(ctx, root, "enoent")
if err != nil {
t.Fatal(err)
}
if ref != nil {
t.Error("unexpected match")
}
root.Value = "enoent"
_, err = si.FindChild(ctx, root, "enoent")
if err == nil {
t.Error("expected error")
}
if _, ok := soap.ToSoapFault(err).VimFault().(types.ManagedObjectNotFound); !ok {
t.Error("expected ManagedObjectNotFound fault")
}
for _, path := range []string{"", "/", "/enoent"} {
ref, err := si.FindByInventoryPath(ctx, path)
if err != nil {
t.Fatal(err)
}
if ref != nil {
t.Error("unexpected match")
}
}
}

View File

@@ -0,0 +1,104 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"time"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type ServiceInstance struct {
mo.ServiceInstance
}
func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *ServiceInstance {
Map = NewRegistry()
s := &ServiceInstance{}
s.Self = methods.ServiceInstance
s.Content = content
Map.Put(s)
f := &Folder{Folder: folder}
Map.Put(f)
var setting []types.BaseOptionValue
if content.About.ApiType == "HostAgent" {
CreateDefaultESX(f)
} else {
setting = vpx.Setting
}
objects := []object.Reference{
NewSessionManager(*s.Content.SessionManager),
NewAuthorizationManager(*s.Content.AuthorizationManager),
NewPerformanceManager(*s.Content.PerfManager),
NewPropertyCollector(s.Content.PropertyCollector),
NewFileManager(*s.Content.FileManager),
NewVirtualDiskManager(*s.Content.VirtualDiskManager),
NewLicenseManager(*s.Content.LicenseManager),
NewSearchIndex(*s.Content.SearchIndex),
NewViewManager(*s.Content.ViewManager),
NewEventManager(*s.Content.EventManager),
NewTaskManager(*s.Content.TaskManager),
NewUserDirectory(*s.Content.UserDirectory),
NewOptionManager(s.Content.Setting, setting),
}
if s.Content.CustomFieldsManager != nil {
objects = append(objects, NewCustomFieldsManager(*s.Content.CustomFieldsManager))
}
if s.Content.IpPoolManager != nil {
objects = append(objects, NewIpPoolManager(*s.Content.IpPoolManager))
}
if s.Content.AccountManager != nil {
objects = append(objects, NewHostLocalAccountManager(*s.Content.AccountManager))
}
for _, o := range objects {
Map.Put(o)
}
return s
}
func (s *ServiceInstance) RetrieveServiceContent(*types.RetrieveServiceContent) soap.HasFault {
return &methods.RetrieveServiceContentBody{
Res: &types.RetrieveServiceContentResponse{
Returnval: s.Content,
},
}
}
func (*ServiceInstance) CurrentTime(*types.CurrentTime) soap.HasFault {
return &methods.CurrentTimeBody{
Res: &types.CurrentTimeResponse{
Returnval: time.Now(),
},
}
}

View File

@@ -0,0 +1,277 @@
/*
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/session"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type SessionManager struct {
mo.SessionManager
ServiceHostName string
sessions map[string]Session
}
func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
s := &SessionManager{
sessions: make(map[string]Session),
}
s.Self = ref
return s
}
func (s *SessionManager) Login(ctx *Context, login *types.Login) soap.HasFault {
body := &methods.LoginBody{}
if login.Locale == "" {
login.Locale = session.Locale
}
if login.UserName == "" || login.Password == "" || ctx.Session != nil {
body.Fault_ = invalidLogin
} else {
session := Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
UserName: login.UserName,
FullName: login.UserName,
LoginTime: time.Now(),
LastActiveTime: time.Now(),
Locale: login.Locale,
MessageLocale: login.Locale,
},
Registry: NewRegistry(),
}
ctx.SetSession(session, true)
body.Res = &types.LoginResponse{
Returnval: session.UserSession,
}
}
return body
}
func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
session := ctx.Session
delete(s.sessions, session.Key)
ctx.postEvent(&types.UserLogoutSessionEvent{
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
SessionId: session.Key,
LoginTime: &session.LoginTime,
})
return &methods.LogoutBody{Res: new(types.LogoutResponse)}
}
func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSession) soap.HasFault {
body := new(methods.TerminateSessionBody)
for _, id := range req.SessionId {
if id == ctx.Session.Key {
body.Fault_ = Fault("", new(types.InvalidArgument))
return body
}
delete(s.sessions, id)
}
body.Res = new(types.TerminateSessionResponse)
return body
}
func (s *SessionManager) AcquireCloneTicket(ctx *Context, _ *types.AcquireCloneTicket) soap.HasFault {
session := *ctx.Session
session.Key = uuid.New().String()
s.sessions[session.Key] = session
return &methods.AcquireCloneTicketBody{
Res: &types.AcquireCloneTicketResponse{
Returnval: session.Key,
},
}
}
func (s *SessionManager) CloneSession(ctx *Context, ticket *types.CloneSession) soap.HasFault {
body := new(methods.CloneSessionBody)
session, exists := s.sessions[ticket.CloneTicket]
if exists {
delete(s.sessions, ticket.CloneTicket) // A clone ticket can only be used once
session.Key = uuid.New().String()
ctx.SetSession(session, true)
body.Res = &types.CloneSessionResponse{
Returnval: session.UserSession,
}
} else {
body.Fault_ = invalidLogin
}
return body
}
func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
return &methods.AcquireGenericServiceTicketBody{
Res: &types.AcquireGenericServiceTicketResponse{
Returnval: types.SessionManagerGenericServiceTicket{
Id: uuid.New().String(),
HostName: s.ServiceHostName,
},
},
}
}
// internalContext is the session for use by the in-memory client (Service.RoundTrip)
var internalContext = &Context{
Context: context.Background(),
Session: &Session{
UserSession: types.UserSession{
Key: uuid.New().String(),
},
Registry: NewRegistry(),
},
}
var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
// Context provides per-request Session management.
type Context struct {
req *http.Request
res http.ResponseWriter
m *SessionManager
context.Context
Session *Session
Caller *types.ManagedObjectReference
}
// mapSession maps an HTTP cookie to a Session.
func (c *Context) mapSession() {
if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
if val, ok := c.m.sessions[cookie.Value]; ok {
c.SetSession(val, false)
}
}
}
// SetSession should be called after successful authentication.
func (c *Context) SetSession(session Session, login bool) {
session.UserAgent = c.req.UserAgent()
session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
session.LastActiveTime = time.Now()
c.m.sessions[session.Key] = session
c.Session = &session
if login {
http.SetCookie(c.res, &http.Cookie{
Name: soap.SessionCookieName,
Value: session.Key,
})
c.postEvent(&types.UserLoginSessionEvent{
SessionId: session.Key,
IpAddress: session.IpAddress,
UserAgent: session.UserAgent,
Locale: session.Locale,
})
}
}
// WithLock holds a lock for the given object while then given function is run.
func (c *Context) WithLock(obj mo.Reference, f func()) {
if c.Caller != nil && *c.Caller == obj.Reference() {
// Internal method invocation, obj is already locked
f()
return
}
Map.WithLock(obj, f)
}
// postEvent wraps EventManager.PostEvent for internal use, with a lock on the EventManager.
func (c *Context) postEvent(events ...types.BaseEvent) {
m := Map.EventManager()
c.WithLock(m, func() {
for _, event := range events {
m.PostEvent(c, &types.PostEvent{EventToPost: event})
}
})
}
// Session combines a UserSession and a Registry for per-session managed objects.
type Session struct {
types.UserSession
*Registry
}
// Put wraps Registry.Put, setting the moref value to include the session key.
func (s *Session) Put(item mo.Reference) mo.Reference {
ref := item.Reference()
if ref.Value == "" {
ref.Value = fmt.Sprintf("session[%s]%s", s.Key, uuid.New())
}
s.Registry.setReference(item, ref)
return s.Registry.Put(item)
}
// Get wraps Registry.Get, session-izing singleton objects such as SessionManager and the root PropertyCollector.
func (s *Session) Get(ref types.ManagedObjectReference) mo.Reference {
obj := s.Registry.Get(ref)
if obj != nil {
return obj
}
// Return a session "view" of certain singleton objects
switch ref.Type {
case "SessionManager":
// Clone SessionManager so the PropertyCollector can properly report CurrentSession
m := *Map.SessionManager()
m.CurrentSession = &s.UserSession
// TODO: we could maintain SessionList as part of the SessionManager singleton
for _, session := range m.sessions {
m.SessionList = append(m.SessionList, session.UserSession)
}
return &m
case "PropertyCollector":
if ref == Map.content().PropertyCollector {
return s.Put(NewPropertyCollector(ref))
}
}
return Map.Get(ref)
}

View File

@@ -0,0 +1,186 @@
/*
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"strings"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func isNotAuthenticated(err error) bool {
if soap.IsSoapFault(err) {
switch soap.ToSoapFault(err).VimFault().(type) {
case types.NotAuthenticated:
return true
}
}
return false
}
func TestSessionManagerAuth(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
u := s.URL.User
s.URL.User = nil // skip Login()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
session, err := c.SessionManager.UserSession(ctx)
if err != nil {
t.Fatal(err)
}
if session != nil {
t.Error("expected nil session")
}
opts := object.NewOptionManager(c.Client, *c.ServiceContent.Setting)
var content []types.ObjectContent
err = opts.Properties(ctx, opts.Reference(), []string{"setting"}, &content)
if err != nil {
t.Fatal(err)
}
if len(content) != 1 {
t.Error("expected content len=1")
}
if len(content[0].PropSet) != 0 {
t.Error("non-empty PropSet")
}
if len(content[0].MissingSet) != 1 {
t.Error("expected MissingSet len=1")
}
if _, ok := content[0].MissingSet[0].Fault.Fault.(*types.NotAuthenticated); !ok {
t.Error("expected NotAuthenticated")
}
_, err = methods.GetCurrentTime(ctx, c)
if !isNotAuthenticated(err) {
t.Error("expected NotAuthenticated")
}
err = c.SessionManager.Login(ctx, u)
if err != nil {
t.Fatal(err)
}
c.UserAgent = "vcsim/x.x"
session, err = c.SessionManager.UserSession(ctx)
if err != nil {
t.Fatal(err)
}
if session == nil {
t.Error("expected session")
}
if session.UserAgent != c.UserAgent {
t.Errorf("UserAgent=%s", session.UserAgent)
}
content = nil
err = opts.Properties(ctx, opts.Reference(), []string{"setting"}, &content)
if err != nil {
t.Fatal(err)
}
if len(content) != 1 {
t.Error("expected content len=1")
}
if len(content[0].PropSet) != 1 {
t.Error("PropSet len=1")
}
if len(content[0].MissingSet) != 0 {
t.Error("expected MissingSet len=0")
}
last := session.LastActiveTime
_, err = methods.GetCurrentTime(ctx, c)
if err != nil {
t.Error(err)
}
pc, err := property.DefaultCollector(c.Client).Create(ctx)
if err != nil {
t.Fatal(err)
}
session, _ = c.SessionManager.UserSession(ctx)
if session.LastActiveTime.Equal(last) {
t.Error("LastActiveTime was not updated")
}
if !strings.Contains(pc.Reference().Value, session.Key) {
t.Errorf("invalid ref=%s", pc.Reference())
}
ticket, err := c.SessionManager.AcquireCloneTicket(ctx)
if err != nil {
t.Fatal(err)
}
c, err = govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
err = c.SessionManager.CloneSession(ctx, ticket)
if err != nil {
t.Fatal(err)
}
err = c.SessionManager.CloneSession(ctx, ticket)
if err == nil {
t.Error("expected error")
}
_, err = methods.GetCurrentTime(ctx, c)
if err != nil {
t.Error(err)
}
}

610
vendor/github.com/vmware/govmomi/simulator/simulator.go generated vendored Normal file
View File

@@ -0,0 +1,610 @@
/*
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path"
"reflect"
"sort"
"strings"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"github.com/vmware/govmomi/vim25/xml"
)
// Trace when set to true, writes SOAP traffic to stderr
var Trace = false
// Method encapsulates a decoded SOAP client request
type Method struct {
Name string
This types.ManagedObjectReference
Body types.AnyType
}
// Service decodes incoming requests and dispatches to a Handler
type Service struct {
client *vim25.Client
sm *SessionManager
readAll func(io.Reader) ([]byte, error)
TLS *tls.Config
ServeMux *http.ServeMux
}
// Server provides a simulator Service over HTTP
type Server struct {
*httptest.Server
URL *url.URL
caFile string
}
// New returns an initialized simulator Service instance
func New(instance *ServiceInstance) *Service {
s := &Service{
readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
}
s.client, _ = vim25.NewClient(context.Background(), s)
return s
}
type serverFaultBody struct {
Reason *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *serverFaultBody) Fault() *soap.Fault { return b.Reason }
func serverFault(msg string) soap.HasFault {
return &serverFaultBody{Reason: Fault(msg, &types.InvalidRequest{})}
}
// Fault wraps the given message and fault in a soap.Fault
func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
f := &soap.Fault{
Code: "ServerFaultCode",
String: msg,
}
f.Detail.Fault = fault
return f
}
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This)
session := ctx.Session
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
NoPermission: types.NoPermission{
Object: method.This,
PrivilegeId: "System.View",
},
}
return &serverFaultBody{Reason: Fault("", fault)}
}
} else {
// Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
if h := session.Get(method.This); h != nil {
handler = h
}
}
if handler == nil {
msg := fmt.Sprintf("managed object not found: %s", method.This)
log.Print(msg)
fault := &types.ManagedObjectNotFound{Obj: method.This}
return &serverFaultBody{Reason: Fault(msg, fault)}
}
name := method.Name
if strings.HasSuffix(name, vTaskSuffix) {
// Make golint happy renaming "Foo_Task" -> "FooTask"
name = name[:len(name)-len(vTaskSuffix)] + sTaskSuffix
}
m := reflect.ValueOf(handler).MethodByName(name)
if !m.IsValid() {
msg := fmt.Sprintf("%s does not implement: %s", method.This, method.Name)
log.Print(msg)
fault := &types.MethodNotFound{Receiver: method.This, Method: method.Name}
return &serverFaultBody{Reason: Fault(msg, fault)}
}
if e, ok := handler.(mo.Entity); ok {
for _, dm := range e.Entity().DisabledMethod {
if name == dm {
msg := fmt.Sprintf("%s method is disabled: %s", method.This, method.Name)
fault := &types.MethodDisabled{}
return &serverFaultBody{Reason: Fault(msg, fault)}
}
}
}
var args, res []reflect.Value
if m.Type().NumIn() == 2 {
args = append(args, reflect.ValueOf(ctx))
}
args = append(args, reflect.ValueOf(method.Body))
Map.WithLock(handler, func() {
res = m.Call(args)
})
return res[0].Interface().(soap.HasFault)
}
// RoundTrip implements the soap.RoundTripper interface in process.
// Rather than encode/decode SOAP over HTTP, this implementation uses reflection.
func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault) error {
field := func(r soap.HasFault, name string) reflect.Value {
return reflect.ValueOf(r).Elem().FieldByName(name)
}
// Every struct passed to soap.RoundTrip has "Req" and "Res" fields
req := field(request, "Req")
// Every request has a "This" field.
this := req.Elem().FieldByName("This")
method := &Method{
Name: req.Elem().Type().Name(),
This: this.Interface().(types.ManagedObjectReference),
Body: req.Interface(),
}
res := s.call(&Context{
Context: ctx,
Session: internalContext.Session,
}, method)
if err := res.Fault(); err != nil {
return soap.WrapSoapFault(err)
}
field(response, "Res").Set(field(res, "Res"))
return nil
}
// soapEnvelope is a copy of soap.Envelope, with namespace changed to "soapenv",
// and additional namespace attributes required by some client libraries.
// Go still has issues decoding with such a namespace, but encoding is ok.
type soapEnvelope struct {
XMLName xml.Name `xml:"soapenv:Envelope"`
Enc string `xml:"xmlns:soapenc,attr"`
Env string `xml:"xmlns:soapenv,attr"`
XSD string `xml:"xmlns:xsd,attr"`
XSI string `xml:"xmlns:xsi,attr"`
Body interface{} `xml:"soapenv:Body"`
}
// soapFault is a copy of soap.Fault, with the same changes as soapEnvelope
type soapFault struct {
XMLName xml.Name `xml:"soapenv:Fault"`
Code string `xml:"faultcode"`
String string `xml:"faultstring"`
Detail struct {
Fault types.AnyType `xml:",any,typeattr"`
} `xml:"detail"`
}
// About generates some info about the simulator.
func (s *Service) About(w http.ResponseWriter, r *http.Request) {
var about struct {
Methods []string
Types []string
}
seen := make(map[string]bool)
f := reflect.TypeOf((*soap.HasFault)(nil)).Elem()
for _, obj := range Map.objects {
kind := obj.Reference().Type
if seen[kind] {
continue
}
seen[kind] = true
about.Types = append(about.Types, kind)
t := reflect.TypeOf(obj)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if seen[m.Name] {
continue
}
seen[m.Name] = true
in := m.Type.NumIn()
if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
continue
}
if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
continue
}
about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
}
}
sort.Strings(about.Methods)
sort.Strings(about.Types)
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
_ = enc.Encode(&about)
}
// ServeSDK implements the http.Handler interface
func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
body, err := s.readAll(r.Body)
_ = r.Body.Close()
if err != nil {
log.Printf("error reading body: %s", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if Trace {
fmt.Fprintf(os.Stderr, "Request: %s\n", string(body))
}
ctx := &Context{
req: r,
res: w,
m: s.sm,
Context: context.Background(),
}
Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault
var soapBody interface{}
method, err := UnmarshalBody(body)
if err != nil {
res = serverFault(err.Error())
} else {
res = s.call(ctx, method)
}
if f := res.Fault(); f != nil {
w.WriteHeader(http.StatusInternalServerError)
// the generated method/*Body structs use the '*soap.Fault' type,
// so we need our own Body type to use the modified '*soapFault' type.
soapBody = struct {
Fault *soapFault
}{
&soapFault{
Code: f.Code,
String: f.String,
Detail: f.Detail,
},
}
} else {
w.WriteHeader(http.StatusOK)
soapBody = res
}
var out bytes.Buffer
fmt.Fprint(&out, xml.Header)
e := xml.NewEncoder(&out)
err = e.Encode(&soapEnvelope{
Enc: "http://schemas.xmlsoap.org/soap/encoding/",
Env: "http://schemas.xmlsoap.org/soap/envelope/",
XSD: "http://www.w3.org/2001/XMLSchema",
XSI: "http://www.w3.org/2001/XMLSchema-instance",
Body: soapBody,
})
if err == nil {
err = e.Flush()
}
if err != nil {
log.Printf("error encoding %s response: %s", method.Name, err)
return
}
if Trace {
fmt.Fprintf(os.Stderr, "Response: %s\n", out.String())
}
_, _ = w.Write(out.Bytes())
}
func (s *Service) findDatastore(query url.Values) (*Datastore, error) {
ctx := context.Background()
finder := find.NewFinder(s.client, false)
dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcName"))
if err != nil {
return nil, err
}
finder.SetDatacenter(dc)
ds, err := finder.DatastoreOrDefault(ctx, query.Get("dsName"))
if err != nil {
return nil, err
}
return Map.Get(ds.Reference()).(*Datastore), nil
}
const folderPrefix = "/folder/"
// ServeDatastore handler for Datastore access via /folder path.
func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
ds, ferr := s.findDatastore(r.URL.Query())
if ferr != nil {
log.Printf("failed to locate datastore with query params: %s", r.URL.RawQuery)
w.WriteHeader(http.StatusNotFound)
return
}
r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
switch r.Method {
case "POST":
_, err := os.Stat(p)
if err == nil {
// File exists
w.WriteHeader(http.StatusConflict)
return
}
// File does not exist, fallthrough to create via PUT logic
fallthrough
case "PUT":
f, err := os.Create(p)
if err != nil {
log.Printf("failed to %s '%s': %s", r.Method, p, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
defer f.Close()
_, _ = io.Copy(f, r.Body)
default:
fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
fs.ServeHTTP(w, r)
}
}
// ServiceVersions handler for the /sdk/vimServiceVersions.xml path.
func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// pyvmomi depends on this
const versions = xml.Header + `<namespaces version="1.0">
<namespace>
<name>urn:vim25</name>
<version>6.5</version>
<priorVersions>
<version>6.0</version>
<version>5.5</version>
</priorVersions>
</namespace>
</namespaces>
`
fmt.Fprint(w, versions)
}
// NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server {
mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
mux.HandleFunc(path, s.ServeSDK)
mux.HandleFunc(path+"/vimServiceVersions.xml", s.ServiceVersions)
mux.HandleFunc(folderPrefix, s.ServeDatastore)
mux.HandleFunc("/about", s.About)
// Using NewUnstartedServer() instead of NewServer(),
// for use in main.go, where Start() blocks, we can still set ServiceHostName
ts := httptest.NewUnstartedServer(mux)
u := &url.URL{
Scheme: "http",
Host: ts.Listener.Addr().String(),
Path: path,
User: url.UserPassword("user", "pass"),
}
// Redirect clients to this http server, rather than HostSystem.Name
Map.SessionManager().ServiceHostName = u.Host
if f := flag.Lookup("httptest.serve"); f != nil {
// Avoid the blocking behaviour of httptest.Server.Start() when this flag is set
_ = f.Value.Set("")
}
if s.TLS == nil {
ts.Start()
} else {
ts.TLS = s.TLS
ts.StartTLS()
u.Scheme += "s"
}
return &Server{
Server: ts,
URL: u,
}
}
// Certificate returns the TLS certificate for the Server if started with TLS enabled.
// This method will panic if TLS is not enabled for the server.
func (s *Server) Certificate() *x509.Certificate {
// By default httptest.StartTLS uses http/internal.LocalhostCert, which we can access here:
cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
return cert
}
// CertificateInfo returns Server.Certificate() as object.HostCertificateInfo
func (s *Server) CertificateInfo() *object.HostCertificateInfo {
info := new(object.HostCertificateInfo)
info.FromCertificate(s.Certificate())
return info
}
// CertificateFile returns a file name, where the file contains the PEM encoded Server.Certificate.
// The temporary file is removed when Server.Close() is called.
func (s *Server) CertificateFile() (string, error) {
if s.caFile != "" {
return s.caFile, nil
}
f, err := ioutil.TempFile("", "vcsim-")
if err != nil {
return "", err
}
defer f.Close()
s.caFile = f.Name()
cert := s.Certificate()
return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
}
// Close shuts down the server and blocks until all outstanding
// requests on this server have completed.
func (s *Server) Close() {
s.Server.Close()
if s.caFile != "" {
_ = os.Remove(s.caFile)
}
}
var (
vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
)
func defaultMapType(name string) (reflect.Type, bool) {
typ, ok := vim25MapType(name)
if !ok {
// See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
// Without this hack, the SelectSet would be all nil's
kind := strings.SplitN(name, ":", 2)
if len(kind) == 2 {
typ, ok = vim25MapType(kind[1])
}
}
return typ, ok
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) {
body := struct {
Content string `xml:",innerxml"`
}{}
req := soap.Envelope{
Body: &body,
}
err := xml.Unmarshal(data, &req)
if err != nil {
return nil, fmt.Errorf("xml.Unmarshal: %s", err)
}
decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content)))
decoder.TypeFunc = typeFunc // required to decode interface types
var start *xml.StartElement
for {
tok, derr := decoder.Token()
if derr != nil {
return nil, fmt.Errorf("decoding body: %s", err)
}
if t, ok := tok.(xml.StartElement); ok {
start = &t
break
}
}
kind := start.Name.Local
rtype, ok := typeFunc(kind)
if !ok {
return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
}
var val interface{}
if rtype != nil {
val = reflect.New(rtype).Interface()
}
err = decoder.DecodeElement(val, start)
if err != nil {
return nil, fmt.Errorf("decoding %s: %s", kind, err)
}
method := &Method{Name: kind, Body: val}
field := reflect.ValueOf(val).Elem().FieldByName("This")
method.This = field.Interface().(types.ManagedObjectReference)
return method, nil
}

View File

@@ -0,0 +1,496 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"reflect"
"testing"
"time"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func TestUnmarshal(t *testing.T) {
requests := []struct {
body interface{}
data string
}{
{
&types.RetrieveServiceContent{
This: types.ManagedObjectReference{
Type: "ServiceInstance", Value: "ServiceInstance",
},
},
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<RetrieveServiceContent xmlns="urn:vim25">
<_this type="ServiceInstance">ServiceInstance</_this>
</RetrieveServiceContent>
</Body>
</Envelope>`,
},
{
&types.Login{
This: types.ManagedObjectReference{
Type: "SessionManager",
Value: "SessionManager",
},
UserName: "root",
Password: "secret",
},
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Login xmlns="urn:vim25">
<_this type="SessionManager">SessionManager</_this>
<userName>root</userName>
<password>secret</password>
</Login>
</Body>
</Envelope>`,
},
{
&types.RetrieveProperties{
This: types.ManagedObjectReference{Type: "PropertyCollector", Value: "ha-property-collector"},
SpecSet: []types.PropertyFilterSpec{
{
DynamicData: types.DynamicData{},
PropSet: []types.PropertySpec{
{
DynamicData: types.DynamicData{},
Type: "ManagedEntity",
All: (*bool)(nil),
PathSet: []string{"name", "parent"},
},
},
ObjectSet: []types.ObjectSpec{
{
DynamicData: types.DynamicData{},
Obj: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
Skip: types.NewBool(false),
SelectSet: []types.BaseSelectionSpec{ // test decode of interface
&types.TraversalSpec{
SelectionSpec: types.SelectionSpec{
DynamicData: types.DynamicData{},
Name: "traverseParent",
},
Type: "ManagedEntity",
Path: "parent",
Skip: types.NewBool(false),
SelectSet: []types.BaseSelectionSpec{
&types.SelectionSpec{
DynamicData: types.DynamicData{},
Name: "traverseParent",
},
},
},
},
},
},
ReportMissingObjectsInResults: (*bool)(nil),
},
}},
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<RetrieveProperties xmlns="urn:vim25">
<_this type="PropertyCollector">ha-property-collector</_this>
<specSet>
<propSet>
<type>ManagedEntity</type>
<pathSet>name</pathSet>
<pathSet>parent</pathSet>
</propSet>
<objectSet>
<obj type="Folder">ha-folder-root</obj>
<skip>false</skip>
<selectSet xmlns:XMLSchema-instance="http://www.w3.org/2001/XMLSchema-instance" XMLSchema-instance:type="TraversalSpec">
<name>traverseParent</name>
<type>ManagedEntity</type>
<path>parent</path>
<skip>false</skip>
<selectSet XMLSchema-instance:type="SelectionSpec">
<name>traverseParent</name>
</selectSet>
</selectSet>
</objectSet>
</specSet>
</RetrieveProperties>
</Body>
</Envelope>`,
},
}
for i, req := range requests {
method, err := UnmarshalBody([]byte(req.data))
if err != nil {
t.Errorf("failed to decode %d (%s): %s", i, req, err)
}
if !reflect.DeepEqual(method.Body, req.body) {
t.Errorf("malformed body %d (%#v):", i, method.Body)
}
}
}
func TestUnmarshalError(t *testing.T) {
requests := []string{
"", // io.EOF
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
</MissingEndTag
</Envelope>`,
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<UnknownType xmlns="urn:vim25">
<_this type="ServiceInstance">ServiceInstance</_this>
</UnknownType>
</Body>
</Envelope>`,
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<!-- no start tag -->
</Body>
</Envelope>`,
`<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<RetrieveServiceContent xmlns="urn:vim25">
<_this type="ServiceInstance">ServiceInstance</_this>
</RetrieveServiceContent>
</Body>
</Envelope>`,
}
defer func() {
typeFunc = defaultMapType // reset
}()
ttypes := map[string]reflect.Type{
// triggers xml.Decoder.DecodeElement error
"RetrieveServiceContent": reflect.TypeOf(nil),
}
typeFunc = func(name string) (reflect.Type, bool) {
typ, ok := ttypes[name]
return typ, ok
}
for i, data := range requests {
_, err := UnmarshalBody([]byte(data))
if err != nil {
continue
}
t.Errorf("expected %d (%s) to return an error", i, data)
}
}
func TestServeHTTP(t *testing.T) {
configs := []struct {
content types.ServiceContent
folder mo.Folder
}{
{esx.ServiceContent, esx.RootFolder},
{vpx.ServiceContent, vpx.RootFolder},
}
for _, config := range configs {
s := New(NewServiceInstance(config.content, config.folder))
ts := s.NewServer()
defer ts.Close()
u := ts.URL.User
ts.URL.User = nil
ctx := context.Background()
client, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
err = client.Login(ctx, nil)
if err == nil {
t.Fatal("expected invalid login error")
}
err = client.Login(ctx, u)
if err != nil {
t.Fatal(err)
}
// Testing http client + reflect client
clients := []soap.RoundTripper{client, s.client}
for _, c := range clients {
now, err := methods.GetCurrentTime(ctx, c)
if err != nil {
t.Fatal(err)
}
if now.After(time.Now()) {
t.Fail()
}
// test the fail/Fault path
_, err = methods.QueryVMotionCompatibility(ctx, c, &types.QueryVMotionCompatibility{})
if err == nil {
t.Errorf("expected error")
}
}
err = client.Logout(ctx)
if err != nil {
t.Error(err)
}
}
}
func TestServeAbout(t *testing.T) {
ctx := context.Background()
m := VPX()
m.App = 1
m.Pod = 1
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
u := *s.URL
u.Path += "/vimServiceVersions.xml"
r, err := c.Get(u.String())
if err != nil {
t.Fatal(err)
}
_ = r.Body.Close()
u.Path = "/about"
r, err = c.Get(u.String())
if err != nil {
t.Fatal(err)
}
_ = r.Body.Close()
}
func TestServeHTTPS(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
s.TLS = new(tls.Config)
ts := s.NewServer()
defer ts.Close()
ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // silence benign "TLS handshake error" log messages
ctx := context.Background()
// insecure=true OK
_, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
// insecure=false should FAIL
_, err = govmomi.NewClient(ctx, ts.URL, false)
if err == nil {
t.Fatal("expected error")
}
uerr, ok := err.(*url.Error)
if !ok {
t.Fatalf("err type=%T", err)
}
_, ok = uerr.Err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("err type=%T", uerr.Err)
}
sinfo := ts.CertificateInfo()
// Test thumbprint validation
sc := soap.NewClient(ts.URL, false)
// Add host with thumbprint mismatch should fail
sc.SetThumbprint(ts.URL.Host, "nope")
_, err = vim25.NewClient(ctx, sc)
if err == nil {
t.Error("expected error")
}
// Add host with thumbprint match should pass
sc.SetThumbprint(ts.URL.Host, sinfo.ThumbprintSHA1)
_, err = vim25.NewClient(ctx, sc)
if err != nil {
t.Fatal(err)
}
var pinfo object.HostCertificateInfo
err = pinfo.FromURL(ts.URL, nil)
if err != nil {
t.Fatal(err)
}
if pinfo.ThumbprintSHA1 != sinfo.ThumbprintSHA1 {
t.Error("thumbprint mismatch")
}
// Test custom RootCAs list
sc = soap.NewClient(ts.URL, false)
caFile, err := ts.CertificateFile()
if err != nil {
t.Fatal(err)
}
if err = sc.SetRootCAs(caFile); err != nil {
t.Fatal(err)
}
_, err = vim25.NewClient(ctx, sc)
if err != nil {
t.Fatal(err)
}
}
type errorMarshal struct {
mo.ServiceInstance
}
func (*errorMarshal) Fault() *soap.Fault {
return nil
}
func (*errorMarshal) MarshalText() ([]byte, error) {
return nil, errors.New("time has stopped")
}
func (h *errorMarshal) CurrentTime(types.AnyType) soap.HasFault {
return h
}
type errorNoSuchMethod struct {
mo.ServiceInstance
}
func TestServeHTTPErrors(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
client, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
// test response to unimplemented method
req := &types.QueryMemoryOverhead{This: esx.HostSystem.Reference()}
_, err = methods.QueryMemoryOverhead(ctx, client.Client, req)
if _, ok := soap.ToSoapFault(err).VimFault().(types.MethodNotFound); !ok {
t.Error("expected MethodNotFound fault")
}
// unregister type, covering the ServeHTTP UnmarshalBody error path
typeFunc = func(name string) (reflect.Type, bool) {
return nil, false
}
_, err = methods.GetCurrentTime(ctx, client)
if err == nil {
t.Error("expected error")
}
typeFunc = types.TypeFunc() // reset
// cover the does not implement method error path
Map.objects[methods.ServiceInstance] = &errorNoSuchMethod{}
_, err = methods.GetCurrentTime(ctx, client)
if err == nil {
t.Error("expected error")
}
// cover the xml encode error path
Map.objects[methods.ServiceInstance] = &errorMarshal{}
_, err = methods.GetCurrentTime(ctx, client)
if err == nil {
t.Error("expected error")
}
// cover the no such object path
Map.Remove(methods.ServiceInstance)
_, err = methods.GetCurrentTime(ctx, client)
if err == nil {
t.Error("expected error")
}
// verify we properly marshal the fault
fault := soap.ToSoapFault(err).VimFault()
f, ok := fault.(types.ManagedObjectNotFound)
if !ok {
t.Fatalf("fault=%#v", fault)
}
if f.Obj != methods.ServiceInstance {
t.Errorf("obj=%#v", f.Obj)
}
// cover the method not supported path
res, err := http.Get(ts.URL.String())
if err != nil {
log.Fatal(err)
}
if res.StatusCode != http.StatusMethodNotAllowed {
t.Errorf("expected status %d, got %s", http.StatusMethodNotAllowed, res.Status)
}
// cover the ioutil.ReadAll error path
s.readAll = func(io.Reader) ([]byte, error) {
return nil, io.ErrShortBuffer
}
res, err = http.Post(ts.URL.String(), "none", nil)
if err != nil {
log.Fatal(err)
}
if res.StatusCode != http.StatusBadRequest {
t.Errorf("expected status %d, got %s", http.StatusBadRequest, res.Status)
}
}

68
vendor/github.com/vmware/govmomi/simulator/snapshot.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type VirtualMachineSnapshot struct {
mo.VirtualMachineSnapshot
}
func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Task) soap.HasFault {
task := CreateTask(v, "removeSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
Map.Remove(req.This)
vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() {
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
vm.Snapshot.CurrentSnapshot = parent
}
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
})
return nil, nil
})
return &methods.RemoveSnapshot_TaskBody{
Res: &types.RemoveSnapshot_TaskResponse{
Returnval: task.Run(),
},
}
}
func (v *VirtualMachineSnapshot) RevertToSnapshotTask(req *types.RevertToSnapshot_Task) soap.HasFault {
task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() { vm.Snapshot.CurrentSnapshot = &v.Self })
return nil, nil
})
return &methods.RevertToSnapshot_TaskBody{
Res: &types.RevertToSnapshot_TaskResponse{
Returnval: task.Run(),
},
}
}

102
vendor/github.com/vmware/govmomi/simulator/task.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
const vTaskSuffix = "_Task" // vmomi suffix
const sTaskSuffix = "Task" // simulator suffix (avoiding golint warning)
type Task struct {
mo.Task
Execute func(*Task) (types.AnyType, types.BaseMethodFault)
}
func NewTask(runner TaskRunner) *Task {
ref := runner.Reference()
name := reflect.TypeOf(runner).Elem().Name()
name = strings.Replace(name, "VM", "Vm", 1) // "VM" for the type to make go-lint happy, but "Vm" for the vmodl ID
return CreateTask(ref, name, runner.Run)
}
func CreateTask(e mo.Reference, name string, run func(*Task) (types.AnyType, types.BaseMethodFault)) *Task {
ref := e.Reference()
id := name
if strings.HasSuffix(id, sTaskSuffix) {
id = id[:len(id)-len(sTaskSuffix)]
name = id + vTaskSuffix
}
task := &Task{
Execute: run,
}
Map.Put(task)
task.Info.Key = task.Self.Value
task.Info.Task = task.Self
task.Info.Name = ucFirst(name)
task.Info.DescriptionId = fmt.Sprintf("%s.%s", ref.Type, id)
task.Info.Entity = &ref
task.Info.EntityName = ref.Value
task.Info.QueueTime = time.Now()
task.Info.State = types.TaskInfoStateQueued
return task
}
type TaskRunner interface {
mo.Reference
Run(*Task) (types.AnyType, types.BaseMethodFault)
}
func (t *Task) Run() types.ManagedObjectReference {
now := time.Now()
t.Info.StartTime = &now
t.Info.State = types.TaskInfoStateRunning
res, err := t.Execute(t)
now = time.Now()
t.Info.CompleteTime = &now
if err != nil {
t.Info.State = types.TaskInfoStateError
t.Info.Error = &types.LocalizedMethodFault{
Fault: err,
LocalizedMessage: fmt.Sprintf("%T", err),
}
} else {
t.Info.Result = res
t.Info.State = types.TaskInfoStateSuccess
}
return t.Self
}

View File

@@ -0,0 +1,52 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
var recentTaskMax = 200 // the VC limit
type TaskManager struct {
mo.TaskManager
}
func NewTaskManager(ref types.ManagedObjectReference) object.Reference {
s := &TaskManager{}
s.Self = ref
Map.AddHandler(s)
return s
}
func (m *TaskManager) PutObject(obj mo.Reference) {
ref := obj.Reference()
if ref.Type != "Task" {
return
}
m.RecentTask = append(m.RecentTask, ref)
if len(m.RecentTask) > recentTaskMax {
m.RecentTask = m.RecentTask[1:]
}
}
func (m *TaskManager) RemoveObject(_ types.ManagedObjectReference) {
}

View File

@@ -0,0 +1,47 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/simulator/esx"
)
func TestTaskManagerRecent(t *testing.T) {
m := ESX()
m.Datastore = 0
m.Machine = 0
err := m.Create()
if err != nil {
t.Fatal(err)
}
recentTaskMax = 5
tm := Map.Get(*esx.ServiceContent.TaskManager).(*TaskManager)
tm.RecentTask = nil
for i := 0; i < recentTaskMax+2; i++ {
CreateTask(esx.RootFolder, "noop", nil)
if len(tm.RecentTask) > recentTaskMax {
t.Errorf("too many tasks %d > %d", len(tm.RecentTask), recentTaskMax)
}
}
}

View File

@@ -0,0 +1,65 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"testing"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type addWaterTask struct {
*mo.Folder
fault types.BaseMethodFault
}
func (a *addWaterTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
return nil, a.fault
}
func TestNewTask(t *testing.T) {
f := &mo.Folder{}
Map.NewEntity(f)
add := &addWaterTask{f, nil}
task := NewTask(add)
info := &task.Info
if info.Name != "AddWater_Task" {
t.Errorf("name=%s", info.Name)
}
if info.DescriptionId != "Folder.addWater" {
t.Errorf("descriptionId=%s", info.DescriptionId)
}
task.Run()
if info.State != types.TaskInfoStateSuccess {
t.Fail()
}
add.fault = &types.ManagedObjectNotFound{}
task.Run()
if info.State != types.TaskInfoStateError {
t.Fail()
}
}

View File

@@ -0,0 +1,110 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
var DefaultUserGroup = []*types.UserSearchResult{
{FullName: "root", Group: true, Principal: "root"},
{FullName: "root", Group: false, Principal: "root"},
{FullName: "administrator", Group: false, Principal: "admin"},
}
type UserDirectory struct {
mo.UserDirectory
userGroup []*types.UserSearchResult
}
func NewUserDirectory(ref types.ManagedObjectReference) object.Reference {
u := &UserDirectory{}
u.Self = ref
u.userGroup = DefaultUserGroup
return u
}
func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.HasFault {
compare := compareFunc(req.SearchStr, req.ExactMatch)
res := u.search(req.FindUsers, req.FindGroups, compare)
body := &methods.RetrieveUserGroupsBody{
Res: &types.RetrieveUserGroupsResponse{
Returnval: res,
},
}
return body
}
func (u *UserDirectory) search(findUsers, findGroups bool, compare func(string) bool) (res []types.BaseUserSearchResult) {
for _, ug := range u.userGroup {
if findUsers && !ug.Group || findGroups && ug.Group {
if compare(ug.Principal) {
res = append(res, ug)
}
}
}
return res
}
func (u *UserDirectory) addUser(id string) {
u.add(id, false)
}
func (u *UserDirectory) removeUser(id string) {
u.remove(id, false)
}
func (u *UserDirectory) add(id string, group bool) {
user := &types.UserSearchResult{
FullName: id,
Group: group,
Principal: id,
}
u.userGroup = append(u.userGroup, user)
}
func (u *UserDirectory) remove(id string, group bool) {
for i, ug := range u.userGroup {
if ug.Group == group && ug.Principal == id {
u.userGroup = append(u.userGroup[:i], u.userGroup[i+1:]...)
return
}
}
}
func compareFunc(compared string, exactly bool) func(string) bool {
return func(s string) bool {
if exactly {
return s == compared
}
return strings.Contains(strings.ToLower(s), strings.ToLower(compared))
}
}

View File

@@ -0,0 +1,140 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
func TestUserDirectory(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
var tests = []struct {
findUsers bool
findGroups bool
}{
{true, true},
{true, false},
{false, true},
{false, false},
}
ref := *c.ServiceContent.UserDirectory
for _, test := range tests {
req := types.RetrieveUserGroups{
This: ref,
SearchStr: "root",
ExactMatch: true,
FindUsers: test.findUsers,
FindGroups: test.findGroups,
}
result, err := methods.RetrieveUserGroups(ctx, c.Client, &req)
if err != nil {
t.Fatal(err)
}
expectedSize := 0
if test.findGroups {
expectedSize++
}
if test.findUsers {
expectedSize++
}
if len(result.Returnval) != expectedSize {
t.Fatalf("expect search result for root is %d; got %d", expectedSize, len(result.Returnval))
}
for _, u := range result.Returnval {
if u.GetUserSearchResult().Principal != "root" {
t.Fatalf("expect principal to be root; got %s", u.GetUserSearchResult().Principal)
}
if !test.findGroups && u.GetUserSearchResult().Group {
t.Fatal("expect search result is non-group; got group")
}
if !test.findUsers && !u.GetUserSearchResult().Group {
t.Fatal("expect search result is non-user; got user")
}
}
}
}
func TestUserDirectoryExactlyMatch(t *testing.T) {
s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder))
ts := s.NewServer()
defer ts.Close()
ctx := context.Background()
c, err := govmomi.NewClient(ctx, ts.URL, true)
if err != nil {
t.Fatal(err)
}
var tests = []struct {
exactly bool
search string
expectedSize int
}{
{true, "root", 2},
{false, "ROO", 2},
}
ref := *c.ServiceContent.UserDirectory
for _, test := range tests {
req := types.RetrieveUserGroups{
This: ref,
SearchStr: test.search,
ExactMatch: test.exactly,
FindUsers: true,
FindGroups: true,
}
result, err := methods.RetrieveUserGroups(ctx, c.Client, &req)
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
if len(result.Returnval) != test.expectedSize {
t.Fatalf("expect result contains %d results; got %d", test.expectedSize, len(result.Returnval))
}
}
}

View File

@@ -0,0 +1,194 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"reflect"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type ViewManager struct {
mo.ViewManager
entities map[string]bool
}
var entities = []struct {
Type reflect.Type
Container bool
}{
{reflect.TypeOf((*mo.ManagedEntity)(nil)).Elem(), true},
{reflect.TypeOf((*mo.Folder)(nil)).Elem(), true},
{reflect.TypeOf((*mo.StoragePod)(nil)).Elem(), true},
{reflect.TypeOf((*mo.Datacenter)(nil)).Elem(), true},
{reflect.TypeOf((*mo.ComputeResource)(nil)).Elem(), true},
{reflect.TypeOf((*mo.ClusterComputeResource)(nil)).Elem(), true},
{reflect.TypeOf((*mo.HostSystem)(nil)).Elem(), true},
{reflect.TypeOf((*mo.ResourcePool)(nil)).Elem(), true},
{reflect.TypeOf((*mo.VirtualApp)(nil)).Elem(), true},
{reflect.TypeOf((*mo.VirtualMachine)(nil)).Elem(), false},
{reflect.TypeOf((*mo.Datastore)(nil)).Elem(), false},
{reflect.TypeOf((*mo.Network)(nil)).Elem(), false},
{reflect.TypeOf((*mo.OpaqueNetwork)(nil)).Elem(), false},
{reflect.TypeOf((*mo.DistributedVirtualPortgroup)(nil)).Elem(), false},
{reflect.TypeOf((*mo.DistributedVirtualSwitch)(nil)).Elem(), false},
{reflect.TypeOf((*mo.VmwareDistributedVirtualSwitch)(nil)).Elem(), false},
}
func NewViewManager(ref types.ManagedObjectReference) object.Reference {
s := &ViewManager{
entities: make(map[string]bool),
}
s.Self = ref
for _, e := range entities {
s.entities[e.Type.Name()] = e.Container
}
return s
}
func destroyView(ref types.ManagedObjectReference) soap.HasFault {
m := Map.ViewManager()
RemoveReference(&m.ViewList, ref)
return &methods.DestroyViewBody{
Res: &types.DestroyViewResponse{},
}
}
func (m *ViewManager) CreateContainerView(ctx *Context, req *types.CreateContainerView) soap.HasFault {
body := &methods.CreateContainerViewBody{}
root := Map.Get(req.Container)
if root == nil {
body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Container})
return body
}
if m.entities[root.Reference().Type] != true {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "container"})
return body
}
container := &ContainerView{
mo.ContainerView{
Container: root.Reference(),
Recursive: req.Recursive,
Type: req.Type,
},
make(map[string]bool),
}
for _, ctype := range container.Type {
if _, ok := m.entities[ctype]; !ok {
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "type"})
return body
}
container.types[ctype] = true
for _, e := range entities {
// Check for embedded types
if f, ok := e.Type.FieldByName(ctype); ok && f.Anonymous {
container.types[e.Type.Name()] = true
}
}
}
ctx.Session.Put(container)
m.ViewList = append(m.ViewList, container.Reference())
body.Res = &types.CreateContainerViewResponse{
Returnval: container.Self,
}
seen := make(map[types.ManagedObjectReference]bool)
container.add(root, seen)
return body
}
type ContainerView struct {
mo.ContainerView
types map[string]bool
}
func (v *ContainerView) DestroyView(c *types.DestroyView) soap.HasFault {
return destroyView(c.This)
}
func (v *ContainerView) include(o types.ManagedObjectReference) bool {
if len(v.types) == 0 {
return true
}
return v.types[o.Type]
}
func walk(root mo.Reference, f func(child types.ManagedObjectReference)) {
var children []types.ManagedObjectReference
switch e := root.(type) {
case *Datacenter:
children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder}
case *Folder:
children = e.ChildEntity
case *mo.ComputeResource:
children = e.Host
children = append(children, *e.ResourcePool)
case *ClusterComputeResource:
children = e.Host
children = append(children, *e.ResourcePool)
case *ResourcePool:
children = e.ResourcePool.ResourcePool
children = append(children, e.Vm...)
case *VirtualApp:
children = e.ResourcePool.ResourcePool
children = append(children, e.Vm...)
case *HostSystem:
children = e.Vm
}
for _, child := range children {
f(child)
}
}
func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectReference]bool) {
walk(root, func(child types.ManagedObjectReference) {
if v.include(child) {
if seen[child] == false {
seen[child] = true
v.View = append(v.View, child)
}
}
if v.Recursive {
v.add(Map.Get(child), seen)
}
})
}

View File

@@ -0,0 +1,144 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/view"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
func TestContainerViewVPX(t *testing.T) {
ctx := context.Background()
m := VPX()
m.Datacenter = 3
m.Folder = 2
m.Pool = 1
m.App = 1
m.Pod = 1
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
v := view.NewManager(c.Client)
root := c.Client.ServiceContent.RootFolder
// test container type validation
_, err = v.CreateContainerView(ctx, v.Reference(), nil, false)
if err == nil {
t.Fatal("expected error")
}
// test container value validation
_, err = v.CreateContainerView(ctx, types.ManagedObjectReference{Value: "enoent"}, nil, false)
if err == nil {
t.Fatal("expected error")
}
// test types validation
_, err = v.CreateContainerView(ctx, root, []string{"enoent"}, false)
if err == nil {
t.Fatal("expected error")
}
vapp := object.NewVirtualApp(c.Client, Map.Any("VirtualApp").Reference())
count := m.Count()
tests := []struct {
root types.ManagedObjectReference
recurse bool
kinds []string
expect int
}{
{root, false, nil, m.Datacenter - m.Folder + m.Folder},
{root, true, nil, count.total - 1}, // not including the root Folder
{root, true, []string{"ManagedEntity"}, count.total - 1}, // not including the root Folder
{root, true, []string{"Folder"}, count.Folder + count.Pod - 1}, // not including the root Folder
{root, false, []string{"HostSystem"}, 0},
{root, true, []string{"HostSystem"}, count.Host},
{root, false, []string{"Datacenter"}, m.Datacenter - m.Folder},
{root, true, []string{"Datacenter"}, count.Datacenter},
{root, true, []string{"Datastore"}, count.Datastore},
{root, true, []string{"VirtualMachine"}, count.Machine},
{root, true, []string{"ResourcePool"}, count.Pool + count.App},
{root, true, []string{"VirtualApp"}, count.App},
{vapp.Reference(), true, []string{"VirtualMachine"}, m.Machine},
{root, true, []string{"ClusterComputeResource"}, count.Cluster},
{root, true, []string{"ComputeResource"}, (m.Cluster + m.Host) * m.Datacenter},
{root, true, []string{"DistributedVirtualSwitch"}, count.Datacenter},
{root, true, []string{"DistributedVirtualPortgroup"}, count.Portgroup},
{root, true, []string{"Network"}, count.Portgroup + m.Datacenter},
{root, true, []string{"OpaqueNetwork"}, 0},
{root, true, []string{"StoragePod"}, m.Pod * m.Datacenter},
}
pc := property.DefaultCollector(c.Client)
mvm := Map.ViewManager()
for i, test := range tests {
cv, err := v.CreateContainerView(ctx, test.root, test.kinds, test.recurse)
if err != nil {
t.Fatal(err)
}
if len(mvm.ViewList) != 1 {
t.Errorf("ViewList=%s", mvm.ViewList)
}
var mcv mo.ContainerView
err = pc.RetrieveOne(ctx, cv.Reference(), nil, &mcv)
if err != nil {
t.Fatal(err)
}
n := len(mcv.View)
if n != test.expect {
t.Errorf("%d: %d != %d", i, n, test.expect)
}
err = cv.Destroy(ctx)
if err != nil {
t.Fatal(err)
}
if len(mvm.ViewList) != 0 {
t.Errorf("ViewList=%s", mvm.ViewList)
}
}
}

View File

@@ -0,0 +1,212 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"os"
"strings"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type VirtualDiskManager struct {
mo.VirtualDiskManager
}
func NewVirtualDiskManager(ref types.ManagedObjectReference) object.Reference {
m := &VirtualDiskManager{}
m.Self = ref
return m
}
func (m *VirtualDiskManager) names(name string) []string {
return []string{
strings.Replace(name, ".vmdk", "-flat.vmdk", 1),
name,
}
}
func (m *VirtualDiskManager) createVirtualDisk(op types.VirtualDeviceConfigSpecFileOperation, req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
fm := Map.FileManager()
file, fault := fm.resolve(req.Datacenter, req.Name)
if fault != nil {
return fault
}
shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
shouldExist := op == ""
for _, name := range m.names(file) {
_, err := os.Stat(name)
if err == nil {
if shouldExist {
return nil
}
if shouldReplace {
if err = os.Truncate(file, 0); err != nil {
return fm.fault(name, err, new(types.CannotCreateFile))
}
return nil
}
return fm.fault(name, nil, new(types.FileAlreadyExists))
} else if shouldExist {
return fm.fault(name, nil, new(types.FileNotFound))
}
f, err := os.Create(name)
if err != nil {
return fm.fault(name, err, new(types.CannotCreateFile))
}
_ = f.Close()
}
return nil
}
func (m *VirtualDiskManager) CreateVirtualDiskTask(req *types.CreateVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
return nil, m.createVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, req)
})
return &methods.CreateVirtualDisk_TaskBody{
Res: &types.CreateVirtualDisk_TaskResponse{
Returnval: task.Run(),
},
}
}
func (m *VirtualDiskManager) DeleteVirtualDiskTask(req *types.DeleteVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "deleteVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
fm := Map.FileManager()
for _, name := range m.names(req.Name) {
err := fm.deleteDatastoreFile(&types.DeleteDatastoreFile_Task{
Name: name,
Datacenter: req.Datacenter,
})
if err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.DeleteVirtualDisk_TaskBody{
Res: &types.DeleteVirtualDisk_TaskResponse{
Returnval: task.Run(),
},
}
}
func (m *VirtualDiskManager) MoveVirtualDiskTask(req *types.MoveVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "moveVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
fm := Map.FileManager()
dest := m.names(req.DestName)
for i, name := range m.names(req.SourceName) {
err := fm.moveDatastoreFile(&types.MoveDatastoreFile_Task{
SourceName: name,
SourceDatacenter: req.SourceDatacenter,
DestinationName: dest[i],
DestinationDatacenter: req.DestDatacenter,
Force: req.Force,
})
if err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.MoveVirtualDisk_TaskBody{
Res: &types.MoveVirtualDisk_TaskResponse{
Returnval: task.Run(),
},
}
}
func (m *VirtualDiskManager) CopyVirtualDiskTask(req *types.CopyVirtualDisk_Task) soap.HasFault {
task := CreateTask(m, "copyVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
if req.DestSpec != nil {
if Map.IsVPX() {
return nil, new(types.NotImplemented)
}
}
fm := Map.FileManager()
dest := m.names(req.DestName)
for i, name := range m.names(req.SourceName) {
err := fm.copyDatastoreFile(&types.CopyDatastoreFile_Task{
SourceName: name,
SourceDatacenter: req.SourceDatacenter,
DestinationName: dest[i],
DestinationDatacenter: req.DestDatacenter,
Force: req.Force,
})
if err != nil {
return nil, err
}
}
return nil, nil
})
return &methods.CopyVirtualDisk_TaskBody{
Res: &types.CopyVirtualDisk_TaskResponse{
Returnval: task.Run(),
},
}
}
func (m *VirtualDiskManager) QueryVirtualDiskUuid(req *types.QueryVirtualDiskUuid) soap.HasFault {
body := new(methods.QueryVirtualDiskUuidBody)
fm := Map.FileManager()
file, fault := fm.resolve(req.Datacenter, req.Name)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
_, err := os.Stat(file)
if err != nil {
fault = fm.fault(file, err, new(types.CannotAccessFile))
body.Fault_ = Fault("", fault)
return body
}
body.Res = &types.QueryVirtualDiskUuidResponse{
Returnval: uuid.NewSHA1(uuid.NameSpaceOID, []byte(file)).String(),
}
return body
}

View File

@@ -0,0 +1,163 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"path"
"strings"
"testing"
"github.com/google/uuid"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
func TestVirtualDiskManager(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
dm := object.NewVirtualDiskManager(c.Client)
fm := object.NewFileManager(c.Client)
spec := &types.FileBackedVirtualDiskSpec{
VirtualDiskSpec: types.VirtualDiskSpec{
AdapterType: string(types.VirtualDiskAdapterTypeLsiLogic),
DiskType: string(types.VirtualDiskTypeThin),
},
CapacityKb: 1024 * 1024,
}
name := "[LocalDS_0] disks/disk1.vmdk"
for i, fail := range []bool{true, false, true} {
task, err := dm.CreateVirtualDisk(ctx, name, nil, spec)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Error("expected error") // disk1 already exists
}
} else {
if err != nil {
t.Error(err)
}
}
if i == 0 {
err = fm.MakeDirectory(ctx, path.Dir(name), nil, true)
if err != nil {
t.Fatal(err)
}
}
}
qname := name
for _, fail := range []bool{false, true} {
id, err := dm.QueryVirtualDiskUuid(ctx, qname, nil)
if fail {
if err == nil {
t.Error("expected error")
}
} else {
if err != nil {
t.Error(err)
}
_, err = uuid.Parse(id)
if err != nil {
t.Error(err)
}
}
qname += "-enoent"
}
old := name
name = strings.Replace(old, "disk1", "disk2", 1)
for _, fail := range []bool{false, true} {
task, err := dm.MoveVirtualDisk(ctx, old, nil, name, nil, false)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Error("expected error") // disk1 no longer exists
}
} else {
if err != nil {
t.Error(err)
}
}
}
for _, fail := range []bool{false, true} {
task, err := dm.CopyVirtualDisk(ctx, name, nil, old, nil, &types.VirtualDiskSpec{}, false)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Error("expected error") // disk1 exists again
}
} else {
if err != nil {
t.Error(err)
}
}
}
for _, fail := range []bool{false, true} {
task, err := dm.DeleteVirtualDisk(ctx, name, nil)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if fail {
if err == nil {
t.Error("expected error") // disk2 no longer exists
}
} else {
if err != nil {
t.Error(err)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,806 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"context"
"fmt"
"math/rand"
"testing"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/task"
"github.com/vmware/govmomi/vim25/types"
)
func TestCreateVm(t *testing.T) {
ctx := context.Background()
for _, model := range []*Model{ESX(), VPX()} {
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
p := property.DefaultCollector(c.Client)
finder := find.NewFinder(c.Client, false)
dc, err := finder.DefaultDatacenter(ctx)
if err != nil {
t.Fatal(err)
}
finder.SetDatacenter(dc)
folders, err := dc.Folders(ctx)
if err != nil {
t.Fatal(err)
}
ds, err := finder.DefaultDatastore(ctx)
if err != nil {
t.Fatal(err)
}
hosts, err := finder.HostSystemList(ctx, "*/*")
if err != nil {
t.Fatal(err)
}
nhosts := len(hosts)
host := hosts[rand.Intn(nhosts)]
pool, err := host.ResourcePool(ctx)
if err != nil {
t.Fatal(err)
}
if nhosts == 1 {
// test the default path against the ESX model
host = nil
}
vmFolder := folders.VmFolder
var vmx string
spec := types.VirtualMachineConfigSpec{
// Note: real ESX allows the VM to be created without a GuestId,
// but will power on will fail.
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
}
steps := []func(){
func() {
spec.Name = "test"
vmx = fmt.Sprintf("%s/%s.vmx", spec.Name, spec.Name)
},
func() {
spec.Files = &types.VirtualMachineFileInfo{
VmPathName: fmt.Sprintf("[%s] %s", ds.Name(), vmx),
}
},
}
// expecting CreateVM to fail until all steps are taken
for _, step := range steps {
task, cerr := vmFolder.CreateVM(ctx, spec, pool, host)
if cerr != nil {
t.Fatal(err)
}
cerr = task.Wait(ctx)
if cerr == nil {
t.Error("expected error")
}
step()
}
task, err := vmFolder.CreateVM(ctx, spec, pool, host)
if err != nil {
t.Fatal(err)
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
t.Fatal(err)
}
// Test that datastore files were created
_, err = ds.Stat(ctx, vmx)
if err != nil {
t.Fatal(err)
}
vm := object.NewVirtualMachine(c.Client, info.Result.(types.ManagedObjectReference))
name, err := vm.ObjectName(ctx)
if err != nil {
t.Fatal(err)
}
if name != spec.Name {
t.Errorf("name=%s", name)
}
_, err = vm.Device(ctx)
if err != nil {
t.Fatal(err)
}
recreate := func(context.Context) (*object.Task, error) {
return vmFolder.CreateVM(ctx, spec, pool, nil)
}
ops := []struct {
method func(context.Context) (*object.Task, error)
state types.VirtualMachinePowerState
fail bool
}{
// Powered off by default
{nil, types.VirtualMachinePowerStatePoweredOff, false},
// Create with same .vmx path should fail
{recreate, "", true},
// Off -> On == ok
{vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false},
// On -> On == fail
{vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, true},
// On -> Off == ok
{vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false},
// Off -> Off == fail
{vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, true},
// Off -> On == ok
{vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false},
// Destroy == fail (power is On)
{vm.Destroy, types.VirtualMachinePowerStatePoweredOn, true},
// On -> Off == ok
{vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false},
// Destroy == ok (power is Off)
{vm.Destroy, "", false},
}
for i, op := range ops {
if op.method != nil {
task, err = op.method(ctx)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if op.fail {
if err == nil {
t.Errorf("%d: expected error", i)
}
} else {
if err != nil {
t.Errorf("%d: %s", i, err)
}
}
}
if len(op.state) != 0 {
state, err := vm.PowerState(ctx)
if err != nil {
t.Fatal(err)
}
if state != op.state {
t.Errorf("state=%s", state)
}
err = property.Wait(ctx, p, vm.Reference(), []string{object.PropRuntimePowerState}, func(pc []types.PropertyChange) bool {
for _, c := range pc {
switch v := c.Val.(type) {
case types.VirtualMachinePowerState:
if v != op.state {
t.Errorf("state=%s", v)
}
default:
t.Errorf("unexpected type %T", v)
}
}
return false
})
}
}
// Test that datastore files were removed
_, err = ds.Stat(ctx, vmx)
if err == nil {
t.Error("expected error")
}
}
}
func TestReconfigVmDevice(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
finder := find.NewFinder(c.Client, false)
finder.SetDatacenter(object.NewDatacenter(c.Client, esx.Datacenter.Reference()))
vms, err := finder.VirtualMachineList(ctx, "*")
if err != nil {
t.Fatal(err)
}
vm := vms[0]
device, err := vm.Device(ctx)
if err != nil {
t.Fatal(err)
}
// verify default device list
_, err = device.FindIDEController("")
if err != nil {
t.Fatal(err)
}
// default list of devices + 1 NIC + 1 SCSI controller + 1 CDROM + 1 disk created by the Model
mdevices := len(esx.VirtualDevice) + 4
if len(device) != mdevices {
t.Errorf("expected %d devices, got %d", mdevices, len(device))
}
d := device.FindByKey(esx.EthernetCard.Key)
err = vm.AddDevice(ctx, d)
if _, ok := err.(task.Error).Fault().(*types.InvalidDeviceSpec); !ok {
t.Fatalf("err=%v", err)
}
err = vm.RemoveDevice(ctx, false, d)
if err != nil {
t.Fatal(err)
}
device, err = vm.Device(ctx)
if err != nil {
t.Fatal(err)
}
if len(device) != mdevices-1 {
t.Error("device list mismatch")
}
// cover the path where the simulator assigns a UnitNumber
d.GetVirtualDevice().UnitNumber = nil
// cover the path where the simulator assigns a Key
d.GetVirtualDevice().Key = -1
err = vm.AddDevice(ctx, d)
if err != nil {
t.Fatal(err)
}
device, err = vm.Device(ctx)
if err != nil {
t.Fatal(err)
}
if len(device) != mdevices {
t.Error("device list mismatch")
}
disks := device.SelectByType((*types.VirtualDisk)(nil))
for _, d := range disks {
disk := d.(*types.VirtualDisk)
info := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
if info.Datastore.Type == "" || info.Datastore.Value == "" {
t.Errorf("invalid datastore for %s", device.Name(d))
}
// RemoveDevice and keep the file backing
if err = vm.RemoveDevice(ctx, true, d); err != nil {
t.Error(err)
}
if err = vm.AddDevice(ctx, d); err == nil {
t.Error("expected FileExists error")
}
// Need FileOperation=="" to add an existing disk, see object.VirtualMachine.configureDevice
disk.CapacityInKB = 0
if err = vm.AddDevice(ctx, d); err != nil {
t.Error(err)
}
// RemoveDevice and delete the file backing
if err = vm.RemoveDevice(ctx, false, d); err != nil {
t.Error(err)
}
if err = vm.AddDevice(ctx, d); err == nil {
t.Error("expected FileNotFound error")
}
}
}
func TestReconfigVm(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
vmm := Map.Any("VirtualMachine").(*VirtualMachine)
vm := object.NewVirtualMachine(c.Client, vmm.Reference())
tests := []struct {
fail bool
spec types.VirtualMachineConfigSpec
}{
{
true, types.VirtualMachineConfigSpec{
CpuAllocation: &types.ResourceAllocationInfo{Reservation: types.NewInt64(-1)},
},
},
{
false, types.VirtualMachineConfigSpec{
CpuAllocation: &types.ResourceAllocationInfo{Reservation: types.NewInt64(100)},
},
},
{
true, types.VirtualMachineConfigSpec{
GuestId: "enoent",
},
},
{
false, types.VirtualMachineConfigSpec{
GuestId: string(GuestID[0]),
},
},
{
false, types.VirtualMachineConfigSpec{
NestedHVEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
CpuHotAddEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
CpuHotRemoveEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
GuestAutoLockEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
MemoryHotAddEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
MemoryReservationLockedToMax: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
MessageBusTunnelEnabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
NpivTemporaryDisabled: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
NpivOnNonRdmDisks: types.NewBool(true),
},
},
{
false, types.VirtualMachineConfigSpec{
ConsolePreferences: &types.VirtualMachineConsolePreferences{
PowerOnWhenOpened: types.NewBool(true),
},
},
},
{
false, types.VirtualMachineConfigSpec{
CpuAffinity: &types.VirtualMachineAffinityInfo{
AffinitySet: []int32{1},
},
},
},
{
false, types.VirtualMachineConfigSpec{
CpuAllocation: &types.ResourceAllocationInfo{
Reservation: types.NewInt64(100),
},
},
},
{
false, types.VirtualMachineConfigSpec{
MemoryAffinity: &types.VirtualMachineAffinityInfo{
AffinitySet: []int32{1},
},
},
},
{
false, types.VirtualMachineConfigSpec{
MemoryAllocation: &types.ResourceAllocationInfo{
Reservation: types.NewInt64(100),
},
},
},
{
false, types.VirtualMachineConfigSpec{
LatencySensitivity: &types.LatencySensitivity{
Sensitivity: 1,
},
},
},
}
for i, test := range tests {
rtask, _ := vm.Reconfigure(ctx, test.spec)
err := rtask.Wait(ctx)
if test.fail {
if err == nil {
t.Errorf("%d: expected failure", i)
}
} else {
if err != nil {
t.Errorf("unexpected failure: %s", err)
}
}
}
// Verify ReConfig actually works
if *vmm.Config.NestedHVEnabled != true {
t.Errorf("vm.Config.NestedHVEnabled expected true; got false")
}
if *vmm.Config.CpuHotAddEnabled != true {
t.Errorf("vm.Config.CpuHotAddEnabled expected true; got false")
}
if *vmm.Config.CpuHotRemoveEnabled != true {
t.Errorf("vm.Config.CpuHotRemoveEnabled expected true; got false")
}
if *vmm.Config.GuestAutoLockEnabled != true {
t.Errorf("vm.Config.GuestAutoLockEnabled expected true; got false")
}
if *vmm.Config.MemoryHotAddEnabled != true {
t.Errorf("vm.Config.MemoryHotAddEnabled expected true; got false")
}
if *vmm.Config.MemoryReservationLockedToMax != true {
t.Errorf("vm.Config.MemoryReservationLockedToMax expected true; got false")
}
if *vmm.Config.MessageBusTunnelEnabled != true {
t.Errorf("vm.Config.MessageBusTunnelEnabled expected true; got false")
}
if *vmm.Config.NpivTemporaryDisabled != true {
t.Errorf("vm.Config.NpivTemporaryDisabled expected true; got false")
}
if *vmm.Config.NpivOnNonRdmDisks != true {
t.Errorf("vm.Config.NpivOnNonRdmDisks expected true; got false")
}
if *vmm.Config.ConsolePreferences.PowerOnWhenOpened != true {
t.Errorf("vm.Config.ConsolePreferences.PowerOnWhenOpened expected true; got false")
}
if vmm.Config.CpuAffinity.AffinitySet[0] != int32(1) {
t.Errorf("vm.Config.CpuAffinity.AffinitySet[0] expected %d; got %d",
1, vmm.Config.CpuAffinity.AffinitySet[0])
}
if vmm.Config.MemoryAffinity.AffinitySet[0] != int32(1) {
t.Errorf("vm.Config.CpuAffinity.AffinitySet[0] expected %d; got %d",
1, vmm.Config.CpuAffinity.AffinitySet[0])
}
if *vmm.Config.CpuAllocation.Reservation != 100 {
t.Errorf("vm.Config.CpuAllocation.Reservation expected %d; got %d",
100, *vmm.Config.CpuAllocation.Reservation)
}
if *vmm.Config.MemoryAllocation.Reservation != 100 {
t.Errorf("vm.Config.MemoryAllocation.Reservation expected %d; got %d",
100, *vmm.Config.MemoryAllocation.Reservation)
}
if vmm.Config.LatencySensitivity.Sensitivity != int32(1) {
t.Errorf("vmm.Config.LatencySensitivity.Sensitivity expected %d; got %d",
1, vmm.Config.LatencySensitivity.Sensitivity)
}
}
func TestCreateVmWithDevices(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c := m.Service.client
folder := object.NewFolder(c, esx.Datacenter.VmFolder)
pool := object.NewResourcePool(c, esx.ResourcePool.Self)
// different set of devices from Model.Create's
var devices object.VirtualDeviceList
ide, _ := devices.CreateIDEController()
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
scsi, _ := devices.CreateSCSIController("scsi")
disk := &types.VirtualDisk{
CapacityInKB: 1024,
VirtualDevice: types.VirtualDevice{
Backing: new(types.VirtualDiskFlatVer2BackingInfo), // Leave fields empty to test defaults
},
}
devices.AssignController(disk, scsi.(*types.VirtualLsiLogicController))
devices = append(devices, ide, cdrom, scsi, disk)
create, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
spec := types.VirtualMachineConfigSpec{
Name: "foo",
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
DeviceChange: create,
Files: &types.VirtualMachineFileInfo{
VmPathName: "[LocalDS_0] foo/foo.vmx",
},
}
ctask, _ := folder.CreateVM(ctx, spec, pool, nil)
info, err := ctask.WaitForResult(ctx, nil)
if err != nil {
t.Fatal(err)
}
vm := Map.Get(info.Result.(types.ManagedObjectReference)).(*VirtualMachine)
expect := len(esx.VirtualDevice) + len(devices)
ndevice := len(vm.Config.Hardware.Device)
if expect != ndevice {
t.Errorf("expected %d, got %d", expect, ndevice)
}
}
func TestShutdownGuest(t *testing.T) {
// use the default vm for testing
ctx := context.Background()
for _, model := range []*Model{ESX(), VPX()} {
defer model.Remove()
err := model.Create()
if err != nil {
t.Fatal(err)
}
s := model.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
vm := object.NewVirtualMachine(c.Client, Map.Any("VirtualMachine").Reference())
// shutdown the vm
err = vm.ShutdownGuest(ctx)
if err != nil {
t.Fatal(err)
}
// state should be poweroff
state, err := vm.PowerState(ctx)
if err != nil {
t.Fatal(err)
}
if state != types.VirtualMachinePowerStatePoweredOff {
t.Errorf("state=%s", state)
}
// shutdown a poweroff vm should fail
err = vm.ShutdownGuest(ctx)
if err == nil {
t.Error("expected error: InvalidPowerState")
}
}
}
func TestVmSnapshot(t *testing.T) {
ctx := context.Background()
m := ESX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
vm := object.NewVirtualMachine(c.Client, Map.Any("VirtualMachine").Reference())
task, err := vm.CreateSnapshot(ctx, "root", "description", true, true)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
task, err = vm.CreateSnapshot(ctx, "child", "description", true, true)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
_, err = vm.FindSnapshot(ctx, "child")
if err != nil {
t.Fatal(err)
}
task, err = vm.RevertToCurrentSnapshot(ctx, true)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
task, err = vm.RevertToSnapshot(ctx, "root", true)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
task, err = vm.RemoveSnapshot(ctx, "child", false, nil)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
_, err = vm.FindSnapshot(ctx, "child")
if err == nil {
t.Fatal("child should be removed")
}
task, err = vm.RemoveAllSnapshot(ctx, nil)
if err != nil {
t.Fatal(err)
}
err = task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
_, err = vm.FindSnapshot(ctx, "root")
if err == nil {
t.Fatal("all snapshots should be removed")
}
}
func TestVmMarkAsTemplate(t *testing.T) {
ctx := context.Background()
m := VPX()
defer m.Remove()
err := m.Create()
if err != nil {
t.Fatal(err)
}
s := m.Service.NewServer()
defer s.Close()
c, err := govmomi.NewClient(ctx, s.URL, true)
if err != nil {
t.Fatal(err)
}
vm := object.NewVirtualMachine(c.Client, Map.Any("VirtualMachine").Reference())
err = vm.MarkAsTemplate(ctx)
if err == nil {
t.Fatal("cannot create template for a powered on vm")
}
task, err := vm.PowerOff(ctx)
if err != nil {
t.Fatal(err)
}
task.Wait(ctx)
err = vm.MarkAsTemplate(ctx)
if err != nil {
t.Fatal(err)
}
_, err = vm.PowerOn(ctx)
if err == nil {
t.Fatal("cannot PowerOn a template")
}
}

20
vendor/github.com/vmware/govmomi/simulator/vpx/doc.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package vpx contains SOAP responses from a vCenter server, captured using `govc ... -dump`.
*/
package vpx

View File

@@ -0,0 +1,64 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package vpx
import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
var RootFolder = mo.Folder{
ManagedEntity: mo.ManagedEntity{
ExtensibleManagedObject: mo.ExtensibleManagedObject{
Self: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
Value: nil,
AvailableField: nil,
},
Parent: (*types.ManagedObjectReference)(nil),
CustomValue: nil,
OverallStatus: "green",
ConfigStatus: "green",
ConfigIssue: nil,
EffectiveRole: []int32{-1},
Permission: []types.Permission{
{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
Principal: "VSPHERE.LOCAL\\Administrator",
Group: false,
RoleId: -1,
Propagate: true,
},
{
DynamicData: types.DynamicData{},
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
Principal: "VSPHERE.LOCAL\\Administrators",
Group: true,
RoleId: -1,
Propagate: true,
},
},
Name: "Datacenters",
DisabledMethod: nil,
RecentTask: nil,
DeclaredAlarmState: nil,
AlarmActionsEnabled: (*bool)(nil),
Tag: nil,
},
ChildType: []string{"Folder", "Datacenter"},
ChildEntity: nil,
}

View File

@@ -0,0 +1,86 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package vpx
import "github.com/vmware/govmomi/vim25/types"
// ServiceContent is the default template for the ServiceInstance content property.
// Capture method:
// govc object.collect -s -dump - content
var ServiceContent = types.ServiceContent{
RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "propertyCollector"},
ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"},
About: types.AboutInfo{
Name: "VMware vCenter Server",
FullName: "VMware vCenter Server 6.5.0 build-5973321",
Vendor: "VMware, Inc.",
Version: "6.5.0",
Build: "5973321",
LocaleVersion: "INTL",
LocaleBuild: "000",
OsType: "linux-x64",
ProductLineId: "vpx",
ApiType: "VirtualCenter",
ApiVersion: "6.5",
InstanceUuid: "dbed6e0c-bd88-4ef6-b594-21283e1c677f",
LicenseProductName: "VMware VirtualCenter Server",
LicenseProductVersion: "6.0",
},
Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "VpxSettings"},
UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "UserDirectory"},
SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "SessionManager"},
AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "AuthorizationManager"},
ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ServiceMgr"},
PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "PerfMgr"},
ScheduledTaskManager: &types.ManagedObjectReference{Type: "ScheduledTaskManager", Value: "ScheduledTaskManager"},
AlarmManager: &types.ManagedObjectReference{Type: "AlarmManager", Value: "AlarmManager"},
EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "EventManager"},
TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "TaskManager"},
ExtensionManager: &types.ManagedObjectReference{Type: "ExtensionManager", Value: "ExtensionManager"},
CustomizationSpecManager: &types.ManagedObjectReference{Type: "CustomizationSpecManager", Value: "CustomizationSpecManager"},
CustomFieldsManager: &types.ManagedObjectReference{Type: "CustomFieldsManager", Value: "CustomFieldsManager"},
AccountManager: (*types.ManagedObjectReference)(nil),
DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "DiagMgr"},
LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "LicenseManager"},
SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "SearchIndex"},
FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "FileManager"},
DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "DatastoreNamespaceManager"},
VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "virtualDiskManager"},
VirtualizationManager: (*types.ManagedObjectReference)(nil),
SnmpSystem: &types.ManagedObjectReference{Type: "HostSnmpSystem", Value: "SnmpSystem"},
VmProvisioningChecker: &types.ManagedObjectReference{Type: "VirtualMachineProvisioningChecker", Value: "ProvChecker"},
VmCompatibilityChecker: &types.ManagedObjectReference{Type: "VirtualMachineCompatibilityChecker", Value: "CompatChecker"},
OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "OvfManager"},
IpPoolManager: &types.ManagedObjectReference{Type: "IpPoolManager", Value: "IpPoolManager"},
DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "DVSManager"},
HostProfileManager: &types.ManagedObjectReference{Type: "HostProfileManager", Value: "HostProfileManager"},
ClusterProfileManager: &types.ManagedObjectReference{Type: "ClusterProfileManager", Value: "ClusterProfileManager"},
ComplianceManager: &types.ManagedObjectReference{Type: "ProfileComplianceManager", Value: "MoComplianceManager"},
LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "LocalizationManager"},
StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "StorageResourceManager"},
GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "guestOperationsManager"},
OverheadMemoryManager: &types.ManagedObjectReference{Type: "OverheadMemoryManager", Value: "OverheadMemoryManager"},
CertificateManager: &types.ManagedObjectReference{Type: "CertificateManager", Value: "certificateManager"},
IoFilterManager: &types.ManagedObjectReference{Type: "IoFilterManager", Value: "IoFilterManager"},
VStorageObjectManager: &types.ManagedObjectReference{Type: "VcenterVStorageObjectManager", Value: "VStorageObjectManager"},
HostSpecManager: &types.ManagedObjectReference{Type: "HostSpecificationManager", Value: "HostSpecificationManager"},
CryptoManager: &types.ManagedObjectReference{Type: "CryptoManagerKmip", Value: "CryptoManager"},
HealthUpdateManager: &types.ManagedObjectReference{Type: "HealthUpdateManager", Value: "HealthUpdateManager"},
FailoverClusterConfigurator: &types.ManagedObjectReference{Type: "FailoverClusterConfigurator", Value: "FailoverClusterConfigurator"},
FailoverClusterManager: &types.ManagedObjectReference{Type: "FailoverClusterManager", Value: "FailoverClusterManager"},
}

View File

@@ -0,0 +1,72 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package vpx
import "github.com/vmware/govmomi/vim25/types"
// Setting is captured from VC's ServiceContent.OptionManager.setting
var Setting = []types.BaseOptionValue{
// This list is currently pruned to include sso options only with sso.enabled set to false
&types.OptionValue{
Key: "config.vpxd.sso.sts.uri",
Value: "https://127.0.0.1/sts/STSService/vsphere.local",
},
&types.OptionValue{
Key: "config.vpxd.sso.solutionUser.privateKey",
Value: "/etc/vmware-vpx/ssl/vcsoluser.key",
},
&types.OptionValue{
Key: "config.vpxd.sso.solutionUser.name",
Value: "vpxd-b643d01c-928f-469b-96a5-d571d762a78e@vsphere.local",
},
&types.OptionValue{
Key: "config.vpxd.sso.solutionUser.certificate",
Value: "/etc/vmware-vpx/ssl/vcsoluser.crt",
},
&types.OptionValue{
Key: "config.vpxd.sso.groupcheck.uri",
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
},
&types.OptionValue{
Key: "config.vpxd.sso.enabled",
Value: "false",
},
&types.OptionValue{
Key: "config.vpxd.sso.default.isGroup",
Value: "false",
},
&types.OptionValue{
Key: "config.vpxd.sso.default.admin",
Value: "Administrator@vsphere.local",
},
&types.OptionValue{
Key: "config.vpxd.sso.admin.uri",
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
},
&types.OptionValue{
Key: "event.batchsize",
Value: int32(2000),
},
&types.OptionValue{
Key: "event.maxAge",
Value: int32(30),
},
&types.OptionValue{
Key: "event.maxAgeEnabled",
Value: bool(true),
},
}