+
+#
+# Allocate each origin to a cluster
+#
+sub allocate_clusters
+{
+ my @mappings = sort { compare_mappings($a, $b) } @_;
+
+ # Loop over the mappings, trying to assign each origin to the
+ # nearest cluster, but subject to the bandwidth limits
+ while (my $mapping = shift @mappings)
+ {
+ my @group;
+
+ push @group, $mapping;
+
+ while (@mappings && compare_mappings($mapping, $mappings[0]) == 0)
+ {
+ push @group, shift @mappings;
+ }
+
+ for my $mapping (sort compare_bandwidth @group)
+ {
+ my $origin = $mapping->{origin};
+ my $cluster = $mapping->{cluster};
+
+ if (!exists($origin->{cluster}) &&
+ $cluster->{bandwidth_used} + $origin->{bandwidth} <= $cluster->{bandwidth_limit})
+ {
+ $origin->{cluster} = $cluster;
+ $cluster->{bandwidth_used} = $cluster->{bandwidth_used} + $origin->{bandwidth};
+ }
+ }
+ }
+
+ return;
+}
+
+#
+# Compare two mappings to decide which to use
+#
+sub compare_mappings
+{
+ my $a = shift;
+ my $b = shift;
+
+ return $b->{priority} <=> $a->{priority} ||
+ $a->{distance} <=> $b->{distance};
+}
+
+#
+# Compare two mappings to decide which to try first
+#
+sub compare_bandwidth
+{
+ my $a_used = ( $a->{cluster}->{bandwidth_used} * 100.0 ) / ( $a->{cluster}->{bandwidth_limit} * 1.0 );
+ my $b_used = ( $b->{cluster}->{bandwidth_used} * 100.0 ) / ( $b->{cluster}->{bandwidth_limit} * 1.0 );
+
+ return $a_used <=> $b_used;
+}
+
+#
+# Output DNS records for a server
+#
+sub output_server
+{
+ my $zonefile = shift;
+ my $name = shift;
+ my $cluster = shift;
+
+ while (my($index,$server) = each @{$cluster->{servers}})
+ {
+ if ($server->{status} eq "up")
+ {
+ $zonefile->printf("+${name}:$server->{ipv4}:600\n", $index + 1);
+
+ if ($server->{ipv6})
+ {
+ $zonefile->printf("3${name}:$server->{ipv6}:600\n", $index + 1);
+ }
+ }
+ }
+
+ return;
+}