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.
Nonblocking nitpick - I don't feel particularly strongly one way or another about there being a dummy singular step vs. not, but the difference in naming on the singular vs plural might be somewhat hard to spot/easy to confuse in tracebacks, and the CLB version does not have a singular version either.

Would it make sense for this to be named something like "BulkAddNodesToLoadBalancers"?