diff --git a/.gitignore b/.gitignore index 224f6c1..b4ab10f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +.idea +*.iml vendor/ dist/ diff --git a/examples/datasource_vm_guest_metrics/main.tf b/examples/datasource_vm_guest_metrics/main.tf new file mode 100644 index 0000000..11f6ded --- /dev/null +++ b/examples/datasource_vm_guest_metrics/main.tf @@ -0,0 +1,44 @@ +# To execute with `credentials.tfvars` in the `examples` folder: +# terraform init && terraform plan --var-file=../credentials.tfvars && terraform apply --var-file=../credentials.tfvars && terraform destroy --var-file=../credentials.tfvars -force + +variable "url" {} +variable "username" {} +variable "password" {} +variable "vm_uuid" {} + +provider "xenserver" { + url = "${var.url}" + username = "${var.username}" + password = "${var.password}" +} + +data "xenserver_vm_guest_metrics" "interfaces" { + vm_uuid = "${vm_uuid}" +} + +data "template_file" "interfaces_written" { + template = "$${uuids}" + + vars { + uuids = "${jsonencode(data.xenserver_vm_guest_metrics.interfaces.networks)}" + } +} + +resource "null_resource" "interfaces_file" { + triggers { + content = "${data.template_file.interfaces_written.rendered}" + } + + provisioner "local-exec" { + command = <<-EOC + tee ${path.cwd}/output.json < 0 { + var vmmetrics VMMetrics + if vmmetrics, err = vm.Metrics(c); err != nil { + return + } + + now := time.Now() + diff := now.Sub(vmmetrics.StartTime).Seconds() + + if delay > diff { + sleep := time.Duration(delay-diff) * time.Second + time.Sleep(sleep) + } + } + + var metrics VMGuestMetrics + + if metrics, err = vm.GuestMetrics(c); err != nil { + return + } + + d.SetId(metrics.UUID) + + log.Printf("[DEBUG] Id is %s\n", d.Id()) + log.Println("[DEBUG] Networks: ", metrics.Networks) + + ipNetworks := make([][]string, 0) + ipv6Networks := make([][]string, 0) + + for _, network := range metrics.Networks { + ipNetworks = append(ipNetworks, network["ip"]) + ipv6Networks = append(ipv6Networks, network["ipv6"]) + } + + d.Set(vmNetworksIp, ipNetworks) + d.Set(vmNetworksIpv6, ipv6Networks) + + return nil +} diff --git a/xenserver/provider.go b/xenserver/provider.go index 8220c2f..0d9af53 100644 --- a/xenserver/provider.go +++ b/xenserver/provider.go @@ -32,9 +32,11 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "xenserver_pif": dataSourceXenServerPif(), - "xenserver_pifs": dataSourceXenServerPifs(), - "xenserver_sr": dataSourceXenServerSR(), + "xenserver_pif": dataSourceXenServerPif(), + "xenserver_pifs": dataSourceXenServerPifs(), + "xenserver_sr": dataSourceXenServerSR(), + "xenserver_vm_guest_metrics": dataSourceVmGuestMetrics(), + "xenserver_vm_networks": dataSourceVmNetworks(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/xenserver/types.go b/xenserver/types.go index 03917c3..796a490 100644 --- a/xenserver/types.go +++ b/xenserver/types.go @@ -24,6 +24,9 @@ import ( "strconv" "github.com/ringods/go-xen-api-client" + "regexp" + "sort" + "time" ) type Range struct { @@ -129,6 +132,27 @@ type VLANDescriptor struct { VLANRef xenAPI.VLANRef } +type VMMetrics struct { + UUID string + StartTime time.Time + InstallTime time.Time +} + +type VMGuestMetrics struct { + UUID string + Disks interface{} + Networks []map[string][]string + Memory interface{} + OSVersion interface{} + PVDriversVersion interface{} + PVDriversDetected bool + PVDriversUpToDate bool + CanUseHotplugVbd string + CanUseHotplugVif string + Live bool + LastUpdated time.Time +} + func (this *NetworkDescriptor) Load(c *Connection) error { var network xenAPI.NetworkRef @@ -224,6 +248,88 @@ func (this *VMDescriptor) Load(c *Connection) error { return this.Query(c) } +func (this *VMDescriptor) GuestMetrics(c *Connection) (metrics VMGuestMetrics, err error) { + var metricsRef xenAPI.VMGuestMetricsRef + var metricsRecord xenAPI.VMGuestMetricsRecord + if metricsRef, err = c.client.VM.GetGuestMetrics(c.session, this.VMRef); err != nil { + return + } + + if metricsRecord, err = c.client.VMGuestMetrics.GetRecord(c.session, metricsRef); err == nil { + metrics.UUID = metricsRecord.UUID + metrics.Disks = metricsRecord.Disks + metrics.Memory = metricsRecord.Memory + metrics.OSVersion = metricsRecord.OSVersion + metrics.PVDriversVersion = metricsRecord.PVDriversVersion + metrics.PVDriversDetected = metricsRecord.PVDriversDetected + metrics.PVDriversUpToDate = metricsRecord.PVDriversUpToDate + metrics.CanUseHotplugVbd = string(metricsRecord.CanUseHotplugVbd) + metrics.CanUseHotplugVif = string(metricsRecord.CanUseHotplugVif) + metrics.Live = metricsRecord.Live + metrics.LastUpdated = metricsRecord.LastUpdated + + var keys []string + for k := range metricsRecord.Networks { + keys = append(keys, k) + } + sort.Strings(keys) + + var re *regexp.Regexp + if re, err = regexp.Compile("([0-9]+)/(ipv?6?)/?[0-9]*"); err != nil { + return + } + + if len(keys) > 0 { + // Find out number of interfaces + lastKey := keys[len(keys)-1:][0] + var count int + + if count, err = strconv.Atoi(re.FindStringSubmatch(lastKey)[1]); err != nil { + return + } + count += 1 // increase by 1 since we obtained index and not length + + metrics.Networks = make([]map[string][]string, count) + + for _, k := range keys { + matches := re.FindStringSubmatch(k) + var index int + if index, err = strconv.Atoi(matches[1]); err != nil { + return + } + + if metrics.Networks[index] == nil { + metrics.Networks[index] = make(map[string][]string) + } + + metrics.Networks[index][matches[2]] = append(metrics.Networks[index][matches[2]], metricsRecord.Networks[k]) + } + } + } + + return +} + +func (this *VMDescriptor) Metrics(c *Connection) (metrics VMMetrics, err error) { + var metricsRecord xenAPI.VMMetricsRecord + var metricsRef xenAPI.VMMetricsRef + if metricsRef, err = c.client.VM.GetMetrics(c.session, this.VMRef); err != nil { + return + } + + if metricsRecord, err = c.client.VMMetrics.GetRecord(c.session, metricsRef); err != nil { + return + } + + metrics = VMMetrics{ + UUID: metricsRecord.UUID, + StartTime: metricsRecord.StartTime, + InstallTime: metricsRecord.LastUpdated, + } + + return +} + func (this *VMDescriptor) Query(c *Connection) error { vm, err := c.client.VM.GetRecord(c.session, this.VMRef) if err != nil {