public RrdDef getRrdDef() throws RrdException {
		if (!root.getTagName().equals("rrd_def")) {
			throw new RrdException("XML definition must start with <rrd_def>");
		}
		validateTagsOnlyOnce(root, new String[] {
			"path", "start", "step", "datasource*", "archive*"
		});
		// PATH must be supplied or exception is thrown
		String path = getChildValue(root, "path");
		RrdDef rrdDef = new RrdDef(path);
		try {
			String startStr = getChildValue(root, "start");
			GregorianCalendar startGc = Util.getGregorianCalendar(startStr);
			rrdDef.setStartTime(startGc);
		} catch (RrdException e) {
			// START is not mandatory
		}
		try {
			long step = getChildValueAsLong(root, "step");
			rrdDef.setStep(step);
		} catch (RrdException e) {
			// STEP is not mandatory
		}
		// datsources
		Node[] dsNodes = getChildNodes(root, "datasource");
		for (int i = 0; i < dsNodes.length; i++) {
			validateTagsOnlyOnce(dsNodes[i], new String[] {
				"name", "type", "heartbeat", "min", "max"
			});
			String name = getChildValue(dsNodes[i], "name");
			String type = getChildValue(dsNodes[i], "type");
			long heartbeat = getChildValueAsLong(dsNodes[i], "heartbeat");
			double min = getChildValueAsDouble(dsNodes[i], "min");
			double max = getChildValueAsDouble(dsNodes[i], "max");
			rrdDef.addDatasource(name, type, heartbeat, min, max);
		}
		// archives
		Node[] arcNodes = getChildNodes(root, "archive");
		for (int i = 0; i < arcNodes.length; i++) {
			validateTagsOnlyOnce(arcNodes[i], new String[] {
				"cf", "xff", "steps", "rows"
			});
			String consolFun = getChildValue(arcNodes[i], "cf");
			double xff = getChildValueAsDouble(arcNodes[i], "xff");
			int steps = getChildValueAsInt(arcNodes[i], "steps");
			int rows = getChildValueAsInt(arcNodes[i], "rows");
			rrdDef.addArchive(consolFun, xff, steps, rows);
		}
		return rrdDef;
	}