348 lines
11 KiB
Go
348 lines
11 KiB
Go
package openstack
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
|
)
|
|
|
|
// LogRoundTripper satisfies the http.RoundTripper interface and is used to
|
|
// customize the default http client RoundTripper to allow for logging.
|
|
type LogRoundTripper struct {
|
|
Rt http.RoundTripper
|
|
OsDebug bool
|
|
MaxRetries int
|
|
}
|
|
|
|
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
|
|
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
|
defer func() {
|
|
if request.Body != nil {
|
|
request.Body.Close()
|
|
}
|
|
}()
|
|
|
|
// for future reference, this is how to access the Transport struct:
|
|
//tlsconfig := lrt.Rt.(*http.Transport).TLSClientConfig
|
|
|
|
var err error
|
|
|
|
if lrt.OsDebug {
|
|
log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL)
|
|
log.Printf("[DEBUG] OpenStack Request Headers:\n%s", FormatHeaders(request.Header, "\n"))
|
|
|
|
if request.Body != nil {
|
|
request.Body, err = lrt.logRequest(request.Body, request.Header.Get("Content-Type"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
response, err := lrt.Rt.RoundTrip(request)
|
|
|
|
// If the first request didn't return a response, retry up to `max_retries`.
|
|
retry := 1
|
|
for response == nil {
|
|
if retry > lrt.MaxRetries {
|
|
if lrt.OsDebug {
|
|
log.Printf("[DEBUG] OpenStack connection error, retries exhausted. Aborting")
|
|
}
|
|
err = fmt.Errorf("OpenStack connection error, retries exhausted. Aborting. Last error was: %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
if lrt.OsDebug {
|
|
log.Printf("[DEBUG] OpenStack connection error, retry number %d: %s", retry, err)
|
|
}
|
|
response, err = lrt.Rt.RoundTrip(request)
|
|
retry += 1
|
|
}
|
|
|
|
if lrt.OsDebug {
|
|
log.Printf("[DEBUG] OpenStack Response Code: %d", response.StatusCode)
|
|
log.Printf("[DEBUG] OpenStack Response Headers:\n%s", FormatHeaders(response.Header, "\n"))
|
|
|
|
response.Body, err = lrt.logResponse(response.Body, response.Header.Get("Content-Type"))
|
|
}
|
|
|
|
return response, err
|
|
}
|
|
|
|
// logRequest will log the HTTP Request details.
|
|
// If the body is JSON, it will attempt to be pretty-formatted.
|
|
func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
|
|
defer original.Close()
|
|
|
|
var bs bytes.Buffer
|
|
_, err := io.Copy(&bs, original)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Handle request contentType
|
|
if strings.HasPrefix(contentType, "application/json") {
|
|
debugInfo := lrt.formatJSON(bs.Bytes())
|
|
log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo)
|
|
}
|
|
|
|
return ioutil.NopCloser(strings.NewReader(bs.String())), nil
|
|
}
|
|
|
|
// logResponse will log the HTTP Response details.
|
|
// If the body is JSON, it will attempt to be pretty-formatted.
|
|
func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
|
|
if strings.HasPrefix(contentType, "application/json") {
|
|
var bs bytes.Buffer
|
|
defer original.Close()
|
|
_, err := io.Copy(&bs, original)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
debugInfo := lrt.formatJSON(bs.Bytes())
|
|
if debugInfo != "" {
|
|
log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
|
|
}
|
|
return ioutil.NopCloser(strings.NewReader(bs.String())), nil
|
|
}
|
|
|
|
log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
|
|
return original, nil
|
|
}
|
|
|
|
// formatJSON will try to pretty-format a JSON body.
|
|
// It will also mask known fields which contain sensitive information.
|
|
func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
|
|
var data map[string]interface{}
|
|
|
|
err := json.Unmarshal(raw, &data)
|
|
if err != nil {
|
|
log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
|
|
return string(raw)
|
|
}
|
|
|
|
// Mask known password fields
|
|
if v, ok := data["auth"].(map[string]interface{}); ok {
|
|
if v, ok := v["identity"].(map[string]interface{}); ok {
|
|
if v, ok := v["password"].(map[string]interface{}); ok {
|
|
if v, ok := v["user"].(map[string]interface{}); ok {
|
|
v["password"] = "***"
|
|
}
|
|
}
|
|
if v, ok := v["application_credential"].(map[string]interface{}); ok {
|
|
v["secret"] = "***"
|
|
}
|
|
if v, ok := v["token"].(map[string]interface{}); ok {
|
|
v["id"] = "***"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ignore the catalog
|
|
if v, ok := data["token"].(map[string]interface{}); ok {
|
|
if _, ok := v["catalog"]; ok {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
pretty, err := json.MarshalIndent(data, "", " ")
|
|
if err != nil {
|
|
log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
|
|
return string(raw)
|
|
}
|
|
|
|
return string(pretty)
|
|
}
|
|
|
|
// Firewall is an OpenStack firewall.
|
|
type Firewall struct {
|
|
firewalls.Firewall
|
|
routerinsertion.FirewallExt
|
|
}
|
|
|
|
// FirewallCreateOpts represents the attributes used when creating a new firewall.
|
|
type FirewallCreateOpts struct {
|
|
firewalls.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToFirewallCreateMap casts a CreateOptsExt struct to a map.
|
|
// It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field.
|
|
func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "firewall")
|
|
}
|
|
|
|
//FirewallUpdateOpts
|
|
type FirewallUpdateOpts struct {
|
|
firewalls.UpdateOptsBuilder
|
|
}
|
|
|
|
func (opts FirewallUpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "firewall")
|
|
}
|
|
|
|
// FloatingIPCreateOpts represents the attributes used when creating a new floating ip.
|
|
type FloatingIPCreateOpts struct {
|
|
floatingips.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToFloatingIPCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field.
|
|
func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "floatingip")
|
|
}
|
|
|
|
// NetworkCreateOpts represents the attributes used when creating a new network.
|
|
type NetworkCreateOpts struct {
|
|
networks.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToNetworkCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
|
|
func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "network")
|
|
}
|
|
|
|
// PolicyCreateOpts represents the attributes used when creating a new firewall policy.
|
|
type PolicyCreateOpts struct {
|
|
policies.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// IKEPolicyCreateOpts represents the attributes used when creating a new IKE policy.
|
|
type IKEPolicyCreateOpts struct {
|
|
ikepolicies.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// IKEPolicyLifetimeCreateOpts represents the attributes used when creating a new lifetime for an IKE policy.
|
|
type IKEPolicyLifetimeCreateOpts struct {
|
|
ikepolicies.LifetimeCreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToPolicyCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field.
|
|
func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "firewall_policy")
|
|
}
|
|
|
|
// PortCreateOpts represents the attributes used when creating a new port.
|
|
type PortCreateOpts struct {
|
|
ports.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToPortCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides ports.ToPortCreateMap to add the ValueSpecs field.
|
|
func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "port")
|
|
}
|
|
|
|
// RouterCreateOpts represents the attributes used when creating a new router.
|
|
type RouterCreateOpts struct {
|
|
routers.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToRouterCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
|
|
func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
|
|
return BuildRequest(opts, "router")
|
|
}
|
|
|
|
// RuleCreateOpts represents the attributes used when creating a new firewall rule.
|
|
type RuleCreateOpts struct {
|
|
rules.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToRuleCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides rules.ToRuleCreateMap to add the ValueSpecs field.
|
|
func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
|
b, err := BuildRequest(opts, "firewall_rule")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" {
|
|
m["protocol"] = nil
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// SubnetCreateOpts represents the attributes used when creating a new subnet.
|
|
type SubnetCreateOpts struct {
|
|
subnets.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ToSubnetCreateMap casts a CreateOpts struct to a map.
|
|
// It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
|
|
func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
|
|
b, err := BuildRequest(opts, "subnet")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
|
|
m["gateway_ip"] = nil
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// SubnetPoolCreateOpts represents the attributes used when creating a new subnet pool.
|
|
type SubnetPoolCreateOpts struct {
|
|
subnetpools.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// IPSecPolicyCreateOpts represents the attributes used when creating a new IPSec policy.
|
|
type IPSecPolicyCreateOpts struct {
|
|
ipsecpolicies.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// ServiceCreateOpts represents the attributes used when creating a new VPN service.
|
|
type ServiceCreateOpts struct {
|
|
services.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// EndpointGroupCreateOpts represents the attributes used when creating a new endpoint group.
|
|
type EndpointGroupCreateOpts struct {
|
|
endpointgroups.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|
|
|
|
// SiteConnectionCreateOpts represents the attributes used when creating a new IPSec site connection.
|
|
type SiteConnectionCreateOpts struct {
|
|
siteconnections.CreateOpts
|
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
|
}
|