pagination of vcenter queries
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -11,8 +11,10 @@ import (
|
|||||||
|
|
||||||
"github.com/vmware/govmomi"
|
"github.com/vmware/govmomi"
|
||||||
"github.com/vmware/govmomi/event"
|
"github.com/vmware/govmomi/event"
|
||||||
|
"github.com/vmware/govmomi/fault"
|
||||||
"github.com/vmware/govmomi/find"
|
"github.com/vmware/govmomi/find"
|
||||||
"github.com/vmware/govmomi/object"
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/property"
|
||||||
"github.com/vmware/govmomi/view"
|
"github.com/vmware/govmomi/view"
|
||||||
"github.com/vmware/govmomi/vim25/mo"
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
"github.com/vmware/govmomi/vim25/soap"
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
@@ -40,6 +42,124 @@ type VmProperties struct {
|
|||||||
|
|
||||||
var clientUserAgent = "vCTP"
|
var clientUserAgent = "vCTP"
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPropertyPageSize int32 = 500
|
||||||
|
)
|
||||||
|
|
||||||
|
func normalizeKinds(kind []string) []string {
|
||||||
|
if len(kind) > 0 {
|
||||||
|
return kind
|
||||||
|
}
|
||||||
|
return []string{"ManagedEntity"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizePageSize(pageSize int32) int32 {
|
||||||
|
if pageSize <= 0 {
|
||||||
|
return defaultPropertyPageSize
|
||||||
|
}
|
||||||
|
return pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPropertySpecs(kind []string, props []string) []types.PropertySpec {
|
||||||
|
kinds := normalizeKinds(kind)
|
||||||
|
specs := make([]types.PropertySpec, 0, len(kinds))
|
||||||
|
for _, t := range kinds {
|
||||||
|
spec := types.PropertySpec{Type: t}
|
||||||
|
if len(props) == 0 {
|
||||||
|
spec.All = types.NewBool(true)
|
||||||
|
} else {
|
||||||
|
spec.PathSet = props
|
||||||
|
}
|
||||||
|
specs = append(specs, spec)
|
||||||
|
}
|
||||||
|
return specs
|
||||||
|
}
|
||||||
|
|
||||||
|
func refKey(ref types.ManagedObjectReference) string {
|
||||||
|
return ref.Type + ":" + ref.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vcenter) retrieveContainerViewPaged(ctx context.Context, cv *view.ContainerView, kind []string, props []string, dst any, pageSize int32) error {
|
||||||
|
pc := property.DefaultCollector(v.client.Client)
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: []types.ObjectSpec{
|
||||||
|
{
|
||||||
|
Obj: cv.Reference(),
|
||||||
|
Skip: types.NewBool(true),
|
||||||
|
SelectSet: []types.BaseSelectionSpec{
|
||||||
|
&types.TraversalSpec{
|
||||||
|
Type: cv.Reference().Type,
|
||||||
|
Path: "view",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PropSet: buildPropertySpecs(kind, props),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := pc.RetrieveProperties(ctx, req, normalizePageSize(pageSize))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, ok := dst.(*[]types.ObjectContent); ok {
|
||||||
|
*d = res.Returnval
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mo.LoadObjectContent(res.Returnval, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vcenter) retrieveByRefsPaged(ctx context.Context, refs []types.ManagedObjectReference, props []string, dst any, pageSize int32) error {
|
||||||
|
if len(refs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
kindSet := make(map[string]struct{}, len(refs))
|
||||||
|
kinds := make([]string, 0, len(refs))
|
||||||
|
for _, ref := range refs {
|
||||||
|
if _, ok := kindSet[ref.Type]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kindSet[ref.Type] = struct{}{}
|
||||||
|
kinds = append(kinds, ref.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
objectSet := make([]types.ObjectSpec, 0, len(refs))
|
||||||
|
for _, ref := range refs {
|
||||||
|
objectSet = append(objectSet, types.ObjectSpec{
|
||||||
|
Obj: ref,
|
||||||
|
Skip: types.NewBool(false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pc := property.DefaultCollector(v.client.Client)
|
||||||
|
req := types.RetrieveProperties{
|
||||||
|
SpecSet: []types.PropertyFilterSpec{
|
||||||
|
{
|
||||||
|
ObjectSet: objectSet,
|
||||||
|
PropSet: buildPropertySpecs(kinds, props),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := pc.RetrieveProperties(ctx, req, normalizePageSize(pageSize))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, ok := dst.(*[]types.ObjectContent); ok {
|
||||||
|
*d = res.Returnval
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mo.LoadObjectContent(res.Returnval, dst)
|
||||||
|
}
|
||||||
|
|
||||||
// SetUserAgent customizes the User-Agent used when talking to vCenter.
|
// SetUserAgent customizes the User-Agent used when talking to vCenter.
|
||||||
func SetUserAgent(ua string) {
|
func SetUserAgent(ua string) {
|
||||||
if strings.TrimSpace(ua) != "" {
|
if strings.TrimSpace(ua) != "" {
|
||||||
@@ -127,46 +247,25 @@ func (v *Vcenter) Logout(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) GetAllVmReferences() ([]*object.VirtualMachine, error) {
|
func (v *Vcenter) GetAllVmReferences() ([]*object.VirtualMachine, error) {
|
||||||
var results []*object.VirtualMachine
|
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
m := view.NewManager(v.client.Client)
|
m := view.NewManager(v.client.Client)
|
||||||
|
cv, err := m.CreateContainerView(v.ctx, v.client.ServiceContent.RootFolder, []string{"VirtualMachine"}, true)
|
||||||
vms, err := m.CreateContainerView(v.ctx, v.client.ServiceContent.RootFolder, []string{"VirtualMachine"}, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to create VM container view: %w", err)
|
||||||
}
|
}
|
||||||
defer vms.Destroy(v.ctx)
|
defer cv.Destroy(v.ctx)
|
||||||
|
|
||||||
// List all datacenters
|
var content []types.ObjectContent
|
||||||
datacenters, err := finder.DatacenterList(v.ctx, "*")
|
if err := v.retrieveContainerViewPaged(v.ctx, cv, []string{"VirtualMachine"}, []string{"name"}, &content, defaultPropertyPageSize); err != nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("failed to retrieve VM references: %w", err)
|
||||||
return nil, fmt.Errorf("failed to list datacenters: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dc := range datacenters {
|
|
||||||
v.Logger.Debug("Getting VMs in", "datacenter", dc.Name())
|
|
||||||
// Set the current datacenter
|
|
||||||
finder.SetDatacenter(dc)
|
|
||||||
|
|
||||||
// Get the list of all virtual machines in the current datacenter
|
|
||||||
vms, err := finder.VirtualMachineList(v.ctx, "*")
|
|
||||||
if err != nil {
|
|
||||||
v.Logger.Error("Failed to list VMs in", "datacenter", dc.Name(), "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, vm := range vms {
|
|
||||||
//vmRef := vm.Reference()
|
|
||||||
//v.Logger.Debug("result", "vm", vm, "MoRef", vmRef, "path", vm.InventoryPath)
|
|
||||||
results = append(results, vm)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results := make([]*object.VirtualMachine, 0, len(content))
|
||||||
|
for _, c := range content {
|
||||||
|
results = append(results, object.NewVirtualMachine(v.client.Client, c.Obj))
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Logger.Debug("Found VM references", "count", len(results))
|
v.Logger.Debug("Found VM references", "count", len(results))
|
||||||
|
return results, nil
|
||||||
return results, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllVMsWithProps returns all VMs with the properties needed for snapshotting in a single property-collector call.
|
// GetAllVMsWithProps returns all VMs with the properties needed for snapshotting in a single property-collector call.
|
||||||
@@ -191,7 +290,7 @@ func (v *Vcenter) GetAllVMsWithProps() ([]mo.VirtualMachine, error) {
|
|||||||
"runtime.host",
|
"runtime.host",
|
||||||
"resourcePool",
|
"resourcePool",
|
||||||
}
|
}
|
||||||
if err := cv.Retrieve(v.ctx, []string{"VirtualMachine"}, props, &vms); err != nil {
|
if err := v.retrieveContainerViewPaged(v.ctx, cv, []string{"VirtualMachine"}, props, &vms, defaultPropertyPageSize); err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve VMs: %w", err)
|
return nil, fmt.Errorf("failed to retrieve VMs: %w", err)
|
||||||
}
|
}
|
||||||
return vms, nil
|
return vms, nil
|
||||||
@@ -413,29 +512,55 @@ func (v *Vcenter) BuildHostLookup() (map[string]HostLookup, error) {
|
|||||||
v.Logger.Warn("failed to list hosts for datacenter", "datacenter", dc.Name(), "error", err)
|
v.Logger.Warn("failed to list hosts for datacenter", "datacenter", dc.Name(), "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if len(hosts) == 0 {
|
||||||
for _, host := range hosts {
|
|
||||||
ref := host.Reference()
|
|
||||||
var moHost mo.HostSystem
|
|
||||||
if err := v.client.RetrieveOne(v.ctx, ref, []string{"parent"}, &moHost); err != nil {
|
|
||||||
v.Logger.Warn("failed to retrieve host info", "host", host.Name(), "error", err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterName := ""
|
hostRefs := make([]types.ManagedObjectReference, 0, len(hosts))
|
||||||
if moHost.Parent != nil {
|
for _, host := range hosts {
|
||||||
if cached, ok := clusterCache[moHost.Parent.Value]; ok {
|
hostRefs = append(hostRefs, host.Reference())
|
||||||
clusterName = cached
|
}
|
||||||
|
|
||||||
|
var moHosts []mo.HostSystem
|
||||||
|
if err := v.retrieveByRefsPaged(v.ctx, hostRefs, []string{"parent"}, &moHosts, defaultPropertyPageSize); err != nil {
|
||||||
|
v.Logger.Warn("failed to retrieve host parents for datacenter", "datacenter", dc.Name(), "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parentRefs := make([]types.ManagedObjectReference, 0, len(moHosts))
|
||||||
|
parentSeen := make(map[string]struct{})
|
||||||
|
for _, host := range moHosts {
|
||||||
|
if host.Parent == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := refKey(*host.Parent)
|
||||||
|
if _, cached := clusterCache[key]; cached {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, seen := parentSeen[key]; seen {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parentSeen[key] = struct{}{}
|
||||||
|
parentRefs = append(parentRefs, *host.Parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parentRefs) > 0 {
|
||||||
|
var computes []mo.ComputeResource
|
||||||
|
if err := v.retrieveByRefsPaged(v.ctx, parentRefs, []string{"name"}, &computes, defaultPropertyPageSize); err != nil {
|
||||||
|
v.Logger.Warn("failed to retrieve cluster names for datacenter", "datacenter", dc.Name(), "error", err)
|
||||||
} else {
|
} else {
|
||||||
var moCompute mo.ComputeResource
|
for _, compute := range computes {
|
||||||
if err := v.client.RetrieveOne(v.ctx, *moHost.Parent, []string{"name"}, &moCompute); err == nil {
|
clusterCache[refKey(compute.Reference())] = compute.Name
|
||||||
clusterName = moCompute.Name
|
|
||||||
clusterCache[moHost.Parent.Value] = clusterName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup[ref.Value] = HostLookup{
|
for _, host := range moHosts {
|
||||||
|
clusterName := ""
|
||||||
|
if host.Parent != nil {
|
||||||
|
clusterName = clusterCache[refKey(*host.Parent)]
|
||||||
|
}
|
||||||
|
lookup[host.Reference().Value] = HostLookup{
|
||||||
Cluster: clusterName,
|
Cluster: clusterName,
|
||||||
Datacenter: dc.Name(),
|
Datacenter: dc.Name(),
|
||||||
}
|
}
|
||||||
@@ -454,7 +579,7 @@ func (v *Vcenter) BuildFolderPathLookup() (FolderLookup, error) {
|
|||||||
defer folders.Destroy(v.ctx)
|
defer folders.Destroy(v.ctx)
|
||||||
|
|
||||||
var results []mo.Folder
|
var results []mo.Folder
|
||||||
if err := folders.Retrieve(v.ctx, []string{"Folder"}, []string{"name", "parent"}, &results); err != nil {
|
if err := v.retrieveContainerViewPaged(v.ctx, folders, []string{"Folder"}, []string{"name", "parent"}, &results, defaultPropertyPageSize); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,109 +634,43 @@ func (v *Vcenter) GetVMFolderPathFromLookup(vm mo.VirtualMachine, lookup FolderL
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) ConvertObjToMoVM(vmObj *object.VirtualMachine) (*mo.VirtualMachine, error) {
|
func (v *Vcenter) ConvertObjToMoVM(vmObj *object.VirtualMachine) (*mo.VirtualMachine, error) {
|
||||||
// Use the InventoryPath to extract the datacenter name and VM path
|
if vmObj == nil {
|
||||||
inventoryPath := vmObj.InventoryPath
|
return nil, fmt.Errorf("vm object is nil")
|
||||||
parts := strings.SplitN(inventoryPath, "/", 3)
|
|
||||||
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return nil, fmt.Errorf("invalid InventoryPath: %s", inventoryPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first part of the path is the datacenter name
|
|
||||||
datacenterName := parts[1]
|
|
||||||
|
|
||||||
// Finder to search for datacenter and VM
|
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
// Find the specific datacenter by name
|
|
||||||
datacenter, err := finder.Datacenter(v.ctx, fmt.Sprintf("/%s", datacenterName))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to find datacenter %s: %w", datacenterName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the found datacenter in the finder
|
|
||||||
finder.SetDatacenter(datacenter)
|
|
||||||
|
|
||||||
// Now retrieve the VM using its ManagedObjectReference
|
|
||||||
vmRef := vmObj.Reference()
|
vmRef := vmObj.Reference()
|
||||||
|
|
||||||
// Retrieve the full mo.VirtualMachine object for the reference
|
|
||||||
var moVM mo.VirtualMachine
|
var moVM mo.VirtualMachine
|
||||||
err = v.client.RetrieveOne(v.ctx, vmRef, nil, &moVM)
|
err := v.client.RetrieveOne(v.ctx, vmRef, []string{"name", "config"}, &moVM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve VM %s in datacenter %s: %w", vmObj.Name(), datacenterName, err)
|
return nil, fmt.Errorf("failed to retrieve VM %s: %w", vmObj.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the found mo.VirtualMachine object
|
|
||||||
//v.Logger.Debug("Found VM in datacenter", "vm_name", moVM.Name, "dc_name", datacenterName)
|
|
||||||
return &moVM, nil
|
return &moVM, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) ConvertObjToMoHost(hostObj *object.HostSystem) (*mo.HostSystem, error) {
|
func (v *Vcenter) ConvertObjToMoHost(hostObj *object.HostSystem) (*mo.HostSystem, error) {
|
||||||
// Use the InventoryPath to extract the datacenter name and Host path
|
if hostObj == nil {
|
||||||
inventoryPath := hostObj.InventoryPath
|
return nil, fmt.Errorf("host object is nil")
|
||||||
parts := strings.SplitN(inventoryPath, "/", 3)
|
|
||||||
v.Logger.Debug("inventory path", "parts", parts)
|
|
||||||
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return nil, fmt.Errorf("invalid InventoryPath: %s", inventoryPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first part of the path is the datacenter name
|
|
||||||
datacenterName := parts[1]
|
|
||||||
|
|
||||||
// Finder to search for datacenter and VM
|
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
// Find the specific datacenter by name
|
|
||||||
datacenter, err := finder.Datacenter(v.ctx, fmt.Sprintf("/%s", datacenterName))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to find datacenter %s: %w", datacenterName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the found datacenter in the finder
|
|
||||||
finder.SetDatacenter(datacenter)
|
|
||||||
|
|
||||||
// Now retrieve the VM using its ManagedObjectReference
|
|
||||||
hostRef := hostObj.Reference()
|
hostRef := hostObj.Reference()
|
||||||
|
|
||||||
// Retrieve the full mo.HostSystem object for the reference
|
|
||||||
var moHost mo.HostSystem
|
var moHost mo.HostSystem
|
||||||
err = v.client.RetrieveOne(v.ctx, hostRef, nil, &moHost)
|
err := v.client.RetrieveOne(v.ctx, hostRef, []string{"name", "parent"}, &moHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve Host %s in datacenter %s: %w", hostObj.Name(), datacenterName, err)
|
return nil, fmt.Errorf("failed to retrieve host %s: %w", hostObj.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the found mo.HostSystem object
|
v.Logger.Debug("found host", "host_name", moHost.Name)
|
||||||
v.Logger.Debug("Found Host in datacenter", "host_name", moHost.Name, "dc_name", datacenterName)
|
|
||||||
return &moHost, nil
|
return &moHost, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) GetHostSystemObject(hostRef types.ManagedObjectReference) (*mo.HostSystem, error) {
|
func (v *Vcenter) GetHostSystemObject(hostRef types.ManagedObjectReference) (*mo.HostSystem, error) {
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
// List all datacenters
|
|
||||||
datacenters, err := finder.DatacenterList(v.ctx, "*")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to list datacenters: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dc := range datacenters {
|
|
||||||
v.Logger.Debug("Checking dc for host", "name", dc.Name(), "hostRef", hostRef.String())
|
|
||||||
// Set the current datacenter
|
|
||||||
finder.SetDatacenter(dc)
|
|
||||||
|
|
||||||
var hs mo.HostSystem
|
var hs mo.HostSystem
|
||||||
err := v.client.RetrieveOne(v.ctx, hostRef, nil, &hs)
|
if err := v.client.RetrieveOne(v.ctx, hostRef, []string{"name", "parent"}, &hs); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
}
|
||||||
v.Logger.Debug("Found hostsystem", "name", hs.Name)
|
v.Logger.Debug("found hostsystem", "name", hs.Name)
|
||||||
return &hs, nil
|
return &hs, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to find the cluster or compute resource from a host reference
|
// Function to find the cluster or compute resource from a host reference
|
||||||
@@ -620,87 +679,43 @@ func (v *Vcenter) GetClusterFromHost(hostRef *types.ManagedObjectReference) (str
|
|||||||
v.Logger.Warn("nil hostRef passed to GetClusterFromHost")
|
v.Logger.Warn("nil hostRef passed to GetClusterFromHost")
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
// Get the host object
|
var host mo.HostSystem
|
||||||
host, err := v.GetHostSystemObject(*hostRef)
|
err := v.client.RetrieveOne(v.ctx, *hostRef, []string{"parent"}, &host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Logger.Error("cant get host", "error", err)
|
return "", fmt.Errorf("failed to retrieve host parent: %w", err)
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
if host == nil {
|
|
||||||
v.Logger.Warn("host lookup returned nil", "host_ref", hostRef)
|
if host.Parent == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if host.Parent.Type != "ClusterComputeResource" && host.Parent.Type != "ComputeResource" {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Logger.Debug("host parent", "parent", host.Parent)
|
|
||||||
|
|
||||||
if host.Parent != nil && host.Parent.Type == "ClusterComputeResource" {
|
|
||||||
// Retrieve properties of the compute resource
|
|
||||||
var moCompute mo.ComputeResource
|
var moCompute mo.ComputeResource
|
||||||
err = v.client.RetrieveOne(v.ctx, *host.Parent, nil, &moCompute)
|
if err := v.client.RetrieveOne(v.ctx, *host.Parent, []string{"name"}, &moCompute); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve compute resource: %w", err)
|
return "", fmt.Errorf("failed to retrieve compute resource: %w", err)
|
||||||
}
|
}
|
||||||
v.Logger.Debug("VM is on host in cluster/compute resource", "name", moCompute.Name)
|
v.Logger.Debug("vm host parent compute resource resolved", "name", moCompute.Name, "host_ref", hostRef.Value)
|
||||||
return moCompute.Name, nil
|
return moCompute.Name, nil
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to determine the datacenter a VM belongs to
|
// Function to determine the datacenter a VM belongs to
|
||||||
func (v *Vcenter) GetDatacenterForVM(vm mo.VirtualMachine) (string, error) {
|
func (v *Vcenter) GetDatacenterForVM(vm mo.VirtualMachine) (string, error) {
|
||||||
// Start with the VM's parent reference
|
entities, err := mo.Ancestors(v.ctx, v.client.Client, v.client.ServiceContent.PropertyCollector, vm.Reference())
|
||||||
ref := vm.Reference()
|
|
||||||
|
|
||||||
// Traverse the inventory hierarchy upwards to find the datacenter
|
|
||||||
for {
|
|
||||||
// Get the parent reference of the current object
|
|
||||||
parentRef, err := v.getParent(ref)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get parent object: %w", err)
|
return "", fmt.Errorf("failed to retrieve VM ancestors: %w", err)
|
||||||
}
|
}
|
||||||
|
for _, entity := range entities {
|
||||||
// If we get a nil parent reference, it means we've hit the root without finding the datacenter
|
if entity.Self.Type != "Datacenter" {
|
||||||
if parentRef == nil {
|
continue
|
||||||
return "", fmt.Errorf("failed to find datacenter for VM")
|
|
||||||
}
|
}
|
||||||
|
if entity.Name == "" {
|
||||||
// Check if the parent is a Datacenter
|
return "", fmt.Errorf("datacenter ancestor found without name for VM %s", vm.Reference().Value)
|
||||||
switch parentRef.Type {
|
|
||||||
case "Datacenter":
|
|
||||||
// If we found a Datacenter, retrieve its properties
|
|
||||||
datacenter := object.NewDatacenter(v.client.Client, *parentRef)
|
|
||||||
var moDC mo.Datacenter
|
|
||||||
err = v.client.RetrieveOne(v.ctx, datacenter.Reference(), nil, &moDC)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve datacenter: %w", err)
|
|
||||||
}
|
}
|
||||||
|
return entity.Name, nil
|
||||||
//log.Printf("VM is in datacenter: %s", moDC.Name)
|
|
||||||
v.Logger.Debug("VM datacenter found", "vm_name", vm.Name, "dc_name", moDC.Name)
|
|
||||||
return moDC.Name, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Continue traversing upwards if not a Datacenter
|
|
||||||
ref = *parentRef
|
|
||||||
}
|
}
|
||||||
}
|
return "", fmt.Errorf("failed to find datacenter for VM %s", vm.Reference().Value)
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to get the parent ManagedObjectReference of a given object
|
|
||||||
func (v *Vcenter) getParent(ref types.ManagedObjectReference) (*types.ManagedObjectReference, error) {
|
|
||||||
// Retrieve the object's properties
|
|
||||||
var obj mo.ManagedEntity
|
|
||||||
err := v.client.RetrieveOne(v.ctx, ref, []string{"parent"}, &obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to retrieve parent of object: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the parent reference
|
|
||||||
if obj.Parent != nil {
|
|
||||||
return obj.Parent, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) FindVMByName(vmName string) ([]mo.VirtualMachine, error) {
|
func (v *Vcenter) FindVMByName(vmName string) ([]mo.VirtualMachine, error) {
|
||||||
@@ -714,7 +729,7 @@ func (v *Vcenter) FindVMByName(vmName string) ([]mo.VirtualMachine, error) {
|
|||||||
|
|
||||||
var matchingVMs []mo.VirtualMachine
|
var matchingVMs []mo.VirtualMachine
|
||||||
|
|
||||||
err = vms.Retrieve(v.ctx, []string{"VirtualMachine"}, []string{"name"}, &matchingVMs)
|
err = v.retrieveContainerViewPaged(v.ctx, vms, []string{"VirtualMachine"}, []string{"name"}, &matchingVMs, defaultPropertyPageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -734,93 +749,39 @@ func (v *Vcenter) FindVMByName(vmName string) ([]mo.VirtualMachine, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) FindVMByID(vmID string) (*VmProperties, error) {
|
func (v *Vcenter) FindVMByID(vmID string) (*VmProperties, error) {
|
||||||
|
|
||||||
v.Logger.Debug("searching for vm id", "vm_id", vmID)
|
v.Logger.Debug("searching for vm id", "vm_id", vmID)
|
||||||
|
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
// List all datacenters
|
|
||||||
datacenters, err := finder.DatacenterList(v.ctx, "*")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to list datacenters: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dc := range datacenters {
|
|
||||||
// Set the current datacenter
|
|
||||||
finder.SetDatacenter(dc)
|
|
||||||
|
|
||||||
// Create a ManagedObjectReference for the VM
|
|
||||||
vmRef := types.ManagedObjectReference{
|
vmRef := types.ManagedObjectReference{
|
||||||
Type: "VirtualMachine",
|
Type: "VirtualMachine",
|
||||||
Value: vmID,
|
Value: vmID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find the VM by ID in the current datacenter
|
|
||||||
//vm, err := finder.ObjectReference(v.ctx, vmRef)
|
|
||||||
|
|
||||||
var vm mo.VirtualMachine
|
var vm mo.VirtualMachine
|
||||||
err := v.client.RetrieveOne(v.ctx, vmRef, []string{"config", "name"}, &vm)
|
if err := v.client.RetrieveOne(v.ctx, vmRef, []string{"config", "name"}, &vm); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve VM %s: %w", vmID, err)
|
||||||
if err == nil {
|
|
||||||
return &VmProperties{
|
|
||||||
//Datacenter: dc.Name(),
|
|
||||||
Vm: vm,
|
|
||||||
}, nil
|
|
||||||
} else if _, ok := err.(*find.NotFoundError); !ok {
|
|
||||||
// If the error is not a NotFoundError, return it
|
|
||||||
//return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err)
|
|
||||||
v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_name", dc.Name())
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("failed to retrieve VM: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("VM with ID %s not found in any datacenter", vmID)
|
return &VmProperties{Vm: vm}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*mo.VirtualMachine, error) {
|
func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*mo.VirtualMachine, error) {
|
||||||
var err error
|
if dcID != "" {
|
||||||
//v.Logger.Debug("searching for vm id", "vm_id", vmID, "datacenter_id", dcID)
|
v.Logger.Debug("searching for vm id in datacenter context", "vm_id", vmID, "datacenter_id", dcID)
|
||||||
|
|
||||||
finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
// Create a ManagedObjectReference for the datacenter
|
|
||||||
dcRef := types.ManagedObjectReference{
|
|
||||||
Type: "Datacenter",
|
|
||||||
Value: dcID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the reference to a Datacenter object
|
|
||||||
datacenter := object.NewDatacenter(v.client.Client, dcRef)
|
|
||||||
if datacenter == nil {
|
|
||||||
return nil, fmt.Errorf("Datacenter with id %s not found", dcID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use finder.SetDatacenter to set the datacenter
|
|
||||||
finder.SetDatacenter(datacenter)
|
|
||||||
|
|
||||||
// Create a ManagedObjectReference for the VM
|
|
||||||
vmRef := types.ManagedObjectReference{
|
vmRef := types.ManagedObjectReference{
|
||||||
Type: "VirtualMachine",
|
Type: "VirtualMachine",
|
||||||
Value: vmID,
|
Value: vmID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var vm mo.VirtualMachine
|
var vm mo.VirtualMachine
|
||||||
//err := v.client.RetrieveOne(v.ctx, vmRef, []string{"config", "name"}, &vm)
|
props := []string{"name", "config", "runtime.powerState", "runtime.host", "resourcePool", "parent"}
|
||||||
err = v.client.RetrieveOne(v.ctx, vmRef, nil, &vm)
|
if err := v.client.RetrieveOne(v.ctx, vmRef, props, &vm); err != nil {
|
||||||
if err == nil {
|
if fault.Is(err, &types.ManagedObjectNotFound{}) {
|
||||||
//v.Logger.Debug("Found VM")
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to retrieve VM %s: %w", vmID, err)
|
||||||
|
}
|
||||||
|
|
||||||
return &vm, nil
|
return &vm, nil
|
||||||
|
|
||||||
} else if _, ok := err.(*find.NotFoundError); !ok {
|
|
||||||
// If the error is not a NotFoundError, return it
|
|
||||||
//return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err)
|
|
||||||
//v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_id", dcID)
|
|
||||||
return nil, nil
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("failed to retrieve VM: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to retrieve the resource pool for the VM
|
// Helper function to retrieve the resource pool for the VM
|
||||||
@@ -850,7 +811,7 @@ func (v *Vcenter) BuildResourcePoolLookup() (map[string]string, error) {
|
|||||||
defer cv.Destroy(v.ctx)
|
defer cv.Destroy(v.ctx)
|
||||||
|
|
||||||
var pools []mo.ResourcePool
|
var pools []mo.ResourcePool
|
||||||
if err := cv.Retrieve(v.ctx, []string{"ResourcePool"}, []string{"name"}, &pools); err != nil {
|
if err := v.retrieveContainerViewPaged(v.ctx, cv, []string{"ResourcePool"}, []string{"name"}, &pools, defaultPropertyPageSize); err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve resource pools: %w", err)
|
return nil, fmt.Errorf("failed to retrieve resource pools: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,55 +824,39 @@ func (v *Vcenter) BuildResourcePoolLookup() (map[string]string, error) {
|
|||||||
|
|
||||||
// Helper function to retrieve the full folder path for the VM
|
// Helper function to retrieve the full folder path for the VM
|
||||||
func (v *Vcenter) GetVMFolderPath(vm mo.VirtualMachine) (string, error) {
|
func (v *Vcenter) GetVMFolderPath(vm mo.VirtualMachine) (string, error) {
|
||||||
//finder := find.NewFinder(v.client.Client, true)
|
|
||||||
|
|
||||||
v.Logger.Debug("Commencing vm folder path search")
|
v.Logger.Debug("Commencing vm folder path search")
|
||||||
|
|
||||||
// Start from the VM's parent
|
entities, err := mo.Ancestors(v.ctx, v.client.Client, v.client.ServiceContent.PropertyCollector, vm.Reference())
|
||||||
parentRef := vm.Parent
|
|
||||||
if parentRef == nil {
|
|
||||||
return "", fmt.Errorf("no parent found for the VM")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse the folder hierarchy to build the full folder path
|
|
||||||
folderPath := ""
|
|
||||||
//v.Logger.Debug("parent is", "parent", parentRef)
|
|
||||||
|
|
||||||
maxHops := 128
|
|
||||||
for parentRef != nil && parentRef.Type != "Datacenter" && maxHops > 0 {
|
|
||||||
// Retrieve the parent object
|
|
||||||
//parentObj, err := finder.ObjectReference(v.ctx, *parentRef)
|
|
||||||
//if err != nil {
|
|
||||||
// return "", fmt.Errorf("failed to find parent object in inventory: %w", err)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Retrieve the folder name
|
|
||||||
|
|
||||||
var parentObj mo.Folder
|
|
||||||
err := v.client.RetrieveOne(v.ctx, *parentRef, nil, &parentObj)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Logger.Error("Failed to get object for parent reference", "ref", parentRef)
|
return "", fmt.Errorf("failed to retrieve VM ancestors: %w", err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend the folder name to the path
|
var folderParts []string
|
||||||
folderPath = path.Join("/", parentObj.Name, folderPath)
|
seenDatacenter := false
|
||||||
|
vmRef := vm.Reference()
|
||||||
// Move up to the next parent
|
for _, entity := range entities {
|
||||||
//if folder, ok := parentObj.(*object.Folder); ok {
|
switch entity.Self.Type {
|
||||||
if parentObj.Parent != nil {
|
case "Datacenter":
|
||||||
parentRef = parentObj.Parent
|
seenDatacenter = true
|
||||||
//v.Logger.Debug("Parent uplevel is", "ref", parentRef)
|
folderParts = folderParts[:0]
|
||||||
} else {
|
case "VirtualMachine":
|
||||||
return "", fmt.Errorf("unexpected parent type: %s", parentObj.Reference().Type)
|
if entity.Self == vmRef {
|
||||||
|
goto done
|
||||||
}
|
}
|
||||||
//break
|
case "Folder":
|
||||||
maxHops--
|
if seenDatacenter {
|
||||||
|
folderParts = append(folderParts, entity.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
|
||||||
|
if !seenDatacenter {
|
||||||
|
return "", fmt.Errorf("failed to find datacenter ancestor for VM %s", vm.Reference().Value)
|
||||||
|
}
|
||||||
|
if len(folderParts) == 0 {
|
||||||
|
return "", fmt.Errorf("failed to find folder path for VM %s", vm.Reference().Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentRef == nil || maxHops == 0 {
|
return path.Join("/", path.Join(folderParts...)), nil
|
||||||
return "", fmt.Errorf("folder traversal terminated early for VM %s", vm.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return folderPath, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
USER="vctp"
|
USER="vctp"
|
||||||
GROUP="dtms"
|
GROUP="dtms"
|
||||||
|
CONFIG_FILE="/etc/dtms/vctp.yml"
|
||||||
|
DEFAULT_PORT=9443
|
||||||
|
|
||||||
# Path to the custom sudoers file
|
# Path to the custom sudoers file
|
||||||
SUDOERS_FILE="/etc/sudoers.d/${USER}"
|
SUDOERS_FILE="/etc/sudoers.d/${USER}"
|
||||||
@@ -28,15 +30,35 @@ getent passwd "$USER" >/dev/null || useradd -r -g "$GROUP" -m -s /bin/bash -c "v
|
|||||||
# set group ownership on vctp data directory if not already done
|
# set group ownership on vctp data directory if not already done
|
||||||
[ "$(stat -c "%G" /var/lib/vctp)" = "$GROUP" ] || chgrp -R "$GROUP" /var/lib/vctp
|
[ "$(stat -c "%G" /var/lib/vctp)" = "$GROUP" ] || chgrp -R "$GROUP" /var/lib/vctp
|
||||||
|
|
||||||
|
# Resolve effective application port from existing config (if present).
|
||||||
|
# Falls back to 9443 when the config file is missing/empty or bind_port is invalid.
|
||||||
|
APP_PORT="$DEFAULT_PORT"
|
||||||
|
if [ -s "$CONFIG_FILE" ]; then
|
||||||
|
CONFIGURED_PORT="$(awk '
|
||||||
|
/^[[:space:]]*#/ { next }
|
||||||
|
/^[[:space:]]*bind_port[[:space:]]*:/ {
|
||||||
|
line=$0
|
||||||
|
sub(/^[[:space:]]*bind_port[[:space:]]*:[[:space:]]*/, "", line)
|
||||||
|
sub(/[[:space:]]*#.*/, "", line)
|
||||||
|
gsub(/["'\''[:space:]]/, "", line)
|
||||||
|
if (line ~ /^[0-9]+$/) {
|
||||||
|
print line
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$CONFIG_FILE")"
|
||||||
|
|
||||||
|
if [ -n "$CONFIGURED_PORT" ] && [ "$CONFIGURED_PORT" -ge 1 ] && [ "$CONFIGURED_PORT" -le 65535 ]; then
|
||||||
|
APP_PORT="$CONFIGURED_PORT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if firewalld is installed and active
|
# Check if firewalld is installed and active
|
||||||
if command -v systemctl >/dev/null 2>&1 && systemctl is-enabled firewalld >/dev/null 2>&1 && systemctl is-active firewalld >/dev/null 2>&1; then
|
if command -v systemctl >/dev/null 2>&1 && systemctl is-enabled firewalld >/dev/null 2>&1 && systemctl is-active firewalld >/dev/null 2>&1; then
|
||||||
echo "Firewalld is enabled and running. Adding necessary ports..."
|
echo "Firewalld is enabled and running. Opening vCTP port ${APP_PORT}/tcp..."
|
||||||
|
|
||||||
# Open HTTPS port (443/tcp)
|
# Open effective application port.
|
||||||
firewall-cmd --permanent --add-service=https >/dev/null 2>&1
|
firewall-cmd --permanent --add-port="${APP_PORT}/tcp" >/dev/null 2>&1
|
||||||
|
|
||||||
# Open custom application port (9443/tcp)
|
|
||||||
firewall-cmd --permanent --add-port=9443/tcp >/dev/null 2>&1
|
|
||||||
|
|
||||||
# Reload firewalld to apply changes
|
# Reload firewalld to apply changes
|
||||||
firewall-cmd --reload >/dev/null 2>&1
|
firewall-cmd --reload >/dev/null 2>&1
|
||||||
|
|||||||
Reference in New Issue
Block a user