summaryrefslogtreecommitdiff
path: root/cloudinit/net/activators.py
diff options
context:
space:
mode:
authorJames Falcon <therealfalcon@gmail.com>2021-07-19 14:13:21 -0500
committerGitHub <noreply@github.com>2021-07-19 14:13:21 -0500
commit184c836a16e9954a2cba11ae21f07923077ec904 (patch)
tree6289d70e4f833d300a25136dde6a56fcd1b0a0dc /cloudinit/net/activators.py
parenteacb0353803263934aa2ac827c37e461c87cb107 (diff)
downloadvyos-cloud-init-184c836a16e9954a2cba11ae21f07923077ec904.tar.gz
vyos-cloud-init-184c836a16e9954a2cba11ae21f07923077ec904.zip
Initial hotplug support (#936)
Adds a udev script which will invoke a hotplug hook script on all net add events. The script will write some udev arguments to a systemd FIFO socket (to ensure we have only instance of cloud-init running at a time), which is then read by a new service that calls a new 'cloud-init devel hotplug-hook' command to handle the new event. This hotplug-hook command will: - Fetch the pickled datsource - Verify that the hotplug event is supported/enabled - Update the metadata for the datasource - Ensure the hotplugged device exists within the datasource - Apply the config change on the datasource metadata - Bring up the new interface (or apply global network configuration) - Save the updated metadata back to the pickle cache Also scattered in some unrelated typing where helpful
Diffstat (limited to 'cloudinit/net/activators.py')
-rw-r--r--cloudinit/net/activators.py174
1 files changed, 135 insertions, 39 deletions
diff --git a/cloudinit/net/activators.py b/cloudinit/net/activators.py
index 34fee3bf..84aaafc9 100644
--- a/cloudinit/net/activators.py
+++ b/cloudinit/net/activators.py
@@ -15,31 +15,80 @@ from cloudinit.net.sysconfig import NM_CFG_FILE
LOG = logging.getLogger(__name__)
+def _alter_interface(cmd, device_name) -> bool:
+ LOG.debug("Attempting command %s for device %s", cmd, device_name)
+ try:
+ (_out, err) = subp.subp(cmd)
+ if len(err):
+ LOG.warning("Running %s resulted in stderr output: %s",
+ cmd, err)
+ return True
+ except subp.ProcessExecutionError:
+ util.logexc(LOG, "Running interface command %s failed", cmd)
+ return False
+
+
class NetworkActivator(ABC):
@staticmethod
@abstractmethod
def available() -> bool:
+ """Return True if activator is available, otherwise return False."""
raise NotImplementedError()
@staticmethod
@abstractmethod
def bring_up_interface(device_name: str) -> bool:
+ """Bring up interface.
+
+ Return True is successful, otherwise return False
+ """
+ raise NotImplementedError()
+
+ @staticmethod
+ @abstractmethod
+ def bring_down_interface(device_name: str) -> bool:
+ """Bring down interface.
+
+ Return True is successful, otherwise return False
+ """
raise NotImplementedError()
@classmethod
def bring_up_interfaces(cls, device_names: Iterable[str]) -> bool:
- all_succeeded = True
- for device in device_names:
- if not cls.bring_up_interface(device):
- all_succeeded = False
- return all_succeeded
+ """Bring up specified list of interfaces.
+
+ Return True is successful, otherwise return False
+ """
+ return all(cls.bring_up_interface(device) for device in device_names)
@classmethod
def bring_up_all_interfaces(cls, network_state: NetworkState) -> bool:
+ """Bring up all interfaces.
+
+ Return True is successful, otherwise return False
+ """
return cls.bring_up_interfaces(
[i['name'] for i in network_state.iter_interfaces()]
)
+ @classmethod
+ def bring_down_interfaces(cls, device_names: Iterable[str]) -> bool:
+ """Bring down specified list of interfaces.
+
+ Return True is successful, otherwise return False
+ """
+ return all(cls.bring_down_interface(device) for device in device_names)
+
+ @classmethod
+ def bring_down_all_interfaces(cls, network_state: NetworkState) -> bool:
+ """Bring down all interfaces.
+
+ Return True is successful, otherwise return False
+ """
+ return cls.bring_down_interfaces(
+ [i['name'] for i in network_state.iter_interfaces()]
+ )
+
class IfUpDownActivator(NetworkActivator):
# Note that we're not overriding bring_up_interfaces to pass something
@@ -53,24 +102,27 @@ class IfUpDownActivator(NetworkActivator):
@staticmethod
def bring_up_interface(device_name: str) -> bool:
- """Bring up interface using ifup."""
+ """Bring up interface using ifup.
+
+ Return True is successful, otherwise return False
+ """
cmd = ['ifup', device_name]
- LOG.debug("Attempting to run bring up interface %s using command %s",
- device_name, cmd)
- try:
- (_out, err) = subp.subp(cmd)
- if len(err):
- LOG.warning("Running %s resulted in stderr output: %s",
- cmd, err)
- return True
- except subp.ProcessExecutionError:
- util.logexc(LOG, "Running interface command %s failed", cmd)
- return False
+ return _alter_interface(cmd, device_name)
+
+ @staticmethod
+ def bring_down_interface(device_name: str) -> bool:
+ """Bring up interface using ifup.
+
+ Return True is successful, otherwise return False
+ """
+ cmd = ['ifdown', device_name]
+ return _alter_interface(cmd, device_name)
class NetworkManagerActivator(NetworkActivator):
@staticmethod
def available(target=None) -> bool:
+ """ Return true if network manager can be used on this system."""
config_present = os.path.isfile(
subp.target_path(target, path=NM_CFG_FILE)
)
@@ -79,44 +131,86 @@ class NetworkManagerActivator(NetworkActivator):
@staticmethod
def bring_up_interface(device_name: str) -> bool:
- try:
- subp.subp(['nmcli', 'connection', 'up', device_name])
- except subp.ProcessExecutionError:
- util.logexc(LOG, "nmcli failed to bring up {}".format(device_name))
- return False
- return True
+ """Bring up interface using nmcli.
+
+ Return True is successful, otherwise return False
+ """
+ cmd = ['nmcli', 'connection', 'up', 'ifname', device_name]
+ return _alter_interface(cmd, device_name)
+
+ @staticmethod
+ def bring_down_interface(device_name: str) -> bool:
+ """Bring down interface using nmcli.
+
+ Return True is successful, otherwise return False
+ """
+ cmd = ['nmcli', 'connection', 'down', device_name]
+ return _alter_interface(cmd, device_name)
class NetplanActivator(NetworkActivator):
+ NETPLAN_CMD = ['netplan', 'apply']
+
@staticmethod
def available(target=None) -> bool:
+ """ Return true if netplan can be used on this system."""
return netplan_available(target=target)
@staticmethod
- def _apply_netplan():
- LOG.debug('Applying current netplan config')
- try:
- subp.subp(['netplan', 'apply'], capture=True)
- except subp.ProcessExecutionError:
- util.logexc(LOG, "netplan apply failed")
- return False
- return True
-
- @staticmethod
def bring_up_interface(device_name: str) -> bool:
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
LOG.debug("Calling 'netplan apply' rather than "
- "bringing up individual interfaces")
- return NetplanActivator._apply_netplan()
+ "altering individual interfaces")
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
@staticmethod
def bring_up_interfaces(device_names: Iterable[str]) -> bool:
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
LOG.debug("Calling 'netplan apply' rather than "
- "bringing up individual interfaces")
- return NetplanActivator._apply_netplan()
+ "altering individual interfaces")
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
@staticmethod
def bring_up_all_interfaces(network_state: NetworkState) -> bool:
- return NetplanActivator._apply_netplan()
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
+
+ @staticmethod
+ def bring_down_interface(device_name: str) -> bool:
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
+ LOG.debug("Calling 'netplan apply' rather than "
+ "altering individual interfaces")
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
+
+ @staticmethod
+ def bring_down_interfaces(device_names: Iterable[str]) -> bool:
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
+ LOG.debug("Calling 'netplan apply' rather than "
+ "altering individual interfaces")
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
+
+ @staticmethod
+ def bring_down_all_interfaces(network_state: NetworkState) -> bool:
+ """Apply netplan config.
+
+ Return True is successful, otherwise return False
+ """
+ return _alter_interface(NetplanActivator.NETPLAN_CMD, 'all')
# This section is mostly copied and pasted from renderers.py. An abstract
@@ -153,4 +247,6 @@ def select_activator(priority=None, target=None) -> Type[NetworkActivator]:
raise RuntimeError(
"No available network activators found%s. Searched "
"through list: %s" % (tmsg, priority))
- return found[0]
+ selected = found[0]
+ LOG.debug('Using selected activator: %s', selected)
+ return selected