Add v2 server.go and its unit test.
Change-Id: Iae151be1afb5ce41cec7998cd09f4191a192c2ca
This commit is contained in:
parent
03aa5209e0
commit
c9c09d9901
260
Compute/v2/server.go
Normal file
260
Compute/v2/server.go
Normal file
@ -0,0 +1,260 @@
|
||||
// server.go
|
||||
package compute
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.openstack.org/stackforge/golang-client.git/misc"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
)
|
||||
|
||||
type serversResp struct {
|
||||
ServerInfos []ServerInfo `json:"servers"`
|
||||
}
|
||||
|
||||
type serversDetailResp struct {
|
||||
ServerInfoDetails []ServerInfoDetail `json:"servers"`
|
||||
}
|
||||
|
||||
type serverDetailResp struct {
|
||||
ServerInfoDetail ServerInfoDetail `json:"server"`
|
||||
}
|
||||
|
||||
type ServerInfo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Links []Link `json:"links"`
|
||||
}
|
||||
|
||||
type ServerInfoDetail struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Created *time.Time `json:"created"`
|
||||
Updated *time.Time `json:"updated"`
|
||||
HostId string `json:"hostId"`
|
||||
Addresses map[string][]Address `json:"addresses"`
|
||||
Links []Link `json:"links"`
|
||||
Image Image `json:"image"`
|
||||
Flavor Flavor `json:"flavor"`
|
||||
TaskState string `json:"OS-EXT-STS:task_state"`
|
||||
VMState string `json:"OS-EXT-STS:vm_state"`
|
||||
PowerState int `json:"OS-EXT-STS:power_state"`
|
||||
AvailabilityZone string `json:"OS-EXT-AZ:availability_zone:"`
|
||||
UserId string `json:"user_id"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
AccessIPv4 string `json:"accessIPv4"`
|
||||
AccessIPv6 string `json:"accessIPv6"`
|
||||
ConfigDrive string `json:"config_drive"`
|
||||
Progress int `json:"progress"`
|
||||
MetaData map[string]string `json:"metadata"`
|
||||
AdminPass string `json:"adminPass"`
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
HRef string `json:"href"`
|
||||
Rel string `json:"rel"`
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Id string `json:"id"`
|
||||
Links []Link `json:"links"`
|
||||
}
|
||||
|
||||
type Flavor struct {
|
||||
Id string `json:"id"`
|
||||
Links []Link `json:"links"`
|
||||
}
|
||||
|
||||
type SecurityGroups struct {
|
||||
SecurityGroups []SecurityGroup `json:"security_groups"`
|
||||
}
|
||||
|
||||
type SecurityGroup struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Addr string `json:"addr"`
|
||||
Version int `json:"version"`
|
||||
Type string `json:"OS-EXT-IPS:type"`
|
||||
MacAddr string `json:"OS-EXT-IPS-MAC:mac_addr"`
|
||||
}
|
||||
|
||||
type ByName []ServerInfo
|
||||
|
||||
func (a ByName) Len() int { return len(a) }
|
||||
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
||||
|
||||
func GetServerInfos(url string, token string) (serverInfos []ServerInfo, err error) {
|
||||
|
||||
_, body, err := misc.CallGetAPI(url+"/servers", "X-Auth-Token", token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sr = serversResp{}
|
||||
if err = json.Unmarshal([]byte(body), &sr); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serverInfos = sr.ServerInfos
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func GetServerInfoDetails(url string, token string) (serverInfoDetails []ServerInfoDetail, err error) {
|
||||
|
||||
_, body, err := misc.CallGetAPI(url+"/servers/detail", "X-Auth-Token", token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sr = serversDetailResp{}
|
||||
if err = json.Unmarshal(body, &sr); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serverInfoDetails = sr.ServerInfoDetails
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func GetServerInfoDetail(url string, token string, id string) (serverInfoDetail ServerInfoDetail, err error) {
|
||||
reqUrl := fmt.Sprintf("%s/servers/%s", url, id)
|
||||
_, body, err := misc.CallGetAPI(reqUrl, "X-Auth-Token", token)
|
||||
if err != nil {
|
||||
return serverInfoDetail, err
|
||||
}
|
||||
|
||||
serverDetailResp := serverDetailResp{}
|
||||
if err = json.Unmarshal(body, &serverDetailResp); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serverInfoDetail = serverDetailResp.ServerInfoDetail
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteServer(url string, token string, id string) (err error) {
|
||||
reqUrl := fmt.Sprintf("%s/servers/%s", url, id)
|
||||
err = misc.CallDeleteAPI(reqUrl, "X-Auth-Token", token)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
type ServerNetworkParameters struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Port string `json:"port"`
|
||||
}
|
||||
|
||||
type ServerServiceParameters struct {
|
||||
Name string `json:"name"`
|
||||
ImageRef string `json:"imageRef"`
|
||||
SSHKey string `json:"sshkey"`
|
||||
FlavorRef int32 `json:"flavorRef"`
|
||||
MaxCount int32 `json:"maxcount"`
|
||||
MinCount int32 `json:"mincount"`
|
||||
UserData string `json:"userdata"`
|
||||
Networks []ServerNetworkParameters `json:"networks"`
|
||||
SecurityGroup []SecurityGroup `json:"securitygroups"`
|
||||
}
|
||||
|
||||
type ServerRequestInfo map[string]ServerServiceParameters
|
||||
|
||||
type ServerService struct {
|
||||
Url string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func CreateServer(client *http.Client, ss ServerService, serverRequestInfo ServerRequestInfo) (serverInfoDetail ServerInfoDetail, err error) {
|
||||
|
||||
reqBody, err := json.Marshal(serverRequestInfo)
|
||||
if err != nil {
|
||||
return serverInfoDetail, err
|
||||
}
|
||||
|
||||
reqUrl := ss.Url + "/servers"
|
||||
|
||||
req, err := http.NewRequest("POST", reqUrl, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return serverInfoDetail, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("X-Auth-Token", ss.Token)
|
||||
|
||||
misc.LogDebug("CreateServer-----------------------------------httputil.DumpRequestOut-------BEGIN")
|
||||
dumpReqByte, err := httputil.DumpRequestOut(req, true)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
misc.LogDebug(string(dumpReqByte))
|
||||
misc.LogDebug("CreateServer-----------------------------------httputil.DumpRequestOut-------END")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
misc.LogDebug("CreateServer-----------------------------------httputil.DumpResponse-------BEGIN")
|
||||
dumpRspByte, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
misc.LogDebug(string(dumpRspByte))
|
||||
misc.LogDebug("CreateServer-----------------------------------httputil.DumpResponse-------END")
|
||||
|
||||
if err != nil {
|
||||
return serverInfoDetail, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&serverInfoDetail)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if !(resp.StatusCode == 201 || resp.StatusCode == 202) {
|
||||
err = errors.New(fmt.Sprintf("Error: status code != 201 or 202, object not created. Status: (%s), reqUrl: %s, reqBody: %s, resp: %s, respBody: %s",
|
||||
resp.Status, reqUrl, reqBody, resp, resp.Body))
|
||||
|
||||
log.Printf(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func ServerAction(url string, token string, id string, action string, key string, value string) (err error) {
|
||||
var reqBody = []byte(fmt.Sprintf(`
|
||||
{
|
||||
"%s":
|
||||
{
|
||||
"%s": "%s"
|
||||
}
|
||||
}`, action, key, value))
|
||||
|
||||
resp, err := misc.CallAPI("POST", url+"/servers/"+id+"/action", &reqBody, "X-Auth-Token", token)
|
||||
|
||||
if err = misc.CheckHttpResponseStatusCode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = nil
|
||||
return
|
||||
}
|
110
Compute/v2/server_test.go
Normal file
110
Compute/v2/server_test.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
//
|
||||
// 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 compute_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"git.openstack.org/stackforge/golang-client.git/Compute/v2"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreateServer(t *testing.T) {
|
||||
|
||||
now := time.Now()
|
||||
|
||||
var serverInfoDetail = compute.ServerInfoDetail{
|
||||
"my_server_id_1",
|
||||
"my_server_name_1",
|
||||
"my_server_status_1",
|
||||
&now, // created time
|
||||
&now, // updated time
|
||||
"my_server_hostId_1",
|
||||
make(map[string][]compute.Address),
|
||||
[]compute.Link{{"href_1", "rel_1"}, {"href_2", "rel_2"}},
|
||||
compute.Image{"image_id1", []compute.Link{{"href_1", "rel_1"}, {"href_2", "rel_2"}}},
|
||||
compute.Flavor{"image_id1", []compute.Link{{"href_1", "rel_1"}, {"href_2", "rel_2"}}},
|
||||
"my_OS-EXT-STS_task_state_1",
|
||||
"my_OS-EXT-STS_vm_state_1",
|
||||
1, // PowerState
|
||||
"my_zone_a",
|
||||
"my_user_id_1",
|
||||
"my_tenant_id_1",
|
||||
"192.168.0.12",
|
||||
"my_accessIPv6_1",
|
||||
"my_config_drive_1",
|
||||
2, // Progress
|
||||
make(map[string]string),
|
||||
"my_adminPass_1",
|
||||
}
|
||||
|
||||
marshaledserverInfoDetail, err := json.Marshal(serverInfoDetail)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var apiServer = httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
w.Header().Set("Version", "my_server_version.1.0.0")
|
||||
w.WriteHeader(201)
|
||||
w.Write([]byte(marshaledserverInfoDetail))
|
||||
return
|
||||
}
|
||||
t.Error(errors.New("Failed: r.Method == POST"))
|
||||
}))
|
||||
defer apiServer.Close()
|
||||
|
||||
serverService := compute.ServerService{
|
||||
apiServer.URL,
|
||||
"my_token"}
|
||||
|
||||
var serverServiceParameters = compute.ServerServiceParameters{
|
||||
"my_server",
|
||||
"8c3cd338-1282-4fbb-bbaf-2256ff97c7b7", // imageRef
|
||||
"my_key_name",
|
||||
101, // flavorRef
|
||||
1, // maxcount
|
||||
1, // mincount
|
||||
"my_user_data",
|
||||
[]compute.ServerNetworkParameters{
|
||||
compute.ServerNetworkParameters{"1111d337-0282-4fbb-bbaf-2256ff97c7b7", "881"},
|
||||
compute.ServerNetworkParameters{"2222d337-0282-4fbb-bbaf-2256ff97c7b7", "882"},
|
||||
compute.ServerNetworkParameters{"3333d337-0282-4fbb-bbaf-2256ff97c7b7", "883"}},
|
||||
[]compute.SecurityGroup{
|
||||
compute.SecurityGroup{"my_security_group_123"},
|
||||
compute.SecurityGroup{"my_security_group_456"}}}
|
||||
|
||||
var serverRequestInfo = make(compute.ServerRequestInfo)
|
||||
serverRequestInfo["server"] = serverServiceParameters
|
||||
|
||||
result, err := compute.CreateServer(new(http.Client), serverService, serverRequestInfo)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result, serverInfoDetail) {
|
||||
t.Error(errors.New("Failed: result != expected serverInfoDetail"))
|
||||
}
|
||||
|
||||
err = nil
|
||||
return
|
||||
|
||||
}
|
@ -153,3 +153,18 @@ func auth(url, jsonStr *string) (Auth, error) {
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (auth Auth) EndpointList() (list map[string]string) {
|
||||
|
||||
list = make(map[string]string)
|
||||
|
||||
for _, v := range auth.Access.ServiceCatalog {
|
||||
for _, endPoint := range v.Endpoints {
|
||||
if endPoint.Region == "region-b.geo-1" {
|
||||
list[v.Type] = endPoint.PublicURL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
75
misc/util.go
75
misc/util.go
@ -18,9 +18,39 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
)
|
||||
|
||||
var zeroByte = &([]byte{}) //pointer to empty []byte
|
||||
|
||||
func CallDeleteAPI(url string, h ...string) (err error) {
|
||||
resp, err := CallAPI("DELETE", url, zeroByte, h...)
|
||||
|
||||
return CheckHttpResponseStatusCode(resp)
|
||||
}
|
||||
|
||||
func CallGetAPI(url string, h ...string) (header http.Header, responseByteArr []byte, err error) {
|
||||
resp, err := CallAPI("GET", url, zeroByte, h...)
|
||||
header = resp.Header
|
||||
|
||||
if err = CheckHttpResponseStatusCode(resp); err != nil {
|
||||
return header, nil, err
|
||||
}
|
||||
|
||||
responseByteArr, err = ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
return header, responseByteArr, err
|
||||
}
|
||||
|
||||
return header, responseByteArr, nil
|
||||
}
|
||||
|
||||
//CallAPI sends an HTTP request using "method" to "url".
|
||||
//For uploading / sending file, caller needs to set the "content". Otherwise,
|
||||
//set it to zero length []byte. If Header fields need to be set, then set it in
|
||||
@ -45,21 +75,46 @@ func CallAPI(method, url string, content *[]byte, h ...string) (*http.Response,
|
||||
//comment it out, if you are confident in your test suites.
|
||||
var req *http.Request
|
||||
var err error
|
||||
contentLength := int64(len(*content))
|
||||
if contentLength > 0 {
|
||||
req, err = http.NewRequest(method, url, bytes.NewReader(*content))
|
||||
//req.Body = *(new(io.ReadCloser)) //these 3 lines do not work but I am
|
||||
//req.Body.Read(content) //keeping them here in case I wonder why
|
||||
//req.Body.Close() //I did not implement it this way :)
|
||||
} else {
|
||||
req, err = http.NewRequest(method, url, nil)
|
||||
}
|
||||
req.ContentLength = contentLength
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(h)-1; i = i + 2 {
|
||||
req.Header.Set(h[i], h[i+1])
|
||||
}
|
||||
req.ContentLength = int64(len(*content))
|
||||
if req.ContentLength > 0 {
|
||||
req.Body = readCloser{bytes.NewReader(*content)}
|
||||
//req.Body = *(new(io.ReadCloser)) //these 3 lines do not work but I am
|
||||
//req.Body.Read(content) //keeping them here in case I wonder why
|
||||
//req.Body.Close() //I did not implement it this way :)
|
||||
|
||||
LogDebug("CallAPI-----------------------------------httputil.DumpRequestOut-------BEGIN")
|
||||
dumpReqByte, err := httputil.DumpRequestOut(req, true)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
return (new(http.Client)).Do(req)
|
||||
LogDebug(string(dumpReqByte))
|
||||
LogDebug("CallAPI-----------------------------------httputil.DumpRequestOut-------END")
|
||||
|
||||
resp, err := (new(http.Client)).Do(req)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
LogDebug("CallAPI-----------------------------------httputil.DumpResponse-------BEGIN")
|
||||
dumpRspByte, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
LogDebug(string(dumpRspByte))
|
||||
LogDebug("CallAPI-----------------------------------httputil.DumpResponse-------END")
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type readCloser struct {
|
||||
@ -108,3 +163,9 @@ func CheckHttpResponseStatusCode(resp *http.Response) error {
|
||||
}
|
||||
return errors.New("Error: unexpected response status code")
|
||||
}
|
||||
|
||||
func LogDebug(s string) {
|
||||
if len(os.Getenv("LOG_DEBUG")) != 0 {
|
||||
log.Printf(s)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user