RackConnect v3.0 load balancer add/remove steps

This adds `IStep` implementations to `otter.convergence` for adding nodes to and removing them from RackConnect v3.0.

Furthermore, it generalized the optimizer step that @radix added to easily be extensible for multiple classes. Maybe one day we'll have to do multi-pass optimization; today is not that day :smile:

There are some `REVIEW` comments inline.

RCv3 supports bulk add/delete steps that allow adding or deleting any combination of nodes and servers in a single request, e.g. the following server, LB pairs can all be added and removed within a single request:

```clojure
[[:a :A]
 [:a :B]
 [:b :B]
 [:c :B]
 [:d :D]]
```

It stands to reason we'd want to use those requests, so that's what the optimizer produces. However, CLB does not support the same "n x n" API. In order to make this future-proof, I've added steps that mimic CLB's API. That way, the convergence code that produces load balancer steps doesn't have to care an awful lot whether it's working with RCv3 or CLB. The optimizer step is repsonsible for turning the intent specified by those steps into the most optimal representation as HTTP requests.
Yep, that's true. That's where I started sketching, actually; I added the "fake API" `IStep` so that the converge function would have to care as little as possible about the actual LB implementation. (If that still sounds like a poor trade-off, I'd be happy to remove it regardless :))

For now, the situation in this comment doesn't actually happen, because we only ever produce "singular" steps and then compile them down to one do-everything-multi-step.