Skip to content

Commit

Permalink
add: computer title generation for zero-touch deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
st3v3nmw committed Feb 7, 2024
1 parent 340e8fc commit c976763
Show file tree
Hide file tree
Showing 11 changed files with 522 additions and 10 deletions.
77 changes: 77 additions & 0 deletions landscape/client/deployment.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import json
import os.path
import subprocess
import sys
from datetime import datetime
from datetime import timezone
from optparse import SUPPRESS_HELP

from twisted.logger import globalLogBeginner

from landscape import VERSION
from landscape.client import DEFAULT_CONFIG
from landscape.client import snap_http
from landscape.client.snap_utils import get_snap_info
from landscape.client.upgraders import UPGRADE_MANAGERS
from landscape.lib import logging
from landscape.lib.config import BaseConfiguration as _BaseConfiguration
from landscape.lib.format import expandvars
from landscape.lib.network import get_active_device_info
from landscape.lib.network import get_fqdn
from landscape.lib.persist import Persist


Expand Down Expand Up @@ -189,6 +198,25 @@ def juju_filename(self):
backwards-compatibility."""
return os.path.join(self.data_path, "juju-info.json")

def auto_configure(self):
"""Automatically configure the client snap."""
client_conf = snap_http.get_conf("landscape-client").result
auto_enroll_conf = client_conf.get("auto-register", {})

enabled = auto_enroll_conf.get("enabled", False)
configured = auto_enroll_conf.get("configured", False)
if not enabled or configured:
return

title = generate_computer_title(auto_enroll_conf)
if title:
self.computer_title = title
self.write()

auto_enroll_conf["configured"] = True
client_conf["auto-register"] = auto_enroll_conf
snap_http.set_conf("landscape-client", client_conf)


def get_versioned_persist(service):
"""Get a L{Persist} database with upgrade rules applied.
Expand All @@ -204,3 +232,52 @@ def get_versioned_persist(service):
upgrade_manager.initialize(persist)
persist.save(service.persist_filename)
return persist


def generate_computer_title(auto_enroll_config):
"""Generate the computer title.
This follows the LA017 specification and falls back to `hostname`
if generating the title fails due to missing data.
"""
snap_info = get_snap_info()
wait_for_serial = auto_enroll_config.get("wait-for-serial-as", True)
if "serial" not in snap_info and wait_for_serial:
return

hostname = get_fqdn()
wait_for_hostname = auto_enroll_config.get("wait-for-hostname", False)
if hostname == "localhost" and wait_for_hostname:
return

nics = get_active_device_info(default_only=True)
nic = nics[0] if nics else {}

lshw = subprocess.run(
["lshw", "-json", "-quiet", "-c", "system"],
capture_output=True,
text=True,
)
hardware = json.loads(lshw.stdout)[0]

computer_title_pattern = auto_enroll_config.get(
"computer-title-pattern",
"${hostname}",
)
title = expandvars(
computer_title_pattern,
serial=snap_info.get("serial", ""),
model=snap_info.get("model", ""),
brand=snap_info.get("brand", ""),
hostname=hostname,
ip=nic.get("ip_address", ""),
mac=nic.get("mac_address", ""),
prodiden=hardware.get("product", ""),
serialno=hardware.get("serial", ""),
datetime=datetime.now(timezone.utc),
)

if title == "": # on the off-chance substitute values are missing
title = hostname

return title
13 changes: 6 additions & 7 deletions landscape/client/monitor/computerinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from twisted.internet.defer import returnValue

from landscape.client.monitor.plugin import MonitorPlugin
from landscape.client.snap_utils import get_assertions
from landscape.client.snap_utils import get_snap_info
from landscape.lib.cloud import fetch_ec2_meta_data
from landscape.lib.fetch import fetch_async
from landscape.lib.fs import read_text_file
Expand Down Expand Up @@ -221,10 +221,9 @@ def log_success(result):
def _create_snap_info_message(self):
"""Create message with the snapd serial metadata."""
message = {}
assertions = get_assertions("serial")
if assertions:
assertion = assertions[0]
self._add_if_new(message, "brand", assertion["brand-id"])
self._add_if_new(message, "model", assertion["model"])
self._add_if_new(message, "serial", assertion["serial"])
snap_info = get_snap_info()
if snap_info:
self._add_if_new(message, "brand", snap_info["brand"])
self._add_if_new(message, "model", snap_info["model"])
self._add_if_new(message, "serial", snap_info["serial"])
return message
4 changes: 2 additions & 2 deletions landscape/client/monitor/tests/test_computerinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ def test_fetch_ec2_meta_data_bad_result_retry(self):
result,
)

@mock.patch("landscape.client.monitor.computerinfo.get_assertions")
@mock.patch("landscape.client.snap_utils.get_assertions")
def test_snap_info(self, mock_get_assertions):
"""Test getting the snap info message."""
mock_get_assertions.return_value = [
Expand All @@ -586,7 +586,7 @@ def test_snap_info(self, mock_get_assertions):
"03961d5d-26e5-443f-838d-6db046126bea",
)

@mock.patch("landscape.client.monitor.computerinfo.get_assertions")
@mock.patch("landscape.client.snap_utils.get_assertions")
def test_snap_info_no_results(self, mock_get_assertions):
"""Test getting the snap info message when there are no results.
Expand Down
13 changes: 13 additions & 0 deletions landscape/client/snap_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,16 @@ def get_assertions(assertion_type: str):
assertions.append(assertion)

return assertions


def get_snap_info():
"""Get the snap device information."""
info = {}

serial_as = get_assertions("serial")
if serial_as:
info["serial"] = serial_as[0]["serial"]
info["model"] = serial_as[0]["model"]
info["brand"] = serial_as[0]["brand-id"]

return info
Loading

0 comments on commit c976763

Please sign in to comment.