Add v2 server.go and its unit test.

Change-Id: Iae151be1afb5ce41cec7998cd09f4191a192c2ca
This commit is contained in:
Yibin Tai 2014-10-20 16:37:52 -07:00
parent 03aa5209e0
commit c9c09d9901
4 changed files with 454 additions and 8 deletions

260
Compute/v2/server.go Normal file
View 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
View 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
}

View File

@ -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
}

View File

@ -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
req, err = http.NewRequest(method, url, nil)
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)
}
}