You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2021/02/02 18:10:58 UTC

[trafficcontrol] branch master updated: Add ort integration testing. (#5298)

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

ocket8888 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 9902027  Add ort integration testing. (#5298)
9902027 is described below

commit 9902027971323ce3ce3f9aa3aad543a072cb2caa
Author: John J. Rushford <jr...@apache.org>
AuthorDate: Tue Feb 2 11:10:44 2021 -0700

    Add ort integration testing. (#5298)
---
 LICENSE                                            |    4 +-
 traffic_ops_ort/testing/README.md                  |  102 +
 traffic_ops_ort/testing/docker/db_init/Dockerfile  |   42 +
 traffic_ops_ort/testing/docker/db_init/dbInit.sh   |   30 +
 traffic_ops_ort/testing/docker/docker-compose.yml  |  101 +
 traffic_ops_ort/testing/docker/ort_test/Dockerfile |   52 +
 .../testing/docker/ort_test/Ort-test.repo          |   22 +
 traffic_ops_ort/testing/docker/ort_test/run.sh     |   82 +
 .../testing/docker/ort_test/systemctl.sh           |   53 +
 .../testing/docker/traffic_ops/Dockerfile          |   62 +
 .../docker/traffic_ops/profile.origin.traffic_ops  |   18 +
 traffic_ops_ort/testing/docker/traffic_ops/run.sh  |  278 ++
 .../testing/docker/traffic_vault/Dockerfile        |   63 +
 .../testing/docker/traffic_vault/functions         |  707 +++
 .../testing/docker/traffic_vault/run.sh            |  126 +
 .../testing/docker/traffic_vault/sslkeys.xml       |   55 +
 traffic_ops_ort/testing/docker/variables.env       |   37 +
 .../testing/docker/yumserver/Dockerfile            |   33 +
 traffic_ops_ort/testing/docker/yumserver/run.sh    |   24 +
 .../testing/docker/yumserver/test-rpms/README.md   |   23 +
 .../ort-tests/baseline-configs/astats.config       |    5 +
 .../baseline-configs/hdr_rw_first_ds-top.config    |    2 +
 .../ort-tests/baseline-configs/hosting.config      |    2 +
 .../ort-tests/baseline-configs/parent.config       |    5 +
 .../ort-tests/baseline-configs/records.config      |   23 +
 .../ort-tests/baseline-configs/remap.config        |    3 +
 .../ort-tests/baseline-configs/storage.config      |    2 +
 .../ort-tests/baseline-configs/volume.config       |    3 +
 .../testing/ort-tests/conf/docker-edge-cache.conf  |   38 +
 .../testing/ort-tests/conf/edge-host.conf          |   38 +
 traffic_ops_ort/testing/ort-tests/config/config.go |  283 ++
 traffic_ops_ort/testing/ort-tests/t3c_mode_test.go |  159 +
 traffic_ops_ort/testing/ort-tests/tc-fixtures.json | 4664 ++++++++++++++++++++
 .../testing/ort-tests/tcdata/cachegroups.go        |  116 +
 .../ort-tests/tcdata/cachegroups_parameters.go     |  103 +
 .../tcdata/cachegroupsdeliveryservices.go          |  153 +
 .../testing/ort-tests/tcdata/cdnfederations.go     |   67 +
 traffic_ops_ort/testing/ort-tests/tcdata/cdns.go   |   60 +
 .../testing/ort-tests/tcdata/coordinates.go        |   55 +
 .../tcdata/deliveryservice_request_comments.go     |   66 +
 .../ort-tests/tcdata/deliveryservice_requests.go   |   64 +
 .../testing/ort-tests/tcdata/deliveryservices.go   |   90 +
 .../deliveryservices_required_capabilities.go      |  212 +
 .../ort-tests/tcdata/deliveryservicesregexes.go    |  106 +
 .../testing/ort-tests/tcdata/divisions.go          |   56 +
 .../ort-tests/tcdata/federation_resolvers.go       |  118 +
 .../testing/ort-tests/tcdata/federation_users.go   |  165 +
 .../testing/ort-tests/tcdata/fixtures.go           |   43 +
 .../testing/ort-tests/tcdata/origins.go            |   56 +
 .../testing/ort-tests/tcdata/parameters.go         |   89 +
 .../testing/ort-tests/tcdata/phys_locations.go     |   59 +
 .../testing/ort-tests/tcdata/profile_parameters.go |  104 +
 .../testing/ort-tests/tcdata/profiles.go           |  142 +
 .../testing/ort-tests/tcdata/regions.go            |   89 +
 traffic_ops_ort/testing/ort-tests/tcdata/roles.go  |   77 +
 .../testing/ort-tests/tcdata/servercapabilities.go |   50 +
 .../ort-tests/tcdata/servercheckextension.go       |  125 +
 .../testing/ort-tests/tcdata/serverchecks.go       |   85 +
 .../testing/ort-tests/tcdata/servers.go            |  143 +
 .../ort-tests/tcdata/serverservercapability.go     |  286 ++
 .../testing/ort-tests/tcdata/servicecategories.go  |   61 +
 .../testing/ort-tests/tcdata/session.go            |   55 +
 .../testing/ort-tests/tcdata/staticdnsentries.go   |   59 +
 .../testing/ort-tests/tcdata/statuses.go           |   62 +
 .../testing/ort-tests/tcdata/steeringtargets.go    |  145 +
 traffic_ops_ort/testing/ort-tests/tcdata/tcdata.go |   74 +
 .../testing/ort-tests/tcdata/tenants.go            |   91 +
 traffic_ops_ort/testing/ort-tests/tcdata/todb.go   |  376 ++
 .../testing/ort-tests/tcdata/topologies.go         |   77 +
 traffic_ops_ort/testing/ort-tests/tcdata/types.go  |  101 +
 traffic_ops_ort/testing/ort-tests/tcdata/user.go   |   90 +
 .../testing/ort-tests/tcdata/withobjs.go           |  115 +
 .../testing/ort-tests/traffic_ops_ort_test.go      |  117 +
 traffic_ops_ort/testing/ort-tests/util/util.go     |   95 +
 .../github.com/kelseyhightower/envconfig/LICENSE   |    0
 .../kelseyhightower/envconfig/MAINTAINERS          |    0
 .../github.com/kelseyhightower/envconfig/README.md |    0
 .../github.com/kelseyhightower/envconfig/doc.go    |    0
 .../github.com/kelseyhightower/envconfig/env_os.go |    0
 .../kelseyhightower/envconfig/env_syscall.go       |    0
 .../kelseyhightower/envconfig/envconfig.go         |    0
 .../envconfig/envconfig_1.8_test.go                |    0
 .../kelseyhightower/envconfig/envconfig_test.go    |    0
 .../kelseyhightower/envconfig/testdata/custom.txt  |    0
 .../envconfig/testdata/default_list.txt            |    0
 .../envconfig/testdata/default_table.txt           |    0
 .../kelseyhightower/envconfig/testdata/fault.txt   |    0
 .../github.com/kelseyhightower/envconfig/usage.go  |    0
 .../kelseyhightower/envconfig/usage_test.go        |    0
 89 files changed, 11436 insertions(+), 2 deletions(-)

diff --git a/LICENSE b/LICENSE
index ebfc84d..7ae9bb8 100644
--- a/LICENSE
+++ b/LICENSE
@@ -382,8 +382,8 @@ The riak-go-client component is used under the Apache license:
 ./vendor/github.com/basho/riak-go-client/LICENSE
 
 The envconfig component is used under the MIT license:
-@traffic_ops/vendor/github.com/kelseyhightower/envconfig/*
-./traffic_ops/vendor/github.com/kelseyhightower/envconfig/LICENSE
+@vendor/github.com/kelseyhightower/envconfig/*
+./vendor/github.com/kelseyhightower/envconfig/LICENSE
 
 The govalidator component is used under the MIT license:
 @vendor/github.com/asaskevich/govalidator/*
diff --git a/traffic_ops_ort/testing/README.md b/traffic_ops_ort/testing/README.md
new file mode 100644
index 0000000..6d3bc16
--- /dev/null
+++ b/traffic_ops_ort/testing/README.md
@@ -0,0 +1,102 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+# Traffic Ops ORT Tests
+
+The ORT tests are used to validate the ORT tools used with a
+release of Traffic Ops.  The tests ensure that the ORT tools
+are able to communicate with Traffic Ops, install required
+packages and generate the correct ATS configuration files on
+a Trafficserver cache.
+
+The first thing you need do is to provide the Traffic Ops and
+Traffic Ops ORT RPM files for the build you wish to test using 
+this framework.  This test environment provides the necessary
+Docker containers used to support and execute the tests when
+given the necessary RPM's.  If you choose to not use the provided
+Docker containers you will need to provide the following resources:
+
+  - A running Traffic Ops with the installed release to be tested
+  - A running Postgres SQL database which is loaded with the proper
+    test data for the Traffic Ops release.
+  - An Apache Traffic Server host that has the installed release of
+    ORT to be tested against the release of Traffic Ops.
+  - A yum server configured to provide the test rpm's herein.
+  - A Traffic vault server.
+
+# Directory layout
+
+  - trafficcontrol/traffic_ops_ort/testing/docker:  has all the 
+    necessary files for running the test Docker containers.
+  - trafficcontrol/traffic_ops_ort/testing/ort-tests:  this directory.
+    contains all the go files used to run the ORT tests.
+
+# Setup.
+
+  1.  Build the Traffic Ops and Traffic Ops ORT RPM's that you wish
+      to test.  See the top level 'build' directory for building 
+      instructions. 
+  2.  Copy the Traffic Ops RPM to docker/trafffic_ops/traffic_ops.rpm 
+      (NOTE:  Use the file name 'traffic_ops.rpm')
+  3.  Copy the Traffic Ops ORT rpm to docker/ort_test/traffic_ops_ort.rpm
+      (NOTE:  Use the file name 'traffic_ops_ort.rpm'
+  4.  Copy the recent copy of riak-2.2.3-1.el7.centos.x86_64.rpm to
+      docker/traffic_vault/riak-2.2.3-1.el7.centos.x86_64.rpm
+  5.  Copy an Apache Trafficserver rpm to 
+      docker/yumserver/test-rpms/trafficserver-$VERSION.$COMMIT-HASH.el7.x86_64.rpm
+      You will need to edit and adjust the trafficserver package value in
+      ort-tests/tc-fixtures.json to match the $VERSION.$COMMIT-HASH used in the name
+      of your RPM.  Search for '8.0.8-19.77cb23a' in the ort-tests/tc-fixtures.json 
+      and change the value to match the RPM version you choose to use.
+      For example, the current value in tc-fixtures.json is '8.0.8-19.77cb23a' and 
+      therefore the rpm file expected in 'docker/yumserver/test-rpms' is
+      'trafficserver-8.0.8-19.77cb23a.el7.x86_64.rpm'.
+  6.  The container Docker files have the usernames and passwords used in the various
+      containers ie, postgresql db, traffic_ops, and traffic_ops_ort.  The usernames
+      and passwords passed to the 't3c' executable in in the 
+      ort-tests/conf/docker-edge-cache.conf file.  Make sure that the usernames/passwords
+      in the Docker files match those in the t3c configuration file.
+      An example ort-tests/conf/edge-cache.conf file is provided should you choose to
+      use your own Traffic Ops and Postgresql environment.
+  7.  Build the Docker images and run the ort test:
+      ``` 
+      cd trafficcontrol/traffic_ops_ort/testing/docker
+      docker-compose build
+      docker-compose run ort_test
+      ```
+      After some time, test results should be available at
+      'ort-tests/test.log'
+  
+  If you wish to run the tests manually use 'docker ps' to obtain the container id for
+  the ort_test host and then:
+
+  ```
+     docker exec -it $ort_test /bin/bash -l
+     cd /ort-tests
+     go test -cfg=conf/docker-edge-cache.conf
+  ```
+
+  If you wish to run the tests manually using your own environment, create a config
+  file with the necessary login information in the 'conf' directory and then rerun
+  the tests using your config file.  WARNING: the traffic ops database will be dropped
+  and initialized using the data in tc-fixtures.json and then the tests are run.  
+  DO NOT USE a production Traffic Ops database with these test scripts.
+
+
+ 
diff --git a/traffic_ops_ort/testing/docker/db_init/Dockerfile b/traffic_ops_ort/testing/docker/db_init/Dockerfile
new file mode 100644
index 0000000..fdc1da6
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/db_init/Dockerfile
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+############################################################
+# Dockerfile to initialized Traffic Ops Database container 
+# Based on CentOS 7.2
+############################################################
+
+FROM centos/systemd
+
+RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
+
+RUN yum -y install \
+  postgresql96 \
+  nmap-ncat \
+  cpanminus && \
+  yum clean all
+
+ENV POSTGRES_HOME $POSTGRES_HOME
+ENV PGPASSWORD $PGPASSWORD 
+ENV DB_USERNAME $DB_USERNAME
+ENV DB_NAME $DB_NAME
+ENV DB_USER_PASS $DB_USER_PASS 
+ENV DB_SERVER $DB_SERVER
+ENV DB_PORT $DB_PORT
+
+ADD db_init/dbInit.sh /
+CMD /dbInit.sh
diff --git a/traffic_ops_ort/testing/docker/db_init/dbInit.sh b/traffic_ops_ort/testing/docker/db_init/dbInit.sh
new file mode 100755
index 0000000..f4392d4
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/db_init/dbInit.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+############################################################
+# Script for creating the database user account for traffic
+# ops. 
+# Used while the Docker Image is initializing itself
+############################################################
+
+while ! nc $DB_SERVER $DB_PORT </dev/null; do # &>/dev/null; do
+        echo "waiting for $DB_SERVER:$DB_PORT"
+        sleep 3
+done
+psql -h $DB_SERVER -U postgres -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_USER_PASS'"
+createdb $DB_NAME -h $DB_SERVER -U postgres --owner $DB_USER
diff --git a/traffic_ops_ort/testing/docker/docker-compose.yml b/traffic_ops_ort/testing/docker/docker-compose.yml
new file mode 100644
index 0000000..d744f68
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/docker-compose.yml
@@ -0,0 +1,101 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# Build trafficcontrol:  
+#   Copy the traffic_ops rpm to traffic_ops/traffic_ops.rpm
+#   Copy the traffic_ops_ort rpm to ort_test/traffic_ops_ort.rpm
+#   Copy an ATS rpm to yumserver/test-rpms and update the 
+#     ../ort-tests/tc-fixtures.json to match the rpm version
+#     string you've chosen
+#   Copy the riak-2.2.3-1.el7.centos.x86_64.rpm to traffic_vault/
+#
+#   Run: docker-compose build
+#   Run: docker-compose run ort_test
+#
+
+---
+version: '2'
+
+volumes:
+  trafficcontrol:
+  traffic_ops:
+  conf:
+
+services:
+  db:
+    image: postgres:9.6.6
+    ports: 
+      - "5432:5432"
+
+  db_init:
+    env_file:
+      - variables.env
+    build:
+      context: .
+      dockerfile: db_init/Dockerfile
+    depends_on: 
+      - db
+
+  to_server:
+    env_file:
+      - variables.env
+    ports: 
+      - "443:443"
+    build:
+      context: .
+      dockerfile: traffic_ops/Dockerfile
+      args:
+        RPM: traffic_ops.rpm 
+    volumes:
+      - ../../../GO_VERSION:/GO_VERSION
+    depends_on:
+      - db_init
+
+  traffic_vault:
+    env_file:
+      - variables.env
+    ports:
+      - "8087:8087"
+      - "8088:8088"
+      - "8098:8098"
+    build:
+      context: .
+      dockerfile: traffic_vault/Dockerfile
+    depends_on:
+      - to_server
+
+  ort_test:
+    env_file:
+      - variables.env
+    build:
+      context: .
+      dockerfile: ort_test/Dockerfile
+    depends_on:
+      - yumserver
+      - to_server
+      - traffic_vault
+    volumes:
+      - ../../..:/root/go/src/github.com/apache/trafficcontrol
+
+  yumserver:
+    build:
+      context: .
+      dockerfile: yumserver/Dockerfile
+    depends_on:
+      - db_init
+    volumes:
+      - ./yumserver/test-rpms:/var/www/html/traffic-control/7/x86_64/Packages
diff --git a/traffic_ops_ort/testing/docker/ort_test/Dockerfile b/traffic_ops_ort/testing/docker/ort_test/Dockerfile
new file mode 100644
index 0000000..3704503
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/ort_test/Dockerfile
@@ -0,0 +1,52 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+############################################################
+# Dockerfile to build Traffic Server container images
+#   as Edges for Traffic Control 1.4
+# Based on CentOS 6.6
+############################################################
+
+# For cache, you may either use (RAM or disk) block devices or disk directories
+# To use RAM block devices, pass them as /dev/ram0 and /dev/ram1 via `docker run --device`
+# To use disk directories, simply don't pass devices, and the container will configure Traffic Server for directories
+
+# Block devices may be created on the native machine with, for example, `modprobe brd`.
+# The recommended minimum size for each block devices is 1G.
+# For example, `sudo modprobe brd rd_size=1048576 rd_nr=2`
+
+FROM centos:7
+MAINTAINER dev@trafficcontrol.apache.org
+
+RUN yum install -y \
+  https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm \
+  epel-release initscripts postgresql96.x86_64 git gcc lua-5.1.4-15.el7 lua-devel-5.1.4-15.el7 \
+  ImageMagick-c++-devel
+
+ARG RPM=traffic_ops_ort.rpm
+ADD ort_test/$RPM /
+RUN yum install -y /$(basename $RPM)
+
+RUN sed -i 's/HOME\/bin/HOME\/bin:\/usr\/local\/go\/bin:/g' /root/.bash_profile
+RUN echo "GOPATH=/root/go; export GOPATH" >> /root/.bash_profile
+RUN mkdir /root/go
+
+EXPOSE 80 443
+ADD ort_test/run.sh /
+ADD ort_test/Ort-test.repo /etc/yum.repos.d
+ADD ort_test/systemctl.sh /
+
+ENTRYPOINT /run.sh
diff --git a/traffic_ops_ort/testing/docker/ort_test/Ort-test.repo b/traffic_ops_ort/testing/docker/ort_test/Ort-test.repo
new file mode 100644
index 0000000..ef416ef
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/ort_test/Ort-test.repo
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+[traffic-control]
+name=traffic-control
+enabled=1
+gpgcheck=0
+baseurl=http://yumserver/traffic-control/7/x86_64/
diff --git a/traffic_ops_ort/testing/docker/ort_test/run.sh b/traffic_ops_ort/testing/docker/ort_test/run.sh
new file mode 100755
index 0000000..a87065d
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/ort_test/run.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# wait period to insure all containers are up and running.
+WAIT="60"
+
+#
+# this seems to wake up the to container.
+#
+function ping_to {
+  /opt/ort/t3c \
+		"--traffic-ops-insecure=true" \
+		"--traffic-ops-timeout-milliseconds=3000" \
+		"--traffic-ops-user=$TO_ADMIN_USER" \
+		"--traffic-ops-password=$TO_ADMIN_PASS" \
+		"--traffic-ops-url=$TO_URI" \
+		"--cache-host-name=atlanta-edge-03" \
+		"--log-location-error=stderr" \
+		"--log-location-info=stderr" \
+		"--log-location-debug=stderr" \
+		"--run-mode=badass" 
+}
+
+set -x
+GOPATH=/root/go; export GOPATH
+PATH=$PATH:/usr/local/go/bin:; export PATH
+TERM=xterm; export TERM
+
+# setup some convienient links
+/bin/ln -s /root/go/src/github.com/apache/trafficcontrol /trafficcontrol
+/bin/ln -s /trafficcontrol/traffic_ops_ort/testing/ort-tests /ort-tests
+
+if [ -f /trafficcontrol/GO_VERSION ]; then
+  go_version=$(cat /trafficcontrol/GO_VERSION) && \
+      curl -Lo go.tar.gz https://dl.google.com/go/go${go_version}.linux-amd64.tar.gz && \
+        tar -C /usr/local -xvzf go.tar.gz && \
+        ln -s /usr/local/go/bin/go /usr/bin/go && \
+        rm go.tar.gz
+else
+  echo "no GO_VERSION file, unable to install go"
+  exit 0
+fi
+
+# fetch dependent packages for tests
+go get golang.org/x/crypto/scrypt
+go get golang.org/x/net/publicsuffix
+
+if [ -f /systemctl.sh ]; then
+  mv /bin/systemctl /bin/systemctl.save
+  cp /systemctl.sh /bin/systemctl
+  chmod 0755 /bin/systemctl
+fi
+
+cd /ort-tests
+echo "Sleeping for $WAIT seconds to ensure all containers have initialized" >> test.log
+sleep $WAIT
+echo "Running tests" >> test.log
+
+# wake up the to_server
+ping_to
+sleep 2
+
+(touch test.log && tail -f test.log)&
+go test -cfg=conf/docker-edge-cache.conf 2>&1 >> test.log
+
diff --git a/traffic_ops_ort/testing/docker/ort_test/systemctl.sh b/traffic_ops_ort/testing/docker/ort_test/systemctl.sh
new file mode 100755
index 0000000..c4b8720
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/ort_test/systemctl.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# This is a work around for testing t3c which uses systemctl.
+# systemctl does not work in a container very well so this script
+# replaces systemctl in the container and always returns a 
+# sucessful result to t3c.
+
+USAGE="\nsystemctl COMMAND NAME\n"
+
+if [ -z $1 ] || [ -z $2 ]; then
+  echo -e $USAGE
+  exit 0
+else
+  COMMAND=$1
+  NAME=$2
+fi
+
+if [ "$2" != "trafficserver" ]; then
+  echo -e "\nFailed to start ${NAME}.service: Unit not found.n"
+  exit 0
+fi
+
+case $COMMAND in 
+  enable)
+    ;;
+  restart)
+    ;;
+  status)
+    ;;
+  start)
+    ;;
+  stop)
+    ;;
+esac
+
+exit 0
diff --git a/traffic_ops_ort/testing/docker/traffic_ops/Dockerfile b/traffic_ops_ort/testing/docker/traffic_ops/Dockerfile
new file mode 100644
index 0000000..5780b1c
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_ops/Dockerfile
@@ -0,0 +1,62 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+############################################################
+# Dockerfile to build Traffic Ops container images
+# Based on CentOS 7.2
+############################################################
+
+# Example Build and Run:
+# docker network create cdnet
+# docker build --rm --tag traffic_ops:1.7.0 --build-arg=RPM=http://traffic-control-cdn.net/downloads/1.7.0/RELEASE-1.7.0/traffic_ops-1.7.0-3908.5b77f60f.x86_64.rpm traffic_ops
+#
+# docker run --name my-traffic-ops-mysql --hostname my-traffic-ops-mysql --net cdnet --env MYSQL_ROOT_PASSWORD=secretrootpass --detach mysql:5.5
+#
+# docker run --name my-traffic-ops --hostname my-traffic-ops --net cdnet --publish 443:443 --env MYSQL_IP=my-traffic-ops-mysql --env MYSQL_PORT=3306 --env MYSQL_ROOT_PASS=secretrootpass --env MYSQL_TRAFFIC_OPS_PASS=supersecretpassword --env ADMIN_USER=superroot --env ADMIN_PASS=supersecreterpassward --env CERT_COUNTRY=US --env CERT_STATE=Colorado --env CERT_CITY=Denver --env CERT_COMPANY=NotComcast --env TRAFFIC_VAULT_PASS=marginallylesssecret --env DOMAIN=cdnet --detach traffic_ops:1.5.1
+
+FROM centos/systemd
+MAINTAINER dev@trafficcontrol.apache.org
+
+RUN yum install -y \
+  https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm \
+  initscripts epel-release perl-Crypt-ScryptKDF perl cpanminus perl-Test-CPAN-Meta perl-DBIx-Connector
+
+RUN cpanm Carton
+
+# Override RPM arg to use a different one using --build-arg RPM=...  Can be local file or http://...
+ARG RPM=traffic_ops.rpm
+ADD traffic_ops/$RPM /
+RUN yum install -y /$(basename $RPM)
+
+# once installed, remove rpm to lower image size
+RUN rm /$(basename $RPM)
+
+RUN POSTGRES_HOME=/usr/pgsql-9.6 cd /opt/traffic_ops/app && carton
+
+RUN export PERL5LIB=/opt/traffic_ops/app/local/lib/perl5/:/opt/traffic_ops/install/lib/ \
+	&& export TERM=xterm \
+	&& export USER=root 
+
+# fixes an 'Invalid Argument' bug; TODO diagnose    , fix, & remove
+RUN cp /opt/traffic_ops/app/bin/traffic_ops_golang{,.new} && mv /opt/traffic_ops/app/bin/traffic_ops_golang{.new,} 
+
+EXPOSE 443
+WORKDIR /opt/traffic_ops/app
+ENV MOJO_MODE production
+ADD traffic_ops/profile.origin.traffic_ops /
+ADD traffic_ops/run.sh /
+CMD /run.sh
diff --git a/traffic_ops_ort/testing/docker/traffic_ops/profile.origin.traffic_ops b/traffic_ops_ort/testing/docker/traffic_ops/profile.origin.traffic_ops
new file mode 100644
index 0000000..f655f89
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_ops/profile.origin.traffic_ops
@@ -0,0 +1,18 @@
+{
+    "parameters": [
+        {
+            "config_file": "CRConfig.json",
+            "name": "domain_name",
+            "value": "{{.Domain}}"
+        },
+        {
+            "config_file": "parent.config",
+            "name": "weight",
+            "value": "1.0"
+        }
+    ],
+    "profile": {
+        "description": "Multi site origin profile 1",
+        "name": "ORG1_CDN1"
+    }
+}
diff --git a/traffic_ops_ort/testing/docker/traffic_ops/run.sh b/traffic_ops_ort/testing/docker/traffic_ops/run.sh
new file mode 100755
index 0000000..4253dbd
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_ops/run.sh
@@ -0,0 +1,278 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Script for running the Dockerfile for Traffic Ops.
+# The Dockerfile sets up a Docker image which can be used for any new Traffic Ops container;
+# This script, which should be run when the container is run (it's the ENTRYPOINT), will configure the container.
+#
+# The following environment variables must be set, ordinarily by `docker run -e` arguments:
+# DB_SERVER
+# DB_PORT
+# DB_ROOT_PASS
+# DB_USER
+# DB_USER_PASS
+# DB_NAME
+# TO_ADMIN_USER
+# TO_ADMIN_PASS
+# CERT_COUNTRY
+# CERT_STATE
+# CERT_CITY
+# CERT_COMPANY
+# TO_DOMAIN
+# TRAFFIC_VAULT_PASS
+
+# Check that env vars are set
+envvars=( DB_SERVER DB_PORT DB_ROOT_PASS DB_USER DB_USER_PASS TO_ADMIN_USER TO_ADMIN_PASS CERT_COUNTRY CERT_STATE CERT_CITY CERT_COMPANY TO_DOMAIN)
+for v in $envvars
+do
+	if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi
+done
+
+start() {
+	service traffic_ops start
+	exec tail -f /var/log/traffic_ops/traffic_ops.log
+}
+
+init() {
+	local postinstall_input_file="postinstall-input.json"
+	cat > "$postinstall_input_file" <<- ENDOFMESSAGE
+{
+  "/opt/traffic_ops/app/conf/production/database.conf":[
+    {
+      "Database type":"Pg",
+      "config_var":"type"
+    },
+    {
+      "Database name":"$DB_NAME",
+      "config_var":"dbname"
+    },
+    {
+      "Database server hostname IP or FQDN":"$DB_SERVER",
+      "config_var":"hostname"
+    },
+    {
+      "Database port number":"$DB_PORT",
+      "config_var":"port"
+    },
+    {
+      "Traffic Ops database user":"$DB_USER",
+      "config_var":"user"
+    },
+    {
+      "Traffic Ops database password":"$DB_USER_PASS",
+      "config_var":"password",
+      "hidden":"1"
+    }
+  ],
+  "/opt/traffic_ops/app/db/dbconf.yml":[
+    {
+      "Database server root (admin) user":"postgres",
+      "config_var":"pgUser"
+    },
+    {
+      "Database server admin password":"$DB_ROOT_PASS",
+      "config_var":"pgPassword",
+      "hidden":"1"
+    },
+    {
+      "Download Maxmind Database?":"yes",
+      "config_var":"maxmind"
+    }
+  ],
+  "/opt/traffic_ops/app/conf/cdn.conf":[
+    {
+      "Generate a new secret?":"yes",
+      "config_var":"genSecret"
+    },
+    {
+      "Port to serve on?": "443",
+      "config_var": "port"
+    },
+    {
+      "Number of workers?": "12",
+      "config_var":"workers"
+    },
+    {
+      "Traffic Ops url?": "https://$TO_HOSTNAME",
+      "config_var": "base_url"
+    },
+    {
+      "Number of secrets to keep?":"1",
+      "config_var":"keepSecrets"
+    }
+  ],
+  "/opt/traffic_ops/app/conf/ldap.conf":[
+    {
+      "Do you want to set up LDAP?":"no",
+      "config_var":"setupLdap"
+    },
+    {
+      "LDAP server hostname":"",
+      "config_var":"host"
+    },
+    {
+      "LDAP Admin DN":"",
+      "config_var":"admin_dn"
+    },
+    {
+      "LDAP Admin Password":"",
+      "config_var":"admin_pass",
+      "hidden":"1"
+    },
+    {
+      "LDAP Search Base":"",
+      "config_var":"search_base"
+    }
+  ],
+  "/opt/traffic_ops/install/data/json/users.json":[
+    {
+      "Administration username for Traffic Ops":"$TO_ADMIN_USER",
+      "config_var":"tmAdminUser"
+    },
+    {
+      "Password for the admin user":"$TO_ADMIN_PASS",
+      "config_var":"tmAdminPw",
+      "hidden":"1"
+    }
+  ],
+  "/opt/traffic_ops/install/data/profiles/":[
+    {
+      "Add custom profiles?":"no",
+      "config_var":"custom_profiles"
+    }
+  ],
+  "/opt/traffic_ops/install/data/json/openssl_configuration.json":[
+    {
+      "Do you want to generate a certificate?":"yes",
+      "config_var":"genCert"
+    },
+    {
+      "Country Name (2 letter code)":"$CERT_COUNTRY",
+      "config_var":"country"
+    },
+    {
+      "State or Province Name (full name)":"$CERT_STATE",
+      "config_var":"state"
+    },
+    {
+      "Locality Name (eg, city)":"$CERT_CITY",
+      "config_var":"locality"
+    },
+    {
+      "Organization Name (eg, company)":"$CERT_COMPANY",
+      "config_var":"company"
+    },
+    {
+      "Organizational Unit Name (eg, section)":"",
+      "config_var":"org_unit"
+    },
+    {
+      "Common Name (eg, your name or your server's hostname)":"$TO_HOSTNAME",
+      "config_var":"common_name"
+    },
+    {
+      "RSA Passphrase":"passphrase",
+      "config_var":"rsaPassword",
+      "hidden":"1"
+    }
+  ],
+  "/opt/traffic_ops/install/data/json/profiles.json":[
+    {
+      "Traffic Ops url":"https://$TO_HOSTNAME",
+      "config_var":"tm.url"
+    },
+    {
+      "Human-readable CDN Name.  (No whitespace, please)":"cdn",
+      "config_var":"cdn_name"
+    },
+    {
+      "Health Polling Interval (milliseconds)":"8000",
+      "config_var":"health_polling_int"
+    },
+    {
+      "DNS sub-domain for which your CDN is authoritative":"$TO_HOSTNAME.$TO_DOMAIN",
+      "config_var":"dns_subdomain"
+    },
+    {
+      "TLD SOA admin":"traffic_ops",
+      "config_var":"soa_admin"
+    },
+    {
+      "TrafficServer Drive Prefix":"/dev/ram",
+      "config_var":"driver_prefix"
+    },
+    {
+      "TrafficServer RAM Drive Prefix":"/dev/ram",
+      "config_var":"ram_drive_prefix"
+    },
+    {
+      "TrafficServer RAM Drive Letters (comma separated)":"1",
+      "config_var":"ram_drive_letters"
+    },
+    {
+      "Health Threshold Load Average":"25",
+      "config_var":"health_thresh_load_avg"
+    },
+    {
+      "Health Threshold Available Bandwidth in Kbps":"1750000",
+      "config_var":"health_thresh_kbps"
+    },
+    {
+      "Traffic Server Health Connection Timeout (milliseconds)":"2000",
+      "config_var":"health_connect_timeout"
+    }
+  ]
+}
+ENDOFMESSAGE
+
+cat > /opt/traffic_ops/app/conf/production/riak.conf << EOM
+{
+  "user": "riakuser",
+  "password": "$RIAK_USER_PASS",
+  "MaxTLSVersion": "1.1",
+  "tlsConfig": {
+    "insecureSkipVerify": true
+  }
+}
+EOM
+
+	# TODO determine if term, user are necessary
+	export TERM=xterm && export USER=root && /opt/traffic_ops/install/bin/postinstall -cfile "$postinstall_input_file"
+
+	# Only listen on IPv4, not IPv6, because Docker doesn't provide a v6 interface by default. See http://mojolicious.org/perldoc/Mojo/Server/Daemon#listen
+	sed -i -e 's#https://\[::\]#https://127\.0\.0\.1#' /opt/traffic_ops/app/conf/cdn.conf
+	service traffic_ops restart
+
+}
+
+if [ -f /GO_VERSION ]; then
+  go_version=$(cat /GO_VERSION) && \
+      curl -Lo go.tar.gz https://dl.google.com/go/go${go_version}.linux-amd64.tar.gz && \
+        tar -C /usr/local -xvzf go.tar.gz && \
+        ln -s /usr/local/go/bin/go /usr/bin/go && \
+        rm go.tar.gz
+else
+  echo "no GO_VERSION file, unable to install go"
+  exit 0
+fi
+/opt/traffic_ops/install/bin/install_goose.sh
+
+(cd /opt/traffic_ops/app && db/admin --env=production reset)
+source /etc/environment
+if [ -z "$INITIALIZED" ]; then init; fi
+start
diff --git a/traffic_ops_ort/testing/docker/traffic_vault/Dockerfile b/traffic_ops_ort/testing/docker/traffic_vault/Dockerfile
new file mode 100644
index 0000000..754166f
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_vault/Dockerfile
@@ -0,0 +1,63 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+############################################################
+# Dockerfile to build Riak container images
+#   as Traffic Vault for Traffic Control 1.6.0
+# Based on CentOS 6.6
+############################################################
+
+FROM centos:7
+MAINTAINER dev@trafficcontrol.apache.org
+
+ARG TV_ADMIN_PASS
+
+# Install openssl
+RUN yum install -y openssl
+
+# Install the initscripts
+RUN yum -y install initscripts
+
+# Install curl which is used to configure the riak search schema.
+RUN yum -y install curl
+
+# On CentOS/RedHat/Fedora (recommended)
+RUN yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
+
+# Install Riak
+ADD traffic_vault/riak-2.2.3-1.el7.centos.x86_64.rpm /
+RUN rpm -i /riak-2.2.3-1.el7.centos.x86_64.rpm
+
+# Set the Riak certs in the config (this cert+key will be created in the run.sh script
+RUN sed -i -- 's/## ssl.certfile = $(platform_etc_dir)\/cert.pem/ssl.certfile = \/etc\/riak\/certs\/server.crt/g' /etc/riak/riak.conf
+RUN sed -i -- 's/## ssl.keyfile = $(platform_etc_dir)\/key.pem/ssl.keyfile = \/etc\/riak\/certs\/server.key/g' /etc/riak/riak.conf
+RUN sed -i -- 's/## ssl.cacertfile = $(platform_etc_dir)\/cacertfile.pem/ssl.cacertfile = \/etc\/riak\/certs\/ca-bundle.crt/g' /etc/riak/riak.conf
+
+RUN sed -i -- "s/nodename = riak@127.0.0.1/nodename = riak@0.0.0.0/g" /etc/riak/riak.conf
+RUN sed -i -- "s/listener.http.internal = 127.0.0.1:8098/listener.http.internal = 0.0.0.0:8098/g" /etc/riak/riak.conf
+RUN sed -i -- "s/listener.protobuf.internal = 127.0.0.1:8087/listener.protobuf.internal = 0.0.0.0:8087/g" /etc/riak/riak.conf
+RUN sed -i -- "s/## listener.https.internal = 127.0.0.1:8098/listener.https.internal = 0.0.0.0:8088/g" /etc/riak/riak.conf
+
+RUN sed -i -- "s/search = off/search = on/g" /etc/riak/riak.conf
+
+RUN mkdir /etc/riak/certs
+
+RUN echo "tls_protocols.tlsv1.1 = on" >> /etc/riak/riak.conf
+
+EXPOSE 8098 8087 8088
+ADD traffic_vault/run.sh /
+ADD traffic_vault/sslkeys.xml /
+ENTRYPOINT /run.sh
diff --git a/traffic_ops_ort/testing/docker/traffic_vault/functions b/traffic_ops_ort/testing/docker/traffic_vault/functions
new file mode 100644
index 0000000..5ae8ba2
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_vault/functions
@@ -0,0 +1,707 @@
+# -*-Shell-script-*-
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# functions This file contains functions to be used by most or all
+#       shell scripts in the /etc/init.d directory.
+#
+
+TEXTDOMAIN=initscripts
+
+# Make sure umask is sane
+umask 022
+
+# Set up a default search path.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"
+export PATH
+
+if [ $PPID -ne 1 -a -z "$SYSTEMCTL_SKIP_REDIRECT" ] && \
+        [ -d /run/systemd/system ] ; then
+    case "$0" in
+    /etc/init.d/*|/etc/rc.d/init.d/*)
+        _use_systemctl=1
+        ;;
+    esac
+fi
+
+systemctl_redirect () {
+    local s
+    local prog=${1##*/}
+    local command=$2
+    local options=""
+
+    case "$command" in
+    start)
+        s="Starting $prog (via systemctl): "
+        ;;
+    stop)
+        s="Stopping $prog (via systemctl): "
+        ;;
+    reload|try-reload)
+        s="Reloading $prog configuration (via systemctl): "
+        ;;
+    restart|try-restart|condrestart)
+        s="Restarting $prog (via systemctl): "
+        ;;
+    esac
+
+    if [ -n "$SYSTEMCTL_IGNORE_DEPENDENCIES" ] ; then
+        options="--ignore-dependencies"
+    fi
+
+    if ! systemctl show "$prog.service" > /dev/null 2>&1 || \
+            systemctl show -p LoadState "$prog.service" | grep -q 'not-found' ; then
+        action $"Reloading systemd: " /bin/systemctl daemon-reload
+    fi
+
+    action "$s" /bin/systemctl $options $command "$prog.service"
+}
+
+# Get a sane screen width
+[ -z "${COLUMNS:-}" ] && COLUMNS=80
+
+if [ -z "${CONSOLETYPE:-}" ]; then
+    if [ -c "/dev/stderr" -a -r "/dev/stderr" ]; then
+        CONSOLETYPE="$(/sbin/consoletype < /dev/stderr 2>/dev/null)"
+    else
+        CONSOLETYPE="serial"
+    fi
+fi
+
+if [ -z "${NOLOCALE:-}" ] && [ -z "${LANGSH_SOURCED:-}" ] && \
+        [ -f /etc/sysconfig/i18n -o -f /etc/locale.conf ] ; then
+    . /etc/profile.d/lang.sh 2>/dev/null
+    # avoid propagating LANGSH_SOURCED any further
+    unset LANGSH_SOURCED
+fi
+
+# Read in our configuration
+if [ -z "${BOOTUP:-}" ]; then
+    if [ -f /etc/sysconfig/init ]; then
+        . /etc/sysconfig/init
+    else
+        # This all seem confusing? Look in /etc/sysconfig/init,
+        # or in /usr/share/doc/initscripts-*/sysconfig.txt
+        BOOTUP=color
+        RES_COL=60
+        MOVE_TO_COL="echo -en \\033[${RES_COL}G"
+        SETCOLOR_SUCCESS="echo -en \\033[1;32m"
+        SETCOLOR_FAILURE="echo -en \\033[1;31m"
+        SETCOLOR_WARNING="echo -en \\033[1;33m"
+        SETCOLOR_NORMAL="echo -en \\033[0;39m"
+        LOGLEVEL=1
+    fi
+    if [ "$CONSOLETYPE" = "serial" ]; then
+        BOOTUP=serial
+        MOVE_TO_COL=
+        SETCOLOR_SUCCESS=
+        SETCOLOR_FAILURE=
+        SETCOLOR_WARNING=
+        SETCOLOR_NORMAL=
+    fi
+fi
+
+# Check if any of $pid (could be plural) are running
+checkpid() {
+    local i
+
+    for i in $* ; do
+        [ -d "/proc/$i" ] && return 0
+    done
+    return 1
+}
+
+__kill_pids_term_kill_checkpids() {
+    local base_stime=$1
+    shift 1
+    local pid=
+    local pids=$*
+    local remaining=
+    local stat=
+    local stime=
+
+    for pid in $pids ; do
+        [ ! -e  "/proc/$pid" ] && continue
+        read -r line < "/proc/$pid/stat" 2> /dev/null
+
+        stat=($line)
+        stime=${stat[21]}
+
+        [ -n "$stime" ] && [ "$base_stime" -lt "$stime" ] && continue
+        remaining+="$pid "
+    done
+
+    echo "$remaining"
+    [ -n "$remaining" ] && return 1
+
+    return 0
+}
+
+__kill_pids_term_kill() {
+    local try=0
+    local delay=3;
+    local pid=
+    local stat=($(< /proc/self/stat))
+    local base_stime=${stat[21]}
+
+    if [ "$1" = "-d" ]; then
+        delay=$2
+        shift 2
+    fi
+
+    local kill_list=$*
+
+    kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list)
+
+    [ -z "$kill_list" ] && return 0
+
+    kill -TERM $kill_list >/dev/null 2>&1
+    usleep 100000
+
+    kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list)
+    if [ -n "$kill_list" ] ; then
+        while [ $try -lt $delay ] ; do
+            sleep 1
+            kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list)
+            [ -z "$kill_list" ] && break
+            let try+=1
+        done
+        if [ -n "$kill_list" ] ; then
+            kill -KILL $kill_list >/dev/null 2>&1
+            usleep 100000
+            kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list)
+        fi
+    fi
+
+    [ -n "$kill_list" ] && return 1
+    return 0
+}
+
+# __proc_pids {program} [pidfile]
+# Set $pid to pids from /var/run* for {program}.  $pid should be declared
+# local in the caller.
+# Returns LSB exit code for the 'status' action.
+__pids_var_run() {
+    local base=${1##*/}
+    local pid_file=${2:-/var/run/$base.pid}
+    local pid_dir=$(/usr/bin/dirname $pid_file > /dev/null)
+    local binary=$3
+
+    [ -d "$pid_dir" -a ! -r "$pid_dir" ] && return 4
+
+    pid=
+    if [ -f "$pid_file" ] ; then
+            local line p
+
+        [ ! -r "$pid_file" ] && return 4 # "user had insufficient privilege"
+        while : ; do
+            read line
+            [ -z "$line" ] && break
+            for p in $line ; do
+                if [ -z "${p//[0-9]/}" ] && [ -d "/proc/$p" ] ; then
+                    if [ -n "$binary" ] ; then
+                        local b=$(readlink /proc/$p/exe | sed -e 's/\s*(deleted)$//')
+                        [ "$b" != "$binary" ] && continue
+                    fi
+                    pid="$pid $p"
+                fi
+            done
+        done < "$pid_file"
+
+            if [ -n "$pid" ]; then
+                    return 0
+            fi
+        return 1 # "Program is dead and /var/run pid file exists"
+    fi
+    return 3 # "Program is not running"
+}
+
+# Output PIDs of matching processes, found using pidof
+__pids_pidof() {
+    pidof -c -m -o $$ -o $PPID -o %PPID -x "$1" || \
+        pidof -c -m -o $$ -o $PPID -o %PPID -x "${1##*/}"
+}
+
+
+# A function to start a program.
+daemon() {
+    # Test syntax.
+    local gotbase= force= nicelevel corelimit
+    local pid base= user= nice= bg= pid_file=
+    local cgroup=
+    nicelevel=0
+    while [ "$1" != "${1##[-+]}" ]; do
+        case $1 in
+        '')
+            echo $"$0: Usage: daemon [+/-nicelevel] {program}" "[arg1]..."
+            return 1
+            ;;
+        --check)
+            base=$2
+            gotbase="yes"
+            shift 2
+            ;;
+        --check=?*)
+            base=${1#--check=}
+            gotbase="yes"
+            shift
+            ;;
+        --user)
+            user=$2
+            shift 2
+            ;;
+        --user=?*)
+            user=${1#--user=}
+            shift
+            ;;
+        --pidfile)
+            pid_file=$2
+            shift 2
+            ;;
+        --pidfile=?*)
+            pid_file=${1#--pidfile=}
+            shift
+            ;;
+        --force)
+            force="force"
+            shift
+            ;;
+        [-+][0-9]*)
+            nice="nice -n $1"
+            shift
+            ;;
+        *)
+            echo $"$0: Usage: daemon [+/-nicelevel] {program}" "[arg1]..."
+            return 1
+            ;;
+      esac
+    done
+
+    # Save basename.
+    [ -z "$gotbase" ] && base=${1##*/}
+
+    # See if it's already running. Look *only* at the pid file.
+    __pids_var_run "$base" "$pid_file"
+
+    [ -n "$pid" -a -z "$force" ] && return
+
+    # make sure it doesn't core dump anywhere unless requested
+    corelimit="ulimit -S -c ${DAEMON_COREFILE_LIMIT:-0}"
+
+    # if they set NICELEVEL in /etc/sysconfig/foo, honor it
+    [ -n "${NICELEVEL:-}" ] && nice="nice -n $NICELEVEL"
+
+    # if they set CGROUP_DAEMON in /etc/sysconfig/foo, honor it
+    if [ -n "${CGROUP_DAEMON}" ]; then
+        if [ ! -x /bin/cgexec ]; then
+            echo -n "Cgroups not installed"; warning
+            echo
+        else
+            cgroup="/bin/cgexec";
+            for i in $CGROUP_DAEMON; do
+                cgroup="$cgroup -g $i";
+            done
+        fi
+    fi
+
+    # Echo daemon
+    [ "${BOOTUP:-}" = "verbose" -a -z "${LSB:-}" ] && echo -n " $base"
+
+    # And start it up.
+    if [ -z "$user" ]; then
+       $cgroup $nice /bin/bash -c "$corelimit >/dev/null 2>&1 ; $*"
+    else
+       $cgroup $nice runuser -s /bin/bash $user -c "$corelimit >/dev/null 2>&1 ; $*"
+    fi
+
+    [ "$?" -eq 0 ] && success $"$base startup" || failure $"$base startup"
+}
+
+# A function to stop a program.
+killproc() {
+    local RC killlevel= base pid pid_file= delay try binary=
+
+    RC=0; delay=3; try=0
+    # Test syntax.
+    if [ "$#" -eq 0 ]; then
+        echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
+        return 1
+    fi
+    if [ "$1" = "-p" ]; then
+        pid_file=$2
+        shift 2
+    fi
+    if [ "$1" = "-b" ]; then
+        if [ -z $pid_file ]; then
+            echo $"-b option can be used only with -p"
+            echo $"Usage: killproc -p pidfile -b binary program"
+            return 1
+        fi
+        binary=$2
+        shift 2
+    fi
+    if [ "$1" = "-d" ]; then
+        delay=$(echo $2 | awk -v RS=' ' -v IGNORECASE=1 '{if($1!~/^[0-9.]+[smhd]?$/) exit 1;d=$1~/s$|^[0-9.]*$/?1:$1~/m$/?60:$1~/h$/?60*60:$1~/d$/?24*60*60:-1;if(d==-1) exit 1;delay+=d*$1} END {printf("%d",delay+0.5)}')
+        if [ "$?" -eq 1 ]; then
+            echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
+            return 1
+        fi
+        shift 2
+    fi
+
+
+    # check for second arg to be kill level
+    [ -n "${2:-}" ] && killlevel=$2
+
+    # Save basename.
+    base=${1##*/}
+
+    # Find pid.
+    __pids_var_run "$1" "$pid_file" "$binary"
+    RC=$?
+    if [ -z "$pid" ]; then
+        if [ -z "$pid_file" ]; then
+            pid="$(__pids_pidof "$1")"
+        else
+            [ "$RC" = "4" ] && { failure $"$base shutdown" ; return $RC ;}
+        fi
+    fi
+
+    # Kill it.
+    if [ -n "$pid" ] ; then
+        [ "$BOOTUP" = "verbose" -a -z "${LSB:-}" ] && echo -n "$base "
+        if [ -z "$killlevel" ] ; then
+            __kill_pids_term_kill -d $delay $pid
+            RC=$?
+            [ "$RC" -eq 0 ] && success $"$base shutdown" || failure $"$base shutdown"
+        # use specified level only
+        else
+            if checkpid $pid; then
+                kill $killlevel $pid >/dev/null 2>&1
+                RC=$?
+                [ "$RC" -eq 0 ] && success $"$base $killlevel" || failure $"$base $killlevel"
+            elif [ -n "${LSB:-}" ]; then
+                RC=7 # Program is not running
+            fi
+        fi
+    else
+        if [ -n "${LSB:-}" -a -n "$killlevel" ]; then
+            RC=7 # Program is not running
+        else
+            failure $"$base shutdown"
+            RC=0
+        fi
+    fi
+
+    # Remove pid file if any.
+    if [ -z "$killlevel" ]; then
+        rm -f "${pid_file:-/var/run/$base.pid}"
+    fi
+    return $RC
+}
+
+# A function to find the pid of a program. Looks *only* at the pidfile
+pidfileofproc() {
+    local pid
+
+    # Test syntax.
+    if [ "$#" = 0 ] ; then
+        echo $"Usage: pidfileofproc {program}"
+        return 1
+    fi
+
+    __pids_var_run "$1"
+    [ -n "$pid" ] && echo $pid
+    return 0
+}
+
+# A function to find the pid of a program.
+pidofproc() {
+    local RC pid pid_file=
+
+    # Test syntax.
+    if [ "$#" = 0 ]; then
+        echo $"Usage: pidofproc [-p pidfile] {program}"
+        return 1
+    fi
+    if [ "$1" = "-p" ]; then
+        pid_file=$2
+        shift 2
+    fi
+    fail_code=3 # "Program is not running"
+
+    # First try "/var/run/*.pid" files
+    __pids_var_run "$1" "$pid_file"
+    RC=$?
+    if [ -n "$pid" ]; then
+        echo $pid
+        return 0
+    fi
+
+    [ -n "$pid_file" ] && return $RC
+    __pids_pidof "$1" || return $RC
+}
+
+status() {
+    local base pid lock_file= pid_file= binary=
+
+    # Test syntax.
+    if [ "$#" = 0 ] ; then
+        echo $"Usage: status [-p pidfile] {program}"
+        return 1
+    fi
+    if [ "$1" = "-p" ]; then
+        pid_file=$2
+        shift 2
+    fi
+    if [ "$1" = "-l" ]; then
+        lock_file=$2
+        shift 2
+    fi
+    if [ "$1" = "-b" ]; then
+        if [ -z $pid_file ]; then
+            echo $"-b option can be used only with -p"
+            echo $"Usage: status -p pidfile -b binary program"
+            return 1
+        fi
+        binary=$2
+        shift 2
+    fi
+    base=${1##*/}
+
+    if [ "$_use_systemctl" = "1" ]; then
+        systemctl status ${0##*/}.service
+        ret=$?
+        # LSB daemons that dies abnormally in systemd looks alive in systemd's eyes due to RemainAfterExit=yes
+        # lets adjust the reality a little bit
+        if systemctl show -p ActiveState ${0##*/}.service | grep -q '=active$' && \
+        systemctl show -p SubState ${0##*/}.service | grep -q '=exited$' ; then
+            ret=3
+        fi
+        return $ret
+    fi
+
+    # First try "pidof"
+    __pids_var_run "$1" "$pid_file" "$binary"
+    RC=$?
+    if [ -z "$pid_file" -a -z "$pid" ]; then
+        pid="$(__pids_pidof "$1")"
+    fi
+    if [ -n "$pid" ]; then
+        echo $"${base} (pid $pid) is running..."
+        return 0
+    fi
+
+    case "$RC" in
+    0)
+        echo $"${base} (pid $pid) is running..."
+        return 0
+        ;;
+    1)
+        echo $"${base} dead but pid file exists"
+        return 1
+        ;;
+    4)
+        echo $"${base} status unknown due to insufficient privileges."
+        return 4
+        ;;
+    esac
+    if [ -z "${lock_file}" ]; then
+        lock_file=${base}
+    fi
+    # See if /var/lock/subsys/${lock_file} exists
+    if [ -f /var/lock/subsys/${lock_file} ]; then
+        echo $"${base} dead but subsys locked"
+        return 2
+    fi
+    echo $"${base} is stopped"
+    return 3
+}
+
+echo_success() {
+    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+    echo -n "["
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+    echo -n $"  OK  "
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+    echo -n "]"
+    echo -ne "\r"
+    return 0
+}
+
+echo_failure() {
+    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+    echo -n "["
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+    echo -n $"FAILED"
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+    echo -n "]"
+    echo -ne "\r"
+    return 1
+}
+
+echo_passed() {
+    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+    echo -n "["
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+    echo -n $"PASSED"
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+    echo -n "]"
+    echo -ne "\r"
+    return 1
+}
+
+echo_warning() {
+    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+    echo -n "["
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+    echo -n $"WARNING"
+    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+    echo -n "]"
+    echo -ne "\r"
+    return 1
+}
+
+# Inform the graphical boot of our current state
+update_boot_stage() {
+    if [ -x /bin/plymouth ]; then
+        /bin/plymouth --update="$1"
+    fi
+    return 0
+}
+
+# Log that something succeeded
+success() {
+    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_success
+    return 0
+}
+
+# Log that something failed
+failure() {
+    local rc=$?
+    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_failure
+    [ -x /bin/plymouth ] && /bin/plymouth --details
+    return $rc
+}
+
+# Log that something passed, but may have had errors. Useful for fsck
+passed() {
+    local rc=$?
+    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_passed
+    return $rc
+}
+
+# Log a warning
+warning() {
+    local rc=$?
+    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_warning
+    return $rc
+}
+
+# Run some action. Log its output.
+action() {
+    local STRING rc
+
+    STRING=$1
+    echo -n "$STRING "
+    shift
+    "$@" && success $"$STRING" || failure $"$STRING"
+    rc=$?
+    echo
+    return $rc
+}
+
+# returns OK if $1 contains $2
+strstr() {
+    [ "${1#*$2*}" = "$1" ] && return 1
+    return 0
+}
+
+# Check whether file $1 is a backup or rpm-generated file and should be ignored
+is_ignored_file() {
+    case "$1" in
+    *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
+        return 0
+        ;;
+    esac
+    return 1
+}
+
+# Evaluate shvar-style booleans
+is_true() {
+    case "$1" in
+    [tT] | [yY] | [yY][eE][sS] | [tT][rR][uU][eE] | 1)
+        return 0
+        ;;
+    esac
+    return 1
+}
+
+# Evaluate shvar-style booleans
+is_false() {
+    case "$1" in
+    [fF] | [nN] | [nN][oO] | [fF][aA][lL][sS][eE] | 0)
+        return 0
+        ;;
+    esac
+    return 1
+}
+
+# Apply sysctl settings, including files in /etc/sysctl.d
+apply_sysctl() {
+    if [ -x /lib/systemd/systemd-sysctl ]; then
+    /lib/systemd/systemd-sysctl
+    else
+        for file in /usr/lib/sysctl.d/*.conf ; do
+            is_ignored_file "$file" && continue
+            [ -f /run/sysctl.d/${file##*/} ] && continue
+            [ -f /etc/sysctl.d/${file##*/} ] && continue
+            test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
+        done
+        for file in /run/sysctl.d/*.conf ; do
+            is_ignored_file "$file" && continue
+            [ -f /etc/sysctl.d/${file##*/} ] && continue
+            test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
+        done
+        for file in /etc/sysctl.d/*.conf ; do
+            is_ignored_file "$file" && continue
+            test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
+        done
+        sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
+    fi
+}
+
+# A sed expression to filter out the files that is_ignored_file recognizes
+__sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d'
+
+if [ "$_use_systemctl" = "1" ]; then
+        if  [ "x$1" = xstart -o \
+              "x$1" = xstop -o \
+              "x$1" = xrestart -o \
+              "x$1" = xreload -o \
+              "x$1" = xtry-restart -o \
+              "x$1" = xforce-reload -o \
+              "x$1" = xcondrestart ] ; then
+
+        systemctl_redirect $0 $1
+        exit $?
+    fi
+fi
+
+strstr "$(cat /proc/cmdline)" "rc.debug" && set -x
+return 0
+
diff --git a/traffic_ops_ort/testing/docker/traffic_vault/run.sh b/traffic_ops_ort/testing/docker/traffic_vault/run.sh
new file mode 100755
index 0000000..0dcdc95
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_vault/run.sh
@@ -0,0 +1,126 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Script for running the Dockerfile for Traffic Vault.
+# The Dockerfile sets up a Docker image which can be used for any new container;
+# This script, which should be run when the container is run (it's the ENTRYPOINT), will configure the container.
+#
+# The following environment variables must be set (ordinarily by `docker run -e` arguments):
+# TV_ADMIN_PASS
+# RIAK_USER_PASS
+# CERT_COUNTRY
+# CERT_STATE
+# CERT_CITY
+# CERT_COMPANY
+# TO_URI
+# TO_USER
+# TO_PASS
+# TV_DOMAIN
+# IP
+# GATEWAY
+# CREATE_TO_DB_ENTRY (If set to yes, create the TO db entry for this server if set to no, assume it it already there)
+
+start() {
+	/etc/init.d/riak restart
+	#exec tail -f /var/log/riak/console.log
+  touch /var/log/messages
+  tail -f /var/log/messages
+}
+
+init() {
+	TMP_TO_COOKIE="$(curl -v -s -k -X POST --data '{ "u":"'"$TO_USER"'", "p":"'"$TO_PASS"'" }' $TO_URI/api/1.2/user/login 2>&1 | grep 'Set-Cookie' | sed -e 's/.*mojolicious=\(.*\); expires.*/\1/')"
+	echo "Got Cookie: $TMP_TO_COOKIE"
+#	curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" -F "filename=profile.traffic_vault.traffic_ops" -F "profile_to_import=@/profile.traffic_vault.traffic_ops" $TO_URI/profile/doImport	
+
+	# \todo figure out a better way to get the IP, domain (Docker network name), gateway
+	TMP_IP=$IP
+	TMP_DOMAIN=$TV_DOMAIN
+	TMP_GATEWAY=$GATEWAY
+
+	echo "Got IP: $TMP_IP"
+	echo "Got Domain: $TMP_DOMAIN"
+	echo "Got Gateway: $TMP_GATEWAY"
+
+	if [ "$CREATE_TO_DB_ENTRY" = "YES" ] ; then
+		TMP_CACHEGROUP_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/cachegroups.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="mid-east"]; print match[0]')"
+		echo "Got cachegroup ID: $TMP_CACHEGROUP_ID"
+	
+		TMP_SERVER_TYPE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/types.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="RIAK"]; print match[0]')"
+		echo "Got server type ID: $TMP_SERVER_TYPE_ID"
+	
+		TMP_SERVER_PROFILE_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/profiles.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="RIAK_ALL"]; print match[0]')"
+		echo "Got server profile ID: $TMP_SERVER_PROFILE_ID"
+	
+		TMP_PHYS_LOCATION_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/phys_locations.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="plocation-nyc-1"]; print match[0]')"
+		echo "Got phys location ID: $TMP_PHYS_LOCATION_ID"
+	
+		TMP_CDN_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/cdns.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["name"]=="cdn"]; print match[0]')"
+		echo "Got cdn ID: $TMP_CDN_ID"
+	
+		curl -v -k -X POST -H "Cookie: mojolicious=$TMP_TO_COOKIE" --data-urlencode "host_name=$HOSTNAME" --data-urlencode "domain_name=$TMP_DOMAIN" --data-urlencode "interface_name=eth0" --data-urlencode "ip_address=$TMP_IP" --data-urlencode "ip_netmask=255.255.0.0" --data-urlencode "ip_gateway=$TMP_GATEWAY" --data-urlencode "interface_mtu=9000" --data-urlencode "cdn=$TMP_CDN_ID" --data-urlencode "cachegroup=$TMP_CACHEGROUP_ID" --data-urlencode "phys_location=$TMP_PHYS_LOCATION_ID" --data-url [...]
+
+	fi
+
+	TMP_SERVER_ID="$(curl -s -k -X GET -H "Cookie: mojolicious=$TMP_TO_COOKIE" $TO_URI/api/1.2/servers.json | python -c 'import json,sys;obj=json.load(sys.stdin);match=[x["id"] for x in obj["response"] if x["hostName"]=="'"$HOSTNAME"'"]; print match[0]')"
+	echo "Got server ID: $TMP_SERVER_ID"
+
+	curl -v -k -H "Content-Type: application/x-www-form-urlencoded" -H "Cookie: mojolicious=$TMP_TO_COOKIE" -X POST --data-urlencode "id=$TMP_SERVER_ID" --data-urlencode "status=ONLINE" $TO_URI/server/updatestatus
+
+	openssl req -newkey rsa:2048 -nodes -keyout /etc/riak/certs/server.key -x509 -days 365 -out /etc/riak/certs/server.crt -subj "/C=$CERT_COUNTRY/ST=$CERT_STATE/L=$CERT_CITY/O=$CERT_COMPANY"
+	cp /etc/riak/certs/server.crt /etc/riak/certs/ca-bundle.crt
+
+	/etc/init.d/riak restart
+	riak-admin security enable
+	riak-admin security add-group admins
+	riak-admin security add-group keysusers
+	riak-admin security add-user admin password=$TV_ADMIN_PASS groups=admins
+	riak-admin security add-user riakuser password=$RIAK_USER_PASS groups=keysusers
+	riak-admin security add-source riakuser 0.0.0.0/0 password
+	riak-admin security add-source admin 0.0.0.0/0 password
+	riak-admin security grant riak_kv.list_buckets,riak_kv.list_keys,riak_kv.get,riak_kv.put,riak_kv.delete on any to admins
+	riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete on default ssl to keysusers
+	riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete on default dnssec to keysusers
+	riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete on default url_sig_keys to keysusers
+
+  # Setting up Riak search permissions.
+  riak-admin security grant search.admin on schema to admin
+  riak-admin security grant search.admin on index to admin
+  riak-admin security grant search.query on index to admin
+  riak-admin security grant search.query on index sslkeys to admin
+  riak-admin security grant search.query on index to riakuser
+  riak-admin security grant search.query on index sslkeys to riakuser
+  riak-admin security grant riak_core.set_bucket on any to admin
+	echo "INITIALIZED=1" >> /etc/environment
+
+  # Add the search schema
+
+  (cd / && curl --tlsv1.1 --tls-max 1.1 -kvsX PUT "https://admin:$TV_ADMIN_PASS@localhost:8088/search/schema/sslkeys" -H "Content-Type: application/xml" -d @sslkeys.xml)
+
+  # Add the search index
+  curl --tlsv1.1 --tls-max 1.1 -kvsX PUT "https://admin:$TV_ADMIN_PASS@localhost:8088/search/index/sslkeys" -H 'Content-Type: application/json' -d '{"schema":"sslkeys"}'
+
+  # Add Index to bucket association for 'sslkeys'
+  curl --tlsv1.1 --tls-max 1.1 -kvs -XPUT "https://admin:$TV_ADMIN_PASS@localhost:8088/buckets/ssl/props" -H'content-type:application/json' -d'{"props":{"search_index":"sslkeys"}}'
+}
+
+# sleeping to wait and make sure traffic ops is up on to_server.
+sleep 30
+
+source /etc/environment
+if [ -z "$INITIALIZED" ]; then init; fi
+start
diff --git a/traffic_ops_ort/testing/docker/traffic_vault/sslkeys.xml b/traffic_ops_ort/testing/docker/traffic_vault/sslkeys.xml
new file mode 100644
index 0000000..245b8bc
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/traffic_vault/sslkeys.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<schema name="schedule" version="1.5">
+ <fields>
+   <field name="cdn"   type="string"  indexed="true" stored="true" />
+   <field name="deliveryservice"   type="string"  indexed="true" stored="true" />
+   <field name="hostname"    type="string"     indexed="true" stored="true" />
+   <field name="certificate.crt" type="string" indexed="false" stored="true" />
+   <field name="certificate.key" type="string" indexed="false" stored="true" />
+
+   <!-- All of these fields are required by Riak Search -->
+   <field name="_yz_id"   type="_yz_str" indexed="true" stored="true"  multiValued="false" required="true"/>
+   <field name="_yz_ed"   type="_yz_str" indexed="true" stored="false" multiValued="false"/>
+   <field name="_yz_pn"   type="_yz_str" indexed="true" stored="false" multiValued="false"/>
+   <field name="_yz_fpn"  type="_yz_str" indexed="true" stored="false" multiValued="false"/>
+   <field name="_yz_vtag" type="_yz_str" indexed="true" stored="false" multiValued="false"/>
+   <field name="_yz_rk"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
+   <field name="_yz_rt"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
+   <field name="_yz_rb"   type="_yz_str" indexed="true" stored="true"  multiValued="false"/>
+   <field name="_yz_err"  type="_yz_str" indexed="true" stored="false" multiValued="false"/>
+
+   <!--catch all field-->
+   <dynamicField name="*" type="ignored"  />
+
+ </fields>
+
+ <uniqueKey>_yz_id</uniqueKey>
+
+ <types>
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
+    <!-- YZ String: Used for non-analyzed fields -->
+    <fieldType name="_yz_str" class="solr.StrField" sortMissingLast="true" />
+    <!-- Used for the catch all field -->
+    <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
+ </types>
+</schema>
diff --git a/traffic_ops_ort/testing/docker/variables.env b/traffic_ops_ort/testing/docker/variables.env
new file mode 100644
index 0000000..368b297
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/variables.env
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+CERT_COUNTRY=US
+CERT_STATE=Colorado
+CERT_CITY=Denver
+CERT_COMPANY=NotComcast
+POSTGRES_HOME=/usr/pgsql-9.6
+PGPASSWORD=secretrootpass
+DB_NAME=traffic_ops
+DB_PORT=5432
+DB_ROOT_PASS=null
+DB_USER=traffic_ops
+DB_USER_PASS=twelve
+DB_SERVER=db
+RIAK_USER_PASS=tvsecret
+TO_ADMIN_USER=admin
+TO_ADMIN_PASS=twelve
+TO_HOSTNAME=to_server
+TO_DOMAIN=trafficops_default
+TO_URI=https://to_server:443
+TV_ADMIN_PASS=tvsecret
+TV_DOMAIN=local
diff --git a/traffic_ops_ort/testing/docker/yumserver/Dockerfile b/traffic_ops_ort/testing/docker/yumserver/Dockerfile
new file mode 100644
index 0000000..254b515
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/yumserver/Dockerfile
@@ -0,0 +1,33 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+############################################################
+# Dockerfile to build Traffic Server container images
+#   as Edges for Traffic Control 1.4
+# Based on CentOS 6.6
+############################################################
+
+FROM centos:7
+MAINTAINER dev@trafficcontrol.apache.org
+
+RUN yum install -y httpd createrepo yum-utils
+
+RUN mkdir -p /var/www/html/traffic-control/7/x86_64/Packages
+RUN sed -i -e 's/#ServerName www.example.com:80/ServerName www.example.com:80/g' /etc/httpd/conf/httpd.conf
+
+EXPOSE 80
+ADD yumserver/run.sh /
+ENTRYPOINT /run.sh
diff --git a/traffic_ops_ort/testing/docker/yumserver/run.sh b/traffic_ops_ort/testing/docker/yumserver/run.sh
new file mode 100755
index 0000000..1379735
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/yumserver/run.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+/usr/sbin/httpd
+
+createrepo /var/www/html/traffic-control/7/x86_64 
+
+tail -f /var/log/httpd/access_log
diff --git a/traffic_ops_ort/testing/docker/yumserver/test-rpms/README.md b/traffic_ops_ort/testing/docker/yumserver/test-rpms/README.md
new file mode 100644
index 0000000..266e2d9
--- /dev/null
+++ b/traffic_ops_ort/testing/docker/yumserver/test-rpms/README.md
@@ -0,0 +1,23 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+# Test rpms
+
+Drop ATS test rpms here.  t3c will utilize yum to install the
+ATS rpm found here as part of the integration testing.
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/astats.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/astats.config
new file mode 100644
index 0000000..7b2f58e
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/astats.config
@@ -0,0 +1,5 @@
+# DO NOT EDIT - Generated for ATS_EDGE_TIER_CACHE by  () on Fri Nov 13 18:49:10 UTC 2020
+allow_ip6=::1/128,fc01:9400:1000:8::/64
+allow_ip=127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+path=_astats
+record_types=122
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/hdr_rw_first_ds-top.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/hdr_rw_first_ds-top.config
new file mode 100644
index 0000000..a1ca67a
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/hdr_rw_first_ds-top.config
@@ -0,0 +1,2 @@
+# DO NOT EDIT - Generated for atlanta-edge-03 by  () on Fri Nov 13 18:49:10 UTC 2020
+first header rewrite
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/hosting.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/hosting.config
new file mode 100644
index 0000000..a5baff0
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/hosting.config
@@ -0,0 +1,2 @@
+# DO NOT EDIT - Generated for atlanta-edge-03 by  () on Fri Nov 13 18:49:10 UTC 2020
+hostname=*   volume=1
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/parent.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/parent.config
new file mode 100644
index 0000000..0bdecd0
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/parent.config
@@ -0,0 +1,5 @@
+# DO NOT EDIT - Generated for atlanta-edge-03 by atstccfg/0.2 from https://to_server:443 ips (172.28.0.5:443) on 2021-01-13T16:46:07.704055764Z
+# ds 'ds-top' topology 'mso-topology'
+dest_domain=origin.topology.example.net port=80 parent="atlanta-mid-16.ga.atlanta.kabletown.net:80|0.999" round_robin=consistent_hash go_direct=false qstring=ignore
+# ds '' topology ''
+dest_domain=. parent="atlanta-mid-16.ga.atlanta.kabletown.net:80|0.999;" round_robin=consistent_hash go_direct=false
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/records.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/records.config
new file mode 100644
index 0000000..04a7a71
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/records.config
@@ -0,0 +1,23 @@
+# DO NOT EDIT - Generated for ATS_EDGE_TIER_CACHE by  () on Fri Nov 13 18:49:10 UTC 2020
+CONFIG proxy.config.admin.user_id STRING ats
+CONFIG proxy.config.body_factory.template_sets_dir STRING /opt/trafficserver/etc/trafficserver/body_factory
+CONFIG proxy.config.config_dir STRING /opt/trafficserver/etc/trafficserver
+CONFIG proxy.config.diags.debug.enabled INT 1
+CONFIG proxy.config.dns.round_robin_nameservers INT 0
+CONFIG proxy.config.exec_thread.autoconfig INT 0
+CONFIG proxy.config.http.cache.required_headers INT 0
+CONFIG proxy.config.http.connect_attempts_timeout INT 10
+CONFIG proxy.config.http.enable_http_stats INT 1
+CONFIG proxy.config.http.insert_response_via_str INT 3
+CONFIG proxy.config.http.parent_proxy.retry_time INT 60
+CONFIG proxy.config.http.parent_proxy_routing_enable INT 1
+CONFIG proxy.config.http.server_ports STRING 80 80:ipv6
+CONFIG proxy.config.http.slow.log.threshold INT 10000
+CONFIG proxy.config.http.transaction_active_timeout_in INT 0
+CONFIG proxy.config.log.logfile_dir STRING /var/log/trafficserver
+CONFIG proxy.config.log.max_space_mb_for_logs INT 512
+CONFIG proxy.config.log.max_space_mb_headroom INT 50
+CONFIG proxy.config.proxy_name STRING atlanta-edge-03.ga.atlanta.kabletown.net
+CONFIG proxy.config.reverse_proxy.enabled INT 0
+CONFIG proxy.config.url_remap.remap_required INT 0
+LOCAL proxy.local.outgoing_ip_to_bind STRING 127.0.0.13
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/remap.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/remap.config
new file mode 100644
index 0000000..b28b2f3
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/remap.config
@@ -0,0 +1,3 @@
+# DO NOT EDIT - Generated for atlanta-edge-03 by  () on Fri Nov 13 18:49:10 UTC 2020
+map	http://atlanta-edge-03.ds-top.test.cdn1.net/     http://origin.topology.example.net/ @plugin=header_rewrite.so @pparam=dscp/set_dscp_40.config @plugin=header_rewrite.so @pparam=hdr_rw_first_ds-top.config  @plugin=cachekey.so @pparam=--separator= @pparam=--remove-all-params=true @pparam=--remove-path=true @pparam=--capture-prefix-uri=/^([^?]*)/$1/ # topology 'mso-topology'
+map	https://atlanta-edge-03.ds-top.test.cdn1.net/     http://origin.topology.example.net/ @plugin=header_rewrite.so @pparam=dscp/set_dscp_40.config @plugin=header_rewrite.so @pparam=hdr_rw_first_ds-top.config  @plugin=cachekey.so @pparam=--separator= @pparam=--remove-all-params=true @pparam=--remove-path=true @pparam=--capture-prefix-uri=/^([^?]*)/$1/ # topology 'mso-topology'
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/storage.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/storage.config
new file mode 100644
index 0000000..a680c88
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/storage.config
@@ -0,0 +1,2 @@
+# DO NOT EDIT - Generated for ATS_EDGE_TIER_CACHE by  () on Fri Nov 13 18:49:10 UTC 2020
+/var/trafficserver/cache volume=1
diff --git a/traffic_ops_ort/testing/ort-tests/baseline-configs/volume.config b/traffic_ops_ort/testing/ort-tests/baseline-configs/volume.config
new file mode 100644
index 0000000..8718379
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/baseline-configs/volume.config
@@ -0,0 +1,3 @@
+# DO NOT EDIT - Generated for ATS_EDGE_TIER_CACHE by  () on Fri Nov 13 18:49:10 UTC 2020
+# TRAFFIC OPS NOTE: This is running with forced volumes - the size is irrelevant
+volume=1 scheme=http size=100%
diff --git a/traffic_ops_ort/testing/ort-tests/conf/docker-edge-cache.conf b/traffic_ops_ort/testing/ort-tests/conf/docker-edge-cache.conf
new file mode 100644
index 0000000..d06370c
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/conf/docker-edge-cache.conf
@@ -0,0 +1,38 @@
+{
+    "default": {
+        "logLocations": {
+            "debug": "stdout",
+            "error": "stdout",
+            "event": "stdout",
+            "info": "stdout",
+            "warning": "stdout"
+        },
+        "session": {
+            "timeoutInSecs": 60
+        },
+        "includeSystemTests": false
+    },
+    "use_ims": true,
+    "trafficOps": {
+        "URL": "https://to_server:443",
+        "password": "twelve",
+        "users": {
+            "disallowed": "disallowed",
+            "operations": "operations",
+            "admin": "admin",
+            "federation": "federation",
+            "portal": "portal",
+            "readOnly": "readOnly",
+            "extension": "extension"
+        }
+    },
+    "trafficOpsDB": {
+        "dbname": "traffic_ops",
+        "description": "Test database to_test",
+        "hostname": "db",
+        "password": "twelve",
+        "port": "5432",
+        "type": "Pg",
+        "user": "traffic_ops"
+    }
+}
diff --git a/traffic_ops_ort/testing/ort-tests/conf/edge-host.conf b/traffic_ops_ort/testing/ort-tests/conf/edge-host.conf
new file mode 100644
index 0000000..09b0c38
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/conf/edge-host.conf
@@ -0,0 +1,38 @@
+{
+    "default": {
+        "logLocations": {
+            "debug": "stdout",
+            "error": "stdout",
+            "event": "stdout",
+            "info": "stdout",
+            "warning": "stdout"
+        },
+        "session": {
+            "timeoutInSecs": 60
+        },
+        "includeSystemTests": false
+    },
+    "use_ims": true,
+    "trafficOps": {
+        "URL": "https://localhost:443",
+        "password": "twelve",
+        "users": {
+            "disallowed": "disallowed",
+            "operations": "operations",
+            "admin": "admin",
+            "federation": "federation",
+            "portal": "portal",
+            "readOnly": "readOnly",
+            "extension": "extension"
+        }
+    },
+    "trafficOpsDB": {
+        "dbname": "traffic_ops",
+        "description": "Test database to_test",
+        "hostname": "localhost",
+        "password": "twelve",
+        "port": "5432",
+        "type": "Pg",
+        "user": "traffic_ops"
+    }
+}
diff --git a/traffic_ops_ort/testing/ort-tests/config/config.go b/traffic_ops_ort/testing/ort-tests/config/config.go
new file mode 100644
index 0000000..d136614
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/config/config.go
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Package config provides tools to load and validate configuration data for
+// Traffic Ops API tests.
+package config
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"reflect"
+	"strings"
+
+	log "github.com/apache/trafficcontrol/lib/go-log"
+	"github.com/kelseyhightower/envconfig"
+)
+
+// Config reflects the structure of the test-to-api.conf file
+type Config struct {
+	TrafficOps   TrafficOps   `json:"trafficOps"`
+	TrafficOpsDB TrafficOpsDB `json:"trafficOpsDB"`
+	Default      Default      `json:"default"`
+	UseIMS       bool         `json:"use_ims"`
+}
+
+// TrafficOps - config section
+type TrafficOps struct {
+	// URL - The point to the Traffic Ops instance being tested
+	URL string `json:"URL" envconfig:"TO_URL"`
+
+	// UserPassword - The Traffic Ops test user password hitting the API
+	UserPassword string `json:"password" envconfig:"TO_USER_PASSWORD"`
+
+	// User - The Traffic Ops Users
+	Users Users `json:"users"`
+
+	// Insecure - ignores insecure ssls certs that were self-generated
+	Insecure bool `json:"sslInsecure" envconfig:"SSL_INSECURE"`
+}
+
+// Users "users" section of the test-to-api.conf file
+type Users struct {
+
+	// DisallowedUser - The Traffic Ops Disallowed user
+	Disallowed string `json:"disallowed" envconfig:"TO_USER_DISALLOWED"`
+
+	// ReadOnly - The Traffic Ops Read Only user
+	ReadOnly string `json:"readOnly" envconfig:"TO_USER_READ_ONLY"`
+
+	// Operations - The Traffic Ops Operations user
+	Operations string `json:"operations" envconfig:"TO_USER_OPERATIONS"`
+
+	// AdminUser - The Traffic Ops Admin user
+	Admin string `json:"admin" envconfig:"TO_USER_ADMIN"`
+
+	// PortalUser - The Traffic Ops Portal user
+	Portal string `json:"portal" envconfig:"TO_USER_PORTAL"`
+
+	// FederationUser - The Traffic Ops Federation user
+	Federation string `json:"federation" envconfig:"TO_USER_FEDERATION"`
+
+	// Extension - The Traffic Ops Extension user
+	Extension string `json:"extension" envconfig:"TO_USER_EXTENSION"`
+}
+
+// TrafficOpsDB - config section
+type TrafficOpsDB struct {
+	// Name - Traffic Ops Database name where the test data will be setup
+	Name string `json:"dbname" envconfig:"TODB_NAME"`
+
+	// Hostname - Traffic Ops Database hostname where Postgres is running
+	Hostname string `json:"hostname" envconfig:"TODB_HOSTNAME"`
+
+	// User - database user that Traffic Ops is using to point to the 'to_test' database
+	User string `json:"user" envconfig:"TODB_USER"`
+
+	// Password - database password for the User above
+	Password string `json:"password" envconfig:"TODB_PASSWORD"`
+
+	// Port - Postgres port running that has the to_test schema
+	Port string `json:"port" envconfig:"TODB_PORT"`
+
+	// DBType - will be 'Pg' by default but tells the Golang database driver
+	//          the database type
+	DBType string `json:"type" envconfig:"TODB_TYPE"`
+
+	// SSL - Flag that tells the database driver that the Postgres instances has TLS enabled
+	SSL bool `json:"ssl" envconfig:"TODB_SSL"`
+
+	// Description - database description
+	Description string `json:"description" envconfig:"TODB_DESCRIPTION"`
+}
+
+// Default - config section
+type Default struct {
+	Session            Session   `json:"session"`
+	Log                Locations `json:"logLocations"`
+	IncludeSystemTests bool      `json:"includeSystemTests"`
+}
+
+// Session - config section
+type Session struct {
+	TimeoutInSecs int `json:"timeoutInSecs" envconfig:"SESSION_TIMEOUT_IN_SECS"`
+}
+
+// Locations - reflects the structure of the database.conf file
+type Locations struct {
+	Debug   string `json:"debug"`
+	Event   string `json:"event"`
+	Error   string `json:"error"`
+	Info    string `json:"info"`
+	Warning string `json:"warning"`
+}
+
+// LoadConfig - reads the config file into the Config struct
+func LoadConfig(confPath string) (Config, error) {
+	var cfg Config
+
+	confBytes, err := ioutil.ReadFile(confPath)
+	if err != nil {
+		return cfg, fmt.Errorf("failed to read CDN configuration: %v", err)
+	}
+
+	err = json.Unmarshal(confBytes, &cfg)
+	if err != nil {
+		return cfg, fmt.Errorf("failed to parse configuration from '%s': %v", confPath, err)
+	}
+
+	if err := validate(confPath, cfg); err != nil {
+		return cfg, fmt.Errorf("failed to validate configuration:\n%v", err)
+	}
+
+	if err := envconfig.Process("traffic-ops-client-tests", &cfg); err != nil {
+		return cfg, fmt.Errorf("failed to parse configuration from environment: %v", err)
+	}
+
+	return cfg, nil
+}
+
+type multiError []error
+
+func (me multiError) Error() string {
+	var sb strings.Builder
+	for _, e := range me {
+		fmt.Fprintln(&sb, e)
+	}
+	return sb.String()
+}
+
+// validate all required fields in the config.
+func validate(confPath string, config Config) error {
+	var errs multiError
+
+	var f string
+	f = "TrafficOps"
+	toTag, ok := getStructTag(config, f)
+	if !ok {
+		errs = append(errs, fmt.Errorf("'%s' must be configured in %s", toTag, confPath))
+	}
+
+	if config.TrafficOps.URL == "" {
+		f = "URL"
+		tag, ok := getStructTag(config.TrafficOps, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.Disallowed == "" {
+		f = "Disallowed"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.ReadOnly == "" {
+		f = "ReadOnly"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.Operations == "" {
+		f = "Operations"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.Admin == "" {
+		f = "Admin"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.Portal == "" {
+		f = "Portal"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if config.TrafficOps.Users.Federation == "" {
+		f = "Federation"
+		tag, ok := getStructTag(config.TrafficOps.Users, f)
+		if !ok {
+			errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f))
+		}
+		errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath))
+	}
+
+	if len(errs) > 0 {
+		return errs
+	}
+	return nil
+}
+
+func getStructTag(thing interface{}, fieldName string) (string, bool) {
+	var tag string
+	var ok bool
+	t := reflect.TypeOf(thing)
+	if t != nil {
+		if f, ok := t.FieldByName(fieldName); ok {
+			tag = f.Tag.Get("json")
+			return tag, ok
+		}
+	}
+	return tag, ok
+}
+
+// ErrorLog - critical messages
+func (c Config) ErrorLog() log.LogLocation {
+	return log.LogLocation(c.Default.Log.Error)
+}
+
+// WarningLog - warning messages
+func (c Config) WarningLog() log.LogLocation {
+	return log.LogLocation(c.Default.Log.Warning)
+}
+
+// InfoLog - information messages
+func (c Config) InfoLog() log.LogLocation {
+	return log.LogLocation(c.Default.Log.Info)
+}
+
+// DebugLog - troubleshooting messages
+func (c Config) DebugLog() log.LogLocation {
+	return log.LogLocation(c.Default.Log.Debug)
+}
+
+// EventLog - access.log high level transactions
+func (c Config) EventLog() log.LogLocation {
+	return log.LogLocation(c.Default.Log.Event)
+}
diff --git a/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go b/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go
new file mode 100644
index 0000000..4958043
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go
@@ -0,0 +1,159 @@
+package orttest
+
+/*
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"bytes"
+	"errors"
+	"github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/tcdata"
+	"github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/util"
+	"os"
+	"os/exec"
+	"testing"
+	"time"
+)
+
+var (
+	base_line_dir   = "baseline-configs"
+	test_config_dir = "/opt/trafficserver/etc/trafficserver"
+
+	testFiles = [8]string{
+		"astats.config",
+		"hdr_rw_first_ds-top.config",
+		"hosting.config",
+		"parent.config",
+		"records.config",
+		"remap.config",
+		"storage.config",
+		"volume.config",
+	}
+)
+
+func TestT3cBadassAndSyncDs(t *testing.T) {
+	t.Logf("------------- Starting TestT3cBadassAndSyncDs ---------------")
+	tcd.WithObjs(t, []tcdata.TCObj{
+		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
+		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
+		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
+		tcdata.DeliveryServices}, func() {
+
+		// run badass and check config files.
+		err := t3c_update("atlanta-edge-03", "badass")
+		if err != nil {
+			t.Fatalf("ERROR: t3c badass failed: %v\n", err)
+		}
+		for _, v := range testFiles {
+			bfn := base_line_dir + "/" + v
+			if !util.FileExists(bfn) {
+				t.Fatalf("ERROR: missing baseline config file, %s,  needed for tests", bfn)
+			}
+			tfn := test_config_dir + "/" + v
+			if !util.FileExists(tfn) {
+				t.Fatalf("ERROR: missing the expected config file, %s", tfn)
+			}
+
+			result, err := util.DiffFiles(bfn, tfn)
+			if err != nil || !result {
+				t.Fatalf("ERROR: the contents of '%s' does not match those in %s",
+					tfn, bfn)
+			}
+		}
+
+		time.Sleep(time.Second * 5)
+
+		t.Logf("------------------------ running SYNCDS Test ------------------")
+		// remove the remap.config in preparation for running syncds
+		remap := test_config_dir + "/remap.config"
+		err = os.Remove(remap)
+		if err != nil {
+			t.Fatalf("ERROR: unable to remove %s\n", remap)
+		}
+		// prepare for running syncds.
+		err = setQueueUpdateStatus("atlanta-edge-03", "true")
+		if err != nil {
+			t.Fatalf("ERROR: queue updates failed: %v\n", err)
+		}
+
+		// remap.config is removed and atlanta-edge-03 should have
+		// queue updates enabled.  run t3c to verify a new remap.config
+		// is pulled down.
+		err = t3c_update("atlanta-edge-03", "syncds")
+		if err != nil {
+			t.Fatalf("ERROR: t3c syncds failed: %v\n", err)
+		}
+		if !util.FileExists(remap) {
+			t.Fatalf("ERROR: syncds failed to pull down %s\n", remap)
+		}
+		t.Logf("------------------------ end SYNCDS Test ------------------")
+
+	})
+	t.Logf("------------- End of TestT3cBadassAndSyncDs ---------------")
+}
+
+func setQueueUpdateStatus(host_name string, update string) error {
+	args := []string{
+		"--dir=/opt/trafficserver/etc/traffficserver",
+		"--traffic-ops-insecure",
+		"--traffic-ops-timeout-milliseconds=30000",
+		"--traffic-ops-disable-proxy=true",
+		"--traffic-ops-user=" + tcd.Config.TrafficOps.Users.Admin,
+		"--traffic-ops-password=" + tcd.Config.TrafficOps.UserPassword,
+		"--traffic-ops-url=" + tcd.Config.TrafficOps.URL,
+		"--cache-host-name=" + host_name,
+		"--log-location-error=stdout",
+		"--log-location-info=stdout",
+		"--log-location-warning=stdout",
+		"--set-queue-status=" + update,
+		"--set-reval-status=false",
+	}
+	cmd := exec.Command("/opt/ort/atstccfg", args...)
+	var out bytes.Buffer
+	var errOut bytes.Buffer
+	cmd.Stdout = &out
+	cmd.Stderr = &errOut
+	err := cmd.Run()
+	if err != nil {
+		return errors.New(err.Error() + ": " + "stdout: " + out.String() + " stderr: " + errOut.String())
+	}
+	return nil
+}
+
+func t3c_update(host string, run_mode string) error {
+	args := []string{
+		"--traffic-ops-insecure=true",
+		"--dispersion=0",
+		"--login-dispersion=0",
+		"--traffic-ops-timeout-milliseconds=3000",
+		"--traffic-ops-user=" + tcd.Config.TrafficOps.Users.Admin,
+		"--traffic-ops-password=" + tcd.Config.TrafficOps.UserPassword,
+		"--traffic-ops-url=" + tcd.Config.TrafficOps.URL,
+		"--cache-host-name=" + host,
+		"--log-location-error=test.log",
+		"--log-location-info=test.log",
+		"--log-location-debug=test.log",
+		"--run-mode=" + run_mode,
+	}
+	cmd := exec.Command("/opt/ort/t3c", args...)
+	var out bytes.Buffer
+	var errOut bytes.Buffer
+	cmd.Stdout = &out
+	cmd.Stderr = &errOut
+	err := cmd.Run()
+	if err != nil {
+		return errors.New(err.Error() + ": " + "stdout: " + out.String() + " stderr: " + errOut.String())
+	}
+	return nil
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tc-fixtures.json b/traffic_ops_ort/testing/ort-tests/tc-fixtures.json
new file mode 100644
index 0000000..3d65f47
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tc-fixtures.json
@@ -0,0 +1,4664 @@
+{
+    "asns": [
+        {
+            "asn": 8888,
+            "cachegroupName": "originCachegroup"
+        },
+        {
+            "asn": 9999,
+            "cachegroupName": "multiOriginCachegroup"
+        }
+    ],
+    "cachegroups": [
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "originCachegroup",
+            "shortName": "og1",
+            "typeName": "ORG_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "multiOriginCachegroup",
+            "shortName": "mog1",
+            "typeName": "ORG_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "parentCachegroup",
+            "shortName": "pg1",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "parentCachegroup2",
+            "shortName": "pg2",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "secondaryCachegroup",
+            "shortName": "sg1",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "cachegroup1",
+            "parentCachegroupName": "parentCachegroup",
+            "secondaryParentCachegroupName": "secondaryCachegroup",
+            "shortName": "cg1",
+            "localizationMethods": [
+                "CZ",
+                "DEEP_CZ",
+                "GEO"
+            ],
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "fallback1",
+            "parentCachegroupName": "parentCachegroup",
+            "secondaryParentCachegroupName": "secondaryCachegroup",
+            "shortName": "fb1",
+            "localizationMethods": [
+                "CZ",
+                "DEEP_CZ",
+                "GEO"
+            ],
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "fallback2",
+            "parentCachegroupName": "parentCachegroup",
+            "secondaryParentCachegroupName": "secondaryCachegroup",
+            "shortName": "fb2",
+            "localizationMethods": [
+                "CZ",
+                "DEEP_CZ",
+                "GEO"
+            ],
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "fallback3",
+            "parentCachegroupName": "parentCachegroup",
+            "secondaryParentCachegroupName": "secondaryCachegroup",
+            "shortName": "fb3",
+            "localizationMethods": [
+                "CZ",
+                "DEEP_CZ",
+                "GEO"
+            ],
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 24.1234,
+            "longitude": -121.1234,
+            "name": "cachegroup2",
+            "parentCachegroupName": "secondaryCachegroup",
+            "secondaryParentCachegroupName": "parentCachegroup",
+            "shortName": "cg2",
+            "typeName": "EDGE_LOC",
+            "fallbacks": [
+                "fallback1",
+                "fallback2",
+                "fallback3"
+            ]
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "cachegroup3",
+            "parentCachegroupName": "parentCachegroup",
+            "secondaryParentCachegroupName": "secondaryCachegroup",
+            "shortName": "cg3",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "edge-parent1",
+            "shortName": "ep1",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "has-edge-parent1",
+            "parentCachegroupName": "edge-parent1",
+            "shortName": "hep1",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-edge-cg-01",
+            "shortName": "te1",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-edge-cg-02",
+            "shortName": "te2",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-01",
+            "shortName": "tm1",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-02",
+            "shortName": "tm2",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-03",
+            "shortName": "tm3",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-04",
+            "shortName": "tm4",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-05",
+            "shortName": "tm5",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-06",
+            "shortName": "tm6",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "topology-mid-cg-07",
+            "shortName": "tm7",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "dtrc1",
+            "shortName": "dtrc1",
+            "typeName": "MID_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "dtrc2",
+            "shortName": "dtrc2",
+            "typeName": "EDGE_LOC"
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "dtrc3",
+            "shortName": "dtrc3",
+            "typeName": "EDGE_LOC"
+        }
+    ],
+    "cdns": [
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.cdn1.net",
+            "name": "cdn1"
+        },
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.cdn2.net",
+            "name": "cdn2"
+        },
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.cdn3.net",
+            "name": "cdn3"
+        },
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.cdn4.net",
+            "name": "cdn4"
+        },
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.bar.net",
+            "name": "bar"
+        }
+    ],
+    "deliveryServiceRequestComments": [
+        {
+            "value": "this is comment one"
+        },
+        {
+            "value": "this is comment two"
+        },
+        {
+            "value": "this is comment three"
+        },
+        {
+            "value": "this is comment four"
+        }
+    ],
+    "deliveryServiceRequests": [
+        {
+            "changeType": "create",
+            "deliveryService": {
+                "active": true,
+                "cdnName": "cdn1",
+                "ccrDnsTtl": 30,
+                "deepCachingType": "NEVER",
+                "displayName": "Good Kabletown CDN",
+                "dscp": 1,
+                "geoLimit": 1,
+                "geoProvider": 1,
+                "initialDispersion": 1,
+                "logsEnabled": true,
+                "longDesc": "long desc",
+                "regionalGeoBlocking": true,
+                "routingName": "goodroute",
+                "tenant": "tenant1",
+                "type": "HTTP",
+                "xmlId": "test-ds1"
+            },
+            "status": "draft"
+        },
+        {
+            "changeType": "create",
+            "deliveryService": {
+                "active": true,
+                "cdnName": "cdn1",
+                "ccrDnsTtl": 30,
+                "deepCachingType": "NEVER",
+                "displayName": "Bad Tenant",
+                "dscp": 0,
+                "geoLimit": 0,
+                "geoProvider": 0,
+                "initialDispersion": 3,
+                "logsEnabled": false,
+                "longDesc": "long desc",
+                "regionalGeoBlocking": false,
+                "tenant": "root",
+                "type": "HTTP",
+                "xmlId": "test-ds2"
+            },
+            "status": "draft"
+        },
+        {
+            "changeType": "create",
+            "deliveryService": {
+                "ccrDnsTtl": 30,
+                "deepCachingType": "NEVER",
+                "displayName": "Bad Test Case CDN",
+                "dscp": 0,
+                "geoLimit": 0,
+                "geoProvider": 1,
+                "infoUrl": "xxx",
+                "initialDispersion": 1,
+                "logsEnabled": true,
+                "longDesc": "long desc",
+                "orgServerFqdn": "xxx",
+                "regionalGeoBlocking": true,
+                "routingName": "x routing",
+                "tenant": "tenant1",
+                "type": "HTTP",
+                "xmlId": "test-ds3"
+            },
+            "status": "draft"
+        },
+        {
+            "changeType": "update",
+            "deliveryService": {
+                "active": false,
+                "cdnName": "cdn1",
+                "ccrDnsTtl": 30,
+                "deepCachingType": "NEVER",
+                "displayName": "Testing transitions",
+                "dscp": 3,
+                "geoLimit": 1,
+                "geoProvider": 1,
+                "initialDispersion": 1,
+                "logsEnabled": true,
+                "longDesc": "long desc",
+                "regionalGeoBlocking": true,
+                "routingName": "goodroute",
+                "tenant": "tenant1",
+                "type": "HTTP",
+                "xmlId": "test-transitions"
+            },
+            "status": "draft"
+        }
+    ],
+    "deliveryServices": [
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl1",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds1DisplayName",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeHeader1\nedgeHeader2",
+            "exampleURLs": [
+                "http://ccr.ds1.example.net",
+                "https://ccr.ds1.example.net"
+            ],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s 1",
+            "longDesc1": "ds1",
+            "longDesc2": "ds1",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds1\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
+            "routingName": "ccr-ds1",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "type": "HTTP",
+            "xmlId": "ds1",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl2",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": ["fmt", "limit", "somethingelse"],
+            "deepCachingType": "NEVER",
+            "displayName": "d s 1",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "exampleURLs": [
+                "http://ccr.ds2.example.net",
+                "https://ccr.ds2x.example.net"
+            ],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s 1",
+            "longDesc1": "ds2",
+            "longDesc2": "ds2",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds2\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "maxOriginConnections": -1,
+            "midHeaderRewrite": "midHeader1\nmidHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin.ds2.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/ds2plugin.lua",
+            "routingName": "ccr-ds2",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant2",
+            "tenantName": "tenant2",
+            "type": "HTTP_LIVE",
+            "xmlId": "ds2",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl3",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": null,
+            "deepCachingType": "NEVER",
+            "displayName": "d s 1",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "exampleURLs": [
+                "http://ccr.ds3.example.net",
+                "https://ccr.ds3x.example.net"
+            ],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s 3",
+            "longDesc1": "ds3",
+            "longDesc2": "ds3",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds3\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "maxOriginConnections": 0,
+            "midHeaderRewrite": "midHeader1\nmidHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin.ds3.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/ds3plugin.lua",
+            "routingName": "ccr-ds3",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant3",
+            "tenantName": "tenant3",
+            "type": "HTTP_LIVE",
+            "xmlId": "ds3",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "deepCachingType": "NEVER",
+            "displayName": "anymap-ds",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "",
+            "exampleURLs": [],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "",
+            "longDesc1": "",
+            "longDesc2": "",
+            "matchList": [],
+            "maxDnsAnswers": 0,
+            "maxOriginConnections": 1,
+            "midHeaderRewrite": "",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://example.com",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "",
+            "regionalGeoBlocking": false,
+            "remapText": "map some raw remap text",
+            "routingName": "",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant3",
+            "tenantName": "tenant3",
+            "type": "ANY_MAP",
+            "xmlId": "anymap-ds",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl1",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": ["a", "b", "c"],
+            "consistentHashRegex": "foo",
+            "deepCachingType": "ALWAYS",
+            "displayName": "ds-test-minor-versions",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "fqPacingRate": 42,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "logsEnabled": false,
+            "longDesc": "d s 1",
+            "longDesc1": "ds1",
+            "longDesc2": "ds1",
+            "maxDnsAnswers": 0,
+            "maxOriginConnections": 1000,
+            "midHeaderRewrite": "midHeader1\nmidHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin-test-minor-version.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
+            "routingName": "cdn",
+            "signed": true,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenantId": 1,
+            "tenant": "root",
+            "trRequestHeaders": "X-Foo\nX-Bar",
+            "trResponseHeaders": "Access-Control-Allow-Origin: *\nContent-Type: text/html; charset=utf-8",
+            "type": "HTTP_LIVE",
+            "xmlId": "ds-test-minor-versions",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl1",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds1DisplayName",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "exampleURLs": [
+                "http://ccr.msods1.example.net",
+                "https://ccr.msods1.example.net"
+            ],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "mso DS 1",
+            "longDesc1": "msods1",
+            "longDesc2": "msods1",
+            "matchList": [
+                {
+                    "pattern": ".*\\.msods1\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": "midHeader1\nmidHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": true,
+            "orgServerFqdn": "http://origin.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
+            "routingName": "ccr-msods1",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "type": "HTTP_LIVE",
+            "xmlId": "msods1",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "cacheUrl1",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds1natDisplayName",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
+            "exampleURLs": [
+                "http://ccr.ds1nat.example.net",
+                "https://ccr.ds1nat.example.net"
+            ],
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s 1",
+            "longDesc1": "ds1nat",
+            "longDesc2": "ds1nat",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds1nat\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": "midHeader1\nmidHeader2",
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": "rr1\nrr2",
+            "regionalGeoBlocking": false,
+            "remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
+            "routingName": "ccr-ds1nat",
+            "signed": false,
+            "signingAlgorithm": "url_sig",
+            "sslKeyVersion": 2,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "type": "HTTP_LIVE_NATNL",
+            "xmlId": "ds1nat",
+            "anonymousBlockingEnabled": true
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds-with-topology",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": null,
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s top",
+            "longDesc1": "ds top",
+            "longDesc2": "ds-top",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds-top\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": null,
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://origin.topology.example.net",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 1,
+            "rangeRequestHandling": 0,
+            "regexRemap": null,
+            "regionalGeoBlocking": false,
+            "remapText": null,
+            "routingName": "cdn",
+            "signed": false,
+            "signingAlgorithm": null,
+            "sslKeyVersion": 0,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "type": "HTTP_LIVE_NATNL",
+            "xmlId": "ds-top",
+            "anonymousBlockingEnabled": false,
+            "topology": "mso-topology",
+            "firstHeaderRewrite": "first header rewrite",
+            "innerHeaderRewrite": "inner header rewrite",
+            "lastHeaderRewrite": "last header rewrite"
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds-top-req-cap",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": null,
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "",
+            "longDesc1": "",
+            "longDesc2": "",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds-top-req-cap\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": null,
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://example.org",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 0,
+            "qstringIgnore": 0,
+            "rangeRequestHandling": 0,
+            "regexRemap": null,
+            "regionalGeoBlocking": false,
+            "remapText": null,
+            "routingName": "cdn",
+            "signed": false,
+            "signingAlgorithm": null,
+            "sslKeyVersion": 0,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "topology": "top-for-ds-req",
+            "type": "HTTP",
+            "xmlId": "ds-top-req-cap",
+            "anonymousBlockingEnabled": false
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds-top-req-cap2",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": null,
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "",
+            "longDesc1": "",
+            "longDesc2": "",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds-top-req-cap2\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": null,
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": "http://example.org",
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 0,
+            "qstringIgnore": 0,
+            "rangeRequestHandling": 0,
+            "regexRemap": null,
+            "regionalGeoBlocking": false,
+            "remapText": null,
+            "routingName": "cdn",
+            "signed": false,
+            "signingAlgorithm": null,
+            "sslKeyVersion": 0,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "topology": "top-for-ds-req2",
+            "type": "HTTP",
+            "xmlId": "ds-top-req-cap2",
+            "anonymousBlockingEnabled": false
+        },
+        {
+            "active": true,
+            "cdnName": "cdn1",
+            "cacheurl": "",
+            "ccrDnsTtl": 3600,
+            "checkPath": "",
+            "consistentHashQueryParams": [],
+            "deepCachingType": "NEVER",
+            "displayName": "ds-client-steering",
+            "dnsBypassCname": null,
+            "dnsBypassIp": "",
+            "dnsBypassIp6": "",
+            "dnsBypassTtl": 30,
+            "dscp": 40,
+            "edgeHeaderRewrite": null,
+            "fqPacingRate": 0,
+            "geoLimit": 0,
+            "geoLimitCountries": "",
+            "geoLimitRedirectURL": null,
+            "geoProvider": 0,
+            "globalMaxMbps": 0,
+            "globalMaxTps": 0,
+            "httpBypassFqdn": "",
+            "infoUrl": "TBD",
+            "initialDispersion": 1,
+            "ipv6RoutingEnabled": true,
+            "lastUpdated": "2018-04-06 16:48:51+00",
+            "logsEnabled": false,
+            "longDesc": "d s client-steering",
+            "longDesc1": "ds client-steering",
+            "longDesc2": "ds-client-steering",
+            "matchList": [
+                {
+                    "pattern": ".*\\.ds-client-steering\\..*",
+                    "setNumber": 0,
+                    "type": "HOST_REGEXP"
+                }
+            ],
+            "maxDnsAnswers": 0,
+            "midHeaderRewrite": null,
+            "missLat": 41.881944,
+            "missLong": -87.627778,
+            "multiSiteOrigin": false,
+            "orgServerFqdn": null,
+            "originShield": null,
+            "profileDescription": null,
+            "profileName": null,
+            "protocol": 2,
+            "qstringIgnore": 0,
+            "rangeRequestHandling": 0,
+            "regexRemap": null,
+            "regionalGeoBlocking": false,
+            "remapText": null,
+            "routingName": "cdn",
+            "signed": false,
+            "signingAlgorithm": null,
+            "sslKeyVersion": 0,
+            "tenant": "tenant1",
+            "tenantName": "tenant1",
+            "type": "CLIENT_STEERING",
+            "xmlId": "ds-client-steering",
+            "anonymousBlockingEnabled": false
+        }
+    ],
+    "deliveryServicesRegexes": [
+        {
+            "dsName": "ds1",
+            "typeName": "HOST_REGEXP",
+            "setNumber": 1,
+            "pattern" : ".*"
+        },
+        {
+            "dsName": "ds1",
+            "typeName": "HOST_REGEXP",
+            "setNumber": 2,
+            "pattern" : "\\d+"
+        },
+        {
+            "dsName": "ds2",
+            "typeName": "HOST_REGEXP",
+            "setNumber": 1,
+            "pattern" : ".*"
+        },
+        {
+            "dsName": "ds1",
+            "typeName": "HOST_REGEXP",
+            "setNumber": 3,
+            "pattern" : ""
+        }
+    ],
+    "deliveryservicesRequiredCapabilities": [
+        {
+            "xmlID": "ds1",
+            "RequiredCapability": "foo"
+        },
+        {
+            "xmlID": "ds2",
+            "RequiredCapability": "bar"
+        },
+        {
+            "xmlID": "msods1",
+            "RequiredCapability": "bar"
+        }
+    ],
+    "topologyBasedDeliveryServicesRequiredCapabilities": [
+        {
+            "xmlID": "ds-top-req-cap",
+            "RequiredCapability": "ram"
+        },
+        {
+            "xmlID": "ds-top-req-cap",
+            "RequiredCapability": "disk"
+        },
+        {
+            "xmlID": "ds-top-req-cap2",
+            "RequiredCapability": "ram"
+        }
+    ],
+    "divisions": [
+        {
+            "name": "division1"
+        },
+        {
+            "name": "cdn-div2"
+        }
+    ],
+    "federations": [
+        {
+            "cname": "the.cname.com.",
+            "ttl": 48,
+            "description": "the description"
+        },
+        {
+            "cname": "booya.com.",
+            "ttl": 34,
+            "description": "fooya"
+        }
+    ],
+    "federation_resolvers": [
+        {
+            "ipAddress": "1.2.3.4",
+            "type": "RESOLVE4",
+            "id": 1
+        },
+        {
+            "ipAddress": "0.0.0.0/12",
+            "type": "RESOLVE4",
+            "id": 2
+        },
+        {
+            "ipAddress": "dead::babe",
+            "type": "RESOLVE6",
+            "id": 3
+        },
+        {
+            "ipAddress": "::f1d0:f00d/123",
+            "type": "RESOLVE6",
+            "id": 4
+        }
+    ],
+    "coordinates": [
+        {
+            "latitude": 1.1,
+            "longitude": 2.2,
+            "name": "coordinate1"
+        },
+        {
+            "latitude": 3.3,
+            "longitude": 4.4,
+            "name": "coordinate2"
+        },
+        {
+            "latitude": 5.5,
+            "longitude": 6.6,
+            "name": "abc-coordinate"
+        }
+    ],
+    "origins": [
+        {
+            "name": "origin1",
+            "cachegroup": "originCachegroup",
+            "deliveryService": "ds1",
+            "fqdn": "origin1.example.com",
+            "ipAddress": "1.2.3.4",
+            "ip6Address": "dead:beef:cafe::42",
+            "port": 1234,
+            "protocol": "http",
+            "tenant": "tenant1"
+        },
+        {
+            "name": "origin2",
+            "cachegroup": "originCachegroup",
+            "deliveryService": "ds2",
+            "fqdn": "origin2.example.com",
+            "ipAddress": "5.6.7.8",
+            "ip6Address": "cafe::42",
+            "port": 5678,
+            "protocol": "https",
+            "tenant": "tenant1"
+        }
+    ],
+    "parameters": [
+        {
+            "configFile": "rascal.properties",
+            "lastUpdated": "2018-01-19T19:01:21.455131+00:00",
+            "name": "health.threshold.loadavg",
+            "secure": false,
+            "value": "25.0"
+        },
+        {
+            "configFile": "rascal.properties",
+            "lastUpdated": "2018-01-19T19:01:21.472279+00:00",
+            "name": "health.threshold.availableBandwidthInKbps",
+            "secure": false,
+            "value": ">1750000"
+        },
+        {
+            "configFile": "rascal.properties",
+            "lastUpdated": "2018-01-19T19:01:21.489534+00:00",
+            "name": "history.count",
+            "secure": false,
+            "value": "30"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.434425+00:00",
+            "name": "CONFIG proxy.config.allocator.enable_reclaim",
+            "secure": false,
+            "value": "INT 0"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.435957+00:00",
+            "name": "CONFIG proxy.config.allocator.max_overage",
+            "secure": false,
+            "value": "INT 3"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.437496+00:00",
+            "name": "CONFIG proxy.config.diags.show_location",
+            "secure": false,
+            "value": "INT 0"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.439033+00:00",
+            "name": "CONFIG proxy.config.http.cache.allow_empty_doc",
+            "secure": false,
+            "value": "INT 0"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.440502+00:00",
+            "name": "LOCAL proxy.config.cache.interim.storage",
+            "secure": false,
+            "value": "STRING NULL"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.441933+00:00",
+            "name": "CONFIG proxy.config.http.parent_proxy.file",
+            "secure": false,
+            "value": "STRING parent.config"
+        },
+        {
+            "configFile": "logs_xml.config",
+            "lastUpdated": "2018-01-19T19:01:21.461206+00:00",
+            "name": "LogFormat.Name",
+            "secure": false,
+            "value": "custom_ats_2"
+        },
+        {
+            "configFile": "logs_xml.config",
+            "lastUpdated": "2018-01-19T19:01:21.462772+00:00",
+            "name": "LogObject.Format",
+            "secure": false,
+            "value": "custom_ats_2"
+        },
+        {
+            "configFile": "logs_xml.config",
+            "lastUpdated": "2018-01-19T19:01:21.464259+00:00",
+            "name": "LogObject.Filename",
+            "secure": false,
+            "value": "custom_ats_2"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.467349+00:00",
+            "name": "CONFIG proxy.config.cache.control.filename",
+            "secure": false,
+            "value": "STRING cache.config"
+        },
+        {
+            "configFile": "plugin.config",
+            "lastUpdated": "2018-01-19T19:01:21.469075+00:00",
+            "name": "regex_revalidate.so",
+            "secure": false,
+            "value": "--config regex_revalidate.config"
+        },
+        {
+            "configFile": "records.config",
+            "lastUpdated": "2018-01-19T19:01:21.49285+00:00",
+            "name": "CONFIG proxy.config.hostdb.storage_size",
+            "secure": false,
+            "value": "INT 33554432"
+        },
+        {
+            "configFile": "regex_revalidate.config",
+            "lastUpdated": "2018-01-19T19:01:21.496195+00:00",
+            "name": "maxRevalDurationDays",
+            "secure": false,
+            "value": "90"
+        },
+        {
+            "configFile": "package",
+            "lastUpdated": "2018-01-19T19:01:21.499423+00:00",
+            "name": "trafficserver",
+            "secure": false,
+            "value": "5.3.2-765.f4354b9.el7.centos.x86_64"
+        },
+        {
+            "configFile": "global",
+            "lastUpdated": "2018-01-19T19:01:21.501151+00:00",
+            "name": "use_tenancy",
+            "secure": false,
+            "value": "1"
+        },
+        {
+            "configFile": "global",
+            "lastUpdated": "2020-04-21T05:19:43.853831+00:00",
+            "name": "tm.instance_name",
+            "secure": false,
+            "value": "Traffic Ops API Tests"
+        }
+    ],
+    "physLocations": [
+        {
+            "address": "1234 mile high circle",
+            "city": "Denver",
+            "comments": null,
+            "email": null,
+            "lastUpdated": "2018-01-19T21:19:32.081465+00:00",
+            "name": "Denver",
+            "phone": "303-111-1111",
+            "poc": null,
+            "region": "region1",
+            "shortName": "denver",
+            "state": "CO",
+            "zip": "80202"
+        },
+        {
+            "address": "1234 green way",
+            "city": "Boulder",
+            "comments": null,
+            "email": null,
+            "lastUpdated": "2018-01-19T21:19:32.086195+00:00",
+            "name": "Boulder",
+            "phone": "303-222-2222",
+            "poc": null,
+            "region": "region1",
+            "shortName": "boulder",
+            "state": "CO",
+            "zip": "80301"
+        },
+        {
+            "address": "1234 southern way",
+            "city": "Atlanta",
+            "comments": null,
+            "email": null,
+            "lastUpdated": "2018-01-19T21:19:32.089538+00:00",
+            "name": "HotAtlanta",
+            "phone": "404-222-2222",
+            "poc": null,
+            "region": "region1",
+            "shortName": "atlanta",
+            "state": "GA",
+            "zip": "30301"
+        }
+    ],
+    "profiles": [
+        {
+            "cdnName": "cdn1",
+            "description": "Edge Cache - Apache Traffic Server",
+            "name": "ATS_EDGE_TIER_CACHE",
+            "routingDisabled": false,
+            "type": "ATS_PROFILE",
+            "params": [
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.proxy_name",
+                    "secure": false,
+                    "value": "STRING __HOSTNAME__"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.config_dir",
+                    "secure": false,
+                    "value": "STRING /opt/trafficserver/etc/trafficserver"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.admin.user_id",
+                    "secure": false,
+                    "value": "STRING ats"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.server_ports",
+                    "secure": false,
+                    "value": "STRING 80 80:ipv6"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.insert_response_via_str",
+                    "secure": false,
+                    "value": "INT 3"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.parent_proxy_routing_enable",
+                    "secure": false,
+                    "value": "INT 1"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.parent_proxy.retry_time",
+                    "secure": false,
+                    "value": "INT 60"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.connect_attempts_timeout",
+                    "secure": false,
+                    "value": "INT 10"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.cache.required_headers",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.enable_http_stats",
+                    "secure": false,
+                    "value": "INT 1"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.dns.round_robin_nameservers",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.log.max_space_mb_for_logs",
+                    "secure": false,
+                    "value": "INT 512"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.log.max_space_mb_headroom",
+                    "secure": false,
+                    "value": "INT 50"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.log.logfile_dir",
+                    "secure": false,
+                    "value": "STRING /var/log/trafficserver"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.reverse_proxy.enabled",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.diags.debug.enabled",
+                    "secure": false,
+                    "value": "INT 1"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.slow.log.threshold",
+                    "secure": false,
+                    "value": "INT 10000"
+                },
+                {
+                    "configFile": "cache.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "hosting.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "parent.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "plugin.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "storage.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "volume.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver/"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.url_remap.remap_required",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "rascal.properties",
+                    "name": "health.threshold.queryTime",
+                    "secure": false,
+                    "value": "1000"
+                },
+                {
+                    "configFile": "rascal.properties",
+                    "name": "health.polling.url",
+                    "secure": false,
+                    "value": "http://${hostname}/_astats?application=&inf.name=${interface_name}"
+                },
+                {
+                    "configFile": "storage.config",
+                    "name": "Disk_Volume",
+                    "secure": false,
+                    "value": "1"
+                },
+                {
+                    "configFile": "rascal.properties",
+                    "name": "health.connection.timeout",
+                    "secure": false,
+                    "value": "2000"
+                },
+                {
+                    "configFile": "chkconfig",
+                    "name": "trafficserver",
+                    "secure": false,
+                    "value": "0:off\t1:off\t2:on\t3:on\t4:on\t5:on\t6:off"
+                },
+                {
+                    "configFile": "regex_revalidate.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.exec_thread.autoconfig",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "astats.config",
+                    "name": "allow_ip",
+                    "secure": false,
+                    "value": "127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
+                },
+                {
+                    "configFile": "astats.config",
+                    "name": "allow_ip6",
+                    "secure": false,
+                    "value": "::1/128,fc01:9400:1000:8::/64"
+                },
+                {
+                    "configFile": "astats.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver"
+                },
+                {
+                    "configFile": "astats.config",
+                    "name": "path",
+                    "secure": false,
+                    "value": "_astats"
+                },
+                {
+                    "configFile": "astats.config",
+                    "name": "record_types",
+                    "secure": false,
+                    "value": "122"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.http.transaction_active_timeout_in",
+                    "secure": false,
+                    "value": "INT 0"
+                },
+                {
+                    "configFile": "records.config",
+                    "name": "CONFIG proxy.config.body_factory.template_sets_dir",
+                    "secure": false,
+                    "value": "STRING /opt/trafficserver/etc/trafficserver/body_factory"
+                },
+                {
+                    "configFile": "storage.config",
+                    "name": "Drive_Letters",
+                    "secure": false,
+                    "value": "cache"
+                },
+                {
+                    "configFile": "ip_allow.config",
+                    "name": "location",
+                    "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver"
+                },
+                {
+                    "configFile": "storage.config",
+                    "name": "Drive_Prefix",
+                    "secure": false,
+                    "value": "/var/trafficserver/"
+                },
+                {
+                    "configFile": "set_dscp_0.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_10.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_12.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_14.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_18.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_20.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_22.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_26.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_28.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_30.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_34.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_36.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_38.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_8.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_16.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_24.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_32.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_40.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_48.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_56.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+                {
+                    "configFile": "set_dscp_37.config",
+                    "name": "location",
+                    "value": "/opt/trafficserver/etc/trafficserver/dscp"
+                },
+        	      {
+            	      "configFile": "package",
+            	      "lastUpdated": "2018-01-19T19:01:21.499423+00:00",
+            	      "name": "trafficserver",
+            	      "secure": true,
+            	      "value": "8.0.8-19.77cb23a.el7.x86_64"
+        	      },
+                {
+                    "configFile": "remap.config",
+                    "name": "location",
+            	      "secure": false,
+                    "value": "/opt/trafficserver/etc/trafficserver"
+                }
+            ]
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "edge1 description",
+            "lastUpdated": "2018-03-02T17:27:11.818418+00:00",
+            "name": "EDGE1",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn2",
+            "description": "edge2 description",
+            "lastUpdated": "2018-03-02T17:27:11.818418+00:00",
+            "name": "EDGEInCDN2",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn4",
+            "description": "edge2 description",
+            "lastUpdated": "2018-03-02T17:27:11.818418+00:00",
+            "name": "EDGE2",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn2",
+            "description": "cdn2 edge description",
+            "name": "CDN2_EDGE",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "mid description",
+            "lastUpdated": "2018-03-02T17:27:11.80173+00:00",
+            "name": "MID1",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn2",
+            "description": "mid description",
+            "lastUpdated": "2018-03-02T17:27:11.80173+00:00",
+            "name": "MID2",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "origin description",
+            "lastUpdated": "2018-03-02T17:27:11.80173+00:00",
+            "name": "ORIGIN1",
+            "routing_disabled": false,
+            "type": "ORG_PROFILE"
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "cdn1 description",
+            "lastUpdated": "2018-03-02T17:27:11.80452+00:00",
+            "name": "CCR1",
+            "routing_disabled": false,
+            "type": "TR_PROFILE"
+        },
+        {
+            "cdnName": "cdn2",
+            "description": "cdn2 description",
+            "lastUpdated": "2018-03-02T17:27:11.807948+00:00",
+            "name": "CCR2",
+            "routing_disabled": false,
+            "type": "TR_PROFILE"
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "rascal description",
+            "lastUpdated": "2018-03-02T17:27:11.813052+00:00",
+            "name": "RASCAL1",
+            "routing_disabled": false,
+            "type": "TM_PROFILE",
+            "params": [
+                {
+                    "configFile": "rascal.properties",
+                    "name": "health.threshold.queryTime",
+                    "secure": false,
+                    "value": "1000"
+                },
+                {
+                    "configFile": "rascal.properties",
+                    "name": "health.polling.url",
+                    "secure": false,
+                    "value": "http://${hostname}/_astats?application=&inf.name=${interface_name}"
+                },
+                {
+                    "configFile": "rascal-config.txt",
+                    "lastUpdated": "2018-01-19T19:01:21.472279+00:00",
+                    "name": "peers.polling.interval",
+                    "secure": false,
+                    "value": "60"
+                },
+                {
+                    "configFile": "rascal-config.txt",
+                    "lastUpdated": "2018-01-19T19:01:21.472279+00:00",
+                    "name": "health.polling.interval",
+                    "secure": false,
+                    "value": "30"
+                }
+            ]
+        },
+        {
+            "cdnName": "cdn1",
+            "description": "mso origin description",
+            "lastUpdated": "2018-03-02T17:27:11.80173+00:00",
+            "name": "MSO",
+            "routing_disabled": false,
+            "type": "ORG_PROFILE"
+        },
+        {
+            "cdnName": "cdn2",
+            "description": "cdn2 mid description",
+            "name": "CDN2_MID",
+            "routing_disabled": false,
+            "type": "ATS_PROFILE"
+        }
+    ],
+    "regions": [
+        {
+            "divisionName": "division1",
+            "name": "region1"
+        },
+        {
+            "divisionName": "cdn-div2",
+            "name": "cdn-region2"
+        }
+    ],
+    "roles": [
+        {
+            "name": "new_admin",
+            "description": "super-user 2",
+            "privLevel": 30,
+            "capabilities": [
+                "all-read",
+                "all-write",
+                "cdn-read"
+            ]
+        },
+        {
+            "name": "bad_admin",
+            "description": "super-user 3",
+            "privLevel": 30,
+            "capabilities": [
+                "all-read",
+                "all-write",
+                "invalid-capability"
+            ]
+        }
+    ],
+    "servers": [
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-edge-01",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.21/30",
+                            "gateway": "127.0.0.21",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:8::1/64",
+                            "gateway": "2345:1234:12:8::1",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-edge-01\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn2",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "cdn2-test-edge",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "0.0.0.0/0",
+                            "gateway": "0.0.0.0",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 1500,
+                    "name": "eth0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGEInCDN2",
+            "rack": "",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": ""
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "guid": null,
+            "hostName": "influxdb02",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.11/22",
+                            "gateway": "127.0.0.11",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:8::2/64",
+                            "gateway": "2345:1234:12:8::2",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 1500,
+                    "name": "eth1"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 8086,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": ""
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-router-01",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.12/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:8::3/64",
+                            "gateway": "2345:1234:12:8::3",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-router-01\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup2",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-edge-03",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:2::4/64",
+                            "gateway": "2345:1234:12:2::4",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.13/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "ATS_EDGE_TIER_CACHE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-edge-03\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-edge-14",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:8::5/64",
+                            "gateway": "2345:1234:12:8::5",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.14/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-edge-14\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-edge-15",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:d::6/64",
+                            "gateway": "2345:1234:12:d::6",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.15/30",
+                            "gateway": "127.0.0.7",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "ipNetmask": "255.255.255.252",
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-edge-15\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "parentCachegroup",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-mid-16",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:d::7/64",
+                            "gateway": "2345:1234:12:d::7",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.16/30",
+                            "gateway": "127.0.0.7",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "MID1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false,
+            "xmppId": "atlanta-mid-16\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "parentCachegroup",
+            "cdnName": "cdn2",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-mid-17",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:17:d::7/64",
+                            "gateway": "2345:1234:17:d::7",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.17/30",
+                            "gateway": "127.0.0.17",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "MID2",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false,
+            "xmppId": "atlanta-mid-17\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-org-1",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:d::8/64",
+                            "gateway": "2345:1234:12:d::8",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.17/30",
+                            "gateway": "127.0.0.17",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-org-1\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-org-2",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.18/30",
+                            "gateway": "127.0.0.18",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:d::9/64",
+                            "gateway": "2345:1234:12:d::9",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "atlanta-org-1\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "ga.atlanta.kabletown.net",
+            "guid": null,
+            "hostName": "atlanta-mid-01",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.2/30",
+                            "gateway": "127.0.0.2",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:9::10/64",
+                            "gateway": "2345:1234:12:9::10",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false,
+            "xmppId": "atlanta-mid-01\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "guid": null,
+            "hostName": "rascal01",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.4/30",
+                            "gateway": "127.0.0.4",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:b::11/64",
+                            "gateway": "2345:1234:12:b::11",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "RASCAL1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 81,
+            "type": "RASCAL",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup2",
+            "cdnName": "cdn2",
+            "domainName": "kabletown2.net",
+            "guid": null,
+            "hostName": "edge1-cdn2",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.31/24",
+                            "gateway": "127.0.0.4",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:b::13/64",
+                            "gateway": "2345:1234:12:b::13",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 81,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup1",
+            "cdnName": "cdn1",
+            "domainName": "docker_default",
+            "guid": null,
+            "hostName": "traffic_vault",
+            "httpsPort": 8087,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.1/22",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:b::12/64",
+                            "gateway": "2345:1234:12:b::12",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 1500,
+                    "name": "eth1"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "ONLINE",
+            "tcpPort": 8088,
+            "type": "RIAK",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": ""
+        },
+        {
+            "cachegroup": "multiOriginCachegroup",
+            "cdnName": "cdn1",
+            "domainName": "ga.denver.kabletown.net",
+            "guid": null,
+            "hostName": "denver-mso-org-01",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "127.0.0.1/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        },
+                        {
+                            "address": "2345:1234:12:8::20/64",
+                            "gateway": "2345:1234:12:8::20",
+                            "serviceAddress": false
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "lastUpdated": "2018-03-28T17:30:00.220351+00:00",
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "MSO",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "ORG",
+            "updPending": false,
+            "xmppId": "denver-mso-org-01\\\\@ocdn.kabletown.net",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown2.net",
+            "guid": null,
+            "hostName": "edge1-cdn1-cg3",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "::13/64",
+                            "gateway": "2345:1234:12:b::13",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.100/24",
+                            "gateway": "127.0.0.4",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 81,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "cachegroup3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown2.net",
+            "guid": null,
+            "hostName": "edge2-cdn1-cg3",
+            "httpsPort": 443,
+            "iloIpAddress": "",
+            "iloIpGateway": "",
+            "iloIpNetmask": "",
+            "iloPassword": "",
+            "iloUsername": "",
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "::14/64",
+                            "gateway": "2345:1234:12:b::13",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.101/24",
+                            "gateway": "127.0.0.4",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "maxBandwidth": null,
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "mgmtIpAddress": "",
+            "mgmtIpGateway": "",
+            "mgmtIpNetmask": "",
+            "offlineReason": null,
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "routerHostName": "",
+            "routerPortName": "",
+            "status": "REPORTED",
+            "tcpPort": 81,
+            "type": "EDGE",
+            "updPending": false,
+            "xmppId": "",
+            "xmppPasswd": "X"
+        },
+        {
+            "cachegroup": "topology-edge-cg-01",
+            "cdnName": "cdn1",
+            "domainName": "edge-01.forked-topology.kabletown.net",
+            "hostName": "topology-edge-01",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:2::4/64",
+                            "gateway": "2345:1234:12:2::4",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.19/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-edge-cg-02",
+            "cdnName": "cdn1",
+            "domainName": "edge-02.forked-topology.kabletown.net",
+            "hostName": "topology-edge-02",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:2::4/64",
+                            "gateway": "2345:1234:12:2::4",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.20/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-04",
+            "cdnName": "cdn1",
+            "domainName": "mid-04.forked-topology.kabletown.net",
+            "hostName": "topology-mid-04",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2345:1234:12:2::4/64",
+                            "gateway": "2345:1234:12:2::4",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "127.0.0.13/30",
+                            "gateway": "127.0.0.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "MID1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc1",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-mid-01",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::2/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.2/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "MID1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc1",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-mid-02",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::3/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.3/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "MID1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc1",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-mid-03",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::4/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.4/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "MID1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc2",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-01",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::5/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.5/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc2",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-02",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::6/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.6/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc2",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-03",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::7/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.7/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-04",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::8/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.8/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-05",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::9/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.9/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-06",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::10/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.10/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc2",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-07",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::11/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.11/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc3",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-edge-08",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.12/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "dtrc1",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "dtrc-mid-04",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::13/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.13/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "cachegroup3",
+            "cdnName": "cdn1",
+            "domainName": "kabletown.net",
+            "hostName": "edgeInCachegroup3",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.13/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "EDGE1",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "parentCachegroup",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInParentCachegroup",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.14/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "secondaryCachegroup",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInSecondaryCachegroup",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.15/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "fallback1",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "edgeInFallback1",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.16/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "fallback2",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "edgeInFallback2",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.17/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "parentCachegroup2",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInParentCachegroup2",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.18/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-edge-cg-01",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "edgeInTopologyEdgeCg01",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.19/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-edge-cg-02",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "edgeInTopologyEdgeCg02",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.20/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_EDGE",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "EDGE",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-01",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg01",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.21/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-02",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg02",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.22/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-03",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg03",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.23/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-04",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg04",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.24/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-05",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg05",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.25/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-06",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg06",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.26/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        },
+        {
+            "cachegroup": "topology-mid-cg-07",
+            "cdnName": "cdn2",
+            "domainName": "kabletown.net",
+            "hostName": "midInTopologyMidCg07",
+            "httpsPort": 443,
+            "interfaces": [
+                {
+                    "ipAddresses": [
+                        {
+                            "address": "2001:db8:dead:beef::12/64",
+                            "gateway": "2001:db8:dead:beef::1",
+                            "serviceAddress": false
+                        },
+                        {
+                            "address": "192.0.2.27/24",
+                            "gateway": "192.0.2.1",
+                            "serviceAddress": true
+                        }
+                    ],
+                    "monitor": true,
+                    "mtu": 9000,
+                    "name": "bond0"
+                }
+            ],
+            "physLocation": "Denver",
+            "profile": "CDN2_MID",
+            "rack": "RR 119.02",
+            "revalPending": false,
+            "status": "REPORTED",
+            "tcpPort": 80,
+            "type": "MID",
+            "updPending": false
+        }
+    ],
+    "serverCapabilities": [
+        {
+            "name": "foo"
+        },
+        {
+            "name": "bar"
+        },
+        {
+            "name": "ram"
+        },
+        {
+            "name": "disk"
+        },
+        {
+            "name": "asdf"
+        }
+    ],
+    "serverServerCapabilities": [
+        {
+            "serverHostName": "atlanta-edge-03",
+            "serverCapability": "foo"
+        },
+        {
+            "serverHostName": "atlanta-edge-03",
+            "serverCapability": "bar"
+        },
+        {
+            "serverHostName": "dtrc-mid-01",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-mid-01",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-mid-02",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-mid-02",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-edge-01",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-edge-01",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-edge-01",
+            "serverCapability": "asdf"
+        },
+        {
+            "serverHostName": "dtrc-edge-02",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-edge-02",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-edge-04",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-edge-04",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-edge-05",
+            "serverCapability": "ram"
+        },
+        {
+            "serverHostName": "dtrc-edge-05",
+            "serverCapability": "disk"
+        },
+        {
+            "serverHostName": "dtrc-edge-07",
+            "serverCapability": "asdf"
+        },
+        {
+            "serverHostName": "dtrc-edge-08",
+            "serverCapability": "asdf"
+        },
+        {
+            "serverHostName": "dtrc-mid-04",
+            "serverCapability": "asdf"
+        }
+    ],
+    "serviceCategories": [
+        {
+            "name": "serviceCategory1"
+        },
+        {
+            "name": "barServiceCategory2"
+        }
+    ],
+    "staticdnsentries": [
+        {
+            "address": "192.168.0.1",
+            "cachegroup": "cachegroup2",
+            "deliveryservice": "ds1",
+            "host": "host2",
+            "type": "A_RECORD",
+            "ttl": 10
+        },
+        {
+            "address": "this.is.a.hostname.",
+            "cachegroup": "cachegroup1",
+            "deliveryservice": "ds1",
+            "host": "host1",
+            "type": "CNAME_RECORD",
+            "ttl": 0
+        },
+        {
+            "address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+            "cachegroup": "cachegroup2",
+            "deliveryservice": "ds1",
+            "host": "host3",
+            "ttl": 10,
+            "type": "AAAA_RECORD"
+        }
+    ],
+    "statuses": [
+        {
+            "description": "Edge: Puts server in CCR config file in this state, but CCR will never route traffic to it. Mid: Server will not be included in parent.config files for its edge caches",
+            "name": "OFFLINE"
+        },
+        {
+            "description": "Edge: Puts server in CCR config file in this state, and CCR will always route traffic to it. Mid: Server will be included in parent.config files for its edges",
+            "name": "ONLINE"
+        },
+        {
+            "description": "Edge: Puts server in CCR config file in this state, and CCR will adhere to the health protocol. Mid: N/A for now",
+            "name": "REPORTED"
+        },
+        {
+            "description": "Temporary down. Edge: XMPP client will send status OFFLINE to CCR, otherwise similar to REPORTED. Mid: Server will not be included in parent.config files for its edge caches",
+            "name": "ADMIN_DOWN"
+        },
+        {
+            "description": "Edge: 12M will not include caches in this state in CCR config files. Mid: N/A for now",
+            "name": "CCR_IGNORE"
+        },
+        {
+            "description": "Pre Production. Not active in any configuration.",
+            "name": "PRE_PROD"
+        },
+        {
+            "name": "TEST_NULL_DESCRIPTION"
+        }
+    ],
+    "tenants": [
+        {
+            "active": true,
+            "name": "tenant1",
+            "parentName": "root"
+        },
+        {
+            "active": false,
+            "name": "tenant2",
+            "parentName": "tenant1"
+        },
+        {
+            "active": true,
+            "name": "tenant3",
+            "parentName": "tenant2"
+        },
+        {
+            "active": true,
+            "name": "tenant4",
+            "parentName": "root"
+        }
+    ],
+    "topologies": [
+        {
+            "name": "mso-topology",
+            "description": "a multi-site origin topology",
+            "nodes": [
+                {
+                    "cachegroup": "multiOriginCachegroup",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "parentCachegroup",
+                    "parents": [0]
+                },
+                {
+                    "cachegroup": "cachegroup2",
+                    "parents": [1]
+                }
+            ]
+        },
+        {
+            "name": "another-topology",
+            "description": "another topology",
+            "nodes": [
+                {
+                    "cachegroup": "parentCachegroup",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "cachegroup1",
+                    "parents": [
+                        0
+                    ]
+                },
+                {
+                    "cachegroup": "secondaryCachegroup",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "cachegroup2",
+                    "parents": [
+                        2
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "secondary-parents",
+            "description": "A topology with secondary parents",
+            "nodes": [
+                {
+                    "cachegroup": "parentCachegroup",
+                    "parent": "",
+                    "secParent": "",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "cachegroup1",
+                    "parent": "parentCachegroup",
+                    "secParent": "secondaryCachegroup",
+                    "parents": [
+                        0,
+                        2
+                    ]
+                },
+                {
+                    "cachegroup": "secondaryCachegroup",
+                    "parent": "",
+                    "secParent": "",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "fallback1",
+                    "parent": "secondaryCachegroup",
+                    "secParent": "parentCachegroup",
+                    "parents": [
+                        2,
+                        0
+                    ]
+                },
+                {
+                    "cachegroup": "fallback2",
+                    "parent": "secondaryCachegroup",
+                    "secParent": "parentCachegroup",
+                    "parents": [
+                        2,
+                        0
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "4-tiers",
+            "description": "A 4-tier topology",
+            "nodes": [
+                {
+                    "cachegroup": "parentCachegroup",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "parentCachegroup2",
+                    "parents": [
+                        0
+                    ]
+                },
+                {
+                    "cachegroup": "cachegroup1",
+                    "parents": [
+                        1
+                    ]
+                },
+                {
+                    "cachegroup": "secondaryCachegroup",
+                    "parents": [
+                        1,
+                        0
+                    ]
+                },
+                {
+                    "cachegroup": "fallback1",
+                    "parents": [
+                        3
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "forked-topology",
+            "description": "This topology stems from 2 ancestors",
+            "nodes": [
+                {
+                    "cachegroup": "topology-edge-cg-01",
+                    "parents": [
+                        2
+                    ]
+                },
+                {
+                    "cachegroup": "topology-edge-cg-02",
+                    "parents": [
+                        6
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-01",
+                    "parents": [
+                        3
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-02",
+                    "parents": [
+                        4
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-03",
+                    "parents": [
+                        5
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-04",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "topology-mid-cg-05",
+                    "parents": [
+                        7
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-06",
+                    "parents": [
+                        8
+                    ]
+                },
+                {
+                    "cachegroup": "topology-mid-cg-07",
+                    "parents": []
+                }
+            ]
+        },
+        {
+            "name": "top-for-ds-req",
+            "description": "a topology",
+            "nodes": [
+                {
+                    "cachegroup": "dtrc1",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "dtrc2",
+                    "parents": [0]
+                },
+                {
+                    "cachegroup": "dtrc3",
+                    "parents": [0]
+                }
+            ]
+        },
+        {
+            "name": "top-for-ds-req2",
+            "description": "a topology",
+            "nodes": [
+                {
+                    "cachegroup": "dtrc1",
+                    "parents": []
+                },
+                {
+                    "cachegroup": "dtrc2",
+                    "parents": [0]
+                }
+            ]
+        }
+    ],
+    "types": [
+        {
+            "description": "Host header regular expression",
+            "lastUpdated": "2018-03-02T19:13:46.788583+00:00",
+            "name": "HOST_REGEXP",
+            "useInTable": "regex"
+        },
+        {
+            "description": "DNS Content routing, RAM cache, National",
+            "lastUpdated": "2018-03-02T19:13:46.792319+00:00",
+            "name": "DNS_LIVE_NATNL",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Other CDN (CDS-IS, Akamai, etc)",
+            "lastUpdated": "2018-03-02T19:13:46.793921+00:00",
+            "name": "OTHER_CDN",
+            "useInTable": "server"
+        },
+        {
+            "description": "Client-Controlled Steering Delivery Service",
+            "lastUpdated": "2018-03-02T19:13:46.795291+00:00",
+            "name": "CLIENT_STEERING",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "influxdb type",
+            "lastUpdated": "2018-03-02T19:13:46.796707+00:00",
+            "name": "INFLUXDB",
+            "useInTable": "server"
+        },
+        {
+            "description": "riak type",
+            "lastUpdated": "2018-03-02T19:13:46.798008+00:00",
+            "name": "RIAK",
+            "useInTable": "server"
+        },
+        {
+            "description": "Origin",
+            "lastUpdated": "2018-03-02T19:13:46.799404+00:00",
+            "name": "ORG",
+            "useInTable": "server"
+        },
+        {
+            "description": "HTTP Content routing cache in RAM ",
+            "lastUpdated": "2018-03-02T19:13:46.800738+00:00",
+            "name": "HTTP_LIVE",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Active Directory User",
+            "lastUpdated": "2018-03-02T19:13:46.802044+00:00",
+            "name": "ACTIVE_DIRECTORY",
+            "useInTable": "tm_user"
+        },
+        {
+            "description": "federation type resolve4",
+            "lastUpdated": "2018-03-02T19:13:46.803471+00:00",
+            "name": "RESOLVE4",
+            "useInTable": "federation"
+        },
+        {
+            "description": "Static DNS A entry",
+            "lastUpdated": "2018-03-02T19:13:46.804776+00:00",
+            "name": "A_RECORD",
+            "useInTable": "staticdnsentry"
+        },
+        {
+            "description": "Local User",
+            "lastUpdated": "2018-03-02T19:13:46.806035+00:00",
+            "name": "LOCAL",
+            "useInTable": "tm_user"
+        },
+        {
+            "description": "Weighted steering target",
+            "lastUpdated": "2018-03-02T19:13:46.80748+00:00",
+            "name": "STEERING_WEIGHT",
+            "useInTable": "steering_target"
+        },
+        {
+            "description": "HTTP Content routing, RAM cache, National",
+            "lastUpdated": "2018-03-02T19:13:46.808911+00:00",
+            "name": "HTTP_LIVE_NATNL",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Ops hosts for management",
+            "lastUpdated": "2018-03-02T19:13:46.810576+00:00",
+            "name": "TOOLS_SERVER",
+            "useInTable": "server"
+        },
+        {
+            "description": "Path regular expression",
+            "lastUpdated": "2018-03-02T19:13:46.812049+00:00",
+            "name": "PATH_REGEXP",
+            "useInTable": "regex"
+        },
+        {
+            "description": "Static DNS CNAME entry",
+            "lastUpdated": "2018-03-02T19:13:46.813461+00:00",
+            "name": "CNAME_RECORD",
+            "useInTable": "staticdnsentry"
+        },
+        {
+            "description": "Kabletown Content Router",
+            "lastUpdated": "2018-03-02T19:13:46.814833+00:00",
+            "name": "CCR",
+            "useInTable": "server"
+        },
+        {
+            "description": "Origin Cachegroup",
+            "lastUpdated": "2018-03-02T19:13:46.816199+00:00",
+            "name": "ORG_LOC",
+            "useInTable": "cachegroup"
+        },
+        {
+            "description": "Mid Cachegroup",
+            "lastUpdated": "2018-03-02T19:13:46.816199+00:00",
+            "name": "MID_LOC",
+            "useInTable": "cachegroup"
+        },
+        {
+            "description": "Edge Cache",
+            "lastUpdated": "2018-03-02T19:13:46.817689+00:00",
+            "name": "EDGE",
+            "useInTable": "server"
+        },
+        {
+            "description": "Ordered steering target",
+            "lastUpdated": "2018-03-02T19:13:46.81913+00:00",
+            "name": "STEERING_ORDER",
+            "useInTable": "steering_target"
+        },
+        {
+            "description": "DNS Content Routing",
+            "lastUpdated": "2018-03-02T19:13:46.820528+00:00",
+            "name": "DNS",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "federation type resolve6",
+            "lastUpdated": "2018-03-02T19:13:46.822161+00:00",
+            "name": "RESOLVE6",
+            "useInTable": "federation"
+        },
+        {
+            "description": "Static DNS AAAA entry",
+            "lastUpdated": "2018-03-02T19:13:46.823506+00:00",
+            "name": "AAAA_RECORD",
+            "useInTable": "staticdnsentry"
+        },
+        {
+            "description": "HTTP Content Routing, no caching",
+            "lastUpdated": "2018-03-02T19:13:46.824798+00:00",
+            "name": "HTTP_NO_CACHE",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "any_map type",
+            "lastUpdated": "2018-03-02T19:13:46.826411+00:00",
+            "name": "ANY_MAP",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Steering Delivery Service",
+            "lastUpdated": "2018-03-02T19:13:46.827779+00:00",
+            "name": "STEERING",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Edge Cachegroup",
+            "lastUpdated": "2018-03-02T19:13:46.829249+00:00",
+            "name": "EDGE_LOC",
+            "useInTable": "cachegroup"
+        },
+        {
+            "description": "HTTP Content routing cache ",
+            "lastUpdated": "2018-03-02T19:13:46.830862+00:00",
+            "name": "HTTP",
+            "useInTable": "deliveryservice"
+        },
+        {
+            "description": "Mid Tier Cache",
+            "lastUpdated": "2018-03-02T19:13:46.832327+00:00",
+            "name": "MID",
+            "useInTable": "server"
+        },
+        {
+            "description": "Traffic Monitor (Rascal)",
+            "lastUpdated": "2018-03-02T19:13:46.832327+00:00",
+            "name": "RASCAL",
+            "useInTable": "server"
+        }
+    ],
+    "users": [
+        {
+            "addressLine1": "address of admin",
+            "addressLine2": "",
+            "city": "Anywhere",
+            "company": "Comcast",
+            "country": "USA",
+            "email": "admin@example.com",
+            "fullName": "Fred the admin",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "810-555-9876",
+            "postalCode": "55443",
+            "publicSshKey": "",
+            "role": 4,
+            "stateOrProvince": "LA",
+            "tenant": "root",
+            "token": "test",
+            "uid": 0,
+            "username": "adminuser"
+        },
+        {
+            "addressLine1": "address of disallowed",
+            "addressLine2": "place",
+            "city": "somewhere",
+            "company": "else",
+            "country": "UK",
+            "email": "disallowed@example.com",
+            "fullName": "Me me",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "",
+            "postalCode": "",
+            "publicSshKey": "",
+            "registrationSent": "",
+            "role": 1,
+            "stateOrProvince": "",
+            "tenant": "tenant1",
+            "token": "quest",
+            "uid": 0,
+            "username": "disalloweduser"
+        },
+        {
+            "addressLine1": "address of readonly",
+            "addressLine2": "place",
+            "city": "somewhere",
+            "company": "else",
+            "country": "UK",
+            "email": "readonly@example.com",
+            "fullName": "Readonly User",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "",
+            "postalCode": "",
+            "publicSshKey": "",
+            "registrationSent": "",
+            "role": 2,
+            "stateOrProvince": "",
+            "tenant": "tenant1",
+            "uid": 0,
+            "username": "readonlyuser"
+        },
+        {
+            "addressLine1": "address of admin",
+            "addressLine2": "",
+            "city": "Anywhere",
+            "company": "Comcast",
+            "country": "USA",
+            "email": "tenant3user@example.com",
+            "fullName": "Fred the admin",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "810-555-9876",
+            "postalCode": "55443",
+            "publicSshKey": "",
+            "role": 4,
+            "stateOrProvince": "LA",
+            "tenant": "tenant3",
+            "uid": 0,
+            "username": "tenant3user"
+        },
+        {
+            "addressLine1": "address of admin",
+            "addressLine2": "",
+            "city": "Anywhere",
+            "company": "Comcast",
+            "country": "USA",
+            "email": "tenant4user@example.com",
+            "fullName": "Fred the admin",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "810-555-9876",
+            "postalCode": "55443",
+            "publicSshKey": "",
+            "role": 4,
+            "stateOrProvince": "LA",
+            "tenant": "tenant4",
+            "uid": 0,
+            "username": "tenant4user"
+        },
+        {
+            "addressLine1": "address of ops",
+            "addressLine2": "place",
+            "city": "somewhere",
+            "company": "else",
+            "country": "UK",
+            "email": "ops@example.com",
+            "fullName": "Operations User",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "",
+            "postalCode": "",
+            "publicSshKey": "",
+            "registrationSent": "",
+            "role": 3,
+            "stateOrProvince": "",
+            "tenant": "root",
+            "uid": 0,
+            "username": "opsuser"
+        },
+        {
+            "addressLine1": "address of steering",
+            "addressLine2": "place",
+            "city": "somewhere",
+            "company": "else",
+            "country": "UK",
+            "email": "steering@example.com",
+            "fullName": "Steering User",
+            "gid": 0,
+            "localPasswd": "pa$$word",
+            "confirmLocalPasswd": "pa$$word",
+            "newUser": false,
+            "phoneNumber": "",
+            "postalCode": "",
+            "publicSshKey": "",
+            "registrationSent": "",
+            "role": 6,
+            "stateOrProvince": "",
+            "tenant": "root",
+            "uid": 0,
+            "username": "steering"
+        }
+    ],
+    "steeringTargets": [
+        {
+            "deliveryService": "ds1",
+            "target": "ds2",
+            "value": 42,
+            "type": "STEERING_WEIGHT"
+        }
+    ],
+    "servercheck_extensions": [
+        {
+            "name": "ILO_PING",
+            "version": "1.0.0",
+            "info_url": "-",
+            "script_file": "ToPingCheck.pl",
+            "isactive": 1,
+            "description": "",
+            "servercheck_short_name": "ILO",
+            "type": "CHECK_EXTENSION_BOOL"
+        },
+        {
+            "name": "ORT_ERROR_COUNT",
+            "version": "1.0.0",
+            "info_url": "-",
+            "script_file": "ToORTCheck.pl",
+            "isactive": 1,
+            "description": "",
+            "servercheck_short_name": "ORT",
+            "type": "CHECK_EXTENSION_NUM"
+        }
+    ],
+    "serverchecks": [
+        {
+            "servercheck_short_name": "ILO",
+            "host_name": "atlanta-edge-01",
+            "value": 1
+        },
+        {
+            "servercheck_short_name": "ORT",
+            "host_name": "atlanta-edge-01",
+            "value": 13
+        }
+    ],
+    "invalidationJobs": [
+        {
+            "deliveryService": "ds1",
+            "regex": "/.*",
+            "startTime": 4117118271000,
+            "ttl": "121m"
+        },
+        {
+            "deliveryService": "ds1",
+            "regex": "/foo",
+            "startTime": 4117118271000,
+            "ttl": 2160
+        },
+        {
+            "deliveryService":  "ds2",
+            "regex": "\\/some-path?.+\\.jpg",
+            "startTime": "2100-06-19T13:57:51-06:00",
+            "ttl": 2.1
+        }
+    ],
+    "statsSummaries": [
+        {
+            "cdnName": "cdn1",
+            "deliveryServiceName": "all",
+            "statName": "daily_maxgbps",
+            "statValue": 5,
+            "summaryTime": "2019-01-01T00:00:00-06:00"
+        },
+        {
+            "cdnName": "cdn1",
+            "deliveryServiceName": "all",
+            "statName": "daily_bytesserved",
+            "statValue": 1000,
+            "summaryTime": "2019-01-01T00:00:00-06:00"
+        }
+    ],
+    "capabilities": [
+        {
+            "name": "test",
+            "description": "quest"
+        },
+        {
+            "name": "foo",
+            "description": "bar"
+        }
+    ]
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups.go b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups.go
new file mode 100644
index 0000000..2f15f3f
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups.go
@@ -0,0 +1,116 @@
+package tcdata
+
+/*
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import (
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func (r *TCData) CreateTestCacheGroups(t *testing.T) {
+
+	var err error
+	var resp *tc.CacheGroupDetailResponse
+
+	for _, cg := range r.TestData.CacheGroups {
+
+		resp, _, err = TOSession.CreateCacheGroupNullable(cg)
+		if err != nil {
+			t.Errorf("could not CREATE cachegroups: %v, request: %v", err, cg)
+			continue
+		}
+
+		// Testing 'join' fields during create
+		if cg.ParentName != nil && resp.Response.ParentName == nil {
+			t.Error("Parent cachegroup is null in response when it should have a value")
+		}
+		if cg.SecondaryParentName != nil && resp.Response.SecondaryParentName == nil {
+			t.Error("Secondary parent cachegroup is null in response when it should have a value\n")
+		}
+		if cg.Type != nil && resp.Response.Type == nil {
+			t.Error("Type is null in response when it should have a value\n")
+		}
+		if resp.Response.LocalizationMethods == nil {
+			t.Error("Localization methods are null")
+		}
+		if resp.Response.Fallbacks == nil {
+			t.Error("Fallbacks are null")
+		}
+
+	}
+}
+
+func (r *TCData) DeleteTestCacheGroups(t *testing.T) {
+	var parentlessCacheGroups []tc.CacheGroupNullable
+
+	// delete the edge caches.
+	for _, cg := range r.TestData.CacheGroups {
+		// Retrieve the CacheGroup by name so we can get the id for the Update
+		resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
+		if err != nil {
+			t.Errorf("cannot GET CacheGroup by name: %v - %v", *cg.Name, err)
+		}
+		cg = resp[0]
+
+		// Cachegroups that are parents (usually mids but sometimes edges)
+		// need to be deleted only after the children cachegroups are deleted.
+		if cg.ParentCachegroupID == nil && cg.SecondaryParentCachegroupID == nil {
+			parentlessCacheGroups = append(parentlessCacheGroups, cg)
+			continue
+		}
+		if len(resp) > 0 {
+			respCG := resp[0]
+			_, _, err := TOSession.DeleteCacheGroupByID(*respCG.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v", *respCG.Name, err)
+			}
+			// Retrieve the CacheGroup to see if it got deleted
+			cgs, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
+			if err != nil {
+				t.Errorf("error deleting CacheGroup by name: %s", err.Error())
+			}
+			if len(cgs) > 0 {
+				t.Errorf("expected CacheGroup name: %s to be deleted", *cg.Name)
+			}
+		}
+	}
+
+	// now delete the parentless cachegroups
+	for _, cg := range parentlessCacheGroups {
+		// Retrieve the CacheGroup by name so we can get the id for the Update
+		resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
+		if err != nil {
+			t.Errorf("cannot GET CacheGroup by name: %v - %v", *cg.Name, err)
+		}
+		if len(resp) > 0 {
+			respCG := resp[0]
+			_, _, err := TOSession.DeleteCacheGroupByID(*respCG.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v", *respCG.Name, err)
+			}
+
+			// Retrieve the CacheGroup to see if it got deleted
+			cgs, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
+			if err != nil {
+				t.Errorf("error deleting CacheGroup name: %s", err.Error())
+			}
+			if len(cgs) > 0 {
+				t.Errorf("expected CacheGroup name: %s to be deleted", *cg.Name)
+			}
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups_parameters.go b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups_parameters.go
new file mode 100644
index 0000000..8ef5509
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroups_parameters.go
@@ -0,0 +1,103 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package tcdata
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func (r *TCData) CreateTestCacheGroupParameters(t *testing.T) {
+	// Get Cache Group to assign parameter to
+	firstCacheGroup := r.TestData.CacheGroups[0]
+	cacheGroupResp, _, err := TOSession.GetCacheGroupNullableByName(*firstCacheGroup.Name)
+	if err != nil {
+		t.Errorf("cannot GET Cache Group by name: %v - %v", firstCacheGroup.Name, err)
+	}
+	if cacheGroupResp == nil {
+		t.Fatal("Cache Groups response should not be nil")
+	}
+
+	// Get Parameter to assign to Cache Group
+	firstParameter := r.TestData.Parameters[0]
+	paramResp, _, err := TOSession.GetParameterByName(firstParameter.Name)
+	if err != nil {
+		t.Errorf("cannot GET Parameter by name: %v - %v", firstParameter.Name, err)
+	}
+	if paramResp == nil {
+		t.Fatal("Parameter response should not be nil")
+	}
+
+	// Assign Parameter to Cache Group
+	cacheGroupID := cacheGroupResp[0].ID
+	parameterID := paramResp[0].ID
+	resp, _, err := TOSession.CreateCacheGroupParameter(*cacheGroupID, parameterID)
+	if err != nil {
+		t.Errorf("could not CREATE cache group parameter: %v", err)
+	}
+	if resp == nil {
+		t.Fatal("Cache Group Parameter response should not be nil")
+	}
+	r.TestData.CacheGroupParameterRequests = append(r.TestData.CacheGroupParameterRequests, resp.Response...)
+}
+
+func (r *TCData) DeleteTestCacheGroupParameters(t *testing.T) {
+	for _, cgp := range r.TestData.CacheGroupParameterRequests {
+		DeleteTestCacheGroupParameter(t, cgp)
+	}
+}
+
+func DeleteTestCacheGroupParameter(t *testing.T, cgp tc.CacheGroupParameterRequest) {
+
+	delResp, _, err := TOSession.DeleteCacheGroupParameter(cgp.CacheGroupID, cgp.ParameterID)
+	if err != nil {
+		t.Fatalf("cannot DELETE Parameter by cache group: %v - %v", err, delResp)
+	}
+
+	// Retrieve the Cache Group Parameter to see if it got deleted
+	queryParams := fmt.Sprintf("?parameterId=%d", cgp.ParameterID)
+
+	parameters, _, err := TOSession.GetCacheGroupParametersByQueryParams(cgp.CacheGroupID, queryParams)
+	if err != nil {
+		t.Errorf("error deleting Parameter name: %s", err.Error())
+	}
+	if parameters == nil {
+		t.Fatal("Cache Group Parameters response should not be nil")
+	}
+	if len(parameters) > 0 {
+		t.Errorf("expected Parameter: %d to be to be disassociated from Cache Group: %d", cgp.ParameterID, cgp.CacheGroupID)
+	}
+
+	// Attempt to delete it again and it should return an error now
+	_, _, err = TOSession.DeleteCacheGroupParameter(cgp.CacheGroupID, cgp.ParameterID)
+	if err == nil {
+		t.Error("expected error when deleting unassociated cache group parameter")
+	}
+
+	// Attempt to delete using a non existing cache group
+	_, _, err = TOSession.DeleteCacheGroupParameter(-1, cgp.ParameterID)
+	if err == nil {
+		t.Error("expected error when deleting cache group parameter with non existing cache group")
+	}
+
+	// Attempt to delete using a non existing parameter
+	_, _, err = TOSession.DeleteCacheGroupParameter(cgp.CacheGroupID, -1)
+	if err == nil {
+		t.Error("expected error when deleting cache group parameter with non existing parameter")
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/cachegroupsdeliveryservices.go b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroupsdeliveryservices.go
new file mode 100644
index 0000000..ffe6d5d
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/cachegroupsdeliveryservices.go
@@ -0,0 +1,153 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+// TODO this is the name hard-coded in the create servers test; change to be dynamic
+// TODO this test assumes that a CDN named "cdn1" exists, has at least one Delivery Service, and also
+// assumes that ALL SERVERS IN "cachegroup3" ARE EDGE-TIER CACHE SERVERS IN "cdn1". If that EVER changes,
+// this WILL break.
+const TestEdgeServerCacheGroupName = "cachegroup3"
+
+func (r *TCData) CreateTestCachegroupsDeliveryServices(t *testing.T) {
+	dss, _, err := TOSession.GetDeliveryServiceServers()
+	if err != nil {
+		t.Fatalf("cannot GET DeliveryServiceServers: %v", err)
+	}
+	if len(dss.Response) > 0 {
+		t.Fatalf("cannot test cachegroups delivery services: expected no initial delivery service servers, actual %v", len(dss.Response))
+	}
+
+	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	if err != nil {
+		t.Fatalf("cannot GET DeliveryServices: %v - %v", err, dses)
+	}
+
+	clientCGs, _, err := TOSession.GetCacheGroupNullableByName(TestEdgeServerCacheGroupName)
+	if err != nil {
+		t.Fatalf("getting cachegroup: %v", err)
+	}
+	if len(clientCGs) != 1 {
+		t.Fatalf("getting cachegroup expected 1, got %v", len(clientCGs))
+	}
+
+	clientCG := clientCGs[0]
+
+	if clientCG.ID == nil {
+		t.Fatalf("Cachegroup has a nil ID")
+	}
+	cgID := *clientCG.ID
+
+	dsIDs := []int{}
+	topologyDsIDs := []int{}
+	for _, ds := range dses {
+		if *ds.CDNName == "cdn1" && ds.Topology == nil {
+			dsIDs = append(dsIDs, *ds.ID)
+		} else if *ds.CDNName == "cdn1" && ds.Topology != nil {
+			topologyDsIDs = append(topologyDsIDs, *ds.ID)
+		}
+	}
+	if len(dsIDs) < 1 {
+		t.Fatal("No Delivery Services found in CDN 'cdn1', cannot continue.")
+	}
+
+	if len(topologyDsIDs) < 1 {
+		t.Fatal("No Topology-based Delivery Services found in CDN 'cdn1', cannot continue.")
+	}
+
+	_, reqInf, err := TOSession.SetCachegroupDeliveryServices(cgID, topologyDsIDs)
+	if err == nil {
+		t.Fatal("assigning Topology-based delivery service to cachegroup - expected: error, actual: nil")
+	}
+	if reqInf.StatusCode < http.StatusBadRequest || reqInf.StatusCode >= http.StatusInternalServerError {
+		t.Fatalf("assigning Topology-based delivery service to cachegroup - expected: 400-level status code, actual: %d", reqInf.StatusCode)
+	}
+
+	resp, _, err := TOSession.SetCachegroupDeliveryServices(cgID, dsIDs)
+	if err != nil {
+		t.Fatalf("setting cachegroup delivery services returned error: %v", err)
+	}
+	if len(resp.Response.ServerNames) == 0 {
+		t.Fatal("setting cachegroup delivery services returned success, but no servers set")
+	}
+
+	// Note this second post of the same cg-dses specifically tests a previous bug, where the query
+	// failed if any servers with location parameters were already assigned, due to a foreign key
+	// violation. See https://github.com/apache/trafficcontrol/pull/3199
+	resp, _, err = TOSession.SetCachegroupDeliveryServices(cgID, dsIDs)
+	if err != nil {
+		t.Fatalf("setting cachegroup delivery services returned error: %v", err)
+	}
+	if len(resp.Response.ServerNames) == 0 {
+		t.Fatal("setting cachegroup delivery services returned success, but no servers set")
+	}
+
+	params := url.Values{}
+	for _, serverName := range resp.Response.ServerNames {
+		params.Set("hostName", string(serverName))
+		resp, _, err := TOSession.GetServers(&params)
+		if err != nil {
+			t.Fatalf("getting server: %v", err)
+		}
+		servers := resp
+		if len(servers) != 1 {
+			t.Fatalf("getting servers: expected 1 got %v", len(servers))
+		}
+		server := servers[0]
+		serverID := server.ID
+
+		serverDSes, _, err := TOSession.GetDeliveryServicesByServer(serverID)
+
+		for _, dsID := range dsIDs {
+			found := false
+			for _, serverDS := range serverDSes {
+				if *serverDS.ID == int(dsID) {
+					found = true
+					break
+				}
+			}
+			if !found {
+				t.Errorf("post succeeded, but didn't assign delivery service %v to server", dsID)
+			}
+		}
+	}
+}
+
+func (r *TCData) DeleteTestCachegroupsDeliveryServices(t *testing.T) {
+	dss, _, err := TOSession.GetDeliveryServiceServersN(1000000)
+	if err != nil {
+		t.Errorf("cannot GET DeliveryServiceServers: %v", err)
+	}
+	for _, ds := range dss.Response {
+		_, _, err := TOSession.DeleteDeliveryServiceServer(*ds.DeliveryService, *ds.Server)
+		if err != nil {
+			t.Errorf("deleting delivery service servers: " + err.Error() + "\n")
+		}
+	}
+
+	dss, _, err = TOSession.GetDeliveryServiceServers()
+	if err != nil {
+		t.Errorf("cannot GET DeliveryServiceServers: %v", err)
+	}
+	if len(dss.Response) > 0 {
+		t.Errorf("deleting delivery service servers: delete succeeded, expected empty subsequent get, actual %v", len(dss.Response))
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/cdnfederations.go b/traffic_ops_ort/testing/ort-tests/tcdata/cdnfederations.go
new file mode 100644
index 0000000..b207dca
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/cdnfederations.go
@@ -0,0 +1,67 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+var fedIDs []int
+
+func (r *TCData) CreateTestCDNFederations(t *testing.T) {
+
+	// Every federation is associated with a cdn
+	for i, f := range r.TestData.Federations {
+
+		// CDNs test data and Federations test data are not naturally parallel
+		if i >= len(r.TestData.CDNs) {
+			break
+		}
+
+		data, _, err := TOSession.CreateCDNFederationByName(f, r.TestData.CDNs[i].Name)
+		if err != nil {
+			t.Errorf("could not POST federations: " + err.Error())
+		}
+		bytes, _ := json.Marshal(data)
+		t.Logf("POST Response: %s\n", bytes)
+
+		// need to save the ids, otherwise the other tests won't be able to reference the federations
+		if data.Response.ID == nil {
+			t.Error("Federation id is nil after posting")
+		} else {
+			fedIDs = append(fedIDs, *data.Response.ID)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestCDNFederations(t *testing.T) {
+
+	for _, id := range fedIDs {
+		resp, _, err := TOSession.DeleteCDNFederationByID("foo", id)
+		if err != nil {
+			t.Errorf("cannot DELETE federation by id: '%d' %v", id, err)
+		}
+		bytes, err := json.Marshal(resp)
+		t.Logf("DELETE Response: %s\n", bytes)
+
+		data, _, err := TOSession.GetCDNFederationsByID("foo", id)
+		if len(data.Response) != 0 {
+			t.Error("expected federation to be deleted")
+		}
+	}
+	fedIDs = nil // reset the global variable for the next test
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/cdns.go b/traffic_ops_ort/testing/ort-tests/tcdata/cdns.go
new file mode 100644
index 0000000..60a03fb
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/cdns.go
@@ -0,0 +1,60 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestCDNs(t *testing.T) {
+
+	for _, cdn := range r.TestData.CDNs {
+		resp, _, err := TOSession.CreateCDN(cdn)
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE cdns: %v", err)
+		}
+	}
+
+}
+
+func (r *TCData) DeleteTestCDNs(t *testing.T) {
+
+	for _, cdn := range r.TestData.CDNs {
+		// Retrieve the CDN by name so we can get the id for the Update
+		resp, _, err := TOSession.GetCDNByName(cdn.Name)
+		if err != nil {
+			t.Errorf("cannot GET CDN by name: %v - %v", cdn.Name, err)
+		}
+		if len(resp) > 0 {
+			respCDN := resp[0]
+
+			_, _, err := TOSession.DeleteCDNByID(respCDN.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE CDN by name: '%s' %v", respCDN.Name, err)
+			}
+
+			// Retrieve the CDN to see if it got deleted
+			cdns, _, err := TOSession.GetCDNByName(cdn.Name)
+			if err != nil {
+				t.Errorf("error deleting CDN name: %s", err.Error())
+			}
+			if len(cdns) > 0 {
+				t.Errorf("expected CDN name: %s to be deleted", cdn.Name)
+			}
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/coordinates.go b/traffic_ops_ort/testing/ort-tests/tcdata/coordinates.go
new file mode 100644
index 0000000..96e51ad
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/coordinates.go
@@ -0,0 +1,55 @@
+package tcdata
+
+/*
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestCoordinates(t *testing.T) {
+	for _, coord := range r.TestData.Coordinates {
+
+		_, _, err := TOSession.CreateCoordinate(coord)
+		if err != nil {
+			t.Errorf("could not CREATE coordinates: %v", err)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestCoordinates(t *testing.T) {
+	for _, coord := range r.TestData.Coordinates {
+		// Retrieve the Coordinate by name so we can get the id for the Update
+		resp, _, err := TOSession.GetCoordinateByName(coord.Name)
+		if err != nil {
+			t.Errorf("cannot GET Coordinate by name: %v - %v", coord.Name, err)
+		}
+		if len(resp) > 0 {
+			respCoord := resp[0]
+			_, _, err := TOSession.DeleteCoordinateByID(respCoord.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE Coordinate by name: '%s' %v", respCoord.Name, err)
+			}
+			// Retrieve the Coordinate to see if it got deleted
+			coords, _, err := TOSession.GetCoordinateByName(coord.Name)
+			if err != nil {
+				t.Errorf("error deleting Coordinate name: %s", err.Error())
+			}
+			if len(coords) > 0 {
+				t.Errorf("expected Coordinate name: %s to be deleted", coord.Name)
+			}
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_request_comments.go b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_request_comments.go
new file mode 100644
index 0000000..5690bb5
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_request_comments.go
@@ -0,0 +1,66 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestDeliveryServiceRequestComments(t *testing.T) {
+
+	// Retrieve a delivery service request by xmlId so we can get the ID needed to create a dsr comment
+	dsr := r.TestData.DeliveryServiceRequests[0].DeliveryService
+
+	resp, _, err := TOSession.GetDeliveryServiceRequestByXMLID(dsr.XMLID)
+	if err != nil {
+		t.Errorf("cannot GET delivery service request by xml id: %v - %v", dsr.XMLID, err)
+	}
+	if len(resp) != 1 {
+		t.Errorf("found %d delivery service request by xml id, expected %d: %s", len(resp), 1, dsr.XMLID)
+	} else {
+		respDSR := resp[0]
+
+		for _, comment := range r.TestData.DeliveryServiceRequestComments {
+			comment.DeliveryServiceRequestID = respDSR.ID
+			resp, _, err := TOSession.CreateDeliveryServiceRequestComment(comment)
+			if err != nil {
+				t.Errorf("could not CREATE delivery service request comment: %v - %v", err, resp)
+			}
+		}
+	}
+
+}
+
+func (r *TCData) DeleteTestDeliveryServiceRequestComments(t *testing.T) {
+
+	comments, _, _ := TOSession.GetDeliveryServiceRequestComments()
+
+	for _, comment := range comments {
+		_, _, err := TOSession.DeleteDeliveryServiceRequestCommentByID(comment.ID)
+		if err != nil {
+			t.Errorf("cannot DELETE delivery service request comment by id: '%d' %v", comment.ID, err)
+		}
+
+		// Retrieve the delivery service request comment to see if it got deleted
+		comments, _, err := TOSession.GetDeliveryServiceRequestCommentByID(comment.ID)
+		if err != nil {
+			t.Errorf("error deleting delivery service request comment: %s", err.Error())
+		}
+		if len(comments) > 0 {
+			t.Errorf("expected delivery service request comment: %d to be deleted", comment.ID)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_requests.go b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_requests.go
new file mode 100644
index 0000000..0e929fe
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservice_requests.go
@@ -0,0 +1,64 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+const (
+	dsrGood      = 0
+	dsrBadTenant = 1
+	dsrRequired  = 2
+	dsrDraft     = 3
+)
+
+func (r *TCData) CreateTestDeliveryServiceRequests(t *testing.T) {
+	t.Log("CreateTestDeliveryServiceRequests")
+
+	dsr := r.TestData.DeliveryServiceRequests[dsrGood]
+	respDSR, _, err := TOSession.CreateDeliveryServiceRequest(dsr)
+	t.Log("Response: ", respDSR)
+	if err != nil {
+		t.Errorf("could not CREATE DeliveryServiceRequests: %v", err)
+	}
+
+}
+
+func (r *TCData) DeleteTestDeliveryServiceRequests(t *testing.T) {
+
+	// Retrieve the DeliveryServiceRequest by name so we can get the id for the Update
+	dsr := r.TestData.DeliveryServiceRequests[dsrGood]
+	resp, _, err := TOSession.GetDeliveryServiceRequestByXMLID(dsr.DeliveryService.XMLID)
+	if err != nil {
+		t.Errorf("cannot GET DeliveryServiceRequest by id: %v - %v", dsr.DeliveryService.XMLID, err)
+	}
+	respDSR := resp[0]
+	alert, _, err := TOSession.DeleteDeliveryServiceRequestByID(respDSR.ID)
+	t.Log("Response: ", alert)
+	if err != nil {
+		t.Errorf("cannot DELETE DeliveryServiceRequest by id: %d - %v - %v", respDSR.ID, err, alert)
+	}
+
+	// Retrieve the DeliveryServiceRequest to see if it got deleted
+	dsrs, _, err := TOSession.GetDeliveryServiceRequestByXMLID(dsr.DeliveryService.XMLID)
+	if err != nil {
+		t.Errorf("error deleting DeliveryServiceRequest name: %s", err.Error())
+	}
+	if len(dsrs) > 0 {
+		t.Errorf("expected DeliveryServiceRequest XMLID: %s to be deleted", dsr.DeliveryService.XMLID)
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices.go b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices.go
new file mode 100644
index 0000000..3c942ff
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices.go
@@ -0,0 +1,90 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"net/url"
+	"strconv"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func (r *TCData) CreateTestDeliveryServices(t *testing.T) {
+	pl := tc.Parameter{
+		ConfigFile: "remap.config",
+		Name:       "location",
+		Value:      "/remap/config/location/parameter/",
+	}
+	_, _, err := TOSession.CreateParameter(pl)
+	if err != nil {
+		t.Errorf("cannot create parameter: %v", err)
+	}
+	for _, ds := range r.TestData.DeliveryServices {
+		_, _, err = TOSession.CreateDeliveryServiceV30(ds)
+		if err != nil {
+			t.Errorf("could not CREATE delivery service '%s': %v", *ds.XMLID, err)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestDeliveryServices(t *testing.T) {
+	dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+	if err != nil {
+		t.Errorf("cannot GET deliveryservices: %v", err)
+	}
+	for _, testDS := range r.TestData.DeliveryServices {
+		var ds tc.DeliveryServiceNullableV30
+		found := false
+		for _, realDS := range dses {
+			if realDS.XMLID != nil && *realDS.XMLID == *testDS.XMLID {
+				ds = realDS
+				found = true
+				break
+			}
+		}
+		if !found {
+			t.Errorf("DeliveryService not found in Traffic Ops: %v", *ds.XMLID)
+			continue
+		}
+
+		delResp, err := TOSession.DeleteDeliveryService(strconv.Itoa(*ds.ID))
+		if err != nil {
+			t.Errorf("cannot DELETE DeliveryService by ID: %v - %v", err, delResp)
+			continue
+		}
+
+		// Retrieve the Server to see if it got deleted
+		params := url.Values{}
+		params.Set("id", strconv.Itoa(*ds.ID))
+		foundDS, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, params)
+		if err != nil {
+			t.Errorf("Unexpected error deleting Delivery Service '%s': %v", *ds.XMLID, err)
+		}
+		if len(foundDS) > 0 {
+			t.Errorf("expected Delivery Service: %s to be deleted, but %d exist with same ID (#%d)", *ds.XMLID, len(foundDS), *ds.ID)
+		}
+	}
+
+	// clean up parameter created in CreateTestDeliveryServices()
+	params, _, err := TOSession.GetParameterByNameAndConfigFile("location", "remap.config")
+	for _, param := range params {
+		deleted, _, err := TOSession.DeleteParameterByID(param.ID)
+		if err != nil {
+			t.Errorf("cannot DELETE parameter by ID (%d): %v - %v", param.ID, err, deleted)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices_required_capabilities.go b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices_required_capabilities.go
new file mode 100644
index 0000000..6a75229
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservices_required_capabilities.go
@@ -0,0 +1,212 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func (r *TCData) CreateTestTopologyBasedDeliveryServicesRequiredCapabilities(t *testing.T) {
+	for _, td := range r.TestData.TopologyBasedDeliveryServicesRequiredCapabilities {
+
+		c := tc.DeliveryServicesRequiredCapability{
+			DeliveryServiceID:  helperGetDeliveryServiceID(t, td.XMLID),
+			RequiredCapability: td.RequiredCapability,
+		}
+
+		_, _, err := TOSession.CreateDeliveryServicesRequiredCapability(c)
+		if err != nil {
+			t.Fatalf("cannot create delivery service required capability: %v", err)
+		}
+	}
+
+	invalid := tc.DeliveryServicesRequiredCapability{
+		DeliveryServiceID:  helperGetDeliveryServiceID(t, util.StrPtr("ds-top-req-cap")),
+		RequiredCapability: util.StrPtr("asdf"),
+	}
+	_, reqInf, err := TOSession.CreateDeliveryServicesRequiredCapability(invalid)
+	if err == nil {
+		t.Fatal("when adding delivery service required capability to a delivery service with a topology that " +
+			"doesn't have cachegroups with at least one server with the required capabilities - expected: error, actual: nil")
+	}
+	if reqInf.StatusCode != http.StatusBadRequest {
+		t.Fatalf("when adding delivery service required capability to a delivery service with a topology that "+
+			"doesn't have cachegroups with at least one server with the required capabilities - expected status code: "+
+			"%d, actual: %d", http.StatusBadRequest, reqInf.StatusCode)
+	}
+}
+
+func (r *TCData) CreateTestDeliveryServicesRequiredCapabilities(t *testing.T) {
+	data := r.TestData.DeliveryServicesRequiredCapabilities
+	if len(data) == 0 {
+		t.Fatal("there must be at least one test ds required capability defined")
+	}
+	ds1 := helperGetDeliveryServiceID(t, data[0].XMLID)
+	amDS := helperGetDeliveryServiceID(t, util.StrPtr("anymap-ds"))
+	testCases := []struct {
+		description string
+		capability  tc.DeliveryServicesRequiredCapability
+	}{
+		{
+			description: fmt.Sprintf("re-assign a deliveryservice to a required capability; deliveryServiceID: %d, requiredCapability: %s", *ds1, *data[0].RequiredCapability),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  ds1,
+				RequiredCapability: data[0].RequiredCapability,
+			},
+		},
+		{
+			description: fmt.Sprintf("assign a deliveryservice to a required capability with no delivery service id; deliveryServiceID: 0, requiredCapability: %s", *data[0].RequiredCapability),
+			capability: tc.DeliveryServicesRequiredCapability{
+				RequiredCapability: data[0].RequiredCapability,
+			},
+		},
+		{
+			description: fmt.Sprintf("assign a deliveryservice to a required capability with no requiredCapability; deliveryServiceID: %d, requiredCapability: 0", *ds1),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID: ds1,
+			},
+		},
+		{
+			description: fmt.Sprintf("assign a deliveryservice to a required capability with an invalid required capability; deliveryServiceID: %d, requiredCapability: bogus", *ds1),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  ds1,
+				RequiredCapability: util.StrPtr("bogus"),
+			},
+		},
+		{
+			description: fmt.Sprintf("assign a deliveryservice to a required capability with an invalid delivery service id; deliveryServiceID: -1, requiredCapability: %s", *data[0].RequiredCapability),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  util.IntPtr(-1),
+				RequiredCapability: data[0].RequiredCapability,
+			},
+		},
+		{
+			description: "assign a deliveryservice to a required capability with an invalid deliveryservice type",
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  amDS,
+				RequiredCapability: data[0].RequiredCapability,
+			},
+		},
+	}
+
+	// Assign all required capability to delivery services listed in `tc-fixtures.json`.
+	for _, td := range r.TestData.DeliveryServicesRequiredCapabilities {
+		var dsID int
+		if td.DeliveryServiceID != nil {
+			dsID = *td.DeliveryServiceID
+		}
+
+		var capability string
+		if td.RequiredCapability != nil {
+			capability = *td.RequiredCapability
+		}
+
+		t.Run(fmt.Sprintf("assign a deliveryservice to a required capability; deliveryServiceID: %d, requiredCapability: %s", dsID, capability), func(t *testing.T) {
+			cap := tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  helperGetDeliveryServiceID(t, td.XMLID),
+				RequiredCapability: td.RequiredCapability,
+			}
+
+			_, _, err := TOSession.CreateDeliveryServicesRequiredCapability(cap)
+			if err != nil {
+				t.Fatalf(err.Error())
+			}
+		})
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			_, _, err := TOSession.CreateDeliveryServicesRequiredCapability(tc.capability)
+			if err == nil {
+				t.Fatalf("%s; expected err", tc.description)
+			}
+		})
+	}
+}
+
+func (r *TCData) DeleteTestDeliveryServicesRequiredCapabilities(t *testing.T) {
+	// Get Required Capabilities to delete them
+	capabilities, _, err := TOSession.GetDeliveryServicesRequiredCapabilitiesWithHdr(nil, nil, nil, nil)
+	if err != nil {
+		t.Fatalf(err.Error())
+	}
+	if len(capabilities) < 1 {
+		t.Fatal("no delivery services returned")
+	}
+
+	type testCase struct {
+		description string
+		capability  tc.DeliveryServicesRequiredCapability
+		err         string
+	}
+
+	testCases := []testCase{
+		testCase{
+			description: fmt.Sprintf("delete a deliveryservices required capability with an invalid delivery service id; deliveryServiceID: -1, requiredCapability: %s", *capabilities[0].RequiredCapability),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  util.IntPtr(-1),
+				RequiredCapability: capabilities[0].RequiredCapability,
+			},
+			err: "no deliveryservice.RequiredCapability with that key found",
+		},
+		testCase{
+			description: fmt.Sprintf("delete a deliveryservices required capability with an invalid required capability; deliveryServiceID: %d, requiredCapability: bogus", *capabilities[0].DeliveryServiceID),
+			capability: tc.DeliveryServicesRequiredCapability{
+				DeliveryServiceID:  capabilities[0].DeliveryServiceID,
+				RequiredCapability: util.StrPtr("bogus"),
+			},
+			err: "no deliveryservice.RequiredCapability with that key found",
+		},
+	}
+
+	for _, c := range capabilities {
+		t := testCase{
+			description: fmt.Sprintf("delete a deliveryservices required capability; deliveryServiceID: %d, requiredCapability: %s", *c.DeliveryServiceID, *c.RequiredCapability),
+			capability:  c,
+		}
+		testCases = append(testCases, t)
+	}
+
+	for _, c := range testCases {
+		t.Run(c.description, func(t *testing.T) {
+			_, _, err := TOSession.DeleteDeliveryServicesRequiredCapability(*c.capability.DeliveryServiceID, *c.capability.RequiredCapability)
+			if err != nil && !strings.Contains(err.Error(), c.err) {
+				t.Fatalf("%s; got err= %s; expected err= %s", c.description, err, c.err)
+			}
+		})
+	}
+}
+
+func helperGetDeliveryServiceID(t *testing.T, xmlID *string) *int {
+	t.Helper()
+	if xmlID == nil {
+		t.Fatal("xml id must not be nil")
+	}
+	ds, _, err := TOSession.GetDeliveryServiceByXMLIDNullableWithHdr(*xmlID, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(ds) < 1 {
+		t.Fatalf("cannot GET deliveyservice by xml id: %v. Response did not include record.", *xmlID)
+	}
+	return ds[0].ID
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservicesregexes.go b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservicesregexes.go
new file mode 100644
index 0000000..d34aa45
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/deliveryservicesregexes.go
@@ -0,0 +1,106 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package tcdata
+
+import (
+	"fmt"
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"testing"
+)
+
+func (r *TCData) CreateTestDeliveryServicesRegexes(t *testing.T) {
+	db, err := r.OpenConnection()
+	if err != nil {
+		t.Fatal("cannot open db")
+	}
+	defer func() {
+		err := db.Close()
+		if err != nil {
+			t.Errorf("unable to close connection to db, error: %v", err)
+		}
+	}()
+
+	dbRegexInsertTemplate := "INSERT INTO regex (pattern, type) VALUES ('%v', '%v');"
+	dbRegexQueryTemplate := "SELECT id FROM regex order by id desc limit 1;"
+	dbDSRegexInsertTemplate := "INSERT INTO deliveryservice_regex (deliveryservice, regex, set_number) VALUES ('%v', '%v', '%v');"
+
+	for i, regex := range r.TestData.DeliveryServicesRegexes {
+		loadDSRegexIDs(t, &regex)
+
+		err = execSQL(db, fmt.Sprintf(dbRegexInsertTemplate, regex.Pattern, regex.Type))
+		if err != nil {
+			t.Fatalf("unable to create regex: %v", err)
+		}
+
+		row := db.QueryRow(dbRegexQueryTemplate)
+		err = row.Scan(&regex.ID)
+		if err != nil {
+			t.Fatalf("unable to query regex: %v", err)
+		}
+
+		err = execSQL(db, fmt.Sprintf(dbDSRegexInsertTemplate, regex.DSID, regex.ID, regex.SetNumber))
+		if err != nil {
+			t.Fatalf("unable to create ds regex %v", err)
+		}
+
+		r.TestData.DeliveryServicesRegexes[i] = regex
+	}
+}
+
+func loadDSRegexIDs(t *testing.T, test *tc.DeliveryServiceRegexesTest) {
+	dsTypes, _, err := TOSession.GetTypeByName(test.TypeName)
+	if err != nil {
+		t.Fatalf("unable to get type by name %v: %v", test.TypeName, err)
+	}
+	if len(dsTypes) < 1 {
+		t.Fatalf("could not find any types by name %v", test.TypeName)
+	}
+	test.Type = dsTypes[0].ID
+
+	dses, _, err := TOSession.GetDeliveryServiceByXMLIDNullable(test.DSName)
+	if err != nil {
+		t.Fatalf("unable to ds by xmlid %v: %v", test.DSName, err)
+	}
+	if len(dses) != 1 {
+		t.Fatalf("unable to find ds by xmlid %v", test.DSName)
+	}
+	test.DSID = *dses[0].ID
+}
+
+func (r *TCData) DeleteTestDeliveryServicesRegexes(t *testing.T) {
+	db, err := r.OpenConnection()
+	if err != nil {
+		t.Fatal("cannot open db")
+	}
+	defer func() {
+		err := db.Close()
+		if err != nil {
+			t.Errorf("unable to close connection to db, error: %v", err)
+		}
+	}()
+
+	for _, regex := range r.TestData.DeliveryServicesRegexes {
+		err = execSQL(db, fmt.Sprintf("DELETE FROM deliveryservice_regex WHERE deliveryservice = '%v' and regex ='%v';", regex.DSID, regex.ID))
+		if err != nil {
+			t.Fatalf("unable to delete deliveryservice_regex by regex %v and ds %v: %v", regex.ID, regex.DSID, err)
+		}
+
+		err := execSQL(db, fmt.Sprintf("DELETE FROM regex WHERE Id = '%v';", regex.ID))
+		if err != nil {
+			t.Fatalf("unable to delete regex %v: %v", regex.ID, err)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/divisions.go b/traffic_ops_ort/testing/ort-tests/tcdata/divisions.go
new file mode 100644
index 0000000..e30171e
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/divisions.go
@@ -0,0 +1,56 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestDivisions(t *testing.T) {
+	for _, division := range r.TestData.Divisions {
+		resp, _, err := TOSession.CreateDivision(division)
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE division: %v", err)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestDivisions(t *testing.T) {
+
+	for _, division := range r.TestData.Divisions {
+		// Retrieve the Division by name so we can get the id
+		resp, _, err := TOSession.GetDivisionByName(division.Name)
+		if err != nil {
+			t.Errorf("cannot GET Division by name: %v - %v", division.Name, err)
+		}
+		respDivision := resp[0]
+
+		delResp, _, err := TOSession.DeleteDivisionByID(respDivision.ID)
+		if err != nil {
+			t.Errorf("cannot DELETE Division by division: %v - %v", err, delResp)
+		}
+
+		// Retrieve the Division to see if it got deleted
+		divisionResp, _, err := TOSession.GetDivisionByName(division.Name)
+		if err != nil {
+			t.Errorf("error deleting Division division: %s", err.Error())
+		}
+		if len(divisionResp) > 0 {
+			t.Errorf("expected Division : %s to be deleted", division.Name)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/federation_resolvers.go b/traffic_ops_ort/testing/ort-tests/tcdata/federation_resolvers.go
new file mode 100644
index 0000000..e89a841
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/federation_resolvers.go
@@ -0,0 +1,118 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func (r *TCData) CreateTestFederationResolvers(t *testing.T) {
+	for _, fr := range r.TestData.FederationResolvers {
+		if fr.Type == nil {
+			t.Fatal("testData Federation Resolver has nil Type")
+		}
+
+		tid, _, err := TOSession.GetTypeByName(*fr.Type)
+		if err != nil {
+			t.Fatalf("Couldn't get an ID for type %s", *fr.Type)
+		}
+		if len(tid) != 1 {
+			t.Fatalf("Expected exactly one Type by name %s, got %d", *fr.Type, len(tid))
+		}
+
+		fr.TypeID = util.UIntPtr(uint(tid[0].ID))
+
+		alerts, _, err := TOSession.CreateFederationResolver(fr)
+		if err != nil {
+			t.Fatalf("failed to create Federation resolver %+v: %v\n\talerts: %+v", fr, err, alerts)
+		}
+		for _, a := range alerts.Alerts {
+			if a.Level != tc.SuccessLevel.String() {
+				t.Errorf("Unexpected %s creating a federation resolver: %s", a.Level, a.Text)
+			} else {
+				t.Logf("Received expected success creating federation resolver: %s", a.Text)
+			}
+		}
+	}
+
+	var invalidFR tc.FederationResolver
+	alerts, _, err := TOSession.CreateFederationResolver(invalidFR)
+	if err == nil {
+		t.Error("Expected an error creating a bad Federation Resolver, but didn't get one")
+	}
+	for _, a := range alerts.Alerts {
+		if a.Level == tc.SuccessLevel.String() {
+			t.Errorf("Unexpected success creating a bad Federation Resolver: %s", a.Text)
+		} else {
+			t.Logf("Received expected %s creating federation resolver: %s", a.Level, a.Text)
+		}
+	}
+
+	invalidFR.TypeID = util.UIntPtr(1)
+	invalidFR.IPAddress = util.StrPtr("not a valid IP address")
+	alerts, _, err = TOSession.CreateFederationResolver(invalidFR)
+	if err == nil {
+		t.Error("Expected an error creating a bad Federation Resolver, but didn't get one")
+	}
+	for _, a := range alerts.Alerts {
+		if a.Level == tc.SuccessLevel.String() {
+			t.Errorf("Unexpected success creating a bad Federation Resolver: %s", a.Text)
+		} else {
+			t.Logf("Received expected %s creating a bad federation resolver: %s", a.Level, a.Text)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestFederationResolvers(t *testing.T) {
+	frs, _, err := TOSession.GetFederationResolvers()
+	if err != nil {
+		t.Errorf("Unexpected error getting Federation Resolvers: %v", err)
+	}
+	if len(frs) < 1 {
+		t.Fatal("Found no Federation Resolvers to delete")
+	}
+	for _, fr := range frs {
+		if fr.ID == nil {
+			t.Fatalf("Malformed Federation Resolver: %+v", fr)
+		}
+		alerts, _, err := TOSession.DeleteFederationResolver(*fr.ID)
+		if err != nil {
+			t.Fatalf("failed to delete Federation Resolver %+v: %v\n\talerts: %+v", fr, err, alerts)
+		}
+		for _, a := range alerts.Alerts {
+			if a.Level != tc.SuccessLevel.String() {
+				t.Errorf("Unexpected %s deleting a federation resolver: %s", a.Level, a.Text)
+			} else {
+				t.Logf("Received expected success deleting federation resolver: %s", a.Text)
+			}
+		}
+	}
+
+	alerts, _, err := TOSession.DeleteFederationResolver(0)
+	if err == nil {
+		t.Error("Expected an error deleting a non-existent Federation Resolver, but didn't get one")
+	}
+	for _, a := range alerts.Alerts {
+		if a.Level == tc.SuccessLevel.String() {
+			t.Errorf("Unexpected success deleting a non-existent Federation Resolver: %s", a.Text)
+		} else {
+			t.Logf("Received expected %s deleting a non-existent federation resolver: %s", a.Level, a.Text)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/federation_users.go b/traffic_ops_ort/testing/ort-tests/tcdata/federation_users.go
new file mode 100644
index 0000000..1815d2e
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/federation_users.go
@@ -0,0 +1,165 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestFederationUsers(t *testing.T) {
+	if len(r.TestData.Federations) == 0 {
+		t.Error("no federations test data")
+	}
+
+	fedID := fedIDs[0]
+
+	// Get Users
+	users, _, err := TOSession.GetUsers()
+	if err != nil {
+		t.Fatalf("getting users: " + err.Error())
+	}
+	if len(users) < 3 {
+		t.Fatal("need > 3 users to create federation users")
+	}
+
+	u1 := users[0].ID
+	u2 := users[1].ID
+	u3 := users[2].ID
+
+	// Associate one user to federation
+	_, _, err = TOSession.CreateFederationUsers(fedID, []int{*u1}, false)
+	if err != nil {
+		t.Fatalf("assigning users %v to federation %v: %v", []int{*u1}, fedID, err.Error())
+	}
+
+	fedUsers, _, err := TOSession.GetFederationUsers(fedID)
+	if err != nil {
+		t.Fatalf("gettings users for federation %v: %v", fedID, err.Error())
+	}
+	if len(fedUsers) != 1 {
+		t.Errorf("federation users expected 1, actual: %+v", len(fedUsers))
+	}
+
+	// Associate two users to federation and replace first one
+	_, _, err = TOSession.CreateFederationUsers(fedID, []int{*u2, *u3}, true)
+	if err != nil {
+		t.Fatalf("assigning users %v to federation %v: %v", []int{*u2, *u3}, fedID, err.Error())
+	}
+
+	fedUsers, _, err = TOSession.GetFederationUsers(fedID)
+	if err != nil {
+		t.Fatalf("gettings users for federation %v: %v", fedID, err.Error())
+	}
+	if len(fedUsers) != 2 {
+		t.Errorf("federation users expected 2, actual: %+v", len(fedUsers))
+	}
+
+	// Associate one more user to federation
+	_, _, err = TOSession.CreateFederationUsers(fedID, []int{*u1}, false)
+	if err != nil {
+		t.Fatalf("assigning users %v to federation %v: %v", []int{*u1}, fedID, err.Error())
+	}
+
+	fedUsers, _, err = TOSession.GetFederationUsers(fedID)
+	if err != nil {
+		t.Fatalf("gettings users for federation %v: %v", fedID, err.Error())
+	}
+	if len(fedUsers) != 3 {
+		t.Errorf("federation users expected 2, actual: %+v", len(fedUsers))
+	}
+}
+
+func (r *TCData) CreateTestValidFederationUsers(t *testing.T) {
+	if len(r.TestData.Federations) == 0 {
+		t.Error("no federations test data")
+	}
+
+	fedID := fedIDs[0]
+
+	// Get Users
+	users, _, err := TOSession.GetUsers()
+	if err != nil {
+		t.Fatalf("getting users: " + err.Error())
+	}
+	if len(users) == 0 {
+		t.Fatal("need at least 1 user to test invalid federation user create")
+	}
+
+	// Associate with invalid federdation id
+	_, _, err = TOSession.CreateFederationUsers(fedID, []int{*users[0].ID}, false)
+	if err == nil {
+		t.Error("expected to get error back from associating non existent federation id")
+	}
+}
+func (r *TCData) CreateTestInvalidFederationUsers(t *testing.T) {
+	if len(r.TestData.Federations) == 0 {
+		t.Error("no federations test data")
+	}
+
+	fedID := fedIDs[0]
+
+	// Get Users
+	users, _, err := TOSession.GetUsers()
+	if err != nil {
+		t.Fatalf("getting users: " + err.Error())
+	}
+	if len(users) == 0 {
+		t.Fatal("need at least 1 user to test invalid federation user create")
+	}
+
+	// Associate with invalid federdation id
+	_, _, err = TOSession.CreateFederationUsers(-1, []int{*users[0].ID}, false)
+	if err == nil {
+		t.Error("expected to get error back from associating non existent federation id")
+	}
+
+	// Associate with invalid user id
+	_, _, err = TOSession.CreateFederationUsers(fedID, []int{-1}, false)
+	if err == nil {
+		t.Error("expected to get error back from associating non existent user id")
+	}
+}
+
+func (r *TCData) DeleteTestFederationUsers(t *testing.T) {
+	if len(r.TestData.Federations) == 0 {
+		t.Error("no federations test data")
+	}
+
+	fedID := fedIDs[0]
+
+	fedUsers, _, err := TOSession.GetFederationUsers(fedID)
+	if err != nil {
+		t.Fatalf("gettings users for federation %v: %v", fedID, err.Error())
+	}
+	if len(fedUsers) != 3 {
+		t.Errorf("federation users expected 3, actual: %+v", len(fedUsers))
+	}
+
+	for _, fedUser := range fedUsers {
+		_, _, err = TOSession.DeleteFederationUser(fedID, *fedUser.ID)
+		if err != nil {
+			t.Fatalf("deleting user %v from federation %v: %v", *fedUser.ID, fedID, err.Error())
+		}
+	}
+
+	fedUsers, _, err = TOSession.GetFederationUsers(fedID)
+	if err != nil {
+		t.Fatalf("gettings users for federation %v: %v", fedID, err.Error())
+	}
+	if len(fedUsers) != 0 {
+		t.Errorf("federation users expected 0, actual: %+v", len(fedUsers))
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/fixtures.go b/traffic_ops_ort/testing/ort-tests/tcdata/fixtures.go
new file mode 100644
index 0000000..24ce0bd
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/fixtures.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package tcdata
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+
+	"github.com/apache/trafficcontrol/lib/go-log"
+)
+
+// LoadFixtures ...
+func (r *TCData) LoadFixtures(fixturesPath string) {
+
+	f, err := ioutil.ReadFile(fixturesPath)
+	if err != nil {
+		log.Errorf("Cannot unmarshal fixtures json %s", err)
+		os.Exit(1)
+	}
+	err = json.Unmarshal(f, &r.TestData)
+	if err != nil {
+		log.Errorf("Cannot unmarshal fixtures json %v", err)
+		os.Exit(1)
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/origins.go b/traffic_ops_ort/testing/ort-tests/tcdata/origins.go
new file mode 100644
index 0000000..ce6e3e8
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/origins.go
@@ -0,0 +1,56 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestOrigins(t *testing.T) {
+	// loop through origins, assign FKs and create
+	for _, origin := range r.TestData.Origins {
+		_, _, err := TOSession.CreateOrigin(origin)
+		if err != nil {
+			t.Errorf("could not CREATE origins: %v", err)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestOrigins(t *testing.T) {
+	for _, origin := range r.TestData.Origins {
+		resp, _, err := TOSession.GetOriginByName(*origin.Name)
+		if err != nil {
+			t.Errorf("cannot GET Origin by name: %v - %v", *origin.Name, err)
+		}
+		if len(resp) > 0 {
+			respOrigin := resp[0]
+
+			delResp, _, err := TOSession.DeleteOriginByID(*respOrigin.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE Origin by ID: %v - %v", err, delResp)
+			}
+
+			// Retrieve the Origin to see if it got deleted
+			org, _, err := TOSession.GetOriginByName(*origin.Name)
+			if err != nil {
+				t.Errorf("error deleting Origin name: %s", err.Error())
+			}
+			if len(org) > 0 {
+				t.Errorf("expected Origin name: %s to be deleted", *origin.Name)
+			}
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/parameters.go b/traffic_ops_ort/testing/ort-tests/tcdata/parameters.go
new file mode 100644
index 0000000..061dec1
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/parameters.go
@@ -0,0 +1,89 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package tcdata
+
+import (
+	"sync"
+	"testing"
+
+	tc "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func (r *TCData) CreateTestParameters(t *testing.T) {
+
+	for _, pl := range r.TestData.Parameters {
+		resp, _, err := TOSession.CreateParameter(pl)
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE parameters: %v", err)
+		}
+	}
+
+}
+
+func (r *TCData) DeleteTestParametersParallel(t *testing.T) {
+
+	var wg sync.WaitGroup
+	for _, pl := range r.TestData.Parameters {
+
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			DeleteTestParameter(t, pl)
+		}()
+
+	}
+	wg.Wait()
+}
+
+func (r *TCData) DeleteTestParameters(t *testing.T) {
+
+	for _, pl := range r.TestData.Parameters {
+		DeleteTestParameter(t, pl)
+	}
+}
+
+func DeleteTestParameter(t *testing.T, pl tc.Parameter) {
+
+	// Retrieve the Parameter by name so we can get the id for the Update
+	resp, _, err := TOSession.GetParameterByNameAndConfigFile(pl.Name, pl.ConfigFile)
+	if err != nil {
+		t.Errorf("cannot GET Parameter by name: %v - %v", pl.Name, err)
+	}
+
+	if len(resp) == 0 {
+		// TODO This fails for the ProfileParameters test; determine a way to check this, even for ProfileParameters
+		// t.Errorf("DeleteTestParameter got no params for %+v %+v", pl.Name, pl.ConfigFile)
+	} else if len(resp) > 1 {
+		// TODO figure out why this happens, and be more precise about deleting things where created.
+		// t.Errorf("DeleteTestParameter params for %+v %+v expected 1, actual %+v", pl.Name, pl.ConfigFile, len(resp))
+	}
+	for _, respParameter := range resp {
+		delResp, _, err := TOSession.DeleteParameterByID(respParameter.ID)
+		if err != nil {
+			t.Errorf("cannot DELETE Parameter by name: %v - %v", err, delResp)
+		}
+
+		// Retrieve the Parameter to see if it got deleted
+		pls, _, err := TOSession.GetParameterByID(pl.ID)
+		if err != nil {
+			t.Errorf("error deleting Parameter name: %s", err.Error())
+		}
+		if len(pls) > 0 {
+			t.Errorf("expected Parameter Name: %s and ConfigFile: %s to be deleted", pl.Name, pl.ConfigFile)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/phys_locations.go b/traffic_ops_ort/testing/ort-tests/tcdata/phys_locations.go
new file mode 100644
index 0000000..907a44f
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/phys_locations.go
@@ -0,0 +1,59 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestPhysLocations(t *testing.T) {
+	for _, pl := range r.TestData.PhysLocations {
+		resp, _, err := TOSession.CreatePhysLocation(pl)
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE physlocations: %v", err)
+		}
+	}
+
+}
+
+func (r *TCData) DeleteTestPhysLocations(t *testing.T) {
+
+	for _, cdn := range r.TestData.PhysLocations {
+		// Retrieve the PhysLocation by name so we can get the id for the Update
+		resp, _, err := TOSession.GetPhysLocationByName(cdn.Name)
+		if err != nil {
+			t.Errorf("cannot GET PhysLocation by name: %v - %v", cdn.Name, err)
+		}
+		if len(resp) > 0 {
+			respPhysLocation := resp[0]
+
+			_, _, err := TOSession.DeletePhysLocationByID(respPhysLocation.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE PhysLocation by name: '%s' %v", respPhysLocation.Name, err)
+			}
+
+			// Retrieve the PhysLocation to see if it got deleted
+			cdns, _, err := TOSession.GetPhysLocationByName(cdn.Name)
+			if err != nil {
+				t.Errorf("error deleting PhysLocation name: %s", err.Error())
+			}
+			if len(cdns) > 0 {
+				t.Errorf("expected PhysLocation name: %s to be deleted", cdn.Name)
+			}
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/profile_parameters.go b/traffic_ops_ort/testing/ort-tests/tcdata/profile_parameters.go
new file mode 100644
index 0000000..cc4fdc9
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/profile_parameters.go
@@ -0,0 +1,104 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package tcdata
+
+import (
+	"fmt"
+	"sync"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+const queryParamFormat = "?profileId=%d&parameterId=%d"
+
+func (r *TCData) CreateTestProfileParameters(t *testing.T) {
+
+	firstProfile := r.TestData.Profiles[0]
+	profileResp, _, err := TOSession.GetProfileByName(firstProfile.Name)
+	if err != nil {
+		t.Errorf("cannot GET Profile by name: %v - %v", firstProfile.Name, err)
+	}
+
+	firstParameter := r.TestData.Parameters[0]
+	paramResp, _, err := TOSession.GetParameterByName(firstParameter.Name)
+	if err != nil {
+		t.Errorf("cannot GET Parameter by name: %v - %v", firstParameter.Name, err)
+	}
+
+	profileID := profileResp[0].ID
+	parameterID := paramResp[0].ID
+
+	pp := tc.ProfileParameter{
+		ProfileID:   profileID,
+		ParameterID: parameterID,
+	}
+	resp, _, err := TOSession.CreateProfileParameter(pp)
+	t.Log("Response: ", resp)
+	if err != nil {
+		t.Errorf("could not CREATE profile parameters: %v", err)
+	}
+
+}
+
+func (r *TCData) DeleteTestProfileParametersParallel(t *testing.T) {
+
+	var wg sync.WaitGroup
+	for _, pp := range r.TestData.ProfileParameters {
+
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			DeleteTestProfileParameter(t, pp)
+		}()
+
+	}
+	wg.Wait()
+}
+
+func (r *TCData) DeleteTestProfileParameters(t *testing.T) {
+
+	for _, pp := range r.TestData.ProfileParameters {
+		DeleteTestProfileParameter(t, pp)
+	}
+}
+
+func DeleteTestProfileParameter(t *testing.T, pp tc.ProfileParameter) {
+
+	queryParams := fmt.Sprintf(queryParamFormat, pp.ProfileID, pp.ParameterID)
+	// Retrieve the PtofileParameter by profile so we can get the id for the Update
+	resp, _, err := TOSession.GetProfileParameterByQueryParams(queryParams)
+	if err != nil {
+		t.Errorf("cannot GET Parameter by profile: %v - %v", pp.Profile, err)
+	}
+	if len(resp) > 0 {
+		respPP := resp[0]
+
+		delResp, _, err := TOSession.DeleteParameterByProfileParameter(respPP.ProfileID, respPP.ParameterID)
+		if err != nil {
+			t.Errorf("cannot DELETE Parameter by profile: %v - %v", err, delResp)
+		}
+
+		// Retrieve the Parameter to see if it got deleted
+		pps, _, err := TOSession.GetProfileParameterByQueryParams(queryParams)
+		if err != nil {
+			t.Errorf("error deleting Parameter name: %s", err.Error())
+		}
+		if len(pps) > 0 {
+			t.Errorf("expected Parameter Name: %s and ConfigFile: %s to be deleted", pp.Profile, pp.Parameter)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/profiles.go b/traffic_ops_ort/testing/ort-tests/tcdata/profiles.go
new file mode 100644
index 0000000..184f647
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/profiles.go
@@ -0,0 +1,142 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package tcdata
+
+import (
+	"strings"
+	"testing"
+
+	tc "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+// CreateBadProfiles ensures that profiles can't be created with bad values
+func (r *TCData) CreateBadProfiles(t *testing.T) {
+
+	// blank profile
+	prs := []tc.Profile{
+		tc.Profile{Type: "", Name: "", Description: "", CDNID: 0},
+		tc.Profile{Type: "ATS_PROFILE", Name: "badprofile", Description: "description", CDNID: 0},
+		tc.Profile{Type: "ATS_PROFILE", Name: "badprofile", Description: "", CDNID: 1},
+		tc.Profile{Type: "ATS_PROFILE", Name: "", Description: "description", CDNID: 1},
+		tc.Profile{Type: "", Name: "badprofile", Description: "description", CDNID: 1},
+	}
+
+	for _, pr := range prs {
+		resp, _, err := TOSession.CreateProfile(pr)
+
+		if err == nil {
+			t.Errorf("Creating bad profile succeeded: %+v\nResponse is %+v", pr, resp)
+		}
+	}
+}
+
+func (r *TCData) CreateTestProfiles(t *testing.T) {
+
+	for _, pr := range r.TestData.Profiles {
+		resp, _, err := TOSession.CreateProfile(pr)
+
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE profiles with name: %s %v", pr.Name, err)
+		}
+		profiles, _, err := TOSession.GetProfileByName(pr.Name)
+		if err != nil {
+			t.Errorf("could not GET profile with name: %s %v", pr.Name, err)
+		}
+		if len(profiles) == 0 {
+			t.Errorf("could not GET profile %+v: not found", pr)
+		}
+		profileID := profiles[0].ID
+
+		for _, param := range pr.Parameters {
+			if param.Name == nil || param.Value == nil || param.ConfigFile == nil {
+				t.Errorf("invalid parameter specification: %+v", param)
+				continue
+			}
+			_, _, err := TOSession.CreateParameter(tc.Parameter{Name: *param.Name, Value: *param.Value, ConfigFile: *param.ConfigFile})
+			if err != nil {
+				// ok if already exists
+				if !strings.Contains(err.Error(), "already exists") {
+					t.Errorf("could not CREATE parameter %+v: %s", param, err.Error())
+					continue
+				}
+			}
+			p, _, err := TOSession.GetParameterByNameAndConfigFileAndValue(*param.Name, *param.ConfigFile, *param.Value)
+			if err != nil {
+				t.Errorf("could not GET parameter %+v: %s", param, err.Error())
+			}
+			if len(p) == 0 {
+				t.Errorf("could not GET parameter %+v: not found", param)
+			}
+			_, _, err = TOSession.CreateProfileParameter(tc.ProfileParameter{ProfileID: profileID, ParameterID: p[0].ID})
+			if err != nil {
+				t.Errorf("could not CREATE profile_parameter %+v: %s", param, err.Error())
+			}
+		}
+
+	}
+}
+
+func (r *TCData) DeleteTestProfiles(t *testing.T) {
+
+	for _, pr := range r.TestData.Profiles {
+		// Retrieve the Profile by name so we can get the id for the Update
+		resp, _, err := TOSession.GetProfileByName(pr.Name)
+		if err != nil {
+			t.Errorf("cannot GET Profile by name: %s - %v", pr.Name, err)
+			continue
+		}
+		if len(resp) == 0 {
+			t.Errorf("cannot GET Profile by name: not found - %s", pr.Name)
+			continue
+		}
+
+		profileID := resp[0].ID
+		// query by name does not retrieve associated parameters.  But query by id does.
+		resp, _, err = TOSession.GetProfileByID(profileID)
+		if err != nil {
+			t.Errorf("cannot GET Profile by id: %v - %v", err, resp)
+		}
+		// delete any profile_parameter associations first
+		// the parameter is what's being deleted, but the delete is cascaded to profile_parameter
+		for _, param := range resp[0].Parameters {
+			_, _, err := TOSession.DeleteParameterByID(*param.ID)
+			if err != nil {
+				t.Errorf("cannot DELETE parameter with parameterID %d: %s", *param.ID, err.Error())
+			}
+		}
+		delResp, _, err := TOSession.DeleteProfileByID(profileID)
+		if err != nil {
+			t.Errorf("cannot DELETE Profile by name: %v - %v", err, delResp)
+		}
+		//time.Sleep(1 * time.Second)
+
+		// Retrieve the Profile to see if it got deleted
+		prs, _, err := TOSession.GetProfileByName(pr.Name)
+		if err != nil {
+			t.Errorf("error deleting Profile name: %s", err.Error())
+		}
+		if len(prs) > 0 {
+			t.Errorf("expected Profile Name: %s to be deleted", pr.Name)
+		}
+
+		// Attempt to export Profile
+		_, _, err = TOSession.ExportProfile(profileID)
+		if err == nil {
+			t.Errorf("expected Profile: %s to be nil on export", pr.Name)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/regions.go b/traffic_ops_ort/testing/ort-tests/tcdata/regions.go
new file mode 100644
index 0000000..099a2ca
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/regions.go
@@ -0,0 +1,89 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"strings"
+	"testing"
+)
+
+func (r *TCData) CreateTestRegions(t *testing.T) {
+
+	for _, region := range r.TestData.Regions {
+		resp, _, err := TOSession.CreateRegion(region)
+		t.Log("Response: ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE region: %v", err)
+		}
+	}
+}
+
+func (r *TCData) DeleteTestRegionsByName(t *testing.T) {
+	for _, region := range r.TestData.Regions {
+		delResp, _, err := TOSession.DeleteRegion(nil, &region.Name)
+		if err != nil {
+			t.Errorf("cannot DELETE Region by name: %v - %v", err, delResp)
+		}
+
+		deleteLog, _, err := TOSession.GetLogsByLimit(1)
+		if err != nil {
+			t.Fatalf("unable to get latest audit log entry")
+		}
+		if len(deleteLog) != 1 {
+			t.Fatalf("log entry length - expected: 1, actual: %d", len(deleteLog))
+		}
+		if !strings.Contains(*deleteLog[0].Message, region.Name) {
+			t.Errorf("region deletion audit log entry - expected: message containing region name '%s', actual: %s", region.Name, *deleteLog[0].Message)
+		}
+
+		// Retrieve the Region to see if it got deleted
+		regionResp, _, err := TOSession.GetRegionByName(region.Name)
+		if err != nil {
+			t.Errorf("error deleting Region region: %s", err.Error())
+		}
+		if len(regionResp) > 0 {
+			t.Errorf("expected Region : %s to be deleted", region.Name)
+		}
+	}
+
+	r.CreateTestRegions(t)
+}
+
+func (r *TCData) DeleteTestRegions(t *testing.T) {
+
+	for _, region := range r.TestData.Regions {
+		// Retrieve the Region by name so we can get the id
+		resp, _, err := TOSession.GetRegionByName(region.Name)
+		if err != nil {
+			t.Errorf("cannot GET Region by name: %v - %v", region.Name, err)
+		}
+		respRegion := resp[0]
+
+		delResp, _, err := TOSession.DeleteRegionByID(respRegion.ID)
+		if err != nil {
+			t.Errorf("cannot DELETE Region by region: %v - %v", err, delResp)
+		}
+
+		// Retrieve the Region to see if it got deleted
+		regionResp, _, err := TOSession.GetRegionByName(region.Name)
+		if err != nil {
+			t.Errorf("error deleting Region region: %s", err.Error())
+		}
+		if len(regionResp) > 0 {
+			t.Errorf("expected Region : %s to be deleted", region.Name)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/roles.go b/traffic_ops_ort/testing/ort-tests/tcdata/roles.go
new file mode 100644
index 0000000..1826bb6
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/roles.go
@@ -0,0 +1,77 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+const (
+	roleGood         = 0
+	roleInvalidCap   = 1
+	roleNeedCap      = 2
+	roleBadPrivLevel = 3
+)
+
+func (r *TCData) CreateTestRoles(t *testing.T) {
+	expectedAlerts := []tc.Alerts{tc.Alerts{[]tc.Alert{tc.Alert{"role was created.", "success"}}}, tc.Alerts{[]tc.Alert{tc.Alert{"can not add non-existent capabilities: [invalid-capability]", "error"}}}, tc.Alerts{[]tc.Alert{tc.Alert{"role was created.", "success"}}}}
+	for i, role := range r.TestData.Roles {
+		var alerts tc.Alerts
+		alerts, _, status, err := TOSession.CreateRole(role)
+		t.Log("Status Code: ", status)
+		t.Log("Response: ", alerts)
+		if err != nil {
+			t.Logf("error: %v", err)
+			//t.Errorf("could not CREATE role: %v", err)
+		}
+		if !reflect.DeepEqual(alerts, expectedAlerts[i]) {
+			t.Errorf("got alerts: %v but expected alerts: %v", alerts, expectedAlerts[i])
+		}
+	}
+}
+
+func (r *TCData) DeleteTestRoles(t *testing.T) {
+
+	role := r.TestData.Roles[roleGood]
+	// Retrieve the Role by name so we can get the id
+	resp, _, status, err := TOSession.GetRoleByName(*role.Name)
+	t.Log("Status Code: ", status)
+	if err != nil {
+		t.Errorf("cannot GET Role by name: %v - %v", role.Name, err)
+	}
+	respRole := resp[0]
+
+	delResp, _, status, err := TOSession.DeleteRoleByID(*respRole.ID)
+	t.Log("Status Code: ", status)
+
+	if err != nil {
+		t.Errorf("cannot DELETE Role by role: %v - %v", err, delResp)
+	}
+
+	// Retrieve the Role to see if it got deleted
+	roleResp, _, status, err := TOSession.GetRoleByName(*role.Name)
+	t.Log("Status Code: ", status)
+
+	if err != nil {
+		t.Errorf("error deleting Role role: %s", err.Error())
+	}
+	if len(roleResp) > 0 {
+		t.Errorf("expected Role : %s to be deleted", *role.Name)
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/servercapabilities.go b/traffic_ops_ort/testing/ort-tests/tcdata/servercapabilities.go
new file mode 100644
index 0000000..3177f03
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/servercapabilities.go
@@ -0,0 +1,50 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+)
+
+func (r *TCData) CreateTestServerCapabilities(t *testing.T) {
+
+	for _, sc := range r.TestData.ServerCapabilities {
+		resp, _, err := TOSession.CreateServerCapability(sc)
+		if err != nil {
+			t.Errorf("could not CREATE server capability: %v", err)
+		}
+		t.Log("Response: ", resp)
+	}
+
+}
+
+func (r *TCData) DeleteTestServerCapabilities(t *testing.T) {
+
+	for _, sc := range r.TestData.ServerCapabilities {
+		delResp, _, err := TOSession.DeleteServerCapability(sc.Name)
+		if err != nil {
+			t.Errorf("cannot DELETE server capability: %v - %v", err, delResp)
+		}
+
+		serverCapability, _, err := TOSession.GetServerCapability(sc.Name)
+		if err == nil {
+			t.Errorf("expected error trying to GET deleted server capability: %s, actual: nil", sc.Name)
+		}
+		if serverCapability != nil {
+			t.Errorf("expected nil trying to GET deleted server capability: %s, actual: non-nil", sc.Name)
+		}
+	}
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/servercheckextension.go b/traffic_ops_ort/testing/ort-tests/tcdata/servercheckextension.go
new file mode 100644
index 0000000..c14d53c
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/servercheckextension.go
@@ -0,0 +1,125 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func (r *TCData) CreateTestServerCheckExtensions(t *testing.T) {
+	toReqTimeout := time.Second * time.Duration(r.Config.Default.Session.TimeoutInSecs)
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword)
+
+	for _, ext := range r.TestData.ServerCheckExtensions {
+		resp, _, err := TOSession.CreateServerCheckExtension(ext)
+		t.Logf("Response: %v %v", *ext.Name, resp)
+		if err != nil {
+			t.Errorf("could not create to_extension %v: %v", ext.Name, err)
+		}
+	}
+
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword)
+}
+
+func (r *TCData) CreateTestInvalidServerCheckExtensions(t *testing.T) {
+	toReqTimeout := time.Second * time.Duration(r.Config.Default.Session.TimeoutInSecs)
+	// Fail Attempt to Create ServerCheckExtension as non extension user
+	_, _, err := TOSession.CreateServerCheckExtension(r.TestData.ServerCheckExtensions[0])
+	if err == nil {
+		t.Error("expected to receive error with non extension user")
+	}
+
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword)
+
+	// Attempt to create another valid ServerCheckExtension and it should fail as there is no open slots
+	toExt := tc.ServerCheckExtensionNullable{
+		Name:                 util.StrPtr("MEM_CHECKER"),
+		Version:              util.StrPtr("3.0.3"),
+		InfoURL:              util.StrPtr("-"),
+		ScriptFile:           util.StrPtr("mem.py"),
+		ServercheckShortName: util.StrPtr("MC"),
+		Type:                 util.StrPtr("CHECK_EXTENSION_MEM"),
+	}
+	_, _, err = TOSession.CreateServerCheckExtension(toExt)
+	if err == nil {
+		t.Error("expected to receive error with no open slots left")
+	}
+
+	// Attempt to create a TO Extension with an invalid type
+	toExt.Type = util.StrPtr("INVALID_TYPE")
+	_, _, err = TOSession.CreateServerCheckExtension(toExt)
+	if err == nil {
+		t.Error("expected to receive error with invalid TO extension type")
+	}
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword)
+
+}
+
+func (r *TCData) DeleteTestServerCheckExtensions(t *testing.T) {
+	toReqTimeout := time.Second * time.Duration(r.Config.Default.Session.TimeoutInSecs)
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword)
+
+	extensions, _, err := TOSession.GetServerCheckExtensions()
+	if err != nil {
+		t.Fatalf("could not get to_extensions: %v", err)
+	}
+
+	ids := []int{}
+	for _, ext := range r.TestData.ServerCheckExtensions {
+		found := false
+		for _, respTOExt := range extensions.Response {
+			if *ext.Name == *respTOExt.Name {
+				ids = append(ids, *respTOExt.ID)
+				found = true
+				continue
+			}
+		}
+		if !found {
+			t.Errorf("expected to find to_extension %v", *ext.Name)
+		}
+	}
+
+	for _, id := range ids {
+		resp, _, err := TOSession.DeleteServerCheckExtension(id)
+		t.Logf("Response: %v %v", id, resp)
+		if err != nil {
+			t.Errorf("cannot delete to_extension: %v - %v", id, err)
+		}
+	}
+	extensions, _, err = TOSession.GetServerCheckExtensions()
+	if err != nil {
+		t.Fatalf("could not get to_extensions: %v", err)
+	}
+
+	for _, ext := range r.TestData.ServerCheckExtensions {
+		found := false
+		for _, respTOExt := range extensions.Response {
+			if *ext.Name == *respTOExt.Name {
+				found = true
+				continue
+			}
+		}
+		if found {
+			t.Errorf("to_extension %v should have been deleted", *ext.Name)
+		}
+	}
+
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword)
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/serverchecks.go b/traffic_ops_ort/testing/ort-tests/tcdata/serverchecks.go
new file mode 100644
index 0000000..33cbee1
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/serverchecks.go
@@ -0,0 +1,85 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"testing"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func (r *TCData) CreateTestServerChecks(t *testing.T) {
+	toReqTimeout := time.Second * time.Duration(r.Config.Default.Session.TimeoutInSecs)
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword)
+
+	for _, servercheck := range r.TestData.Serverchecks {
+		resp, _, err := TOSession.InsertServerCheckStatus(servercheck)
+		t.Logf("Response: %v host_name %v check %v", *servercheck.HostName, *servercheck.Name, resp)
+		if err != nil {
+			t.Errorf("could not CREATE servercheck: %v", err)
+		}
+	}
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword)
+}
+
+func (r *TCData) CreateTestInvalidServerChecks(t *testing.T) {
+	toReqTimeout := time.Second * time.Duration(r.Config.Default.Session.TimeoutInSecs)
+
+	_, _, err := TOSession.InsertServerCheckStatus(r.TestData.Serverchecks[0])
+	if err == nil {
+		t.Error("expected to receive error with non extension user")
+	}
+
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword)
+
+	invalidServerCheck := tc.ServercheckRequestNullable{
+		Name:     util.StrPtr("BOGUS"),
+		Value:    util.IntPtr(1),
+		ID:       util.IntPtr(-1),
+		HostName: util.StrPtr("bogus_hostname"),
+	}
+
+	// Attempt to create a ServerCheck with invalid server ID
+	_, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+	if err == nil {
+		t.Error("expected to receive error with invalid id")
+	}
+
+	invalidServerCheck.ID = nil
+	// Attempt to create a ServerCheck with invalid host name
+	_, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+	if err == nil {
+		t.Error("expected to receive error with invalid host name")
+	}
+
+	// get valid name to get past host check
+	invalidServerCheck.Name = r.TestData.Serverchecks[0].Name
+
+	// Attempt to create a ServerCheck with invalid servercheck name
+	_, _, err = TOSession.InsertServerCheckStatus(invalidServerCheck)
+	if err == nil {
+		t.Error("expected to receive error with invalid servercheck name")
+	}
+	r.SwitchSession(toReqTimeout, r.Config.TrafficOps.URL, r.Config.TrafficOps.Users.Extension, r.Config.TrafficOps.UserPassword, r.Config.TrafficOps.Users.Admin, r.Config.TrafficOps.UserPassword)
+}
+
+// Need to define no-op function as TCObj interface expects a delete function
+// There is no delete path for serverchecks
+func (r *TCData) DeleteTestServerChecks(t *testing.T) {
+	return
+}
diff --git a/traffic_ops_ort/testing/ort-tests/tcdata/servers.go b/traffic_ops_ort/testing/ort-tests/tcdata/servers.go
new file mode 100644
index 0000000..bfdb15f
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/tcdata/servers.go
@@ -0,0 +1,143 @@
+package tcdata
+
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+import (
+	"net/url"
+	"testing"
+
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func (r *TCData) CreateTestServers(t *testing.T) {
+	// loop through servers, assign FKs and create
+	for _, server := range r.TestData.Servers {
+		if server.HostName == nil {
+			t.Errorf("found server with nil hostname: %+v", server)
+			continue
+		}
+		resp, _, err := TOSession.CreateServerWithHdr(server, nil)
+		t.Log("Response: ", *server.HostName, " ", resp)
+		if err != nil {
+			t.Errorf("could not CREATE servers: %v", err)
+		}
+	}
+}
+
+func (r *TCData) CreateTestBlankFields(t *testing.T) {
+	serverResp, _, err := TOSession.GetServersWithHdr(nil, nil)
+	if err != nil {
+		t.Fatalf("couldnt get servers: %v", err)
+	}
+	if len(serverResp.Response) < 1 {
+		t.Fatal("expected at least one server")
+	}
+	server := serverResp.Response[0]
+	originalHost := server.HostName
... 2048 lines suppressed ...