initial
This commit is contained in:
4
go.mod
Normal file
4
go.mod
Normal file
@@ -0,0 +1,4 @@
|
||||
module nathan/go-ntp
|
||||
go 1.24.1
|
||||
|
||||
require github.com/vmware/govmomi v0.43.0
|
6
go.sum
Normal file
6
go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/vmware/govmomi v0.30.4 h1:BCKLoTmiBYRuplv3GxKEMBLtBaJm8PA56vo9bddIpYQ=
|
||||
github.com/vmware/govmomi v0.30.4/go.mod h1:F7adsVewLNHsW/IIm7ziFURaXDaHEwcc+ym4r3INMdY=
|
||||
github.com/vmware/govmomi v0.43.0 h1:7Kg3Bkdly+TrE67BYXzRq7ZrDnn7xqpKX95uEh2f9Go=
|
||||
github.com/vmware/govmomi v0.43.0/go.mod h1:IOv5nTXCPqH9qVJAlRuAGffogaLsNs8aF+e7vLgsHJU=
|
311
main.go
Normal file
311
main.go
Normal file
@@ -0,0 +1,311 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/vmware/govmomi"
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/view"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
)
|
||||
|
||||
type HostTimeErrors struct {
|
||||
HostName string
|
||||
HostTime time.Time
|
||||
Cluster string
|
||||
Vcenter string
|
||||
}
|
||||
|
||||
var (
|
||||
c *govmomi.Client
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
location *time.Location
|
||||
sha1ver string // sha1 revision used to build the program
|
||||
buildTime string // when the executable was built
|
||||
hostTimeErrors []HostTimeErrors
|
||||
)
|
||||
|
||||
/*
|
||||
type dateInfo struct {
|
||||
types.HostDateTimeInfo
|
||||
Service *types.HostService `json:"service"`
|
||||
Current *time.Time `json:"current"`
|
||||
}
|
||||
*/
|
||||
|
||||
func prettyPrint(args ...interface{}) {
|
||||
var caller string
|
||||
|
||||
timeNow := time.Now().Format("01-02-2006 15:04:05")
|
||||
prefix := fmt.Sprintf("[%s] %s -- ", "PrettyPrint", timeNow)
|
||||
_, fileName, fileLine, ok := runtime.Caller(1)
|
||||
|
||||
if ok {
|
||||
caller = fmt.Sprintf("%s:%d", fileName, fileLine)
|
||||
} else {
|
||||
caller = ""
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s%s\n", prefix, caller)
|
||||
|
||||
if len(args) == 2 {
|
||||
label := args[0]
|
||||
value := args[1]
|
||||
|
||||
s, _ := json.MarshalIndent(value, "", "\t")
|
||||
fmt.Printf("%s%s: %s\n", prefix, label, string(s))
|
||||
} else {
|
||||
s, _ := json.MarshalIndent(args, "", "\t")
|
||||
fmt.Printf("%s%s\n", prefix, string(s))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Command line flags for the vCenter connection
|
||||
vURL := flag.String("url", "", "The URL of a vCenter server, eg https://server.domain.example/sdk")
|
||||
vUser := flag.String("user", "", "The username to use when connecting to vCenter")
|
||||
vPass := flag.String("password", "", "The password to use when connecting to vCenter")
|
||||
vTZ := flag.String("tz", "Australia/Sydney", "The timezone to use when converting vCenter UTC times")
|
||||
vInsecure := flag.Bool("insecure", true, "Allow insecure connections to vCenter")
|
||||
vAllowedDiff := flag.Int("diff", 300, "Permitted time difference in seconds")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// Print logs to file
|
||||
f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("error opening file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
log.SetOutput(f)
|
||||
|
||||
log.Printf("Starting execution. Built on %s from sha1 %s\n", buildTime, sha1ver)
|
||||
|
||||
// So we can convert vCenter UTC to our local timezone
|
||||
log.Printf("Setting timezone to '%s'\n", *vTZ)
|
||||
location, err = time.LoadLocation(*vTZ)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error setting timezone to %s : %s\n", *vTZ, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.Parse(*vURL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing url %s : %s\n", *vURL, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
if !strings.HasSuffix(u.Path, "/sdk") {
|
||||
u.Path, _ = url.JoinPath(u.Path, "/sdk")
|
||||
log.Printf("Updated vCenter URL to '%v'\n", u)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Connecting to vCenter %s\n", u)
|
||||
u.User = url.UserPassword(*vUser, *vPass)
|
||||
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Login to vcenter
|
||||
c, err = govmomi.NewClient(ctx, u, *vInsecure)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Logging in error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer c.Logout(ctx)
|
||||
|
||||
// Create a view manager
|
||||
m := view.NewManager(c.Client)
|
||||
|
||||
// Create a container view for all Datacenters
|
||||
dcView, err := m.CreateContainerView(ctx, c.Client.ServiceContent.RootFolder, []string{"Datacenter"}, true)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create container view: %v", err)
|
||||
}
|
||||
defer dcView.Destroy(ctx)
|
||||
|
||||
// Retrieve all Datacenters
|
||||
var dcs []mo.Datacenter
|
||||
err = dcView.Retrieve(ctx, []string{"Datacenter"}, []string{"name", "hostFolder"}, &dcs)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to retrieve datacenters: %v", err)
|
||||
}
|
||||
|
||||
// Iterate through each datacenter and find hosts
|
||||
for _, dc := range dcs {
|
||||
//fmt.Printf("Datacenter: %s\n", dc.Name)
|
||||
|
||||
// Create a finder for the datacenter
|
||||
finder := find.NewFinder(c.Client, false)
|
||||
datacenter, err := finder.Datacenter(ctx, dc.Name)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get datacenter object: %v", err)
|
||||
continue
|
||||
}
|
||||
finder.SetDatacenter(datacenter)
|
||||
|
||||
// Find all ESXi hosts in this datacenter
|
||||
hosts, err := finder.HostSystemList(ctx, "*")
|
||||
if err != nil {
|
||||
log.Printf("Failed to get hosts in datacenter %s: %v", dc.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Retrieve host properties
|
||||
var hostProperties []mo.HostSystem
|
||||
err = dcView.Retrieve(ctx, []string{"HostSystem"}, []string{"name", "hardware.systemInfo", "configManager"}, &hostProperties)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to retrieve host properties: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Print hosts
|
||||
for _, host := range hosts {
|
||||
clusterName := ""
|
||||
var hs mo.HostSystem
|
||||
|
||||
err = host.Properties(ctx, host.Reference(), []string{"name", "Parent"}, &hs)
|
||||
if err != nil {
|
||||
log.Printf("Failed to retrieve host properties: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
dts, err := host.ConfigManager().DateTimeSystem(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
hostTime, err := dts.Query(ctx)
|
||||
//fmt.Printf(" - Host: %s; Time: %v\n", hs.Name, hostTime)
|
||||
|
||||
// Convert ESXi UTC time to local time
|
||||
esxiTimeLocal := hostTime.Local()
|
||||
localTime := time.Now()
|
||||
diff := localTime.Sub(esxiTimeLocal)
|
||||
maxDiff := time.Duration(*vAllowedDiff) * time.Second
|
||||
|
||||
if diff > maxDiff || diff < -maxDiff {
|
||||
fmt.Printf("ESXi %s time differs from local time by more than %d seconds (ESXi: %v, Local: %v)", hs.Name, *vAllowedDiff, esxiTimeLocal, localTime)
|
||||
|
||||
// Get the cluster name
|
||||
if hs.Parent.Type == "ClusterComputeResource" {
|
||||
// Retrieve properties of the compute resource
|
||||
var moCompute mo.ComputeResource
|
||||
err = c.RetrieveOne(ctx, *hs.Parent, nil, &moCompute)
|
||||
if err == nil {
|
||||
clusterName = moCompute.Name
|
||||
}
|
||||
}
|
||||
|
||||
thisResult := HostTimeErrors{
|
||||
HostName: hs.Name,
|
||||
Cluster: clusterName,
|
||||
Vcenter: u.Host,
|
||||
HostTime: esxiTimeLocal,
|
||||
}
|
||||
|
||||
hostTimeErrors = append(hostTimeErrors, thisResult)
|
||||
}
|
||||
|
||||
/*
|
||||
var hostDts mo.HostDateTimeSystem
|
||||
if err = dts.Properties(ctx, dts.Reference(), nil, &hostDts); err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ss, err := host.ConfigManager().ServiceSystem(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
services, err := ss.Service(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
res := &dateInfo{HostDateTimeInfo: hostDts.DateTimeInfo}
|
||||
|
||||
for i, service := range services {
|
||||
if service.Key == "ntpd" {
|
||||
res.Service = &services[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
res.Current, err = dts.Query(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
prettyPrint(res)
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Create a new result
|
||||
result := OutageResults{
|
||||
VM: event.Vm.Name,
|
||||
OutageDuration: out.Format("15:04:05"),
|
||||
OutageStart: outageStart,
|
||||
RestartTime: restartTime,
|
||||
Cluster: event.ComputeResource.Name,
|
||||
FailedHost: failedHost,
|
||||
NewHost: event.Host.Name,
|
||||
GuestOS: vmOS,
|
||||
CurrentPowerState: vmPowerState,
|
||||
Description: event.FullFormattedMessage,
|
||||
}
|
||||
// Append to list of all results
|
||||
results = append(results, result)
|
||||
}
|
||||
|
||||
for _, hostEvent := range hostFailures {
|
||||
hostResults = append(hostResults, HostFailureResults{
|
||||
HostName: hostEvent.Host.Name,
|
||||
FailureTime: hostEvent.CreatedTime.In(location),
|
||||
Cluster: hostEvent.ComputeResource.Name,
|
||||
Vcenter: u.Host,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
log.Printf("Found %d hostfailure messages in last %.1f hour(s)", len(hostFailures), begin.Abs().Hours())
|
||||
}
|
||||
*/
|
||||
|
||||
// Combine details of host outages and VM outages into one interface
|
||||
/*
|
||||
var combined []interface{}
|
||||
for _, h := range hostResults {
|
||||
combined = append(combined, h)
|
||||
}
|
||||
for _, v := range results {
|
||||
combined = append(combined, v)
|
||||
}
|
||||
*/
|
||||
|
||||
// Output final results in JSON
|
||||
if len(hostTimeErrors) > 0 {
|
||||
j, _ := json.Marshal(hostTimeErrors)
|
||||
fmt.Println(string(j))
|
||||
} else {
|
||||
fmt.Println("{}")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user