This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 33feb76f10 T3c add parent retry params for first./inner./last. (#6859)
33feb76f10 is described below

commit 33feb76f1040fab09d7893772b95e8a0b010955a
Author: Brian Olsen <[email protected]>
AuthorDate: Thu Jul 28 14:05:31 2022 -0600

    T3c add parent retry params for first./inner./last. (#6859)
    
    * Add support for first./inner./last. for parent.config retries
    
    * move merge_parent_groups from mso table into parameters list in another 
section
    
    * add documentation for first./inner./last. retry/markdown options
    
    * refactor how parent retry config keys are made
    
    * fix serverPlacement logic for non topology ds, add godoc function comment
    
    * remove extra secondary_mode check and add hasValues check for 
IsLastCacheTier
    
    * snidotyaml_test: convert to using ParentConfigRetryKeysDefault for 
Algorithm
---
 CHANGELOG.md                                 |   1 +
 docs/source/admin/quick_howto/multi_site.rst |  44 +-
 docs/source/overview/delivery_services.rst   |  60 +-
 lib/go-atscfg/parentdotconfig.go             | 748 ++++++++++++-----------
 lib/go-atscfg/parentdotconfig_test.go        | 849 +++++++++++++++++++++++++--
 lib/go-atscfg/snidotyaml_test.go             |   8 +-
 lib/go-atscfg/sslservernamedotyaml_test.go   |   8 +-
 lib/go-atscfg/strategiesdotconfig.go         |   7 +-
 lib/go-atscfg/strategiesdotconfig_test.go    | 755 ++++++++++++++++++++++--
 9 files changed, 1992 insertions(+), 488 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ac611ea77..8fec71c9e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Change to t3c regex_revalidate so that STALE is no longer explicitly added 
for default revalidate rule for ATS version backwards compatibility.
 - Change to t3c diff to flag a config file for replacement if owner/group 
settings are not `ats` 
[#6879](https://github.com/apache/trafficcontrol/issues/6879).
 - t3c now looks in the executable dir path for t3c- utilities
+- Added support for parent.config markdown/retry DS parameters using 
first./inner./last. prefixes.  mso. and <null> prefixes should be deprecated.
 
 ### Fixed
 - Fixed TO to default route ID to 0, if it is not present in the request 
context.
diff --git a/docs/source/admin/quick_howto/multi_site.rst 
b/docs/source/admin/quick_howto/multi_site.rst
index 2b648182bf..5e64937ec8 100644
--- a/docs/source/admin/quick_howto/multi_site.rst
+++ b/docs/source/admin/quick_howto/multi_site.rst
@@ -68,33 +68,35 @@ The following steps will take you through the procedure of 
setting up an :abbr:`
 
        #) Click :guilabel:`Show profile parameters` to bring up the 
:term:`Parameters` screen for the :term:`Profile`. Create the following 
:term:`Parameters`:
 
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
-               | Parameter Name                         | Config File Name | 
Value                    | ATS parent.config value |
-               
+========================================+==================+==========================+=========================+
-               | mso.algorithm                          | parent.config    | 
true, false, strict,     | round_robin             |
-               |                                        |                  | 
consistent_hash          |                         |
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
-               | mso.parent_retry                       | parent.config    | 
simple_retry, both,      | parent_retry            |
-               |                                        |                  | 
unavailable_server_retry |                         |
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
-               | mso.unavailable_server_retry_responses | parent.config    | 
list of server response  | defaults to the value   |
-               |                                        |                  | 
codes, eg "500,502,503"  | in records.config       |
-               |                                        |                  |   
                       | when unused.            |
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
-               | mso.max_simple_retries                 | parent.config    | 
Nubmer of retries made   | defaults to the value   |
-               |                                        |                  | 
after a 4xx error        | in records.config       |
-               |                                        |                  |   
                       | when unused.            |
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
-               | mso.max_unavailable_server_retries     | parent.config    | 
Nubmer of retries made   | defaults to the value   |
-               |                                        |                  | 
after a 5xx error        | in records.config       |
-               |                                        |                  |   
                       | when unused.            |
-               
+----------------------------------------+------------------+--------------------------+-------------------------+
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
+               | Parameter Name                          | Config File Name | 
Value                    | ATS parent.config value |
+               
+=========================================+==================+==========================+=========================+
+               | last.algorithm                          | parent.config    | 
true, false, strict,     | round_robin             |
+               |                                         |                  | 
consistent_hash          |                         |
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
+               | last.parent_retry                       | parent.config    | 
simple_retry, both,      | parent_retry            |
+               |                                         |                  | 
unavailable_server_retry |                         |
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
+               | last.unavailable_server_retry_responses | parent.config    | 
list of server response  | defaults to the value   |
+               |                                         |                  | 
codes, eg "500,502,503"  | in records.config       |
+               |                                         |                  |  
                        | when unused.            |
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
+               | last.max_simple_retries                 | parent.config    | 
Nubmer of retries made   | defaults to the value   |
+               |                                         |                  | 
after a 4xx error        | in records.config       |
+               |                                         |                  |  
                        | when unused.            |
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
+               | last.max_unavailable_server_retries     | parent.config    | 
Nubmer of retries made   | defaults to the value   |
+               |                                         |                  | 
after a 5xx error        | in records.config       |
+               |                                         |                  |  
                        | when unused.            |
+               
+-----------------------------------------+------------------+--------------------------+-------------------------+
 
 
                .. figure:: multi_site/ds_profile_parameters.png
                        :scale: 100%
                        :align: center
 
+    .. deprecated:: ATC 6.2
+
        #) In the :term:`Delivery Service` page, select the newly created 
``DS_PROFILE`` and save the :term:`Delivery Service`.
 
 #. Turn on parent_proxy_routing in the MID :term:`Profile`.
diff --git a/docs/source/overview/delivery_services.rst 
b/docs/source/overview/delivery_services.rst
index 811da8d1b4..d043383f35 100644
--- a/docs/source/overview/delivery_services.rst
+++ b/docs/source/overview/delivery_services.rst
@@ -1003,11 +1003,13 @@ The following :term:`Parameters` must have the 
:ref:`Config File <parameter-conf
 
        .. impl-detail:: This :term:`Parameter` does not affect the contents of 
``parent.config``, but instead ``strategies.yaml`` in :abbr:`ATS (Apache 
Traffic Server)` 9. It has the ``parent.config`` :ref:`parameter-config-file` 
value for consistency.
 
+- ``merge_parent_groups`` - on a Deliver Service :term:`Profile`, if this 
exists, moves each of the space-separated :term:`Cache Groups` named in the 
:ref:`parameter-value` from the secondary parent list into the primary parent 
list. This can be used to combine all parents into a single consistent hash 
ring.
+
        .. deprecated:: ATCv6.2
                In :ref:`to-api` version 4 (unstable at the time of this 
writing), TLS versions should be configured using the `TLS Versions`_ property 
of the Delivery Service, and support for this :term:`Parameter` will be removed 
at some point after the stabilization of :ref:`to-api` version 4.
 
-Parameters that Affect Multi-Site Origin
-''''''''''''''''''''''''''''''''''''''''
+Parameters that Affect Multi-Site Origin and inside a CDN
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 Each :term:`Parameter` directly corresponds to a field in a line of the 
:abbr:`ATS (Apache Traffic Server)` `parent.config file 
<https://docs.trafficserver.apache.org/en/7.1.x/admin-guide/files/parent.config.en.html>`_
 (usually by almost the same name), and documentation for these fields is 
provided in the form of links to their entries in the :abbr:`ATS (Apache 
Traffic Server)` documentation.
 
 .. _round_robin: 
https://docs.trafficserver.apache.org/en/9.1.x/admin-guide/files/parent.config.en.html#parent-config-format-round-robin
@@ -1023,33 +1025,33 @@ Each :term:`Parameter` directly corresponds to a field 
in a line of the :abbr:`A
 
 .. table:: :term:`Parameters` of a Delivery Service Profile_ that Affect 
:abbr:`MSO (Multi-Site-Origin)` Configuration
 
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | Name                                        | :abbr:`ATS (Apache 
Traffic Server)` `parent.config`_ field                 | Effect                
                                                              |
-       
+=============================================+============================================================================+=====================================================================================+
-       | mso.algorithm                               | `round_robin`_          
                                                   | Sets the algorithm used to 
determine from which :term:`origin server` content will  |
-       |                                             |                         
                                                   | be requested.              
                                                         |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | mso.max_simple_retries                      | `max_simple_retries`_   
                                                   | Sets a strict limit on the 
number of "simple retries" allowed before giving up      |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | mso.max_unavailable_server_retries          | 
`max_unavailable_server_retries`_                                          | 
Sets a strict limit on the number of times the :term:`cache server` will 
attempt to |
-       |                                             |                         
                                                   | request content from an 
:term:`origin server` that has previously been considered   |
-       |                                             |                         
                                                   | "unavailable".             
                                                         |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | mso.parent_retry                            | `parent_retry`_         
                                                   | Sets whether the 
:term:`cache servers` will use "simple retries",                   |
-       |                                             |                         
                                                   | "unavailable server 
retries", or both.                                              |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | mso.simple_retry_response_codes             | **UNKNOWN**             
                                                   | **UNKNOWN** - supposedly 
defines HTTP response codes from an :term:`origin server`  |
-       |                                             |                         
                                                   | that necessitate a "simple 
retry".                                                  |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | mso.unavailable_server_retry_response_codes | 
`unavailable_server_retry_responses`_                                      | 
Defines HTTP response codes from an :term:`origin server` that indicate it is   
    |
-       |                                             |                         
                                                   | currently "unavailable".   
                                                         |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-       | merge_parent_groups                         | `parent`_ and 
`secondary_parent`_                                          | Moves each of 
the space-separated :term:`Cache Groups` named in the                 |
-       |                                             |                         
                                                   | :ref:`parameter-value` 
from the secondary parent list into the primary parent list. |
-       |                                             |                         
                                                   | This can be used to 
combine all parents into a single consistent hash ring.         |
-       
+---------------------------------------------+----------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
-
-.. warning:: The ``mso.simple_retry_response_codes`` :term:`Parameter` has no 
apparent, possible use according to the :abbr:`ATS (Apache Traffic Server)` 
`parent.config documentation 
<https://docs.trafficserver.apache.org/en/9.1.x/admin-guide/files/parent.config.en.html>`_.
 Whether or not it has any effect - let alone the *intended* effect - is not 
known, and its use is therefore strongly discouraged.
+       
+---------------------------------------------+--------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | Name                                    | :abbr:`ATS (Apache Traffic 
Server)` `parent.config`_ field | Effect                                        
                                      |
+       
+=========================================+============================================================+=====================================================================================+
+       | algorithm                               | `round_robin`_              
                               | Sets the algorithm used to determine from 
which :term:`origin server` content will  |
+       |                                         |                             
                               | be requested.                                  
                                     |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | max_simple_retries                      | `max_simple_retries`_       
                               | Sets a strict limit on the number of "simple 
retries" allowed before giving up      |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | max_unavailable_server_retries          | 
`max_unavailable_server_retries`_                          | Sets a strict 
limit on the number of times the :term:`cache server` will attempt to |
+       |                                         |                             
                               | request content from an :term:`origin server` 
that has previously been considered   |
+       |                                         |                             
                               | "unavailable".                                 
                                     |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | parent_retry                            | `parent_retry`_             
                               | Sets whether the :term:`cache servers` will 
use "simple retries",                   |
+       |                                         |                             
                               | "unavailable server retries", or both.         
                                     |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | simple_retry_response_codes             | **UNKNOWN**                 
                               | **UNKNOWN** - supposedly defines HTTP response 
codes from an :term:`origin server`  |
+       |                                         |                             
                               | that necessitate a "simple retry".             
                                     |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+       | unavailable_server_retry_response_codes | 
`unavailable_server_retry_responses`_                      | Defines HTTP 
response codes from an :term:`origin server` that indicate it is       |
+       |                                         |                             
                               | currently "unavailable".                       
                                     |
+       
+-----------------------------------------+------------------------------------------------------------+-------------------------------------------------------------------------------------+
+
+The above :term:`Parameters` are supported for ``first``, ``inner`` and 
``last`` tiers by specifying prefixes ``first.``, ``inner.`` and ``last.``, 
applicable to both topology and non topology.  This allows fine tuning of 
markdown and retry behavior inside a CDN.
+
+.. deprecated:: The ``mso.`` prefix is deprecated.  ``last.`` prefix should be 
preferred although no prefix can also be used.
+
+.. warning:: The `simple_retry_response_codes`` :term:`Parameter` has no 
apparent, possible use according to the :abbr:`ATS (Apache Traffic Server)` 
`parent.config documentation 
<https://docs.trafficserver.apache.org/en/9.1.x/admin-guide/files/parent.config.en.html>`_.
 Whether or not it has any effect - let alone the *intended* effect - is not 
known, and its use is therefore strongly discouraged.
 
 .. seealso:: To see how the :ref:`Values <parameter-value>` of these 
Parameters are interpreted, refer to the `Apache Traffic Server documentation 
on the parent.config configuration file 
<https://docs.trafficserver.apache.org/en/7.1.x/admin-guide/files/parent.config.en.html>`_
 
diff --git a/lib/go-atscfg/parentdotconfig.go b/lib/go-atscfg/parentdotconfig.go
index b57a920720..c23c72382c 100644
--- a/lib/go-atscfg/parentdotconfig.go
+++ b/lib/go-atscfg/parentdotconfig.go
@@ -39,20 +39,7 @@ const LineCommentParentDotConfig = LineCommentHash
 const ParentConfigFileName = "parent.config"
 
 const ParentConfigParamQStringHandling = "psel.qstring_handling"
-const ParentConfigParamMSOAlgorithm = "mso.algorithm"
-const ParentConfigParamMSOParentRetry = "mso.parent_retry"
-const ParentConfigParamMSOUnavailableServerRetryResponses = 
"mso.unavailable_server_retry_responses"
-const ParentConfigParamMSOMaxSimpleRetries = "mso.max_simple_retries"
-const ParentConfigParamMSOMaxUnavailableServerRetries = 
"mso.max_unavailable_server_retries"
 const ParentConfigParamMergeGroups = "merge_parent_groups"
-const ParentConfigParamAlgorithm = "algorithm"
-const ParentConfigParamQString = "qstring"
-const ParentConfigParamSecondaryMode = "try_all_primaries_before_secondary"
-
-const ParentConfigParamParentRetry = "parent_retry"
-const ParentConfigParamUnavailableServerRetryResponses = 
"unavailable_server_retry_responses"
-const ParentConfigParamMaxSimpleRetries = "max_simple_retries"
-const ParentConfigParamMaxUnavailableServerRetries = 
"max_unavailable_server_retries"
 
 const ParentConfigDSParamDefaultMSOAlgorithm = 
ParentAbstractionServiceRetryPolicyConsistentHash
 const ParentConfigDSParamDefaultMSOParentRetry = "both"
@@ -67,6 +54,38 @@ const ParentConfigCacheParamRank = "rank"
 const ParentConfigCacheParamNotAParent = "not_a_parent"
 const StrategyConfigUsePeering = "use_peering"
 
+// same across DS
+const ParentConfigParamQString = "qstring"
+
+type ParentConfigRetryKeys struct {
+       Algorithm                 string
+       SecondaryMode             string
+       ParentRetry               string
+       MaxSimpleRetries          string
+       SimpleRetryResponses      string
+       MaxUnavailableRetries     string
+       UnavailableRetryResponses string
+}
+
+func MakeParentConfigRetryKeysWithPrefix(prefix string) ParentConfigRetryKeys {
+       return ParentConfigRetryKeys{
+               Algorithm:                 prefix + "algorithm",
+               SecondaryMode:             prefix + 
"try_all_primaries_before_secondary",
+               ParentRetry:               prefix + "parent_retry",
+               MaxSimpleRetries:          prefix + "max_simple_retries",
+               SimpleRetryResponses:      prefix + 
"simple_server_retry_responses",
+               MaxUnavailableRetries:     prefix + 
"max_unavailable_server_retries",
+               UnavailableRetryResponses: prefix + 
"unavailable_server_retry_responses",
+       }
+}
+
+var ParentConfigRetryKeysFirst = MakeParentConfigRetryKeysWithPrefix("first.")
+var ParentConfigRetryKeysInner = MakeParentConfigRetryKeysWithPrefix("inner.")
+var ParentConfigRetryKeysLast = MakeParentConfigRetryKeysWithPrefix("last.")
+
+var ParentConfigRetryKeysMSO = MakeParentConfigRetryKeysWithPrefix("mso.")
+var ParentConfigRetryKeysDefault = MakeParentConfigRetryKeysWithPrefix("")
+
 type OriginHost string
 type OriginFQDN string
 
@@ -331,6 +350,7 @@ func makeParentDotConfigData(
        warnings = append(warnings, dsOriginWarns...)
 
        for _, ds := range dses {
+
                if ds.XMLID == nil || *ds.XMLID == "" {
                        warnings = append(warnings, "got ds with missing XMLID, 
skipping!")
                        continue
@@ -357,14 +377,11 @@ func makeParentDotConfigData(
                        continue
                }
 
-               // Note these Parameters are only used for MSO for legacy 
DeliveryServiceServers DeliveryServices (except QueryStringHandling which is 
used by all DeliveryServices).
-               //      Topology DSes use them for all DSes, MSO and non-MSO.
-               dsParams, dsParamsWarnings := getParentDSParams(ds, 
profileParentConfigParams)
-               warnings = append(warnings, dsParamsWarnings...)
+               isMSO := ds.MultiSiteOrigin != nil && *ds.MultiSiteOrigin
 
                // TODO put these in separate functions. No if-statement should 
be this long.
                if ds.Topology != nil && *ds.Topology != "" {
-                       txt, topoWarnings, err := getTopologyParentConfigLine(
+                       pasvc, topoWarnings, err := getTopologyParentConfigLine(
                                server,
                                serversWithParams,
                                &ds,
@@ -374,7 +391,8 @@ func makeParentDotConfigData(
                                serverCapabilities,
                                dsRequiredCapabilities,
                                cacheGroups,
-                               dsParams,
+                               profileParentConfigParams,
+                               isMSO,
                                atsMajorVer,
                                dsOrigins[DeliveryServiceID(*ds.ID)],
                                opt.AddComments,
@@ -386,214 +404,231 @@ func makeParentDotConfigData(
                                continue
                        }
 
-                       if txt != nil { // will be nil with no error if this 
server isn't in the Topology, or if it doesn't have the Required Capabilities
-                               parentAbstraction.Services = 
append(parentAbstraction.Services, txt)
-                       }
-               } else if cacheIsTopLevel {
-                       parentQStr := false
-                       if dsParams.QueryStringHandling == "" && 
dsParams.Algorithm == tc.AlgorithmConsistentHash && ds.QStringIgnore != nil && 
tc.QStringIgnore(*ds.QStringIgnore) == tc.QStringIgnoreUseInCacheKeyAndPassUp {
-                               parentQStr = true
+                       if pasvc != nil { // will be nil with no error if this 
server isn't in the Topology, or if it doesn't have the Required Capabilities
+                               parentAbstraction.Services = 
append(parentAbstraction.Services, pasvc)
                        }
-
-                       orgFQDNStr := *ds.OrgServerFQDN
-                       // if this cache isn't the last tier, i.e. we're not 
going to the origin, use http not https
-                       if isLastCacheTier := 
noTopologyServerIsLastCacheForDS(server, &ds); !isLastCacheTier {
-                               orgFQDNStr = strings.Replace(orgFQDNStr, 
`https://`, `http://`, -1)
-                       }
-                       orgURI, orgWarns, err := getOriginURI(orgFQDNStr)
-                       warnings = append(warnings, orgWarns...)
-                       if err != nil {
-                               warnings = append(warnings, "DS '"+*ds.XMLID+"' 
has malformed origin URI: '"+orgFQDNStr+"': skipping!"+err.Error())
-                               continue
+               } else {
+                       isLastCacheTier := 
noTopologyServerIsLastCacheForDS(server, &ds)
+                       serverPlacement := TopologyPlacement{
+                               IsLastCacheTier:  isLastCacheTier,
+                               IsFirstCacheTier: !isLastCacheTier || 
!ds.Type.UsesMidCache(),
                        }
 
-                       textLine := &ParentAbstractionService{}
-                       textLine.Name = *ds.XMLID
+                       dsParams, dswarns := getParentDSParams(ds, 
profileParentConfigParams, serverPlacement, isMSO)
+                       warnings = append(warnings, dswarns...)
 
-                       if ds.OriginShield != nil && *ds.OriginShield != "" {
+                       if cacheIsTopLevel {
+                               parentQStr := false
+                               if dsParams.QueryStringHandling == "" && 
dsParams.Algorithm == tc.AlgorithmConsistentHash && ds.QStringIgnore != nil && 
tc.QStringIgnore(*ds.QStringIgnore) == tc.QStringIgnoreUseInCacheKeyAndPassUp {
+                                       parentQStr = true
+                               }
 
-                               policy := 
ParentAbstractionServiceRetryPolicyConsistentHash
-                               if parentSelectAlg := 
serverParams[ParentConfigParamAlgorithm]; strings.TrimSpace(parentSelectAlg) != 
"" {
-                                       paramPolicy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(parentSelectAlg)
-                                       if paramPolicy != 
ParentAbstractionServiceRetryPolicyInvalid {
-                                               policy = paramPolicy
-                                       } else {
-                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed "+ParentConfigParamAlgorithm+" parameter 
'"+parentSelectAlg+"', not using!")
-                                       }
+                               orgFQDNStr := *ds.OrgServerFQDN
+                               // if this cache isn't the last tier, i.e. 
we're not going to the origin, use http not https
+                               if !isLastCacheTier {
+                                       orgFQDNStr = 
strings.Replace(orgFQDNStr, `https://`, `http://`, -1)
                                }
-                               textLine.Comment = 
makeParentComment(opt.AddComments, *ds.XMLID, "")
-                               textLine.DestDomain = orgURI.Hostname()
-                               textLine.Port, err = strconv.Atoi(orgURI.Port())
+                               orgURI, orgWarns, err := 
getOriginURI(orgFQDNStr)
+                               warnings = append(warnings, orgWarns...)
                                if err != nil {
-                                       if strings.ToLower(orgURI.Scheme) == 
"https" {
-                                               textLine.Port = 443
-                                       } else {
-                                               textLine.Port = 80
-                                       }
-                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(textLine.Port)+"! : "+err.Error())
+                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' has malformed origin URI: '"+orgFQDNStr+"': 
skipping!"+err.Error())
+                                       continue
                                }
 
-                               fqdnPort := strings.Split(*ds.OriginShield, ":")
-                               parent := &ParentAbstractionServiceParent{}
-                               parent.FQDN = fqdnPort[0]
-                               if len(fqdnPort) > 1 {
-                                       parent.Port, err = 
strconv.Atoi(fqdnPort[1])
+                               pasvc := &ParentAbstractionService{}
+                               pasvc.Name = *ds.XMLID
+
+                               if ds.OriginShield != nil && *ds.OriginShield 
!= "" {
+
+                                       policy := 
ParentAbstractionServiceRetryPolicyConsistentHash
+
+                                       if parentSelectAlg := 
serverParams[ParentConfigRetryKeysDefault.Algorithm]; 
strings.TrimSpace(parentSelectAlg) != "" {
+                                               paramPolicy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(parentSelectAlg)
+                                               if paramPolicy != 
ParentAbstractionServiceRetryPolicyInvalid {
+                                                       policy = paramPolicy
+                                               } else {
+                                                       warnings = 
append(warnings, "DS '"+*ds.XMLID+"' had malformed 
"+ParentConfigRetryKeysDefault.Algorithm+" parameter '"+parentSelectAlg+"', not 
using!")
+                                               }
+                                       }
+                                       pasvc.Comment = 
makeParentComment(opt.AddComments, *ds.XMLID, "")
+                                       pasvc.DestDomain = orgURI.Hostname()
+                                       pasvc.Port, err = 
strconv.Atoi(orgURI.Port())
                                        if err != nil {
+                                               if 
strings.ToLower(orgURI.Scheme) == "https" {
+                                                       pasvc.Port = 443
+                                               } else {
+                                                       pasvc.Port = 80
+                                               }
+                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(pasvc.Port)+"! : "+err.Error())
+                                       }
+
+                                       fqdnPort := 
strings.Split(*ds.OriginShield, ":")
+                                       parent := 
&ParentAbstractionServiceParent{}
+                                       parent.FQDN = fqdnPort[0]
+                                       if len(fqdnPort) > 1 {
+                                               parent.Port, err = 
strconv.Atoi(fqdnPort[1])
+                                               if err != nil {
+                                                       parent.Port = 80
+                                                       warnings = 
append(warnings, "DS '"+*ds.XMLID+"' had malformed origin  port: 
'"+*ds.OriginShield+"': using "+strconv.Itoa(parent.Port)+"! : "+err.Error())
+                                               }
+                                       } else {
                                                parent.Port = 80
-                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+*ds.OriginShield+"': using 
"+strconv.Itoa(parent.Port)+"! : "+err.Error())
+                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had no origin port: '"+*ds.OriginShield+"': using 
"+strconv.Itoa(parent.Port)+"!")
                                        }
-                               } else {
-                                       parent.Port = 80
-                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had no origin port: '"+*ds.OriginShield+"': using 
"+strconv.Itoa(parent.Port)+"!")
-                               }
-                               textLine.Parents = append(textLine.Parents, 
parent)
-                               textLine.RetryPolicy = policy
-                               textLine.GoDirect = true
+                                       pasvc.Parents = append(pasvc.Parents, 
parent)
+                                       pasvc.RetryPolicy = policy
+                                       pasvc.GoDirect = true
 
-                               // textLine += "dest_domain=" + 
orgURI.Hostname() + " port=" + orgURI.Port() + " parent=" + *ds.OriginShield + 
" " + algorithm + " go_direct=true\n"
+                                       // textLine += "dest_domain=" + 
orgURI.Hostname() + " port=" + orgURI.Port() + " parent=" + *ds.OriginShield + 
" " + algorithm + " go_direct=true\n"
 
-                       } else if ds.MultiSiteOrigin != nil && 
*ds.MultiSiteOrigin {
-                               textLine.Comment = 
makeParentComment(opt.AddComments, *ds.XMLID, "")
-                               textLine.DestDomain = orgURI.Hostname()
-                               textLine.Port, err = strconv.Atoi(orgURI.Port())
-                               if err != nil {
-                                       textLine.Port = 80
-                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(textLine.Port)+"! : "+err.Error())
-                               }
+                               } else if ds.MultiSiteOrigin != nil && 
*ds.MultiSiteOrigin {
+                                       pasvc.Comment = 
makeParentComment(opt.AddComments, *ds.XMLID, "")
+                                       pasvc.DestDomain = orgURI.Hostname()
+                                       pasvc.Port, err = 
strconv.Atoi(orgURI.Port())
+                                       if err != nil {
+                                               pasvc.Port = 80
+                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(pasvc.Port)+"! : "+err.Error())
+                                       }
 
-                               // textLine += "dest_domain=" + 
orgURI.Hostname() + " port=" + orgURI.Port() + " "
-                               if len(parentInfos) == 0 {
-                               }
+                                       // textLine += "dest_domain=" + 
orgURI.Hostname() + " port=" + orgURI.Port() + " "
+                                       if len(parentInfos) == 0 {
+                                       }
 
-                               if 
len(parentInfos[OriginHost(orgURI.Hostname())]) == 0 {
-                                       // TODO error? emulates Perl
-                                       warnings = append(warnings, "DS 
"+*ds.XMLID+" has no parent servers")
-                               }
+                                       if 
len(parentInfos[OriginHost(orgURI.Hostname())]) == 0 {
+                                               // TODO error? emulates Perl
+                                               warnings = append(warnings, "DS 
"+*ds.XMLID+" has no parent servers")
+                                       }
 
-                               parents, secondaryParents, secondaryMode, 
parentWarns := getMSOParentStrs(&ds, 
parentInfos[OriginHost(orgURI.Hostname())], atsMajorVer, dsParams.Algorithm, 
dsParams.TryAllPrimariesBeforeSecondary)
-                               warnings = append(warnings, parentWarns...)
-                               textLine.Parents = parents
-                               textLine.SecondaryParents = secondaryParents
-                               textLine.SecondaryMode = secondaryMode
-                               textLine.RetryPolicy = dsParams.Algorithm // 
TODO convert
-                               textLine.IgnoreQueryStringInParentSelection = 
!parentQStr
-                               textLine.GoDirect = true
-
-                               // textLine += parents + secondaryParents + ` 
round_robin=` + dsParams.Algorithm + ` qstring=` + parentQStr + ` 
go_direct=false parent_is_proxy=false`
-                               prWarns := []string{}
-                               textLine.MaxSimpleRetries, 
textLine.MaxMarkdownRetries, textLine.MarkdownResponseCodes, 
textLine.ErrorResponseCodes, prWarns = getParentRetryStr(true, atsMajorVer, 
dsParams.ParentRetry, dsParams.UnavailableServerRetryResponses, 
dsParams.MaxSimpleRetries, dsParams.MaxUnavailableServerRetries)
-                               warnings = append(warnings, prWarns...)
+                                       parents, secondaryParents, 
secondaryMode, parentWarns := getMSOParentStrs(&ds, 
parentInfos[OriginHost(orgURI.Hostname())], atsMajorVer, dsParams.Algorithm, 
dsParams.TryAllPrimariesBeforeSecondary)
+                                       warnings = append(warnings, 
parentWarns...)
+                                       pasvc.Parents = parents
+                                       pasvc.SecondaryParents = 
secondaryParents
+                                       pasvc.SecondaryMode = secondaryMode
+                                       pasvc.RetryPolicy = dsParams.Algorithm 
// TODO convert
+                                       
pasvc.IgnoreQueryStringInParentSelection = !parentQStr
+                                       pasvc.GoDirect = true
 
-                               parentAbstraction.Services = 
append(parentAbstraction.Services, textLine)
-                       }
-               } else {
-                       queryStringHandling := 
ParentSelectParamQStringHandlingToBool(serverParams[ParentConfigParamQStringHandling])
 // "qsh" in Perl
-                       if queryStringHandling == nil && 
serverParams[ParentConfigParamQStringHandling] != "" {
-                               warnings = append(warnings, "Server Parameter 
'"+ParentConfigParamQStringHandling+"' value 
'"+serverParams[ParentConfigParamQStringHandling]+"' malformed, not using!")
-                       }
+                                       // textLine += parents + 
secondaryParents + ` round_robin=` + dsParams.Algorithm + ` qstring=` + 
parentQStr + ` go_direct=false parent_is_proxy=false`
 
-                       roundRobin := 
ParentAbstractionServiceRetryPolicyConsistentHash
-                       // roundRobin := `round_robin=consistent_hash`
-                       goDirect := false
-                       // goDirect := `go_direct=false`
+                                       prWarns := 
dsParams.FillParentSvcRetries(cacheIsTopLevel, atsMajorVer, pasvc)
+                                       warnings = append(warnings, prWarns...)
 
-                       parents, secondaryParents, secondaryMode, parentWarns 
:= getParentStrs(&ds, dsRequiredCapabilities, 
parentInfos[deliveryServicesAllParentsKey], atsMajorVer, 
dsParams.TryAllPrimariesBeforeSecondary)
-                       warnings = append(warnings, parentWarns...)
+                                       parentAbstraction.Services = 
append(parentAbstraction.Services, pasvc)
+                               }
+                       } else {
+                               queryStringHandling := 
ParentSelectParamQStringHandlingToBool(serverParams[ParentConfigParamQStringHandling])
 // "qsh" in Perl
+                               if queryStringHandling == nil && 
serverParams[ParentConfigParamQStringHandling] != "" {
+                                       warnings = append(warnings, "Server 
Parameter '"+ParentConfigParamQStringHandling+"' value 
'"+serverParams[ParentConfigParamQStringHandling]+"' malformed, not using!")
+                               }
 
-                       text := &ParentAbstractionService{}
-                       text.Name = *ds.XMLID
+                               roundRobin := 
ParentAbstractionServiceRetryPolicyConsistentHash
+                               // roundRobin := `round_robin=consistent_hash`
+                               goDirect := false
+                               // goDirect := `go_direct=false`
 
-                       // peering ring check
-                       if dsParams.UsePeering {
-                               secondaryMode = 
ParentAbstractionServiceParentSecondaryModePeering
-                       }
+                               parents, secondaryParents, secondaryMode, 
parentWarns := getParentStrs(&ds, dsRequiredCapabilities, 
parentInfos[deliveryServicesAllParentsKey], atsMajorVer, 
dsParams.TryAllPrimariesBeforeSecondary)
+                               warnings = append(warnings, parentWarns...)
 
-                       orgFQDNStr := *ds.OrgServerFQDN
-                       // if this cache isn't the last tier, i.e. we're not 
going to the origin, use http not https
-                       if isLastCacheTier := 
noTopologyServerIsLastCacheForDS(server, &ds); !isLastCacheTier {
-                               orgFQDNStr = strings.Replace(orgFQDNStr, 
`https://`, `http://`, -1)
-                       }
-                       orgURI, orgWarns, err := getOriginURI(orgFQDNStr)
-                       warnings = append(warnings, orgWarns...)
-                       if err != nil {
-                               warnings = append(warnings, "DS '"+*ds.XMLID+"' 
had malformed origin  URI: '"+*ds.OrgServerFQDN+"': skipping!"+err.Error())
-                               continue
-                       }
+                               pasvc := &ParentAbstractionService{}
+                               pasvc.Name = *ds.XMLID
 
-                       text.Comment = makeParentComment(opt.AddComments, 
*ds.XMLID, "")
+                               // peering ring check
+                               if dsParams.UsePeering {
+                                       secondaryMode = 
ParentAbstractionServiceParentSecondaryModePeering
+                               }
 
-                       // TODO encode this in a DSType func, IsGoDirect() ?
-                       if *ds.Type == tc.DSTypeHTTPNoCache || *ds.Type == 
tc.DSTypeHTTPLive || *ds.Type == tc.DSTypeDNSLive {
-                               text.DestDomain = orgURI.Hostname()
-                               text.Port, err = strconv.Atoi(orgURI.Port())
+                               orgFQDNStr := *ds.OrgServerFQDN
+                               // if this cache isn't the last tier, i.e. 
we're not going to the origin, use http not https
+                               if !isLastCacheTier {
+                                       orgFQDNStr = 
strings.Replace(orgFQDNStr, `https://`, `http://`, -1)
+                               }
+                               orgURI, orgWarns, err := 
getOriginURI(orgFQDNStr)
+                               warnings = append(warnings, orgWarns...)
                                if err != nil {
-                                       if strings.ToLower(orgURI.Scheme) == 
"https" {
-                                               text.Port = 443
-                                       } else {
-                                               text.Port = 80
-                                       }
-                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(text.Port)+"! : "+err.Error())
+                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  URI: '"+*ds.OrgServerFQDN+"': 
skipping!"+err.Error())
+                                       continue
                                }
-                               text.GoDirect = true
-
-                               text.Parents = 
[]*ParentAbstractionServiceParent{&ParentAbstractionServiceParent{
-                                       FQDN:   text.DestDomain,
-                                       Port:   text.Port,
-                                       Weight: 1.0,
-                               }}
 
-                               // text += `dest_domain=` + orgURI.Hostname() + 
` port=` + orgURI.Port() + ` go_direct=true` + "\n"
+                               pasvc.Comment = 
makeParentComment(opt.AddComments, *ds.XMLID, "")
 
-                       } else {
-                               // check for profile psel.qstring_handling.  If 
this parameter is assigned to the server profile,
-                               // then edges will use the qstring handling 
value specified in the parameter for all profiles.
-
-                               // If there is no defined parameter in the 
profile, then check the delivery service profile.
-                               // If psel.qstring_handling exists in the DS 
profile, then we use that value for the specified DS only.
-                               // This is used only if not overridden by a 
server profile qstring handling parameter.
-
-                               // TODO refactor this logic, hard to understand 
(transliterated from Perl)
-                               dsQSH := queryStringHandling
-                               if dsQSH == nil {
-                                       dsQSH = 
ParentSelectParamQStringHandlingToBool(dsParams.QueryStringHandling)
-                                       if dsQSH == nil && 
dsParams.QueryStringHandling != "" {
-                                               warnings = append(warnings, 
"Delivery Service parameter '"+ParentConfigParamQStringHandling+"' value 
'"+dsParams.QueryStringHandling+"' malformed, not using!")
+                               // TODO encode this in a DSType func, 
IsGoDirect() ?
+                               if *ds.Type == tc.DSTypeHTTPNoCache || *ds.Type 
== tc.DSTypeHTTPLive || *ds.Type == tc.DSTypeDNSLive {
+                                       pasvc.DestDomain = orgURI.Hostname()
+                                       pasvc.Port, err = 
strconv.Atoi(orgURI.Port())
+                                       if err != nil {
+                                               if 
strings.ToLower(orgURI.Scheme) == "https" {
+                                                       pasvc.Port = 443
+                                               } else {
+                                                       pasvc.Port = 80
+                                               }
+                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(pasvc.Port)+"! : "+err.Error())
                                        }
 
-                               }
-                               parentQStr := dsQSH
-                               if parentQStr == nil {
-                                       v := false
-                                       parentQStr = &v
-                               }
-                               if ds.QStringIgnore != nil && 
tc.QStringIgnore(*ds.QStringIgnore) == tc.QStringIgnoreUseInCacheKeyAndPassUp 
&& dsQSH == nil {
-                                       v := true
-                                       parentQStr = &v
-                               }
-                               if parentQStr == nil {
-                                       b := 
!DefaultIgnoreQueryStringInParentSelection
-                                       parentQStr = &b
-                               }
+                                       pasvc.GoDirect = true
 
-                               text.DestDomain = orgURI.Hostname()
-                               text.Port, err = strconv.Atoi(orgURI.Port())
-                               if err != nil {
-                                       if strings.ToLower(orgURI.Scheme) == 
"https" {
-                                               text.Port = 443
-                                       } else {
-                                               text.Port = 80
+                                       pasvc.Parents = 
[]*ParentAbstractionServiceParent{&ParentAbstractionServiceParent{
+                                               FQDN:   pasvc.DestDomain,
+                                               Port:   pasvc.Port,
+                                               Weight: 1.0,
+                                       }}
+
+                                       // text += `dest_domain=` + 
orgURI.Hostname() + ` port=` + orgURI.Port() + ` go_direct=true` + "\n"
+                               } else {
+
+                                       // check for profile 
psel.qstring_handling.  If this parameter is assigned to the server profile,
+                                       // then edges will use the qstring 
handling value specified in the parameter for all profiles.
+
+                                       // If there is no defined parameter in 
the profile, then check the delivery service profile.
+                                       // If psel.qstring_handling exists in 
the DS profile, then we use that value for the specified DS only.
+                                       // This is used only if not overridden 
by a server profile qstring handling parameter.
+
+                                       // TODO refactor this logic, hard to 
understand (transliterated from Perl)
+                                       dsQSH := queryStringHandling
+                                       if dsQSH == nil {
+                                               dsQSH = 
ParentSelectParamQStringHandlingToBool(dsParams.QueryStringHandling)
+                                               if dsQSH == nil && 
dsParams.QueryStringHandling != "" {
+                                                       warnings = 
append(warnings, "Delivery Service parameter 
'"+ParentConfigParamQStringHandling+"' value '"+dsParams.QueryStringHandling+"' 
malformed, not using!")
+                                               }
+
+                                       }
+                                       parentQStr := dsQSH
+                                       if parentQStr == nil {
+                                               v := false
+                                               parentQStr = &v
+                                       }
+                                       if ds.QStringIgnore != nil && 
tc.QStringIgnore(*ds.QStringIgnore) == tc.QStringIgnoreUseInCacheKeyAndPassUp 
&& dsQSH == nil {
+                                               v := true
+                                               parentQStr = &v
+                                       }
+                                       if parentQStr == nil {
+                                               b := 
!DefaultIgnoreQueryStringInParentSelection
+                                               parentQStr = &b
                                        }
-                                       warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(text.Port)+"! : "+err.Error())
+
+                                       pasvc.DestDomain = orgURI.Hostname()
+                                       pasvc.Port, err = 
strconv.Atoi(orgURI.Port())
+                                       if err != nil {
+                                               if 
strings.ToLower(orgURI.Scheme) == "https" {
+                                                       pasvc.Port = 443
+                                               } else {
+                                                       pasvc.Port = 80
+                                               }
+                                               warnings = append(warnings, "DS 
'"+*ds.XMLID+"' had malformed origin  port: '"+orgURI.Port()+"': using 
"+strconv.Itoa(pasvc.Port)+"! : "+err.Error())
+                                       }
+                                       pasvc.Parents = parents
+                                       pasvc.SecondaryParents = 
secondaryParents
+                                       pasvc.SecondaryMode = secondaryMode
+                                       pasvc.RetryPolicy = roundRobin
+                                       pasvc.GoDirect = goDirect
+                                       
pasvc.IgnoreQueryStringInParentSelection = !*parentQStr
+                                       // text += `dest_domain=` + 
orgURI.Hostname() + ` port=` + orgURI.Port() + ` ` + parents + ` ` + 
secondaryParents + ` ` + roundRobin + ` ` + goDirect + ` qstring=` + parentQStr 
+ "\n"
                                }
-                               text.Parents = parents
-                               text.SecondaryParents = secondaryParents
-                               text.SecondaryMode = secondaryMode
-                               text.RetryPolicy = roundRobin
-                               text.GoDirect = goDirect
-                               text.IgnoreQueryStringInParentSelection = 
!*parentQStr
-                               // text += `dest_domain=` + orgURI.Hostname() + 
` port=` + orgURI.Port() + ` ` + parents + ` ` + secondaryParents + ` ` + 
roundRobin + ` ` + goDirect + ` qstring=` + parentQStr + "\n"
+
+                               prWarns := 
dsParams.FillParentSvcRetries(cacheIsTopLevel, atsMajorVer, pasvc)
+                               warnings = append(warnings, prWarns...)
+
+                               parentAbstraction.Services = 
append(parentAbstraction.Services, pasvc)
                        }
-                       parentAbstraction.Services = 
append(parentAbstraction.Services, text)
                }
        }
 
@@ -612,7 +647,8 @@ func makeParentDotConfigData(
                defaultDestText.DestDomain = `.`
                defaultDestText.Parents = parents
                // defaultDestText = `dest_domain=. ` + parents
-               if serverParams[ParentConfigParamAlgorithm] == 
tc.AlgorithmConsistentHash {
+
+               if serverParams[ParentConfigRetryKeysDefault.Algorithm] == 
tc.AlgorithmConsistentHash {
                        defaultDestText.SecondaryParents = secondaryParents
                        defaultDestText.SecondaryMode = secondaryMode
                        // defaultDestText += secondaryParents
@@ -802,32 +838,101 @@ type originURI struct {
 const deliveryServicesAllParentsKey = "all_parents"
 
 type parentDSParams struct {
-       Algorithm                       ParentAbstractionServiceRetryPolicy
+       Algorithm           ParentAbstractionServiceRetryPolicy
+       QueryStringHandling string
+
+       HasRetryParams                  bool
        ParentRetry                     string
-       UnavailableServerRetryResponses string
        MaxSimpleRetries                string
        MaxUnavailableServerRetries     string
-       QueryStringHandling             string
+       SimpleServerRetryResponses      string
+       UnavailableServerRetryResponses string
        TryAllPrimariesBeforeSecondary  bool
-       UsePeering                      bool
-       MergeGroups                     []string
+
+       UsePeering  bool
+       MergeGroups []string
+}
+
+// FillParentRetries populates the parentDSParams retries values from the ds 
parameters map for given ds parameter keys.
+// Returns if any params found and any warnings.
+func (dsp *parentDSParams) FillParentRetries(keys ParentConfigRetryKeys, 
dsParams map[string]string, dsid string) (bool, []string) {
+       var warnings []string
+       hasValues := false
+
+       if v, ok := dsParams[keys.Algorithm]; ok && strings.TrimSpace(v) != "" {
+               policy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(v)
+               if policy != ParentAbstractionServiceRetryPolicyInvalid {
+                       dsp.Algorithm = policy
+                       hasValues = true
+               } else {
+                       warnings = append(warnings, "DS '"+dsid+"' had 
malformed "+keys.Algorithm+" parameter '"+v+"', not using!")
+               }
+       }
+
+       if v, ok := dsParams[keys.ParentRetry]; ok {
+               dsp.ParentRetry = v
+               hasValues = true
+       }
+
+       if v, ok := dsParams[keys.MaxSimpleRetries]; ok {
+               dsp.MaxSimpleRetries = v
+               hasValues = true
+       }
+       if v, ok := dsParams[keys.MaxUnavailableRetries]; ok {
+               dsp.MaxUnavailableServerRetries = v
+               hasValues = true
+       }
+
+       if v, ok := dsParams[keys.SimpleRetryResponses]; ok {
+               if unavailableServerRetryResponsesValid(v) {
+                       dsp.SimpleServerRetryResponses = v
+                       hasValues = true
+               } else {
+                       warnings = append(warnings, "DS '"+dsid+"' had 
malformed "+keys.SimpleRetryResponses+" parameter '"+v+"', not using!")
+               }
+       }
+
+       if v, ok := dsParams[keys.UnavailableRetryResponses]; ok {
+               if unavailableServerRetryResponsesValid(v) {
+                       dsp.UnavailableServerRetryResponses = v
+                       hasValues = true
+               } else {
+                       warnings = append(warnings, "DS '"+dsid+"' had 
malformed "+keys.UnavailableRetryResponses+" parameter '"+v+"', not using!")
+               }
+       }
+
+       if v, ok := dsParams[keys.SecondaryMode]; ok {
+               if v == "false" {
+                       dsp.TryAllPrimariesBeforeSecondary = false
+               } else {
+                       dsp.TryAllPrimariesBeforeSecondary = true
+                       if v != "" {
+                               warnings = append(warnings, "DS '"+dsid+"' had 
Parameter "+keys.SecondaryMode+" which is used if it exists, the value is 
ignored (unless false) ! Non-empty value '"+v+"' will be ignored!")
+                       }
+               }
+       }
+
+       return hasValues, warnings
 }
 
 // getDSParams returns the Delivery Service Profile Parameters used in 
parent.config, and any warnings.
 // If Parameters don't exist, defaults are returned. Non-MSO Delivery Services 
default to no custom retry logic (we should reevaluate that).
 // Note these Parameters are only used for MSO for legacy 
DeliveryServiceServers DeliveryServices.
 //      Topology DSes use them for all DSes, MSO and non-MSO.
-func getParentDSParams(ds DeliveryService, profileParentConfigParams 
map[string]map[string]string) (parentDSParams, []string) {
+func getParentDSParams(ds DeliveryService, profileParentConfigParams 
map[string]map[string]string, serverPlacement TopologyPlacement, isMSO bool) 
(parentDSParams, []string) {
        warnings := []string{}
        params := parentDSParams{}
-       isMSO := ds.MultiSiteOrigin != nil && *ds.MultiSiteOrigin
-       if isMSO {
+
+       // Default values for origin facing MSO tier
+       if serverPlacement.IsLastCacheTier && isMSO {
                params.Algorithm = ParentConfigDSParamDefaultMSOAlgorithm
                params.ParentRetry = ParentConfigDSParamDefaultMSOParentRetry
                params.UnavailableServerRetryResponses = 
ParentConfigDSParamDefaultMSOUnavailableServerRetryResponses
                params.MaxSimpleRetries = 
strconv.Itoa(ParentConfigDSParamDefaultMaxSimpleRetries)
                params.MaxUnavailableServerRetries = 
strconv.Itoa(ParentConfigDSParamDefaultMaxUnavailableServerRetries)
+               params.HasRetryParams = true
        }
+
        if ds.ProfileName == nil || *ds.ProfileName == "" {
                return params, warnings
        }
@@ -840,68 +945,42 @@ func getParentDSParams(ds DeliveryService, 
profileParentConfigParams map[string]
                        params.UsePeering = true
                }
        }
+
        // the following may be blank, no default
        params.QueryStringHandling = dsParams[ParentConfigParamQStringHandling]
        params.MergeGroups = 
strings.Split(dsParams[ParentConfigParamMergeGroups], " ")
 
-       // TODO deprecate & remove "mso." Parameters - there was never a reason 
to restrict these settings to MSO.
-       if isMSO {
-               if v, ok := dsParams[ParentConfigParamMSOAlgorithm]; ok && 
strings.TrimSpace(v) != "" {
-                       policy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(v)
-                       if policy != ParentAbstractionServiceRetryPolicyInvalid 
{
-                               params.Algorithm = policy
-                       } else {
-                               warnings = append(warnings, "DS '"+*ds.XMLID+"' 
had malformed "+ParentConfigParamMSOAlgorithm+" parameter '"+v+"', not using!")
-                       }
-               }
-               if v, ok := dsParams[ParentConfigParamMSOParentRetry]; ok {
-                       params.ParentRetry = v
-               }
-               if v, ok := 
dsParams[ParentConfigParamMSOUnavailableServerRetryResponses]; ok {
-                       if v != "" && !unavailableServerRetryResponsesValid(v) {
-                               warnings = append(warnings, "DS '"+*ds.XMLID+"' 
had malformed "+ParentConfigParamMSOUnavailableServerRetryResponses+" parameter 
'"+v+"', not using!")
-                       } else if v != "" {
-                               params.UnavailableServerRetryResponses = v
-                       }
+       // progressively fill in the params
+       if serverPlacement.IsLastCacheTier {
+               // mso. prefix lowest priority
+               if isMSO {
+                       hasVals, warns := 
params.FillParentRetries(ParentConfigRetryKeysMSO, dsParams, *ds.XMLID)
+                       params.HasRetryParams = params.HasRetryParams || hasVals
+                       warnings = append(warnings, warns...)
                }
-               if v, ok := dsParams[ParentConfigParamMSOMaxSimpleRetries]; ok {
-                       params.MaxSimpleRetries = v
-               }
-               if v, ok := 
dsParams[ParentConfigParamMSOMaxUnavailableServerRetries]; ok {
-                       params.MaxUnavailableServerRetries = v
-               }
-       }
 
-       // Even if the DS is MSO, non-"mso." Parameters override "mso." ones, 
because they're newer.
-       if v, ok := dsParams[ParentConfigParamAlgorithm]; ok && 
strings.TrimSpace(v) != "" {
-               policy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(v)
-               if policy != ParentAbstractionServiceRetryPolicyInvalid {
-                       params.Algorithm = policy
-               } else {
-                       warnings = append(warnings, "DS '"+*ds.XMLID+"' had 
malformed "+ParentConfigParamAlgorithm+" parameter '"+v+"', not using!")
-               }
-       }
-       if v, ok := dsParams[ParentConfigParamParentRetry]; ok {
-               params.ParentRetry = v
-       }
-       if v, ok := dsParams[ParentConfigParamUnavailableServerRetryResponses]; 
ok {
-               if v != "" && !unavailableServerRetryResponsesValid(v) {
-                       warnings = append(warnings, "DS '"+*ds.XMLID+"' had 
malformed "+ParentConfigParamUnavailableServerRetryResponses+" parameter 
'"+v+"', not using!")
-               } else if v != "" {
-                       params.UnavailableServerRetryResponses = v
-               }
-       }
-       if v, ok := dsParams[ParentConfigParamMaxSimpleRetries]; ok {
-               params.MaxSimpleRetries = v
-       }
-       if v, ok := dsParams[ParentConfigParamMaxUnavailableServerRetries]; ok {
-               params.MaxUnavailableServerRetries = v
-       }
-       if v, ok := dsParams[ParentConfigParamSecondaryMode]; ok {
-               if v != "" {
-                       warnings = append(warnings, "DS '"+*ds.XMLID+"' had 
Parameter "+ParentConfigParamSecondaryMode+" which is used if it exists, the 
value is ignored! Non-empty value '"+v+"' will be ignored!")
-               }
-               params.TryAllPrimariesBeforeSecondary = true
+               // no prefix next priority
+               hasVals, warns := 
params.FillParentRetries(ParentConfigRetryKeysDefault, dsParams, *ds.XMLID)
+               warnings = append(warnings, warns...)
+               params.HasRetryParams = params.HasRetryParams || hasVals
+
+               // last. prefix highest priority
+               hasVals, warns = 
params.FillParentRetries(ParentConfigRetryKeysLast, dsParams, *ds.XMLID)
+               warnings = append(warnings, warns...)
+               params.HasRetryParams = params.HasRetryParams || hasVals
+
+       } else if serverPlacement.IsInnerCacheTier {
+               hasVals, warns := 
params.FillParentRetries(ParentConfigRetryKeysInner, dsParams, *ds.XMLID)
+               warnings = append(warnings, warns...)
+
+               // Normal inner behavior has no parent retry strings
+               params.HasRetryParams = hasVals
+       } else { // if serverPlacement.IsFirstCacheTier {
+               hasVals, warns := 
params.FillParentRetries(ParentConfigRetryKeysFirst, dsParams, *ds.XMLID)
+               warnings = append(warnings, warns...)
+
+               // Normal first behavior has no parent retry strings
+               params.HasRetryParams = hasVals
        }
 
        return params, warnings
@@ -919,7 +998,8 @@ func getTopologyParentConfigLine(
        serverCapabilities map[int]map[ServerCapability]struct{},
        dsRequiredCapabilities map[int]map[ServerCapability]struct{},
        cacheGroups map[tc.CacheGroupName]tc.CacheGroupNullable,
-       dsParams parentDSParams,
+       profileParentConfigParams map[string]map[string]string,
+       isMSO bool,
        atsMajorVer int,
        dsOrigins map[ServerID]struct{},
        addComments bool,
@@ -935,14 +1015,17 @@ func getTopologyParentConfigLine(
        }
 
        serverPlacement, err := 
getTopologyPlacement(tc.CacheGroupName(*server.Cachegroup), topology, 
cacheGroups, ds)
-
        if err != nil {
                return nil, warnings, errors.New("getting topology placement: " 
+ err.Error())
        }
+
        if !serverPlacement.InTopology {
                return nil, warnings, nil // server isn't in topology, no error
        }
 
+       dsParams, dswarns := getParentDSParams(*ds, profileParentConfigParams, 
serverPlacement, isMSO)
+       warnings = append(warnings, dswarns...)
+
        orgFQDNStr := *ds.OrgServerFQDN
        // if this cache isn't the last tier, i.e. we're not going to the 
origin, use http not https
        if !serverPlacement.IsLastCacheTier {
@@ -954,11 +1037,11 @@ func getTopologyParentConfigLine(
                return nil, warnings, errors.New("DS '" + *ds.XMLID + "' has 
malformed origin URI: '" + *ds.OrgServerFQDN + "': skipping!" + err.Error())
        }
 
-       txt := &ParentAbstractionService{}
-       txt.Name = *ds.XMLID
-       txt.Comment = makeParentComment(addComments, *ds.XMLID, *ds.Topology)
-       txt.DestDomain = orgURI.Hostname()
-       txt.Port, err = strconv.Atoi(orgURI.Port())
+       pasvc := &ParentAbstractionService{}
+       pasvc.Name = *ds.XMLID
+       pasvc.Comment = makeParentComment(addComments, *ds.XMLID, *ds.Topology)
+       pasvc.DestDomain = orgURI.Hostname()
+       pasvc.Port, err = strconv.Atoi(orgURI.Port())
        if err != nil {
                return nil, warnings, fmt.Errorf("parent %v port '%v' was not 
an integer", orgURI, orgURI.Port())
        }
@@ -966,6 +1049,7 @@ func getTopologyParentConfigLine(
 
        parents, secondaryParents, parentWarnings, err := 
getTopologyParents(server, ds, serversWithParams, parentConfigParams, topology, 
serverPlacement.IsLastTier, serverCapabilities, dsRequiredCapabilities, 
dsOrigins, dsParams.MergeGroups)
        warnings = append(warnings, parentWarnings...)
+
        if err != nil {
                return nil, warnings, errors.New("getting topology parents for 
'" + *ds.XMLID + "': skipping! " + err.Error())
        }
@@ -979,22 +1063,22 @@ func getTopologyParentConfigLine(
                }
        }
 
-       txt.Parents = parents
+       pasvc.Parents = parents
        // txt += ` parent="` + strings.Join(parents, `;`) + `"`
        if len(secondaryParents) > 0 {
-               txt.SecondaryParents = secondaryParents
+               pasvc.SecondaryParents = secondaryParents
                // txt += ` secondary_parent="` + 
strings.Join(secondaryParents, `;`) + `"`
 
                secondaryModeStr, secondaryModeWarnings := 
getSecondaryModeStr(dsParams.TryAllPrimariesBeforeSecondary, atsMajorVer, 
tc.DeliveryServiceName(*ds.XMLID))
                warnings = append(warnings, secondaryModeWarnings...)
                // txt += secondaryModeStr
-               txt.SecondaryMode = secondaryModeStr // TODO convert
+               pasvc.SecondaryMode = secondaryModeStr // TODO convert
        }
 
-       txt.RetryPolicy = getTopologyRoundRobin(ds, serverParams, 
serverPlacement.IsLastCacheTier, dsParams.Algorithm)
+       pasvc.RetryPolicy = getTopologyRoundRobin(ds, serverParams, 
serverPlacement.IsLastCacheTier, dsParams.Algorithm)
        // txt += ` round_robin=` + getTopologyRoundRobin(ds, serverParams, 
serverPlacement.IsLastCacheTier, dsParams.Algorithm)
 
-       txt.GoDirect = getTopologyGoDirect(ds, serverPlacement.IsLastTier, 
serverPlacement.IsLastCacheTier)
+       pasvc.GoDirect = getTopologyGoDirect(ds, serverPlacement)
        // txt += ` go_direct=` + getTopologyGoDirect(ds, 
serverPlacement.IsLastTier)
 
        // TODO convert
@@ -1009,7 +1093,7 @@ func getTopologyParentConfigLine(
        }
 
        tqWarns := []string{}
-       txt.IgnoreQueryStringInParentSelection, tqWarns = 
getTopologyQueryStringIgnore(ds, serverParams, serverPlacement.IsLastCacheTier, 
dsParams.Algorithm, useQueryStringInParentSelection)
+       pasvc.IgnoreQueryStringInParentSelection, tqWarns = 
getTopologyQueryStringIgnore(ds, serverParams, serverPlacement, 
dsParams.Algorithm, useQueryStringInParentSelection)
        warnings = append(warnings, tqWarns...)
 
        // txt += ` qstring=` + getTopologyQueryString(ds, serverParams, 
serverPlacement.IsLastCacheTier, dsParams.Algorithm, 
dsParams.QueryStringHandling)
@@ -1018,23 +1102,22 @@ func getTopologyParentConfigLine(
        // txt += getTopologyParentIsProxyStr(serverPlacement.IsLastCacheTier)
 
        // TODO convert
-       prWarns := []string{}
-       txt.MaxSimpleRetries, txt.MaxMarkdownRetries, 
txt.MarkdownResponseCodes, txt.ErrorResponseCodes, prWarns = 
getParentRetryStr(serverPlacement.IsLastCacheTier, atsMajorVer, 
dsParams.ParentRetry, dsParams.UnavailableServerRetryResponses, 
dsParams.MaxSimpleRetries, dsParams.MaxUnavailableServerRetries)
+       prWarns := 
dsParams.FillParentSvcRetries(serverPlacement.IsLastCacheTier, atsMajorVer, 
pasvc)
        warnings = append(warnings, prWarns...)
 
        // txt += getParentRetryStr(serverPlacement.IsLastCacheTier, 
atsMajorVer, dsParams.ParentRetry, dsParams.UnavailableServerRetryResponses, 
dsParams.MaxSimpleRetries, dsParams.MaxUnavailableServerRetries)
        // txt += "\n"
 
        if dsParams.UsePeering {
-               txt.SecondaryMode = 
ParentAbstractionServiceParentSecondaryModePeering
+               pasvc.SecondaryMode = 
ParentAbstractionServiceParentSecondaryModePeering
        }
 
-       return txt, warnings, nil
+       return pasvc, warnings, nil
 }
 
-// getParentRetryStr builds the parent retry directive(s).
+// getParentRetries builds the parent retry directive(s).
 //
-// Returns the MaxSimpleRetries, MaxMarkdownRetries, MarkdownResponseCodes, 
and ErrorResponseCodes.
+// Returns the MaxSimpleRetries, MaxMarkdownRetries, ErrorREsponseCodes, 
MarkdownResponseCodes.
 //
 // If atsMajorVer < 6, "" is returned (ATS 5 and below don't support retry 
directives).
 // If isLastCacheTier is false, "" is returned. This argument exists to 
simplify usage.
@@ -1045,71 +1128,77 @@ func getTopologyParentConfigLine(
 //
 // Does not return errors. If any input is malformed, warnings are returned 
and that value is set to -1.
 //
-func getParentRetryStr(isLastCacheTier bool, atsMajorVer int, parentRetry 
string, unavailableServerRetryResponses string, maxSimpleRetries string, 
maxUnavailableServerRetries string) (int, int, []int, []int, []string) {
+func (dsparams parentDSParams) FillParentSvcRetries(isLastCacheTier bool, 
atsMajorVer int, pasvc *ParentAbstractionService) []string {
        warnings := []string{}
-       if !isLastCacheTier || // allow !isLastCacheTier, to simplify usage.
-               parentRetry == "" || // allow parentRetry to be empty, to 
simplify usage.
+
+       if !dsparams.HasRetryParams || // allow parentRetry to be empty, to 
simplify usage.
                atsMajorVer < 6 { // ATS 5 and below don't support parent_retry 
directives
                // warnings = append(warnings, "ATS 5 doesn't support parent 
retry, not using parent retry values")
-               return -1, -1, nil, nil, warnings // TODO move to formatter?
+               pasvc.MaxSimpleRetries = -1
+               pasvc.MaxMarkdownRetries = -1
+               pasvc.ErrorResponseCodes = nil
+               pasvc.MarkdownResponseCodes = nil
+               return warnings
        }
 
-       err := error(nil)
+       // Set initial defaults
+       pasvc.MaxSimpleRetries = 0
+       pasvc.MaxMarkdownRetries = 0
+       pasvc.ErrorResponseCodes = []int{}
+       pasvc.MarkdownResponseCodes = []int{}
 
-       maxSimpleRetriesInt := ParentConfigDSParamDefaultMaxSimpleRetries
-       if maxSimpleRetries != "" {
-               maxSimpleRetriesInt, err = strconv.Atoi(maxSimpleRetries)
-               if err != nil {
-                       maxSimpleRetriesInt = 
ParentConfigDSParamDefaultMaxSimpleRetries
-                       warnings = append(warnings, "malformed maxSimpleRetries 
'"+maxSimpleRetries+"', using default "+strconv.Itoa(maxSimpleRetriesInt))
+       if isLastCacheTier {
+               pasvc.MaxSimpleRetries = 
ParentConfigDSParamDefaultMaxSimpleRetries
+               pasvc.MaxMarkdownRetries = 
ParentConfigDSParamDefaultMaxUnavailableServerRetries
+       }
+
+       if dsparams.MaxSimpleRetries != "" {
+               if retriesint, err := strconv.Atoi(dsparams.MaxSimpleRetries); 
err == nil {
+                       pasvc.MaxSimpleRetries = retriesint
+               } else {
+                       warnings = append(warnings, "malformed maxSimpleRetries 
'"+dsparams.MaxSimpleRetries+"', using default 
"+strconv.Itoa(pasvc.MaxSimpleRetries))
                }
        }
 
-       maxUnavailableServerRetriesInt := 
ParentConfigDSParamDefaultMaxUnavailableServerRetries
-       if maxUnavailableServerRetries != "" {
-               maxUnavailableServerRetriesInt, err = 
strconv.Atoi(maxUnavailableServerRetries)
-               if err != nil {
-                       maxUnavailableServerRetriesInt = 
ParentConfigDSParamDefaultMaxUnavailableServerRetries
-                       warnings = append(warnings, "malformed 
maxUnavailableServerRetries '"+maxUnavailableServerRetries+"', using default 
"+strconv.Itoa(maxUnavailableServerRetriesInt))
+       if dsparams.MaxUnavailableServerRetries != "" {
+               if retriesint, err := 
strconv.Atoi(dsparams.MaxUnavailableServerRetries); err == nil {
+                       pasvc.MaxMarkdownRetries = retriesint
+               } else {
+                       warnings = append(warnings, "malformed 
maxUnavailableServerRetries '"+dsparams.MaxUnavailableServerRetries+"', using 
default "+strconv.Itoa(pasvc.MaxMarkdownRetries))
                }
        }
 
-       unavailableServerRetryResponsesInts, err := 
ParseRetryResponses(unavailableServerRetryResponses)
-       if err != nil {
-               warnings = append(warnings, "malformed 
unavailableServerRetryResponses '"+unavailableServerRetryResponses+"', using 
default")
-               unavailableServerRetryResponsesInts = []int{}
+       // simple retry responses only supported int ATS for 9.1.x and above
+       if simpleResponseInts, err := 
ParseRetryResponses(dsparams.SimpleServerRetryResponses); err == nil {
+               pasvc.ErrorResponseCodes = simpleResponseInts
+       } else {
+               warnings = append(warnings, "malformed 
simpleServerRetryResponses '"+dsparams.SimpleServerRetryResponses+"', using 
default")
        }
 
-       simpleRetryResponsesInts := []int{}
-       // TODO add support for 9.1
-       // simpleRetryResponsesInts, err := 
ParseRetryResponses(simpleRetryResponses)
-       // if err != nil {
-       //      warnings = append(warnings, "malformed simpleRetryResponses 
'"+simpleRetryResponses+"', using default")
-       //      simpleRetryResponsesInts = []int{}
-       // }
+       if unavailResponseInts, err := 
ParseRetryResponses(dsparams.UnavailableServerRetryResponses); err == nil {
+               pasvc.MarkdownResponseCodes = unavailResponseInts
+       } else {
+               warnings = append(warnings, "malformed 
unavailableServerRetryResponses '"+dsparams.UnavailableServerRetryResponses+"', 
using default")
+       }
 
        // TODO make consts
-       switch strings.ToLower(strings.TrimSpace(parentRetry)) {
+       switch strings.ToLower(strings.TrimSpace(dsparams.ParentRetry)) {
        case "simple_retry":
-               unavailableServerRetryResponsesInts = []int{}
-               if len(simpleRetryResponsesInts) == 0 {
-                       simpleRetryResponsesInts = 
append(simpleRetryResponsesInts, DefaultSimpleRetryCodes...)
+               if len(pasvc.ErrorResponseCodes) == 0 {
+                       pasvc.ErrorResponseCodes = DefaultSimpleRetryCodes
                }
        case "unavailable_server_retry":
-               simpleRetryResponsesInts = []int{}
-               if len(unavailableServerRetryResponsesInts) == 0 {
-                       unavailableServerRetryResponsesInts = 
append(unavailableServerRetryResponsesInts, 
DefaultUnavailableServerRetryCodes...)
+               if len(pasvc.MarkdownResponseCodes) == 0 {
+                       pasvc.MarkdownResponseCodes = 
DefaultUnavailableServerRetryCodes
                }
        case "both":
-               if len(unavailableServerRetryResponsesInts) == 0 {
-                       unavailableServerRetryResponsesInts = 
append(unavailableServerRetryResponsesInts, 
DefaultUnavailableServerRetryCodes...)
+               if len(pasvc.ErrorResponseCodes) == 0 {
+                       pasvc.ErrorResponseCodes = DefaultSimpleRetryCodes
                }
-               if len(simpleRetryResponsesInts) == 0 {
-                       simpleRetryResponsesInts = 
append(simpleRetryResponsesInts, DefaultSimpleRetryCodes...)
+               if len(pasvc.MarkdownResponseCodes) == 0 {
+                       pasvc.MarkdownResponseCodes = 
DefaultUnavailableServerRetryCodes
                }
        default:
-               unavailableServerRetryResponsesInts = []int{}
-               simpleRetryResponsesInts = []int{}
        }
 
        // txt := ` parent_retry=` + parentRetry
@@ -1117,7 +1206,7 @@ func getParentRetryStr(isLastCacheTier bool, atsMajorVer 
int, parentRetry string
        //      txt += ` unavailable_server_retry_responses=` + 
unavailableServerRetryResponses
        // }
        // txt += ` max_simple_retries=` + maxSimpleRetries + ` 
max_unavailable_server_retries=` + maxUnavailableServerRetries
-       return maxSimpleRetriesInt, maxUnavailableServerRetriesInt, 
unavailableServerRetryResponsesInts, simpleRetryResponsesInts, warnings
+       return warnings
 }
 
 // getSecondaryModeStr returns the secondary_mode string, and any warnings.
@@ -1127,7 +1216,7 @@ func getSecondaryModeStr(tryAllPrimariesBeforeSecondary 
bool, atsMajorVer int, d
                return ParentAbstractionServiceParentSecondaryModeDefault, 
warnings
        }
        if atsMajorVer < 8 {
-               warnings = append(warnings, "DS '"+string(ds)+"' had Parameter 
"+ParentConfigParamSecondaryMode+" but this cache is 
"+strconv.Itoa(atsMajorVer)+" and secondary_mode isn't supported in ATS until 
8. Not using!")
+               warnings = append(warnings, "DS '"+string(ds)+"' had Parameter 
"+ParentConfigRetryKeysDefault.SecondaryMode+" but this cache is 
"+strconv.Itoa(atsMajorVer)+" and secondary_mode isn't supported in ATS until 
8. Not using!")
                return ParentAbstractionServiceParentSecondaryModeDefault, 
warnings
        }
 
@@ -1152,7 +1241,7 @@ func getTopologyRoundRobin(
        if !serverIsLastTier {
                return ParentAbstractionServiceRetryPolicyConsistentHash
        }
-       if parentSelectAlg := serverParams[ParentConfigParamAlgorithm]; 
ds.OriginShield != nil && *ds.OriginShield != "" && 
strings.TrimSpace(parentSelectAlg) != "" {
+       if parentSelectAlg := 
serverParams[ParentConfigRetryKeysDefault.Algorithm]; ds.OriginShield != nil && 
*ds.OriginShield != "" && strings.TrimSpace(parentSelectAlg) != "" {
                if policy := 
ParentSelectAlgorithmToParentAbstractionServiceRetryPolicy(parentSelectAlg); 
policy != ParentAbstractionServiceRetryPolicyInvalid {
                        return policy
                }
@@ -1163,17 +1252,14 @@ func getTopologyRoundRobin(
        return ParentAbstractionServiceRetryPolicyConsistentHash
 }
 
-func getTopologyGoDirect(ds *DeliveryService, serverIsLastTier bool, 
serverIsLastCacheTier bool) bool {
-       if serverIsLastCacheTier {
+func getTopologyGoDirect(ds *DeliveryService, serverPlacement 
TopologyPlacement) bool {
+       if serverPlacement.IsLastCacheTier {
                return true
-       }
-       if !serverIsLastTier {
+       } else if !serverPlacement.IsLastTier {
                return false
-       }
-       if ds.OriginShield != nil && *ds.OriginShield != "" {
+       } else if ds.OriginShield != nil && *ds.OriginShield != "" {
                return true
-       }
-       if ds.MultiSiteOrigin != nil && *ds.MultiSiteOrigin {
+       } else if ds.MultiSiteOrigin != nil && *ds.MultiSiteOrigin {
                return false
        }
        return true
@@ -1182,12 +1268,12 @@ func getTopologyGoDirect(ds *DeliveryService, 
serverIsLastTier bool, serverIsLas
 func getTopologyQueryStringIgnore(
        ds *DeliveryService,
        serverParams map[string]string,
-       serverIsLastTier bool,
+       serverPlacement TopologyPlacement,
        algorithm ParentAbstractionServiceRetryPolicy,
        qStringHandling *bool,
 ) (bool, []string) {
        warnings := []string{}
-       if serverIsLastTier {
+       if serverPlacement.IsLastCacheTier {
                if ds.MultiSiteOrigin != nil && *ds.MultiSiteOrigin && 
qStringHandling == nil && algorithm == 
ParentAbstractionServiceRetryPolicyConsistentHash && ds.QStringIgnore != nil && 
tc.QStringIgnore(*ds.QStringIgnore) == tc.QStringIgnoreUseInCacheKeyAndPassUp {
                        return false, warnings
                }
@@ -1845,7 +1931,7 @@ func getServerParentConfigParams(server *Server, 
allParentConfigParams []paramet
                name := pa.Name
                val := pa.Value
                if name == ParentConfigParamQStringHandling ||
-                       name == ParentConfigParamAlgorithm ||
+                       name == ParentConfigRetryKeysDefault.Algorithm ||
                        name == ParentConfigParamQString {
                        serverParams[name] = val
                }
diff --git a/lib/go-atscfg/parentdotconfig_test.go 
b/lib/go-atscfg/parentdotconfig_test.go
index 239fed2b7a..25595c37c9 100644
--- a/lib/go-atscfg/parentdotconfig_test.go
+++ b/lib/go-atscfg/parentdotconfig_test.go
@@ -53,7 +53,7 @@ func TestMakeParentDotConfig(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -164,7 +164,7 @@ func TestMakeParentDotConfigCapabilities(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -308,7 +308,7 @@ func TestMakeParentDotConfigMSOSecondaryParent(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -402,6 +402,10 @@ func TestMakeParentDotConfigMSOSecondaryParent(t 
*testing.T) {
        if !strings.Contains(txtx, 
`secondary_parent="my-parent-1.my-parent-1-domain`) {
                t.Errorf("expected secondary parent 
'my-parent-1.my-parent-1-domain', actual: '%v'", txt)
        }
+
+       if strings.Contains(txtx, "parent_retry") {
+               t.Errorf("Did not expect parent_retry parameter at edge/inner: 
'%v'", txt)
+       }
 }
 
 func TestMakeParentDotConfigMSONoPrimaryParent(t *testing.T) {
@@ -423,7 +427,7 @@ func TestMakeParentDotConfigMSONoPrimaryParent(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -547,7 +551,7 @@ func TestMakeParentDotConfigTopologies(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -689,7 +693,7 @@ func TestMakeParentDotConfigNotInTopologies(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -829,7 +833,7 @@ func TestMakeParentDotConfigTopologiesCapabilities(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -977,7 +981,7 @@ func TestMakeParentDotConfigTopologiesOmitOfflineParents(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1120,7 +1124,7 @@ func 
TestMakeParentDotConfigTopologiesOmitDifferentCDNParents(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1254,7 +1258,7 @@ func TestMakeParentDotConfigTopologiesMSO(t *testing.T) {
 
        parentConfigParams := []tc.Parameter{
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1445,7 +1449,7 @@ func 
TestMakeParentDotConfigTopologiesMSOWithCapabilities(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1585,7 +1589,7 @@ func TestMakeParentDotConfigMSOWithCapabilities(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1715,7 +1719,7 @@ func TestMakeParentDotConfigTopologiesMSOParams(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1727,33 +1731,33 @@ func TestMakeParentDotConfigTopologiesMSOParams(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMSOAlgorithm,
+                       Name:       ParentConfigRetryKeysMSO.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      "consistent_hash",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMSOParentRetry,
+                       Name:       ParentConfigRetryKeysMSO.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMSOUnavailableServerRetryResponses,
+                       Name:       ParentConfigRetryKeysMSO.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMSOMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysMSO.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMSOMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysMSO.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
        }
@@ -1891,7 +1895,7 @@ func TestMakeParentDotConfigTopologiesParams(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1903,33 +1907,33 @@ func TestMakeParentDotConfigTopologiesParams(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      "consistent_hash",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamParentRetry,
+                       Name:       ParentConfigRetryKeysDefault.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamUnavailableServerRetryResponses,
+                       Name:       
ParentConfigRetryKeysDefault.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysDefault.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysDefault.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
        }
@@ -2070,7 +2074,7 @@ func 
TestMakeParentDotConfigTopologiesNonStandardServerTypes(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2218,7 +2222,7 @@ func TestMakeParentDotConfigSecondaryMode(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2230,7 +2234,13 @@ func TestMakeParentDotConfigSecondaryMode(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamSecondaryMode,
+                       Name:       ParentConfigRetryKeysDefault.SecondaryMode,
+                       ConfigFile: "parent.config",
+                       Value:      "",
+                       Profiles:   []byte(`["ds0Profile","ds1Profile"]`),
+               },
+               tc.Parameter{
+                       Name:       ParentConfigRetryKeysFirst.SecondaryMode,
                        ConfigFile: "parent.config",
                        Value:      "",
                        Profiles:   []byte(`["ds0Profile","ds1Profile"]`),
@@ -2379,7 +2389,7 @@ func TestMakeParentDotConfigNoSecondaryMode(t *testing.T) 
{
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2536,7 +2546,7 @@ func TestMakeParentDotConfigComments(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                {
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2662,7 +2672,7 @@ func TestMakeParentDotConfigCommentTopology(t *testing.T) 
{
                        Profiles:   []byte(`["serverprofile"]`),
                },
                {
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2816,7 +2826,7 @@ func TestMakeParentDotConfigHTTPSOrigin(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -2933,7 +2943,7 @@ func TestMakeParentDotConfigHTTPSOriginTopology(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                {
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -3067,7 +3077,7 @@ func TestMakeParentDotConfigNoParentNoTopology(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -3196,7 +3206,7 @@ func 
TestMakeParentDotConfigHTTPSOriginTopologyNoPrimaryParent(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                {
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -3334,7 +3344,7 @@ func TestMakeParentDotConfigMergeParentGroupTopology(t 
*testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                {
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -3543,7 +3553,7 @@ func 
TestMakeParentDotConfigTopologiesServerMultipleProfileParams(t *testing.T)
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -3555,33 +3565,33 @@ func 
TestMakeParentDotConfigTopologiesServerMultipleProfileParams(t *testing.T)
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      "consistent_hash",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamParentRetry,
+                       Name:       ParentConfigRetryKeysDefault.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamUnavailableServerRetryResponses,
+                       Name:       
ParentConfigRetryKeysDefault.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysDefault.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysDefault.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
@@ -3710,6 +3720,745 @@ func 
TestMakeParentDotConfigTopologiesServerMultipleProfileParams(t *testing.T)
        }
 }
 
+func TestMakeParentDotConfigFirstLastNoTopo(t *testing.T) {
+       hdr := &ParentConfigOpts{AddComments: true, HdrComment: 
"myHeaderComment"}
+
+       // Non Toplogy ds
+       ds0 := makeParentDS()
+       ds0Type := tc.DSTypeHTTP
+       ds0.Type = &ds0Type
+       ds0.QStringIgnore = 
util.IntPtr(int(tc.QStringIgnoreUseInCacheKeyAndPassUp))
+       ds0.OrgServerFQDN = util.StrPtr("https://ds0.example.net";)
+       ds0.ProfileID = util.IntPtr(311)
+       ds0.ProfileName = util.StrPtr("ds0Profile")
+
+       // Non Toplogy ds, MSO
+       ds1 := makeParentDS()
+       ds1Type := tc.DSTypeHTTP
+       ds1.Type = &ds1Type
+       ds1.QStringIgnore = 
util.IntPtr(int(tc.QStringIgnoreUseInCacheKeyAndPassUp))
+       ds1.OrgServerFQDN = util.StrPtr("https://ds1.example.net";)
+       ds1.ProfileID = util.IntPtr(312)
+       ds1.ProfileName = util.StrPtr("ds0Profile")
+       ds1.MultiSiteOrigin = util.BoolPtr(true)
+
+       dses := []DeliveryService{*ds0, *ds1}
+
+       parentConfigParams := []tc.Parameter{
+               {
+                       Name:       ParentConfigParamQStringHandling,
+                       ConfigFile: "parent.config",
+                       Value:      "myQStringHandlingParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
+                       ConfigFile: "parent.config",
+                       Value:      tc.AlgorithmConsistentHash,
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigParamQString,
+                       ConfigFile: "parent.config",
+                       Value:      "myQstringParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+       }
+
+       // Create set of DS params
+       params := map[string]string{
+               ParentConfigRetryKeysDefault.Algorithm: "strict",
+               ParentConfigRetryKeysMSO.Algorithm:     "strict",
+               ParentConfigRetryKeysFirst.Algorithm:   "true",
+               ParentConfigRetryKeysInner.Algorithm:   "latched",
+               ParentConfigRetryKeysLast.Algorithm:    "true",
+
+               ParentConfigRetryKeysDefault.SecondaryMode: "exhaust",
+               ParentConfigRetryKeysMSO.SecondaryMode:     "exhaust",
+               ParentConfigRetryKeysFirst.SecondaryMode:   "alternate",
+               ParentConfigRetryKeysInner.SecondaryMode:   "peering",
+               ParentConfigRetryKeysLast.SecondaryMode:    "alternate",
+
+               ParentConfigRetryKeysMSO.ParentRetry:   
"unavailable_server_retry",
+               ParentConfigRetryKeysFirst.ParentRetry: "both",
+               ParentConfigRetryKeysInner.ParentRetry: "both",
+               ParentConfigRetryKeysLast.ParentRetry:  "both",
+
+               ParentConfigRetryKeysDefault.MaxSimpleRetries: "11",
+               ParentConfigRetryKeysMSO.MaxSimpleRetries:     "11",
+               ParentConfigRetryKeysFirst.MaxSimpleRetries:   "12",
+               ParentConfigRetryKeysInner.MaxSimpleRetries:   "13",
+               ParentConfigRetryKeysLast.MaxSimpleRetries:    "14",
+
+               ParentConfigRetryKeysDefault.SimpleRetryResponses: `"401"`,
+               ParentConfigRetryKeysMSO.SimpleRetryResponses:     `"401"`,
+               ParentConfigRetryKeysFirst.SimpleRetryResponses:   `"401,402"`,
+               ParentConfigRetryKeysInner.SimpleRetryResponses:   `"401,403"`,
+               ParentConfigRetryKeysLast.SimpleRetryResponses:    `"401,404"`,
+
+               ParentConfigRetryKeysDefault.MaxUnavailableRetries: "21",
+               ParentConfigRetryKeysMSO.MaxUnavailableRetries:     "21",
+               ParentConfigRetryKeysFirst.MaxUnavailableRetries:   "22",
+               ParentConfigRetryKeysInner.MaxUnavailableRetries:   "23",
+               ParentConfigRetryKeysLast.MaxUnavailableRetries:    "24",
+
+               ParentConfigRetryKeysDefault.UnavailableRetryResponses: `"501"`,
+               ParentConfigRetryKeysMSO.UnavailableRetryResponses:     `"501"`,
+               ParentConfigRetryKeysFirst.UnavailableRetryResponses:   
`"501,502"`,
+               ParentConfigRetryKeysInner.UnavailableRetryResponses:   
`"501,503"`,
+               ParentConfigRetryKeysLast.UnavailableRetryResponses:    
`"501,504"`,
+       }
+
+       // Assign them to the profile
+       for key, val := range params {
+               tcparam := tc.Parameter{
+                       Name:       key,
+                       ConfigFile: "parent.config",
+                       Value:      val,
+                       Profiles:   []byte(`["ds0Profile"]`),
+               }
+               parentConfigParams = append(parentConfigParams, tcparam)
+       }
+
+       serverParams := []tc.Parameter{
+               {
+                       Name:       "trafficserver",
+                       ConfigFile: "package",
+                       Value:      "9",
+                       Profiles:   []byte(`["global"]`),
+               },
+       }
+
+       edge := makeTestParentServer()
+       edge.Cachegroup = util.StrPtr("edgeCG")
+       edge.CachegroupID = util.IntPtr(400)
+
+       mid0 := makeTestParentServer()
+       mid0.Cachegroup = util.StrPtr("midCG0")
+       mid0.CachegroupID = util.IntPtr(500)
+       mid0.HostName = util.StrPtr("mymid0")
+       mid0.ID = util.IntPtr(45)
+       setIP(mid0, "192.168.2.2")
+
+       mid1 := makeTestParentServer()
+       mid1.Cachegroup = util.StrPtr("midCG1")
+       mid1.CachegroupID = util.IntPtr(501)
+       mid1.HostName = util.StrPtr("mymid1")
+       mid1.ID = util.IntPtr(46)
+       setIP(mid0, "192.168.2.3")
+
+       org0 := makeTestParentServer()
+       org0.Cachegroup = util.StrPtr("orgCG0")
+       org0.CachegroupID = util.IntPtr(502)
+       org0.HostName = util.StrPtr("myorg0")
+       org0.ID = util.IntPtr(47)
+       setIP(org0, "192.168.2.4")
+       org0.Type = tc.OriginTypeName
+       org0.TypeID = util.IntPtr(991)
+
+       org1 := makeTestParentServer()
+       org1.Cachegroup = util.StrPtr("orgCG1")
+       org1.CachegroupID = util.IntPtr(503)
+       org1.HostName = util.StrPtr("myorg1")
+       org1.ID = util.IntPtr(48)
+       setIP(org1, "192.168.2.5")
+       org1.Type = tc.OriginTypeName
+       org1.TypeID = util.IntPtr(991)
+
+       servers := []Server{*edge, *mid0, *mid1, *org0, *org1}
+
+       topologies := []tc.Topology{
+               {
+                       Name: "t0",
+                       Nodes: []tc.TopologyNode{
+                               {
+                                       Cachegroup: "edgeCG",
+                                       Parents:    []int{1, 2},
+                               },
+                               {
+                                       Cachegroup: "midCG0",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "midCG1",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "orgCG0",
+                               },
+                               {
+                                       Cachegroup: "orgCG1",
+                               },
+                       },
+               },
+       }
+
+       serverCapabilities := map[int]map[ServerCapability]struct{}{}
+       dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
+
+       eCG := &tc.CacheGroupNullable{}
+       eCG.Name = edge.Cachegroup
+       eCG.ID = edge.CachegroupID
+       eCG.ParentName = mid0.Cachegroup
+       eCG.ParentCachegroupID = mid0.CachegroupID
+       eCG.SecondaryParentName = mid1.Cachegroup
+       eCG.SecondaryParentCachegroupID = mid1.CachegroupID
+       eCGType := tc.CacheGroupEdgeTypeName
+       eCG.Type = &eCGType
+
+       mCG0 := &tc.CacheGroupNullable{}
+       mCG0.Name = mid0.Cachegroup
+       mCG0.ID = mid0.CachegroupID
+       mCG0.ParentName = org0.Cachegroup
+       mCG0.ParentCachegroupID = org0.CachegroupID
+       mCG0.SecondaryParentName = org1.Cachegroup
+       mCG0.SecondaryParentCachegroupID = org1.CachegroupID
+       mCGType0 := tc.CacheGroupMidTypeName
+       mCG0.Type = &mCGType0
+
+       mCG1 := &tc.CacheGroupNullable{}
+       mCG1.Name = mid1.Cachegroup
+       mCG1.ID = mid1.CachegroupID
+       mCG1.ParentName = org1.Cachegroup
+       mCG1.ParentCachegroupID = org1.CachegroupID
+       mCG1.SecondaryParentName = org0.Cachegroup
+       mCG1.SecondaryParentCachegroupID = org0.CachegroupID
+       mCGType1 := tc.CacheGroupMidTypeName
+       mCG1.Type = &mCGType1
+
+       oCG0 := &tc.CacheGroupNullable{}
+       oCG0.Name = org0.Cachegroup
+       oCG0.ID = org0.CachegroupID
+       oCGType0 := tc.CacheGroupOriginTypeName
+       oCG0.Type = &oCGType0
+
+       oCG1 := &tc.CacheGroupNullable{}
+       oCG1.Name = org1.Cachegroup
+       oCG1.ID = org1.CachegroupID
+       oCGType1 := tc.CacheGroupOriginTypeName
+       oCG1.Type = &oCGType1
+
+       cgs := []tc.CacheGroupNullable{*eCG, *mCG0, *mCG1, *oCG0, *oCG1}
+
+       dss := []DeliveryServiceServer{
+               {Server: *edge.ID, DeliveryService: *ds0.ID},
+               {Server: *mid0.ID, DeliveryService: *ds0.ID},
+               {Server: *mid1.ID, DeliveryService: *ds0.ID},
+               {Server: *org0.ID, DeliveryService: *ds0.ID},
+               {Server: *org1.ID, DeliveryService: *ds0.ID},
+
+               {Server: *edge.ID, DeliveryService: *ds1.ID},
+               {Server: *mid0.ID, DeliveryService: *ds1.ID},
+               {Server: *mid1.ID, DeliveryService: *ds1.ID},
+               {Server: *org0.ID, DeliveryService: *ds1.ID},
+               {Server: *org1.ID, DeliveryService: *ds1.ID},
+       }
+       cdn := &tc.CDN{
+               DomainName: "cdndomain.example",
+               Name:       "my-cdn-name",
+       }
+
+       dsstrs := []string{
+               `dest_domain=ds0.example.net `,
+               `dest_domain=ds1.example.net `,
+       }
+
+       { // test edge config
+               cfg, err := MakeParentDotConfig(dses, edge, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, hdr)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, hdr.HdrComment)
+
+               needs := []string{
+                       ` secondary_mode=2`,
+                       ` round_robin=consistent_hash`,
+                       ` go_direct=false`,
+                       ` parent_is_proxy=true`,
+                       ` parent_retry=both`,
+                       ` max_simple_retries=12`,
+                       ` max_unavailable_server_retries=22`,
+                       ` simple_server_retry_responses="401,402"`,
+                       ` unavailable_server_retry_responses="501,502"`,
+               }
+
+               for _, dsstr := range dsstrs {
+                       cnt := strings.Count(txt, dsstr)
+                       if 1 != cnt {
+                               t.Errorf("Expected one entry for %s got 
%d\n%v", dsstr, cnt, txt)
+                       } else {
+                               lines := strings.Split(txt, "\n")
+                               dsline := lineWhichContains(lines, dsstr)
+                               missing := missingFrom(dsline, needs)
+                               if 0 < len(missing) {
+                                       t.Errorf("Missing required string(s) 
from line: %v\n%v", missing, dsline)
+                               }
+                       }
+               }
+       }
+
+       { // test mid config
+               cfg, err := MakeParentDotConfig(dses, mid0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, hdr)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, hdr.HdrComment)
+
+               needs := []string{
+                       ` round_robin=true`,
+                       ` go_direct=true`,
+                       ` parent_is_proxy=false`,
+                       ` parent_retry=both`,
+                       ` max_simple_retries=12`,
+                       ` max_unavailable_server_retries=22`,
+                       ` simple_server_retry_responses="401,402"`,
+                       ` unavailable_server_retry_responses="501,502"`,
+               }
+
+               {
+                       dsstr := "dest_domain=ds1.example.net"
+                       cnt := strings.Count(txt, dsstr)
+                       if 1 != cnt {
+                               t.Errorf("Expected one entry for %s got 
%d\n%v", dsstr, cnt, txt)
+                       } else {
+                               lines := strings.Split(txt, "\n")
+                               dsline := lineWhichContains(lines, dsstr)
+                               missing := missingFrom(dsline, needs)
+                               if 0 < len(missing) {
+                                       t.Errorf("Missing required string(s) 
from line: %v\n%v", missing, dsline)
+                               }
+                       }
+               }
+       }
+}
+
+func TestMakeParentDotConfigFirstInnerLastTopology(t *testing.T) {
+       hdr := &ParentConfigOpts{AddComments: true, HdrComment: 
"myHeaderComment"}
+
+       // Toplogy ds, MSO
+       ds0 := makeParentDS()
+       ds0Type := tc.DSTypeHTTP
+       ds0.Type = &ds0Type
+       ds0.QStringIgnore = 
util.IntPtr(int(tc.QStringIgnoreUseInCacheKeyAndPassUp))
+       ds0.OrgServerFQDN = util.StrPtr("http://ds0.example.net";)
+       ds0.ProfileID = util.IntPtr(311)
+       ds0.ProfileName = util.StrPtr("ds0Profile")
+       ds0.MultiSiteOrigin = util.BoolPtr(true)
+       ds0.Topology = util.StrPtr("t0")
+
+       // Toplogy ds, non MSO
+       ds1 := makeParentDS()
+       ds1.ID = util.IntPtr(44)
+       ds1Type := tc.DSTypeHTTP
+       ds1.Type = &ds1Type
+       ds1.QStringIgnore = 
util.IntPtr(int(tc.QStringIgnoreUseInCacheKeyAndPassUp))
+       ds1.OrgServerFQDN = util.StrPtr("http://ds1.example.net";)
+       ds1.ProfileID = util.IntPtr(311)
+       ds1.ProfileName = util.StrPtr("ds0Profile")
+       ds1.Topology = util.StrPtr("t0")
+
+       dses := []DeliveryService{*ds0, *ds1}
+
+       parentConfigParams := []tc.Parameter{
+               {
+                       Name:       ParentConfigParamQStringHandling,
+                       ConfigFile: "parent.config",
+                       Value:      "myQStringHandlingParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
+                       ConfigFile: "parent.config",
+                       Value:      tc.AlgorithmConsistentHash,
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigParamQString,
+                       ConfigFile: "parent.config",
+                       Value:      "myQstringParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+       }
+
+       // Create set of DS params
+       params := map[string]string{
+               ParentConfigRetryKeysDefault.Algorithm: "strict",
+               ParentConfigRetryKeysMSO.Algorithm:     "strict",
+               ParentConfigRetryKeysFirst.Algorithm:   "true",
+               ParentConfigRetryKeysInner.Algorithm:   "latched",
+               ParentConfigRetryKeysLast.Algorithm:    "true",
+
+               ParentConfigRetryKeysDefault.SecondaryMode: "exhaust",
+               ParentConfigRetryKeysMSO.SecondaryMode:     "exhaust",
+               ParentConfigRetryKeysFirst.SecondaryMode:   "alternate",
+               ParentConfigRetryKeysInner.SecondaryMode:   "peering",
+               ParentConfigRetryKeysLast.SecondaryMode:    "alternate",
+
+               ParentConfigRetryKeysMSO.ParentRetry:   
"unavailable_server_retry",
+               ParentConfigRetryKeysFirst.ParentRetry: "both",
+               ParentConfigRetryKeysInner.ParentRetry: "both",
+               ParentConfigRetryKeysLast.ParentRetry:  "both",
+
+               ParentConfigRetryKeysDefault.MaxSimpleRetries: "11",
+               ParentConfigRetryKeysMSO.MaxSimpleRetries:     "11",
+               ParentConfigRetryKeysFirst.MaxSimpleRetries:   "12",
+               ParentConfigRetryKeysInner.MaxSimpleRetries:   "13",
+               ParentConfigRetryKeysLast.MaxSimpleRetries:    "14",
+
+               ParentConfigRetryKeysDefault.SimpleRetryResponses: `"401"`,
+               ParentConfigRetryKeysMSO.SimpleRetryResponses:     `"401"`,
+               ParentConfigRetryKeysFirst.SimpleRetryResponses:   `"401,402"`,
+               ParentConfigRetryKeysInner.SimpleRetryResponses:   `"401,403"`,
+               ParentConfigRetryKeysLast.SimpleRetryResponses:    `"401,404"`,
+
+               ParentConfigRetryKeysDefault.MaxUnavailableRetries: "21",
+               ParentConfigRetryKeysMSO.MaxUnavailableRetries:     "21",
+               ParentConfigRetryKeysFirst.MaxUnavailableRetries:   "22",
+               ParentConfigRetryKeysInner.MaxUnavailableRetries:   "23",
+               ParentConfigRetryKeysLast.MaxUnavailableRetries:    "24",
+
+               ParentConfigRetryKeysDefault.UnavailableRetryResponses: `"501"`,
+               ParentConfigRetryKeysMSO.UnavailableRetryResponses:     `"501"`,
+               ParentConfigRetryKeysFirst.UnavailableRetryResponses:   
`"501,502"`,
+               ParentConfigRetryKeysInner.UnavailableRetryResponses:   
`"501,503"`,
+               ParentConfigRetryKeysLast.UnavailableRetryResponses:    
`"501,504"`,
+       }
+
+       // Assign them to the profile
+       for key, val := range params {
+               tcparam := tc.Parameter{
+                       Name:       key,
+                       ConfigFile: "parent.config",
+                       Value:      val,
+                       Profiles:   []byte(`["ds0Profile"]`),
+               }
+               parentConfigParams = append(parentConfigParams, tcparam)
+       }
+
+       serverParams := []tc.Parameter{
+               {
+                       Name:       "trafficserver",
+                       ConfigFile: "package",
+                       Value:      "9",
+                       Profiles:   []byte(`["global"]`),
+               },
+       }
+
+       edge := makeTestParentServer()
+       edge.Cachegroup = util.StrPtr("edgeCG")
+       edge.CachegroupID = util.IntPtr(400)
+       edge.HostName = util.StrPtr("edge")
+
+       mid0 := makeTestParentServer()
+       mid0.Cachegroup = util.StrPtr("midCG0")
+       mid0.CachegroupID = util.IntPtr(500)
+       mid0.HostName = util.StrPtr("mid0")
+       mid0.ID = util.IntPtr(45)
+       setIP(mid0, "192.168.2.2")
+
+       mid1 := makeTestParentServer()
+       mid1.Cachegroup = util.StrPtr("midCG1")
+       mid1.CachegroupID = util.IntPtr(501)
+       mid1.HostName = util.StrPtr("mid1")
+       mid1.ID = util.IntPtr(46)
+       setIP(mid1, "192.168.2.3")
+
+       opl0 := makeTestParentServer()
+       opl0.Cachegroup = util.StrPtr("oplCG0")
+       opl0.CachegroupID = util.IntPtr(502)
+       opl0.HostName = util.StrPtr("opl0")
+       opl0.ID = util.IntPtr(47)
+       setIP(opl0, "192.168.2.4")
+
+       opl1 := makeTestParentServer()
+       opl1.Cachegroup = util.StrPtr("oplCG1")
+       opl1.CachegroupID = util.IntPtr(503)
+       opl1.HostName = util.StrPtr("opl1")
+       opl1.ID = util.IntPtr(48)
+       setIP(opl1, "192.168.2.5")
+
+       org0 := makeTestParentServer()
+       org0.Cachegroup = util.StrPtr("orgCG0")
+       org0.CachegroupID = util.IntPtr(504)
+       org0.HostName = util.StrPtr("org0")
+       org0.ID = util.IntPtr(49)
+       setIP(org0, "192.168.2.6")
+       org0.Type = tc.OriginTypeName
+       org0.TypeID = util.IntPtr(991)
+
+       org1 := makeTestParentServer()
+       org1.Cachegroup = util.StrPtr("orgCG1")
+       org1.CachegroupID = util.IntPtr(505)
+       org1.HostName = util.StrPtr("org1")
+       org1.ID = util.IntPtr(50)
+       setIP(org1, "192.168.2.7")
+       org1.Type = tc.OriginTypeName
+       org1.TypeID = util.IntPtr(991)
+
+       servers := []Server{*edge, *mid0, *mid1, *opl0, *opl1, *org0, *org1}
+
+       topologies := []tc.Topology{
+               {
+                       Name: "t0",
+                       Nodes: []tc.TopologyNode{
+                               {
+                                       Cachegroup: "edgeCG",
+                                       Parents:    []int{1, 2},
+                               },
+                               {
+                                       Cachegroup: "midCG0",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "midCG1",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "oplCG0",
+                                       Parents:    []int{5, 6},
+                               },
+                               {
+                                       Cachegroup: "oplCG1",
+                                       Parents:    []int{5, 6},
+                               },
+                               {
+                                       Cachegroup: "orgCG0",
+                               },
+                               {
+                                       Cachegroup: "orgCG1",
+                               },
+                       },
+               },
+       }
+
+       serverCapabilities := map[int]map[ServerCapability]struct{}{}
+       dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
+
+       eCG := &tc.CacheGroupNullable{}
+       eCG.Name = edge.Cachegroup
+       eCG.ID = edge.CachegroupID
+       eCG.ParentName = mid0.Cachegroup
+       eCG.ParentCachegroupID = mid0.CachegroupID
+       eCG.SecondaryParentName = mid1.Cachegroup
+       eCG.SecondaryParentCachegroupID = mid1.CachegroupID
+       eCGType := tc.CacheGroupEdgeTypeName
+       eCG.Type = &eCGType
+
+       mCG0 := &tc.CacheGroupNullable{}
+       mCG0.Name = mid0.Cachegroup
+       mCG0.ID = mid0.CachegroupID
+       mCG0.ParentName = opl0.Cachegroup
+       mCG0.ParentCachegroupID = opl0.CachegroupID
+       mCG0.SecondaryParentName = opl1.Cachegroup
+       mCG0.SecondaryParentCachegroupID = opl1.CachegroupID
+       mCGType0 := tc.CacheGroupMidTypeName
+       mCG0.Type = &mCGType0
+
+       mCG1 := &tc.CacheGroupNullable{}
+       mCG1.Name = mid1.Cachegroup
+       mCG1.ID = mid1.CachegroupID
+       mCG1.ParentName = opl1.Cachegroup
+       mCG1.ParentCachegroupID = opl1.CachegroupID
+       mCG1.SecondaryParentName = opl0.Cachegroup
+       mCG1.SecondaryParentCachegroupID = opl0.CachegroupID
+       mCGType1 := tc.CacheGroupMidTypeName
+       mCG1.Type = &mCGType1
+
+       oplCG0 := &tc.CacheGroupNullable{}
+       oplCG0.Name = opl0.Cachegroup
+       oplCG0.ID = opl0.CachegroupID
+       oplCG0.ParentName = org0.Cachegroup
+       oplCG0.ParentCachegroupID = org0.CachegroupID
+       oplCG0.SecondaryParentName = org1.Cachegroup
+       oplCG0.SecondaryParentCachegroupID = org1.CachegroupID
+       oplCGType0 := tc.CacheGroupMidTypeName
+       oplCG0.Type = &oplCGType0
+
+       oplCG1 := &tc.CacheGroupNullable{}
+       oplCG1.Name = opl1.Cachegroup
+       oplCG1.ID = opl1.CachegroupID
+       oplCG1.ParentName = org1.Cachegroup
+       oplCG1.ParentCachegroupID = org1.CachegroupID
+       oplCG1.SecondaryParentName = org0.Cachegroup
+       oplCG1.SecondaryParentCachegroupID = org0.CachegroupID
+       oplCGType1 := tc.CacheGroupMidTypeName
+       oplCG1.Type = &oplCGType1
+
+       oCG0 := &tc.CacheGroupNullable{}
+       oCG0.Name = org0.Cachegroup
+       oCG0.ID = org0.CachegroupID
+       oCGType0 := tc.CacheGroupOriginTypeName
+       oCG0.Type = &oCGType0
+
+       oCG1 := &tc.CacheGroupNullable{}
+       oCG1.Name = org1.Cachegroup
+       oCG1.ID = org1.CachegroupID
+       oCGType1 := tc.CacheGroupOriginTypeName
+       oCG1.Type = &oCGType1
+
+       cgs := []tc.CacheGroupNullable{*eCG, *mCG0, *mCG1, *oplCG0, *oplCG1, 
*oCG0, *oCG1}
+
+       dss := []DeliveryServiceServer{
+               /*
+                       {Server: *edge.ID, DeliveryService: *ds0.ID},
+                       {Server: *mid0.ID, DeliveryService: *ds0.ID},
+                       {Server: *mid1.ID, DeliveryService: *ds0.ID},
+                       {Server: *opl0.ID, DeliveryService: *ds0.ID},
+                       {Server: *opl1.ID, DeliveryService: *ds0.ID},
+                       {Server: *org0.ID, DeliveryService: *ds0.ID},
+                       {Server: *org1.ID, DeliveryService: *ds0.ID},
+
+                       {Server: *edge.ID, DeliveryService: *ds1.ID},
+                       {Server: *mid0.ID, DeliveryService: *ds1.ID},
+                       {Server: *mid1.ID, DeliveryService: *ds1.ID},
+                       {Server: *opl0.ID, DeliveryService: *ds1.ID},
+                       {Server: *opl1.ID, DeliveryService: *ds1.ID},
+                       {Server: *org0.ID, DeliveryService: *ds1.ID},
+                       {Server: *org1.ID, DeliveryService: *ds1.ID},
+               */
+       }
+       cdn := &tc.CDN{
+               DomainName: "cdndomain.example",
+               Name:       "my-cdn-name",
+       }
+
+       dsstrs := []string{
+               `dest_domain=ds0.example.net `,
+               `dest_domain=ds1.example.net `,
+       }
+
+       { // test edge config
+               cfg, err := MakeParentDotConfig(dses, edge, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, hdr)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, hdr.HdrComment)
+
+               needs := []string{
+                       ` secondary_mode=2`,
+                       ` round_robin=consistent_hash`,
+                       ` go_direct=false`,
+                       ` parent_is_proxy=true`,
+                       ` parent_retry=both`,
+                       ` max_simple_retries=12`,
+                       ` max_unavailable_server_retries=22`,
+                       ` simple_server_retry_responses="401,402"`,
+                       ` unavailable_server_retry_responses="501,502"`,
+               }
+
+               for _, dsstr := range dsstrs {
+                       cnt := strings.Count(txt, dsstr)
+                       if 1 != cnt {
+                               t.Errorf("Expected one entry for %s got 
%d\n%v", dsstr, cnt, txt)
+                       } else {
+                               lines := strings.Split(txt, "\n")
+                               dsline := lineWhichContains(lines, dsstr)
+                               missing := missingFrom(dsline, needs)
+                               if 0 < len(missing) {
+                                       t.Errorf("Missing required string(s) 
from line: %v\n%v", missing, dsline)
+                               }
+                       }
+               }
+       }
+
+       { // test mid config
+               cfg, err := MakeParentDotConfig(dses, mid0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, hdr)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, hdr.HdrComment)
+
+               needs := []string{
+                       ` round_robin=consistent_hash`,
+                       ` go_direct=false`,
+                       ` parent_is_proxy=true`,
+                       ` parent_retry=both`,
+                       ` max_simple_retries=13`,
+                       ` max_unavailable_server_retries=23`,
+                       ` simple_server_retry_responses="401,403"`,
+                       ` unavailable_server_retry_responses="501,503"`,
+               }
+
+               for _, dsstr := range dsstrs {
+                       cnt := strings.Count(txt, dsstr)
+                       if 1 != cnt {
+                               t.Errorf("Expected one entry for %s got 
%d\n%v", dsstr, cnt, txt)
+                       } else {
+                               lines := strings.Split(txt, "\n")
+                               dsline := lineWhichContains(lines, dsstr)
+                               missing := missingFrom(dsline, needs)
+                               if 0 < len(missing) {
+                                       t.Errorf("Missing required string(s) 
from line: %v\n%v", missing, dsline)
+                               }
+                       }
+               }
+       }
+
+       { // test opl config
+               cfg, err := MakeParentDotConfig(dses, opl0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, hdr)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, hdr.HdrComment)
+
+               needs := []string{
+                       ` round_robin=true`,
+                       ` go_direct=true`,
+                       ` parent_is_proxy=false`,
+                       ` parent_retry=both`,
+                       ` max_simple_retries=14`,
+                       ` max_unavailable_server_retries=24`,
+                       ` simple_server_retry_responses="401,404"`,
+                       ` unavailable_server_retry_responses="501,504"`,
+               }
+
+               dsstr := `dest_domain=ds1.example.net `
+               cnt := strings.Count(txt, dsstr)
+               if 1 != cnt {
+                       t.Errorf("Expected one entry for %s got %d\n%v", dsstr, 
cnt, txt)
+               } else {
+                       lines := strings.Split(txt, "\n")
+                       dsline := lineWhichContains(lines, dsstr)
+                       missing := missingFrom(dsline, needs)
+                       if 0 < len(missing) {
+                               t.Errorf("Missing required string(s) from line: 
%v\n%v", missing, dsline)
+                       }
+               }
+       }
+}
+
+// returns which elements in "needs" is missing from "line"
+func missingFrom(line string, needs []string) []string {
+       misses := []string{}
+       for _, need := range needs {
+               if !strings.Contains(line, need) {
+                       misses = append(misses, need)
+               }
+       }
+       return misses
+}
+
+// returns first line which contains
+func lineWhichContains(lines []string, str string) (res string) {
+       for _, line := range lines {
+               if strings.Contains(line, str) {
+                       res = line
+                       break
+               }
+       }
+       return res
+}
+
 // warningsContains returns whether the given warnings has str as a substring 
of any warning.
 // Note this is different than lib/go-util.ContainsStr, which only returns if 
the array has the exact value as one of its values.
 func warningsContains(warnings []string, str string) bool {
diff --git a/lib/go-atscfg/snidotyaml_test.go b/lib/go-atscfg/snidotyaml_test.go
index c7d67eb6a9..33d125a5a7 100644
--- a/lib/go-atscfg/snidotyaml_test.go
+++ b/lib/go-atscfg/snidotyaml_test.go
@@ -59,7 +59,7 @@ func TestMakeSNIDotYAMLH2(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -170,7 +170,7 @@ func TestMakeSNIDotYAMLH2(t *testing.T) {
                                Profiles:   []byte(`["serverprofile"]`),
                        },
                        tc.Parameter{
-                               Name:       ParentConfigParamAlgorithm,
+                               Name:       
ParentConfigRetryKeysDefault.Algorithm,
                                ConfigFile: "parent.config",
                                Value:      tc.AlgorithmConsistentHash,
                                Profiles:   []byte(`["serverprofile"]`),
@@ -223,7 +223,7 @@ func TestMakeSNIDotYAMLH2(t *testing.T) {
                                Profiles:   []byte(`["serverprofile"]`),
                        },
                        tc.Parameter{
-                               Name:       ParentConfigParamAlgorithm,
+                               Name:       
ParentConfigRetryKeysDefault.Algorithm,
                                ConfigFile: "parent.config",
                                Value:      tc.AlgorithmConsistentHash,
                                Profiles:   []byte(`["serverprofile"]`),
@@ -270,7 +270,7 @@ func TestMakeSNIDotYAMLH2(t *testing.T) {
                                Profiles:   []byte(`["serverprofile"]`),
                        },
                        tc.Parameter{
-                               Name:       ParentConfigParamAlgorithm,
+                               Name:       
ParentConfigRetryKeysDefault.Algorithm,
                                ConfigFile: "parent.config",
                                Value:      tc.AlgorithmConsistentHash,
                                Profiles:   []byte(`["serverprofile"]`),
diff --git a/lib/go-atscfg/sslservernamedotyaml_test.go 
b/lib/go-atscfg/sslservernamedotyaml_test.go
index 659733d1e0..1d1ab82f2a 100644
--- a/lib/go-atscfg/sslservernamedotyaml_test.go
+++ b/lib/go-atscfg/sslservernamedotyaml_test.go
@@ -54,7 +54,7 @@ func TestMakeSSLServerNameYAML(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -174,7 +174,7 @@ func TestMakeSSLServerNameYAMLParams(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -306,7 +306,7 @@ func TestMakeSSLServerNameYAMLParamInvalid(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -442,7 +442,7 @@ func TestMakeSSLServerNameYAMLDSTLSVersions(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
diff --git a/lib/go-atscfg/strategiesdotconfig.go 
b/lib/go-atscfg/strategiesdotconfig.go
index 0b2d46168c..b84b113bdf 100644
--- a/lib/go-atscfg/strategiesdotconfig.go
+++ b/lib/go-atscfg/strategiesdotconfig.go
@@ -155,10 +155,13 @@ func getStrategySecondaryMode(mode 
ParentAbstractionServiceParentSecondaryMode)
 }
 
 func getStrategyErrorCodes(codes []int) string {
-       str := ""
+       str := " ["
+       join := " "
        for _, code := range codes {
-               str += "\n" + `        - ` + strconv.Itoa(code)
+               str += join + strconv.Itoa(code)
+               join = ", "
        }
+       str += " ]"
        return str
 }
 
diff --git a/lib/go-atscfg/strategiesdotconfig_test.go 
b/lib/go-atscfg/strategiesdotconfig_test.go
index 88d5b0bcb1..a354cac29e 100644
--- a/lib/go-atscfg/strategiesdotconfig_test.go
+++ b/lib/go-atscfg/strategiesdotconfig_test.go
@@ -55,7 +55,7 @@ func TestMakeStrategiesDotConfig(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -179,7 +179,7 @@ func TestMakeStrategiesTopologiesParams(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -191,33 +191,33 @@ func TestMakeStrategiesTopologiesParams(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
-                       Value:      "consistent_hash",
+                       Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamParentRetry,
+                       Name:       ParentConfigRetryKeysDefault.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamUnavailableServerRetryResponses,
+                       Name:       
ParentConfigRetryKeysDefault.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysDefault.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysDefault.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
        }
@@ -314,8 +314,8 @@ func TestMakeStrategiesTopologiesParams(t *testing.T) {
                "strategy:'strategy-ds1'",
                "max_simple_retries:14",
                "max_unavailable_retries:9",
-               "response_codes:\n-404",
-               "markdown_codes:\n-400\n-503",
+               "response_codes:[404]",
+               "markdown_codes:[400,503]",
                
"host__ds1__parent__myorigin0-dot-mydomain-dot-example-dot-net__80\nhost:myorigin0.mydomain.example.net",
        }
 
@@ -353,7 +353,7 @@ func TestMakeStrategiesHTTPSOrigin(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -476,7 +476,7 @@ func TestMakeStrategiesPeeringRing(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -488,33 +488,33 @@ func TestMakeStrategiesPeeringRing(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
-                       Value:      "consistent_hash",
+                       Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamParentRetry,
+                       Name:       ParentConfigRetryKeysDefault.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamUnavailableServerRetryResponses,
+                       Name:       
ParentConfigRetryKeysDefault.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysDefault.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysDefault.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
        }
@@ -629,8 +629,8 @@ func TestMakeStrategiesPeeringRing(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__ds1-dot-example-dot-net__80\nhost:ds1.example.net",
                        "groups:\n-*peers_group\n-*group_parents_ds1", // peer 
ring group before parent group, param 'true'
                }
@@ -666,8 +666,8 @@ func TestMakeStrategiesPeeringRing(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__ds1-dot-example-dot-net__80\nhost:ds1.example.net",
                        "groups:\n-*group_parents_ds1\nfailover:", // no peer 
ring group, param is not 'true'
                }
@@ -694,8 +694,8 @@ func TestMakeStrategiesPeeringRing(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__ds1-dot-example-dot-net__80\nhost:ds1.example.net",
                        "groups:\n-*group_parents_ds1\nfailover:", // no peer 
ring group, no parameter
                }
@@ -732,7 +732,7 @@ func TestMakeStrategiesPeeringRingMSO(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -744,33 +744,33 @@ func TestMakeStrategiesPeeringRingMSO(t *testing.T) {
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
-                       Value:      "consistent_hash",
+                       Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamParentRetry,
+                       Name:       ParentConfigRetryKeysDefault.ParentRetry,
                        ConfigFile: "parent.config",
                        Value:      "both",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamUnavailableServerRetryResponses,
+                       Name:       
ParentConfigRetryKeysDefault.MaxSimpleRetries,
                        ConfigFile: "parent.config",
-                       Value:      `"400,503"`,
+                       Value:      "14",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamMaxSimpleRetries,
+                       Name:       
ParentConfigRetryKeysDefault.MaxUnavailableRetries,
                        ConfigFile: "parent.config",
-                       Value:      "14",
+                       Value:      "9",
                        Profiles:   []byte(`["ds1Profile"]`),
                },
                tc.Parameter{
-                       Name:       
ParentConfigParamMaxUnavailableServerRetries,
+                       Name:       
ParentConfigRetryKeysDefault.UnavailableRetryResponses,
                        ConfigFile: "parent.config",
-                       Value:      "9",
+                       Value:      `"400,503"`,
                        Profiles:   []byte(`["ds1Profile"]`),
                },
        }
@@ -885,8 +885,8 @@ func TestMakeStrategiesPeeringRingMSO(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__myorigin0-dot-mydomain-dot-example-dot-net__80\nhost:myorigin0.mydomain.example.net",
                        "groups:\n-*peers_group\n-*group_parents_ds1", // peer 
ring group before parent group, param 'true'
                }
@@ -922,8 +922,8 @@ func TestMakeStrategiesPeeringRingMSO(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__myorigin0-dot-mydomain-dot-example-dot-net__80\nhost:myorigin0.mydomain.example.net",
                        "groups:\n-*group_parents_ds1\nfailover:", // no peer 
ring group, param is not 'true'
                }
@@ -950,8 +950,8 @@ func TestMakeStrategiesPeeringRingMSO(t *testing.T) {
                        "strategy:'strategy-ds1'",
                        "max_simple_retries:14",
                        "max_unavailable_retries:9",
-                       "response_codes:\n-404",
-                       "markdown_codes:\n-400\n-503",
+                       "response_codes:[404]",
+                       "markdown_codes:[400,503]",
                        
"host__ds1__parent__myorigin0-dot-mydomain-dot-example-dot-net__80\nhost:myorigin0.mydomain.example.net",
                        "groups:\n-*group_parents_ds1\nfailover:", // no peer 
ring group, no parameter
                }
@@ -993,7 +993,7 @@ func TestMakeStrategiesPeeringRingNonTopology(t *testing.T) 
{
                        Profiles:   []byte(`["serverprofile"]`),
                },
                tc.Parameter{
-                       Name:       ParentConfigParamAlgorithm,
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
                        ConfigFile: "parent.config",
                        Value:      tc.AlgorithmConsistentHash,
                        Profiles:   []byte(`["serverprofile"]`),
@@ -1187,3 +1187,664 @@ func TestMakeStrategiesPeeringRingNonTopology(t 
*testing.T) {
                }
        })
 }
+
+func TestMakeStrategiesDotYAMLFirstLastNoTopoParams(t *testing.T) {
+       opt := &StrategiesYAMLOpts{VerboseComments: false, HdrComment: 
"myHeaderComment"}
+
+       // Non Toplogy
+       ds0 := makeParentDS()
+       ds0.ID = util.IntPtr(42)
+       ds0Type := tc.DSTypeDNS
+       ds0.Type = &ds0Type
+       ds0.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreDrop))
+       ds0.OrgServerFQDN = util.StrPtr("http://ds0.example.net";)
+       ds0.ProfileID = util.IntPtr(310)
+       ds0.ProfileName = util.StrPtr("ds0Profile")
+
+       // Non Toplogy, MSO
+       ds1 := makeParentDS()
+       ds1.ID = util.IntPtr(43)
+       ds1Type := tc.DSTypeDNS
+       ds1.Type = &ds1Type
+       ds1.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreDrop))
+       ds1.OrgServerFQDN = util.StrPtr("http://ds1.example.net";)
+       ds1.ProfileID = util.IntPtr(310)
+       ds1.ProfileName = util.StrPtr("ds0Profile")
+       ds1.MultiSiteOrigin = util.BoolPtr(true)
+
+       dsesall := []DeliveryService{*ds0, *ds1}
+
+       parentConfigParams := []tc.Parameter{
+               {
+                       Name:       ParentConfigParamQStringHandling,
+                       ConfigFile: "parent.config",
+                       Value:      "myQStringHandlingParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
+                       ConfigFile: "parent.config",
+                       Value:      tc.AlgorithmConsistentHash,
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigParamQString,
+                       ConfigFile: "parent.config",
+                       Value:      "myQstringParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+       }
+
+       // Create set of DS params
+       params := map[string]string{
+               ParentConfigRetryKeysDefault.Algorithm: "strict",
+               ParentConfigRetryKeysMSO.Algorithm:     "strict",
+               ParentConfigRetryKeysFirst.Algorithm:   "true",
+               ParentConfigRetryKeysInner.Algorithm:   "latched",
+               ParentConfigRetryKeysLast.Algorithm:    "true",
+
+               ParentConfigRetryKeysDefault.SecondaryMode: "exhaust",
+               ParentConfigRetryKeysMSO.SecondaryMode:     "exhaust",
+               ParentConfigRetryKeysFirst.SecondaryMode:   "alternate",
+               ParentConfigRetryKeysInner.SecondaryMode:   "peering",
+               ParentConfigRetryKeysLast.SecondaryMode:    "alternate",
+
+               ParentConfigRetryKeysMSO.ParentRetry:   
"unavailable_server_retry",
+               ParentConfigRetryKeysFirst.ParentRetry: "both",
+               ParentConfigRetryKeysInner.ParentRetry: "both",
+               ParentConfigRetryKeysLast.ParentRetry:  "both",
+
+               ParentConfigRetryKeysDefault.MaxSimpleRetries: "11",
+               ParentConfigRetryKeysMSO.MaxSimpleRetries:     "11",
+               ParentConfigRetryKeysFirst.MaxSimpleRetries:   "12",
+               ParentConfigRetryKeysInner.MaxSimpleRetries:   "13",
+               ParentConfigRetryKeysLast.MaxSimpleRetries:    "14",
+
+               ParentConfigRetryKeysDefault.SimpleRetryResponses: `"401"`,
+               ParentConfigRetryKeysMSO.SimpleRetryResponses:     `"401"`,
+               ParentConfigRetryKeysFirst.SimpleRetryResponses:   `"401,402"`,
+               ParentConfigRetryKeysInner.SimpleRetryResponses:   `"401,403"`,
+               ParentConfigRetryKeysLast.SimpleRetryResponses:    `"401,404"`,
+
+               ParentConfigRetryKeysDefault.MaxUnavailableRetries: "21",
+               ParentConfigRetryKeysMSO.MaxUnavailableRetries:     "21",
+               ParentConfigRetryKeysFirst.MaxUnavailableRetries:   "22",
+               ParentConfigRetryKeysInner.MaxUnavailableRetries:   "23",
+               ParentConfigRetryKeysLast.MaxUnavailableRetries:    "24",
+
+               ParentConfigRetryKeysDefault.UnavailableRetryResponses: `"501"`,
+               ParentConfigRetryKeysMSO.UnavailableRetryResponses:     `"501"`,
+               ParentConfigRetryKeysFirst.UnavailableRetryResponses:   
`"501,502"`,
+               ParentConfigRetryKeysInner.UnavailableRetryResponses:   
`"501,503"`,
+               ParentConfigRetryKeysLast.UnavailableRetryResponses:    
`"501,504"`,
+       }
+
+       // Assign them to the profile
+       for key, val := range params {
+               tcparam := tc.Parameter{
+                       Name:       key,
+                       ConfigFile: "parent.config",
+                       Value:      val,
+                       Profiles:   []byte(`["ds0Profile"]`),
+               }
+               parentConfigParams = append(parentConfigParams, tcparam)
+       }
+
+       serverParams := []tc.Parameter{
+               {
+                       Name:       "trafficserver",
+                       ConfigFile: "package",
+                       Value:      "9",
+                       Profiles:   []byte(`["global"]`),
+               },
+       }
+
+       edge := makeTestParentServer()
+       edge.Cachegroup = util.StrPtr("edgeCG")
+       edge.CachegroupID = util.IntPtr(400)
+
+       mid0 := makeTestParentServer()
+       mid0.Cachegroup = util.StrPtr("midCG0")
+       mid0.CachegroupID = util.IntPtr(500)
+       mid0.HostName = util.StrPtr("mymid0")
+       mid0.ID = util.IntPtr(45)
+       setIP(mid0, "192.168.2.2")
+       mid0.Type = tc.CacheGroupMidTypeName
+       mid0.TypeID = util.IntPtr(990)
+
+       mid1 := makeTestParentServer()
+       mid1.Cachegroup = util.StrPtr("midCG1")
+       mid1.CachegroupID = util.IntPtr(501)
+       mid1.HostName = util.StrPtr("mymid1")
+       mid1.ID = util.IntPtr(46)
+       setIP(mid1, "192.168.2.3")
+       mid1.Type = tc.CacheGroupMidTypeName
+       mid1.TypeID = util.IntPtr(990)
+
+       org0 := makeTestParentServer()
+       org0.Cachegroup = util.StrPtr("orgCG0")
+       org0.CachegroupID = util.IntPtr(502)
+       org0.HostName = util.StrPtr("myorg0")
+       org0.ID = util.IntPtr(48)
+       setIP(org0, "192.168.2.4")
+       org0.Type = tc.OriginTypeName
+       org0.TypeID = util.IntPtr(991)
+
+       org1 := makeTestParentServer()
+       org1.Cachegroup = util.StrPtr("orgCG1")
+       org1.CachegroupID = util.IntPtr(503)
+       org1.HostName = util.StrPtr("myorg1")
+       org1.ID = util.IntPtr(49)
+       setIP(org1, "192.168.2.5")
+       org1.Type = tc.OriginTypeName
+       org1.TypeID = util.IntPtr(991)
+
+       servers := []Server{*edge, *mid0, *mid1, *org0, *org1}
+
+       topologies := []tc.Topology{
+               {
+                       Name: "t0",
+                       Nodes: []tc.TopologyNode{
+                               {
+                                       Cachegroup: "edgeCG",
+                                       Parents:    []int{1, 2},
+                               },
+                               {
+                                       Cachegroup: "midCG0",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "midCG1",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "orgCG0",
+                               },
+                               {
+                                       Cachegroup: "orgCG1",
+                               },
+                       },
+               },
+       }
+
+       serverCapabilities := map[int]map[ServerCapability]struct{}{}
+       dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
+
+       eCG := &tc.CacheGroupNullable{}
+       eCG.Name = edge.Cachegroup
+       eCG.ID = edge.CachegroupID
+       eCG.ParentName = mid0.Cachegroup
+       eCG.ParentCachegroupID = mid0.CachegroupID
+       eCG.SecondaryParentName = mid1.Cachegroup
+       eCG.SecondaryParentCachegroupID = mid1.CachegroupID
+       eCGType := tc.CacheGroupEdgeTypeName
+       eCG.Type = &eCGType
+
+       mCG0 := &tc.CacheGroupNullable{}
+       mCG0.Name = mid0.Cachegroup
+       mCG0.ID = mid0.CachegroupID
+       mCG0.ParentName = org0.Cachegroup
+       mCG0.ParentCachegroupID = org0.CachegroupID
+       mCG0.SecondaryParentName = org1.Cachegroup
+       mCG0.SecondaryParentCachegroupID = org1.CachegroupID
+       mCGType0 := tc.CacheGroupMidTypeName
+       mCG0.Type = &mCGType0
+
+       mCG1 := &tc.CacheGroupNullable{}
+       mCG1.Name = mid1.Cachegroup
+       mCG1.ID = mid1.CachegroupID
+       mCG1.ParentName = org1.Cachegroup
+       mCG1.ParentCachegroupID = org1.CachegroupID
+       mCG1.SecondaryParentName = org0.Cachegroup
+       mCG1.SecondaryParentCachegroupID = org0.CachegroupID
+       mCGType1 := tc.CacheGroupMidTypeName
+       mCG1.Type = &mCGType1
+
+       oCG0 := &tc.CacheGroupNullable{}
+       oCG0.Name = org0.Cachegroup
+       oCG0.ID = org0.CachegroupID
+       oCGType0 := tc.CacheGroupOriginTypeName
+       oCG0.Type = &oCGType0
+
+       oCG1 := &tc.CacheGroupNullable{}
+       oCG1.Name = org1.Cachegroup
+       oCG1.ID = org1.CachegroupID
+       oCGType1 := tc.CacheGroupOriginTypeName
+       oCG1.Type = &oCGType1
+
+       cgs := []tc.CacheGroupNullable{*eCG, *mCG0, *mCG1, *oCG0, *oCG1}
+
+       dss := []DeliveryServiceServer{
+               {Server: *edge.ID, DeliveryService: *ds0.ID},
+               {Server: *mid0.ID, DeliveryService: *ds0.ID},
+               {Server: *mid1.ID, DeliveryService: *ds0.ID},
+               {Server: *org0.ID, DeliveryService: *ds0.ID},
+               {Server: *org1.ID, DeliveryService: *ds0.ID},
+
+               {Server: *edge.ID, DeliveryService: *ds1.ID},
+               {Server: *mid0.ID, DeliveryService: *ds1.ID},
+               {Server: *mid1.ID, DeliveryService: *ds1.ID},
+               {Server: *org0.ID, DeliveryService: *ds1.ID},
+               {Server: *org1.ID, DeliveryService: *ds1.ID},
+       }
+       cdn := &tc.CDN{
+               DomainName: "cdndomain.example",
+               Name:       "my-cdn-name",
+       }
+
+       // edge config
+       /*
+               for _, ds := range dsesall {
+                       dses := []DeliveryService{ds}
+                       cfg, err := MakeStrategiesDotYAML(dses, edge, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, opt)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       txt := cfg.Text
+
+                       testComment(t, txt, opt.HdrComment)
+
+                       needs := []string{
+                               ` policy: consistent_hash`,
+                               ` go_direct: false`,
+                               ` max_simple_retries: 12`,
+                               ` max_unavailable_retries: 22`,
+                               ` response_codes: [ 401, 402 ]`,
+                               ` markdown_codes: [ 501, 502 ]`,
+                       }
+
+                       missing := missingFrom(txt, needs)
+                       if 0 < len(missing) {
+                               t.Errorf("Missing required string(s) from line: 
%v\n%v", missing, txt)
+                       }
+               }
+       */
+
+       // test mid config, MS only
+       {
+               ds := dsesall[1]
+               dses := []DeliveryService{ds}
+               cfg, err := MakeStrategiesDotYAML(dses, mid0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, opt)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, opt.HdrComment)
+
+               needs := []string{
+                       ` policy: rr_ip`,
+                       ` go_direct: true`,
+                       ` max_simple_retries: 14`,
+                       ` max_unavailable_retries: 24`,
+                       ` response_codes: [ 401, 404 ]`,
+                       ` markdown_codes: [ 501, 504 ]`,
+               }
+
+               missing := missingFrom(txt, needs)
+               if 0 < len(missing) {
+                       t.Errorf("Missing required string(s) from ds/line: 
%s/%v\n%v", *ds.XMLID, missing, txt)
+               }
+       }
+}
+
+func TestMakeStrategiesDotYAMLFirstInnerLastParams(t *testing.T) {
+       opt := &StrategiesYAMLOpts{VerboseComments: false, HdrComment: 
"myHeaderComment"}
+
+       // Toplogy ds, MSO
+       ds0 := makeParentDS()
+       ds0Type := tc.DSTypeHTTP
+       ds0.Type = &ds0Type
+       ds0.QStringIgnore = 
util.IntPtr(int(tc.QStringIgnoreUseInCacheKeyAndPassUp))
+       ds0.OrgServerFQDN = util.StrPtr("http://ds0.example.net";)
+       ds0.ProfileID = util.IntPtr(311)
+       ds0.ProfileName = util.StrPtr("ds0Profile")
+       ds0.MultiSiteOrigin = util.BoolPtr(true)
+       ds0.Topology = util.StrPtr("t0")
+
+       // Toplogy ds, non MSO
+       ds1 := makeParentDS()
+       ds1.ID = util.IntPtr(43)
+       ds1Type := tc.DSTypeDNS
+       ds1.Type = &ds1Type
+       ds1.QStringIgnore = util.IntPtr(int(tc.QStringIgnoreDrop))
+       ds1.OrgServerFQDN = util.StrPtr("http://ds1.example.net";)
+       ds1.ProfileID = util.IntPtr(311)
+       ds1.ProfileName = util.StrPtr("ds0Profile")
+       ds1.Topology = util.StrPtr("t0")
+
+       dsesall := []DeliveryService{*ds0, *ds1}
+
+       parentConfigParams := []tc.Parameter{
+               {
+                       Name:       ParentConfigParamQStringHandling,
+                       ConfigFile: "parent.config",
+                       Value:      "myQStringHandlingParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigRetryKeysDefault.Algorithm,
+                       ConfigFile: "parent.config",
+                       Value:      tc.AlgorithmConsistentHash,
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+               {
+                       Name:       ParentConfigParamQString,
+                       ConfigFile: "parent.config",
+                       Value:      "myQstringParam",
+                       Profiles:   []byte(`["serverprofile"]`),
+               },
+       }
+
+       // Create set of DS params
+       params := map[string]string{
+               ParentConfigRetryKeysDefault.Algorithm: "strict",
+               ParentConfigRetryKeysMSO.Algorithm:     "strict",
+               ParentConfigRetryKeysFirst.Algorithm:   "true",
+               ParentConfigRetryKeysInner.Algorithm:   "latched",
+               ParentConfigRetryKeysLast.Algorithm:    "true",
+
+               ParentConfigRetryKeysDefault.SecondaryMode: "exhaust",
+               ParentConfigRetryKeysMSO.SecondaryMode:     "exhaust",
+               ParentConfigRetryKeysFirst.SecondaryMode:   "alternate",
+               ParentConfigRetryKeysInner.SecondaryMode:   "peering",
+               ParentConfigRetryKeysLast.SecondaryMode:    "alternate",
+
+               ParentConfigRetryKeysMSO.ParentRetry:   
"unavailable_server_retry",
+               ParentConfigRetryKeysFirst.ParentRetry: "both",
+               ParentConfigRetryKeysInner.ParentRetry: "both",
+               ParentConfigRetryKeysLast.ParentRetry:  "both",
+
+               ParentConfigRetryKeysDefault.MaxSimpleRetries: "11",
+               ParentConfigRetryKeysMSO.MaxSimpleRetries:     "11",
+               ParentConfigRetryKeysFirst.MaxSimpleRetries:   "12",
+               ParentConfigRetryKeysInner.MaxSimpleRetries:   "13",
+               ParentConfigRetryKeysLast.MaxSimpleRetries:    "14",
+
+               ParentConfigRetryKeysDefault.SimpleRetryResponses: `"401"`,
+               ParentConfigRetryKeysMSO.SimpleRetryResponses:     `"401"`,
+               ParentConfigRetryKeysFirst.SimpleRetryResponses:   `"401,402"`,
+               ParentConfigRetryKeysInner.SimpleRetryResponses:   `"401,403"`,
+               ParentConfigRetryKeysLast.SimpleRetryResponses:    `"401,404"`,
+
+               ParentConfigRetryKeysDefault.MaxUnavailableRetries: "21",
+               ParentConfigRetryKeysMSO.MaxUnavailableRetries:     "21",
+               ParentConfigRetryKeysFirst.MaxUnavailableRetries:   "22",
+               ParentConfigRetryKeysInner.MaxUnavailableRetries:   "23",
+               ParentConfigRetryKeysLast.MaxUnavailableRetries:    "24",
+
+               ParentConfigRetryKeysDefault.UnavailableRetryResponses: `"501"`,
+               ParentConfigRetryKeysMSO.UnavailableRetryResponses:     `"501"`,
+               ParentConfigRetryKeysFirst.UnavailableRetryResponses:   
`"501,502"`,
+               ParentConfigRetryKeysInner.UnavailableRetryResponses:   
`"501,503"`,
+               ParentConfigRetryKeysLast.UnavailableRetryResponses:    
`"501,504"`,
+       }
+
+       // Assign them to the profile
+       for key, val := range params {
+               tcparam := tc.Parameter{
+                       Name:       key,
+                       ConfigFile: "parent.config",
+                       Value:      val,
+                       Profiles:   []byte(`["ds0Profile"]`),
+               }
+               parentConfigParams = append(parentConfigParams, tcparam)
+       }
+
+       serverParams := []tc.Parameter{
+               {
+                       Name:       "trafficserver",
+                       ConfigFile: "package",
+                       Value:      "9",
+                       Profiles:   []byte(`["global"]`),
+               },
+       }
+
+       edge := makeTestParentServer()
+       edge.Cachegroup = util.StrPtr("edgeCG")
+       edge.CachegroupID = util.IntPtr(400)
+
+       mid0 := makeTestParentServer()
+       mid0.Cachegroup = util.StrPtr("midCG0")
+       mid0.CachegroupID = util.IntPtr(500)
+       mid0.HostName = util.StrPtr("mymid0")
+       mid0.ID = util.IntPtr(45)
+       setIP(mid0, "192.168.2.2")
+
+       mid1 := makeTestParentServer()
+       mid1.Cachegroup = util.StrPtr("midCG1")
+       mid1.CachegroupID = util.IntPtr(501)
+       mid1.HostName = util.StrPtr("mymid1")
+       mid1.ID = util.IntPtr(46)
+       setIP(mid1, "192.168.2.3")
+
+       opl0 := makeTestParentServer()
+       opl0.Cachegroup = util.StrPtr("oplCG0")
+       opl0.CachegroupID = util.IntPtr(502)
+       opl0.HostName = util.StrPtr("myopl0")
+       opl0.ID = util.IntPtr(46)
+       setIP(opl0, "192.168.2.4")
+
+       opl1 := makeTestParentServer()
+       opl1.Cachegroup = util.StrPtr("oplCG1")
+       opl1.CachegroupID = util.IntPtr(503)
+       opl1.HostName = util.StrPtr("myopl1")
+       opl1.ID = util.IntPtr(47)
+       setIP(opl1, "192.168.2.5")
+
+       org0 := makeTestParentServer()
+       org0.Cachegroup = util.StrPtr("orgCG0")
+       org0.CachegroupID = util.IntPtr(504)
+       org0.HostName = util.StrPtr("myorg0")
+       org0.ID = util.IntPtr(48)
+       setIP(org0, "192.168.2.6")
+       org0.Type = tc.OriginTypeName
+       org0.TypeID = util.IntPtr(991)
+
+       org1 := makeTestParentServer()
+       org1.Cachegroup = util.StrPtr("orgCG1")
+       org1.CachegroupID = util.IntPtr(505)
+       org1.HostName = util.StrPtr("myorg1")
+       org1.ID = util.IntPtr(49)
+       setIP(org1, "192.168.2.7")
+       org1.Type = tc.OriginTypeName
+       org1.TypeID = util.IntPtr(991)
+
+       servers := []Server{*edge, *mid0, *mid1, *opl0, *opl1, *org0, *org1}
+
+       topologies := []tc.Topology{
+               {
+                       Name: "t0",
+                       Nodes: []tc.TopologyNode{
+                               {
+                                       Cachegroup: "edgeCG",
+                                       Parents:    []int{1, 2},
+                               },
+                               {
+                                       Cachegroup: "midCG0",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "midCG1",
+                                       Parents:    []int{3, 4},
+                               },
+                               {
+                                       Cachegroup: "oplCG0",
+                                       Parents:    []int{5, 6},
+                               },
+                               {
+                                       Cachegroup: "oplCG1",
+                                       Parents:    []int{5, 6},
+                               },
+                               {
+                                       Cachegroup: "orgCG0",
+                               },
+                               {
+                                       Cachegroup: "orgCG1",
+                               },
+                       },
+               },
+       }
+
+       serverCapabilities := map[int]map[ServerCapability]struct{}{}
+       dsRequiredCapabilities := map[int]map[ServerCapability]struct{}{}
+
+       eCG := &tc.CacheGroupNullable{}
+       eCG.Name = edge.Cachegroup
+       eCG.ID = edge.CachegroupID
+       eCG.ParentName = mid0.Cachegroup
+       eCG.ParentCachegroupID = mid0.CachegroupID
+       eCG.SecondaryParentName = mid1.Cachegroup
+       eCG.SecondaryParentCachegroupID = mid1.CachegroupID
+       eCGType := tc.CacheGroupEdgeTypeName
+       eCG.Type = &eCGType
+
+       mCG0 := &tc.CacheGroupNullable{}
+       mCG0.Name = mid0.Cachegroup
+       mCG0.ID = mid0.CachegroupID
+       mCG0.ParentName = opl0.Cachegroup
+       mCG0.ParentCachegroupID = opl0.CachegroupID
+       mCG0.SecondaryParentName = opl1.Cachegroup
+       mCG0.SecondaryParentCachegroupID = opl1.CachegroupID
+       mCGType0 := tc.CacheGroupMidTypeName
+       mCG0.Type = &mCGType0
+
+       mCG1 := &tc.CacheGroupNullable{}
+       mCG1.Name = mid1.Cachegroup
+       mCG1.ID = mid1.CachegroupID
+       mCG1.ParentName = opl1.Cachegroup
+       mCG1.ParentCachegroupID = opl1.CachegroupID
+       mCG1.SecondaryParentName = opl0.Cachegroup
+       mCG1.SecondaryParentCachegroupID = opl0.CachegroupID
+       mCGType1 := tc.CacheGroupMidTypeName
+       mCG1.Type = &mCGType1
+
+       oplCG0 := &tc.CacheGroupNullable{}
+       oplCG0.Name = opl0.Cachegroup
+       oplCG0.ID = opl0.CachegroupID
+       oplCG0.ParentName = org0.Cachegroup
+       oplCG0.ParentCachegroupID = org0.CachegroupID
+       oplCG0.SecondaryParentName = org1.Cachegroup
+       oplCG0.SecondaryParentCachegroupID = org1.CachegroupID
+       oplCGType0 := tc.CacheGroupMidTypeName
+       oplCG0.Type = &oplCGType0
+
+       oplCG1 := &tc.CacheGroupNullable{}
+       oplCG1.Name = opl1.Cachegroup
+       oplCG1.ID = opl1.CachegroupID
+       oplCG1.ParentName = org1.Cachegroup
+       oplCG1.ParentCachegroupID = org1.CachegroupID
+       oplCG1.SecondaryParentName = org0.Cachegroup
+       oplCG1.SecondaryParentCachegroupID = org0.CachegroupID
+       oplCGType1 := tc.CacheGroupMidTypeName
+       oplCG1.Type = &oplCGType1
+
+       oCG0 := &tc.CacheGroupNullable{}
+       oCG0.Name = org0.Cachegroup
+       oCG0.ID = org0.CachegroupID
+       oCGType0 := tc.CacheGroupOriginTypeName
+       oCG0.Type = &oCGType0
+
+       oCG1 := &tc.CacheGroupNullable{}
+       oCG1.Name = org1.Cachegroup
+       oCG1.ID = org1.CachegroupID
+       oCGType1 := tc.CacheGroupOriginTypeName
+       oCG1.Type = &oCGType1
+
+       cgs := []tc.CacheGroupNullable{*eCG, *mCG0, *mCG1, *oplCG0, *oplCG1, 
*oCG0, *oCG1}
+
+       dss := []DeliveryServiceServer{
+               {Server: *org0.ID, DeliveryService: *ds0.ID},
+               {Server: *org1.ID, DeliveryService: *ds0.ID},
+
+               {Server: *edge.ID, DeliveryService: *ds1.ID},
+               {Server: *mid0.ID, DeliveryService: *ds1.ID},
+               {Server: *mid1.ID, DeliveryService: *ds1.ID},
+               {Server: *opl0.ID, DeliveryService: *ds1.ID},
+               {Server: *opl1.ID, DeliveryService: *ds1.ID},
+               {Server: *org0.ID, DeliveryService: *ds1.ID},
+               {Server: *org1.ID, DeliveryService: *ds1.ID},
+       }
+       cdn := &tc.CDN{
+               DomainName: "cdndomain.example",
+               Name:       "my-cdn-name",
+       }
+
+       // edge config
+       for _, ds := range dsesall {
+               dses := []DeliveryService{ds}
+               cfg, err := MakeStrategiesDotYAML(dses, edge, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, opt)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, opt.HdrComment)
+
+               needs := []string{
+                       ` policy: consistent_hash`,
+                       ` go_direct: false`,
+                       ` max_simple_retries: 12`,
+                       ` max_unavailable_retries: 22`,
+                       ` response_codes: [ 401, 402 ]`,
+                       ` markdown_codes: [ 501, 502 ]`,
+               }
+
+               missing := missingFrom(txt, needs)
+               if 0 < len(missing) {
+                       t.Errorf("Missing required string(s) from line: 
%v\n%v", missing, txt)
+               }
+       }
+
+       // test mid config
+       for _, ds := range dsesall {
+               dses := []DeliveryService{ds}
+               cfg, err := MakeStrategiesDotYAML(dses, mid0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, opt)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, opt.HdrComment)
+
+               needs := []string{
+                       ` policy: consistent_hash`,
+                       ` go_direct: false`,
+                       ` max_simple_retries: 13`,
+                       ` max_unavailable_retries: 23`,
+                       ` response_codes: [ 401, 403 ]`,
+                       ` markdown_codes: [ 501, 503 ]`,
+               }
+
+               missing := missingFrom(txt, needs)
+               if 0 < len(missing) {
+                       t.Errorf("Missing required string(s) from ds/line: 
%s/%v\n%v", *ds.XMLID, missing, txt)
+               }
+       }
+
+       // test opl config
+       for _, ds := range dsesall {
+               dses := []DeliveryService{ds}
+               cfg, err := MakeStrategiesDotYAML(dses, opl0, servers, 
topologies, serverParams, parentConfigParams, serverCapabilities, 
dsRequiredCapabilities, cgs, dss, cdn, opt)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               txt := cfg.Text
+
+               testComment(t, txt, opt.HdrComment)
+
+               needs := []string{
+                       ` policy: rr_ip`,
+                       ` go_direct: true`,
+                       ` max_simple_retries: 14`,
+                       ` max_unavailable_retries: 24`,
+                       ` response_codes: [ 401, 404 ]`,
+                       ` markdown_codes: [ 501, 504 ]`,
+               }
+
+               missing := missingFrom(txt, needs)
+               if 0 < len(missing) {
+                       t.Errorf("Missing required string(s) from line: 
%v\n%v", missing, txt)
+               }
+       }
+}

Reply via email to