canonical/pycloudlib

LXD's `ip` method fails if instance has multiple NICs

TheRealFalcon opened this issue · 4 comments

My lxc list output has something like:

+-------------------------------+---------+---------------------+-----------------------------------------------+-----------------+-----------+
| cloudinit-1017-040803ieevd0hh | RUNNING | 10.20.30.99 (eth2)  | fd42:640c:ab08:2325:216:3eff:fe9f:385d (eth2) | CONTAINER       | 0         |
|                               |         | 10.20.30.218 (eth0) | fd42:640c:ab08:2325:216:3eff:fe3c:2dc9 (eth0) |                 |           |
+-------------------------------+---------+---------------------+-----------------------------------------------+-----------------+-----------+

The ip method doesn't understand this and raises exception after timing out.

Is the exception happening at the assignment?

Testing locally I see:

arc~ lxc list me -c4 --format csv
"10.161.80.42 (eth0)
10.161.80.127 (eth0)"

and

>>> ips = """10.161.80.42 (eth0)
... 10.161.80.127 (eth0)"""
>>> ips.split()
['10.161.80.42', '(eth0)', '10.161.80.127', '(eth0)']
>>> ip_address, _dev = ips.split()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

If that's the case, this cheeky one-character fix could work:

  diff --git a/pycloudlib/lxd/instance.py b/pycloudlib/lxd/instance.py
  index 41f18b8..8fcc30a 100644
  --- a/pycloudlib/lxd/instance.py
  +++ b/pycloudlib/lxd/instance.py
  @@ -120,7 +120,7 @@ class LXDInstance(BaseInstance):
                   ip_address = None
                   try:
                       # Expect "<ip> (<interface>)" when network fully configured
  -                    ip_address, _dev = result.stdout.split()
  +                    ip_address, *_dev = result.stdout.split()
                   except ValueError:
                       self._log.debug(
                           "Unable to parse output of cmd: %s. Expected"

Probably...I didn't really look into it all. I just wanted an issue to reference in my cloud-init workaround 😄 .

Looks like what you found can work, but it'd be semantically wrong. In your example, after assignment we'd have:

ip_address = '10.161.80.42'
dev = ['(eth0)', '10.161.80.127', '(eth0)']

which will return the correct IP, but the contents of dev won't make any sense. Shouldn't be too hard to fix though.

I'm hitting this in my own tests, I'll propose a solution. My previous suggestion of using the first IP wouldn't work since it looks like ordering is not consistent:

| cloudinit-1019-175813zv91ry49 | RUNNING | 10.188.195.1 (lxdbr0)  | fd42:e1f1:c9e1:6604::1 (lxdbr0)                 | VIRTUAL-MACHINE | 0         |
|                               |         | 10.161.80.155 (enp5s0) | fd42:80e2:4695:1e96:216:3eff:feb2:76b5 (enp5s0) |                 |           |
+-------------------------------+---------+------------------------+-------------------------------------------------+-----------------+-----------+
| cloudinit-1019-181007i7li02zg | RUNNING | 10.161.80.191 (enp5s0) | fd42:f242:64d5:b17e::1 (lxdbr0)                 | VIRTUAL-MACHINE | 0         |
|                               |         | 10.149.174.1 (lxdbr0)  | fd42:80e2:4695:1e96:216:3eff:fe26:6a11 (enp5s0) |                 |           |
+-------------------------------+---------+------------------------+-------------------------------------------------+-----------------+-----------+
| cloudinit-1019-182718xu5ks3ge | RUNNING | 10.217.237.1 (lxdbr0)  | fd42:80e2:4695:1e96:216:3eff:fe4c:f4d5 (enp5s0) | VIRTUAL-MACHINE | 0         |
|                               |         | 10.161.80.66 (enp5s0)  | fd42:1720:cc61:c13e::1 (lxdbr0)                 |                 |           |

Fixed in #221