- include SMB drop-ins from /etc/samba/smb.conf.d into the generated smb.conf - keep smbd/nfs-server running while other services (/etc/exports.d, drop-ins) share, instead of disabling them when nasmon has no shares of its own - list those shares read-only on /shares - settings: RepoISO URL; poll its /api/integration/status, dashboard card, menu link, alerts for portal down, stale worker, failed jobs, unmounted publications Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package shares
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Other services on the host (e.g. RepoISO) publish their own shares through
|
||||
// drop-in files. nasmon includes the SMB drop-ins into the smb.conf it
|
||||
// generates and keeps smbd / nfs-server running while any of them define a
|
||||
// share, even when nasmon itself has none.
|
||||
const (
|
||||
DefaultSMBIncludeDir = "/etc/samba/smb.conf.d"
|
||||
DefaultExportsDir = "/etc/exports.d"
|
||||
)
|
||||
|
||||
// ExternalShare is a share defined outside nasmon (read-only in the UI).
|
||||
type ExternalShare struct {
|
||||
Kind string // smb | nfs
|
||||
Source string // drop-in file name without extension, e.g. "repoiso"
|
||||
Name string // SMB share name (empty for NFS)
|
||||
Path string
|
||||
Clients string // NFS clients with options, as written in the file
|
||||
}
|
||||
|
||||
// IncludeFiles lists the *.conf drop-ins in dir, sorted. A missing dir yields nil.
|
||||
func IncludeFiles(dir string) []string {
|
||||
if dir == "" {
|
||||
return nil
|
||||
}
|
||||
m, _ := filepath.Glob(filepath.Join(dir, "*.conf"))
|
||||
sort.Strings(m)
|
||||
return m
|
||||
}
|
||||
|
||||
func sourceName(file string) string {
|
||||
return strings.TrimSuffix(filepath.Base(file), filepath.Ext(file))
|
||||
}
|
||||
|
||||
// ParseSMBDropIn returns the shares ([section] other than [global], [homes],
|
||||
// [printers]) defined in a Samba config fragment.
|
||||
func ParseSMBDropIn(text, source string) []ExternalShare {
|
||||
var out []ExternalShare
|
||||
var cur *ExternalShare
|
||||
for _, raw := range strings.Split(text, "\n") {
|
||||
line := strings.TrimSpace(raw)
|
||||
if line == "" || line[0] == '#' || line[0] == ';' {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
name := strings.TrimSpace(line[1 : len(line)-1])
|
||||
switch strings.ToLower(name) {
|
||||
case "global", "homes", "printers":
|
||||
cur = nil
|
||||
default:
|
||||
out = append(out, ExternalShare{Kind: "smb", Source: source, Name: name})
|
||||
cur = &out[len(out)-1]
|
||||
}
|
||||
continue
|
||||
}
|
||||
if cur == nil {
|
||||
continue
|
||||
}
|
||||
if k, v, ok := strings.Cut(line, "="); ok && strings.EqualFold(strings.TrimSpace(k), "path") {
|
||||
cur.Path = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ParseExportsFile returns the exports in an exports(5) file.
|
||||
func ParseExportsFile(text, source string) []ExternalShare {
|
||||
var out []ExternalShare
|
||||
for _, raw := range strings.Split(text, "\n") {
|
||||
line := strings.TrimSpace(raw)
|
||||
if line == "" || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
var path, rest string
|
||||
if line[0] == '"' {
|
||||
if end := strings.Index(line[1:], `"`); end >= 0 {
|
||||
path, rest = line[1:end+1], line[end+2:]
|
||||
}
|
||||
} else {
|
||||
path, rest, _ = strings.Cut(line, " ")
|
||||
}
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, ExternalShare{Kind: "nfs", Source: source, Path: path, Clients: strings.TrimSpace(rest)})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// External reads the SMB drop-ins and the exports.d files of other services.
|
||||
func (m *Manager) External() []ExternalShare {
|
||||
var out []ExternalShare
|
||||
for _, f := range IncludeFiles(m.IncludeDir) {
|
||||
if b, err := os.ReadFile(f); err == nil {
|
||||
out = append(out, ParseSMBDropIn(string(b), sourceName(f))...)
|
||||
}
|
||||
}
|
||||
if m.ExportsDir != "" {
|
||||
files, _ := filepath.Glob(filepath.Join(m.ExportsDir, "*.exports"))
|
||||
sort.Strings(files)
|
||||
for _, f := range files {
|
||||
if filepath.Clean(f) == filepath.Clean(m.Exports) {
|
||||
continue
|
||||
}
|
||||
if b, err := os.ReadFile(f); err == nil {
|
||||
out = append(out, ParseExportsFile(string(b), sourceName(f))...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func countKind(ext []ExternalShare, kind string) int {
|
||||
n := 0
|
||||
for _, e := range ext {
|
||||
if e.Kind == kind {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
Reference in New Issue
Block a user