Skip to content

Commit

Permalink
commit command
Browse files Browse the repository at this point in the history
  • Loading branch information
mihakralj committed Sep 28, 2023
1 parent 0a49963 commit 5ce5a61
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 88 deletions.
8 changes: 0 additions & 8 deletions clearversion.xml

This file was deleted.

37 changes: 32 additions & 5 deletions cmd/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var commitCmd = &cobra.Command{
fmt.Println("no staging.xml detected - nothing to commit.")
return
}
bash = `diff -q "` + configfile + `" "` + stagingfile + `" >& /dev/null && echo "same" || echo "diff"`
bash = `cmp -s "` + configfile + `" "` + stagingfile + `" && echo "same" || echo "diff"`
filesame := internal.ExecuteCmd(bash, host)
if strings.TrimSpace(filesame) != "diff" {
fmt.Println("staging.xml and config.xml are the same - nothing to commit.")
Expand All @@ -55,16 +55,43 @@ var commitCmd = &cobra.Command{
fmt.Println("\nChanges to be commited:")
internal.PrintDocument(deltadoc, "opnsense")

internal.Log(2, "commiting %s to %s", stagingfile, configfile)
internal.Log(2, "commiting %s to %s and reloading all services", stagingfile, configfile)

// copy config.xml to /conf/backup dir
backupname := internal.GenerateBackupFilename()
bash = `sudo cp -f ` + configfile + ` /conf/backup/` + backupname + ` && sudo mv -f /conf/staging.xml ` + configfile
internal.ExecuteCmd(bash, host)

fmt.Println("time to reload OPNSense!")

//TODO: run php /usr/local/etc/rc.reload_all
include := "php -r \"require_once('/usr/local/etc/inc/config.inc'); require_once('/usr/local/etc/inc/interfaces.inc'); require_once('/usr/local/etc/inc/filter.inc'); require_once('/usr/local/etc/inc/auth.inc'); require_once('/usr/local/etc/inc/rrd.inc'); require_once('/usr/local/etc/inc/util.inc'); require_once('/usr/local/etc/inc/system.inc'); require_once('/usr/local/etc/inc/interfaces.inc'); "

var result string

result = internal.ExecuteCmd(include+"system_firmware_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_trust_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_login_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_cron_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_timezone_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_hostname_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_resolver_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"interfaces_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"system_routing_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"rrd_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"filter_configure(true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"plugins_configure('vpn', true); \"", host)
fmt.Println(result)
result = internal.ExecuteCmd(include+"plugins_configure('local', true); \"", host)
fmt.Println(result)

},
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ var exportCmd = &cobra.Command{
olddoc := internal.LoadXMLFile(oldconfig, host, false)
newdoc := internal.LoadXMLFile(newconfig, host, true)
if newdoc == nil {
newdoc = olddoc
newdoc = olddoc.Copy()
}

deltadoc := internal.DiffXML(olddoc, newdoc, false)
internal.RemoveChgSpace(deltadoc.Root())

output := internal.ConfigToXML(deltadoc, path)
fmt.Print(output)
},
Expand Down
70 changes: 31 additions & 39 deletions cmd/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,84 +16,76 @@ limitations under the License.
package cmd

import (
"bufio"
"fmt"
"io"
"os"

"github.com/beevik/etree"
"github.com/mihakralj/opnsense/internal"
"github.com/spf13/cobra"
)

var execute bool

// compareCmd represents the compare command
var importCmd = &cobra.Command{
Use: "import",
Use: "import [patch.xml]",
Short: `Import XML patch and stage it for configuraiton change`,
Long: `The 'import' command allows bulk import of configuration changes by injecting an XML patch file that specifies what to add, or delete in the current configuration. Patch file is in the standard XML format generated by the 'export' command, using namespace tags indicating the type of change (e.g., add:, del:).
The command reads the patch file from the standard input. You can pipe the patch file into this command:
cat patch.xml | opnsense import
Or insert patch using I/O redirection:
opnsense import < patch.xml
Once the patch is imported, it is added to currently staged changes in 'staging.xml'. You can review these changes using 'opnsense compare -c' and apply them using 'opnsense commit' when ready.`,
Once the patch is imported, it is added to currently staged changes in 'staging.xml'. You can review staged changes using 'opnsense compare --compact' and apply them using 'opnsense commit' when ready.`,

Run: func(cmd *cobra.Command, args []string) {
if !cmd.Flag("depth").Changed {
depth = 5
}
internal.SetFlags(verbose, force, host, configfile, nocolor, depth, xmlFlag, yamlFlag, jsonFlag)

patchdoc := etree.NewDocument()

stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
reader := bufio.NewReader(os.Stdin)
var output []rune
if len(args) > 0 {
//check parameters
if len(args) > 0 {
importfilename := args[0]
if _, err := os.Stat(importfilename); os.IsNotExist(err) {
internal.Log(1, "import file %s does not exist\n", importfilename)
}
// Read file contents into a string
fileContents, err := os.ReadFile(importfilename)
if err != nil {
internal.Log(1, "failed to read file %s: %v\n", importfilename, err)
return
}
fileString := string(fileContents)

for {
input, _, err := reader.ReadRune()
if err != nil && err == io.EOF {
break
err = patchdoc.ReadFromString(fileString)
if err != nil {
internal.Log(1, "%s is not an XML file", importfilename)
}
output = append(output, input)
}
err := patchdoc.ReadFromString(string(output))
if err != nil {
internal.Log(1, "received stdin is not in XML format")
}

} else {
internal.Log(1, "No data received on stdin, please pipe the XML file into this command")
internal.Log(1, "No patch XML file provided")
}

internal.Checkos()
configdoc := internal.LoadXMLFile(configfile, host, false)

stagingdoc := internal.LoadXMLFile(stagingfile, host, true)
if stagingdoc == nil {
stagingdoc = internal.LoadXMLFile(stagingfile, host, true)
stagingdoc = configdoc.Copy()
}

internal.PatchElements(patchdoc.Root(), stagingdoc)
deltadoc := internal.DiffXML(configdoc, stagingdoc, false)

if !execute {
fmt.Println("Preview of modifications scheduled for imported into staging.xml:")
}
fmt.Println("Preview of patch scheduled for imported into staging.xml:")

internal.PrintDocument(deltadoc, "opnsense")

if execute {
internal.SaveXMLFile(stagingfile, stagingdoc, host, true)
fmt.Println("\nModifications imported into staging.xml")
}
internal.Log(2, "importing patch into staging.xml")
internal.SaveXMLFile(stagingfile, stagingdoc, host, true)
fmt.Println("\nModifications imported into staging.xml")
},
}

func init() {
importCmd.Flags().IntVarP(&depth, "depth", "d", 1, "Specifies number of depth levels of returned tree (default: 1)")
importCmd.Flags().BoolVarP(&execute, "execute", "e", false, "Apply the changes to the staging.xml, rather than just previewing them.")
importCmd.Flags().IntVarP(&depth, "depth", "d", 5, "Specifies number of depth levels of returned tree (default: 5)")
rootCmd.AddCommand(importCmd)
}
6 changes: 3 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

var (
Version string = "0.13.0"
Version string = "0.14.0"
verbose int
force bool
host string
Expand Down Expand Up @@ -84,8 +84,8 @@ To streamline remote operations, add your private key to the SSH agent using 'ss
}

func Execute() {
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.CompletionOptions.DisableNoDescFlag = true
//rootCmd.CompletionOptions.DisableDefaultCmd = true
//rootCmd.CompletionOptions.DisableNoDescFlag = true
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing CLI '%s'", err)
os.Exit(1)
Expand Down
14 changes: 10 additions & 4 deletions cmd/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,12 @@ The XPath parameter offers node targeting, enabling you to navigate to the exact
if !strings.HasPrefix(path, "opnsense/") {
path = "opnsense/" + path
}
path = strings.ReplaceAll(path, "[", ".")
path = strings.ReplaceAll(path, "]", "")

if matched, _ := regexp.MatchString(`\[0\]`, path); matched {
internal.Log(1, "XPath indexing of elements starts with 1, not 0")
return
}
// convert list targeting in path from item[1] to item.1
path = internal.EnumeratePath(path)

var attribute, value string
if len(args) == 2 {
Expand Down Expand Up @@ -98,16 +97,19 @@ The XPath parameter offers node targeting, enabling you to navigate to the exact
}
}

// stagingdoc is converted to enumeratedXML, path is converted to enumerated path
element := stagingdoc.FindElement(path)
if !deleteFlag {
if element == nil {
element = stagingdoc.Root()
parts := strings.Split(path, "/")

for i, part := range parts {
part = fixXMLName(part)
if i == 0 && part == "opnsense" {
continue
}

if element.SelectElement(part) == nil {
if element.SelectElement(part+".1") != nil {
var maxIndex int
Expand Down Expand Up @@ -183,8 +185,12 @@ The XPath parameter offers node targeting, enabling you to navigate to the exact
}
}

path = internal.ReverseEnumeratePath(path)

internal.ReverseEnumerateListElements(configdoc.Root())
internal.ReverseEnumerateListElements(stagingdoc.Root())

deltadoc := internal.DiffXML(configdoc, stagingdoc, true)
//internal.FullDepth()

internal.PrintDocument(deltadoc, path)
internal.SaveXMLFile(stagingfile, stagingdoc, host, true)
Expand Down
24 changes: 18 additions & 6 deletions internal/DiffXML.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package internal

import (
"fmt"
"regexp"
"strings"

"github.com/beevik/etree"
Expand All @@ -32,13 +33,14 @@ func DiffXML(oldDoc, newDoc *etree.Document, fulltree bool) *etree.Document {
addMissingElements(newDoc.Root(), diffDoc)
checkElements(diffDoc.Root(), newDoc)

ReverseEnumerateListElements(diffDoc.Root())
ReverseEnumerateListElements(newDoc.Root())

if !fulltree {
removeNodesWithoutSpace(diffDoc.Root())
removeAttSpace(diffDoc.Root())
}

//ReverseEnumerateListElements(diffDoc.Root())
//ReverseEnumerateListElements(newDoc.Root())

return diffDoc
}

Expand Down Expand Up @@ -104,7 +106,7 @@ func RemoveChgSpace(el *etree.Element) {
}
el.Space = "add"
}
// Process attributes
// Process attributes
for i := range el.Attr {
// Check if the attribute space is "chg"
if el.Attr[i].Space == "chg" {
Expand Down Expand Up @@ -139,9 +141,9 @@ func checkElements(oldEl *etree.Element, newDoc *etree.Document) {
oldEl.Space = "chg"
oldEl.SetText(fmt.Sprintf("%s|||%s", oldElText, newElText))
markParentSpace(oldEl)
} else if newElText != "" && oldElText == ""{
} else if newElText != "" && oldElText == "" {
oldEl.Space = "chg"
oldEl.SetText("N/A|||"+newEl.Text())
oldEl.SetText("N/A|||" + newEl.Text())
markParentSpace(oldEl)
}
}
Expand Down Expand Up @@ -291,6 +293,16 @@ func ReverseEnumerateListElements(el *etree.Element) {
}
}

func EnumeratePath(path string) string {
re := regexp.MustCompile(`\[(\d+)\]`)
return re.ReplaceAllString(path, ".$1")
}

func ReverseEnumeratePath(path string) string {
re := regexp.MustCompile(`\.(\d+)`)
return re.ReplaceAllString(path, "[$1]")
}

func getComments(el *etree.Element) []string {
var comments []string
for _, token := range el.Child {
Expand Down
14 changes: 8 additions & 6 deletions internal/EtreeToTTY.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package internal

import (
"fmt"
"regexp"
"strings"

"github.com/beevik/etree"
Expand Down Expand Up @@ -77,11 +76,14 @@ func EtreeToTTY(el *etree.Element, level int, indent int) string {
}

// Replace ".n" with "[n]" in the tag name
match, _ := regexp.MatchString(`\.\d+$`, el.Tag)
if match {
lastIndex := strings.LastIndex(el.Tag, ".")
el.Tag = el.Tag[:lastIndex] + "[" + el.Tag[lastIndex+1:] + "]"
}
el.Tag = ReverseEnumeratePath(el.Tag)
/*
match, _ := regexp.MatchString(`\.\d+$`, el.Tag)
if match {
lastIndex := strings.LastIndex(el.Tag, ".")
el.Tag = el.Tag[:lastIndex] + "[" + el.Tag[lastIndex+1:] + "]"
}
*/

// Build the content string
if len(el.ChildElements()) > 0 {
Expand Down
6 changes: 5 additions & 1 deletion internal/FocusEtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ import (
func FocusEtree(doc *etree.Document, path string) *etree.Element {
path = strings.TrimPrefix(path, "/")

path = EnumeratePath(path)
EnumerateListElements(doc.Root())

// Find all elements that match the path
foundElements := doc.FindElements(path)

if len(foundElements) == 0 {
Log(1, "Xpath element \"%s\" does not exist", path)
return nil
Expand Down Expand Up @@ -71,7 +75,7 @@ func FocusEtree(doc *etree.Document, path string) *etree.Element {
if space != "" {
// Set the space of the focused element to "att"
focused.Space = "att"
Log(5, "element maked with attention flag: %s", focused.GetPath())
}

return focused
}
Loading

0 comments on commit 5ce5a61

Please sign in to comment.