zrhoffman commented on a change in pull request #4790:
URL: https://github.com/apache/trafficcontrol/pull/4790#discussion_r464691507



##########
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:
       On second thought, maybe we should perform validation to make sure a 
topology node's cachegroup contains at least 1 server.




----------------------------------------------------------------
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]


Reply via email to