Create an execution plan for the given logical pipeline. This is used for batch pipelines.
Though it may eventually be useful to mark windowing points for realtime pipelines.
A plan consists of one or more phases, with connections between phases.
A connection between a phase indicates control flow, and not necessarily
data flow. This class assumes that it receives a valid pipeline spec.
That is, the pipeline has no cycles, all its nodes have unique names,
sources don't have any input, sinks don't have any output,
everything else has both an input and an output, etc.
We start by inserting connector nodes into the logical dag,
which are used to mark boundaries between mapreduce jobs.
Each connector represents a node where we will need to write to a local dataset.
Next, the logical pipeline is broken up into phases,
using the connectors as sinks in one phase, and a source in another.
After this point, connections between phases do not indicate data flow, but control flow.
spec - the pipeline spec, representing a logical pipeline