Hi Christian,
First, do note you can always fall back to Fabric 1 for a while if Fabric 2 isn't yet up to snuff for your particular use case. We're still feeling out some of the APIs in terms of ease of use. Unfortunately, Group is one of those - it's real basic right now.
I just looked and there's no great way to set global connect_kwargs in such an object, without creating your own Connections to hand to its alternate constructor 'from_connections()':
```
hosts = ['switch1', 'switch2', 'switch3']
connections = [Connection(host=host, user='user', connect_kwargs={'password': 'pw'}) for host in hosts]
switch_group = group.SerialGroup.from_connections(connections)
@task
def inventory(c):
switch_group.run('show chassis hardware')
```
That's not the end of the world, but it's not ideal, so I just made
https://github.com/fabric/fabric/issues/1831 and then (since I was thinking about it and it was very easy for once) implemented it. It'll be out in the next feature release!
The rest of your issues are more high level, so here's a bit of a wall of text (sorry!):
Version 2 is much less implicit/magic than v1 – all objects need to fit together in obvious, Pythonic ways (such as being handed to one another in constructors or method calls). For example, context managers don't change any object besides the one that they yield, so trying to do things to switch_group inside a context manager about some other Connection object won't do anything. (This is as opposed to e.g. 'with settings()' in v1, which I assume you were thinking of; it, like the rest of that API, is all about magically twiddling data behind the scenes.)
Connections can't be created without some 'host' argument, which I think is triggering your TypeError. This ought to be in its __init__ docs, FWIW :) See the code example above; you either need to make your Group from explicitly-created Connections, where a host arg is being given, or (after the next release) you can tell the Group's constructor about connect_kwargs.
Which brings us to that context argument in the task signature: that's how `fab` explicitly transmits CLI and config info to your tasks, including the connection password. However, this introduces some ordering problems with your code - you need to pass that context's config into your Group or Connections.
Should be easily solved by moving away from module-level code exec (which is honestly an antipattern anyways). Based on my above snippet:
```
hosts = ['switch1', 'switch2', 'switch3']
def make_group(c):
connections = [
Connection(host=host, user='user', config=c.config)
for host in hosts
]
return group.SerialGroup.from_connections(connections)
@task
def inventory(c):
switch_group = make_group(c)
switch_group.run('show chassis hardware')
```
Or, after 2.3 comes out, you could arguably nix make_group() again, though hopefully you can see the various opportunities for refactoring, given that everything is explicit:
```
hosts = ['switch1', 'switch2', 'switch3']
@task
def inventory(c):
switch_group = SerialGroup(*hosts, config=c.config)
switch_group.run('show chassis hardware')
```
Finally: if you're thinking "ok, but it'd be so nice to just plop my switch hostnames and the fact that I want password prompting all the time, into a config file and call it done": that sort of thing is definitely in the pipeline! As I said up top, we're still hashing out a lot of the 'convenience' angles here, even though the core APIs are mostly in good shape.
Hope that all helps,
Jeff