zrhoffman commented on a change in pull request #4790:
URL: https://github.com/apache/trafficcontrol/pull/4790#discussion_r463884113
##########
File path: lib/go-atscfg/parentdotconfig.go
##########
@@ -331,13 +330,268 @@ func MakeParentDotConfig(
defaultDestText += ` qstring=` + qStr
}
defaultDestText += "\n"
-
- sort.Sort(sort.StringSlice(textArr))
- text = hdr + strings.Join(textArr, "") + defaultDestText
}
+
+ sort.Sort(sort.StringSlice(textArr))
+ text := hdr + strings.Join(textArr, "") + defaultDestText
return text
}
+func GetTopologyParentConfigLine(
+ server tc.Server,
+ servers []tc.Server,
+ ds ParentConfigDSTopLevel,
+ serverParams map[string]string,
+ parentConfigParams []ParameterWithProfilesMap, // all params with
configFile parent.config
+ topologies []tc.Topology,
+ serverCapabilities map[int]map[ServerCapability]struct{},
+ cacheGroups map[tc.CacheGroupName]tc.CacheGroupNullable,
+) (string, error) {
+ txt := ""
+
+ if !HasRequiredCapabilities(serverCapabilities[server.ID],
ds.RequiredCapabilities) {
+ return "", nil
+ }
+
+ orgURI, err := GetOriginURI(ds.OriginFQDN)
+ if err != nil {
+ return "", errors.New("Malformed ds '" + string(ds.Name) + "'
origin URI: '" + ds.OriginFQDN + "': skipping!" + err.Error())
+ }
+
+ // This could be put in a map beforehand to only iterate once, if
performance mattered
+ topology := tc.Topology{}
+ for _, to := range topologies {
+ if to.Name == ds.Topology {
+ topology = to
+ break
+ }
+ }
+ if topology.Name == "" {
+ return "", errors.New("DS " + string(ds.Name) + " topology '" +
ds.Topology + "' not found in Topologies!")
+ }
+
+ txt += "dest_domain=" + orgURI.Hostname() + " port=" + orgURI.Port()
+
+ log.Errorf("DEBUG topo GetTopologyParentConfigLine calling
getTopologyPlacement cg '" + server.Cachegroup + "'\n")
+ serverPlacement :=
getTopologyPlacement(tc.CacheGroupName(server.Cachegroup), topology,
cacheGroups)
+ if !serverPlacement.InTopology {
+ return "", nil // server isn't in topology, no error
+ }
+ // TODO add Topology/Capabilities to remap.config
+
+ parents, secondaryParents, err := GetTopologyParents(server, ds,
servers, parentConfigParams, topology, serverPlacement.IsLastTier,
serverCapabilities)
+ if err != nil {
+ return "", errors.New("getting topology parents for '" +
string(ds.Name) + "': skipping! " + err.Error())
+ }
+ txt += ` parent="` + strings.Join(parents, `;`) + `"`
+ if len(secondaryParents) > 0 {
+ txt += ` secondary_parent="` + strings.Join(secondaryParents,
`;`) + `"`
+ }
+ txt += ` round_robin=` + getTopologyRoundRobin(ds, serverParams,
serverPlacement.IsLastTier)
+ txt += ` go_direct=` + getTopologyGoDirect(ds,
serverPlacement.IsLastTier)
+ txt += ` qstring=` + getTopologyQueryString(ds, serverParams,
serverPlacement.IsLastTier)
+ txt += getTopologyParentIsProxyStr(serverPlacement.IsLastTier)
+ txt += " # topology '" + ds.Topology + "'"
+ txt += "\n"
+ return txt, nil
+}
+
+func getTopologyParentIsProxyStr(serverIsLastTier bool) string {
+ if serverIsLastTier {
+ return ` parent_is_proxy=false`
+ }
+ return ""
+}
+
+func getTopologyRoundRobin(ds ParentConfigDSTopLevel, serverParams
map[string]string, serverIsLastTier bool) string {
+ roundRobinConsistentHash := "consistent_hash"
+ if !serverIsLastTier {
+ return roundRobinConsistentHash
+ }
+ if parentSelectAlg := serverParams[ParentConfigParamAlgorithm];
ds.OriginShield != "" && strings.TrimSpace(parentSelectAlg) != "" {
+ return parentSelectAlg
+ }
+ if ds.MultiSiteOrigin {
+ return ds.MSOAlgorithm
+ }
+ return roundRobinConsistentHash
+}
+
+func getTopologyGoDirect(ds ParentConfigDSTopLevel, serverIsLastTier bool)
string {
+ if !serverIsLastTier {
+ return "false"
+ }
+ if ds.OriginShield != "" {
+ return "true"
+ }
+ if ds.MultiSiteOrigin {
+ return "false"
+ }
+ return "true"
+}
+
+func getTopologyQueryString(ds ParentConfigDSTopLevel, serverParams
map[string]string, serverIsLastTier bool) string {
+ if serverIsLastTier {
+ if ds.MultiSiteOrigin && ds.QStringHandling == "" &&
ds.MSOAlgorithm == tc.AlgorithmConsistentHash && ds.QStringIgnore ==
tc.QStringIgnoreUseInCacheKeyAndPassUp {
+ return "consider"
+ }
+ return "ignore"
+ }
+
+ if param := serverParams[ParentConfigParamQStringHandling]; param != ""
{
+ return param
+ }
+ if ds.QStringHandling != "" {
+ return ds.QStringHandling
+ }
+ if ds.QStringIgnore == tc.QStringIgnoreUseInCacheKeyAndPassUp {
+ return "consider"
+ }
+ return "ignore"
+}
+
+// serverParentageParams gets the Parameters used for parent= line, or
defaults if they don't exist
+// Returns the Parameters used for parent= lines, for the given server.
+func serverParentageParams(sv tc.Server, params []ParameterWithProfilesMap)
ProfileCache {
+ // TODO deduplicate with atstccfg/parentdotconfig.go
+ profileCache := DefaultProfileCache()
+ profileCache.Port = sv.TCPPort
+ for _, param := range params {
+ if _, ok := param.ProfileNames[sv.Profile]; !ok {
+ continue
+ }
+ switch param.Name {
+ case ParentConfigCacheParamWeight:
+ profileCache.Weight = param.Value
+ case ParentConfigCacheParamPort:
+ i, err := strconv.ParseInt(param.Value, 10, 64)
+ if err != nil {
+ log.Errorln("parent.config generation: port
param is not an integer, skipping! : " + err.Error())
+ } else {
+ profileCache.Port = int(i)
+ }
+ case ParentConfigCacheParamUseIP:
+ profileCache.UseIP = param.Value == "1"
+ case ParentConfigCacheParamRank:
+ i, err := strconv.ParseInt(param.Value, 10, 64)
+ if err != nil {
+ log.Errorln("parent.config generation: rank
param is not an integer, skipping! : " + err.Error())
+ } else {
+ profileCache.Rank = int(i)
+ }
+ case ParentConfigCacheParamNotAParent:
+ profileCache.NotAParent = param.Value != "false"
+ }
+ }
+ return profileCache
+}
+
+func serverParentStr(sv tc.Server, params []ParameterWithProfilesMap) string {
+ svParams := serverParentageParams(sv, params)
+ if svParams.NotAParent {
+ return ""
+ }
+ host := ""
+ if svParams.UseIP {
+ host = sv.IPAddress
+ } else {
+ host = sv.HostName + "." + sv.DomainName
+ }
+ return host + ":" + strconv.Itoa(svParams.Port) + "|" + svParams.Weight
+}
+
+func GetTopologyParents(
+ server tc.Server,
+ ds ParentConfigDSTopLevel,
+ servers []tc.Server,
+ parentConfigParams []ParameterWithProfilesMap, // all params with
configFile parent.confign
+ topology tc.Topology,
+ serverIsLastTier bool,
+ serverCapabilities map[int]map[ServerCapability]struct{},
+) ([]string, []string, error) {
+ // If it's the last tier, then the parent is the origin.
+ // Note this doesn't include MSO, whose final tier cachegroup points to
the origin cachegroup.
+ if serverIsLastTier {
+ orgURI, err := GetOriginURI(ds.OriginFQDN) // TODO pass,
instead of calling again
+ if err != nil {
+ return nil, nil, err
+ }
+ return []string{orgURI.Host}, nil, nil
+ }
+
+ svNode := tc.TopologyNode{}
+ for _, node := range topology.Nodes {
+ if node.Cachegroup == server.Cachegroup {
+ svNode = node
+ break
+ }
+ }
+ if svNode.Cachegroup == "" {
+ return nil, nil, errors.New("This server '" + server.HostName +
"' not in DS " + string(ds.Name) + " topology, skipping")
+ }
+
+ if len(svNode.Parents) == 0 {
+ return nil, nil, errors.New("DS " + string(ds.Name) + "
topology '" + ds.Topology + "' is last tier, but NonLastTier called! Should
never happen")
+ }
+ if numParents := len(svNode.Parents); numParents > 2 {
+ log.Errorln("DS " + string(ds.Name) + " topology '" +
ds.Topology + "' has " + strconv.Itoa(numParents) + " parent nodes, but Apache
Traffic Server only supports Primary and Secondary (2) lists of parents.
CacheGroup nodes after the first 2 will be ignored!")
+ }
+ if len(topology.Nodes) <= svNode.Parents[0] {
+ return nil, nil, errors.New("DS " + string(ds.Name) + "
topology '" + ds.Topology + "' node parent " + strconv.Itoa(svNode.Parents[0])
+ " greater than number of topology nodes " + strconv.Itoa(len(topology.Nodes))
+ ". Cannot create parents!")
+ }
+ if len(svNode.Parents) > 1 && len(topology.Nodes) <= svNode.Parents[1] {
+ log.Errorln("DS " + string(ds.Name) + " topology '" +
ds.Topology + "' node secondary parent " + strconv.Itoa(svNode.Parents[1]) + "
greater than number of topology nodes " + strconv.Itoa(len(topology.Nodes)) +
". Secondary parent will be ignored!")
+ }
+
+ parentCG := topology.Nodes[svNode.Parents[0]].Cachegroup
+ secondaryParentCG := ""
+ if len(svNode.Parents) > 1 && len(topology.Nodes) > svNode.Parents[1] {
+ secondaryParentCG = topology.Nodes[svNode.Parents[1]].Cachegroup
+ }
+
+ if parentCG == "" {
+ return nil, nil, errors.New("Server '" + server.HostName + "'
DS " + string(ds.Name) + " topology '" + ds.Topology + "' cachegroup '" +
server.Cachegroup + "' topology node parent " + strconv.Itoa(svNode.Parents[0])
+ " is not in the topology!")
+ }
+
+ parentStrs := []string{}
+ secondaryParentStrs := []string{}
+ for _, sv := range servers {
+ if tc.CacheType(sv.Type) != tc.CacheTypeEdge &&
tc.CacheType(sv.Type) != tc.CacheTypeMid && sv.Type != tc.OriginTypeName {
+ continue // only consider edges, mids, and origins in
the CacheGroup.
+ }
+ if !HasRequiredCapabilities(serverCapabilities[sv.ID],
ds.RequiredCapabilities) {
+ continue
+ }
+ if sv.Cachegroup == parentCG {
+ parentStr := serverParentStr(sv, parentConfigParams)
+ if parentStr != "" { // will be empty if server is
not_a_parent (possibly other reasons)
+ parentStrs = append(parentStrs, parentStr)
+ }
+ }
+ if sv.Cachegroup == secondaryParentCG {
+ secondaryParentStrs = append(secondaryParentStrs,
serverParentStr(sv, parentConfigParams))
+ }
Review comment:
If the immediate parent cachegroup has no servers in it
`GetTopologyParents()`, returns empty arrays, even if there are servers in
further ancestor topology nodes. Should it keep going until it finds a topology
node with a non-empty cachegroup?
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]