Skip to content

Commit

Permalink
Snapshot interfaces (#4709)
Browse files Browse the repository at this point in the history
* Reworked snapshots to use server interfaces

* Fixed typo in query

* Updated changelog

* Performance improvements by pre-fetching all interface data

* Fixed missing interface name in snapshotted servers

* Fixed unit tests, and one bug they helped me find

* Fixed unclosed rows
  • Loading branch information
ocket8888 authored Jun 11, 2020
1 parent 74bc7f2 commit a3d76f8
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 119 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Changed the access logs in Traffic Ops to now show the route ID with every API endpoint call. The Route ID is appended to the end of the access log line.
- With the addition of multiple server interfaces, interface data is constructed from IP Address/Gateway/Netmask (and their IPv6 counterparts) and Interface Name and Interface MTU fields on services. These **MUST** have proper, valid data before attempting to upgrade or the upgrade **WILL** fail. In particular IP fields need to be valid IP addresses/netmasks, and MTU must only be positive integers of at least 1280.
- The `/servers` and `/servers/{{ID}}}` API endpoints have been updated to use and reflect multi-interface servers.
- CDN Snapshots now use a server's "service addresses" to provide its IP addresses

### Deprecated
- Deprecated the non-nullable `DeliveryService` Go struct and other structs that use it. `DeliveryServiceNullable` structs should be used instead.
Expand Down
13 changes: 11 additions & 2 deletions traffic_ops/traffic_ops_golang/ats/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,12 +612,21 @@ func GetServerInfo(tx *sql.Tx, qry string, qryParams []interface{}) (*atscfg.Ser
return nil, false, errors.New("querying server info: " + err.Error())
}

infs, err := dbhelpers.GetServerInterfaces(s.ID, tx)
infs, err := dbhelpers.GetServersInterfaces([]int{s.ID}, tx)
if err != nil {
return nil, false, fmt.Errorf("querying server info interfaces: %v", err)
}

legacyInfo, err := tc.InterfaceInfoToLegacyInterfaces(infs)
interfaces, ok := infs[s.ID]
if !ok || len(interfaces) < 1 {
return nil, false, fmt.Errorf("server #%d has no interfaces", s.ID)
}

ifaces := make([]tc.ServerInterfaceInfo, 0, len(interfaces))
for _, inf := range interfaces {
ifaces = append(ifaces, inf)
}
legacyInfo, err := tc.InterfaceInfoToLegacyInterfaces(ifaces)
if err != nil {
return nil, false, fmt.Errorf("converting server info interfaces to legacy: %v", err)
}
Expand Down
153 changes: 96 additions & 57 deletions traffic_ops/traffic_ops_golang/crconfig/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/apache/trafficcontrol/lib/go-util"
"strconv"
"strings"

"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"

"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
)

const RouterTypeName = "CCR"
Expand Down Expand Up @@ -99,100 +100,92 @@ type ServerUnion struct {
SecureAPIPort *string
}

type ServerAndHost struct {
Server ServerUnion
Host string
}

const DefaultWeightMultiplier = 1000.0
const DefaultWeight = 0.999

func getAllServers(cdn string, tx *sql.Tx) (map[string]ServerUnion, error) {
servers := map[string]ServerUnion{}

serverParams, err := getServerParams(cdn, tx)
if err != nil {
return nil, errors.New("Error getting server params: " + err.Error())
}

// TODO select deliveryservices as array?
q := `
select s.host_name,
cg.name as cachegroup,
concat(s.host_name, '.', s.domain_name) as fqdn,
s.xmpp_id as hashid,
s.https_port,
s.interface_name,
s.ip_address_is_service,
s.ip6_address_is_service,
s.ip_address,
s.ip6_address,
s.tcp_port,
p.name as profile_name,
cast(p.routing_disabled as int),
st.name as status,
t.name as type
from server as s
inner join cachegroup as cg ON cg.id = s.cachegroup
inner join type as t on t.id = s.type
inner join profile as p ON p.id = s.profile
inner join status as st ON st.id = s.status
where cdn_id = (select id from cdn where name = $1)
and (st.name = 'REPORTED' or st.name = 'ONLINE' or st.name = 'ADMIN_DOWN')
`
SELECT
s.id,
s.host_name,
cg.name as cachegroup,
concat(s.host_name, '.', s.domain_name) AS fqdn,
s.xmpp_id AS hashid,
s.https_port,
s.tcp_port,
p.name AS profile_name,
cast(p.routing_disabled AS int),
st.name AS status,
t.name AS type
FROM server AS s
INNER JOIN cachegroup AS cg ON cg.id = s.cachegroup
INNER JOIN type AS t on t.id = s.type
INNER JOIN profile AS p ON p.id = s.profile
INNER JOIN status AS st ON st.id = s.status
WHERE cdn_id = (SELECT id FROM cdn WHERE name = $1)
AND (st.name = 'REPORTED' OR st.name = 'ONLINE' OR st.name = 'ADMIN_DOWN')
`
rows, err := tx.Query(q, cdn)
if err != nil {
return nil, errors.New("Error querying servers: " + err.Error())
}
defer rows.Close()

servers := map[int]ServerAndHost{}
ids := []int{}
for rows.Next() {
port := sql.NullInt64{}
ip6 := sql.NullString{}
hashId := sql.NullString{}
httpsPort := sql.NullInt64{}
var port sql.NullInt64
var hashId sql.NullString
var httpsPort sql.NullInt64

ipIsService := false
ip6IsService := false
var s ServerAndHost

s := ServerUnion{}

host := ""
status := ""
if err := rows.Scan(&host, &s.CacheGroup, &s.Fqdn, &hashId, &httpsPort, &s.InterfaceName, &ipIsService, &ip6IsService, &s.Ip, &ip6, &port, &s.Profile, &s.RoutingDisabled, &status, &s.ServerType); err != nil {
var status string
var id int
if err := rows.Scan(&id, &s.Host, &s.Server.CacheGroup, &s.Server.Fqdn, &hashId, &httpsPort, &port, &s.Server.Profile, &s.Server.RoutingDisabled, &status, &s.Server.ServerType); err != nil {
return nil, errors.New("Error scanning server: " + err.Error())
}
if !ipIsService {
s.Ip = util.StrPtr("")
}
if !ip6IsService {
s.Ip6 = util.StrPtr("")
} else {
s.Ip6 = &ip6.String // Don't check valid, assign empty string if null
}

s.LocationId = s.CacheGroup
ids = append(ids, id)

s.Server.LocationId = s.Server.CacheGroup

serverStatus := tc.CRConfigServerStatus(status)
s.ServerStatus = &serverStatus
s.Server.ServerStatus = &serverStatus
if port.Valid {
i := int(port.Int64)
s.Port = &i
s.Server.Port = &i
}

if hashId.String != "" {
s.HashId = &hashId.String
s.Server.HashId = &hashId.String
} else {
s.HashId = &host
s.Server.HashId = &s.Host
}

if httpsPort.Valid {
i := int(httpsPort.Int64)
s.HttpsPort = &i
s.Server.HttpsPort = &i
}

params, hasParams := serverParams[host]
params, hasParams := serverParams[s.Host]
if hasParams && params.APIPort != nil {
s.APIPort = params.APIPort
s.Server.APIPort = params.APIPort
}

if hasParams && params.SecureAPIPort != nil {
s.SecureAPIPort = params.SecureAPIPort
s.Server.SecureAPIPort = params.SecureAPIPort
}

weightMultiplier := DefaultWeightMultiplier
Expand All @@ -204,15 +197,61 @@ and (st.name = 'REPORTED' or st.name = 'ONLINE' or st.name = 'ADMIN_DOWN')
weight = *params.Weight
}
hashCount := int(weight * weightMultiplier)
s.HashCount = &hashCount
s.Server.HashCount = &hashCount

servers[host] = s
servers[id] = s
}
if err := rows.Err(); err != nil {
return nil, errors.New("Error iterating router param rows: " + err.Error())
}

return servers, nil
interfaces, err := dbhelpers.GetServersInterfaces(ids, tx)
if err != nil {
return nil, fmt.Errorf("getting interfaces for servers: %v", err)
}

hostToServerMap := make(map[string]ServerUnion, len(servers))
for id, server := range servers {
ifaces, ok := interfaces[id]
if !ok {
log.Warnf("server '%s' (#%d) has no interfaces", server.Host, id)
server.Server.InterfaceName = new(string)
server.Server.Ip = new(string)
server.Server.Ip6 = new(string)
hostToServerMap[server.Host] = server.Server
continue
}

infs := make([]tc.ServerInterfaceInfo, 0, len(ifaces))
for _, inf := range ifaces {
infs = append(infs, inf)
}

legacyNet, err := tc.InterfaceInfoToLegacyInterfaces(infs)
if err != nil {
return nil, fmt.Errorf("Error converting interfaces to legacy data for server '%s' (#%d): %v", server.Host, id, err)
}

server.Server.Ip = legacyNet.IPAddress
server.Server.Ip6 = legacyNet.IP6Address

if server.Server.Ip == nil {
server.Server.Ip = new(string)
}
if server.Server.Ip6 == nil {
server.Server.Ip6 = new(string)
}

server.Server.InterfaceName = legacyNet.InterfaceName
if server.Server.InterfaceName == nil {
server.Server.InterfaceName = new(string)
log.Warnf("Server %s (#%d) had no service-address-containing interfaces", server.Host, id)
}

hostToServerMap[server.Host] = server.Server
}

return hostToServerMap, nil
}

func getServerDSNames(cdn string, tx *sql.Tx) (map[tc.CacheName][]tc.DeliveryServiceName, error) {
Expand Down
Loading

0 comments on commit a3d76f8

Please sign in to comment.