diff --git a/app/collins/graphs/GangliaGraphConfig.scala b/app/collins/graphs/GangliaGraphConfig.scala new file mode 100644 index 000000000..10f41dfd3 --- /dev/null +++ b/app/collins/graphs/GangliaGraphConfig.scala @@ -0,0 +1,38 @@ +package collins.graphs + +import util.config.{Configurable, ConfigValue} + +// todo: support by role custom metrics/graphs +object GangliaGraphConfig extends Configurable { + + override val namespace = "graph.GangliaGraphs" + override val referenceConfigFilename = "ganglia_graph_reference.conf" + + def url = getString("url")(ConfigValue.Required).get + + def timeRange = getString("timeRange", "1hr"); + + def hostSuffix = getString("hostSuffix", ""); + + // Ganglia has a distinction between graphs of individual metrics + // "load_one", and some pre-created reports that pulls things + // together in one graph image (show load_one, and load_five). + // Confusingly, these are sometimes called graphs, and sometimes + // reports. + def defaultGraphs = getStringList("defaultMetrics", List( + "load_all_report", "load_report", "mem_report", "cpu_report", "network_report", "packet_report" + )) + + def defaultMetrics = getStringList("defaultMetrics", List( + "disk_free", "disk_total" + )) + + override def validateConfig() { + url + hostSuffix + timeRange + defaultGraphs + defaultMetrics + } + +} diff --git a/app/collins/graphs/GangliaGraphs.scala b/app/collins/graphs/GangliaGraphs.scala new file mode 100644 index 000000000..3583270e0 --- /dev/null +++ b/app/collins/graphs/GangliaGraphs.scala @@ -0,0 +1,54 @@ +package collins.graphs + +import models.asset.AssetView + +import java.net.URLEncoder +import play.api.libs.json._ +import com.codahale.jerkson.Json._ + +import play.api.Application +import play.api.mvc.Content +import play.api.templates.Html + +case class GangliaGraphs(override val app: Application) extends GraphView { + + override def get(asset: AssetView): Option[Content] = { + if (asset.isServerNode && asset.getHostnameMetaValue.isDefined) { + Some(getIframe(generate_dynamic_view_json(asset.getHostnameMetaValue.get))) + } else { + None + } + } + + override def validateConfig() { + GangliaGraphConfig.pluginInitialize(app.configuration) + } + + protected def getIframe(view_json: String) = { + collins.graphs.templates.html.ganglia(URLEncoder.encode(view_json, "UTF-8")) + } + + def hostKey(hostname: String): String = { + hostname + GangliaGraphConfig.hostSuffix + } + + def generate_dynamic_view_json(hostname: String): String = { + generate( + Map( + "view_name" -> "ad-hoc", + "view_type" -> "standard", + "items" -> (GangliaGraphConfig.defaultGraphs.map(g => + Map( + "hostname" -> hostKey(hostname), + "graph" -> g + )) ++ GangliaGraphConfig.defaultMetrics.map(m => + Map( + "hostname" -> hostKey(hostname), + "metric" -> m + ) + )) + ) + ) + } + +} diff --git a/app/collins/graphs/templates/ganglia.scala.html b/app/collins/graphs/templates/ganglia.scala.html new file mode 100644 index 000000000..82293005b --- /dev/null +++ b/app/collins/graphs/templates/ganglia.scala.html @@ -0,0 +1,29 @@ +@(ad_hoc_view: String) + +@genURL() = @{ + Seq( + "%s?hide-hf=true".format(collins.graphs.GangliaGraphConfig.url), + "tab=v", + "r=%s".format(collins.graphs.GangliaGraphConfig.timeRange), + "ad-hoc-view=%s".format(ad_hoc_view) + ).mkString("&") +} + +<style type="text/css"> +#ganglia-graph { + width: 100%; + height: 1600px; +} +</style> + +<iframe id="ganglia-graph" src="" frameborder="0"></iframe> + +<script type="text/javascript"> + $('a[data-toggle="pill"]').on('show', function(e) { + var target = $(e.target) + var iframe = $('#ganglia-graph') + if (target.attr('href') === '#graph-info' && iframe.attr('src') === "") { + iframe.attr('src', "@Html(genURL())") + } + }) +</script> diff --git a/app/util/config/ConfigAccessor.scala b/app/util/config/ConfigAccessor.scala index 5781ba4ad..329078da8 100644 --- a/app/util/config/ConfigAccessor.scala +++ b/app/util/config/ConfigAccessor.scala @@ -89,6 +89,14 @@ trait ConfigAccessor { protected def getStringList(key: String): List[String] = getValue(key, _.getStringList(key).asScala.toList).getOrElse(List()) + protected def getStringList(key: String, default: List[String]): List[String] = { + val list = getStringList(key) + if (list.isEmpty) + default + else + list + } + protected def getStringSet(key: String): Set[String] = getStringList(key).toSet protected def getStringSet(key: String, default: Set[String]): Set[String] = { diff --git a/conf/reference/ganglia_graph_reference.conf b/conf/reference/ganglia_graph_reference.conf new file mode 100644 index 000000000..56b1d4dbf --- /dev/null +++ b/conf/reference/ganglia_graph_reference.conf @@ -0,0 +1,9 @@ +graph { + + class = "collins.graphs.GangliaGraphs" + enabled = false + GangliaGraphs { + url = "http://my-ganglia.domain.local/" + } + +}