From ced2adee822574ffd5012b1ecef11c28ec9dcc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Gy=C3=A1csok?= Date: Wed, 10 Aug 2016 11:19:15 +0200 Subject: [PATCH] Added hypervisors (API v.2) support --- .../v2/privileged/hypervisors/fixtures.go | 422 ++++++++++++++++++ .../v2/privileged/hypervisors/objects.go | 59 +++ .../v2/privileged/hypervisors/requests.go | 57 +++ .../privileged/hypervisors/requests_test.go | 83 ++++ .../v2/privileged/hypervisors/results.go | 109 +++++ .../compute/v2/privileged/hypervisors/urls.go | 27 ++ 6 files changed, 757 insertions(+) create mode 100644 openstack/compute/v2/privileged/hypervisors/fixtures.go create mode 100644 openstack/compute/v2/privileged/hypervisors/objects.go create mode 100644 openstack/compute/v2/privileged/hypervisors/requests.go create mode 100644 openstack/compute/v2/privileged/hypervisors/requests_test.go create mode 100644 openstack/compute/v2/privileged/hypervisors/results.go create mode 100644 openstack/compute/v2/privileged/hypervisors/urls.go diff --git a/openstack/compute/v2/privileged/hypervisors/fixtures.go b/openstack/compute/v2/privileged/hypervisors/fixtures.go new file mode 100644 index 00000000..8e9930e4 --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/fixtures.go @@ -0,0 +1,422 @@ +package hypervisors + +import ( + "testing" + "net/http" + + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" + "fmt" +) + + +// nova hypervisor-list (List hypervisors.) +const HypervisorListBody = ` +{ + "hypervisors": [ + { + "status": "enabled", + "state": "up", + "id": 2, + "hypervisor_hostname": "compute-0-3.domain.tld" + }, + { + "status": "enabled", + "state": "up", + "id": 8, + "hypervisor_hostname": "compute-0-1.domain.tld" + }, + { + "status": "enabled", + "state": "up", + "id": 14, + "hypervisor_hostname": "compute-0-2.domain.tld" + } + ] +} +` +// If we call "nova hypervisor-show compute-2.domain.tld" +// (hypervisor hostname), Nova will return details of all hypervisors. +const HypervisorsDetailsBody = ` +{ + "hypervisors": [ + { + "status": "enabled", + "service": { + "host": "compute-0-3.domain.tld", + "disabled_reason": null, + "id": 33 + }, + "vcpus_used": 0, + "hypervisor_type": "QEMU", + "local_gb_used": 240, + "vcpus": 24, + "hypervisor_hostname": "compute-0-3.domain.tld", + "memory_mb_used": 59016, + "memory_mb": 64136, + "current_workload": 0, + "state": "up", + "host_ip": "192.168.2.28", + "cpu_info": "{\"vendor\": \"Intel\", \"model\": \"SandyBridge\", \"arch\": \"x86_64\", \"features\": [\"pge\", \"avx\", \"clflush\", \"sep\", \"syscall\", \"vme\", \"dtes64\", \"msr\", \"fsgsbase\", \"xsave\", \"vmx\", \"erms\", \"xtpr\", \"cmov\", \"smep\", \"ssse3\", \"est\", \"pat\", \"monitor\", \"smx\", \"pbe\", \"lm\", \"tsc\", \"nx\", \"fxsr\", \"tm\", \"sse4.1\", \"pae\", \"sse4.2\", \"pclmuldq\", \"acpi\", \"tsc-deadline\", \"mmx\", \"osxsave\", \"cx8\", \"mce\", \"de\", \"tm2\", \"ht\", \"dca\", \"lahf_lm\", \"popcnt\", \"mca\", \"pdpe1gb\", \"apic\", \"sse\", \"f16c\", \"pse\", \"ds\", \"invtsc\", \"pni\", \"rdtscp\", \"aes\", \"sse2\", \"ss\", \"ds_cpl\", \"pcid\", \"fpu\", \"cx16\", \"pse36\", \"mtrr\", \"pdcm\", \"rdrand\", \"x2apic\"], \"topology\": {\"cores\": 10, \"threads\": 2, \"sockets\": 1}}", + "running_vms": 0, + "free_disk_gb": 414, + "hypervisor_version": 2002000, + "disk_available_least": 633, + "local_gb": 654, + "free_ram_mb": 5120, + "id": 2 + }, + { + "status": "enabled", + "service": { + "host": "compute-0-1.domain.tld", + "disabled_reason": null, + "id": 38 + }, + "vcpus_used": 0, + "hypervisor_type": "QEMU", + "local_gb_used": 290, + "vcpus": 22, + "hypervisor_hostname": "compute-0-1.domain.tld", + "memory_mb_used": 62088, + "memory_mb": 64136, + "current_workload": 0, + "state": "up", + "host_ip": "192.168.2.25", + "cpu_info": "{\"vendor\": \"Intel\", \"model\": \"SandyBridge\", \"arch\": \"x86_64\", \"features\": [\"pge\", \"avx\", \"clflush\", \"sep\", \"syscall\", \"vme\", \"dtes64\", \"msr\", \"fsgsbase\", \"xsave\", \"vmx\", \"erms\", \"xtpr\", \"cmov\", \"smep\", \"ssse3\", \"est\", \"pat\", \"monitor\", \"smx\", \"pbe\", \"lm\", \"tsc\", \"nx\", \"fxsr\", \"tm\", \"sse4.1\", \"pae\", \"sse4.2\", \"pclmuldq\", \"acpi\", \"tsc-deadline\", \"mmx\", \"osxsave\", \"cx8\", \"mce\", \"de\", \"tm2\", \"ht\", \"dca\", \"lahf_lm\", \"popcnt\", \"mca\", \"pdpe1gb\", \"apic\", \"sse\", \"f16c\", \"pse\", \"ds\", \"invtsc\", \"pni\", \"rdtscp\", \"aes\", \"sse2\", \"ss\", \"ds_cpl\", \"pcid\", \"fpu\", \"cx16\", \"pse36\", \"mtrr\", \"pdcm\", \"rdrand\", \"x2apic\"], \"topology\": {\"cores\": 10, \"threads\": 2, \"sockets\": 1}}", + "running_vms": 0, + "free_disk_gb": 364, + "hypervisor_version": 2002000, + "disk_available_least": 601, + "local_gb": 654, + "free_ram_mb": 2048, + "id": 8 + }, + { + "status": "enabled", + "service": { + "host": "compute-0-2.domain.tld", + "disabled_reason": null, + "id": 42 + }, + "vcpus_used": 2, + "hypervisor_type": "QEMU", + "local_gb_used": 130, + "vcpus": 32, + "hypervisor_hostname": "compute-0-2.domain.tld", + "memory_mb_used": 20104, + "memory_mb": 64136, + "current_workload": 0, + "state": "up", + "host_ip": "192.168.2.23", + "cpu_info": "{\"vendor\": \"Intel\", \"model\": \"SandyBridge\", \"arch\": \"x86_64\", \"features\": [\"pge\", \"avx\", \"clflush\", \"sep\", \"syscall\", \"vme\", \"dtes64\", \"msr\", \"fsgsbase\", \"xsave\", \"vmx\", \"erms\", \"xtpr\", \"cmov\", \"smep\", \"ssse3\", \"est\", \"pat\", \"monitor\", \"smx\", \"pbe\", \"lm\", \"tsc\", \"nx\", \"fxsr\", \"tm\", \"sse4.1\", \"pae\", \"sse4.2\", \"pclmuldq\", \"acpi\", \"tsc-deadline\", \"mmx\", \"osxsave\", \"cx8\", \"mce\", \"de\", \"tm2\", \"ht\", \"dca\", \"lahf_lm\", \"popcnt\", \"mca\", \"pdpe1gb\", \"apic\", \"sse\", \"f16c\", \"pse\", \"ds\", \"invtsc\", \"pni\", \"rdtscp\", \"aes\", \"sse2\", \"ss\", \"ds_cpl\", \"pcid\", \"fpu\", \"cx16\", \"pse36\", \"mtrr\", \"pdcm\", \"rdrand\", \"x2apic\"], \"topology\": {\"cores\": 10, \"threads\": 2, \"sockets\": 1}}", + "running_vms": 1, + "free_disk_gb": 524, + "hypervisor_version": 2002000, + "disk_available_least": 521, + "local_gb": 654, + "free_ram_mb": 44032, + "id": 14 + } + ] +} + +` + +// nova hypervisor-show 14 +const HypervisorDetailsBody = ` +{"hypervisor": {"status": "enabled", + "service": { + "host": "compute-0-2.domain.tld", + "disabled_reason": null, + "id": 42 + }, + "vcpus_used": 2, + "hypervisor_type": "QEMU", + "local_gb_used": 130, + "vcpus": 32, + "hypervisor_hostname": "compute-0-2.domain.tld", + "memory_mb_used": 20104, + "memory_mb": 64136, + "current_workload": 0, + "state": "up", + "host_ip": "192.168.2.23", + "cpu_info": "{\"vendor\": \"Intel\", \"model\": \"SandyBridge\", \"arch\": \"x86_64\", \"features\": [\"pge\", \"avx\", \"clflush\", \"sep\", \"syscall\", \"vme\", \"dtes64\", \"msr\", \"fsgsbase\", \"xsave\", \"vmx\", \"erms\", \"xtpr\", \"cmov\", \"smep\", \"ssse3\", \"est\", \"pat\", \"monitor\", \"smx\", \"pbe\", \"lm\", \"tsc\", \"nx\", \"fxsr\", \"tm\", \"sse4.1\", \"pae\", \"sse4.2\", \"pclmuldq\", \"acpi\", \"tsc-deadline\", \"mmx\", \"osxsave\", \"cx8\", \"mce\", \"de\", \"tm2\", \"ht\", \"dca\", \"lahf_lm\", \"popcnt\", \"mca\", \"pdpe1gb\", \"apic\", \"sse\", \"f16c\", \"pse\", \"ds\", \"invtsc\", \"pni\", \"rdtscp\", \"aes\", \"sse2\", \"ss\", \"ds_cpl\", \"pcid\", \"fpu\", \"cx16\", \"pse36\", \"mtrr\", \"pdcm\", \"rdrand\", \"x2apic\"], \"topology\": {\"cores\": 10, \"threads\": 2, \"sockets\": 1}}", + "running_vms": 1, + "free_disk_gb": 524, + "hypervisor_version": 2002000, + "disk_available_least": 521, + "local_gb": 654, + "free_ram_mb": 44032, + "id": 14 +} +} +` + +// nova hypervisor-servers compute-0-2.domain.tld +const HypervisorServersList = ` +{ + "hypervisors": [ + { + "status": "enabled", + "state": "up", + "id": 14, + "hypervisor_hostname": "compute-0-2.domain.tld", + "servers": [ + { + "uuid": "b32aa057-1880-46b8-888a-9855414e4a47", + "name": "instance-00000030" + }, + { + "uuid": "1fd61852-2a6e-4f1f-8b62-c1af7f04e997", + "name": "instance-0000015c" + } + ] + } + ] +} +` + +// nova hypervisor-uptime compute-0-2.domain.tld (or id which is faster) +const HypervisorUptimeBody = ` +{ + "hypervisor": { + "status": "enabled", + "state": "up", + "id": 14, + "hypervisor_hostname": "compute-0-2.domain.tld", + "uptime": " 15:22:34 up 20:27, 4 users, load average: 2.23, 2.31, 2.35\n" + } +} +` +var ( + // Hypervisor_3 is a Server struct that should correspond to the first + // result in HypervisorListBody. + hyper_1 = Hypervisor{ + HypervisorHostname: "compute-0-1.domain.tld", + Id: 8, + State: "up", + Status: "enabled", + } + hyper_2 = Hypervisor{ + HypervisorHostname: "compute-0-2.domain.tld", + Id: 14, + State: "up", + Status: "enabled", + } + hyper_3 = Hypervisor{ + HypervisorHostname: "compute-0-3.domain.tld", + Id: 2, + State: "up", + Status: "enabled", + } + ListHypervisorsExpected = []Hypervisor{ hyper_3, hyper_1, hyper_2} +) + +func HandleHypervisorsListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-hypervisors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorListBody) + }) +} + +var ( + HypervisorDetail_1 = HypervisorDetail{ + HypervisorHostname: "compute-0-1.domain.tld", + Id: 8, + State: "up", + Status: "enabled", + Service: Service{ + Host: "compute-0-1.domain.tld", + DisabledReason: "", + Id: 38, + }, + VcpuUsed: 0, + HypervisorType: "QEMU", + LocalGBUsed: 290, + Vcpus: 22, + MemoryMBUsed: 62088, + MemoryMB: 64136, + CurrentWorkload: 0, + HostIP: "192.168.2.25", + CPUInfo: map[string]interface{}{"vendor": "Intel", + "model": "SandyBridge", + "arch": "x86_64", + "features": []string{"pge", "avx", "clflush", "sep", + "syscall", "vme", "dtes64", "msr", "fsgsbase", + "xsave", "vmx", "erms", "xtpr", "cmov", "smep", + "ssse3", "est", "pat", "monitor", "smx", "pbe", + "lm", "tsc", "nx", "fxsr", "tm", "sse4.1", "pae", + "sse4.2", "pclmuldq", "acpi", "tsc-deadline", + "mmx", "osxsave", "cx8", "mce", "de", "tm2", + "ht", "dca", "lahf_lm", "popcnt", "mca", "pdpe1gb", + "apic", "sse", "f16c", "pse", "ds", "invtsc", "pni", + "rdtscp", "aes", "sse2", "ss", "ds_cpl", "pcid", + "fpu", "cx16", "pse36", "mtrr", "pdcm", "rdrand", "x2apic"}, + "topology": map[string]int{"cores": 10, "threads": 2, "sockets": 1}, + }, + RunningVMs: 0, + FreeDiskGB: 364, + HypervisorVersion: 2002000, + DistAvailableLeast: 601, + LocalGB: 654, + FreeRamMB: 2048, + } + HypervisorDetail_2 = HypervisorDetail{ + HypervisorHostname: "compute-0-2.domain.tld", + Id: 14, + State: "up", + Status: "enabled", + Service: Service{ + Host: "compute-0-2.domain.tld", + DisabledReason: "", + Id: 42, + }, + VcpuUsed: 2, + HypervisorType: "QEMU", + LocalGBUsed: 130, + Vcpus: 32, + MemoryMBUsed: 20104, + MemoryMB: 64136, + CurrentWorkload: 0, + HostIP: "192.168.2.23", + CPUInfo: map[string]interface{}{"vendor": "Intel", + "model": "SandyBridge", + "arch": "x86_64", + "features": []string{"pge", "avx", "clflush", "sep", + "syscall", "vme", "dtes64", "msr", "fsgsbase", + "xsave", "vmx", "erms", "xtpr", "cmov", "smep", + "ssse3", "est", "pat", "monitor", "smx", "pbe", + "lm", "tsc", "nx", "fxsr", "tm", "sse4.1", "pae", + "sse4.2", "pclmuldq", "acpi", "tsc-deadline", + "mmx", "osxsave", "cx8", "mce", "de", "tm2", + "ht", "dca", "lahf_lm", "popcnt", "mca", "pdpe1gb", + "apic", "sse", "f16c", "pse", "ds", "invtsc", "pni", + "rdtscp", "aes", "sse2", "ss", "ds_cpl", "pcid", + "fpu", "cx16", "pse36", "mtrr", "pdcm", "rdrand", "x2apic"}, + "topology": map[string]int{"cores": 10, "threads": 2, "sockets": 1}, + }, + RunningVMs: 1, + FreeDiskGB: 524, + HypervisorVersion: 2002000, + DistAvailableLeast: 521, + LocalGB: 654, + FreeRamMB: 44032, + } + HypervisorDetail_3 = HypervisorDetail{ + HypervisorHostname: "compute-0-3.domain.tld", + Id: 2, + State: "up", + Status: "enabled", + Service: Service{ + Host: "compute-0-3.domain.tld", + DisabledReason: "", + Id: 33, + }, + VcpuUsed: 0, + HypervisorType: "QEMU", + LocalGBUsed: 240, + Vcpus: 24, + MemoryMBUsed: 59016, + MemoryMB: 64136, + CurrentWorkload: 0, + HostIP: "192.168.2.28", + CPUInfo: map[string]interface{}{"vendor": "Intel", + "model": "SandyBridge", + "arch": "x86_64", + "features": []string{"pge", "avx", "clflush", "sep", + "syscall", "vme", "dtes64", "msr", "fsgsbase", + "xsave", "vmx", "erms", "xtpr", "cmov", "smep", + "ssse3", "est", "pat", "monitor", "smx", "pbe", + "lm", "tsc", "nx", "fxsr", "tm", "sse4.1", "pae", + "sse4.2", "pclmuldq", "acpi", "tsc-deadline", + "mmx", "osxsave", "cx8", "mce", "de", "tm2", + "ht", "dca", "lahf_lm", "popcnt", "mca", "pdpe1gb", + "apic", "sse", "f16c", "pse", "ds", "invtsc", "pni", + "rdtscp", "aes", "sse2", "ss", "ds_cpl", "pcid", + "fpu", "cx16", "pse36", "mtrr", "pdcm", "rdrand", "x2apic"}, + "topology": map[string]int{"cores": 10, "threads": 2, "sockets": 1}, + }, + RunningVMs: 0, + FreeDiskGB: 414, + HypervisorVersion: 2002000, + DistAvailableLeast: 633, + LocalGB: 654, + FreeRamMB: 5120, + } + + HypervisorsDetailsListExpected = []HypervisorDetail{HypervisorDetail_3, + HypervisorDetail_1, HypervisorDetail_2} +) + +func HandleHypervisorsDetailsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorsDetailsBody) + }) +} + +func HandleHypervisorDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-hypervisors/14", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorDetailsBody) + }) +} + +var HypervisorServiersListExpected = []HypervisorServersInfo{ + HypervisorServersInfo{ + HypervisorHostname: "compute-0-2.domain.tld", + Id: 14, + State: "up", + Status: "enabled", + Servers: []ServerBriefInfo { + ServerBriefInfo{ + UUID: "b32aa057-1880-46b8-888a-9855414e4a47", + Name: "instance-00000030", + }, + ServerBriefInfo{ + UUID: "1fd61852-2a6e-4f1f-8b62-c1af7f04e997", + Name: "instance-0000015c", + }, + }, + }, +} + +func HandleHypervisorServerListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-hypervisors/compute-0-2.domain.tld/servers", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorServersList) + }) +} + +var HypervisorUptimeExpected = HypervisorUptimeInfo{ + HypervisorHostname: "compute-0-2.domain.tld", + Id: 14, + State: "up", + Status: "enabled", + Uptime: " 15:22:34 up 20:27, 4 users, load average: 2.23, 2.31, 2.35\n", +} + +func HandleHypervisorUptimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-hypervisors/14/uptime", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorUptimeBody) + }) +} diff --git a/openstack/compute/v2/privileged/hypervisors/objects.go b/openstack/compute/v2/privileged/hypervisors/objects.go new file mode 100644 index 00000000..77a901a8 --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/objects.go @@ -0,0 +1,59 @@ +package hypervisors + + +type Hypervisor struct { + HypervisorHostname string `mapstructure:"hypervisor_hostname"` + Id int + State string + Status string +} + +type Service struct { + Host string + DisabledReason string `mapstructure:"disabled_reason"` + Id int +} + +type HypervisorDetail struct { + HypervisorHostname string `mapstructure:"hypervisor_hostname"` + Id int + State string + Status string + Service Service + VcpuUsed int16 `mapstructure:"vcpus_used"` + HypervisorType string `mapstructure:"hypervisor_type"` + LocalGBUsed int16 `mapstructure:"local_gb_used"` + Vcpus int16 + MemoryMBUsed int32 `mapstructure:"memory_mb_used"` + MemoryMB int32 `mapstructure:"memory_mb"` + CurrentWorkload int16 `mapstructure:"current_workload"` + HostIP string `mapstructure:"host_ip"` + CPUInfo map[string]interface{} `mapstructure:"cpu_info"` + RunningVMs int `mapstructure:"running_vms"` + FreeDiskGB int16 `mapstructure:"free_disk_gb"` + HypervisorVersion int32 `mapstructure:"hypervisor_version"` + DistAvailableLeast int16 `mapstructure:"disk_available_least"` + LocalGB int16 `mapstructure:"local_gb"` + FreeRamMB int32 `mapstructure:"free_ram_mb"` +} + +type ServerBriefInfo struct { + UUID string + Name string +} + +type HypervisorServersInfo struct { + HypervisorHostname string `mapstructure:"hypervisor_hostname"` + Id int + State string + Status string + Servers []ServerBriefInfo +} + +type HypervisorUptimeInfo struct { + HypervisorHostname string `mapstructure:"hypervisor_hostname"` + Id int + State string + Status string + Uptime string +} diff --git a/openstack/compute/v2/privileged/hypervisors/requests.go b/openstack/compute/v2/privileged/hypervisors/requests.go new file mode 100644 index 00000000..d484b7d6 --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/requests.go @@ -0,0 +1,57 @@ +package hypervisors + +import ( + "github.com/rackspace/gophercloud" +) + +// TODO: introduce list options for the pagination - marker, limit, page_size +// as this is defined in gophercloud/openstack/v2/servers/requests.go:{row 25} +// currently OpenStack paging only supports servers and flavors + +// List makes a request against the API to list hypervisors accessible to you +// in pagination way (Not supported now by OpenStack for hypervisors). +func List(client *gophercloud.ServiceClient) GetResult { + var result GetResult + _, result.Err = client.Get(getListURL(client), + &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return result +} + +// Get details about specified by id hypervisor +func GetDetailsList(client *gophercloud.ServiceClient) GetResult { + var result GetResult + _, result.Err = client.Get(getDetailedListURL(client), &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return result +} + +// Get details about specified by id hypervisor +func GetDetail(client *gophercloud.ServiceClient, id string) GetResult { + var result GetResult + _, result.Err = client.Get(getDetailURL(client, id), &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return result +} + +// Get VMs booted on specified by hostname hypervisor +func GetHypervisorServers(client *gophercloud.ServiceClient, hypervisorHostname string) GetResult { + var result GetResult + _, result.Err = client.Get(getHypervisorServersURL(client, hypervisorHostname), + &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return result +} + +func GetHypervisorUptime(client *gophercloud.ServiceClient, id string) GetResult { + var result GetResult + _, result.Err = client.Get(getHypervisorUptimeURL(client, id), + &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return result +} diff --git a/openstack/compute/v2/privileged/hypervisors/requests_test.go b/openstack/compute/v2/privileged/hypervisors/requests_test.go new file mode 100644 index 00000000..a1cc5f74 --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/requests_test.go @@ -0,0 +1,83 @@ +package hypervisors + +import ( + "testing" + + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" +) + + +func TestListHypervisors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHypervisorsListSuccessfully(t) + + actual, err := List(client.ServiceClient()).Extract() + + th.AssertNoErr(t, err) + + if len(actual) != 3 { + t.Errorf("Expected 3 hypervisors, saw %d", len(actual)) + } + + th.CheckDeepEquals(t, ListHypervisorsExpected[0], actual[0]) + th.CheckDeepEquals(t, ListHypervisorsExpected[1], actual[1]) + th.CheckDeepEquals(t, ListHypervisorsExpected[2], actual[2]) +} + +func TestListDetailsHypervisors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHypervisorsDetailsSuccessfully(t) + + actual, err := GetDetailsList(client.ServiceClient()).ExtractDetails() + + th.AssertNoErr(t, err) + + if len(actual) != 3 { + t.Errorf("Expected 3 hypervisors, saw %d", len(actual)) + } + + th.CheckDeepEquals(t, HypervisorsDetailsListExpected[0], actual[0]) + th.CheckDeepEquals(t, HypervisorsDetailsListExpected[1], actual[1]) + th.CheckDeepEquals(t, HypervisorsDetailsListExpected[2], actual[2]) +} + +func TestHypervisorDetails(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHypervisorDetailSuccessfully(t) + + actual, err := GetDetail(client.ServiceClient(), "14").ExtractDetail() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HypervisorDetail_2, *actual) +} + + +func TestServersList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHypervisorServerListSuccessfully(t) + + actual, err := GetHypervisorServers(client.ServiceClient(), "compute-0-2.domain.tld").ExtractServersInfo() + + th.AssertNoErr(t, err) + if len(actual) != 1 { + t.Errorf("Expected 1 hypervisors, saw %d", len(actual)) + } + + th.CheckDeepEquals(t, HypervisorServiersListExpected[0], actual[0]) +} + +func TestHypervisorUptime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHypervisorUptimeSuccessfully(t) + + actual, err := GetHypervisorUptime(client.ServiceClient(), "14").ExtractUptime() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HypervisorUptimeExpected, *actual) +} diff --git a/openstack/compute/v2/privileged/hypervisors/results.go b/openstack/compute/v2/privileged/hypervisors/results.go new file mode 100644 index 00000000..c0c39b5f --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/results.go @@ -0,0 +1,109 @@ +package hypervisors + +import ( + "github.com/rackspace/gophercloud" + "github.com/mitchellh/mapstructure" + "reflect" +) + + +// Decodes response body. Accepts empty entity pointer and initiates +// this entity with decoded values. +func processResponse(response interface{}, body interface{}) error{ + config := &mapstructure.DecoderConfig{ + DecodeHook: toMapFromString, + Result: response, + } + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + err = decoder.Decode(body) + if err != nil { + return err + } + return err +} + +type hypervisorResult struct { + gophercloud.Result +} + +// GetResult temporarily contains the response from a Get call. +type GetResult struct { + hypervisorResult +} + + +// Extract interprets any hypervisorResult as a Hypervisor, if possible. +func (r hypervisorResult) Extract() ([]Hypervisor, error) { + if r.Err != nil { + return nil, r.Err + } + + var response struct { + Hypervisors []Hypervisor `mapstructure:"hypervisors"` + } + err := processResponse(&response, r.Body) + return response.Hypervisors, err +} + +// Extract interprets any hypervisorResult as a HypervisorDetails, if possible. +func (r hypervisorResult) ExtractDetails() ([]HypervisorDetail, error) { + if r.Err != nil { + return nil, r.Err + } + + var response struct { + Hypervisor []HypervisorDetail `mapstructure:"hypervisors"` + } + err := processResponse(&response, r.Body) + return response.Hypervisor, err +} + +// Extract interprets any hypervisorResult as a HypervisorDetail, if possible. +func (r hypervisorResult) ExtractDetail() (*HypervisorDetail, error) { + if r.Err != nil { + return nil, r.Err + } + + var response struct { + Hypervisor HypervisorDetail `mapstructure:"hypervisor"` + } + err := processResponse(&response, r.Body) + return &response.Hypervisor, err +} + +// Extract interprets any hypervisorResult as a HypervisorServersInfo, if possible. +func (r hypervisorResult) ExtractServersInfo() ([]HypervisorServersInfo, error) { + if r.Err != nil { + return nil, r.Err + } + + var response struct { + Hypervisor []HypervisorServersInfo `mapstructure:"hypervisors"` + } + err := processResponse(&response, r.Body) + return response.Hypervisor, err +} + +// Extract interprets any hypervisorResult as a HypervisorUptime, if possible. +func (r hypervisorResult) ExtractUptime() (*HypervisorUptimeInfo, error) { + if r.Err != nil { + return nil, r.Err + } + + var response struct { + Hypervisor HypervisorUptimeInfo `mapstructure:"hypervisor"` + } + err := processResponse(&response, r.Body) + return &response.Hypervisor, err +} + +func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) { + if (from == reflect.String) && (to == reflect.Map) { + return map[string]interface{}{}, nil + } + return data, nil +} diff --git a/openstack/compute/v2/privileged/hypervisors/urls.go b/openstack/compute/v2/privileged/hypervisors/urls.go new file mode 100644 index 00000000..56477691 --- /dev/null +++ b/openstack/compute/v2/privileged/hypervisors/urls.go @@ -0,0 +1,27 @@ +package hypervisors + +import ( + "github.com/rackspace/gophercloud" + "fmt" +) + + +func getListURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("os-hypervisors") +} + +func getDetailedListURL(client *gophercloud.ServiceClient) string{ + return client.ServiceURL("os-hypervisors/detail") +} + +func getDetailURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(fmt.Sprintf("os-hypervisors/%s", id)) +} + +func getHypervisorServersURL(client *gophercloud.ServiceClient, hypervisorHostname string) string { + return client.ServiceURL(fmt.Sprintf("os-hypervisors/%s/servers", hypervisorHostname)) +} + +func getHypervisorUptimeURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(fmt.Sprintf("os-hypervisors/%s/uptime", id)) +}