Skip to content

Commit

Permalink
Ability to hot-attach NICs to preprovisioned VMs before reprovisioning (
Browse files Browse the repository at this point in the history
#613)

Adds the ability to run the Azure preprovisioned VMs as NIC-less and
then hot-attach them when assigned for reprovision.

The NIC on the preprovisioned VM is hot-detached as soon as it reports
ready and goes into wait for one or more interfaces to be hot-attached.
Once they are attached, cloud-init gets the expected number of NICs (in
case there are more than one) that will be attached from IMDS and waits
until all of them are attached. After all the NICs are attached,
reprovision proceeds as usual.
  • Loading branch information
aswinrajamannar committed Nov 23, 2020
1 parent 66b4be8 commit a4d0feb
Show file tree
Hide file tree
Showing 7 changed files with 1,109 additions and 65 deletions.
15 changes: 15 additions & 0 deletions cloudinit/distros/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os

from cloudinit import subp
from cloudinit import net, util


Expand Down Expand Up @@ -175,6 +176,10 @@ def wait_for_physdevs(
if strict:
raise RuntimeError(msg)

@abc.abstractmethod
def try_set_link_up(self, devname: DeviceName) -> bool:
"""Try setting the link to up explicitly and return if it is up."""


class BSDNetworking(Networking):
"""Implementation of networking functionality shared across BSDs."""
Expand All @@ -185,6 +190,9 @@ def is_physical(self, devname: DeviceName) -> bool:
def settle(self, *, exists=None) -> None:
"""BSD has no equivalent to `udevadm settle`; noop."""

def try_set_link_up(self, devname: DeviceName) -> bool:
raise NotImplementedError()


class LinuxNetworking(Networking):
"""Implementation of networking functionality common to Linux distros."""
Expand Down Expand Up @@ -214,3 +222,10 @@ def settle(self, *, exists=None) -> None:
if exists is not None:
exists = net.sys_dev_path(exists)
util.udevadm_settle(exists=exists)

def try_set_link_up(self, devname: DeviceName) -> bool:
"""Try setting the link to up explicitly and return if it is up.
Not guaranteed to bring the interface up. The caller is expected to
add wait times before retrying."""
subp.subp(['ip', 'link', 'set', devname, 'up'])
return self.is_up(devname)
31 changes: 31 additions & 0 deletions cloudinit/distros/tests/test_networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ def is_physical(self, *args, **kwargs):
def settle(self, *args, **kwargs):
raise NotImplementedError

def try_set_link_up(self, *args, **kwargs):
raise NotImplementedError

error = AssertionError("Unexpectedly used /sys in generic networking code")
with mock.patch(
"cloudinit.net.get_sys_class_path", side_effect=error,
Expand Down Expand Up @@ -74,6 +77,34 @@ def test_returns_true_if_device_is_physical(self, sys_class_net):
assert LinuxNetworking().is_physical(devname)


class TestBSDNetworkingTrySetLinkUp:
def test_raises_notimplementederror(self):
with pytest.raises(NotImplementedError):
BSDNetworking().try_set_link_up("eth0")


@mock.patch("cloudinit.net.is_up")
@mock.patch("cloudinit.distros.networking.subp.subp")
class TestLinuxNetworkingTrySetLinkUp:
def test_calls_subp_return_true(self, m_subp, m_is_up):
devname = "eth0"
m_is_up.return_value = True
is_success = LinuxNetworking().try_set_link_up(devname)

assert (mock.call(['ip', 'link', 'set', devname, 'up']) ==
m_subp.call_args_list[-1])
assert is_success

def test_calls_subp_return_false(self, m_subp, m_is_up):
devname = "eth0"
m_is_up.return_value = False
is_success = LinuxNetworking().try_set_link_up(devname)

assert (mock.call(['ip', 'link', 'set', devname, 'up']) ==
m_subp.call_args_list[-1])
assert not is_success


class TestBSDNetworkingSettle:
def test_settle_doesnt_error(self):
# This also implicitly tests that it doesn't use subp.subp
Expand Down
Loading

0 comments on commit a4d0feb

Please sign in to comment.