More info, from @whd:
Some good news.
You won't need to roll a new graphite to get the programmatic dashboard import stuff. It's all there under the hood in the latest stable; only the GUI edit button didn't make it to 0.9.10.
API Summary: (all payloads are JSON)
/GET GRAPHITE_HOST/dashboard/find/?query=NAME
Return an array of matching results, for our purposes simply filtering on name suffices to determine if a dashboard is defined.
/GET GRAPHITE_HOST/dashboard/load/NAME
Return the specification of the dashboard NAME.
/POST GRAPHITE_HOST/dashboard/save/NAME
Save the dashboard definition to Django's internal structures. Here is the annoying part. The server interprets the POST as a URL-encoded query string, since it's usually constructed by the JS of the webapp. The webapp does a conversion from:
{"state": STUFF}
to
state=STUFF
so the latter is what the server expects to receive. STUFF is a giant embedded JSON string and since it's URL-encoded it is nigh impossible to actually read, e.g.
state=%7B%22name%22%3A%22bar%22%2C%22timeConfig%22%3A%7B%22type%22%3A%22relative%22%2C%22relativeStartQuantity%22%3A%222%22%2C%22relativeStartUnits%22%3A%22hours%22%2C%22relativeUntilQuantity%22%3A%22%22%2C%22relativeUntilUnits%22%3A%22now%22%2C%22startDate%22%3A%222013-06-03T08%3A26%3A46%22%2C%22startTime%22%3A%229%3A00%20AM%22%2C%22endDate%22%3A%222013-06-03T08%3A26%3A46%22%2C%22endTime%22%3A%225%3A00%20PM%22%7D%2C%22refreshConfig%22%3A%7B%22enabled%22%3Afalse%2C%22interval%22%3A60000%7D%2C%22graphSize%22%3A%7B%22width%22%3A400%2C%22height%22%3A250%7D%2C%22defaultGraphParams%22%3A%7B%22width%22%3A400%2C%22from%22%3A%22-2hours%22%2C%22until%22%3A%22now%22%2C%22height%22%3A250%7D%2C%22graphs%22%3A%5B%5B%5B%22stats_counts.statsd.packets_received%22%5D%2C%7B%22target%22%3A%5B%22stats_counts.statsd.packets_received%22%5D%7D%2C%22%2Frender%3Fwidth%3D400%26from%3D-2hours%26until%3Dnow%26height%3D250%26target%3Dstats_counts.statsd.packets_received%26_uniq%3D0.79433781532812864%26title%3Dstats_counts.statsd.packets_received%22%5D%5D%7D
c.f.
{"state": {"name": "bar", "defaultGraphParams": {"width": 400, "from": "-2hours", "until": "now", "height": 250}, "refreshConfig": {"interval": 60000, "enabled": false}, "graphs": [[["stats_counts.statsd.packets_received"], {"target": ["stats_counts.statsd.packets_received"]}, "/render?width=400&from=-2hours&until=now&height=250&target=stats_counts.statsd.packets_received&_uniq=0.21331518678925931&title=stats_counts.statsd.packets_received"]], "timeConfig": {"startDate": "2013-06-03T01:26:46", "relativeStartUnits": "hours", "endDate": "2013-06-03T01:26:46", "relativeStartQuantity": "2", "relativeUntilQuantity": "", "startTime": "9:00 AM", "endTime": "5:00 PM", "type": "relative", "relativeUntilUnits": "now"}, "graphSize": {"width": 400, "height": 250}}}
Some crappy python for demonstration at:
https://people.mozilla.org/~wdawson/dashboard_api