You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by jv...@apache.org on 2017/02/27 17:16:49 UTC

[06/20] incubator-trafficcontrol git commit: Create ATS Config API for master branch

Create ATS Config API for master branch


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/4362e31c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/4362e31c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/4362e31c

Branch: refs/heads/master
Commit: 4362e31c9ec0a43b2e65ec298ba95af396609f59
Parents: ed05636
Author: Derek Gelinas <de...@cable.comcast.com>
Authored: Wed Jan 18 14:35:01 2017 -0500
Committer: Jan van Doorn <jv...@apache.org>
Committed: Mon Feb 27 16:57:46 2017 +0000

----------------------------------------------------------------------
 .../20161216094845_add_scope_parameters.sql     |  146 ++
 .../app/lib/API/Configs/ApacheTrafficServer.pm  | 1959 ++++++++++++++++++
 .../Result/DeliveryServiceInfoForCdnList.pm     |  101 +
 traffic_ops/app/lib/TrafficOpsRoutes.pm         |    6 +
 traffic_ops/app/lib/UI/ConfigFiles.pm           |    3 +
 traffic_ops/app/lib/UI/DeliveryService.pm       |  156 ++
 6 files changed, 2371 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4362e31c/traffic_ops/app/db/migrations/20161216094845_add_scope_parameters.sql
----------------------------------------------------------------------
diff --git a/traffic_ops/app/db/migrations/20161216094845_add_scope_parameters.sql b/traffic_ops/app/db/migrations/20161216094845_add_scope_parameters.sql
new file mode 100644
index 0000000..40dbfb9
--- /dev/null
+++ b/traffic_ops/app/db/migrations/20161216094845_add_scope_parameters.sql
@@ -0,0 +1,146 @@
+
+-- +goose Up
+-- SQL in section 'Up' is executed when this migration is applied
+INSERT INTO
+   PARAMETER (name, config_file, VALUE) 
+   SELECT
+      'scope',
+      config_file,
+      'server' 
+   FROM
+      PARAMETER 
+   WHERE
+      name = 'location' 
+      AND config_file LIKE 'to_ext_%' 
+      OR name = 'location' 
+      AND config_file = '12M_facts' 
+      OR name = 'location' 
+      AND config_file = 'ip_allow.config' 
+      OR name = 'location' 
+      AND config_file = 'parent.config' 
+      OR name = 'location' 
+      AND config_file = 'records.config' 
+      OR name = 'location' 
+      AND config_file = 'remap.config' 
+      OR name = 'location' 
+      AND config_file = 'hosting.config' 
+      OR name = 'location' 
+      AND config_file = 'traffic_ops_ort_syncds.cron' 
+      OR name = 'location' 
+      AND config_file = 'cache.config';
+INSERT INTO
+   PARAMETER (name, config_file, VALUE) 
+   SELECT
+      'scope',
+      config_file,
+      'cdn' 
+   FROM
+      PARAMETER 
+   WHERE
+      name = 'location' 
+      AND config_file LIKE 'cacheurl%' 
+      OR name = 'location' 
+      AND config_file LIKE 'hdr_rw_%' 
+      OR name = 'location' 
+      AND config_file LIKE 'regex_remap_%' 
+      OR name = 'location' 
+      AND config_file = 'regex_revalidate.config' 
+      OR name = 'location' 
+      AND config_file LIKE 'set_dscp_%' 
+      OR name = 'location' 
+      AND config_file = 'ssl_multicert.config' 
+      OR name = 'location' 
+      AND config_file = 'bg_fetch.config';
+INSERT INTO
+   PARAMETER (name, config_file, VALUE) 
+   SELECT
+      'scope',
+      config_file,
+      'profile' 
+   FROM
+      PARAMETER 
+   WHERE
+      name = 'location' 
+      AND config_file = '50-ats.rules' 
+      OR name = 'location' 
+      AND config_file = 'astats.config' 
+      OR name = 'location' 
+      AND config_file = 'drop_qstring.config' 
+      OR name = 'location' 
+      AND config_file = 'logs_xml.config' 
+      OR name = 'location' 
+      AND config_file = 'plugin.config' 
+      OR name = 'location' 
+      AND config_file = 'storage.config' 
+      OR name = 'location' 
+      AND config_file = 'sysctl.conf' 
+      OR name = 'location' 
+      AND config_file LIKE 'url_sig_%' 
+      OR name = 'location' 
+      AND config_file = 'volume.config';
+INSERT INTO
+   profile_parameter (profile, PARAMETER) (
+   SELECT DISTINCT
+      p3.profile, p2.id AS scope_id 
+   FROM
+      (
+         SELECT
+            id,
+            config_file,
+            name 
+         FROM
+            PARAMETER 
+         WHERE
+            name = 'location'
+      )
+      p1 
+      INNER JOIN
+         (
+            SELECT
+               id,
+               config_file,
+               name 
+            FROM
+               PARAMETER 
+            WHERE
+               name = 'scope'
+         )
+         p2 
+         ON (p1.config_file = p2.config_file) 
+      INNER JOIN
+         profile_parameter p3 
+         ON (p1.id = p3.PARAMETER));
+
+
+-- +goose Down
+-- SQL section 'Down' is executed when this migration is rolled back
+DELETE
+FROM
+   profile_parameter 
+WHERE
+   PARAMETER IN 
+   (
+      SELECT DISTINCT
+         p1.id AS scope_id 
+      FROM
+         (
+            SELECT
+               id,
+               config_file,
+               name 
+            FROM
+               PARAMETER 
+            WHERE
+               name = 'scope'
+         )
+         p1 
+         INNER JOIN
+            profile_parameter p2 
+            ON (p1.id = p2.PARAMETER)
+   )
+;
+DELETE
+FROM
+   PARAMETER 
+WHERE
+   name = 'scope';

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4362e31c/traffic_ops/app/lib/API/Configs/ApacheTrafficServer.pm
----------------------------------------------------------------------
diff --git a/traffic_ops/app/lib/API/Configs/ApacheTrafficServer.pm b/traffic_ops/app/lib/API/Configs/ApacheTrafficServer.pm
new file mode 100644
index 0000000..e7b47fb
--- /dev/null
+++ b/traffic_ops/app/lib/API/Configs/ApacheTrafficServer.pm
@@ -0,0 +1,1959 @@
+package API::Configs::ApacheTrafficServer;
+
+#
+#
+# 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.
+#
+#
+#
+use UI::Utils;
+use Mojo::Base 'Mojolicious::Controller';
+use Date::Manip;
+use NetAddr::IP;
+use Data::Dumper;
+use UI::DeliveryService;
+use JSON;
+use API::DeliveryService::KeysUrlSig qw(URL_SIG_KEYS_BUCKET);
+use URI;
+use File::Basename;
+use File::Path;
+
+#Sub to generate ORT json
+sub ort {
+	my $self     = shift;
+	my $id       = $self->param('id');
+	my $filename = 'ort';
+	my $scope    = 'server';
+
+	##check user access
+	if ( !&is_oper($self) ) {
+		return $self->forbidden();
+	}
+
+	##verify that a valid server ID has been used
+	my $server_obj = $self->server_data($id);
+	if ( !defined($server_obj) ) {
+		return $self->not_found();
+	}
+
+	my $data_obj;
+	my $host_name = $server_obj->host_name;
+
+	my %condition = ( 'me.host_name' => $host_name );
+	my $rs_profile = $self->db->resultset('Server')->search( \%condition, { prefetch => [ 'cdn', 'profile' ] } );
+
+	my $row = $rs_profile->next;
+	if ($row) {
+		my $cdn_name = defined( $row->cdn_id ) ? $row->cdn->name : "";
+
+		$data_obj->{'profile'}->{'name'}   = $row->profile->name;
+		$data_obj->{'profile'}->{'id'}     = $row->profile->id;
+		$data_obj->{'other'}->{'CDN_name'} = $cdn_name;
+
+		%condition = (
+			'profile_parameters.profile' => $data_obj->{'profile'}->{'id'},
+			-or                          => [ 'name' => 'location', 'name' => 'scope' ]
+		);
+		my $rs_config = $self->db->resultset('Parameter')->search( \%condition, { join => 'profile_parameters' } );
+		while ( my $row = $rs_config->next ) {
+			if ( $row->name eq 'location' ) {
+				$data_obj->{'config_files'}->{ $row->config_file }->{'location'} = $row->value;
+			}
+			elsif ( $row->name eq 'scope' ) {
+				$data_obj->{'config_files'}->{ $row->config_file }->{'scope'} = $row->value;
+			}
+		}
+	}
+
+	foreach my $file ( keys %$data_obj->{'config_files'} ) {
+		if ( !defined( $data_obj->{'config_files'}->{$file}->{'scope'} ) ) {
+			$data_obj->{'config_files'}->{$file}->{'scope'} = $self->get_scope($file);
+		}
+	}
+
+	my $file_contents = encode_json($data_obj);
+
+	return $self->render( text => $file_contents, format => 'txt' );
+}
+
+#entry point for server scope api route.
+sub get_server_config {
+	my $self     = shift;
+	my $filename = $self->param("filename");
+	my $id       = $self->param('id');
+	my $scope    = $self->get_scope($filename);
+
+	##check user access
+	if ( !&is_oper($self) ) {
+		return $self->forbidden();
+	}
+
+	##check the scope - is this the correct route?
+	if ( $scope ne 'server' ) {
+		return $self->alert( "Error - incorrect file scope for route used.  Please use the " . $scope . " route." );
+	}
+
+	##verify that a valid server ID has been used
+	my $server_obj = $self->server_data($id);
+	if ( !defined($server_obj) ) {
+		return $self->not_found();
+	}
+
+	#generate the config file using the appropriate function
+	my $file_contents;
+	if ( $filename eq "12M_facts" ) { $file_contents = $self->facts( $server_obj, $filename, $scope ); }
+	elsif ( $filename =~ /to_ext_.*\.config/ ) { $file_contents = $self->to_ext_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "ip_allow.config" ) { $file_contents = $self->ip_allow_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "parent.config" ) { $file_contents = $self->parent_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "records.config" ) { $file_contents = $self->generic_server_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "remap.config" ) { $file_contents = $self->remap_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "hosting.config" ) { $file_contents = $self->hosting_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "cache.config" ) { $file_contents = $self->cache_dot_config( $server_obj, $filename, $scope ); }
+	elsif ( $filename eq "packages" ) {
+		$file_contents = $self->get_package_versions( $server_obj, $filename, $scope );
+		$file_contents = encode_json($file_contents);
+	}
+	elsif ( $filename eq "chkconfig" ) {
+		$file_contents = $self->get_chkconfig( $server_obj, $filename, $scope );
+		$file_contents = encode_json($file_contents);
+	}
+	else {
+		my $file_param = $self->db->resultset('Parameter')->search( [ config_file => $filename ] )->single;
+		if ( !defined($file_param) ) {
+			return $self->not_found();
+		}
+		$file_contents = $self->take_and_bake_server( $server_obj, $filename, $scope );
+	}
+
+	#if we get an empty file, just send back an error.
+	if ( !defined($file_contents) ) {
+		return $self->not_found();
+	}
+
+	#return the file contents for fetch and db actions.
+	return $self->render( text => $file_contents, format => 'txt' );
+}
+
+#entry point for cdn scope api route.
+sub get_cdn_config {
+	my $self     = shift;
+	my $filename = $self->param("filename");
+	my $id       = $self->param('id');
+	my $scope    = $self->get_scope($filename);
+
+	##check user access
+	if ( !&is_oper($self) ) {
+		return $self->forbidden();
+	}
+	print STDERR "well i'm here anyway.";
+	##check the scope - is this the correct route?
+	if ( $scope ne 'cdn' ) {
+		return $self->alert( "Error - incorrect file scope for route used.  Please use the " . $scope . " route." );
+	}
+
+	##verify that a valid cdn ID has been used
+	my $cdn_obj = $self->cdn_data($id);
+	if ( !defined($cdn_obj) ) {
+		return $self->not_found();
+	}
+
+	#generate the config file using the appropriate function
+	my $file_contents;
+	if ( $filename eq "bg_fetch.config" ) { $file_contents = $self->bg_fetch_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename =~ /cacheurl.*\.config/ ) { $file_contents = $self->cacheurl_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename =~ /hdr_rw_.*\.config/ ) { $file_contents = $self->header_rewrite_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename =~ /regex_remap_.*\.config/ ) { $file_contents = $self->regex_remap_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename eq "regex_revalidate.config" ) { $file_contents = $self->regex_revalidate_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename =~ /set_dscp_.*\.config/ ) { $file_contents = $self->set_dscp_dot_config( $cdn_obj, $filename, $scope ); }
+	elsif ( $filename eq "ssl_multicert.config" ) { $file_contents = $self->ssl_multicert_dot_config( $cdn_obj, $filename, $scope ); }
+	else                                          { return $self->not_found(); }
+
+	if ( !defined($file_contents) ) {
+		return $self->not_found();
+	}
+
+	return $self->render( text => $file_contents, format => 'txt' );
+}
+
+#entry point for profile scope api route.
+sub get_profile_config {
+	my $self     = shift;
+	my $filename = $self->param("filename");
+	my $id       = $self->param('id');
+	my $scope    = $self->get_scope($filename);
+
+	##check user access
+	if ( !&is_oper($self) ) {
+		return $self->forbidden();
+	}
+
+	##check the scope - is this the correct route?
+	if ( $scope ne 'profile' ) {
+		return $self->alert( "Error - incorrect file scope for route used.  Please use the " . $scope . " route." );
+	}
+
+	##verify that a valid profile ID has been used
+	my $profile_obj = $self->profile_data($id);
+	if ( !defined($profile_obj) ) {
+		return $self->not_found();
+	}
+
+	#generate the config file using the appropriate function
+	my $file_contents;
+	if ( $filename eq "50-ats.rules" ) { $file_contents = $self->ats_dot_rules( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "astats.config" ) { $file_contents = $self->generic_profile_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "drop_qstring.config" ) { $file_contents = $self->drop_qstring_dot_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "logs_xml.config" ) { $file_contents = $self->logs_xml_dot_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "plugin.config" ) { $file_contents = $self->generic_profile_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "storage.config" ) { $file_contents = $self->storage_dot_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "sysctl.conf" ) { $file_contents = $self->generic_profile_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename =~ /url_sig_.*\.config/ ) { $file_contents = $self->url_sig_dot_config( $profile_obj, $filename, $scope ); }
+	elsif ( $filename eq "volume.config" ) { $file_contents = $self->volume_dot_config( $profile_obj, $filename, $scope ); }
+	else {
+		my $file_param = $self->db->resultset('Parameter')->search( [ config_file => $filename ] )->single;
+		if ( !defined($file_param) ) {
+			return $self->not_found();
+		}
+		$file_contents = $self->take_and_bake_profile( $profile_obj, $filename, $scope );
+	}
+
+	if ( !defined($file_contents) ) {
+		return $self->not_found();
+	}
+
+	return $self->render( text => $file_contents, format => 'txt' );
+}
+
+my $separator ||= {
+	"records.config"  => " ",
+	"plugin.config"   => " ",
+	"sysctl.conf"     => " = ",
+	"url_sig_.config" => " = ",
+	"astats.config"   => "=",
+};
+
+#identify the correct scope for each filename.  if not found, returns server scope as any
+#undefined parameter based configs are designed with server scope using the take-and-bake sub.
+sub get_scope {
+	my $self  = shift;
+	my $fname = shift;
+	my $scope;
+
+	if    ( $fname eq "12M_facts" )               { $scope = 'server' }
+	elsif ( $fname eq "ip_allow.config" )         { $scope = 'server' }
+	elsif ( $fname eq "parent.config" )           { $scope = 'server' }
+	elsif ( $fname eq "records.config" )          { $scope = 'server' }
+	elsif ( $fname eq "remap.config" )            { $scope = 'server' }
+	elsif ( $fname =~ /to_ext_.*\.config/ )       { $scope = 'server' }
+	elsif ( $fname eq "hosting.config" )          { $scope = 'server' }
+	elsif ( $fname eq "cache.config" )            { $scope = 'server' }
+	elsif ( $fname eq "packages" )                { $scope = 'server' }
+	elsif ( $fname eq "chkconfig" )               { $scope = 'server' }
+	elsif ( $fname eq "50-ats.rules" )            { $scope = 'profile' }
+	elsif ( $fname eq "astats.config" )           { $scope = 'profile' }
+	elsif ( $fname eq "drop_qstring.config" )     { $scope = 'profile' }
+	elsif ( $fname eq "logs_xml.config" )         { $scope = 'profile' }
+	elsif ( $fname eq "plugin.config" )           { $scope = 'profile' }
+	elsif ( $fname eq "storage.config" )          { $scope = 'profile' }
+	elsif ( $fname eq "sysctl.conf" )             { $scope = 'profile' }
+	elsif ( $fname =~ /url_sig_.*\.config/ )      { $scope = 'profile' }
+	elsif ( $fname eq "volume.config" )           { $scope = 'profile' }
+	elsif ( $fname eq "bg_fetch.config" )         { $scope = 'cdn' }
+	elsif ( $fname =~ /cacheurl.*\.config/ )      { $scope = 'cdn' }
+	elsif ( $fname =~ /hdr_rw_.*\.config/ )       { $scope = 'cdn' }
+	elsif ( $fname =~ /regex_remap_.*\.config/ )  { $scope = 'cdn' }
+	elsif ( $fname eq "regex_revalidate.config" ) { $scope = 'cdn' }
+	elsif ( $fname =~ /set_dscp_.*\.config/ )     { $scope = 'cdn' }
+	elsif ( $fname eq "ssl_multicert.config" )    { $scope = 'cdn' }
+	else {
+		$scope = $self->db->resultset('Parameter')->search( { -and => [ name => 'scope', config_file => $fname ] } )->get_column('value')->first();
+		if ( !defined($scope) ) {
+			$self->app->log->error("Filename not found.  Setting Server scope.");
+			$scope = 'server';
+		}
+	}
+
+	return $scope;
+}
+
+#takes the server name or ID and turns it into a server object that can reference either, making either work for the request.
+sub server_data {
+	my $self = shift;
+	my $id   = shift;
+
+	my $server_obj;
+
+	#if an ID is passed, look up by ID.  Otherwise, look up by hostname.
+	if ( $id =~ /^\d+$/ ) {
+		$server_obj = $self->db->resultset('Server')->search( { id => $id } )->single;
+	}
+	else {
+		$server_obj = $self->db->resultset('Server')->search( { host_name => $id } )->single;
+	}
+
+	return $server_obj;
+}
+
+#takes the profile name or ID and turns it into a server object that can reference either, making either work for the request.
+sub profile_data {
+	my $self = shift;
+	my $id   = shift;
+
+	#if an ID is passed, look up by ID.  Otherwise, look up by profile name.
+	my $profile_obj;
+	if ( $id =~ /^\d+$/ ) {
+		$profile_obj = $self->db->resultset('Profile')->search( { id => $id } )->single;
+	}
+	else {
+		$profile_obj = $self->db->resultset('Profile')->search( { name => $id } )->single;
+	}
+
+	return $profile_obj;
+}
+
+#takes the server name or ID and turns it into a server object that can reference either, making either work for the request.
+sub cdn_data {
+	my $self = shift;
+	my $id   = shift;
+	my $cdn_obj;
+
+	if ( $id =~ /^\d+$/ ) {
+		$cdn_obj = $self->db->resultset('Cdn')->search( { id => $id } )->single;
+	}
+	else {
+		$cdn_obj = $self->db->resultset('Cdn')->search( { name => $id } )->single;
+	}
+
+	return $cdn_obj;
+}
+
+#generates the comment at the top of config files.
+sub header_comment {
+	my $self = shift;
+	my $name = shift;
+
+	my $text = "# DO NOT EDIT - Generated for " . $name . " by " . &name_version_string($self) . " on " . `date`;
+	return $text;
+}
+
+#retrieves parameter data for a specific server by searching by the server's assigned profile.
+sub param_data {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $data;
+
+	my $rs = $self->db->resultset('ProfileParameter')->search( { -and => [ profile => $server_obj->profile->id, 'parameter.config_file' => $filename ] },
+		{ prefetch => [ { parameter => undef }, { profile => undef } ] } );
+	while ( my $row = $rs->next ) {
+		if ( $row->parameter->name eq "location" ) {
+			next;
+		}
+		if ( $row->parameter->name eq "scope" ) {
+			next;
+		}
+		my $value = $row->parameter->value;
+
+		# some files have multiple lines with the same key... handle that with param id.
+		my $key = $row->parameter->name;
+		if ( defined( $data->{$key} ) ) {
+			$key .= "__" . $row->parameter->id;
+		}
+		if ( $value =~ /^STRING __HOSTNAME__$/ ) {
+			$value = "STRING " . $server_obj->host_name . "." . $server_obj->domain_name;
+		}
+		$data->{$key} = $value;
+	}
+	return $data;
+}
+
+#retrieves parameter data for a specific profile by searching by the profile id.
+sub profile_param_data {
+	my $self     = shift;
+	my $profile  = shift;
+	my $filename = shift;
+	my $data;
+
+	my $rs = $self->db->resultset('ProfileParameter')->search( { -and => [ profile => $profile, 'parameter.config_file' => $filename ] },
+		{ prefetch => [ { parameter => undef }, { profile => undef } ] } );
+	while ( my $row = $rs->next ) {
+		if ( $row->parameter->name eq "location" ) {
+			next;
+		}
+		if ( $row->parameter->name eq "scope" ) {
+			next;
+		}
+		my $value = $row->parameter->value;
+
+		# some files have multiple lines with the same key... handle that with param id.
+		my $key = $row->parameter->name;
+		if ( defined( $data->{$key} ) ) {
+			$key .= "__" . $row->parameter->id;
+		}
+		$data->{$key} = $value;
+	}
+	return $data;
+}
+
+#searches for the a specific parameter by name in a specific profile by profile ID and returns the data.
+sub profile_param_value {
+	my $self       = shift;
+	my $pid        = shift;
+	my $file       = shift;
+	my $param_name = shift;
+	my $default    = shift;
+
+	# assign $ds_domain, $weight and $port, and cache the results %profile_cache
+	my $param =
+		$self->db->resultset('ProfileParameter')
+		->search( { -and => [ profile => $pid, 'parameter.config_file' => $file, 'parameter.name' => $param_name ] },
+		{ prefetch => [ 'parameter', 'profile' ] } )->first();
+
+	return ( defined $param ? $param->parameter->value : $default );
+}
+
+#gets the delivery service data for an entire CDN.
+sub cdn_ds_data {
+	my $self = shift;
+	my $id   = shift;
+	my $dsinfo;
+
+	my $rs;
+	$rs = $self->db->resultset('DeliveryServiceInfoForCdnList')->search( {}, { bind => [$id] } );
+
+	my $j = 0;
+	while ( my $row = $rs->next ) {
+		my $org_server                  = $row->org_server_fqdn;
+		my $dscp                        = $row->dscp;
+		my $re_type                     = $row->re_type;
+		my $ds_type                     = $row->ds_type;
+		my $signed                      = $row->signed;
+		my $qstring_ignore              = $row->qstring_ignore;
+		my $ds_xml_id                   = $row->xml_id;
+		my $ds_domain                   = $row->domain_name;
+		my $edge_header_rewrite         = $row->edge_header_rewrite;
+		my $mid_header_rewrite          = $row->mid_header_rewrite;
+		my $regex_remap                 = $row->regex_remap;
+		my $protocol                    = $row->protocol;
+		my $range_request_handling      = $row->range_request_handling;
+		my $origin_shield               = $row->origin_shield;
+		my $cacheurl                    = $row->cacheurl;
+		my $remap_text                  = $row->remap_text;
+		my $multi_site_origin           = $row->multi_site_origin;
+		my $multi_site_origin_algorithm = $row->multi_site_origin_algorithm;
+
+		if ( $re_type eq 'HOST_REGEXP' && $ds_type ne 'ANY_MAP' ) {
+			my $host_re = $row->pattern;
+			my $map_to  = $org_server . "/";
+			if ( $host_re =~ /\.\*$/ ) {
+				my $re = $host_re;
+				$re =~ s/\\//g;
+				$re =~ s/\.\*//g;
+				my $hname    = $ds_type =~ /^DNS/ ? "edge" : "ccr";
+				my $portstr  = ":" . "SERVER_TCP_PORT";
+				my $map_from = "http://" . $hname . $re . $ds_domain . $portstr . "/";
+				if ( $protocol == 0 ) {
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 1 || $protocol == 3 ) {
+					$map_from = "https://" . $hname . $re . $ds_domain . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 2 ) {
+
+					#add the first one with http
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+
+					#add the second one for https
+					my $map_from2 = "https://" . $hname . $re . $ds_domain . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line2"}->{$map_from2} = $map_to;
+				}
+			}
+			else {
+				my $map_from = "http://" . $host_re . "/";
+				if ( $protocol == 0 ) {
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 1 || $protocol == 3 ) {
+					$map_from = "https://" . $host_re . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 2 ) {
+
+					#add the first with http
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+
+					#add the second with https
+					my $map_from2 = "https://" . $host_re . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line2"}->{$map_from2} = $map_to;
+				}
+			}
+		}
+
+		$dsinfo->{dslist}->[$j]->{"dscp"}                        = $dscp;
+		$dsinfo->{dslist}->[$j]->{"org"}                         = $org_server;
+		$dsinfo->{dslist}->[$j]->{"type"}                        = $ds_type;
+		$dsinfo->{dslist}->[$j]->{"domain"}                      = $ds_domain;
+		$dsinfo->{dslist}->[$j]->{"signed"}                      = $signed;
+		$dsinfo->{dslist}->[$j]->{"qstring_ignore"}              = $qstring_ignore;
+		$dsinfo->{dslist}->[$j]->{"ds_xml_id"}                   = $ds_xml_id;
+		$dsinfo->{dslist}->[$j]->{"edge_header_rewrite"}         = $edge_header_rewrite;
+		$dsinfo->{dslist}->[$j]->{"mid_header_rewrite"}          = $mid_header_rewrite;
+		$dsinfo->{dslist}->[$j]->{"regex_remap"}                 = $regex_remap;
+		$dsinfo->{dslist}->[$j]->{"range_request_handling"}      = $range_request_handling;
+		$dsinfo->{dslist}->[$j]->{"origin_shield"}               = $origin_shield;
+		$dsinfo->{dslist}->[$j]->{"cacheurl"}                    = $cacheurl;
+		$dsinfo->{dslist}->[$j]->{"remap_text"}                  = $remap_text;
+		$dsinfo->{dslist}->[$j]->{"multi_site_origin"}           = $multi_site_origin;
+		$dsinfo->{dslist}->[$j]->{"multi_site_origin_algorithm"} = $multi_site_origin_algorithm;
+
+		if ( defined($edge_header_rewrite) ) {
+			my $fname = "hdr_rw_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"hdr_rw_file"} = $fname;
+		}
+		if ( defined($mid_header_rewrite) ) {
+			my $fname = "hdr_rw_mid_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"mid_hdr_rw_file"} = $fname;
+		}
+		if ( defined($cacheurl) ) {
+			my $fname = "cacheurl_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"cacheurl_file"} = $fname;
+		}
+
+		$j++;
+	}
+
+	#	$self->app->session->{dsinfo} = $dsinfo;
+	return $dsinfo;
+}
+
+#gets the delivery service data for a specific server.
+sub ds_data {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $dsinfo;
+
+	$dsinfo->{host_name}   = $server_obj->host_name;
+	$dsinfo->{domain_name} = $server_obj->domain_name;
+
+	my @server_ids = ();
+	my $rs;
+	if ( $server_obj->type->name =~ m/^MID/ ) {
+
+		# the mids will do all deliveryservices in this CDN
+		my $domain = $self->profile_param_value( $server_obj->profile->id, 'CRConfig.json', 'domain_name', '' );
+		$rs = $self->db->resultset('DeliveryServiceInfoForDomainList')->search( {}, { bind => [$domain] } );
+	}
+	else {
+		$rs = $self->db->resultset('DeliveryServiceInfoForServerList')->search( {}, { bind => [ $server_obj->id ] } );
+	}
+
+	my $j = 0;
+	while ( my $row = $rs->next ) {
+		my $org_server                  = $row->org_server_fqdn;
+		my $dscp                        = $row->dscp;
+		my $re_type                     = $row->re_type;
+		my $ds_type                     = $row->ds_type;
+		my $signed                      = $row->signed;
+		my $qstring_ignore              = $row->qstring_ignore;
+		my $ds_xml_id                   = $row->xml_id;
+		my $ds_domain                   = $row->domain_name;
+		my $edge_header_rewrite         = $row->edge_header_rewrite;
+		my $mid_header_rewrite          = $row->mid_header_rewrite;
+		my $regex_remap                 = $row->regex_remap;
+		my $protocol                    = $row->protocol;
+		my $range_request_handling      = $row->range_request_handling;
+		my $origin_shield               = $row->origin_shield;
+		my $cacheurl                    = $row->cacheurl;
+		my $remap_text                  = $row->remap_text;
+		my $multi_site_origin           = $row->multi_site_origin;
+		my $multi_site_origin_algorithm = $row->multi_site_origin_algorithm;
+
+		if ( $re_type eq 'HOST_REGEXP' && $ds_type ne 'ANY_MAP' ) {
+			my $host_re = $row->pattern;
+			my $map_to  = $org_server . "/";
+			if ( $host_re =~ /\.\*$/ ) {
+				my $re = $host_re;
+				$re =~ s/\\//g;
+				$re =~ s/\.\*//g;
+				my $hname = $ds_type =~ /^DNS/ ? "edge" : "ccr";
+				my $portstr = "";
+				if ( $hname eq "ccr" && $server_obj->tcp_port > 0 && $server_obj->tcp_port != 80 ) {
+					$portstr = ":" . $server_obj->tcp_port;
+				}
+				my $map_from = "http://" . $hname . $re . $ds_domain . $portstr . "/";
+				if ( $protocol == 0 ) {
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 1 || $protocol == 3 ) {
+					$map_from = "https://" . $hname . $re . $ds_domain . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 2 ) {
+
+					#add the first one with http
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+
+					#add the second one for https
+					my $map_from2 = "https://" . $hname . $re . $ds_domain . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line2"}->{$map_from2} = $map_to;
+				}
+			}
+			else {
+				my $map_from = "http://" . $host_re . "/";
+				if ( $protocol == 0 ) {
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 1 || $protocol == 3 ) {
+					$map_from = "https://" . $host_re . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+				}
+				elsif ( $protocol == 2 ) {
+
+					#add the first with http
+					$dsinfo->{dslist}->[$j]->{"remap_line"}->{$map_from} = $map_to;
+
+					#add the second with https
+					my $map_from2 = "https://" . $host_re . "/";
+					$dsinfo->{dslist}->[$j]->{"remap_line2"}->{$map_from2} = $map_to;
+				}
+			}
+		}
+		$dsinfo->{dslist}->[$j]->{"dscp"}                        = $dscp;
+		$dsinfo->{dslist}->[$j]->{"org"}                         = $org_server;
+		$dsinfo->{dslist}->[$j]->{"type"}                        = $ds_type;
+		$dsinfo->{dslist}->[$j]->{"domain"}                      = $ds_domain;
+		$dsinfo->{dslist}->[$j]->{"signed"}                      = $signed;
+		$dsinfo->{dslist}->[$j]->{"qstring_ignore"}              = $qstring_ignore;
+		$dsinfo->{dslist}->[$j]->{"ds_xml_id"}                   = $ds_xml_id;
+		$dsinfo->{dslist}->[$j]->{"edge_header_rewrite"}         = $edge_header_rewrite;
+		$dsinfo->{dslist}->[$j]->{"mid_header_rewrite"}          = $mid_header_rewrite;
+		$dsinfo->{dslist}->[$j]->{"regex_remap"}                 = $regex_remap;
+		$dsinfo->{dslist}->[$j]->{"range_request_handling"}      = $range_request_handling;
+		$dsinfo->{dslist}->[$j]->{"origin_shield"}               = $origin_shield;
+		$dsinfo->{dslist}->[$j]->{"cacheurl"}                    = $cacheurl;
+		$dsinfo->{dslist}->[$j]->{"remap_text"}                  = $remap_text;
+		$dsinfo->{dslist}->[$j]->{"multi_site_origin"}           = $multi_site_origin;
+		$dsinfo->{dslist}->[$j]->{"multi_site_origin_algorithm"} = $multi_site_origin_algorithm;
+
+		if ( defined($edge_header_rewrite) ) {
+			my $fname = "hdr_rw_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"hdr_rw_file"} = $fname;
+		}
+		if ( defined($mid_header_rewrite) ) {
+			my $fname = "hdr_rw_mid_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"mid_hdr_rw_file"} = $fname;
+		}
+		if ( defined($cacheurl) ) {
+			my $fname = "cacheurl_" . $ds_xml_id . ".config";
+			$dsinfo->{dslist}->[$j]->{"cacheurl_file"} = $fname;
+		}
+
+		$j++;
+	}
+
+	return $dsinfo;
+}
+
+#generates the 12m_facts file
+sub facts {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+	my $text       = $self->header_comment( $server_obj->host_name );
+	$text .= "profile:" . $server_obj->profile->name . "\n";
+
+	return $text;
+}
+
+#generates a generic config file based on a server and parameters which match the supplied filename.
+sub take_and_bake_server {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $data = $self->param_data( $server_obj, $filename );
+	my $text = $self->header_comment( $server_obj->host_name );
+	foreach my $parameter ( sort keys %{$data} ) {
+		$text .= $data->{$parameter} . "\n";
+	}
+	return $text;
+}
+
+#generates a generic config file based on a profile and parameters which match the supplied filename.
+sub take_and_bake_profile {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $data = $self->profile_param_data( $profile_obj->id, $filename );
+	my $text = $self->header_comment( $profile_obj->name );
+	foreach my $parameter ( sort keys %{$data} ) {
+		$text .= $data->{$parameter} . "\n";
+	}
+	return $text;
+}
+
+#generates a generic config file based on a profile and parameters which match the supplied filename.
+#differs from take and bake in that it uses predefined separators.
+sub generic_profile_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $sep = defined( $separator->{$filename} ) ? $separator->{$filename} : " = ";
+
+	my $data = $self->profile_param_data( $profile_obj->id, $filename );
+	my $text = $self->header_comment( $profile_obj->name );
+	foreach my $parameter ( sort keys %{$data} ) {
+		my $p_name = $parameter;
+		$p_name =~ s/__\d+$//;
+		$text .= $p_name . $sep . $data->{$parameter} . "\n";
+	}
+
+	return $text;
+}
+
+#generates a generic config file based on a server and parameters which match the supplied filename.
+#differs from take and bake in that it uses predefined separators.
+sub generic_server_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $sep = defined( $separator->{$filename} ) ? $separator->{$filename} : " = ";
+
+	my $data = $self->param_data( $server_obj, $filename );
+	my $text = $self->header_comment( $server_obj->host_name );
+	foreach my $parameter ( sort keys %{$data} ) {
+		my $p_name = $parameter;
+		$p_name =~ s/__\d+$//;
+		$text .= $p_name . $sep . $data->{$parameter} . "\n";
+	}
+
+	return $text;
+}
+
+sub ats_dot_rules {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $text = $self->header_comment( $profile_obj->name );
+	my $data = $self->profile_param_data( $profile_obj->id, "storage.config" );    # ats.rules is based on the storage.config params
+
+	my $drive_prefix = $data->{Drive_Prefix};
+	my @drive_postfix = split( /,/, $data->{Drive_Letters} );
+	foreach my $l ( sort @drive_postfix ) {
+		$drive_prefix =~ s/\/dev\///;
+		$text .= "KERNEL==\"" . $drive_prefix . $l . "\", OWNER=\"ats\"\n";
+	}
+	if ( defined( $data->{RAM_Drive_Prefix} ) ) {
+		$drive_prefix = $data->{RAM_Drive_Prefix};
+		@drive_postfix = split( /,/, $data->{RAM_Drive_Letters} );
+		foreach my $l ( sort @drive_postfix ) {
+			$drive_prefix =~ s/\/dev\///;
+			$text .= "KERNEL==\"" . $drive_prefix . $l . "\", OWNER=\"ats\"\n";
+		}
+	}
+
+	return $text;
+}
+
+sub drop_qstring_dot_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $text = $self->header_comment( $profile_obj->name );
+
+	my $drop_qstring = $self->profile_param_value( $profile_obj->id, 'drop_qstring.config', 'content', undef );
+
+	if ($drop_qstring) {
+		$text .= $drop_qstring . "\n";
+	}
+	else {
+		$text .= "/([^?]+) \$s://\$t/\$1\n";
+	}
+
+	return $text;
+}
+
+sub logs_xml_dot_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $data = $self->profile_param_data( $profile_obj->id, "logs_xml.config" );
+	my $text = $self->header_comment( $profile_obj->name );
+
+	my $log_format_name                 = $data->{"LogFormat.Name"}               || "";
+	my $log_object_filename             = $data->{"LogObject.Filename"}           || "";
+	my $log_object_format               = $data->{"LogObject.Format"}             || "";
+	my $log_object_rolling_enabled      = $data->{"LogObject.RollingEnabled"}     || "";
+	my $log_object_rolling_interval_sec = $data->{"LogObject.RollingIntervalSec"} || "";
+	my $log_object_rolling_offset_hr    = $data->{"LogObject.RollingOffsetHr"}    || "";
+	my $log_object_rolling_size_mb      = $data->{"LogObject.RollingSizeMb"}      || "";
+	my $format                          = $data->{"LogFormat.Format"};
+	$format =~ s/"/\\\"/g;
+	$text .= "<LogFormat>\n";
+	$text .= "  <Name = \"" . $log_format_name . "\"/>\n";
+	$text .= "  <Format = \"" . $format . "\"/>\n";
+	$text .= "</LogFormat>\n";
+	$text .= "<LogObject>\n";
+	$text .= "  <Format = \"" . $log_object_format . "\"/>\n";
+	$text .= "  <Filename = \"" . $log_object_filename . "\"/>\n";
+	$text .= "  <RollingEnabled = " . $log_object_rolling_enabled . "/>\n" unless defined();
+	$text .= "  <RollingIntervalSec = " . $log_object_rolling_interval_sec . "/>\n";
+	$text .= "  <RollingOffsetHr = " . $log_object_rolling_offset_hr . "/>\n";
+	$text .= "  <RollingSizeMb = " . $log_object_rolling_size_mb . "/>\n";
+	$text .= "</LogObject>\n";
+
+	return $text;
+}
+
+sub storage_dot_config_volume_text {
+	my $prefix  = shift;
+	my $letters = shift;
+	my $volume  = shift;
+
+	my $text = "";
+	my @postfix = split( /,/, $letters );
+	foreach my $l ( sort @postfix ) {
+		$text .= $prefix . $l;
+		$text .= " volume=" . $volume;
+		$text .= "\n";
+	}
+	return $text;
+}
+
+sub storage_dot_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $text = $self->header_comment( $profile_obj->name );
+	my $data = $self->profile_param_data( $profile_obj->id, "storage.config" );
+
+	my $next_volume = 1;
+	if ( defined( $data->{Drive_Prefix} ) ) {
+		$text .= storage_dot_config_volume_text( $data->{Drive_Prefix}, $data->{Drive_Letters}, $next_volume );
+		$next_volume++;
+	}
+
+	if ( defined( $data->{RAM_Drive_Prefix} ) ) {
+		$text .= storage_dot_config_volume_text( $data->{RAM_Drive_Prefix}, $data->{RAM_Drive_Letters}, $next_volume );
+		$next_volume++;
+	}
+
+	if ( defined( $data->{SSD_Drive_Prefix} ) ) {
+		$text .= storage_dot_config_volume_text( $data->{SSD_Drive_Prefix}, $data->{SSD_Drive_Letters}, $next_volume );
+		$next_volume++;
+	}
+
+	return $text;
+}
+
+sub to_ext_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $text = $self->header_comment( $server_obj->host_name );
+
+	# get the subroutine name for this file from the parameter
+	my $subroutine = $self->profile_param_value( $server_obj->profile->id, $filename, 'SubRoutine', undef );
+	$self->app->log->error( "ToExtDotConfigFile == " . $subroutine );
+
+	if ( defined $subroutine ) {
+		my $package;
+		( $package = $subroutine ) =~ s/(.*)(::)(.*)/$1/;
+		eval "use $package;";
+
+		# And call it - the below calls the subroutine in the var $subroutine.
+		no strict 'refs';
+		$text .= $subroutine->( $self, $server_obj->host_name, $filename );
+		return $text;
+	}
+	else {
+		return;
+	}
+}
+
+sub get_num_volumes {
+	my $data = shift;
+
+	my $num            = 0;
+	my @drive_prefixes = qw( Drive_Prefix SSD_Drive_Prefix RAM_Drive_Prefix);
+	foreach my $pre (@drive_prefixes) {
+		if ( exists $data->{$pre} ) {
+			$num++;
+		}
+	}
+	return $num;
+}
+
+sub volume_dot_config_volume_text {
+	my $volume      = shift;
+	my $num_volumes = shift;
+	my $size        = int( 100 / $num_volumes );
+	return "volume=$volume scheme=http size=$size%\n";
+}
+
+sub volume_dot_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $data = $self->profile_param_data( $profile_obj->id, "storage.config" );
+	my $text = $self->header_comment( $profile_obj->name );
+
+	my $num_volumes = get_num_volumes($data);
+
+	my $next_volume = 1;
+	$text .= "# 12M NOTE: This is running with forced volumes - the size is irrelevant\n";
+	if ( defined( $data->{Drive_Prefix} ) ) {
+		$text .= volume_dot_config_volume_text( $next_volume, $num_volumes );
+		$next_volume++;
+	}
+	if ( defined( $data->{RAM_Drive_Prefix} ) ) {
+		$text .= volume_dot_config_volume_text( $next_volume, $num_volumes );
+		$next_volume++;
+	}
+	if ( defined( $data->{SSD_Drive_Prefix} ) ) {
+		$text .= volume_dot_config_volume_text( $next_volume, $num_volumes );
+		$next_volume++;
+	}
+
+	return $text;
+}
+
+# This is a temporary workaround until we have real partial object caching support in ATS, so hardcoding for now
+sub bg_fetch_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = $self->header_comment( $cdn_obj->name );
+	$text .= "include User-Agent *\n";
+
+	return $text;
+}
+
+sub header_rewrite_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text      = $self->header_comment( $cdn_obj->name );
+	my $ds_xml_id = undef;
+	if ( $filename =~ /^hdr_rw_mid_(.*)\.config$/ ) {
+		$ds_xml_id = $1;
+		my $ds = $self->db->resultset('Deliveryservice')->search( { xml_id => $ds_xml_id }, { prefetch => [ 'type', 'profile' ] } )->first();
+		my $actions = $ds->mid_header_rewrite;
+		$text .= $actions . "\n";
+	}
+	elsif ( $filename =~ /^hdr_rw_(.*)\.config$/ ) {
+		$ds_xml_id = $1;
+		my $ds = $self->db->resultset('Deliveryservice')->search( { xml_id => $ds_xml_id }, { prefetch => [ 'type', 'profile' ] } )->first();
+		my $actions = $ds->edge_header_rewrite;
+		$text .= $actions . "\n";
+	}
+
+	$text =~ s/\s*__RETURN__\s*/\n/g;
+
+	return $text;
+}
+
+sub cacheurl_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = $self->header_comment( $cdn_obj->name );
+	my $data = $self->cdn_ds_data( $cdn_obj->id );
+
+	if ( $filename eq "cacheurl_qstring.config" ) {    # This is the per remap drop qstring w cacheurl use case, the file is the same for all remaps
+		$text .= "http://([^?]+)(?:\\?|\$)  http://\$1\n";
+		$text .= "https://([^?]+)(?:\\?|\$)  https://\$1\n";
+	}
+	elsif ( $filename =~ /cacheurl_(.*).config/ )
+	{    # Yes, it's possibe to have the same plugin invoked multiple times on the same remap line, this is from the remap entry
+		my $ds_xml_id = $1;
+		my $ds = $self->db->resultset('Deliveryservice')->search( { xml_id => $ds_xml_id }, { prefetch => [ 'type', 'profile' ] } )->first();
+		if ($ds) {
+			$text .= $ds->cacheurl . "\n";
+		}
+	}
+	elsif ( $filename eq "cacheurl.config" ) {    # this is the global drop qstring w cacheurl use case
+		foreach my $remap ( @{ $data->{dslist} } ) {
+			if ( $remap->{qstring_ignore} == 1 ) {
+				my $org = $remap->{org};
+				$org =~ /(https?:\/\/)(.*)/;
+				$text .= "$1(" . $2 . "/[^?]+)(?:\\?|\$)  $1\$1\n";
+			}
+		}
+
+	}
+
+	$text =~ s/\s*__RETURN__\s*/\n/g;
+
+	return $text;
+}
+
+sub regex_remap_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = $self->header_comment( $cdn_obj->name );
+
+	if ( $filename =~ /^regex_remap_(.*)\.config$/ ) {
+		my $ds_xml_id = $1;
+		my $ds = $self->db->resultset('Deliveryservice')->search( { xml_id => $ds_xml_id }, { prefetch => [ 'type', 'profile' ] } )->first();
+		$text .= $ds->regex_remap . "\n";
+	}
+
+	$text =~ s/\s*__RETURN__\s*/\n/g;
+
+	return $text;
+}
+
+sub regex_revalidate_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = "# DO NOT EDIT - Generated for CDN " . $cdn_obj->name . " by " . &name_version_string($self) . " on " . `date`;
+
+	my $max_days =
+		$self->db->resultset('Parameter')->search( { name => "maxRevalDurationDays" }, { config_file => "regex_revalidate.config" } )->get_column('value')
+		->single;
+	my $interval = "> now() - interval '$max_days day'";
+
+	my %regex_time;
+	$max_days =
+		$self->db->resultset('Parameter')->search( { name => "maxRevalDurationDays" }, { config_file => "regex_revalidate.config" } )->get_column('value')
+		->first;
+	my $max_hours = $max_days * 24;
+	my $min_hours = 1;
+
+	my $rs = $self->db->resultset('Job')->search( { start_time => \$interval }, { prefetch => 'job_deliveryservice' } );
+	while ( my $row = $rs->next ) {
+		next unless defined( $row->job_deliveryservice );
+
+		# Purges are CDN - wide, and the job entry has the ds id in it.
+		my $parameters = $row->parameters;
+		my $ttl;
+		if ( $row->keyword eq "PURGE" && ( defined($parameters) && $parameters =~ /TTL:(\d+)h/ ) ) {
+			$ttl = $1;
+			if ( $ttl < $min_hours ) {
+				$ttl = $min_hours;
+			}
+			elsif ( $ttl > $max_hours ) {
+				$ttl = $max_hours;
+			}
+		}
+		else {
+			next;
+		}
+
+		my $date       = new Date::Manip::Date();
+		my $start_time = $row->start_time;
+		my $start_date = ParseDate($start_time);
+		my $end_date   = DateCalc( $start_date, ParseDateDelta( $ttl . ':00:00' ) );
+		my $err        = $date->parse($end_date);
+		if ($err) {
+			print "ERROR ON DATE CONVERSION:" . $err;
+			next;
+		}
+		my $purge_end = $date->printf("%s");    # this is in secs since the unix epoch
+
+		if ( $purge_end < time() ) {            # skip purges that have an end_time in the past
+			next;
+		}
+		my $asset_url = $row->asset_url;
+
+		my $job_cdn_id = $row->job_deliveryservice->cdn_id;
+		if ( $cdn_obj->id == $job_cdn_id ) {
+
+			# if there are multipe with same re, pick the longes lasting.
+			if ( !defined( $regex_time{ $row->asset_url } )
+				|| ( defined( $regex_time{ $row->asset_url } ) && $purge_end > $regex_time{ $row->asset_url } ) )
+			{
+				$regex_time{ $row->asset_url } = $purge_end;
+			}
+		}
+	}
+
+	foreach my $re ( sort keys %regex_time ) {
+		$text .= $re . " " . $regex_time{$re} . "\n";
+	}
+
+	return $text;
+}
+
+sub set_dscp_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = $self->header_comment( $cdn_obj->name );
+	my $dscp_decimal;
+	if ( $filename =~ /^set_dscp_(\d+)\.config$/ ) {
+		$dscp_decimal = $1;
+	}
+	else {
+		$text = "An error occured generating the DSCP header rewrite file.";
+	}
+	$text .= "cond %{REMAP_PSEUDO_HOOK}\n" . "set-conn-dscp " . $dscp_decimal . " [L]\n";
+
+	return $text;
+}
+
+sub ssl_multicert_dot_config {
+	my $self     = shift;
+	my $cdn_obj  = shift;
+	my $filename = shift;
+	my $scope    = shift;
+
+	my $text = $self->header_comment( $cdn_obj->name );
+
+	## We should break this search out into a separate sub later
+	my $protocol_search = '> 0';
+	my @ds_list = $self->db->resultset('Deliveryservice')->search( { -and => [ cdn_id => $cdn_obj->id, 'me.protocol' => \$protocol_search ] } )->all();
+	foreach my $ds (@ds_list) {
+		my $ds_id        = $ds->id;
+		my $xml_id       = $ds->xml_id;
+		my $rs_ds        = $self->db->resultset('Deliveryservice')->search( { 'me.id' => $ds_id } );
+		my $data         = $rs_ds->single;
+		my $domain_name  = UI::DeliveryService::get_cdn_domain( $self, $ds_id );
+		my $ds_regexes   = UI::DeliveryService::get_regexp_set( $self, $ds_id );
+		my @example_urls = UI::DeliveryService::get_example_urls( $self, $ds_id, $ds_regexes, $data, $domain_name, $data->protocol );
+
+		#first one is the one we want
+		my $hostname = $example_urls[0];
+		$hostname =~ /(https?:\/\/)(.*)/;
+		my $new_host = $2;
+		my $key_name = "$new_host.key";
+		$new_host =~ tr/./_/;
+		my $cer_name = $new_host . "_cert.cer";
+
+		$text .= "ssl_cert_name=$cer_name\t ssl_key_name=$key_name\n";
+	}
+
+	return $text;
+}
+
+sub url_sig_dot_config {
+	my $self        = shift;
+	my $profile_obj = shift;
+	my $filename    = shift;
+	my $scope       = shift;
+
+	my $sep = defined( $separator->{$filename} ) ? $separator->{$filename} : " = ";
+	my $data = $self->profile_param_data( $profile_obj->id, $filename );
+	my $text = $self->header_comment( $profile_obj->name );
+
+	my $response_container = $self->riak_get( URL_SIG_KEYS_BUCKET, $filename );
+	my $response = $response_container->{response};
+	if ( $response->is_success() ) {
+		my $response_json = decode_json( $response->content );
+		my $keys          = $response_json;
+		foreach my $parameter ( sort keys %{$data} ) {
+			if ( !defined($keys) || $parameter !~ /^key\d+/ ) {    # only use key parameters as a fallback (temp, remove me later)
+				$text .= $parameter . $sep . $data->{$parameter} . "\n";
+			}
+		}
+
+		foreach my $parameter ( sort keys %{$keys} ) {
+			$text .= $parameter . $sep . $keys->{$parameter} . "\n";
+		}
+
+		return $text;
+	}
+	else {
+		return;
+	}
+}
+
+sub cache_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $text = $self->header_comment( $server_obj->host_name );
+	my $data = $self->ds_data($server_obj);
+
+	foreach my $remap ( @{ $data->{dslist} } ) {
+		if ( $remap->{type} eq "HTTP_NO_CACHE" ) {
+			my $org_fqdn = $remap->{org};
+			$org_fqdn =~ s/https?:\/\///;
+			$text .= "dest_domain=" . $org_fqdn . " scheme=http action=never-cache\n";
+		}
+	}
+
+	return $text;
+}
+
+sub hosting_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $storage_data = $self->param_data( $server_obj, "storage.config" );
+	my $text = $self->header_comment( $server_obj->host_name );
+
+	my $data = $self->ds_data($server_obj);
+
+	if ( defined( $storage_data->{RAM_Drive_Prefix} ) ) {
+		my $next_volume = 1;
+		if ( defined( $storage_data->{Drive_Prefix} ) ) {
+			my $disk_volume = $next_volume;
+			$text .= "# 12M NOTE: volume " . $disk_volume . " is the Disk volume\n";
+			$next_volume++;
+		}
+		my $ram_volume = $next_volume;
+		$text .= "# 12M NOTE: volume " . $ram_volume . " is the RAM volume\n";
+
+		my %listed = ();
+		foreach my $remap ( @{ $data->{dslist} } ) {
+			if (   ( ( $remap->{type} =~ /_LIVE$/ || $remap->{type} =~ /_LIVE_NATNL$/ ) && $server_obj->type->name =~ m/^EDGE/ )
+				|| ( $remap->{type} =~ /_LIVE_NATNL$/ && $server_obj->type->name =~ m/^MID/ ) )
+			{
+				if ( defined( $listed{ $remap->{org} } ) ) { next; }
+				my $org_fqdn = $remap->{org};
+				$org_fqdn =~ s/https?:\/\///;
+				$text .= "hostname=" . $org_fqdn . " volume=" . $ram_volume . "\n";
+				$listed{ $remap->{org} } = 1;
+			}
+		}
+	}
+	my $disk_volume = 1;    # note this will actually be the RAM (RAM_Drive_Prefix) volume if there is no Drive_Prefix parameter.
+	$text .= "hostname=*   volume=" . $disk_volume . "\n";
+
+	return $text;
+}
+
+sub ip_allow_data {
+	my $self       = shift;
+	my $server_obj = shift;
+
+	my $ipallow;
+	$ipallow = ();
+
+	my $i = 0;
+
+	# localhost is trusted.
+	$ipallow->[$i]->{src_ip} = '127.0.0.1';
+	$ipallow->[$i]->{action} = 'ip_allow';
+	$ipallow->[$i]->{method} = "ALL";
+	$i++;
+	$ipallow->[$i]->{src_ip} = '::1';
+	$ipallow->[$i]->{action} = 'ip_allow';
+	$ipallow->[$i]->{method} = "ALL";
+	$i++;
+	my $rs_parameter =
+		$self->db->resultset('ProfileParameter')
+		->search( { profile => $server_obj->profile->id }, { prefetch => [ { parameter => undef }, { profile => undef } ] } );
+
+	while ( my $row = $rs_parameter->next ) {
+		if ( $row->parameter->name eq 'purge_allow_ip' && $row->parameter->config_file eq 'ip_allow.config' ) {
+			$ipallow->[$i]->{src_ip} = $row->parameter->value;
+			$ipallow->[$i]->{action} = "ip_allow";
+			$ipallow->[$i]->{method} = "ALL";
+			$i++;
+		}
+	}
+
+	if ( $server_obj->type->name =~ m/^MID/ ) {
+		my @edge_locs = $self->db->resultset('Cachegroup')->search( { parent_cachegroup_id => $server_obj->cachegroup->id } )->get_column('id')->all();
+		my %allow_locs;
+		foreach my $loc (@edge_locs) {
+			$allow_locs{$loc} = 1;
+		}
+
+		# get all the EDGE and RASCAL nets
+		my @allowed_netaddrips;
+		my @allowed_ipv6_netaddrips;
+		my @types;
+		push( @types, &type_ids( $self, 'EDGE%', 'server' ) );
+		my $rtype = &type_id( $self, 'RASCAL' );
+		push( @types, $rtype );
+		my $rs_allowed = $self->db->resultset('Server')->search( { 'me.type' => { -in => \@types } }, { prefetch => [ 'type', 'cachegroup' ] } );
+
+		while ( my $allow_row = $rs_allowed->next ) {
+			if ( $allow_row->type->id == $rtype
+				|| ( defined( $allow_locs{ $allow_row->cachegroup->id } ) && $allow_locs{ $allow_row->cachegroup->id } == 1 ) )
+			{
+				my $ipv4 = NetAddr::IP->new( $allow_row->ip_address, $allow_row->ip_netmask );
+
+				if ( defined($ipv4) ) {
+					push( @allowed_netaddrips, $ipv4 );
+				}
+				else {
+					$self->app->log->error(
+						$allow_row->host_name . " has an invalid IPv4 address; excluding from ip_allow data for " . $server_obj->host_name );
+				}
+
+				if ( defined $allow_row->ip6_address ) {
+					my $ipv6 = NetAddr::IP->new( $allow_row->ip6_address );
+
+					if ( defined($ipv6) ) {
+						push( @allowed_ipv6_netaddrips, NetAddr::IP->new( $allow_row->ip6_address ) );
+					}
+					else {
+						$self->app->log->error(
+							$allow_row->host_name . " has an invalid IPv6 address; excluding from ip_allow data for " . $server_obj->host_name );
+					}
+				}
+			}
+		}
+
+		# compact, coalesce and compact combined list again
+		# if more than 5 servers are in a /24, list that /24 - TODO JvD: parameterize
+		my @compacted_list = NetAddr::IP::Compact(@allowed_netaddrips);
+		my $coalesced_list = NetAddr::IP::Coalesce( 24, 5, @allowed_netaddrips );
+		my @combined_list  = NetAddr::IP::Compact( @allowed_netaddrips, @{$coalesced_list} );
+		foreach my $net (@combined_list) {
+			my $range = $net->range();
+			$range =~ s/\s+//g;
+			$ipallow->[$i]->{src_ip} = $range;
+			$ipallow->[$i]->{action} = "ip_allow";
+			$ipallow->[$i]->{method} = "ALL";
+			$i++;
+		}
+
+		# now add IPv6. TODO JvD: paremeterize support enabled on/ofd and /48 and number 5
+		my @compacted__ipv6_list = NetAddr::IP::Compact(@allowed_ipv6_netaddrips);
+		my $coalesced_ipv6_list  = NetAddr::IP::Coalesce( 48, 5, @allowed_ipv6_netaddrips );
+		my @combined_ipv6_list   = NetAddr::IP::Compact( @allowed_ipv6_netaddrips, @{$coalesced_ipv6_list} );
+		foreach my $net (@combined_ipv6_list) {
+			my $range = $net->range();
+			$range =~ s/\s+//g;
+			$ipallow->[$i]->{src_ip} = $range;
+			$ipallow->[$i]->{action} = "ip_allow";
+			$ipallow->[$i]->{method} = "ALL";
+			$i++;
+		}
+
+		# allow RFC 1918 server space - TODO JvD: parameterize
+		$ipallow->[$i]->{src_ip} = '10.0.0.0-10.255.255.255';
+		$ipallow->[$i]->{action} = 'ip_allow';
+		$ipallow->[$i]->{method} = "ALL";
+		$i++;
+
+		$ipallow->[$i]->{src_ip} = '172.16.0.0-172.31.255.255';
+		$ipallow->[$i]->{action} = 'ip_allow';
+		$ipallow->[$i]->{method} = "ALL";
+		$i++;
+
+		$ipallow->[$i]->{src_ip} = '192.168.0.0-192.168.255.255';
+		$ipallow->[$i]->{action} = 'ip_allow';
+		$ipallow->[$i]->{method} = "ALL";
+		$i++;
+
+		# end with a deny
+		$ipallow->[$i]->{src_ip} = '0.0.0.0-255.255.255.255';
+		$ipallow->[$i]->{action} = 'ip_deny';
+		$ipallow->[$i]->{method} = "ALL";
+		$i++;
+		$ipallow->[$i]->{src_ip} = '::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff';
+		$ipallow->[$i]->{action} = 'ip_deny';
+		$ipallow->[$i]->{method} = "ALL";
+		$i++;
+	}
+	else {
+
+		# for edges deny "PUSH|PURGE|DELETE", allow everything else to everyone.
+		$ipallow->[$i]->{src_ip} = '0.0.0.0-255.255.255.255';
+		$ipallow->[$i]->{action} = 'ip_deny';
+		$ipallow->[$i]->{method} = "PUSH|PURGE|DELETE";
+		$i++;
+		$ipallow->[$i]->{src_ip} = '::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff';
+		$ipallow->[$i]->{action} = 'ip_deny';
+		$ipallow->[$i]->{method} = "PUSH|PURGE|DELETE";
+		$i++;
+	}
+
+	return $ipallow;
+}
+
+sub ip_allow_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $text = $self->header_comment( $server_obj->host_name );
+	my $data = $self->ip_allow_data( $server_obj, $filename );
+
+	foreach my $access ( @{$data} ) {
+		$text .= sprintf( "src_ip=%-70s action=%-10s method=%-20s\n", $access->{src_ip}, $access->{action}, $access->{method} );
+	}
+
+	return $text;
+}
+
+sub by_parent_rank {
+	my ($arank) = $a->{"rank"};
+	my ($brank) = $b->{"rank"};
+	( $arank || 1 ) <=> ( $brank || 1 );
+}
+
+sub cachegroup_profiles {
+	my $self             = shift;
+	my $ids              = shift;
+	my $profile_cache    = shift;
+	my $deliveryservices = shift;
+
+	if ( !@$ids ) {
+		return;    # nothing to see here..
+	}
+	my $online   = &admin_status_id( $self, "ONLINE" );
+	my $reported = &admin_status_id( $self, "REPORTED" );
+
+	my %condition = (
+		status     => { -in => [ $online, $reported ] },
+		cachegroup => { -in => $ids }
+	);
+
+	my $rs_parent = $self->db->resultset('Server')->search( \%condition, { prefetch => [ 'cachegroup', 'status', 'type', 'profile' ] } );
+
+	while ( my $row = $rs_parent->next ) {
+
+		next unless ( $row->type->name eq 'ORG' || $row->type->name =~ m/^EDGE/ || $row->type->name =~ m/^MID/ );
+		if ( $row->type->name eq 'ORG' ) {
+			my $rs_ds = $self->db->resultset('DeliveryserviceServer')->search( { server => $row->id }, { prefetch => ['deliveryservice'] } );
+			while ( my $ds_row = $rs_ds->next ) {
+				my $ds_domain = $ds_row->deliveryservice->org_server_fqdn;
+				$ds_domain =~ s/https?:\/\/(.*)/$1/;
+				push( @{ $deliveryservices->{$ds_domain} }, $row );
+			}
+		}
+		else {
+			push( @{ $deliveryservices->{all_parents} }, $row );
+		}
+
+		# get the profile info, and cache it in %profile_cache
+		my $pid = $row->profile->id;
+		if ( !defined( $profile_cache->{$pid} ) ) {
+
+			# assign $ds_domain, $weight and $port, and cache the results %profile_cache
+			$profile_cache->{$pid} = {
+				domain_name    => $self->profile_param_value( $pid, 'CRConfig.json', 'domain_name',    undef ),
+				weight         => $self->profile_param_value( $pid, 'parent.config', 'weight',         '0.999' ),
+				port           => $self->profile_param_value( $pid, 'parent.config', 'port',           undef ),
+				use_ip_address => $self->profile_param_value( $pid, 'parent.config', 'use_ip_address', 0 ),
+				rank           => $self->profile_param_value( $pid, 'parent.config', 'rank',           1 ),
+			};
+		}
+	}
+}
+
+sub parent_data {
+	my $self       = shift;
+	my $server_obj = shift;
+
+	my @parent_cachegroup_ids;
+	my @secondary_parent_cachegroup_ids;
+	my $org_loc_type_id = &type_id( $self, "ORG_LOC" );
+	if ( $server_obj->type->name =~ m/^MID/ ) {
+
+		# multisite origins take all the org groups in to account
+		@parent_cachegroup_ids = $self->db->resultset('Cachegroup')->search( { type => $org_loc_type_id } )->get_column('id')->all();
+	}
+	else {
+		@parent_cachegroup_ids =
+			grep {defined} $self->db->resultset('Cachegroup')->search( { id => $server_obj->cachegroup->id } )->get_column('parent_cachegroup_id')->all();
+		@secondary_parent_cachegroup_ids =
+			grep {defined}
+			$self->db->resultset('Cachegroup')->search( { id => $server_obj->cachegroup->id } )->get_column('secondary_parent_cachegroup_id')->all();
+	}
+
+	# get the server's cdn domain
+	my $server_domain = $self->profile_param_value( $server_obj->profile->id, 'CRConfig.json', 'domain_name' );
+
+	my %profile_cache;
+	my %deliveryservices;
+	my %parent_info;
+
+	$self->cachegroup_profiles( \@parent_cachegroup_ids,           \%profile_cache, \%deliveryservices );
+	$self->cachegroup_profiles( \@secondary_parent_cachegroup_ids, \%profile_cache, \%deliveryservices );
+	foreach my $prefix ( keys %deliveryservices ) {
+		foreach my $row ( @{ $deliveryservices{$prefix} } ) {
+			my $pid              = $row->profile->id;
+			my $ds_domain        = $profile_cache{$pid}->{domain_name};
+			my $weight           = $profile_cache{$pid}->{weight};
+			my $port             = $profile_cache{$pid}->{port};
+			my $use_ip_address   = $profile_cache{$pid}->{use_ip_address};
+			my $rank             = $profile_cache{$pid}->{rank};
+			my $primary_parent   = $server_obj->cachegroup->parent_cachegroup_id // -1;
+			my $secondary_parent = $server_obj->cachegroup->secondary_parent_cachegroup_id // -1;
+			if ( defined($ds_domain) && defined($server_domain) && $ds_domain eq $server_domain ) {
+				my %p = (
+					host_name        => $row->host_name,
+					port             => defined($port) ? $port : $row->tcp_port,
+					domain_name      => $row->domain_name,
+					weight           => $weight,
+					use_ip_address   => $use_ip_address,
+					rank             => $rank,
+					ip_address       => $row->ip_address,
+					primary_parent   => ( $primary_parent == $row->cachegroup->id ) ? 1 : 0,
+					secondary_parent => ( $secondary_parent == $row->cachegroup->id ) ? 1 : 0,
+				);
+				push @{ $parent_info{$prefix} }, \%p;
+			}
+		}
+	}
+	return \%parent_info;
+}
+
+sub format_parent_info {
+	my $parent = shift;
+	if ( !defined $parent ) {
+		return "";    # should never happen..
+	}
+	my $host =
+		( $parent->{use_ip_address} == 1 )
+		? $parent->{ip_address}
+		: $parent->{host_name} . '.' . $parent->{domain_name};
+
+	my $port   = $parent->{port};
+	my $weight = $parent->{weight};
+	my $text   = "$host:$port|$weight;";
+	return $text;
+}
+
+sub parent_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $data;
+
+	my $server_type = $server_obj->type->name;
+	my $parent_qstring;
+	my $pinfo;
+	my $text = $self->header_comment( $server_obj->host_name );
+	if ( !defined($data) ) {
+		$data = $self->ds_data($server_obj);
+	}
+
+	if ( $server_type =~ m/^MID/ ) {
+		my @unique_origin;
+		foreach my $ds ( @{ $data->{dslist} } ) {
+			my $xml_id = $ds->{ds_xml_id};
+			my $os     = $ds->{origin_shield};
+			$parent_qstring = "ignore";
+			my $multi_site_origin           = defined( $ds->{multi_site_origin} )           ? $ds->{multi_site_origin}           : 0;
+			my $multi_site_origin_algorithm = defined( $ds->{multi_site_origin_algorithm} ) ? $ds->{multi_site_origin_algorithm} : 0;
+
+			my $org_uri = URI->new( $ds->{org} );
+
+			# Don't duplicate origin line if multiple seen
+			next if ( grep( /^$org_uri$/, @unique_origin ) );
+			push @unique_origin, $org_uri;
+
+			if ( defined($os) ) {
+				my $pselect_alg = $self->profile_param_value( $server_obj->profile->id, 'parent.config', 'algorithm', undef );
+				my $algorithm = "";
+				if ( defined($pselect_alg) ) {
+					$algorithm = "round_robin=$pselect_alg";
+				}
+				$text .= "dest_domain=" . $org_uri->host . " port=" . $org_uri->port . " parent=$os $algorithm go_direct=true\n";
+			}
+			elsif ($multi_site_origin) {
+				$text .= "dest_domain=" . $org_uri->host . " port=" . $org_uri->port . " ";
+
+				# If we have multi-site origin, get parent_data once
+				if ( not defined($pinfo) ) {
+					$pinfo = $self->parent_data($server_obj);
+				}
+
+				my @ranked_parents = ();
+				if ( exists( $pinfo->{ $org_uri->host } ) ) {
+					@ranked_parents = sort by_parent_rank @{ $pinfo->{ $org_uri->host } };
+				}
+				else {
+					$self->app->log->debug( "BUG: Did not match an origin: " . $org_uri );
+				}
+
+				my @parent_info;
+				my @secondary_parent_info;
+				my @null_parent_info;
+				foreach my $parent (@ranked_parents) {
+					if ( $parent->{primary_parent} ) {
+						push @parent_info, format_parent_info($parent);
+					}
+					elsif ( $parent->{secondary_parent} ) {
+						push @secondary_parent_info, format_parent_info($parent);
+					}
+					else {
+						push @null_parent_info, format_parent_info($parent);
+					}
+				}
+				my %seen;
+				@parent_info = grep { !$seen{$_}++ } @parent_info;
+
+				if ( scalar @secondary_parent_info > 0 ) {
+					my %seen;
+					@secondary_parent_info = grep { !$seen{$_}++ } @secondary_parent_info;
+				}
+				if ( scalar @null_parent_info > 0 ) {
+					my %seen;
+					@null_parent_info = grep { !$seen{$_}++ } @null_parent_info;
+				}
+
+				my $parents = 'parent="' . join( '', @parent_info ) . '' . join( '', @secondary_parent_info ) . '' . join( '', @null_parent_info ) . '"';
+				my $mso_algorithm = "";
+				if ( $multi_site_origin_algorithm == 0 ) {
+					$mso_algorithm = "consistent_hash";
+					if ( $ds->{qstring_ignore} == 0 ) {
+						$parent_qstring = "consider";
+					}
+				}
+				elsif ( $multi_site_origin_algorithm == 1 ) {
+					$mso_algorithm = "false";
+				}
+				elsif ( $multi_site_origin_algorithm == 2 ) {
+					$mso_algorithm = "strict";
+				}
+				elsif ( $multi_site_origin_algorithm == 3 ) {
+					$mso_algorithm = "true";
+				}
+				elsif ( $multi_site_origin_algorithm == 4 ) {
+					$mso_algorithm = "latched";
+				}
+				else {
+					$mso_algorithm = "consistent_hash";
+				}
+				$text .= "$parents round_robin=$mso_algorithm qstring=$parent_qstring go_direct=false parent_is_proxy=false\n";
+			}
+		}
+
+		#$text .= "dest_domain=. go_direct=true\n"; # this is implicit.
+		$self->app->log->debug( "MID PARENT.CONFIG:\n" . $text . "\n" );
+
+		return $text;
+	}
+	else {
+
+		#"True" Parent
+		$pinfo = $self->parent_data($server_obj);
+
+		my %done = ();
+
+		foreach my $remap ( @{ $data->{dslist} } ) {
+			my $org = $remap->{org};
+			$parent_qstring = "ignore";
+			next if !defined $org || $org eq "";
+			next if $done{$org};
+			my $org_uri = URI->new($org);
+			if ( $remap->{type} eq "HTTP_NO_CACHE" || $remap->{type} eq "HTTP_LIVE" || $remap->{type} eq "DNS_LIVE" ) {
+				$text .= "dest_domain=" . $org_uri->host . " port=" . $org_uri->port . " go_direct=true\n";
+			}
+			else {
+				if ( $remap->{qstring_ignore} == 0 ) {
+					$parent_qstring = "consider";
+				}
+
+				my @parent_info;
+				my @secondary_parent_info;
+				foreach my $parent ( @{ $pinfo->{all_parents} } ) {
+					my $ptxt = format_parent_info($parent);
+					if ( $parent->{primary_parent} ) {
+						push @parent_info, $ptxt;
+					}
+					elsif ( $parent->{secondary_parent} ) {
+						push @secondary_parent_info, $ptxt;
+					}
+				}
+				my %seen;
+				@parent_info = grep { !$seen{$_}++ } @parent_info;
+				my $parents = 'parent="' . join( '', @parent_info ) . '"';
+				my $secparents = '';
+				if ( scalar @secondary_parent_info > 0 ) {
+					my %seen;
+					@secondary_parent_info = grep { !$seen{$_}++ } @secondary_parent_info;
+					$secparents = 'secondary_parent="' . join( '', @secondary_parent_info ) . '"';
+				}
+				my $round_robin = 'round_robin=consistent_hash';
+				my $go_direct   = 'go_direct=false';
+				$text
+					.= "dest_domain="
+					. $org_uri->host
+					. " port="
+					. $org_uri->port
+					. " $parents $secparents $round_robin $go_direct qstring=$parent_qstring\n";
+			}
+			$done{$org} = 1;
+		}
+
+		my $pselect_alg = $self->profile_param_value( $server_obj->profile->id, 'parent.config', 'algorithm', undef );
+		if ( defined($pselect_alg) && $pselect_alg eq 'consistent_hash' ) {
+			my @parent_info;
+			foreach my $parent ( @{ $pinfo->{"all_parents"} } ) {
+				push @parent_info, $parent->{"host_name"} . "." . $parent->{"domain_name"} . ":" . $parent->{"port"} . "|" . $parent->{"weight"} . ";";
+			}
+			my %seen;
+			@parent_info = grep { !$seen{$_}++ } @parent_info;
+			$text .= "dest_domain=.";
+			$text .= " parent=\"" . join( '', @parent_info ) . "\"";
+			$text .= " round_robin=consistent_hash go_direct=false";
+		}
+		else {    # default to old situation.
+			$text .= "dest_domain=.";
+			my @parent_info;
+			foreach my $parent ( @{ $pinfo->{"all_parents"} } ) {
+				push @parent_info, $parent->{"host_name"} . "." . $parent->{"domain_name"} . ":" . $parent->{"port"} . ";";
+			}
+			my %seen;
+			@parent_info = grep { !$seen{$_}++ } @parent_info;
+			$text .= " parent=\"" . join( '', @parent_info ) . "\"";
+			$text .= " round_robin=urlhash go_direct=false";
+		}
+
+		my $qstring = $self->profile_param_value( $server_obj->profile->id, 'parent.config', 'qstring', undef );
+		if ( defined($qstring) ) {
+			$text .= " qstring=" . $qstring;
+		}
+
+		$text .= "\n";
+
+		return $text;
+	}
+}
+
+sub remap_dot_config {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+	my $data;
+
+	my $pdata = $self->param_data( $server_obj, 'package' );
+	my $text = $self->header_comment( $server_obj->host_name );
+	if ( !defined($data) ) {
+		$data = $self->ds_data($server_obj);
+	}
+
+	if ( $server_obj->type->name =~ m/^MID/ ) {
+		my %mid_remap;
+		foreach my $remap ( @{ $data->{dslist} } ) {
+			if ( $remap->{type} =~ /LIVE/ && $remap->{type} !~ /NATNL/ ) {
+				next;    # Live local delivery services skip mids
+			}
+			if ( defined( $remap->{org} ) && defined( $mid_remap{ $remap->{org} } ) ) {
+				next;    # skip remap rules from extra HOST_REGEXP entries
+			}
+
+			if ( defined( $remap->{mid_header_rewrite} ) && $remap->{mid_header_rewrite} ne "" ) {
+				$mid_remap{ $remap->{org} } .= " \@plugin=header_rewrite.so \@pparam=" . $remap->{mid_hdr_rw_file};
+			}
+			if ( $remap->{qstring_ignore} == 1 ) {
+				$mid_remap{ $remap->{org} } .= " \@plugin=cacheurl.so \@pparam=cacheurl_qstring.config";
+			}
+			if ( defined( $remap->{cacheurl} ) && $remap->{cacheurl} ne "" ) {
+				$mid_remap{ $remap->{org} } .= " \@plugin=cacheurl.so \@pparam=" . $remap->{cacheurl_file};
+			}
+			if ( $remap->{range_request_handling} == 2 ) {
+				$mid_remap{ $remap->{org} } .= " \@plugin=cache_range_requests.so";
+			}
+		}
+		foreach my $key ( keys %mid_remap ) {
+			$text .= "map " . $key . " " . $key . $mid_remap{$key} . "\n";
+		}
+
+		return $text;
+	}
+
+	# mids don't get here.
+	foreach my $remap ( @{ $data->{dslist} } ) {
+		foreach my $map_from ( keys %{ $remap->{remap_line} } ) {
+			my $map_to = $remap->{remap_line}->{$map_from};
+			$text = $self->build_remap_line( $server_obj, $pdata, $text, $data, $remap, $map_from, $map_to );
+		}
+		foreach my $map_from ( keys %{ $remap->{remap_line2} } ) {
+			my $map_to = $remap->{remap_line2}->{$map_from};
+			$text = $self->build_remap_line( $server_obj, $pdata, $text, $data, $remap, $map_from, $map_to );
+		}
+	}
+	return $text;
+}
+
+sub build_remap_line {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $pdata      = shift;
+	my $text       = shift;
+	my $data       = shift;
+	my $remap      = shift;
+	my $map_from   = shift;
+	my $map_to     = shift;
+
+	if ( $remap->{type} eq 'ANY_MAP' ) {
+		$text .= $remap->{remap_text} . "\n";
+		return $text;
+	}
+
+	my $host_name = $data->{host_name};
+	my $dscp      = $remap->{dscp};
+
+	$map_from =~ s/ccr/$host_name/;
+
+	if ( defined( $pdata->{'dscp_remap'} ) ) {
+		$text .= "map	" . $map_from . "     " . $map_to . " \@plugin=dscp_remap.so \@pparam=" . $dscp;
+	}
+	else {
+		$text .= "map	" . $map_from . "     " . $map_to . " \@plugin=header_rewrite.so \@pparam=dscp/set_dscp_" . $dscp . ".config";
+	}
+	if ( defined( $remap->{edge_header_rewrite} ) ) {
+		$text .= " \@plugin=header_rewrite.so \@pparam=" . $remap->{hdr_rw_file};
+	}
+	if ( $remap->{signed} == 1 ) {
+		$text .= " \@plugin=url_sig.so \@pparam=url_sig_" . $remap->{ds_xml_id} . ".config";
+	}
+	if ( $remap->{qstring_ignore} == 2 ) {
+		my $dqs_file = "drop_qstring.config";
+		$text .= " \@plugin=regex_remap.so \@pparam=" . $dqs_file;
+	}
+	elsif ( $remap->{qstring_ignore} == 1 ) {
+		my $global_exists = $self->profile_param_value( $server_obj->profile->id, 'cacheurl.config', 'location', undef );
+		if ($global_exists) {
+			$self->app->log->debug(
+				"qstring_ignore == 1, but global cacheurl.config param exists, so skipping remap rename config_file=cacheurl.config parameter if you want to change"
+			);
+		}
+		else {
+			$text .= " \@plugin=cacheurl.so \@pparam=cacheurl_qstring.config";
+		}
+	}
+	if ( defined( $remap->{cacheurl} ) && $remap->{cacheurl} ne "" ) {
+		$text .= " \@plugin=cacheurl.so \@pparam=" . $remap->{cacheurl_file};
+	}
+
+	# Note: should use full path here?
+	if ( defined( $remap->{regex_remap} ) && $remap->{regex_remap} ne "" ) {
+		$text .= " \@plugin=regex_remap.so \@pparam=regex_remap_" . $remap->{ds_xml_id} . ".config";
+	}
+	if ( $remap->{range_request_handling} == 1 ) {
+		$text .= " \@plugin=background_fetch.so \@pparam=bg_fetch.config";
+	}
+	elsif ( $remap->{range_request_handling} == 2 ) {
+		$text .= " \@plugin=cache_range_requests.so ";
+	}
+	if ( defined( $remap->{remap_text} ) ) {
+		$text .= " " . $remap->{remap_text};
+	}
+	$text .= "\n";
+	return $text;
+}
+
+sub __get_json_parameter_list_by_host {
+	my $self      = shift;
+	my $host      = shift;
+	my $value     = shift;
+	my $key_name  = shift || "name";
+	my $key_value = shift || "value";
+	my $data_obj  = [];
+
+	my $profile_id = $self->db->resultset('Server')->search( { host_name => $host } )->get_column('profile')->first();
+
+	my %condition = ( 'profile_parameters.profile' => $profile_id, config_file => $value );
+	my $rs_config = $self->db->resultset('Parameter')->search( \%condition, { join => 'profile_parameters' } );
+
+	while ( my $row = $rs_config->next ) {
+		push( @{$data_obj}, { $key_name => $row->name, $key_value => $row->value } );
+	}
+
+	return ($data_obj);
+}
+
+sub __get_json_parameter_by_host {
+	my $self      = shift;
+	my $host      = shift;
+	my $parameter = shift;
+	my $value     = shift;
+	my $key_name  = shift || "name";
+	my $key_value = shift || "value";
+	my $data_obj;
+
+	my $rs_profile = $self->db->resultset('Server')->search( { 'me.host_name' => $host }, { prefectch => [ 'cdn', 'profile' ] } );
+
+	my $row = $rs_profile->next;
+	my $id  = $row->id;
+	if ( defined($row) && defined( $row->cdn->name ) ) {
+		push( @{$data_obj}, { $key_name => "CDN_Name", $key_value => $row->cdn->name } );
+	}
+
+	my %condition = (
+		'profile_parameters.profile' => $id,
+		'config_file'                => $value,
+		name                         => $parameter
+	);
+	my $rs_config = $self->db->resultset('Parameter')->search( \%condition, { join => 'profile_parameters' } );
+	$row = $rs_config->next;
+
+	if ( defined($row) && defined( $row->name ) && defined( $row->value ) ) {
+		push( @{$data_obj}, { $key_name => $row->name, $key_value => $row->value } );
+	}
+
+	return ($data_obj);
+}
+
+sub get_package_versions {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $host_name = $server_obj->host_name;
+	my $data_obj = __get_json_parameter_list_by_host( $self, $host_name, "package", "name", "version" );
+
+	return ($data_obj);
+}
+
+sub get_chkconfig {
+	my $self       = shift;
+	my $server_obj = shift;
+	my $filename   = shift;
+	my $scope      = shift;
+
+	my $host_name = $server_obj->host_name;
+	my $data_obj = __get_json_parameter_list_by_host( $self, $host_name, "chkconfig" );
+
+	return ($data_obj);
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4362e31c/traffic_ops/app/lib/Schema/Result/DeliveryServiceInfoForCdnList.pm
----------------------------------------------------------------------
diff --git a/traffic_ops/app/lib/Schema/Result/DeliveryServiceInfoForCdnList.pm b/traffic_ops/app/lib/Schema/Result/DeliveryServiceInfoForCdnList.pm
new file mode 100644
index 0000000..274528b
--- /dev/null
+++ b/traffic_ops/app/lib/Schema/Result/DeliveryServiceInfoForCdnList.pm
@@ -0,0 +1,101 @@
+
+#
+#
+# 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 Schema::Result::DeliveryServiceInfoForCdnList;
+
+# this view returns the regexp set for a delivery services, ordered by type, set_number.
+# to use, do
+#
+# $rs = $self->db->resultset('DeliveryServiceInfoForCdnList')->search({}, { bind => [ $id ]});
+#
+# where $id is the CDN id.
+
+use strict;
+use warnings;
+use utf8;
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
+
+__PACKAGE__->table("DeliveryServiceInfoForCdnList:");
+
+__PACKAGE__->result_source_instance->is_virtual(1);
+
+__PACKAGE__->result_source_instance->view_definition( "
+SELECT DISTINCT
+    deliveryservice.xml_id AS xml_id,
+    deliveryservice.id AS ds_id,
+    deliveryservice.dscp AS dscp,
+    deliveryservice.signed AS signed,
+    deliveryservice.qstring_ignore AS qstring_ignore,
+    deliveryservice.org_server_fqdn as org_server_fqdn,
+    deliveryservice.multi_site_origin as multi_site_origin,
+    deliveryservice.multi_site_origin_algorithm as multi_site_origin_algorithm,
+    deliveryservice.range_request_handling as range_request_handling,
+    deliveryservice.origin_shield as origin_shield,
+    regex.pattern AS pattern,
+    retype.name AS re_type,
+    dstype.name AS ds_type,
+    parameter.value AS domain_name,
+    deliveryservice_regex.set_number AS set_number,
+    deliveryservice.edge_header_rewrite as edge_header_rewrite,
+    deliveryservice.mid_header_rewrite as mid_header_rewrite,
+    deliveryservice.regex_remap as regex_remap,
+    deliveryservice.cacheurl as cacheurl,
+    deliveryservice.remap_text as remap_text,
+    deliveryservice.protocol as protocol
+FROM
+    deliveryservice
+        JOIN deliveryservice_regex ON deliveryservice_regex.deliveryservice = deliveryservice.id
+        JOIN regex ON deliveryservice_regex.regex = regex.id
+        JOIN type as retype ON regex.type = retype.id
+        JOIN type as dstype ON deliveryservice.type = dstype.id
+        JOIN profile_parameter ON deliveryservice.profile = profile_parameter.profile
+        JOIN parameter ON parameter.id = profile_parameter.parameter
+        JOIN deliveryservice_server ON deliveryservice_server.deliveryservice = deliveryservice.id
+        JOIN server ON deliveryservice_server.server = server.id
+WHERE deliveryservice.cdn_id = ? AND parameter.name = 'domain_name'
+ORDER BY ds_id, re_type , deliveryservice_regex.set_number
+"
+);
+
+__PACKAGE__->add_columns(
+	"xml_id",          { data_type => "varchar", is_nullable => 0, size => 45 },
+	"org_server_fqdn", { data_type => "varchar", is_nullable => 0, size => 45 },
+	"multi_site_origin",           { data_type => "integer", is_nullable => 0 },
+	"multi_site_origin_algorithm", { data_type => "tinyint", is_nullable => 1 },
+	"ds_id",                       { data_type => "integer", is_nullable => 0 },
+	"dscp",                        { data_type => "integer", is_nullable => 0 },
+	"signed",                      { data_type => "integer", is_nullable => 0 },
+	"qstring_ignore",              { data_type => "integer", is_nullable => 0 },
+	"pattern",                     { data_type => "varchar", is_nullable => 0, size => 45 },
+	"re_type",                     { data_type => "varchar", is_nullable => 0, size => 45 },
+	"ds_type",                     { data_type => "varchar", is_nullable => 0, size => 45 },
+	"set_number",                  { data_type => "integer", is_nullable => 0 },
+	"domain_name",                 { data_type => "varchar", is_nullable => 0, size => 45 },
+	"edge_header_rewrite",         { data_type => "varchar", is_nullable => 0, size => 1024 },
+	"mid_header_rewrite",          { data_type => "varchar", is_nullable => 0, size => 1024 },
+	"regex_remap",                 { data_type => "varchar", is_nullable => 0, size => 1024 },
+	"cacheurl",                    { data_type => "varchar", is_nullable => 0, size => 1024 },
+	"remap_text",                  { data_type => "varchar", is_nullable => 0, size => 2048 },
+	"protocol",                    { data_type => "tinyint", is_nullable => 0, size => 4 },
+	"range_request_handling",      { data_type => "tinyint", is_nullable => 0, size => 4 },
+	"origin_shield",               { data_type => "varchar", is_nullable => 0, size => 1024 },
+);
+
+1;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4362e31c/traffic_ops/app/lib/TrafficOpsRoutes.pm
----------------------------------------------------------------------
diff --git a/traffic_ops/app/lib/TrafficOpsRoutes.pm b/traffic_ops/app/lib/TrafficOpsRoutes.pm
index e1fae2e..e32e2b3 100644
--- a/traffic_ops/app/lib/TrafficOpsRoutes.pm
+++ b/traffic_ops/app/lib/TrafficOpsRoutes.pm
@@ -471,6 +471,12 @@ sub api_routes {
 	$r->get("/api/$version/logs/:days/days")->over( authenticated => 1 )->to( 'ChangeLog#index', namespace => $namespace );
 	$r->get("/api/$version/logs/newcount")->over( authenticated => 1 )->to( 'ChangeLog#newlogcount', namespace => $namespace );
 
+	# -- CONFIG FILES
+	$r->get("/api/$version/server/:id/configfiles/ats")->over( authenticated => 1 )->to ( 'ApacheTrafficServer#ort', namespace => 'API::Configs' );
+	$r->get("/api/$version/profile/:id/configfiles/ats/#filename")->over( authenticated => 1 )->to ( 'ApacheTrafficServer#get_profile_config', namespace => 'API::Configs' );
+	$r->get("/api/$version/server/:id/configfiles/ats/#filename")->over( authenticated => 1 )->to ( 'ApacheTrafficServer#get_server_config', namespace => 'API::Configs' );
+	$r->get("/api/$version/cdn/:id/configfiles/ats/#filename")->over( authenticated => 1 )->to ( 'ApacheTrafficServer#get_cdn_config', namespace => 'API::Configs' );
+
 	# -- DELIVERYSERVICES
 	# -- DELIVERYSERVICES: CRUD
 	$r->get("/api/$version/deliveryservices")->over( authenticated => 1 )->to( 'Deliveryservice#index', namespace => $namespace );

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4362e31c/traffic_ops/app/lib/UI/ConfigFiles.pm
----------------------------------------------------------------------
diff --git a/traffic_ops/app/lib/UI/ConfigFiles.pm b/traffic_ops/app/lib/UI/ConfigFiles.pm
index a609963..c4fba9f 100644
--- a/traffic_ops/app/lib/UI/ConfigFiles.pm
+++ b/traffic_ops/app/lib/UI/ConfigFiles.pm
@@ -312,6 +312,9 @@ sub param_data {
 		if ( $row->parameter->name eq "location" ) {
 			next;
 		}
+		if ( $row->parameter->name eq "scope" ) {
+			next;
+		}
 		my $value = $row->parameter->value;
 
 		# some files have multiple lines with the same key... handle that with param id.