From af03834e648f50aa915cab89b5756e67b6b89383 Mon Sep 17 00:00:00 2001 From: Gabriel Mougard Date: Wed, 23 Oct 2024 11:57:19 +0200 Subject: [PATCH] lxd/network/acl: If the OVN chassis is in MicroOVN, read the ACL logs from syslogs If the OVN controller is deployed as part of MicroOVN, it means that the `ovn-chassis` snap connection hook between LXD and MicroOVN has been fired and that a symlink like: `/run/openvswitch -> /var/snap/microovn/...` exists. Otherwise, there might still be a symlink but not with that target prefix. That's how we detect the ovn chassis connection without introducing a new plug / slot between LXD and MicroOVN. Then, if this case is detected, we check the status of the systemd unit 'snap.microovn.chassis.service' (the one containing the OVN controller in its sd journal) If it is loaded and active, we read the last 5000 ACL log entries (we can discuss on this limit) in the journal and return them. Signed-off-by: Gabriel Mougard --- lxd/network/acl/driver_common.go | 61 ++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/lxd/network/acl/driver_common.go b/lxd/network/acl/driver_common.go index 9d863a3c29a0..8c6727fa6627 100644 --- a/lxd/network/acl/driver_common.go +++ b/lxd/network/acl/driver_common.go @@ -752,32 +752,55 @@ func (d *common) Delete() error { func (d *common) GetLog(clientType request.ClientType) (string, error) { // ACLs aren't specific to a particular network type but the log only works with OVN. logPath := shared.HostPath("/var/log/ovn/ovn-controller.log") + logEntries := []string{} if !shared.PathExists(logPath) { - return "", fmt.Errorf("Only OVN log entries may be retrieved at this time") - } + // Check if this is OVN builtin of if OVN is provided by a MicroOVN deployment. + targetPath, err := os.Readlink("/run/openvswitch") + if err != nil { + return "", fmt.Errorf("Failed to read symlink while checking for OVN logs: %v\n", err) + } - // Open the log file. - logFile, err := os.Open(logPath) - if err != nil { - return "", fmt.Errorf("Couldn't open OVN log file: %w", err) - } + if !strings.HasPrefix(targetPath, "/var/snap/microovn") { + // This is a builtin OVN deployment (snap-based or not) but the log file doesn't exist. This is an error. + return "", fmt.Errorf("Only OVN log entries may be retrieved at this time") + } - defer func() { _ = logFile.Close() }() + // This means that the OVN controller is provided as part of a MicroOVN deployment. + // (the 'ovn-chassis' snap interface is connected to the MicroOVN snap). + // In this case, we can't access the OVN log directly from a file because MicroOVN stores + // them in its systemd journal. LXD should have root access to the host so it should be able to read them. + err = checkSystemDUnitStatus("snap.microovn.chassis.service") + if err != nil { + return "", fmt.Errorf("Failed to check status of MicroOVN systemd unit: %v\n", err) + } - logEntries := []string{} - scanner := bufio.NewScanner(logFile) - for scanner.Scan() { - logEntry := ovnParseLogEntry(scanner.Text(), fmt.Sprintf("lxd_acl%d-", d.id)) - if logEntry == "" { - continue + logEntries, err = ovnParseLogEntriesFromSyslog(d.logger, "snap.microovn.chassis.service", fmt.Sprintf("lxd_acl%d-", d.id)) + if err != nil { + return "", fmt.Errorf("Failed to get OVN log entries from syslog: %v\n", err) + } + } else { + // Open the log file. + logFile, err := os.Open(logPath) + if err != nil { + return "", fmt.Errorf("Couldn't open OVN log file: %w", err) } - logEntries = append(logEntries, logEntry) - } + defer func() { _ = logFile.Close() }() - err = scanner.Err() - if err != nil { - return "", fmt.Errorf("Failed to read OVN log file: %w", err) + scanner := bufio.NewScanner(logFile) + for scanner.Scan() { + logEntry := ovnParseLogEntry(scanner.Text(), fmt.Sprintf("lxd_acl%d-", d.id)) + if logEntry == "" { + continue + } + + logEntries = append(logEntries, logEntry) + } + + err = scanner.Err() + if err != nil { + return "", fmt.Errorf("Failed to read OVN log file: %w", err) + } } // Aggregates the entries from the rest of the cluster.