Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e2afff4
Add the vim coloration files
cvaroqui Apr 21, 2026
4de4df7
Better disk.xp8 driver `split_start` keyword doc
cvaroqui Apr 21, 2026
b42804b
Fix the ip.netns `mode` keyword example
cvaroqui Apr 21, 2026
de3a25f
Add a stringer to args.T
cvaroqui Apr 21, 2026
1cb875c
Add a StatusInfo() to oci container drivers
cvaroqui Apr 21, 2026
5a0292d
Add a wait for undefined res in the disk.drbd unprovision codepath
cvaroqui Apr 21, 2026
ccf3085
Honor pools mkblk_opt and disk.zvol create_options
cvaroqui Apr 21, 2026
1ba7dc6
Add refs highlighter to "cf show" and use the same colors as vim
cvaroqui Apr 21, 2026
831b69c
Fix vim colorization of the staged foreign config files
cvaroqui Apr 21, 2026
0e88932
Add colors the ox tui cluster, node and objects config renderers
cvaroqui Apr 22, 2026
93bb466
Add scope highlighting in configs
cvaroqui Apr 22, 2026
0407078
Rename "template" to "reference" in the highlighter
cvaroqui Apr 22, 2026
5eb0346
Support <volname>/<relpath> in app start, stop, check kws
cvaroqui Apr 22, 2026
46a2996
Add scope highlighting to options in the vim syntax file
cvaroqui Apr 22, 2026
847a443
Stricter condition to replace the volname in the app script path
cvaroqui Apr 22, 2026
49223a0
Fix the "app info" feature
cvaroqui Apr 22, 2026
b185ec6
Fix a container.oci stack on StatusInfo
cvaroqui Apr 22, 2026
c34a691
Ignore some errors of `{secrets,configs}_environment=<dsname>/*`
cvaroqui Apr 22, 2026
ea4813c
Better ip.netns mode doc
cvaroqui Apr 23, 2026
134916f
Verify t.executer is not nil before calling t.executer.Inspect(ctx)
cvaroqui Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 92 additions & 17 deletions core/commoncmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package commoncmd
import (
"bufio"
"bytes"
"fmt"
"regexp"
"strings"

Expand Down Expand Up @@ -92,6 +91,9 @@ func ColorizeINI(b []byte) []byte {
var continuedValue string
var continuation bool

// Compile regexes once
referenceRE := regexp.MustCompile(`\{[^{}]*\}`)

for scanner.Scan() {
line := scanner.Text()

Expand All @@ -109,35 +111,108 @@ func ColorizeINI(b []byte) []byte {

// Section header
if sectionRE.MatchString(line) {
color.Set(color.FgYellow).Fprintln(out, line)
color.Set(color.FgHiYellow, color.Bold).Fprintln(out, line)
continue
}

// Comment
if commentRE.MatchString(line) {
color.Set(color.FgHiBlack).Fprintln(out, line)
color.Set(color.FgHiBlack, color.Italic).Fprintln(out, line)
continue
}

// Key-value
if strings.Contains(line, "=") && !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
parts := strings.SplitN(line, "=", 2)
key := strings.TrimSpace(parts[0])
value := parts[1] // preserve spacing
color.Set(color.FgMagenta).Fprint(out, key)
out.WriteString(fmt.Sprintf(" =%s\n", value))

// Check if line continues
if strings.HasSuffix(strings.TrimRight(value, " \t"), `\`) {
continuedValue = ""
continuedValue += value
continuation = true
// Use regex to preserve spacing around equals sign
kvRE := regexp.MustCompile(`^(\s*[^=\s]+(?:\s+[^=\s]+)*)\s*=\s*(.*)$`)
matches := kvRE.FindStringSubmatch(line)
if len(matches) == 3 {
key := matches[1]
equalAndValue := matches[2]

// Colorize key
key, scope, scopeFound := strings.Cut(key, "@")
color.Set(color.FgCyan).Fprint(out, key)
if scopeFound {
color.Set(color.FgHiMagenta).Fprint(out, "@"+scope)
}

// Find the equals sign position to preserve exact spacing
equalPos := strings.Index(line, "=")
if equalPos >= 0 {
// Extract the equals sign with surrounding spaces
start := equalPos
end := equalPos + 1
// Include leading spaces
for start > 0 && line[start-1] == ' ' {
start--
}
// Include trailing spaces
for end < len(line) && line[end] == ' ' {
end++
}
equalSign := line[start:end]
color.Set(color.FgHiBlack).Fprint(out, equalSign)

// The rest is the value
value := line[end:]

// Highlight references in the value
referenceMatches := referenceRE.FindAllStringIndex(value, -1)
if len(referenceMatches) > 0 {
lastPos := 0
for _, match := range referenceMatches {
// Write non-reference part
out.WriteString(value[lastPos:match[0]])

// Write reference part in green + bold
referenceText := value[match[0]:match[1]]
color.Set(color.FgGreen, color.Bold).Fprint(out, referenceText)
lastPos = match[1]
}
// Write remaining part after last reference
out.WriteString(value[lastPos:])
} else {
// No references
out.WriteString(value)
}
} else {
// Fallback: output the rest as-is
out.WriteString(equalAndValue)
}
out.WriteString("\n")

// Check if line continues
if strings.HasSuffix(strings.TrimRight(line, " \t"), `\`) {
continuedValue = ""
continuedValue += line
continuation = true
}
continue
}
continue
}

// Unmatched line (output as-is)
out.WriteString(line + "\n")
// Unmatched line - check for references
referenceMatches := referenceRE.FindAllStringIndex(line, -1)
if len(referenceMatches) > 0 {
lastPos := 0
for _, match := range referenceMatches {
// Write non-reference part
out.WriteString(line[lastPos:match[0]])

// Write reference part in green + bold
referenceText := line[match[0]:match[1]]
color.Set(color.FgGreen, color.Bold).Fprint(out, referenceText)
lastPos = match[1]
}
// Write remaining part after last reference
out.WriteString(line[lastPos:])
out.WriteString("\n")
} else {
// Unmatched line (output as-is)
out.WriteString(line)
out.WriteString("\n")
}
}

return out.Bytes()
Expand Down
4 changes: 2 additions & 2 deletions core/object/core_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,13 @@ func (t *core) dereferenceVolumeHead(ref string) (string, error) {
var i any = t.config.Referrer
actor, ok := i.(Actor)
if !ok {
return ref, fmt.Errorf("can't dereference volume mnt on a non-actor object: %s", ref)
return ref, fmt.Errorf("can't dereference volume head on a non-actor object: %s", ref)
}
type header interface {
Head() string
}
if len(l) != 2 {
return ref, fmt.Errorf("misformatted volume mnt ref: %s", ref)
return ref, fmt.Errorf("misformatted volume head ref: %s", ref)
}
rid := l[0]
r := actor.ResourceByID(rid)
Expand Down
1 change: 1 addition & 0 deletions core/object/node_keywords.go
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,7 @@ var (
Text: keywords.NewText(fs, "text/kw/node/pool.mkfs_opt"),
}
kwNodePoolMkblkOpt = keywords.Keyword{
Example: "-b 16k",
Option: "mkblk_opt",
Section: "pool",
Text: keywords.NewText(fs, "text/kw/node/pool.mkblk_opt"),
Expand Down
2 changes: 1 addition & 1 deletion core/object/text/kw/node/pool.mkblk_opt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
The zvol, lv, and other block device creation command options to use to
prepare the pool devices.
prepare the pool volumes devices.
3 changes: 2 additions & 1 deletion core/omcmd/lib_remote_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/opensvc/om3/v3/core/client"
"github.com/opensvc/om3/v3/core/naming"
"github.com/opensvc/om3/v3/core/rawconfig"
)

func createTempRemoteConfig(p naming.Path, c *client.T) (string, error) {
Expand All @@ -19,7 +20,7 @@ func createTempRemoteConfig(p naming.Path, c *client.T) (string, error) {
if buff, err = fetchConfig(p, c); err != nil {
return "", err
}
if f, err = os.CreateTemp("", ".opensvc.remote.config.*"); err != nil {
if f, err = os.CreateTemp(rawconfig.Paths.Tmp, "remote.*.conf.tmp"); err != nil {
return "", err
}
filename := f.Name()
Expand Down
3 changes: 2 additions & 1 deletion core/oxcmd/lib_remote_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/opensvc/om3/v3/core/client"
"github.com/opensvc/om3/v3/core/naming"
"github.com/opensvc/om3/v3/core/rawconfig"
)

func createTempRemoteNodeConfig(nodename string, c *client.T) (string, error) {
Expand All @@ -27,7 +28,7 @@ func createTempRemoteObjectConfig(p naming.Path, c *client.T) (string, error) {
}

func createTempRemoteConfig(buff []byte) (string, error) {
f, err := os.CreateTemp("", ".opensvc.remote.config.*")
f, err := os.CreateTemp(rawconfig.Paths.Tmp, "remote.*.conf.tmp")
if err != nil {
return "", err
}
Expand Down
59 changes: 41 additions & 18 deletions core/pool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/opensvc/om3/v3/core/nodesinfo"
"github.com/opensvc/om3/v3/core/volaccess"
"github.com/opensvc/om3/v3/core/xconfig"
"github.com/opensvc/om3/v3/util/args"
"github.com/opensvc/om3/v3/util/key"
"github.com/opensvc/om3/v3/util/san"
"github.com/opensvc/om3/v3/util/sizeconv"
Expand Down Expand Up @@ -309,36 +310,58 @@ func (t *T) MntOptions() string {
return t.GetString("mnt_opt")
}

func (t *T) AddFS(name string, shared bool, fsIndex int, diskIndex int, onDisk string) []string {
type fsPooler interface {
MntOptions() string
MkblkOptions() string
MkfsOptions() string
FSType() string
}

type FS struct {
Pool fsPooler
Name string
Shared bool
FsIndex int
DiskIndex int
OnDisk string
DefaultMkfsOptions []string
DefaultMntOptions []string
}

func (t *FS) Keywords() []string {
data := make([]string, 0)
fsType := t.FSType()
fsType := t.Pool.FSType()
switch fsType {
case "zfs":
data = append(data, []string{
fmt.Sprintf("disk#%d.type=zpool", diskIndex),
fmt.Sprintf("disk#%d.name=%s", diskIndex, name),
fmt.Sprintf("disk#%d.vdev={%s.exposed_devs[0]}", diskIndex, onDisk),
fmt.Sprintf("disk#%d.shared=%t", diskIndex, shared),
fmt.Sprintf("fs#%d.type=zfs", fsIndex),
fmt.Sprintf("fs#%d.dev=%s/root", fsIndex, name),
fmt.Sprintf("fs#%d.mnt=%s", fsIndex, MountPointFromName(name)),
fmt.Sprintf("fs#%d.shared=%t", fsIndex, shared),
fmt.Sprintf("disk#%d.type=zpool", t.DiskIndex),
fmt.Sprintf("disk#%d.name=%s", t.DiskIndex, t.Name),
fmt.Sprintf("disk#%d.vdev={%s.exposed_devs[0]}", t.DiskIndex, t.OnDisk),
fmt.Sprintf("disk#%d.shared=%t", t.DiskIndex, t.Shared),
fmt.Sprintf("fs#%d.type=zfs", t.FsIndex),
fmt.Sprintf("fs#%d.dev=%s/root", t.FsIndex, t.Name),
fmt.Sprintf("fs#%d.mnt=%s", t.FsIndex, MountPointFromName(t.Name)),
fmt.Sprintf("fs#%d.shared=%t", t.FsIndex, t.Shared),
}...)
case "":
panic("fsType should not be empty at this point")
default:
data = append(data, []string{
fmt.Sprintf("fs#%d.type=%s", fsIndex, fsType),
fmt.Sprintf("fs#%d.dev={%s.exposed_devs[0]}", fsIndex, onDisk),
fmt.Sprintf("fs#%d.mnt=%s", fsIndex, MountPointFromName(name)),
fmt.Sprintf("fs#%d.shared=%t", fsIndex, shared),
fmt.Sprintf("fs#%d.type=%s", t.FsIndex, fsType),
fmt.Sprintf("fs#%d.dev={%s.exposed_devs[0]}", t.FsIndex, t.OnDisk),
fmt.Sprintf("fs#%d.mnt=%s", t.FsIndex, MountPointFromName(t.Name)),
fmt.Sprintf("fs#%d.shared=%t", t.FsIndex, t.Shared),
}...)
}
if opts := t.MkfsOptions(); opts != "" {
data = append(data, fmt.Sprintf("fs#%d.mkfs_opt=%s", fsIndex, opts))
if opts := t.Pool.MkfsOptions(); opts != "" {
data = append(data, fmt.Sprintf("fs#%d.mkfs_opt=%s", t.FsIndex, opts))
} else if len(t.DefaultMkfsOptions) > 0 {
data = append(data, fmt.Sprintf("fs#%d.mkfs_opt=%s", t.FsIndex, args.New(t.DefaultMkfsOptions...).String()))
}
if opts := t.MntOptions(); opts != "" {
data = append(data, fmt.Sprintf("fs#%d.mnt_opt=%s", fsIndex, opts))
if opts := t.Pool.MntOptions(); opts != "" {
data = append(data, fmt.Sprintf("fs#%d.mnt_opt=%s", t.FsIndex, opts))
} else if len(t.DefaultMntOptions) > 0 {
data = append(data, fmt.Sprintf("fs#%d.mnt_opt=%s", t.FsIndex, args.New(t.DefaultMntOptions...).String()))
}
return data
}
Expand Down
22 changes: 16 additions & 6 deletions core/tui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/opensvc/om3/v3/core/client"
"github.com/opensvc/om3/v3/core/clientcontext"
"github.com/opensvc/om3/v3/core/clusterdump"
"github.com/opensvc/om3/v3/core/commoncmd"
"github.com/opensvc/om3/v3/core/event"
"github.com/opensvc/om3/v3/core/monitor"
"github.com/opensvc/om3/v3/core/naming"
Expand Down Expand Up @@ -2248,8 +2249,11 @@ func (t *App) updateClusterConfigView() {
return
}

text := tview.TranslateANSI(string(resp.Body))
t.textView.SetDynamicColors(false)
// Apply syntax highlighting to the config
colorizedConfig := commoncmd.ColorizeINI(resp.Body)
text := tview.Escape(string(colorizedConfig))
text = tview.TranslateANSI(text)
t.textView.SetDynamicColors(true)
t.textView.Clear()
t.textView.SetTitle("cluster configuration")
fmt.Fprint(t.textView, text)
Expand All @@ -2268,8 +2272,11 @@ func (t *App) updateNodeConfigView() {
return
}

text := tview.TranslateANSI(string(resp.Body))
t.textView.SetDynamicColors(false)
// Apply syntax highlighting to the config
colorizedConfig := commoncmd.ColorizeINI(resp.Body)
text := tview.Escape(string(colorizedConfig))
text = tview.TranslateANSI(text)
t.textView.SetDynamicColors(true)
t.textView.SetTitle(fmt.Sprintf("%s configuration", t.viewNode))
t.textView.Clear()
fmt.Fprint(t.textView, text)
Expand All @@ -2287,8 +2294,11 @@ func (t *App) updateObjectConfigView() {
return
}

text := tview.TranslateANSI(string(resp.Body))
t.textView.SetDynamicColors(false)
// Apply syntax highlighting to the config
colorizedConfig := commoncmd.ColorizeINI(resp.Body)
text := tview.Escape(string(colorizedConfig))
text = tview.TranslateANSI(text)
t.textView.SetDynamicColors(true)
t.textView.SetTitle(fmt.Sprintf("%s configuration", t.viewPath.String()))
t.textView.Clear()
fmt.Fprint(t.textView, text)
Expand Down
Loading
Loading