# Copyright (c) 2010-2011 Red Hat, Inc.
#
# This file is part of HekaFS.
#
# HekaFS is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License, version 3, as published by the Free
# Software Foundation.
#
# HekaFS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License * along
# with HekaFS.  If not, see .
import copy
import string
import sys
import types
good_xlators = [
	"cluster/afr",
	"cluster/dht",
	"cluster/distribute",
	"cluster/replicate",
	"cluster/stripe",
	"debug/io-stats",
	"features/access-control",
	"features/locks",
	"features/marker",
	"features/uidmap",
	"performance/io-threads",
	"protocol/client",
	"protocol/server",
	"storage/posix",
]
def copy_stack (old_xl,suffix,recursive=False):
	if recursive:
		new_name = old_xl.name + "-" + suffix
	else:
		new_name = suffix
	new_xl = Translator(new_name)
	new_xl.type = old_xl.type
	# The results with normal assignment here are . . . amusing.
	new_xl.opts = copy.deepcopy(old_xl.opts)
	for sv in old_xl.subvols:
		new_xl.subvols.append(copy_stack(sv,suffix,True))
	# Patch up the path at the bottom.
	if new_xl.type == "storage/posix":
		new_xl.opts["directory"] += ("/" + suffix)
	return new_xl
def cleanup (parent, graph):
	if parent.type in good_xlators:
		# Temporary fix so that HekaFS volumes can use the
		# SSL-enabled multi-threaded socket transport.
		if parent.type == "protocol/server":
			parent.type = "protocol/server2"
			parent.opts["transport-type"] = "ssl"
		elif parent.type == "protocol/client":
			parent.type = "protocol/client2"
			parent.opts["transport-type"] = "ssl"
		sv = []
		for child in parent.subvols:
			sv.append(cleanup(child,graph))
		parent.subvols = sv
	else:
		parent = cleanup(parent.subvols[0],graph)
	return parent
class Translator:
	def __init__ (self, name):
		self.name = name
		self.type = ""
		self.opts = {}
		self.subvols = []
		self.dumped = False
	def __repr__ (self):
		return "" % self.name
def load (path):
	# If it's a string, open it; otherwise, assume it's already a
	# file-like object (most notably from urllib*).
	if type(path) in types.StringTypes:
		fp = file(path,"r")
	else:
		fp = path
	all_xlators = {}
	xlator = None
	last_xlator = None
	while True:
		text = fp.readline()
		if text == "":
			break
		text = text.split()
		if not len(text):
			continue
		if text[0] == "volume":
			if xlator:
				raise RuntimeError, "nested volume definition"
			xlator = Translator(text[1])
			continue
		if not xlator:
			raise RuntimeError, "text outside volume definition"
		if text[0] == "type":
			xlator.type = text[1]
			continue
		if text[0] == "option":
			xlator.opts[text[1]] = string.join(text[2:])
			continue
		if text[0] == "subvolumes":
			for sv in text[1:]:
				xlator.subvols.append(all_xlators[sv])
			continue
		if text[0] == "end-volume":
			all_xlators[xlator.name] = xlator
			last_xlator = xlator
			xlator = None
			continue
		raise RuntimeError, "unrecognized keyword %s" % text[0]
	if xlator:
		raise RuntimeError, "unclosed volume definition"
	return all_xlators, last_xlator
def generate (graph, last, stream=sys.stdout):
	for sv in last.subvols:
		if not sv.dumped:
			generate(graph,sv,stream)
			print >> stream, ""
			sv.dumped = True
	print >> stream, "volume %s" % last.name
	print >> stream, "    type %s" % last.type
	for k, v in last.opts.iteritems():
		print >> stream, "    option %s %s" % (k, v)
	if last.subvols:
		print >> stream, "    subvolumes %s" % string.join(
			[ sv.name for sv in last.subvols ])
	print >> stream, "end-volume"
def push_filter (graph, old_xl, filt_type, opts={}):
	suffix = "-" + old_xl.type.split("/")[1]
	if len(old_xl.name) > len(suffix):
		if old_xl.name[-len(suffix):] == suffix:
			old_xl.name = old_xl.name[:-len(suffix)]
	new_xl = Translator(old_xl.name+suffix)
	new_xl.type = old_xl.type
	new_xl.opts = old_xl.opts
	new_xl.subvols = old_xl.subvols
	graph[new_xl.name] = new_xl
	old_xl.name += ("-" + filt_type.split("/")[1])
	old_xl.type = filt_type
	old_xl.opts = opts
	old_xl.subvols = [new_xl]
	graph[old_xl.name] = old_xl
def delete (graph, victim):
	if len(victim.subvols) != 1:
		raise RuntimeError, "attempt to delete non-unary translator"
	for xl in graph.itervalues():
		while xl.subvols.count(victim):
			i = xl.subvols.index(victim)
			xl.subvols[i] = victim.subvols[0]
if __name__ == "__main__":
	graph, last = load(sys.argv[1])
	generate(graph,last)