You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ne...@apache.org on 2017/01/30 15:29:19 UTC

[13/19] incubator-trafficcontrol git commit: Move TM2 to trafficcontrol/traffic_monitor_golang

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/manager/peer.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/manager/peer.go b/traffic_monitor/experimental/traffic_monitor/manager/peer.go
deleted file mode 100644
index 3b4a209..0000000
--- a/traffic_monitor/experimental/traffic_monitor/manager/peer.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package manager
-
-/*
- * 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.
- */
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-	"time"
-
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/log"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/util"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/config"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/health"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/peer"
-	todata "github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/trafficopsdata"
-)
-
-// StartPeerManager listens for peer results, and when it gets one, it adds it to the peerStates list, and optimistically combines the good results into combinedStates
-func StartPeerManager(
-	peerChan <-chan peer.Result,
-	localStates peer.CRStatesThreadsafe,
-	peerStates peer.CRStatesPeersThreadsafe,
-	events health.ThreadsafeEvents,
-	peerOptimistic bool,
-	toData todata.TODataThreadsafe,
-	cfg config.Config,
-) (peer.CRStatesThreadsafe, health.ThreadsafeEvents) {
-	combinedStates := peer.NewCRStatesThreadsafe()
-	overrideMap := map[enum.CacheName]bool{}
-
-	go func() {
-		for peerResult := range peerChan {
-			comparePeerState(events, peerResult, peerStates)
-			peerStates.Set(peerResult)
-			combineCrStates(events, peerOptimistic, peerStates, localStates.Get(), combinedStates, overrideMap, toData)
-			peerResult.PollFinished <- peerResult.PollID
-		}
-	}()
-	return combinedStates, events
-}
-
-func comparePeerState(events health.ThreadsafeEvents, result peer.Result, peerStates peer.CRStatesPeersThreadsafe) {
-	if result.Available != peerStates.GetPeerAvailability(result.ID) {
-		events.Add(health.Event{Time: result.Time, Unix: result.Time.Unix(), Description: util.JoinErrorsString(result.Errors), Name: result.ID.String(), Hostname: result.ID.String(), Type: "Peer", Available: result.Available})
-	}
-}
-
-// TODO JvD: add deliveryservice stuff
-func combineCrStates(events health.ThreadsafeEvents, peerOptimistic bool, peerStates peer.CRStatesPeersThreadsafe, localStates peer.Crstates, combinedStates peer.CRStatesThreadsafe, overrideMap map[enum.CacheName]bool, toData todata.TODataThreadsafe) {
-	toDataCopy := toData.Get()
-
-	for cacheName, localCacheState := range localStates.Caches { // localStates gets pruned when servers are disabled, it's the source of truth
-		var overrideCondition string
-		available := false
-		override := overrideMap[cacheName]
-
-		if localCacheState.IsAvailable {
-			available = true // we don't care about the peers, we got a "good one", and we're optimistic
-
-			if override {
-				overrideCondition = "cleared; healthy locally"
-				overrideMap[cacheName] = false
-			}
-		} else if peerOptimistic {
-			if !peerStates.HasAvailablePeers() {
-				if override {
-					overrideCondition = "irrelevant; no peers online"
-					overrideMap[cacheName] = false
-				}
-			} else {
-				onlineOnPeers := make([]string, 0)
-
-				for peer, peerCrStates := range peerStates.GetCrstates() {
-					if peerStates.GetPeerAvailability(peer) {
-						if peerCrStates.Caches[cacheName].IsAvailable {
-							onlineOnPeers = append(onlineOnPeers, peer.String())
-						}
-					}
-				}
-
-				if len(onlineOnPeers) > 0 {
-					available = true
-
-					if !override {
-						overrideCondition = fmt.Sprintf("detected; healthy on (at least) %s", strings.Join(onlineOnPeers, ", "))
-						overrideMap[cacheName] = true
-					}
-				} else {
-					if override {
-						overrideCondition = "irrelevant; not online on any peers"
-						overrideMap[cacheName] = false
-					}
-				}
-			}
-		}
-
-		if overrideCondition != "" {
-			events.Add(health.Event{Time: time.Now(), Unix: time.Now().Unix(), Description: fmt.Sprintf("Health protocol override condition %s", overrideCondition), Name: cacheName.String(), Hostname: cacheName.String(), Type: toDataCopy.ServerTypes[cacheName].String(), Available: available})
-		}
-
-		combinedStates.SetCache(cacheName, peer.IsAvailable{IsAvailable: available})
-	}
-
-	for deliveryServiceName, localDeliveryService := range localStates.Deliveryservice {
-		deliveryService := peer.Deliveryservice{IsAvailable: false, DisabledLocations: []enum.CacheName{}} // important to initialize DisabledLocations, so JSON is `[]` not `null`
-		if localDeliveryService.IsAvailable {
-			deliveryService.IsAvailable = true
-		}
-		deliveryService.DisabledLocations = localDeliveryService.DisabledLocations
-
-		for peerName, iPeerStates := range peerStates.GetCrstates() {
-			peerDeliveryService, ok := iPeerStates.Deliveryservice[deliveryServiceName]
-			if !ok {
-				log.Warnf("local delivery service %s not found in peer %s\n", deliveryServiceName, peerName)
-				continue
-			}
-			if peerDeliveryService.IsAvailable {
-				deliveryService.IsAvailable = true
-			}
-			deliveryService.DisabledLocations = intersection(deliveryService.DisabledLocations, peerDeliveryService.DisabledLocations)
-		}
-		combinedStates.SetDeliveryService(deliveryServiceName, deliveryService)
-	}
-}
-
-// CacheNameSlice is a slice of cache names, which fulfills the `sort.Interface` interface.
-type CacheNameSlice []enum.CacheName
-
-func (p CacheNameSlice) Len() int           { return len(p) }
-func (p CacheNameSlice) Less(i, j int) bool { return p[i] < p[j] }
-func (p CacheNameSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-
-// intersection returns strings in both a and b.
-// Note this modifies a and b. Specifically, it sorts them. If that isn't acceptable, pass copies of your real data.
-func intersection(a []enum.CacheName, b []enum.CacheName) []enum.CacheName {
-	sort.Sort(CacheNameSlice(a))
-	sort.Sort(CacheNameSlice(b))
-	c := []enum.CacheName{} // important to initialize, so JSON is `[]` not `null`
-	for _, s := range a {
-		i := sort.Search(len(b), func(i int) bool { return b[i] >= s })
-		if i < len(b) && b[i] == s {
-			c = append(c, s)
-		}
-	}
-	return c
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/manager/stat.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/manager/stat.go b/traffic_monitor/experimental/traffic_monitor/manager/stat.go
deleted file mode 100644
index 50ce052..0000000
--- a/traffic_monitor/experimental/traffic_monitor/manager/stat.go
+++ /dev/null
@@ -1,219 +0,0 @@
-package manager
-
-/*
- * 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.
- */
-
-import (
-	"time"
-
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/log"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/cache"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/config"
-	ds "github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/deliveryservice"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/health"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/peer"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/threadsafe"
-	todata "github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/trafficopsdata"
-	to "github.com/apache/incubator-trafficcontrol/traffic_ops/client"
-)
-
-func pruneHistory(history []cache.Result, limit uint64) []cache.Result {
-	if uint64(len(history)) > limit {
-		history = history[:limit-1]
-	}
-	return history
-}
-
-func getNewCaches(localStates peer.CRStatesThreadsafe, monitorConfigTS TrafficMonitorConfigMapThreadsafe) map[enum.CacheName]struct{} {
-	monitorConfig := monitorConfigTS.Get()
-	caches := map[enum.CacheName]struct{}{}
-	for cacheName := range localStates.GetCaches() {
-		// ONLINE and OFFLINE caches are not polled.
-		// TODO add a function IsPolled() which can be called by this and the monitorConfig func which sets the polling, to prevent updating in one place breaking the other.
-		if ts, ok := monitorConfig.TrafficServer[string(cacheName)]; !ok || ts.Status == "ONLINE" || ts.Status == "OFFLINE" {
-			continue
-		}
-		caches[cacheName] = struct{}{}
-	}
-	return caches
-}
-
-// StartStatHistoryManager fetches the full statistics data from ATS Astats. This includes everything needed for all calculations, such as Delivery Services. This is expensive, though, and may be hard on ATS, so it should poll less often.
-// For a fast 'is it alive' poll, use the Health Result Manager poll.
-// Returns the stat history, the duration between the stat poll for each cache, the last Kbps data, the calculated Delivery Service stats, and the unpolled caches list.
-func StartStatHistoryManager(
-	cacheStatChan <-chan cache.Result,
-	localStates peer.CRStatesThreadsafe,
-	combinedStates peer.CRStatesThreadsafe,
-	toData todata.TODataThreadsafe,
-	cachesChanged <-chan struct{},
-	errorCount threadsafe.Uint,
-	cfg config.Config,
-	monitorConfig TrafficMonitorConfigMapThreadsafe,
-	events health.ThreadsafeEvents,
-) (threadsafe.ResultInfoHistory, threadsafe.ResultStatHistory, threadsafe.CacheKbpses, DurationMapThreadsafe, threadsafe.LastStats, threadsafe.DSStatsReader, threadsafe.UnpolledCaches, threadsafe.CacheAvailableStatus) {
-	statInfoHistory := threadsafe.NewResultInfoHistory()
-	statResultHistory := threadsafe.NewResultStatHistory()
-	statMaxKbpses := threadsafe.NewCacheKbpses()
-	lastStatDurations := NewDurationMapThreadsafe()
-	lastStatEndTimes := map[enum.CacheName]time.Time{}
-	lastStats := threadsafe.NewLastStats()
-	dsStats := threadsafe.NewDSStats()
-	unpolledCaches := threadsafe.NewUnpolledCaches()
-	tickInterval := cfg.StatFlushInterval
-	localCacheStatus := threadsafe.NewCacheAvailableStatus()
-
-	precomputedData := map[enum.CacheName]cache.PrecomputedData{}
-	lastResults := map[enum.CacheName]cache.Result{}
-
-	process := func(results []cache.Result) {
-		processStatResults(results, statInfoHistory, statResultHistory, statMaxKbpses, combinedStates.Get(), lastStats, toData.Get(), errorCount, dsStats, lastStatEndTimes, lastStatDurations, unpolledCaches, monitorConfig.Get(), precomputedData, lastResults, localStates, events, localCacheStatus)
-	}
-
-	go func() {
-		var ticker *time.Ticker
-		<-cachesChanged // wait for the signal that localStates have been set
-		unpolledCaches.SetNewCaches(getNewCaches(localStates, monitorConfig))
-
-		for {
-			var results []cache.Result
-			results = append(results, <-cacheStatChan)
-			if ticker != nil {
-				ticker.Stop()
-			}
-			ticker = time.NewTicker(tickInterval)
-		innerLoop:
-			for {
-				select {
-				case <-cachesChanged:
-					unpolledCaches.SetNewCaches(getNewCaches(localStates, monitorConfig))
-				case <-ticker.C:
-					log.Infof("StatHistoryManager flushing queued results\n")
-					process(results)
-					break innerLoop
-				default:
-					select {
-					case r := <-cacheStatChan:
-						results = append(results, r)
-					default:
-						process(results)
-						break innerLoop
-					}
-				}
-			}
-		}
-	}()
-	return statInfoHistory, statResultHistory, statMaxKbpses, lastStatDurations, lastStats, &dsStats, unpolledCaches, localCacheStatus
-}
-
-// processStatResults processes the given results, creating and setting DSStats, LastStats, and other stats. Note this is NOT threadsafe, and MUST NOT be called from multiple threads.
-func processStatResults(
-	results []cache.Result,
-	statInfoHistoryThreadsafe threadsafe.ResultInfoHistory,
-	statResultHistoryThreadsafe threadsafe.ResultStatHistory,
-	statMaxKbpsesThreadsafe threadsafe.CacheKbpses,
-	combinedStates peer.Crstates,
-	lastStats threadsafe.LastStats,
-	toData todata.TOData,
-	errorCount threadsafe.Uint,
-	dsStats threadsafe.DSStats,
-	lastStatEndTimes map[enum.CacheName]time.Time,
-	lastStatDurationsThreadsafe DurationMapThreadsafe,
-	unpolledCaches threadsafe.UnpolledCaches,
-	mc to.TrafficMonitorConfigMap,
-	precomputedData map[enum.CacheName]cache.PrecomputedData,
-	lastResults map[enum.CacheName]cache.Result,
-	localStates peer.CRStatesThreadsafe,
-	events health.ThreadsafeEvents,
-	localCacheStatusThreadsafe threadsafe.CacheAvailableStatus,
-) {
-	if len(results) == 0 {
-		return
-	}
-	defer func() {
-		for _, r := range results {
-			// log.Debugf("poll %v %v statfinish\n", result.PollID, endTime)
-			r.PollFinished <- r.PollID
-		}
-	}()
-
-	// setting the statHistory could be put in a goroutine concurrent with `ds.CreateStats`, if it were slow
-	statInfoHistory := statInfoHistoryThreadsafe.Get().Copy()
-	statResultHistory := statResultHistoryThreadsafe.Get().Copy()
-	statMaxKbpses := statMaxKbpsesThreadsafe.Get().Copy()
-
-	for i, result := range results {
-		maxStats := uint64(mc.Profile[mc.TrafficServer[string(result.ID)].Profile].Parameters.HistoryCount)
-		if maxStats < 1 {
-			log.Infof("processStatResults got history count %v for %v, setting to 1\n", maxStats, result.ID)
-			maxStats = 1
-		}
-
-		// TODO determine if we want to add results with errors, or just print the errors now and don't add them.
-		if lastResult, ok := lastResults[result.ID]; ok && result.Error == nil {
-			health.GetVitals(&result, &lastResult, &mc) // TODO precompute
-			if result.Error == nil {
-				results[i] = result
-			} else {
-				log.Errorf("stat poll getting vitals for %v: %v\n", result.ID, result.Error)
-			}
-		}
-		statInfoHistory.Add(result, maxStats)
-		statResultHistory.Add(result, maxStats)
-		// Don't add errored maxes or precomputed DSStats
-		if result.Error == nil {
-			// max and precomputed always contain the latest result from each cache
-			statMaxKbpses.AddMax(result)
-			// if we failed to compute the OutBytes, keep the outbytes of the last result.
-			if result.PrecomputedData.OutBytes == 0 {
-				result.PrecomputedData.OutBytes = precomputedData[result.ID].OutBytes
-			}
-			precomputedData[result.ID] = result.PrecomputedData
-
-		}
-		lastResults[result.ID] = result
-	}
-	statInfoHistoryThreadsafe.Set(statInfoHistory)
-	statResultHistoryThreadsafe.Set(statResultHistory)
-	statMaxKbpsesThreadsafe.Set(statMaxKbpses)
-
-	newDsStats, newLastStats, err := ds.CreateStats(precomputedData, toData, combinedStates, lastStats.Get().Copy(), time.Now(), mc, events)
-	if err != nil {
-		errorCount.Inc()
-		log.Errorf("getting deliveryservice: %v\n", err)
-	} else {
-		dsStats.Set(newDsStats)
-		lastStats.Set(newLastStats)
-	}
-
-	health.CalcAvailability(results, "stat", statResultHistory, mc, toData, localCacheStatusThreadsafe, localStates, events)
-
-	endTime := time.Now()
-	lastStatDurations := lastStatDurationsThreadsafe.Get().Copy()
-	for _, result := range results {
-		if lastStatStart, ok := lastStatEndTimes[result.ID]; ok {
-			d := time.Since(lastStatStart)
-			lastStatDurations[result.ID] = d
-		}
-		lastStatEndTimes[result.ID] = endTime
-	}
-	lastStatDurationsThreadsafe.Set(lastStatDurations)
-	unpolledCaches.SetPolled(results, lastStats.Get())
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/peer/crstates.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/peer/crstates.go b/traffic_monitor/experimental/traffic_monitor/peer/crstates.go
deleted file mode 100644
index cac90e1..0000000
--- a/traffic_monitor/experimental/traffic_monitor/peer/crstates.go
+++ /dev/null
@@ -1,233 +0,0 @@
-package peer
-
-/*
- * 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.
- */
-
-import (
-	"encoding/json"
-	"sync"
-
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-)
-
-// Crstates includes availability data for caches and delivery services, as gathered and aggregated by this Traffic Monitor. It is designed to be served at an API endpoint primarily for Traffic Routers (Content Router) to consume.
-// TODO rename to `CRStates`
-type Crstates struct {
-	Caches          map[enum.CacheName]IsAvailable               `json:"caches"`
-	Deliveryservice map[enum.DeliveryServiceName]Deliveryservice `json:"deliveryServices"`
-}
-
-// NewCrstates creates a new CR states object, initializing pointer members.
-func NewCrstates() Crstates {
-	return Crstates{
-		Caches:          map[enum.CacheName]IsAvailable{},
-		Deliveryservice: map[enum.DeliveryServiceName]Deliveryservice{},
-	}
-}
-
-// Copy creates a deep copy of this object. It does not mutate, and is thus safe for multiple goroutines.
-func (a Crstates) Copy() Crstates {
-	b := NewCrstates()
-	for k, v := range a.Caches {
-		b.Caches[k] = v
-	}
-	for k, v := range a.Deliveryservice {
-		b.Deliveryservice[k] = v
-	}
-	return b
-}
-
-// CopyDeliveryservices creates a deep copy of the delivery service availability data.. It does not mutate, and is thus safe for multiple goroutines.
-func (a Crstates) CopyDeliveryservices() map[enum.DeliveryServiceName]Deliveryservice {
-	b := map[enum.DeliveryServiceName]Deliveryservice{}
-	for k, v := range a.Deliveryservice {
-		b[k] = v
-	}
-	return b
-}
-
-// CopyCaches creates a deep copy of the cache availability data.. It does not mutate, and is thus safe for multiple goroutines.
-func (a Crstates) CopyCaches() map[enum.CacheName]IsAvailable {
-	b := map[enum.CacheName]IsAvailable{}
-	for k, v := range a.Caches {
-		b[k] = v
-	}
-	return b
-}
-
-// IsAvailable contains whether the given cache or delivery service is available. It is designed for JSON serialization, namely in the Traffic Monitor 1.0 API.
-type IsAvailable struct {
-	IsAvailable bool `json:"isAvailable"`
-}
-
-// Deliveryservice contains data about the availability of a particular delivery service, and which caches in that delivery service have been marked as unavailable.
-type Deliveryservice struct {
-	DisabledLocations []enum.CacheName `json:"disabledLocations"`
-	IsAvailable       bool             `json:"isAvailable"`
-}
-
-// CrstatesUnMarshall takes bytes of a JSON string, and unmarshals them into a Crstates object.
-func CrstatesUnMarshall(body []byte) (Crstates, error) {
-	var crStates Crstates
-	err := json.Unmarshal(body, &crStates)
-	return crStates, err
-}
-
-// CrstatesMarshall serializes the given Crstates into bytes.
-func CrstatesMarshall(states Crstates) ([]byte, error) {
-	return json.Marshal(states)
-}
-
-// CRStatesThreadsafe provides safe access for multiple goroutines to read a single Crstates object, with a single goroutine writer.
-// This could be made lock-free, if the performance was necessary
-// TODO add separate locks for Caches and Deliveryservice maps?
-type CRStatesThreadsafe struct {
-	crStates *Crstates
-	m        *sync.RWMutex
-}
-
-// NewCRStatesThreadsafe creates a new CRStatesThreadsafe object safe for multiple goroutine readers and a single writer.
-func NewCRStatesThreadsafe() CRStatesThreadsafe {
-	crs := NewCrstates()
-	return CRStatesThreadsafe{m: &sync.RWMutex{}, crStates: &crs}
-}
-
-// Get returns the internal Crstates object for reading.
-func (t *CRStatesThreadsafe) Get() Crstates {
-	t.m.RLock()
-	defer t.m.RUnlock()
-	return t.crStates.Copy()
-}
-
-// GetDeliveryServices returns the internal Crstates delivery services map for reading.
-// TODO add GetCaches, GetDeliveryservices?
-func (t *CRStatesThreadsafe) GetDeliveryServices() map[enum.DeliveryServiceName]Deliveryservice {
-	t.m.RLock()
-	defer t.m.RUnlock()
-	return t.crStates.CopyDeliveryservices()
-}
-
-// GetCache returns the availability data of the given cache. This does not mutate, and is thus safe for multiple goroutines to call.
-func (t *CRStatesThreadsafe) GetCache(name enum.CacheName) (available IsAvailable, ok bool) {
-	t.m.RLock()
-	available, ok = t.crStates.Caches[name]
-	t.m.RUnlock()
-	return
-}
-
-// GetCaches returns the availability data of all caches. This does not mutate, and is thus safe for multiple goroutines to call.
-func (t *CRStatesThreadsafe) GetCaches() map[enum.CacheName]IsAvailable {
-	t.m.RLock()
-	defer t.m.RUnlock()
-	return t.crStates.CopyCaches()
-}
-
-// GetDeliveryService returns the availability data of the given delivery service. This does not mutate, and is thus safe for multiple goroutines to call.
-func (t *CRStatesThreadsafe) GetDeliveryService(name enum.DeliveryServiceName) (ds Deliveryservice, ok bool) {
-	t.m.RLock()
-	ds, ok = t.crStates.Deliveryservice[name]
-	t.m.RUnlock()
-	return
-}
-
-// SetCache sets the internal availability data for a particular cache.
-func (t *CRStatesThreadsafe) SetCache(cacheName enum.CacheName, available IsAvailable) {
-	t.m.Lock()
-	t.crStates.Caches[cacheName] = available
-	t.m.Unlock()
-}
-
-// DeleteCache deletes the given cache from the internal data.
-func (t *CRStatesThreadsafe) DeleteCache(name enum.CacheName) {
-	t.m.Lock()
-	delete(t.crStates.Caches, name)
-	t.m.Unlock()
-}
-
-// SetDeliveryService sets the availability data for the given delivery service.
-func (t *CRStatesThreadsafe) SetDeliveryService(name enum.DeliveryServiceName, ds Deliveryservice) {
-	t.m.Lock()
-	t.crStates.Deliveryservice[name] = ds
-	t.m.Unlock()
-}
-
-// DeleteDeliveryService deletes the given delivery service from the internal data. This MUST NOT be called by multiple goroutines.
-func (t *CRStatesThreadsafe) DeleteDeliveryService(name enum.DeliveryServiceName) {
-	t.m.Lock()
-	delete(t.crStates.Deliveryservice, name)
-	t.m.Unlock()
-}
-
-// CRStatesPeersThreadsafe provides safe access for multiple goroutines to read a map of Traffic Monitor peers to their returned Crstates, with a single goroutine writer.
-// This could be made lock-free, if the performance was necessary
-type CRStatesPeersThreadsafe struct {
-	crStates   map[enum.TrafficMonitorName]Crstates
-	peerStates map[enum.TrafficMonitorName]bool
-	m          *sync.RWMutex
-}
-
-// NewCRStatesPeersThreadsafe creates a new CRStatesPeers object safe for multiple goroutine readers and a single writer.
-func NewCRStatesPeersThreadsafe() CRStatesPeersThreadsafe {
-	return CRStatesPeersThreadsafe{m: &sync.RWMutex{}, crStates: map[enum.TrafficMonitorName]Crstates{}, peerStates: map[enum.TrafficMonitorName]bool{}}
-}
-
-// GetCrstates returns the internal Traffic Monitor peer Crstates data. This MUST NOT be modified.
-func (t *CRStatesPeersThreadsafe) GetCrstates() map[enum.TrafficMonitorName]Crstates {
-	t.m.RLock()
-	m := map[enum.TrafficMonitorName]Crstates{}
-	for k, v := range t.crStates {
-		m[k] = v.Copy()
-	}
-	t.m.RUnlock()
-	return m
-}
-
-// GetPeerAvailability returns the state of the given peer
-func (t *CRStatesPeersThreadsafe) GetPeerAvailability(peer enum.TrafficMonitorName) bool {
-	t.m.RLock()
-	availability := t.peerStates[peer]
-	t.m.RUnlock()
-	return availability
-}
-
-// HasAvailablePeers returns true if at least one peer is online
-func (t *CRStatesPeersThreadsafe) HasAvailablePeers() bool {
-	availablePeers := false
-
-	t.m.RLock()
-
-	for _, available := range t.peerStates {
-		if available {
-			availablePeers = true
-			break
-		}
-	}
-
-	t.m.RUnlock()
-
-	return availablePeers
-}
-
-// Set sets the internal Traffic Monitor peer state and Crstates data. This MUST NOT be called by multiple goroutines.
-func (t *CRStatesPeersThreadsafe) Set(result Result) {
-	t.m.Lock()
-	t.crStates[result.ID] = result.PeerStates
-	t.peerStates[result.ID] = result.Available
-	t.m.Unlock()
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/peer/crstates.json
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/peer/crstates.json b/traffic_monitor/experimental/traffic_monitor/peer/crstates.json
deleted file mode 100644
index d8ffe03..0000000
--- a/traffic_monitor/experimental/traffic_monitor/peer/crstates.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "caches": {
-    "ats-edge-cache-0": {"isAvailable": true},
-    "ats-edge-cache-1": {"isAvailable": false}
-  },
-  "deliveryServices": {
-    "delivery-service-0": {
-      "disabledLocations": [],
-      "isAvailable": true
-    },
-    "delivery-service-1": {
-      "disabledLocations": [],
-      "isAvailable": true
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/peer/peer.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/peer/peer.go b/traffic_monitor/experimental/traffic_monitor/peer/peer.go
deleted file mode 100644
index 66755f8..0000000
--- a/traffic_monitor/experimental/traffic_monitor/peer/peer.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package peer
-
-/*
- * 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.
- */
-
-import (
-	"encoding/json"
-	"io"
-	"time"
-
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-)
-
-// Handler handles peer Traffic Monitor data, taking a raw reader, parsing the data, and passing a result object to the ResultChannel. This fulfills the common `Handler` interface.
-type Handler struct {
-	ResultChannel chan Result
-	Notify        int
-}
-
-// NewHandler returns a new peer Handler.
-func NewHandler() Handler {
-	return Handler{ResultChannel: make(chan Result)}
-}
-
-// Result contains the data parsed from polling a peer Traffic Monitor.
-type Result struct {
-	ID           enum.TrafficMonitorName
-	Available    bool
-	Errors       []error
-	PeerStates   Crstates
-	PollID       uint64
-	PollFinished chan<- uint64
-	Time         time.Time
-}
-
-// Handle handles a response from a polled Traffic Monitor peer, parsing the data and forwarding it to the ResultChannel.
-func (handler Handler) Handle(id string, r io.Reader, reqTime time.Duration, err error, pollID uint64, pollFinished chan<- uint64) {
-	result := Result{
-		ID:           enum.TrafficMonitorName(id),
-		Available:    false,
-		Errors:       []error{},
-		PollID:       pollID,
-		PollFinished: pollFinished,
-		Time:         time.Now(),
-	}
-
-	if err != nil {
-		result.Errors = append(result.Errors, err)
-	}
-
-	if r != nil {
-		dec := json.NewDecoder(r)
-		err = dec.Decode(&result.PeerStates)
-
-		if err == nil {
-			result.Available = true
-		} else {
-			result.Errors = append(result.Errors, err)
-		}
-	}
-
-	handler.ResultChannel <- result
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/peer/peer_test.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/peer/peer_test.go b/traffic_monitor/experimental/traffic_monitor/peer/peer_test.go
deleted file mode 100644
index 627a825..0000000
--- a/traffic_monitor/experimental/traffic_monitor/peer/peer_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package peer
-
-/*
- * 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.
- */
-
-
-import (
-	"fmt"
-	"io/ioutil"
-	"testing"
-)
-
-func TestCrStates(t *testing.T) {
-	t.Log("Running Peer Tests")
-
-	text, err := ioutil.ReadFile("crstates.json")
-	if err != nil {
-		t.Log(err)
-	}
-	crStates, err := CrstatesUnMarshall(text)
-	if err != nil {
-		t.Log(err)
-	}
-	fmt.Println(len(crStates.Caches), "caches found")
-	for cacheName, crState := range crStates.Caches {
-		t.Logf("%v -> %v", cacheName, crState.IsAvailable)
-	}
-
-	fmt.Println(len(crStates.Deliveryservice), "deliveryservices found")
-	for dsName, deliveryService := range crStates.Deliveryservice {
-		t.Logf("%v -> %v (len:%v)", dsName, deliveryService.IsAvailable, len(deliveryService.DisabledLocations))
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/srvhttp/srvhttp.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/srvhttp/srvhttp.go b/traffic_monitor/experimental/traffic_monitor/srvhttp/srvhttp.go
deleted file mode 100644
index c7c1362..0000000
--- a/traffic_monitor/experimental/traffic_monitor/srvhttp/srvhttp.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package srvhttp
-
-/*
- * 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.
- */
-
-import (
-	"fmt"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"net/url"
-	"sync"
-	"time"
-
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/log"
-	"github.com/hydrogen18/stoppableListener"
-)
-
-// GetCommonAPIData calculates and returns API data common to most endpoints
-func GetCommonAPIData(params url.Values, t time.Time) CommonAPIData {
-	return CommonAPIData{
-		QueryParams: ParametersStr(params),
-		DateStr:     DateStr(t),
-	}
-}
-
-// CommonAPIData contains generic data common to most endpoints.
-type CommonAPIData struct {
-	QueryParams string `json:"pp"`
-	DateStr     string `json:"date"`
-}
-
-// Server is a re-runnable HTTP server. Server.Run() may be called repeatedly, and
-// each time the previous running server will be stopped, and the server will be
-// restarted with the new port address and data request channel.
-type Server struct {
-	stoppableListener          *stoppableListener.StoppableListener
-	stoppableListenerWaitGroup sync.WaitGroup
-}
-
-func (s *Server) registerEndpoints(sm *http.ServeMux, endpoints map[string]http.HandlerFunc, staticFileDir string) error {
-	handleRoot, err := s.handleRootFunc(staticFileDir)
-	if err != nil {
-		return fmt.Errorf("Error getting root endpoint: %v", err)
-	}
-	handleSortableJs, err := s.handleSortableFunc(staticFileDir)
-	if err != nil {
-		return fmt.Errorf("Error getting sortable endpoint: %v", err)
-	}
-
-	for path, f := range endpoints {
-		sm.HandleFunc(path, f)
-	}
-
-	sm.HandleFunc("/", handleRoot)
-	sm.HandleFunc("/sorttable.js", handleSortableJs)
-
-	return nil
-}
-
-// Run runs a new HTTP service at the given addr, making data requests to the given c.
-// Run may be called repeatedly, and each time, will shut down any existing service first.
-// Run is NOT threadsafe, and MUST NOT be called concurrently by multiple goroutines.
-func (s *Server) Run(endpoints map[string]http.HandlerFunc, addr string, readTimeout time.Duration, writeTimeout time.Duration, staticFileDir string) error {
-	if s.stoppableListener != nil {
-		log.Infof("Stopping Web Server\n")
-		s.stoppableListener.Stop()
-		s.stoppableListenerWaitGroup.Wait()
-	}
-	log.Infof("Starting Web Server\n")
-
-	var err error
-	var originalListener net.Listener
-	if originalListener, err = net.Listen("tcp", addr); err != nil {
-		return err
-	}
-	if s.stoppableListener, err = stoppableListener.New(originalListener); err != nil {
-		return err
-	}
-
-	sm := http.NewServeMux()
-	err = s.registerEndpoints(sm, endpoints, staticFileDir)
-	if err != nil {
-		return err
-	}
-	server := &http.Server{
-		Addr:           addr,
-		Handler:        sm,
-		ReadTimeout:    readTimeout,
-		WriteTimeout:   writeTimeout,
-		MaxHeaderBytes: 1 << 20,
-	}
-
-	s.stoppableListenerWaitGroup = sync.WaitGroup{}
-	s.stoppableListenerWaitGroup.Add(1)
-	go func() {
-		defer s.stoppableListenerWaitGroup.Done()
-		err := server.Serve(s.stoppableListener)
-		if err != nil {
-			log.Warnf("HTTP server stopped with error: %v\n", err)
-		}
-	}()
-
-	log.Infof("Web server listening on %s", addr)
-	return nil
-}
-
-// ParametersStr takes the URL query parameters, and returns a string as used by the Traffic Monitor 1.0 endpoints "pp" key.
-func ParametersStr(params url.Values) string {
-	pp := ""
-	for param, vals := range params {
-		for _, val := range vals {
-			pp += param + "=[" + val + "], "
-		}
-	}
-	if len(pp) > 2 {
-		pp = pp[:len(pp)-2]
-	}
-	return pp
-}
-
-//CommonAPIDataDataFormat is a common Date format for the API
-const CommonAPIDataDateFormat = "Mon Jan 02 15:04:05 UTC 2006"
-
-// DateStr returns the given time in the format expected by Traffic Monitor 1.0 API users
-func DateStr(t time.Time) string {
-	return t.UTC().Format(CommonAPIDataDateFormat)
-}
-
-func (s *Server) handleRootFunc(staticFileDir string) (http.HandlerFunc, error) {
-	return s.handleFile(staticFileDir + "/index.html")
-}
-
-func (s *Server) handleSortableFunc(staticFileDir string) (http.HandlerFunc, error) {
-	return s.handleFile(staticFileDir + "/sorttable.js")
-}
-
-func (s *Server) handleFile(name string) (http.HandlerFunc, error) {
-	bytes, err := ioutil.ReadFile(name)
-	if err != nil {
-		return nil, err
-	}
-	contentType := http.DetectContentType(bytes)
-	return func(w http.ResponseWriter, req *http.Request) {
-		w.Header().Set("Content-Type", contentType)
-		fmt.Fprintf(w, "%s", bytes)
-	}, nil
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/static/index.html
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/static/index.html b/traffic_monitor/experimental/traffic_monitor/static/index.html
deleted file mode 100644
index 66cd03b..0000000
--- a/traffic_monitor/experimental/traffic_monitor/static/index.html
+++ /dev/null
@@ -1,560 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-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.
--->
-
-
-<html>
-	<head>
-		<!-- <script src="sorttable.js"></script> -->
-		<meta charset="UTF-8">
-		<title>Traffic Monitor</title>
-		<style>
-		 body {
-			 font-family: "Lato", sans-serif;
-			 font-size: 14px;
-		 }
-
-		 ul.tab {
-			 list-style-type: none;
-			 margin: 0;
-			 padding: 0;
-			 overflow: hidden;
-			 border: 1px solid #ccc;
-			 background-color: #f1f1f1;
-		 }
-
-		 ul.tab li {float: left;}
-
-		 ul.tab li a {
-			 display: inline-block;
-			 color: black;
-			 text-align: center;
-			 padding: 8px 8px;
-			 text-decoration: none;
-			 transition: 0.3s;
-		 }
-
-		 ul.tab li a:hover {
-			 background-color: #cfd;
-		 }
-
-		 .tab-header-selected {
-			 background-color: #adb;
-		 }
-
-		 .tabcontent {
-			 display: none;
-			 padding: 6px 6px;
-			 border: 1px solid #ccc;
-			 border-top: none;
-		 }
-
-		 table, th, td {
-			 border: 0px solid black;
-		 }
-
-		 table {
-			 border-collapse: separate;
-			 border-spacing: 0px 0;
-			 width: 100%;
-		 }
-
-		 th, td {
-			 padding:5px 20px 5px 5px;
-		 }
-
-		 th {
-			 white-space: nowrap;
-		 }
-
-		 tr.stripes:nth-child(even) {
-			 background: #dfe
-		 }
-		 tr.stripes:nth-child(odd) {
-			 background: #fff
-		 }
-
-		 li.endpoint {
-			 margin: 4px 0;
-		 }
-
-		 ul {
-			 list-style: none;
-		 }
-
-		 #top-bar {
-			 border-collapse: collapse;
-			 border-color: #adb;;
-			 border-width: 0px 0px 1px 0px;
-			 padding-bottom: 10px;
-		 }
-
-		 .links {
-			 display: table;
-		 }
-		 .links ul {
-			 display: table-cell;
-			 vertical-align: top;
-		 }
-
-		 .error {
-			 background-color: #f00;
-		 }
-		 .warning {
-			 background-color: #f80;
-		 }
-		</style>
-		<script>
-		 function init() {
-			 openTab('cache-states-content');
-			 getTopBar();
-			 setInterval(getCacheCount, 4755);
-			 setInterval(getCacheAvailableCount, 4800);
-			 setInterval(getBandwidth, 4621);
-			 setInterval(getBandwidthCapacity, 4591);
-			 setInterval(getCacheDownCount, 4832);
-			 setInterval(getVersion, 10007); // change to retry on failure, and only do on startup
-			 setInterval(getTrafficOpsUri, 10019); // change to retry on failure, and only do on startup
-			 setInterval(getTrafficOpsCdn, 10500); // change to retry on failure, and only do on startup
-			 setInterval(getEvents, 2004); // change to retry on failure, and only do on startup
-			 setInterval(getCacheStatuses, 5009);
-			 setInterval(getDsStats, 4003);
-		 }
-
-		 // source: http://stackoverflow.com/a/2901298/292623
-		 function numberStrWithCommas(x) {
-			 return x.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
-		 }
-
-		 function openTab(tabName) {
-			 var i, x, tablinks;
-			 x = document.getElementsByClassName("tabcontent");
-			 for (i = 0; i < x.length; i++) {
-				 x[i].style.display = "none";
-			 }
-			 tablinks = document.getElementsByClassName("tablinks");
-			 for (i = 0; i < x.length; i++) {
-				 tablinks[i].className = tablinks[i].className.replace(" tab-selected", "");
-			 }
-
-			 tabheaders = document.getElementsByClassName("tab-header");
-			 for (i = 0; i < tabheaders.length; i++) {
-				 tabheaders[i].className = tabheaders[i].className.replace(" tab-header-selected", "");
-			 }
-
-			 document.getElementById(tabName).style.display = "block";
-			 document.getElementById(tabName).className += " tab-selected";
-			 document.getElementById(tabName + "-tab").className += " tab-header-selected";
-		 }
-
-		 function getCacheCount() {
-			 ajax("/api/cache-count", function(r) {
-				 document.getElementById("cache-count").innerHTML = r;
-			 });
-		 }
-
-		 function getCacheAvailableCount() {
-			 ajax("/api/cache-available-count", function(r) {
-				 document.getElementById("cache-available").innerHTML = r;
-			 });
-		 }
-
-		 function getBandwidth() {
-			 ajax("/api/bandwidth-kbps", function(r) {
-				 document.getElementById("bandwidth").innerHTML = numberStrWithCommas((parseFloat(r) / kilobitsInGigabit).toFixed(2));
-			 });
-		 }
-
-		 function getBandwidthCapacity() {
-			 ajax("/api/bandwidth-capacity-kbps", function(r) {
-				 document.getElementById("bandwidth-capacity").innerHTML = numberStrWithCommas((r / kilobitsInGigabit).toString());
-			 });
-		 }
-
-		 function getCacheDownCount() {
-			 ajax("/api/cache-down-count", function(r) {
-				 document.getElementById("cache-down").innerHTML = r;
-			 });
-		 }
-
-		 function getVersion() {
-			 ajax("/api/version", function(r) {
-				 document.getElementById("version").innerHTML = r;
-			 });
-		 }
-
-		 function getTrafficOpsUri() {
-			 ajax("/api/traffic-ops-uri", function(r) {
-				 document.getElementById("source-uri").innerHTML = "<a href='" + r + "'>" + r + "</a>";
-			 });
-		 }
-
-
-		 function getTrafficOpsCdn() {
-			 ajax("/publish/ConfigDoc", function(r) {
-				 var j = JSON.parse(r);
-				 document.getElementById("cdn-name").innerHTML = j.cdnName;
-			 });
-		 }
-
-		 var lastEvent = 0;
-		 function getEvents() {
-			 /// \todo add /api/events-since/{index} (and change Traffic Monitor to keep latest
-			 ajax("/publish/EventLog", function(r) {
-				 var jdata = JSON.parse(r);
-				 for (i = jdata.events.length - 1; i >= 0; i--) {
-					 var event = jdata.events[i];
-					 if (event.index <= lastEvent) {
-						 continue;
-					 }
-					 lastEvent = event.index
-					 var row = document.getElementById("event-log").insertRow(1); //document.createElement("tr");
-					 row.classList.add("stripes");
-					 row.insertCell(0).id = row.id + "-name";
-					 document.getElementById(row.id + "-name").textContent = event.name;
-					 document.getElementById(row.id + "-name").style.whiteSpace = "nowrap";
-					 row.insertCell(1).textContent = event.type;
-					 row.insertCell(2).textContent = event.isAvailable ? "available" : "offline";
-					 if(event.isAvailable) {
-						 row.classList.add("stripes");
-						 row.classList.remove("error");
-					 } else {
-						 row.classList.add("error");
-						 row.classList.remove("stripes");
-					 }
-					 row.insertCell(3).textContent = event.description;
-					 row.insertCell(4).id = row.id + "-last";
-					 document.getElementById(row.id + "-last").textContent = new Date(event.time * 1000).toISOString();
-					 document.getElementById(row.id + "-last").style.whiteSpace = "nowrap";
-					 document.getElementById(row.id + "-last").style.textAlign = "right";
-				 }
-			 });
-		 }
-
-		 function getCacheStates() {
-			 ajax("/api/cache-statuses", function(r) {
-				 var jdata = JSON.parse(r);
-				 var servers = Object.keys(jdata); //debug
-				 for (i = 0; i < servers.length; i++) {
-					 var server = servers[i];
-					 if (!document.getElementById("cache-states-" + server)) {
-						 var row = document.getElementById("cache-states").insertRow(1); //document.createElement("tr");
-						 row.classList.add("stripes");
-						 row.id = "cache-states-" + server
-						 row.insertCell(0).id = row.id + "-server";
-						 row.insertCell(1).id = row.id + "-type";
-						 row.insertCell(2).id = row.id + "-status";
-						 row.insertCell(3).id = row.id + "-load-average";
-						 row.insertCell(4).id = row.id + "-query-time";
-						 row.insertCell(5).id = row.id + "-health-time";
-						 row.insertCell(6).id = row.id + "-stat-time";
-						 row.insertCell(7).id = row.id + "-health-span";
-						 row.insertCell(8).id = row.id + "-stat-span";
-						 row.insertCell(9).id = row.id + "-bandwidth";
-						 row.insertCell(10).id = row.id + "-connection-count";
-						 document.getElementById(row.id + "-server").textContent = server;
-						 document.getElementById(row.id + "-server").style.whiteSpace = "nowrap";
-						 document.getElementById(row.id + "-load-average").style.textAlign = "right";
-						 document.getElementById(row.id + "-query-time").style.textAlign = "right";
-						 document.getElementById(row.id + "-health-time").style.textAlign = "right";
-						 document.getElementById(row.id + "-stat-time").style.textAlign = "right";
-						 document.getElementById(row.id + "-health-span").style.textAlign = "right";
-						 document.getElementById(row.id + "-stat-span").style.textAlign = "right";
-						 document.getElementById(row.id + "-bandwidth").style.textAlign = "right";
-						 document.getElementById(row.id + "-connection-count").style.textAlign = "right";
-					 }
-
-					 /* \todo change to iterate over members, and make cells id constructed from these*/
-					 if (jdata[server].hasOwnProperty("type")) {
-						 document.getElementById("cache-states-" + server + "-type").textContent = jdata[server].type;
-					 }
-					 if (jdata[server].hasOwnProperty("status")) {
-						 document.getElementById("cache-states-" + server + "-status").textContent = jdata[server].status;
-						 var row = document.getElementById("cache-states-" + server);
-						 if (jdata[server].status.indexOf("ADMIN_DOWN") != -1) {
-							 row.classList.add("warning");
-							 row.classList.remove("error");
-							 row.classList.remove("stripes");
-						 } else if (jdata[server].status.indexOf(" available") == -1) {
-							 row.classList.add("error");
-							 row.classList.remove("warning");
-							 row.classList.remove("stripes");
-						 } else {
-							 row.classList.add("stripe");
-							 row.classList.remove("warning");
-							 row.classList.remove("error");
-						 }
-					 }
-					 if (jdata[server].hasOwnProperty("load_average")) {
-						 document.getElementById("cache-states-" + server + "-load-average").textContent = jdata[server].load_average;
-					 }
-					 if (jdata[server].hasOwnProperty("query_time_ms")) {
-						 document.getElementById("cache-states-" + server + "-query-time").textContent = jdata[server].query_time_ms;
-					 }
-					 if (jdata[server].hasOwnProperty("health_time_ms")) {
-						 document.getElementById("cache-states-" + server + "-health-time").textContent = jdata[server].health_time_ms;
-					 }
-					 if (jdata[server].hasOwnProperty("stat_time_ms")) {
-						 document.getElementById("cache-states-" + server + "-stat-time").textContent = jdata[server].stat_time_ms;
-					 }
-					 if (jdata[server].hasOwnProperty("health_span_ms")) {
-						 document.getElementById("cache-states-" + server + "-health-span").textContent = jdata[server].health_span_ms;
-					 }
-					 if (jdata[server].hasOwnProperty("stat_span_ms")) {
-						 document.getElementById("cache-states-" + server + "-stat-span").textContent = jdata[server].stat_span_ms;
-					 }
-					 if (jdata[server].hasOwnProperty("bandwidth_kbps")) {
-						 var kbps = (jdata[server].bandwidth_kbps / kilobitsInMegabit).toFixed(2);
-						 var max = numberStrWithCommas((jdata[server].bandwidth_capacity_kbps / kilobitsInMegabit).toFixed(0));
-						 document.getElementById("cache-states-" + server + "-bandwidth").textContent = '' + kbps + ' / ' + max;
-					 } else {
-						 document.getElementById("cache-states-" + server + "-bandwidth").textContent = "N/A";
-					 }
-					 if (jdata[server].hasOwnProperty("connection_count")) {
-						 document.getElementById("cache-states-" + server + "-connection-count").textContent = jdata[server].connection_count;
-					 } else {
-						 document.getElementById("cache-states-" + server + "-connection-count").textContent = "N/A";
-					 }
-				 }
-			 })
-		 }
-
-		 var millisecondsInSecond = 1000;
-		 var kilobitsInGigabit = 1000000;
-		 var kilobitsInMegabit = 1000;
-
-		 // dsDisplayFloat takes a float, and returns the string to display. For nonzero values, it returns two decimal places. For zero values, it returns an empty string, to make nonzero values more visible.
-		 function dsDisplayFloat(f) {
-			 var s = f
-			 if (f != 0.0) {
-				 s = f.toFixed(2);
-			 }
-			 return s
-		 }
-
-		 function getDsStats() {
-			 var now = Date.now();
-
-			 /// \todo add /api/delivery-service-stats which only returns the data needed by the UI, for efficiency
-			 ajax("/publish/DsStats", function(r) {
-				 var j = JSON.parse(r);
-				 var jds = j.deliveryService
-				 var deliveryServiceNames = Object.keys(jds); //debug
-				 //decrementing for loop so DsNames are alphabetical A-Z
-				 //TODO allow for filtering of columns so this isn't necessary
-					for (var i = deliveryServiceNames.length - 1; i >= 0; i--) {
-					 var deliveryService = deliveryServiceNames[i];
-
-					 if (!document.getElementById("deliveryservice-stats-" + deliveryService)) {
-						 var row = document.getElementById("deliveryservice-stats").insertRow(1); //document.createElement("tr");
-						 row.id = "deliveryservice-stats-" + deliveryService
-						 row.insertCell(0).id = row.id + "-delivery-service";
-						 row.insertCell(1).id = row.id + "-status";
-						 row.insertCell(2).id = row.id + "-caches-reporting";
-						 row.insertCell(3).id = row.id + "-bandwidth";
-						 row.insertCell(4).id = row.id + "-tps";
-						 row.insertCell(5).id = row.id + "-2xx";
-						 row.insertCell(6).id = row.id + "-3xx";
-						 row.insertCell(7).id = row.id + "-4xx";
-						 row.insertCell(8).id = row.id + "-5xx";
-						 row.insertCell(9).id = row.id + "-disabled-locations";
-						 document.getElementById(row.id + "-delivery-service").textContent = deliveryService;
-						 document.getElementById(row.id + "-delivery-service").style.whiteSpace = "nowrap";
-						 document.getElementById(row.id + "-caches-reporting").style.textAlign = "right";
-						 document.getElementById(row.id + "-bandwidth").style.textAlign = "right";
-						 document.getElementById(row.id + "-tps").style.textAlign = "right";
-						 document.getElementById(row.id + "-2xx").style.textAlign = "right";
-						 document.getElementById(row.id + "-3xx").style.textAlign = "right";
-						 document.getElementById(row.id + "-4xx").style.textAlign = "right";
-						 document.getElementById(row.id + "-5xx").style.textAlign = "right";
-					 }
-
-					 // \todo check that array has a member before dereferencing [0]
-					 if (jds[deliveryService].hasOwnProperty("isAvailable")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-status").textContent = jds[deliveryService]["isAvailable"][0].value == "true" ? "available" : "unavailable - " + jds[deliveryService]["error-string"][0].value;
-					 }
-					 if (jds[deliveryService].hasOwnProperty("caches-reporting") && jds[deliveryService].hasOwnProperty("caches-available") && jds[deliveryService].hasOwnProperty("caches-configured")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-caches-reporting").textContent = jds[deliveryService]['caches-reporting'][0].value + " / " + jds[deliveryService]['caches-available'][0].value + " / " + jds[deliveryService]['caches-configured'][0].value;
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.kbps")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-bandwidth").textContent = (jds[deliveryService]['total.kbps'][0].value / kilobitsInMegabit).toFixed(2);
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-bandwidth").textContent = "N/A";
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.tps_total")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-tps").textContent = dsDisplayFloat(parseFloat(jds[deliveryService]['total.tps_total'][0].value));
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-tps").textContent = "N/A";
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.tps_2xx")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-2xx").textContent = dsDisplayFloat(parseFloat(jds[deliveryService]['total.tps_2xx'][0].value));
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-2xx").textContent = "N/A";
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.tps_3xx")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-3xx").textContent = dsDisplayFloat(parseFloat(jds[deliveryService]['total.tps_3xx'][0].value));
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-3xx").textContent = "N/A";
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.tps_4xx")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-4xx").textContent = dsDisplayFloat(parseFloat(jds[deliveryService]['total.tps_4xx'][0].value));
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-4xx").textContent = "N/A";
-					 }
-					 if (jds[deliveryService].hasOwnProperty("total.tps_5xx")) {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-5xx").textContent = dsDisplayFloat(parseFloat(jds[deliveryService]['total.tps_5xx'][0].value));
-					 } else {
-						 document.getElementById("deliveryservice-stats-" + deliveryService + "-5xx").textContent = "N/A";
-					 }
-
-					 // \todo implement disabled locations
-
-					 var row = document.getElementById("deliveryservice-stats-" + deliveryService);
-					 if (jds[deliveryService]["isAvailable"][0].value == "true") {
-						 row.classList.add("stripes");
-						 row.classList.remove("error");
-					 } else {
-						 row.classList.add("error");
-						 row.classList.remove("stripes");
-					 }
-				 }
-			 })
-		 }
-
-		 function getCacheStatuses() {
-			 getCacheCount();
-			 getCacheAvailableCount();
-			 getCacheDownCount();
-			 getCacheStates();
-		 }
-
-		 function getTopBar() {
-			 getVersion();
-			 getTrafficOpsUri();
-			 getTrafficOpsCdn();
-			 getCacheStatuses();
-		 }
-
-		 function ajax(endpoint, f) {
-			 var xhttp = new XMLHttpRequest();
-			 xhttp.onreadystatechange = function() {
-				 if (xhttp.readyState == 4 && xhttp.status == 200) {
-					 f(xhttp.responseText);
-				 }
-			 };
-			 xhttp.open("GET", endpoint, true);
-			 xhttp.send();
-		 }
-		</script>
-	</head>
-	<body onload="init()">
-
-		<table id="top-bar">
-			<tr>
-				<td>Caches: count=<span id="cache-count">0</span> available=<span id="cache-available">0</span> down=<span id="cache-down">0</span> </td>
-				<td>Bandwidth: <span id="bandwidth">0</span> / <span id="bandwidth-capacity">\u221e</span> gbps</td>
-				<td>Traffic Ops: <span id="source-uri">unknown</span></td>
-				<td>CDN: <span id="cdn-name">unknown</span></td>
-				<td>Version: <span id="version">unknown</span></td>
-			</tr>
-		</table>
-
-		<div class="links">
-			<ul>
-				<li class="endpoint"><a href="/publish/EventLog">EventLog</a></li>
-				<li class="endpoint"><a href="/publish/CacheStats">CacheStats</a></li>
-				<li class="endpoint"><a href="/publish/DsStats">DsStats</a></li>
-				<li class="endpoint"><a href="/publish/CrStates">CrStates (as published to Traffic Routers)</a></li>
-				<li class="endpoint"><a href="/publish/CrConfig">CrConfig (json)</a></li>
-				<li class="endpoint"><a href="/publish/PeerStates">PeerStates</a></li>
-				<li class="endpoint"><a href="/publish/Stats">Stats</a></li>
-				<li class="endpoint"><a href="/publish/StatSummary">StatSummary</a></li>
-				<li class="endpoint"><a href="/publish/ConfigDoc">ConfigDoc</a></li>
-			</ul>
-
-			<ul>
-				<li class="endpoint"><a href="/api/cache-count">/api/cache-count</a></li>
-				<li class="endpoint"><a href="/api/cache-available-count">/api/cache-available-count</a></li>
-				<li class="endpoint"><a href="/api/cache-down-count">/api/cache-down-count</a></li>
-				<li class="endpoint"><a href="/api/version">/api/version</a></li>
-				<li class="endpoint"><a href="/api/traffic-ops-uri">/api/traffic-ops-uri</a></li>
-				<li class="endpoint"><a href="/api/cache-statuses">/api/cache-statuses</a></li>
-				<li class="endpoint"><a href="/api/bandwidth-kbps">/api/bandwidth-kbps</a></li>
-				<li class="endpoint"><a href="/api/bandwidth-capacity-kbps">/api/bandwidth-capacity-kbps</a></li>
-			</ul>
-		</div>
-
-		<ul class="tab">
-			<li id="cache-states-content-tab" class="tab-header"><a href="#" onclick="openTab('cache-states-content')" class="tablinks">Cache States</a></li>
-			<li id="deliveryservice-stats-content-tab" class="tab-header"><a href="#" onclick="openTab('deliveryservice-stats-content')" class="tablinks">Delivery Service States</a></li>
-			<li id="event-log-content-tab" class="tab-header"><a href="#" onclick="openTab('event-log-content')" class="tablinks">Event Log</a></li>
-		</ul>
-
-		<div id="cache-states-content" class="tabcontent">
-			<table id="cache-states" class="tab-grid sortable">
-				<tr>
-					<th>Server</th>
-					<th>Type</th>
-					<th>Status</th>
-					<th align="right">Load Average</th>
-					<th align="right">Query Time (ms)</th>
-					<th align="right">Health Time (ms)</th>
-					<th align="right">Stat Time (ms)</th>
-					<th align="right">Health Span (ms)</th>
-					<th align="right">Stat Span (ms)</th>
-					<th align="right">Bandwidth (mbps)</th>
-					<th align="right">Connection Count</th>
-				</tr>
-			</table>
-		</div>
-		<div id="deliveryservice-stats-content" class="tabcontent">
-			<table id="deliveryservice-stats" class="tab-grid sortable">
-				<tr>
-					<th>Delivery Service</th>
-					<th>Status</th>
-					<th align="right">Caches Reporting/Available/Configured</th>
-					<th align="right">Bandwidth (mbps)</th>
-					<th align="right">t/sec</th>
-					<th align="right">2xx/sec</th>
-					<th align="right">3xx/sec</th>
-					<th align="right">4xx/sec</th>
-					<th align="right">5xx/sec</th>
-					<th>Disabled Locations</th>
-				</tr>
-			</table>
-		</div>
-
-		<div id="event-log-content" class="tabcontent">
-			<table id="event-log" class="tab-grid sortable">
-				<tr>
-					<th>Name</th>
-					<th>Type</th>
-					<th>Status</th>
-					<th>Description</th>
-					<th align="center" id="event-log-last-header">Event Time</th>
-				</tr>
-			</table>
-		</div>
-
-		<div id="update-num-text">Number of updates: <span id="update-num">0</span></div>
-		<div id="last-val-text">Last Val: <span id="last-val">0</span></div>
-		<a href="/">Refresh Server List</a>
-	</body>
-</html>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/static/sorttable.js
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/static/sorttable.js b/traffic_monitor/experimental/traffic_monitor/static/sorttable.js
deleted file mode 100644
index 38b0fc6..0000000
--- a/traffic_monitor/experimental/traffic_monitor/static/sorttable.js
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
-  SortTable
-  version 2
-  7th April 2007
-  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
-
-  Instructions:
-  Download this file
-  Add <script src="sorttable.js"></script> to your HTML
-  Add class="sortable" to any table you'd like to make sortable
-  Click on the headers to sort
-
-  Thanks to many, many people for contributions and suggestions.
-  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
-  This basically means: do what you want with it.
-*/
-
-
-var stIsIE = /*@cc_on!@*/false;
-
-sorttable = {
-  init: function() {
-    // quit if this function has already been called
-    if (arguments.callee.done) return;
-    // flag this function so we don't do the same thing twice
-    arguments.callee.done = true;
-    // kill the timer
-    if (_timer) clearInterval(_timer);
-
-    if (!document.createElement || !document.getElementsByTagName) return;
-
-    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
-
-    forEach(document.getElementsByTagName('table'), function(table) {
-      if (table.className.search(/\bsortable\b/) != -1) {
-        sorttable.makeSortable(table);
-      }
-    });
-
-  },
-
-  makeSortable: function(table) {
-    if (table.getElementsByTagName('thead').length == 0) {
-      // table doesn't have a tHead. Since it should have, create one and
-      // put the first table row in it.
-      the = document.createElement('thead');
-      the.appendChild(table.rows[0]);
-      table.insertBefore(the,table.firstChild);
-    }
-    // Safari doesn't support table.tHead, sigh
-    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
-
-    if (table.tHead.rows.length != 1) return; // can't cope with two header rows
-
-    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
-    // "total" rows, for example). This is B&R, since what you're supposed
-    // to do is put them in a tfoot. So, if there are sortbottom rows,
-    // for backwards compatibility, move them to tfoot (creating it if needed).
-    sortbottomrows = [];
-    for (var i=0; i<table.rows.length; i++) {
-      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
-        sortbottomrows[sortbottomrows.length] = table.rows[i];
-      }
-    }
-    if (sortbottomrows) {
-      if (table.tFoot == null) {
-        // table doesn't have a tfoot. Create one.
-        tfo = document.createElement('tfoot');
-        table.appendChild(tfo);
-      }
-      for (var i=0; i<sortbottomrows.length; i++) {
-        tfo.appendChild(sortbottomrows[i]);
-      }
-      delete sortbottomrows;
-    }
-
-    // work through each column and calculate its type
-    headrow = table.tHead.rows[0].cells;
-    for (var i=0; i<headrow.length; i++) {
-      // manually override the type with a sorttable_type attribute
-      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
-        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
-        if (mtch) { override = mtch[1]; }
-	      if (mtch && typeof sorttable["sort_"+override] == 'function') {
-	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
-	      } else {
-	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
-	      }
-	      // make it clickable to sort
-	      headrow[i].sorttable_columnindex = i;
-	      headrow[i].sorttable_tbody = table.tBodies[0];
-	      dean_addEvent(headrow[i],"click", sorttable.innerSortFunction = function(e) {
-
-          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
-            // if we're already sorted by this column, just
-            // reverse the table, which is quicker
-            sorttable.reverse(this.sorttable_tbody);
-            this.className = this.className.replace('sorttable_sorted',
-                                                    'sorttable_sorted_reverse');
-            this.removeChild(document.getElementById('sorttable_sortfwdind'));
-            sortrevind = document.createElement('span');
-            sortrevind.id = "sorttable_sortrevind";
-            sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
-            this.appendChild(sortrevind);
-            return;
-          }
-          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
-            // if we're already sorted by this column in reverse, just
-            // re-reverse the table, which is quicker
-            sorttable.reverse(this.sorttable_tbody);
-            this.className = this.className.replace('sorttable_sorted_reverse',
-                                                    'sorttable_sorted');
-            this.removeChild(document.getElementById('sorttable_sortrevind'));
-            sortfwdind = document.createElement('span');
-            sortfwdind.id = "sorttable_sortfwdind";
-            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
-            this.appendChild(sortfwdind);
-            return;
-          }
-
-          // remove sorttable_sorted classes
-          theadrow = this.parentNode;
-          forEach(theadrow.childNodes, function(cell) {
-            if (cell.nodeType == 1) { // an element
-              cell.className = cell.className.replace('sorttable_sorted_reverse','');
-              cell.className = cell.className.replace('sorttable_sorted','');
-            }
-          });
-          sortfwdind = document.getElementById('sorttable_sortfwdind');
-          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
-          sortrevind = document.getElementById('sorttable_sortrevind');
-          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
-
-          this.className += ' sorttable_sorted';
-          sortfwdind = document.createElement('span');
-          sortfwdind.id = "sorttable_sortfwdind";
-          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
-          this.appendChild(sortfwdind);
-
-	        // build an array to sort. This is a Schwartzian transform thing,
-	        // i.e., we "decorate" each row with the actual sort key,
-	        // sort based on the sort keys, and then put the rows back in order
-	        // which is a lot faster because you only do getInnerText once per row
-	        row_array = [];
-	        col = this.sorttable_columnindex;
-	        rows = this.sorttable_tbody.rows;
-	        for (var j=0; j<rows.length; j++) {
-	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
-	        }
-	        /* If you want a stable sort, uncomment the following line */
-	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
-	        /* and comment out this one */
-	        row_array.sort(this.sorttable_sortfunction);
-
-	        tb = this.sorttable_tbody;
-	        for (var j=0; j<row_array.length; j++) {
-	          tb.appendChild(row_array[j][1]);
-	        }
-
-	        delete row_array;
-	      });
-	    }
-    }
-  },
-
-  guessType: function(table, column) {
-    // guess the type of a column based on its first non-blank row
-    sortfn = sorttable.sort_alpha;
-    for (var i=0; i<table.tBodies[0].rows.length; i++) {
-      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
-      if (text != '') {
-        if (text.match(/^-?[\ufffd$\ufffd]?[\d,.]+%?$/)) {
-          return sorttable.sort_numeric;
-        }
-        // check for a date: dd/mm/yyyy or dd/mm/yy
-        // can have / or . or - as separator
-        // can be mm/dd as well
-        possdate = text.match(sorttable.DATE_RE)
-        if (possdate) {
-          // looks like a date
-          first = parseInt(possdate[1]);
-          second = parseInt(possdate[2]);
-          if (first > 12) {
-            // definitely dd/mm
-            return sorttable.sort_ddmm;
-          } else if (second > 12) {
-            return sorttable.sort_mmdd;
-          } else {
-            // looks like a date, but we can't tell which, so assume
-            // that it's dd/mm (English imperialism!) and keep looking
-            sortfn = sorttable.sort_ddmm;
-          }
-        }
-      }
-    }
-    return sortfn;
-  },
-
-  getInnerText: function(node) {
-    // gets the text we want to use for sorting for a cell.
-    // strips leading and trailing whitespace.
-    // this is *not* a generic getInnerText function; it's special to sorttable.
-    // for example, you can override the cell text with a customkey attribute.
-    // it also gets .value for <input> fields.
-
-    if (!node) return "";
-
-    hasInputs = (typeof node.getElementsByTagName == 'function') &&
-                 node.getElementsByTagName('input').length;
-
-    if (node.getAttribute("sorttable_customkey") != null) {
-      return node.getAttribute("sorttable_customkey");
-    }
-    else if (typeof node.textContent != 'undefined' && !hasInputs) {
-      return node.textContent.replace(/^\s+|\s+$/g, '');
-    }
-    else if (typeof node.innerText != 'undefined' && !hasInputs) {
-      return node.innerText.replace(/^\s+|\s+$/g, '');
-    }
-    else if (typeof node.text != 'undefined' && !hasInputs) {
-      return node.text.replace(/^\s+|\s+$/g, '');
-    }
-    else {
-      switch (node.nodeType) {
-        case 3:
-          if (node.nodeName.toLowerCase() == 'input') {
-            return node.value.replace(/^\s+|\s+$/g, '');
-          }
-        case 4:
-          return node.nodeValue.replace(/^\s+|\s+$/g, '');
-          break;
-        case 1:
-        case 11:
-          var innerText = '';
-          for (var i = 0; i < node.childNodes.length; i++) {
-            innerText += sorttable.getInnerText(node.childNodes[i]);
-          }
-          return innerText.replace(/^\s+|\s+$/g, '');
-          break;
-        default:
-          return '';
-      }
-    }
-  },
-
-  reverse: function(tbody) {
-    // reverse the rows in a tbody
-    newrows = [];
-    for (var i=0; i<tbody.rows.length; i++) {
-      newrows[newrows.length] = tbody.rows[i];
-    }
-    for (var i=newrows.length-1; i>=0; i--) {
-       tbody.appendChild(newrows[i]);
-    }
-    delete newrows;
-  },
-
-  /* sort functions
-     each sort function takes two parameters, a and b
-     you are comparing a[0] and b[0] */
-  sort_numeric: function(a,b) {
-    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
-    if (isNaN(aa)) aa = 0;
-    bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
-    if (isNaN(bb)) bb = 0;
-    return aa-bb;
-  },
-  sort_alpha: function(a,b) {
-    if (a[0]==b[0]) return 0;
-    if (a[0]<b[0]) return -1;
-    return 1;
-  },
-  sort_ddmm: function(a,b) {
-    mtch = a[0].match(sorttable.DATE_RE);
-    y = mtch[3]; m = mtch[2]; d = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt1 = y+m+d;
-    mtch = b[0].match(sorttable.DATE_RE);
-    y = mtch[3]; m = mtch[2]; d = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt2 = y+m+d;
-    if (dt1==dt2) return 0;
-    if (dt1<dt2) return -1;
-    return 1;
-  },
-  sort_mmdd: function(a,b) {
-    mtch = a[0].match(sorttable.DATE_RE);
-    y = mtch[3]; d = mtch[2]; m = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt1 = y+m+d;
-    mtch = b[0].match(sorttable.DATE_RE);
-    y = mtch[3]; d = mtch[2]; m = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt2 = y+m+d;
-    if (dt1==dt2) return 0;
-    if (dt1<dt2) return -1;
-    return 1;
-  },
-
-  shaker_sort: function(list, comp_func) {
-    // A stable sort function to allow multi-level sorting of data
-    // see: http://en.wikipedia.org/wiki/Cocktail_sort
-    // thanks to Joseph Nahmias
-    var b = 0;
-    var t = list.length - 1;
-    var swap = true;
-
-    while(swap) {
-        swap = false;
-        for(var i = b; i < t; ++i) {
-            if ( comp_func(list[i], list[i+1]) > 0 ) {
-                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
-                swap = true;
-            }
-        } // for
-        t--;
-
-        if (!swap) break;
-
-        for(var i = t; i > b; --i) {
-            if ( comp_func(list[i], list[i-1]) < 0 ) {
-                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
-                swap = true;
-            }
-        } // for
-        b++;
-
-    } // while(swap)
-  }
-}
-
-/* ******************************************************************
-   Supporting functions: bundled here to avoid depending on a library
-   ****************************************************************** */
-
-// Dean Edwards/Matthias Miller/John Resig
-
-/* for Mozilla/Opera9 */
-if (document.addEventListener) {
-    document.addEventListener("DOMContentLoaded", sorttable.init, false);
-}
-
-/* for Internet Explorer */
-/*@cc_on @*/
-/*@if (@_win32)
-    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
-    var script = document.getElementById("__ie_onload");
-    script.onreadystatechange = function() {
-        if (this.readyState == "complete") {
-            sorttable.init(); // call the onload handler
-        }
-    };
-/*@end @*/
-
-/* for Safari */
-if (/WebKit/i.test(navigator.userAgent)) { // sniff
-    var _timer = setInterval(function() {
-        if (/loaded|complete/.test(document.readyState)) {
-            sorttable.init(); // call the onload handler
-        }
-    }, 10);
-}
-
-/* for other browsers */
-window.onload = sorttable.init;
-
-// written by Dean Edwards, 2005
-// with input from Tino Zijdel, Matthias Miller, Diego Perini
-
-// http://dean.edwards.name/weblog/2005/10/add-event/
-
-function dean_addEvent(element, type, handler) {
-	if (element.addEventListener) {
-		element.addEventListener(type, handler, false);
-	} else {
-		// assign each event handler a unique ID
-		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
-		// create a hash table of event types for the element
-		if (!element.events) element.events = {};
-		// create a hash table of event handlers for each element/event pair
-		var handlers = element.events[type];
-		if (!handlers) {
-			handlers = element.events[type] = {};
-			// store the existing event handler (if there is one)
-			if (element["on" + type]) {
-				handlers[0] = element["on" + type];
-			}
-		}
-		// store the event handler in the hash table
-		handlers[handler.$$guid] = handler;
-		// assign a global event handler to do all the work
-		element["on" + type] = handleEvent;
-	}
-};
-// a counter used to create unique IDs
-dean_addEvent.guid = 1;
-
-function removeEvent(element, type, handler) {
-	if (element.removeEventListener) {
-		element.removeEventListener(type, handler, false);
-	} else {
-		// delete the event handler from the hash table
-		if (element.events && element.events[type]) {
-			delete element.events[type][handler.$$guid];
-		}
-	}
-};
-
-function handleEvent(event) {
-	var returnValue = true;
-	// grab the event object (IE uses a global event object)
-	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
-	// get a reference to the hash table of event handlers
-	var handlers = this.events[event.type];
-	// execute each event handler
-	for (var i in handlers) {
-		this.$$handleEvent = handlers[i];
-		if (this.$$handleEvent(event) === false) {
-			returnValue = false;
-		}
-	}
-	return returnValue;
-};
-
-function fixEvent(event) {
-	// add W3C standard event methods
-	event.preventDefault = fixEvent.preventDefault;
-	event.stopPropagation = fixEvent.stopPropagation;
-	return event;
-};
-fixEvent.preventDefault = function() {
-	this.returnValue = false;
-};
-fixEvent.stopPropagation = function() {
-  this.cancelBubble = true;
-}
-
-// Dean's forEach: http://dean.edwards.name/base/forEach.js
-/*
-	forEach, version 1.0
-	Copyright 2006, Dean Edwards
-	License: http://www.opensource.org/licenses/mit-license.php
-*/
-
-// array-like enumeration
-if (!Array.forEach) { // mozilla already supports this
-	Array.forEach = function(array, block, context) {
-		for (var i = 0; i < array.length; i++) {
-			block.call(context, array[i], i, array);
-		}
-	};
-}
-
-// generic enumeration
-Function.prototype.forEach = function(object, block, context) {
-	for (var key in object) {
-		if (typeof this.prototype[key] == "undefined") {
-			block.call(context, object[key], key, object);
-		}
-	}
-};
-
-// character enumeration
-String.forEach = function(string, block, context) {
-	Array.forEach(string.split(""), function(chr, index) {
-		block.call(context, chr, index, string);
-	});
-};
-
-// globally resolve forEach enumeration
-var forEach = function(object, block, context) {
-	if (object) {
-		var resolve = Object; // default
-		if (object instanceof Function) {
-			// functions have a "length" property
-			resolve = Function;
-		} else if (object.forEach instanceof Function) {
-			// the object implements a custom forEach method so use that
-			object.forEach(block, context);
-			return;
-		} else if (typeof object == "string") {
-			// the object is a string
-			resolve = String;
-		} else if (typeof object.length == "number") {
-			// the object is array-like
-			resolve = Array;
-		}
-		resolve.forEach(object, block, context);
-	}
-};
-

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/threadsafe/cacheavailablestatus.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/threadsafe/cacheavailablestatus.go b/traffic_monitor/experimental/traffic_monitor/threadsafe/cacheavailablestatus.go
deleted file mode 100644
index edf7b37..0000000
--- a/traffic_monitor/experimental/traffic_monitor/threadsafe/cacheavailablestatus.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package threadsafe
-
-/*
- * 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.
- */
-
-import (
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/cache"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-	"sync"
-)
-
-// CacheAvailableStatus wraps a map of cache available statuses to be safe for multiple reader goroutines and one writer.
-type CacheAvailableStatus struct {
-	caches *cache.AvailableStatuses
-	m      *sync.RWMutex
-}
-
-// NewCacheAvailableStatus creates and returns a new CacheAvailableStatus, initializing internal pointer values.
-func NewCacheAvailableStatus() CacheAvailableStatus {
-	c := cache.AvailableStatuses(map[enum.CacheName]cache.AvailableStatus{})
-	return CacheAvailableStatus{m: &sync.RWMutex{}, caches: &c}
-}
-
-// Get returns the internal map of cache statuses. The returned map MUST NOT be modified. If modification is necessary, copy.
-func (o *CacheAvailableStatus) Get() cache.AvailableStatuses {
-	o.m.RLock()
-	defer o.m.RUnlock()
-	return *o.caches
-}
-
-// Set sets the internal map of cache availability. This MUST NOT be called by multiple goroutines.
-func (o *CacheAvailableStatus) Set(v cache.AvailableStatuses) {
-	o.m.Lock()
-	*o.caches = v
-	o.m.Unlock()
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/threadsafe/cachemaxkbpses.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/threadsafe/cachemaxkbpses.go b/traffic_monitor/experimental/traffic_monitor/threadsafe/cachemaxkbpses.go
deleted file mode 100644
index 5bb584c..0000000
--- a/traffic_monitor/experimental/traffic_monitor/threadsafe/cachemaxkbpses.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package threadsafe
-
-/*
- * 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.
- */
-
-import (
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/cache"
-	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
-	"sync"
-)
-
-// CacheAvailableStatus wraps a map of cache available statuses to be safe for multiple reader goroutines and one writer.
-type CacheKbpses struct {
-	v *cache.Kbpses
-	m *sync.RWMutex
-}
-
-// NewCacheAvailableStatus creates and returns a new CacheAvailableStatus, initializing internal pointer values.
-func NewCacheKbpses() CacheKbpses {
-	v := cache.Kbpses(map[enum.CacheName]int64{})
-	return CacheKbpses{m: &sync.RWMutex{}, v: &v}
-}
-
-// Get returns the internal map of cache statuses. The returned map MUST NOT be modified. If modification is necessary, copy.
-func (o *CacheKbpses) Get() cache.Kbpses {
-	o.m.RLock()
-	defer o.m.RUnlock()
-	return *o.v
-}
-
-// Set sets the internal map of cache availability. This MUST NOT be called by multiple goroutines.
-func (o *CacheKbpses) Set(v cache.Kbpses) {
-	o.m.Lock()
-	*o.v = v
-	o.m.Unlock()
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/threadsafe/dsstats.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/threadsafe/dsstats.go b/traffic_monitor/experimental/traffic_monitor/threadsafe/dsstats.go
deleted file mode 100644
index a13c01a..0000000
--- a/traffic_monitor/experimental/traffic_monitor/threadsafe/dsstats.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package threadsafe
-
-/*
- * 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.
- */
-
-import (
-	"sync"
-
-	dsdata "github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/deliveryservicedata"
-)
-
-// DSStats wraps a deliveryservice.Stats object to be safe for multiple reader goroutines and a single writer.
-type DSStats struct {
-	dsStats *dsdata.Stats
-	m       *sync.RWMutex
-}
-
-// DSStatsReader permits reading of a dsdata.Stats object, but not writing. This is designed so a Stats object can safely be passed to multiple goroutines, without worry one may unsafely write.
-type DSStatsReader interface {
-	Get() dsdata.StatsReadonly
-}
-
-// NewDSStats returns a deliveryservice.Stats object wrapped to be safe for multiple readers and a single writer.
-func NewDSStats() DSStats {
-	s := dsdata.NewStats()
-	return DSStats{m: &sync.RWMutex{}, dsStats: &s}
-}
-
-// Get returns a Stats object safe for reading by multiple goroutines
-func (o *DSStats) Get() dsdata.StatsReadonly {
-	o.m.RLock()
-	defer o.m.RUnlock()
-	return *o.dsStats
-}
-
-// Set sets the internal Stats object. This MUST NOT be called by multiple goroutines.
-func (o *DSStats) Set(newDsStats dsdata.Stats) {
-	o.m.Lock()
-	*o.dsStats = newDsStats
-	o.m.Unlock()
-}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/594b8517/traffic_monitor/experimental/traffic_monitor/threadsafe/lastkbpsstats.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/threadsafe/lastkbpsstats.go b/traffic_monitor/experimental/traffic_monitor/threadsafe/lastkbpsstats.go
deleted file mode 100644
index 610b8a9..0000000
--- a/traffic_monitor/experimental/traffic_monitor/threadsafe/lastkbpsstats.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package threadsafe
-
-/*
- * 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.
- */
-
-import (
-	"sync"
-
-	dsdata "github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/deliveryservicedata"
-)
-
-// LastStats wraps a deliveryservice.LastStats object to be safe for multiple readers and one writer.
-type LastStats struct {
-	stats *dsdata.LastStats
-	m     *sync.RWMutex
-}
-
-// NewLastStats returns a wrapped a deliveryservice.LastStats object safe for multiple readers and one writer.
-func NewLastStats() LastStats {
-	s := dsdata.NewLastStats()
-	return LastStats{m: &sync.RWMutex{}, stats: &s}
-}
-
-// Get returns the last KBPS stats object. Callers MUST NOT modify the object. It is not threadsafe for writing. If the object must be modified, callers must call LastStats.Copy() and modify the copy.
-func (o *LastStats) Get() dsdata.LastStats {
-	o.m.RLock()
-	defer o.m.RUnlock()
-	return *o.stats
-}
-
-// Set sets the internal LastStats object. This MUST NOT be called by multiple goroutines.
-func (o *LastStats) Set(s dsdata.LastStats) {
-	o.m.Lock()
-	*o.stats = s
-	o.m.Unlock()
-}