summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorAlberto Contreras <alberto.contreras@canonical.com>2023-04-27 21:11:07 +0200
committerGitHub <noreply@github.com>2023-04-27 14:11:07 -0500
commitaa0cd62c81866d632522bbc54dc03eb4fa7fd913 (patch)
tree9e861a7841de93b125ea325dc6043cd62fc40b0f /cloudinit
parentbe3441b217949f608cf1cba677e3484ba49f7e7b (diff)
downloadcloud-init-git-aa0cd62c81866d632522bbc54dc03eb4fa7fd913.tar.gz
gce: activate network discovery on every boot (#2128)
Google wants to allow users to make changes on nics while the instance is stopped. Activate network discovery on every boot. Additionally, skip the call to `netplan generate` if the rendered config is the same on subsequent boots.
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/net/netplan.py28
-rw-r--r--cloudinit/net/renderer.py2
-rw-r--r--cloudinit/sources/DataSourceGCE.py7
-rw-r--r--cloudinit/util.py14
4 files changed, 44 insertions, 7 deletions
diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
index 1c28e16e..8b52a641 100644
--- a/cloudinit/net/netplan.py
+++ b/cloudinit/net/netplan.py
@@ -1,6 +1,7 @@
# This file is part of cloud-init. See LICENSE file ...
import copy
+import io
import ipaddress
import os
import textwrap
@@ -277,31 +278,48 @@ class Renderer(renderer.Renderer):
fpnplan = os.path.join(subp.target_path(target), self.netplan_path)
util.ensure_dir(os.path.dirname(fpnplan))
- header = self.netplan_header if self.netplan_header else ""
# render from state
content = self._render_content(network_state)
+ # normalize header
+ header = self.netplan_header if self.netplan_header else ""
if not header.endswith("\n"):
header += "\n"
+ content = header + content
- mode = 0o600 if features.NETPLAN_CONFIG_ROOT_READ_ONLY else 0o644
+ # determine if existing config files have the same content
+ same_content = False
if os.path.exists(fpnplan):
+ hashed_content = util.hash_buffer(io.BytesIO(content.encode()))
+ with open(fpnplan, "rb") as f:
+ hashed_original_content = util.hash_buffer(f)
+ if hashed_content == hashed_original_content:
+ same_content = True
+
+ mode = 0o600 if features.NETPLAN_CONFIG_ROOT_READ_ONLY else 0o644
+ if not same_content and os.path.exists(fpnplan):
current_mode = util.get_permissions(fpnplan)
if current_mode & mode == current_mode:
# preserve mode if existing perms are more strict than default
mode = current_mode
- util.write_file(fpnplan, header + content, mode=mode)
+ util.write_file(fpnplan, content, mode=mode)
if self.clean_default:
_clean_default(target=target)
- self._netplan_generate(run=self._postcmds)
+ self._netplan_generate(run=self._postcmds, same_content=same_content)
self._net_setup_link(run=self._postcmds)
- def _netplan_generate(self, run=False):
+ def _netplan_generate(self, run: bool = False, same_content: bool = False):
if not run:
LOG.debug("netplan generate postcmd disabled")
return
+ if same_content:
+ LOG.debug(
+ "skipping call to `netplan generate`."
+ " reason: identical netplan config"
+ )
+ return
subp.subp(self.NETPLAN_GENERATE, capture=True)
def _net_setup_link(self, run=False):
diff --git a/cloudinit/net/renderer.py b/cloudinit/net/renderer.py
index 72813e32..c429d068 100644
--- a/cloudinit/net/renderer.py
+++ b/cloudinit/net/renderer.py
@@ -24,7 +24,7 @@ def filter_by_attr(match_name):
filter_by_physical = filter_by_type("physical")
-class Renderer:
+class Renderer(abc.ABC):
def __init__(self, config=None):
pass
diff --git a/cloudinit/sources/DataSourceGCE.py b/cloudinit/sources/DataSourceGCE.py
index 041c8914..27d6089a 100644
--- a/cloudinit/sources/DataSourceGCE.py
+++ b/cloudinit/sources/DataSourceGCE.py
@@ -11,6 +11,7 @@ from cloudinit import dmi
from cloudinit import log as logging
from cloudinit import sources, url_helper, util
from cloudinit.distros import ug_util
+from cloudinit.event import EventScope, EventType
from cloudinit.net.ephemeral import EphemeralDHCPv4
from cloudinit.sources import DataSourceHostname
@@ -63,6 +64,12 @@ class DataSourceGCE(sources.DataSource):
dsname = "GCE"
perform_dhcp_setup = False
+ default_update_events = {
+ EventScope.NETWORK: {
+ EventType.BOOT_NEW_INSTANCE,
+ EventType.BOOT,
+ }
+ }
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
diff --git a/cloudinit/util.py b/cloudinit/util.py
index fc777b82..2eb79d33 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -1725,7 +1725,7 @@ def logexc(log, msg, *args):
log.debug(msg, exc_info=exc_info, *args)
-def hash_blob(blob, routine, mlen=None):
+def hash_blob(blob, routine: str, mlen=None) -> str:
hasher = hashlib.new(routine)
hasher.update(encode_text(blob))
digest = hasher.hexdigest()
@@ -1736,6 +1736,18 @@ def hash_blob(blob, routine, mlen=None):
return digest
+def hash_buffer(f: io.BufferedIOBase) -> bytes:
+ """Hash the content of a binary buffer using SHA1.
+
+ @param f: buffered binary stream to hash.
+ @return: digested data as bytes.
+ """
+ hasher = hashlib.sha1()
+ for chunk in iter(lambda: f.read(io.DEFAULT_BUFFER_SIZE), b""):
+ hasher.update(chunk)
+ return hasher.digest()
+
+
def is_user(name):
try:
if pwd.getpwnam(name):