From cc56a51f2622e2ee4902f9dc3a814b2239239935 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Tue, 11 Dec 2018 11:06:56 +0100 Subject: [PATCH 1/3] l4xnat support --- zapi_farms.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 7 deletions(-) diff --git a/zapi_farms.go b/zapi_farms.go index 214584c..e0cbf26 100644 --- a/zapi_farms.go +++ b/zapi_farms.go @@ -63,9 +63,10 @@ func (s *ZapiSession) GetAllFarms() ([]FarmInfo, error) { } type farmDetailsResponse struct { - Description string `json:"description"` - Params FarmDetails `json:"params"` - Services []ServiceDetails `json:"services"` + Description string `json:"description"` + Params FarmDetails `json:"params"` + Services []ServiceDetails `json:"services"` + Backends [][]BackendDetails `json:"backends"` } // FarmCiphers is an enumeration of possible selections of *Ciphers* to be used for an https listener. @@ -102,8 +103,9 @@ const ( type FarmListener string const ( - FarmListener_HTTP FarmListener = "http" - FarmListener_HTTPS FarmListener = "https" + FarmListener_HTTP FarmListener = "http" + FarmListener_HTTPS FarmListener = "https" + FarmListener_L4XNAT FarmListener = "l4xnat" ) // FarmRewriteLocation is an enumeration of possible selections of *RewriteLocation* values. @@ -166,6 +168,9 @@ type FarmDetails struct { VirtualIP string `json:"vip"` VirtualPort int `json:"vport"` Services []ServiceDetails `json:"services"` + Backends []BackendDetails `json:"backends"` // l4xnat farms have these directly under services + Protocol string `json:"protocol,omitempty"` //l4xnat advanced option + NATType string `json:"nattype,omitempty"` //l4xnat advanced option } // String returns the farm's name and listener. @@ -194,6 +199,28 @@ func (fd *FarmDetails) GetService(serviceName string) (*ServiceDetails, error) { return nil, nil } +// GetBackend retrieves a backend by its ID, or returns *nil* if not found. +func (fd *FarmDetails) GetBackend(backendID int) (*BackendDetails, error) { + for _, s := range fd.Backends { + if s.ID == backendID { + return &s, nil + } + } + + return nil, nil +} + +// GetBackendByAddress gets a backend by it's address - used for l4xnat farms only, returns *nil* if not found. +func (fd *FarmDetails) GetBackendByAddress(ipAddress string, port int) (*BackendDetails, error) { + for _, s := range fd.Backends { + if s.IPAddress == ipAddress && (port <= 0 || s.Port == port) { + return &s, nil + } + } + + return nil, nil +} + // GetFarm returns details on a specific farm. func (s *ZapiSession) GetFarm(farmName string) (*FarmDetails, error) { var result *farmDetailsResponse @@ -228,6 +255,14 @@ func (s *ZapiSession) GetFarm(farmName string) (*FarmDetails, error) { backend.ServiceName = service.ServiceName } } + if result.Params.Listener == FarmListener_L4XNAT { + // Backends is an array of arrays in the response so we take the first (and should be only) level. + for _, backend := range result.Backends[0] { + backend.FarmName = farmName + backend.ServiceName = "" + result.Params.Backends = append(result.Params.Backends, backend) + } + } } return &result.Params, nil @@ -602,8 +637,14 @@ type backendCreate struct { Port int `json:"port"` } -// DeleteBackend will delete an existing backend (or do nothing if backend or service or farm is missing) +// DeleteBackend backward-compatible signature. Please use DeleteServiceBackend. func (s *ZapiSession) DeleteBackend(farmName string, serviceName string, backendId int) (bool, error) { + ret, err := s.DeleteServiceBackend(farmName, serviceName, backendId) + return ret, err +} + +// DeleteServiceBackend will delete an existing backend (or do nothing if backend or service or farm is missing) +func (s *ZapiSession) DeleteServiceBackend(farmName string, serviceName string, backendId int) (bool, error) { // retrieve farm details farm, err := s.GetFarm(farmName) @@ -642,8 +683,43 @@ func (s *ZapiSession) DeleteBackend(farmName string, serviceName string, backend return true, s.delete("farms", farmName, "services", serviceName, "backends", strconv.Itoa(backendId)) } -// CreateBackend creates a new backend on a service on a farm. +// DeleteL4xnatBackend will delete an existing backend (or do nothing if backend or service or farm is missing) +func (s *ZapiSession) DeleteL4xnatBackend(farmName string, backendId int) (bool, error) { + // retrieve farm details + farm, err := s.GetFarm(farmName) + + if err != nil { + return false, err + } + + // farm does not exist? + if farm == nil { + return false, nil + } + + // does the backend exist? + backend, err := farm.GetBackend(backendId) + + if err != nil { + return false, err + } + + if backend == nil { + return false, nil + } + + // delete the backend + return true, s.delete("farms", farmName, "backends", strconv.Itoa(backendId)) +} + +// CreateBackend backward-compatible signature. Please use CreateServiceBackend. func (s *ZapiSession) CreateBackend(farmName string, serviceName string, backendIP string, backendPort int) (*BackendDetails, error) { + ret, err := s.CreateServiceBackend(farmName, serviceName, backendIP, backendPort) + return ret, err +} + +// CreateServiceBackend creates a new backend on a service on a farm. +func (s *ZapiSession) CreateServiceBackend(farmName string, serviceName string, backendIP string, backendPort int) (*BackendDetails, error) { // create the backend req := backendCreate{ IPAddress: backendIP, @@ -672,6 +748,30 @@ func (s *ZapiSession) CreateBackend(farmName string, serviceName string, backend return service.GetBackendByAddress(backendIP, backendPort) } +// CreateL4xnatBackend creates a new backend on a l4xnat farm. +func (s *ZapiSession) CreateL4xnatBackend(farmName string, backendIP string, backendPort int) (*BackendDetails, error) { + // create the backend + req := backendCreate{ + IPAddress: backendIP, + Port: backendPort, + } + + err := s.post(req, "farms", farmName, "backends") + + if err != nil { + return nil, err + } + + // retrieve status + farm, err := s.GetFarm(farmName) + + if err != nil { + return nil, err + } + + return farm.GetBackendByAddress(backendIP, backendPort) +} + // UpdateBackend updates a backend on a service on a farm. func (s *ZapiSession) UpdateBackend(backend *BackendDetails) error { return s.put(backend, "farms", backend.FarmName, "services", backend.ServiceName, "backends", strconv.Itoa(backend.ID)) From ff8f425042915c4e04e85665ea40853f09e7db1d Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Tue, 11 Dec 2018 17:00:24 +0100 Subject: [PATCH 2/3] FarmGuardian is now at base level as well --- zapi_farms.go | 81 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/zapi_farms.go b/zapi_farms.go index e0cbf26..e42931e 100644 --- a/zapi_farms.go +++ b/zapi_farms.go @@ -144,33 +144,37 @@ const ( // FarmDetails contains all information regarding a farm and the services. // See https://www.zevenet.com/zapidoc_ce_v3.1/#retrieve-farm-by-name type FarmDetails struct { - Certificates []CertificateInfo `json:"certlist"` - FarmName string `json:"farmname"` - CiphersCustom string `json:"cipherc,omitempty"` - Ciphers FarmCiphers `json:"ciphers,omitempty"` - ConnectionTimeoutSeconds int `json:"contimeout"` - DisableSSLv2 bool `json:"disable_sslv2,string"` - DisableSSLv3 bool `json:"disable_sslv3,string"` - DisableTLSv1 bool `json:"disable_tlsv1,string"` - DisableTLSv11 bool `json:"disable_tlsv1_1,string"` - DisableTLSv12 bool `json:"disable_tlsv1_2,string"` - ErrorString414 string `json:"error414"` - ErrorString500 string `json:"error500"` - ErrorString501 string `json:"error501"` - ErrorString503 string `json:"error503"` - HTTPVerbs FarmHTTPVerb `json:"httpverb"` - Listener FarmListener `json:"listener"` - RequestTimeoutSeconds int `json:"reqtimeout"` - ResponseTimeoutSeconds int `json:"restimeout"` - ResurrectIntervalSeconds int `json:"resurrectime"` - RewriteLocation FarmRewriteLocation `json:"rewritelocation"` - Status FarmStatus `json:"status"` - VirtualIP string `json:"vip"` - VirtualPort int `json:"vport"` - Services []ServiceDetails `json:"services"` - Backends []BackendDetails `json:"backends"` // l4xnat farms have these directly under services - Protocol string `json:"protocol,omitempty"` //l4xnat advanced option - NATType string `json:"nattype,omitempty"` //l4xnat advanced option + Certificates []CertificateInfo `json:"certlist"` + FarmName string `json:"farmname"` + CiphersCustom string `json:"cipherc,omitempty"` + Ciphers FarmCiphers `json:"ciphers,omitempty"` + ConnectionTimeoutSeconds int `json:"contimeout"` + DisableSSLv2 bool `json:"disable_sslv2,string"` + DisableSSLv3 bool `json:"disable_sslv3,string"` + DisableTLSv1 bool `json:"disable_tlsv1,string"` + DisableTLSv11 bool `json:"disable_tlsv1_1,string"` + DisableTLSv12 bool `json:"disable_tlsv1_2,string"` + ErrorString414 string `json:"error414"` + ErrorString500 string `json:"error500"` + ErrorString501 string `json:"error501"` + ErrorString503 string `json:"error503"` + HTTPVerbs FarmHTTPVerb `json:"httpverb"` + Listener FarmListener `json:"listener"` + RequestTimeoutSeconds int `json:"reqtimeout"` + ResponseTimeoutSeconds int `json:"restimeout"` + ResurrectIntervalSeconds int `json:"resurrectime"` + RewriteLocation FarmRewriteLocation `json:"rewritelocation"` + Status FarmStatus `json:"status"` + VirtualIP string `json:"vip"` + VirtualPort int `json:"vport"` + Services []ServiceDetails `json:"services"` + Backends []BackendDetails `json:"backends"` // l4xnat farms have these directly under services + Protocol string `json:"protocol,omitempty"` // l4xnat advanced option + NATType string `json:"nattype,omitempty"` // l4xnat advanced option + FarmGuardianEnabled bool `json:"fgenabled,string"` // l4xnat farms have fg at top level + FarmGuardianLogsEnabled OptionalBool `json:"fglog"` // l4xnat farms have fg at top level + FarmGuardianScript string `json:"fgscript"` // l4xnat farms have fg at top level + FarmGuardianCheckIntervalSeconds int `json:"fgtimecheck"` // l4xnat farms have fg at top level } // String returns the farm's name and listener. @@ -373,7 +377,28 @@ func (s *ZapiSession) CreateFarmAsHTTPS(farmName string, virtualIP string, virtu // UpdateFarm updates the HTTP/S farm. // This method does *not* update the *services*. Use *UpdateService()* instead. func (s *ZapiSession) UpdateFarm(farm *FarmDetails) error { - return s.put(farm, "farms", farm.FarmName) + err := s.put(farm, "farms", farm.FarmName) + if err != nil { + return err + } + if farm.Listener == FarmListener_L4XNAT { + + // update farm guardian + fg := farmguardianUpdate{ + ServiceName: "", + FarmGuardianEnabled: farm.FarmGuardianEnabled, + FarmGuardianScript: farm.FarmGuardianScript, + FarmGuardianCheckIntervalSeconds: farm.FarmGuardianCheckIntervalSeconds, + FarmGuardianLogsEnabled: farm.FarmGuardianLogsEnabled, + } + + if fg.FarmGuardianScript == "" { + fg.FarmGuardianScript = "check_http -H HOST -p PORT" + } + + return s.put(fg, "farms", farm.FarmName, "fg") + } + return nil } type farmAction struct { From 3369ad73a9f350ea8cbd2672bfbfb99c8c9fcdad Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Thu, 13 Dec 2018 14:59:23 +0100 Subject: [PATCH 3/3] We have to have a default here --- zapi_farms.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zapi_farms.go b/zapi_farms.go index e42931e..12d4eed 100644 --- a/zapi_farms.go +++ b/zapi_farms.go @@ -383,6 +383,10 @@ func (s *ZapiSession) UpdateFarm(farm *FarmDetails) error { } if farm.Listener == FarmListener_L4XNAT { + if farm.FarmGuardianCheckIntervalSeconds == 0 { + farm.FarmGuardianCheckIntervalSeconds = 5 // This is the zevenet default and if it's not set, the api call fails + } + // update farm guardian fg := farmguardianUpdate{ ServiceName: "",