blob: 08c43d1a440a576d58196c957ac9741e065ccd80 [file] [log] [blame]
package tc
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"net"
"time"
"github.com/apache/trafficcontrol/lib/go-util"
)
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
// ServersV3Response is the format of a response to a GET request for /servers.
type ServersV3Response struct {
Response []ServerNullable `json:"response"`
Summary struct {
Count uint64 `json:"count"`
} `json:"summary"`
Alerts
}
// ServersResponse is a list of Servers as a response to an API v2 request.
// This can't change because it will break ORT. Unfortunately.
type ServersResponse struct {
Response []Server `json:"response"`
Alerts
}
// ServersDetailResponse is the JSON object returned for a single server.
type ServersDetailResponse struct {
Response Server `json:"response"`
Alerts
}
// ServersV1Response is a list of Servers for v1 as a response.
type ServersV1Response struct {
Response []ServerV1 `json:"response"`
Alerts
}
type ServerDetailV11 struct {
ServerDetail
LegacyInterfaceDetails
}
// ServerDetailV30 is the details for a server for API v3
type ServerDetailV30 struct {
ServerDetail
ServerInterfaces *[]ServerInterfaceInfo `json:"interfaces"`
}
// ServersV1DetailResponse is the JSON object returned for a single server for v1.
type ServersV1DetailResponse struct {
Response []ServerDetailV11 `json:"response"`
Alerts
}
// ServersV3DetailResponse is the JSON object returned for a single server for v3.
type ServersV3DetailResponse struct {
Response []ServerDetailV30 `json:"response"`
Alerts
}
// ServerIPAddress is the data associated with a server's interface's IP address.
type ServerIPAddress struct {
Address string `json:"address" db:"address"`
Gateway *string `json:"gateway" db:"gateway"`
ServiceAddress bool `json:"serviceAddress" db:"service_address"`
}
// ServerInterfaceInfo is the data associated with a server's interface.
type ServerInterfaceInfo struct {
IPAddresses []ServerIPAddress `json:"ipAddresses" db:"ip_addresses"`
MaxBandwidth *uint64 `json:"maxBandwidth" db:"max_bandwidth"`
Monitor bool `json:"monitor" db:"monitor"`
MTU *uint64 `json:"mtu" db:"mtu"`
Name string `json:"name" db:"name"`
}
// Value implements the driver.Valuer interface
// marshals struct to json to pass back as a json.RawMessage
func (sii *ServerInterfaceInfo) Value() (driver.Value, error) {
b, err := json.Marshal(sii)
return b, err
}
// Scan implements the sql.Scanner interface
// expects json.RawMessage and unmarshals to a ServerInterfaceInfo struct
func (sii *ServerInterfaceInfo) Scan(src interface{}) error {
b, ok := src.([]byte)
if !ok {
return fmt.Errorf("expected deliveryservice in byte array form; got %T", src)
}
return json.Unmarshal([]byte(b), sii)
}
// LegacyInterfaceDetails is the details for interfaces on servers for API v1 and v2.
type LegacyInterfaceDetails struct {
InterfaceMtu *int `json:"interfaceMtu" db:"interface_mtu"`
InterfaceName *string `json:"interfaceName" db:"interface_name"`
IP6Address *string `json:"ip6Address" db:"ip6_address"`
IP6Gateway *string `json:"ip6Gateway" db:"ip6_gateway"`
IPAddress *string `json:"ipAddress" db:"ip_address"`
IPGateway *string `json:"ipGateway" db:"ip_gateway"`
IPNetmask *string `json:"ipNetmask" db:"ip_netmask"`
}
// ToInterfaces converts a LegacyInterfaceDetails to a slice of
// ServerInterfaceInfo structures. No interfaces will be marked for monitoring,
// and it will generate service addresses according to the passed indicators
// for each address family.
func (lid *LegacyInterfaceDetails) ToInterfaces(ipv4IsService, ipv6IsService bool) ([]ServerInterfaceInfo, error) {
var iface ServerInterfaceInfo
if lid.InterfaceMtu == nil {
return nil, errors.New("interfaceMtu is null")
}
mtu := uint64(*lid.InterfaceMtu)
iface.MTU = &mtu
if lid.InterfaceName == nil {
return nil, errors.New("interfaceName is null")
}
iface.Name = *lid.InterfaceName
var ips []ServerIPAddress
if lid.IPAddress != nil && *lid.IPAddress != "" {
if lid.IPGateway != nil && *lid.IPGateway == "" {
lid.IPGateway = nil
}
ipStr := *lid.IPAddress
if lid.IPNetmask != nil && *lid.IPNetmask != "" {
mask := net.ParseIP(*lid.IPNetmask).To4()
if mask == nil {
return nil, fmt.Errorf("Failed to parse netmask '%s'", *lid.IPNetmask)
}
cidr, _ := net.IPv4Mask(mask[0], mask[1], mask[2], mask[3]).Size()
ipStr = fmt.Sprintf("%s/%d", ipStr, cidr)
}
ips = append(ips, ServerIPAddress{
Address: ipStr,
Gateway: lid.IPGateway,
ServiceAddress: ipv4IsService,
})
}
if lid.IP6Address != nil && *lid.IP6Address != "" {
if lid.IP6Gateway != nil && *lid.IP6Gateway == "" {
lid.IP6Gateway = nil
}
ips = append(ips, ServerIPAddress{
Address: *lid.IP6Address,
Gateway: lid.IP6Gateway,
ServiceAddress: ipv6IsService,
})
}
iface.IPAddresses = ips
return []ServerInterfaceInfo{iface}, nil
}
// InterfaceInfoToLegacyInterfaces converts a ServerInterfaceInfo to an
// equivalent LegacyInterfaceDetails structure. It does this by creating the
// IP address fields using the "service" interface's IP addresses. All others
// are discarded, as the legacy format is incapable of representing them.
func InterfaceInfoToLegacyInterfaces(serverInterfaces []ServerInterfaceInfo) (LegacyInterfaceDetails, error) {
var legacyDetails LegacyInterfaceDetails
for _, intFace := range serverInterfaces {
for _, addr := range intFace.IPAddresses {
if !addr.ServiceAddress {
continue
}
address := addr.Address
gateway := addr.Gateway
var parsedIp net.IP
var mask *net.IPNet
var err error
parsedIp, mask, err = net.ParseCIDR(address)
if err != nil {
parsedIp = net.ParseIP(address)
if parsedIp == nil {
return legacyDetails, fmt.Errorf("Failed to parse '%s' as network or CIDR string: %v", address, err)
}
}
if parsedIp.To4() == nil {
legacyDetails.IP6Address = &address
legacyDetails.IP6Gateway = gateway
} else if mask != nil {
legacyDetails.IPAddress = util.StrPtr(parsedIp.String())
legacyDetails.IPGateway = gateway
legacyDetails.IPNetmask = util.StrPtr(fmt.Sprintf("%d.%d.%d.%d", mask.Mask[0], mask.Mask[1], mask.Mask[2], mask.Mask[3]))
} else {
legacyDetails.IPAddress = util.StrPtr(parsedIp.String())
legacyDetails.IPGateway = gateway
legacyDetails.IPNetmask = new(string)
}
if intFace.MTU != nil {
legacyDetails.InterfaceMtu = util.IntPtr(int(*intFace.MTU))
}
legacyDetails.InterfaceName = &intFace.Name
// we can jump out here since servers can only legally have one
// IPv4 and one IPv6 service address
if legacyDetails.IPAddress != nil && *legacyDetails.IPAddress != "" && legacyDetails.IP6Address != nil && *legacyDetails.IP6Address != "" {
return legacyDetails, nil
}
}
}
return legacyDetails, nil
}
type Server struct {
Cachegroup string `json:"cachegroup" db:"cachegroup"`
CachegroupID int `json:"cachegroupId" db:"cachegroup_id"`
CDNID int `json:"cdnId" db:"cdn_id"`
CDNName string `json:"cdnName" db:"cdn_name"`
DeliveryServices map[string][]string `json:"deliveryServices,omitempty"`
DomainName string `json:"domainName" db:"domain_name"`
FQDN *string `json:"fqdn,omitempty"`
FqdnTime time.Time `json:"-"`
GUID string `json:"guid" db:"guid"`
HostName string `json:"hostName" db:"host_name"`
HTTPSPort int `json:"httpsPort" db:"https_port"`
ID int `json:"id" db:"id"`
ILOIPAddress string `json:"iloIpAddress" db:"ilo_ip_address"`
ILOIPGateway string `json:"iloIpGateway" db:"ilo_ip_gateway"`
ILOIPNetmask string `json:"iloIpNetmask" db:"ilo_ip_netmask"`
ILOPassword string `json:"iloPassword" db:"ilo_password"`
ILOUsername string `json:"iloUsername" db:"ilo_username"`
InterfaceMtu int `json:"interfaceMtu" db:"interface_mtu"`
InterfaceName string `json:"interfaceName" db:"interface_name"`
IP6Address string `json:"ip6Address" db:"ip6_address"`
IP6IsService bool `json:"ip6IsService" db:"ip6_address_is_service"`
IP6Gateway string `json:"ip6Gateway" db:"ip6_gateway"`
IPAddress string `json:"ipAddress" db:"ip_address"`
IPIsService bool `json:"ipIsService" db:"ip_address_is_service"`
IPGateway string `json:"ipGateway" db:"ip_gateway"`
IPNetmask string `json:"ipNetmask" db:"ip_netmask"`
LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
MgmtIPAddress string `json:"mgmtIpAddress" db:"mgmt_ip_address"`
MgmtIPGateway string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
MgmtIPNetmask string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
OfflineReason string `json:"offlineReason" db:"offline_reason"`
PhysLocation string `json:"physLocation" db:"phys_location"`
PhysLocationID int `json:"physLocationId" db:"phys_location_id"`
Profile string `json:"profile" db:"profile"`
ProfileDesc string `json:"profileDesc" db:"profile_desc"`
ProfileID int `json:"profileId" db:"profile_id"`
Rack string `json:"rack" db:"rack"`
RevalPending bool `json:"revalPending" db:"reval_pending"`
RouterHostName string `json:"routerHostName" db:"router_host_name"`
RouterPortName string `json:"routerPortName" db:"router_port_name"`
Status string `json:"status" db:"status"`
StatusID int `json:"statusId" db:"status_id"`
TCPPort int `json:"tcpPort" db:"tcp_port"`
Type string `json:"type" db:"server_type"`
TypeID int `json:"typeId" db:"server_type_id"`
UpdPending bool `json:"updPending" db:"upd_pending"`
XMPPID string `json:"xmppId" db:"xmpp_id"`
XMPPPasswd string `json:"xmppPasswd" db:"xmpp_passwd"`
}
type ServerV1 struct {
Cachegroup string `json:"cachegroup" db:"cachegroup"`
CachegroupID int `json:"cachegroupId" db:"cachegroup_id"`
CDNID int `json:"cdnId" db:"cdn_id"`
CDNName string `json:"cdnName" db:"cdn_name"`
DeliveryServices map[string][]string `json:"deliveryServices,omitempty"`
DomainName string `json:"domainName" db:"domain_name"`
FQDN *string `json:"fqdn,omitempty"`
FqdnTime time.Time `json:"-"`
GUID string `json:"guid" db:"guid"`
HostName string `json:"hostName" db:"host_name"`
HTTPSPort int `json:"httpsPort" db:"https_port"`
ID int `json:"id" db:"id"`
ILOIPAddress string `json:"iloIpAddress" db:"ilo_ip_address"`
ILOIPGateway string `json:"iloIpGateway" db:"ilo_ip_gateway"`
ILOIPNetmask string `json:"iloIpNetmask" db:"ilo_ip_netmask"`
ILOPassword string `json:"iloPassword" db:"ilo_password"`
ILOUsername string `json:"iloUsername" db:"ilo_username"`
InterfaceMtu int `json:"interfaceMtu" db:"interface_mtu"`
InterfaceName string `json:"interfaceName" db:"interface_name"`
IP6Address string `json:"ip6Address" db:"ip6_address"`
IP6Gateway string `json:"ip6Gateway" db:"ip6_gateway"`
IPAddress string `json:"ipAddress" db:"ip_address"`
IPGateway string `json:"ipGateway" db:"ip_gateway"`
IPNetmask string `json:"ipNetmask" db:"ip_netmask"`
LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
MgmtIPAddress string `json:"mgmtIpAddress" db:"mgmt_ip_address"`
MgmtIPGateway string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
MgmtIPNetmask string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
OfflineReason string `json:"offlineReason" db:"offline_reason"`
PhysLocation string `json:"physLocation" db:"phys_location"`
PhysLocationID int `json:"physLocationId" db:"phys_location_id"`
Profile string `json:"profile" db:"profile"`
ProfileDesc string `json:"profileDesc" db:"profile_desc"`
ProfileID int `json:"profileId" db:"profile_id"`
Rack string `json:"rack" db:"rack"`
RevalPending bool `json:"revalPending" db:"reval_pending"`
RouterHostName string `json:"routerHostName" db:"router_host_name"`
RouterPortName string `json:"routerPortName" db:"router_port_name"`
Status string `json:"status" db:"status"`
StatusID int `json:"statusId" db:"status_id"`
TCPPort int `json:"tcpPort" db:"tcp_port"`
Type string `json:"type" db:"server_type"`
TypeID int `json:"typeId" db:"server_type_id"`
UpdPending bool `json:"updPending" db:"upd_pending"`
XMPPID string `json:"xmppId" db:"xmpp_id"`
XMPPPasswd string `json:"xmppPasswd" db:"xmpp_passwd"`
}
// CommonServerProperties is just the collection of properties which are
// shared by all servers across API versions.
type CommonServerProperties struct {
Cachegroup *string `json:"cachegroup" db:"cachegroup"`
CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"`
CDNID *int `json:"cdnId" db:"cdn_id"`
CDNName *string `json:"cdnName" db:"cdn_name"`
DeliveryServices *map[string][]string `json:"deliveryServices,omitempty"`
DomainName *string `json:"domainName" db:"domain_name"`
FQDN *string `json:"fqdn,omitempty"`
FqdnTime time.Time `json:"-"`
GUID *string `json:"guid" db:"guid"`
HostName *string `json:"hostName" db:"host_name"`
HTTPSPort *int `json:"httpsPort" db:"https_port"`
ID *int `json:"id" db:"id"`
ILOIPAddress *string `json:"iloIpAddress" db:"ilo_ip_address"`
ILOIPGateway *string `json:"iloIpGateway" db:"ilo_ip_gateway"`
ILOIPNetmask *string `json:"iloIpNetmask" db:"ilo_ip_netmask"`
ILOPassword *string `json:"iloPassword" db:"ilo_password"`
ILOUsername *string `json:"iloUsername" db:"ilo_username"`
LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"`
MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
OfflineReason *string `json:"offlineReason" db:"offline_reason"`
PhysLocation *string `json:"physLocation" db:"phys_location"`
PhysLocationID *int `json:"physLocationId" db:"phys_location_id"`
Profile *string `json:"profile" db:"profile"`
ProfileDesc *string `json:"profileDesc" db:"profile_desc"`
ProfileID *int `json:"profileId" db:"profile_id"`
Rack *string `json:"rack" db:"rack"`
RevalPending *bool `json:"revalPending" db:"reval_pending"`
RouterHostName *string `json:"routerHostName" db:"router_host_name"`
RouterPortName *string `json:"routerPortName" db:"router_port_name"`
Status *string `json:"status" db:"status"`
StatusID *int `json:"statusId" db:"status_id"`
TCPPort *int `json:"tcpPort" db:"tcp_port"`
Type string `json:"type" db:"server_type"`
TypeID *int `json:"typeId" db:"server_type_id"`
UpdPending *bool `json:"updPending" db:"upd_pending"`
XMPPID *string `json:"xmppId" db:"xmpp_id"`
XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"`
}
// ServerNullableV11 is a server as it appeared in API version 1.1.
type ServerNullableV11 struct {
LegacyInterfaceDetails
CommonServerProperties
}
// ServerNullableV2 is a server as it appeared in API v2.
type ServerNullableV2 struct {
ServerNullableV11
IPIsService *bool `json:"ipIsService" db:"ip_address_is_service"`
IP6IsService *bool `json:"ip6IsService" db:"ip6_address_is_service"`
}
// ServerNullable represents an ATC server, as returned by the TO API.
type ServerNullable struct {
CommonServerProperties
Interfaces []ServerInterfaceInfo `json:"interfaces" db:"interfaces"`
}
// ToServerV2 converts the server to an equivalent ServerNullableV2 structure,
// if possible. If the conversion could not be performed, an error is returned.
func (s *ServerNullable) ToServerV2() (ServerNullableV2, error) {
legacyServer := ServerNullableV2{
ServerNullableV11: ServerNullableV11{
CommonServerProperties: s.CommonServerProperties,
},
IPIsService: new(bool),
IP6IsService: new(bool),
}
var err error
legacyServer.LegacyInterfaceDetails, err = InterfaceInfoToLegacyInterfaces(s.Interfaces)
if err != nil {
return legacyServer, err
}
*legacyServer.IPIsService = legacyServer.LegacyInterfaceDetails.IPAddress != nil && *legacyServer.LegacyInterfaceDetails.IPAddress != ""
*legacyServer.IP6IsService = legacyServer.LegacyInterfaceDetails.IP6Address != nil && *legacyServer.LegacyInterfaceDetails.IP6Address != ""
return legacyServer, nil
}
type ServerUpdateStatus struct {
HostName string `json:"host_name"`
UpdatePending bool `json:"upd_pending"`
RevalPending bool `json:"reval_pending"`
UseRevalPending bool `json:"use_reval_pending"`
HostId int `json:"host_id"`
Status string `json:"status"`
ParentPending bool `json:"parent_pending"`
ParentRevalPending bool `json:"parent_reval_pending"`
}
type ServerPutStatus struct {
Status util.JSONNameOrIDStr `json:"status"`
OfflineReason *string `json:"offlineReason"`
}
type ServerInfo struct {
CachegroupID int `json:"cachegroupId" db:"cachegroup_id"`
CDNID int `json:"cdnId" db:"cdn_id"`
DomainName string `json:"domainName" db:"domain_name"`
HostName string `json:"hostName" db:"host_name"`
Type string `json:"type" db:"server_type"`
}
type ServerDetail struct {
CacheGroup *string `json:"cachegroup" db:"cachegroup"`
CDNName *string `json:"cdnName" db:"cdn_name"`
DeliveryServiceIDs []int64 `json:"deliveryservices,omitempty"`
DomainName *string `json:"domainName" db:"domain_name"`
GUID *string `json:"guid" db:"guid"`
HardwareInfo map[string]string `json:"hardwareInfo"`
HostName *string `json:"hostName" db:"host_name"`
HTTPSPort *int `json:"httpsPort" db:"https_port"`
ID *int `json:"id" db:"id"`
ILOIPAddress *string `json:"iloIpAddress" db:"ilo_ip_address"`
ILOIPGateway *string `json:"iloIpGateway" db:"ilo_ip_gateway"`
ILOIPNetmask *string `json:"iloIpNetmask" db:"ilo_ip_netmask"`
ILOPassword *string `json:"iloPassword" db:"ilo_password"`
ILOUsername *string `json:"iloUsername" db:"ilo_username"`
MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"`
MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
OfflineReason *string `json:"offlineReason" db:"offline_reason"`
PhysLocation *string `json:"physLocation" db:"phys_location"`
Profile *string `json:"profile" db:"profile"`
ProfileDesc *string `json:"profileDesc" db:"profile_desc"`
Rack *string `json:"rack" db:"rack"`
RouterHostName *string `json:"routerHostName" db:"router_host_name"`
RouterPortName *string `json:"routerPortName" db:"router_port_name"`
Status *string `json:"status" db:"status"`
TCPPort *int `json:"tcpPort" db:"tcp_port"`
Type string `json:"type" db:"server_type"`
XMPPID *string `json:"xmppId" db:"xmpp_id"`
XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"`
}
// ServerQueueUpdateRequest encodes the request data for the POST
// servers/{{ID}}/queue_update endpoint.
type ServerQueueUpdateRequest struct {
Action string `json:"action"`
}
// ServerQueueUpdateResponse decodes the full response with alerts from the POST
// servers/{{ID}}/queue_update endpoint.
type ServerQueueUpdateResponse struct {
Response ServerQueueUpdate `json:"response"`
Alerts
}
// ServerQueueUpdate decodes the update data from the POST
// servers/{{ID}}/queue_update endpoint.
type ServerQueueUpdate struct {
ServerID util.JSONIntStr `json:"serverId"`
Action string `json:"action"`
}