Skip to content

Commit

Permalink
lxd/network/acl: Read OVN logs from systemd journal
Browse files Browse the repository at this point in the history
In the case of an OVN controller being deployed as part of a MicroOVN deployment,
the OVN controller logs are stored in MicroOVN's snap syslog. The LXD snap should have root access,
which means that it should be authorized (this is being tested) to read the OVN controller logs.

In the case where logs couldn't be read (e.g, AppArmor restrictions), we could try to connect the `log-observe` snap interface on LXD
and see if this works.

Signed-off-by: Gabriel Mougard <[email protected]>
  • Loading branch information
gabrielmougard committed Oct 23, 2024
1 parent 1abb2ef commit f78d704
Showing 1 changed file with 100 additions and 0 deletions.
100 changes: 100 additions & 0 deletions lxd/network/acl/acl_ovn.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/revert"
"github.com/canonical/lxd/shared/validate"
"github.com/coreos/go-systemd/v22/dbus"
"github.com/coreos/go-systemd/v22/sdjournal"
)

// OVN ACL rule priorities.
Expand Down Expand Up @@ -1131,3 +1133,101 @@ func ovnParseLogEntry(input string, prefix string) string {

return string(out)
}

func checkSystemDUnitStatus(unitName string) error {
ctx := context.Background()
conn, err := dbus.NewSystemConnectionContext(ctx)
if err != nil {
return fmt.Errorf("Failed to connect to systemd: %v", err)
}

defer conn.Close()
unitStatus, err := conn.GetUnitPropertiesContext(ctx, unitName)
if err != nil {
return fmt.Errorf("Failed to get unit properties: %v", err)
}

loadState, ok := unitStatus["LoadState"].(string)
if !ok || loadState != "loaded" {
return fmt.Errorf("Unit %q is not loaded (LoadState=%s)", unitName, loadState)
}

activeState, ok := unitStatus["ActiveState"].(string)
if !ok || activeState != "active" {
return fmt.Errorf("Unit %q is not active (ActiveState=%s)", unitName, activeState)
}

return nil
}

// ovnParseLogEntriesFromSyslog
func ovnParseLogEntriesFromSyslog(l logger.Logger, systemdUnitName string, prefix string) ([]string, error) {
var logEntries []string
j, err := sdjournal.NewJournal()
if err != nil {
return nil, fmt.Errorf("Failed to open a Systemd journal instance: %v", err)
}

defer j.Close()

match := sdjournal.Match{
Field: sdjournal.SD_JOURNAL_FIELD_SYSTEMD_UNIT,
Value: systemdUnitName,
}
err = j.AddMatch(match.String())
if err != nil {
return nil, fmt.Errorf("Failed to add match: %v", err)
}

err = j.SeekTail()
if err != nil {
return nil, fmt.Errorf("Failed to seek to journal tail: %v", err)
}

_, err = j.Previous()
if err != nil {
return nil, fmt.Errorf("Failed to move to previous entry: %v", err)
}

// Iterate over the journal entries backwards.
// For now, we output a maximum of 5000 entries.
count := 0
maxCount := 5000
for count < maxCount {
n, err := j.Previous()
if err != nil {
return nil, fmt.Errorf("Error reading journal: %v", err)
}

if n == 0 {
break // No more entries
}

entry, err := j.GetEntry()
if err != nil {
return nil, fmt.Errorf("Error getting journal entry: %v", err)
}

message, ok := entry.Fields["MESSAGE"]
if !ok {
l.Error("GABRIEL 1 - No message field in journal entry", logger.Ctx{"entry": entry})
continue
}

logEntry := ovnParseLogEntry(message, prefix)
if logEntry == "" {
l.Error("GABRIEL 2 - log entry empty", logger.Ctx{"message": message})
continue
}

l.Error("GABRIEL 3 - log entry", logger.Ctx{"logEntry": logEntry})
logEntries = append(logEntries, logEntry)
count++
}

for i, j := 0, len(logEntries)-1; i < j; i, j = i+1, j-1 {
logEntries[i], logEntries[j] = logEntries[j], logEntries[i]
}

return logEntries, nil
}

0 comments on commit f78d704

Please sign in to comment.