mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-22 13:33:52 +03:00
F #2772: GOCA - leverage use of current xmlrpc package and update error handling
This commit is contained in:
parent
e65dd8c251
commit
96e71ec99d
153
src/oca/go/src/goca/errors.go
Normal file
153
src/oca/go/src/goca/errors.go
Normal file
@ -0,0 +1,153 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Client errors
|
||||
|
||||
/*
|
||||
The OpenNebula server use the library xmlrpc-c.
|
||||
|
||||
List of xmlrpc fault codes:
|
||||
XMLRPC_INTERNAL_ERROR -500
|
||||
XMLRPC_TYPE_ERROR -501
|
||||
XMLRPC_INDEX_ERROR -502
|
||||
XMLRPC_PARSE_ERROR -503
|
||||
XMLRPC_NETWORK_ERROR -504
|
||||
XMLRPC_TIMEOUT_ERROR -505
|
||||
XMLRPC_NO_SUCH_METHOD_ERROR -506
|
||||
XMLRPC_REQUEST_REFUSED_ERROR -507
|
||||
XMLRPC_INTROSPECTION_DISABLED_ERROR -508
|
||||
XMLRPC_LIMIT_EXCEEDED_ERROR -509
|
||||
XMLRPC_INVALID_UTF8_ERROR -510
|
||||
*/
|
||||
|
||||
// ClientErrCode is used by ClientError to give more accurate information
|
||||
type ClientErrCode int
|
||||
|
||||
const (
|
||||
// ClientReqBuild if we can't build the xmlrpc request, ie. http or xml error
|
||||
ClientReqBuild ClientErrCode = iota
|
||||
|
||||
// ClientReqHTTP if we can't do a request, ie. connectivity, redirection problems...
|
||||
ClientReqHTTP
|
||||
|
||||
// ClientRespHTTP if we have an http response error or if we can't have a full response
|
||||
ClientRespHTTP
|
||||
|
||||
// ClientRespXMLRPCFault if the response is an xmlrpc fault
|
||||
ClientRespXMLRPCFault
|
||||
|
||||
// ClientRespXMLRPCParse if we can't parse the xmlrpc from the response
|
||||
ClientRespXMLRPCParse
|
||||
|
||||
// ClientRespONeParse if we can't parse a correct OpenNebula response
|
||||
ClientRespONeParse
|
||||
)
|
||||
|
||||
func (s ClientErrCode) String() string {
|
||||
switch s {
|
||||
case ClientReqBuild:
|
||||
return "REQUEST_BUILD"
|
||||
case ClientReqHTTP:
|
||||
return "REQUEST_HTTP"
|
||||
case ClientRespHTTP:
|
||||
return "RESPONSE_HTTP"
|
||||
case ClientRespXMLRPCFault:
|
||||
return "RESPONSE_XMLRPC_FAULT"
|
||||
case ClientRespXMLRPCParse:
|
||||
return "RESPONSE_XMLRPC_PARSE"
|
||||
case ClientRespONeParse:
|
||||
return "RESPONSE_ONE_PARSE"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ClientError is returned when we can't have a complete and well formed response from the client
|
||||
type ClientError struct {
|
||||
Code ClientErrCode
|
||||
msg string
|
||||
|
||||
// Provide more informations to the user
|
||||
httpResp *http.Response
|
||||
err error
|
||||
}
|
||||
|
||||
// Cause allow to get the underlying error
|
||||
func (e *ClientError) Cause() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// GetHTTPResponse return the http response for the codes ClientRespXMLRPCFault, ClientRespXMLRPCParse, ClientRespONeParse
|
||||
func (e *ClientError) GetHTTPResponse() *http.Response {
|
||||
return e.httpResp
|
||||
}
|
||||
|
||||
func (e *ClientError) Error() string {
|
||||
if e.err != nil {
|
||||
return fmt.Sprintf("GOCA client error [%s]: %s: %s", e.Code.String(), e.msg, e.Cause())
|
||||
}
|
||||
return fmt.Sprintf("GOCA client error [%s]: %s", e.Code.String(), e.msg)
|
||||
}
|
||||
|
||||
// OpenNebula errors
|
||||
|
||||
// OneErrCode is the error code from an OpenNebula error response
|
||||
type OneErrCode int
|
||||
|
||||
const (
|
||||
// OneSuccess code for a successful response
|
||||
OneSuccess = 0x0000
|
||||
|
||||
// OneAuthenticationError code if the user could not be authenticated
|
||||
OneAuthenticationError = 0x0100
|
||||
|
||||
// OneAuthorizationError code if the user is not authorized to perform the requested action
|
||||
OneAuthorizationError = 0x0200
|
||||
|
||||
// OneNoExistsError code if the requested resource does not exist
|
||||
OneNoExistsError = 0x0400
|
||||
|
||||
// OneActionError code if the state is wrong to perform the action
|
||||
OneActionError = 0x0800
|
||||
|
||||
// OneXMLRPCAPIError code if there is wrong parameter passed, e.g. param should be -1 or -2, but -3 was received
|
||||
OneXMLRPCAPIError = 0x1000
|
||||
|
||||
// OneInternalError code if there is an internal error, e.g. the resource could not be loaded from the DB
|
||||
OneInternalError = 0x2000
|
||||
)
|
||||
|
||||
func (s OneErrCode) String() string {
|
||||
switch s {
|
||||
case OneSuccess:
|
||||
return "SUCCESS"
|
||||
case OneAuthenticationError:
|
||||
return "AUTHENTICATION"
|
||||
case OneAuthorizationError:
|
||||
return "AUTHORIZATION"
|
||||
case OneNoExistsError:
|
||||
return "NO_EXISTS"
|
||||
case OneActionError:
|
||||
return "ACTION"
|
||||
case OneXMLRPCAPIError:
|
||||
return "XML_RPC_API"
|
||||
case OneInternalError:
|
||||
return "INTERNAL"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseError is a complete well formed OpenNebula response error
|
||||
type ResponseError struct {
|
||||
Code OneErrCode
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *ResponseError) Error() string {
|
||||
return fmt.Sprintf("OpenNebula error [%s]: %s", e.Code.String(), e.msg)
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -13,53 +15,6 @@ var (
|
||||
client *oneClient
|
||||
)
|
||||
|
||||
// OneClientErrCode is the error code from the OpenNebula response
|
||||
type OneClientErrCode int
|
||||
|
||||
const (
|
||||
// OneSuccess code for a successful response
|
||||
OneSuccess = 0x0000
|
||||
|
||||
// OneAuthenticationError code if the user could not be authenticated
|
||||
OneAuthenticationError = 0x0100
|
||||
|
||||
// OneAuthorizationError code if the user is not authorized to perform the requested action
|
||||
OneAuthorizationError = 0x0200
|
||||
|
||||
// OneNoExistsError code if the requested resource does not exist
|
||||
OneNoExistsError = 0x0400
|
||||
|
||||
// OneActionError code if the state is wrong to perform the action
|
||||
OneActionError = 0x0800
|
||||
|
||||
// OneXmlRpcApiError code if there is wrong parameter passed, e.g. param should be -1 or -2, but -3 was received
|
||||
OneXMLRPCAPIError = 0x1000
|
||||
|
||||
// OneInteralError code if there is an internal error, e.g. the resource could not be loaded from the DB
|
||||
OneInteralError = 0x2000
|
||||
)
|
||||
|
||||
func (s OneClientErrCode) String() string {
|
||||
switch s {
|
||||
case OneSuccess:
|
||||
return "SUCCESS"
|
||||
case OneAuthenticationError:
|
||||
return "AUTHENTICATION"
|
||||
case OneAuthorizationError:
|
||||
return "AUTHORIZATION"
|
||||
case OneNoExistsError:
|
||||
return "NO_EXISTS"
|
||||
case OneActionError:
|
||||
return "ACTION"
|
||||
case OneXMLRPCAPIError:
|
||||
return "XML_RPC_API"
|
||||
case OneInteralError:
|
||||
return "INTERNAL"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// OneConfig contains the information to communicate with OpenNebula
|
||||
type OneConfig struct {
|
||||
// Token is the authentication string. In the format of <user>:<password>
|
||||
@ -71,36 +26,9 @@ type OneConfig struct {
|
||||
}
|
||||
|
||||
type oneClient struct {
|
||||
url string
|
||||
token string
|
||||
xmlrpcClient *xmlrpc.Client
|
||||
xmlrpcClientError error
|
||||
}
|
||||
|
||||
type InitError struct {
|
||||
token string
|
||||
xmlRpcErr error
|
||||
}
|
||||
|
||||
func (r *InitError) Error() string {
|
||||
return fmt.Sprintf("Unitialized client. Token: '%s', xmlrpcClientError: '%s'", r.token, r.xmlRpcErr)
|
||||
}
|
||||
|
||||
type BadResponseError struct {
|
||||
expectedType string
|
||||
}
|
||||
|
||||
func (r *BadResponseError) Error() string {
|
||||
return fmt.Sprintf("Unexpected XML-RPC response, Expected: %s", r.expectedType)
|
||||
}
|
||||
|
||||
// ResponseError contains the error datas from an OpenNebula error reponse
|
||||
type ResponseError struct {
|
||||
Code OneClientErrCode
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *ResponseError) Error() string {
|
||||
return fmt.Sprintf("%s (%s)", e.msg, e.Code.String())
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
type response struct {
|
||||
@ -166,12 +94,10 @@ func NewConfig(user string, password string, xmlrpcURL string) OneConfig {
|
||||
// SetClient assigns a value to the client variable
|
||||
func SetClient(conf OneConfig) {
|
||||
|
||||
xmlrpcClient, xmlrpcClientError := xmlrpc.NewClient(conf.XmlrpcURL, nil)
|
||||
|
||||
client = &oneClient{
|
||||
url: conf.XmlrpcURL,
|
||||
token: conf.Token,
|
||||
xmlrpcClient: xmlrpcClient,
|
||||
xmlrpcClientError: xmlrpcClientError,
|
||||
httpClient: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,25 +133,67 @@ func (c *oneClient) Call(method string, args ...interface{}) (*response, error)
|
||||
errCode int64
|
||||
)
|
||||
|
||||
if c.xmlrpcClientError != nil {
|
||||
return nil, &InitError{token: c.token, xmlRpcErr: c.xmlrpcClientError}
|
||||
}
|
||||
|
||||
result := []interface{}{}
|
||||
|
||||
xmlArgs := make([]interface{}, len(args)+1)
|
||||
|
||||
xmlArgs[0] = c.token
|
||||
copy(xmlArgs[1:], args[:])
|
||||
|
||||
err := c.xmlrpcClient.Call(method, xmlArgs, &result)
|
||||
buf, err := xmlrpc.EncodeMethodCall(method, xmlArgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil,
|
||||
&ClientError{Code: ClientReqBuild, msg: "xmlrpc request encoding", err: err}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", c.url, bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
return nil,
|
||||
&ClientError{Code: ClientReqBuild, msg: "http request build", err: err}
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil,
|
||||
&ClientError{Code: ClientReqHTTP, msg: "http make request", err: err}
|
||||
}
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return nil, &ClientError{
|
||||
Code: ClientRespHTTP,
|
||||
msg: fmt.Sprintf("http status code: %d", resp.StatusCode),
|
||||
httpResp: resp,
|
||||
}
|
||||
}
|
||||
|
||||
respData, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil,
|
||||
&ClientError{Code: ClientRespHTTP, msg: "read http response body", err: err}
|
||||
}
|
||||
|
||||
// Server side XML-RPC library: xmlrpc-c
|
||||
xmlrpcResp := xmlrpc.NewResponse(respData)
|
||||
|
||||
// Handle the <fault> tag in the xml server response
|
||||
if xmlrpcResp.Failed() {
|
||||
err = xmlrpcResp.Err()
|
||||
return nil,
|
||||
&ClientError{ClientRespXMLRPCFault, "server response", resp, err}
|
||||
}
|
||||
|
||||
result := []interface{}{}
|
||||
|
||||
// Unmarshall the XML-RPC response
|
||||
if err := xmlrpcResp.Unmarshal(&result); err != nil {
|
||||
return nil,
|
||||
&ClientError{ClientRespXMLRPCParse, "unmarshal xmlrpc", resp, err}
|
||||
}
|
||||
|
||||
// Parse according the XML-RPC OpenNebula API documentation
|
||||
status, ok = result[0].(bool)
|
||||
if ok == false {
|
||||
return nil, &BadResponseError{expectedType: "Index 0: Boolean"}
|
||||
return nil,
|
||||
&ClientError{ClientRespONeParse, "index 0: boolean expected", resp, err}
|
||||
}
|
||||
|
||||
body, ok = result[1].(string)
|
||||
@ -234,18 +202,23 @@ func (c *oneClient) Call(method string, args ...interface{}) (*response, error)
|
||||
if ok == false {
|
||||
bodyBool, ok = result[1].(bool)
|
||||
if ok == false {
|
||||
return nil, &BadResponseError{expectedType: "Index 1: Int or String"}
|
||||
return nil,
|
||||
&ClientError{ClientRespONeParse, "index 1: boolean expected", resp, err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errCode, ok = result[2].(int64)
|
||||
if ok == false {
|
||||
return nil, &BadResponseError{expectedType: "Index 2: Int"}
|
||||
return nil,
|
||||
&ClientError{ClientRespONeParse, "index 2: boolean expected", resp, err}
|
||||
}
|
||||
|
||||
if status == false {
|
||||
return nil, &ResponseError{Code: OneClientErrCode(errCode), msg: body}
|
||||
return nil, &ResponseError{
|
||||
Code: OneErrCode(errCode),
|
||||
msg: body,
|
||||
}
|
||||
}
|
||||
|
||||
r := &response{status, body, int(bodyInt), bodyBool}
|
||||
|
Loading…
Reference in New Issue
Block a user