Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(validation): Improve validation option in Panorama #111

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 27 additions & 38 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ import (
//
// The bit-wise flags are as follows:
//
// * LogQuiet: disables all logging
// * LogAction: action being performed (Set / Edit / Delete functions)
// * LogQuery: queries being run (Get / Show functions)
// * LogOp: operation commands (Op functions)
// * LogUid: User-Id commands (Uid functions)
// * LogLog: log retrieval commands
// * LogExport: log export commands
// * LogXpath: the resultant xpath
// * LogSend: xml docuemnt being sent
// * LogReceive: xml responses being received
// * LogOsxCurl: output an OSX cURL command for the data being sent in
// * LogCurlWithPersonalData: If doing a curl style logging, then include
// personal data in the curl command instead of tokens.
// - LogQuiet: disables all logging
// - LogAction: action being performed (Set / Edit / Delete functions)
// - LogQuery: queries being run (Get / Show functions)
// - LogOp: operation commands (Op functions)
// - LogUid: User-Id commands (Uid functions)
// - LogLog: log retrieval commands
// - LogExport: log export commands
// - LogXpath: the resultant xpath
// - LogSend: xml docuemnt being sent
// - LogReceive: xml responses being received
// - LogOsxCurl: output an OSX cURL command for the data being sent in
// - LogCurlWithPersonalData: If doing a curl style logging, then include
// personal data in the curl command instead of tokens.
const (
LogQuiet = 1 << (iota + 1)
LogAction
Expand Down Expand Up @@ -168,10 +168,10 @@ func (c *Client) Plugins() []plugin.Info {
// client's SystemInfo map.
//
// If not specified, the following is assumed:
// * Protocol: https
// * Port: (unspecified)
// * Timeout: 10
// * Logging: LogAction | LogUid
// - Protocol: https
// - Port: (unspecified)
// - Timeout: 10
// - Logging: LogAction | LogUid
func (c *Client) Initialize() error {
if len(c.rb) == 0 {
var e error
Expand Down Expand Up @@ -318,34 +318,23 @@ func (c *Client) RequestPasswordHash(val string) (string, error) {

// ValidateConfig performs a commit config validation check.
//
// Setting sync to true means that this function will block until the job
// finishes.
//
//
// The sleep param is an optional sleep duration to wait between polling for
// job completion. This param is only used if sync is set to true.
// # Cmd interface contained a formatted XML string struct which can be used to either marshalled into XML or one of the
// validate function.
//
// This function returns the job ID and if any errors were encountered.
func (c *Client) ValidateConfig(sync bool, sleep time.Duration) (uint, error) {
func (c *Client) ValidateConfig(cmd interface{}) (uint, []byte, error) {
var err error
data := url.Values{}
data.Set("type", "op")

c.LogOp("(op) validating config")
type op_req struct {
XMLName xml.Name `xml:"validate"`
Cmd string `xml:"full"`
}
job_ans := util.JobResponse{}
_, err = c.Op(op_req{}, "", nil, &job_ans)
if err != nil {
return 0, err
if err = addToData("cmd", cmd, true, &data); err != nil {
return 0, nil, err
}

id := job_ans.Id
if !sync {
return id, nil
}
ans := util.JobResponse{}

return id, c.WaitForJob(id, sleep, nil, nil)
b, _, err := c.Communicate(data, &ans)
return ans.Id, b, err
}

// RevertToRunningConfig discards any changes made and reverts to the last
Expand Down
30 changes: 23 additions & 7 deletions commit/pano.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commit

import (
"encoding/xml"

"github.com/PaloAltoNetworks/pango/util"
)

Expand Down Expand Up @@ -116,6 +115,8 @@ type PanoramaCommitAll struct {
Description string
IncludeTemplate bool
ForceTemplateValues bool
ValidateOnly bool
Admins []string
Devices []string
}

Expand All @@ -139,26 +140,38 @@ func (o PanoramaCommitAll) Element() interface{} {
Description: o.Description,
IncludeTemplate: util.YesNo(o.IncludeTemplate),
ForceTemplateValues: util.YesNo(o.ForceTemplateValues),
ValidateOnly: util.YesNo(o.ValidateOnly),
},
}
if len(o.Admins) > 0 {
ans.DeviceGroup.Admins = util.StrToMem(o.Admins)
}
case TypeTemplate:
ans = panoCommitAll{
Template: &pcaTemplate{
Name: o.Name,
Description: o.Description,
ForceTemplateValues: util.YesNo(o.ForceTemplateValues),
Devices: util.StrToMem(o.Devices),
ValidateOnly: util.YesNo(o.ValidateOnly),
},
}
if len(o.Admins) > 0 {
ans.Template.Admins = util.StrToMem(o.Admins)
}
case TypeTemplateStack:
ans = panoCommitAll{
TemplateStack: &pcaTemplate{
Name: o.Name,
Description: o.Description,
ForceTemplateValues: util.YesNo(o.ForceTemplateValues),
Devices: util.StrToMem(o.Devices),
ValidateOnly: util.YesNo(o.ValidateOnly),
},
}
if len(o.Admins) > 0 {
ans.TemplateStack.Admins = util.StrToMem(o.Admins)
}
case TypeLogCollectorGroup:
ans = panoCommitAll{
LogCollectorGroup: &pcaLogCollectorGroup{
Expand All @@ -181,7 +194,6 @@ func (o PanoramaCommitAll) Element() interface{} {
},
}
}

return ans
}

Expand All @@ -195,10 +207,12 @@ type panoCommitAll struct {
}

type pcaDeviceGroup struct {
DgInfo pcaDgInfo `xml:"device-group"`
Description string `xml:"description,omitempty"`
IncludeTemplate string `xml:"include-template"`
ForceTemplateValues string `xml:"force-template-values"`
DgInfo pcaDgInfo `xml:"device-group"`
Admins *util.MemberType `xml:"admin,omitempty"`
Description string `xml:"description,omitempty"`
IncludeTemplate string `xml:"include-template"`
ForceTemplateValues string `xml:"force-template-values"`
ValidateOnly string `xml:"validate-only"`
}

type pcaDgInfo struct {
Expand All @@ -211,10 +225,12 @@ type pcaDgInfoEntry struct {
}

type pcaTemplate struct {
Admins *util.MemberType `xml:"admin,omitempty"`
Devices *util.MemberType `xml:"device"`
Name string `xml:"name"`
Description string `xml:"description,omitempty"`
Devices *util.MemberType `xml:"device"`
ForceTemplateValues string `xml:"force-template-values"`
ValidateOnly string `xml:"validate-only"`
}

type pcaLogCollectorGroup struct {
Expand Down
7 changes: 5 additions & 2 deletions commit/pano_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func TestPanoCommitAllDeviceGroup(t *testing.T) {
"<description>device group</description>",
"<include-template>yes</include-template>",
"<force-template-values>no</force-template-values>",
"<validate-only>no</validate-only>",
"</shared-policy>",
"</commit-all>",
}
Expand All @@ -142,10 +143,11 @@ func TestPanoCommitAllTemplate(t *testing.T) {
s := []string{
"<commit-all>",
"<template>",
"<device><member>12321</member></device>",
"<name>tmpl1</name>",
"<description>template</description>",
"<device><member>12321</member></device>",
"<force-template-values>yes</force-template-values>",
"<validate-only>no</validate-only>",
"</template>",
"</commit-all>",
}
Expand All @@ -169,10 +171,11 @@ func TestPanoCommitAllTemplateStack(t *testing.T) {
s := []string{
"<commit-all>",
"<template-stack>",
"<device><member>12321</member></device>",
"<name>ts1</name>",
"<description>template stack</description>",
"<device><member>12321</member></device>",
"<force-template-values>yes</force-template-values>",
"<validate-only>no</validate-only>",
"</template-stack>",
"</commit-all>",
}
Expand Down
81 changes: 81 additions & 0 deletions commit/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package commit

import (
"encoding/xml"
"github.com/PaloAltoNetworks/pango/util"
)

// PanoramaValidate is a Panorama Valid structure contained all option for Client.ValidateConfig().
type PanoramaValidate struct {
Admins []string
DeviceGroups []string
LogCollectors []string
LogCollectorGroups []string
Templates []string
TemplateStacks []string
WildfireAppliances []string
WildfireApplianceClusters []string
ExcludeDeviceAndNetwork bool
ExcludeSharedObjects bool
Full bool
}

// PanoramaValidateAll it is a shell struct for doing validate all changes in Panorama.
type PanoramaValidateAll struct{}

// Element this function return an interface ready to be marshalled into XML for validate.
func (o PanoramaValidate) Element() interface{} {
ans := panoValidate{}

var p *panoValidatePartial
if len(o.Admins) > 0 || len(o.DeviceGroups) > 0 || len(o.Templates) > 0 ||
len(o.TemplateStacks) > 0 || len(o.LogCollectors) > 0 || len(o.LogCollectorGroups) > 0 {
p = &panoValidatePartial{
Admins: util.StrToMem(o.Admins),
DeviceGroups: util.StrToMem(o.DeviceGroups),
Templates: util.StrToMem(o.Templates),
TemplateStacks: util.StrToMem(o.TemplateStacks),
WildfireAppliances: util.StrToMem(o.WildfireAppliances),
WildfireClusters: util.StrToMem(o.WildfireApplianceClusters),
LogCollectors: util.StrToMem(o.LogCollectors),
LogCollectorGroups: util.StrToMem(o.LogCollectorGroups),
}

if o.ExcludeSharedObjects {
p.ExcludeSharedObjects = "excluded"
}

if o.ExcludeDeviceAndNetwork {
p.ExcludeDeviceAndNetwork = "excluded"
}
} else {
ans.Full = &panoValidateFull{}
}

ans.Partial = p

return ans
}

// panoValidate main XML structure for validate, it contains main 'validate' option and regarded for the action
// it can be either 'full' or 'partial'
type panoValidate struct {
XMLName xml.Name `xml:"validate"`
Full *panoValidateFull `xml:"full"`
Partial *panoValidatePartial `xml:"partial"`
}

type panoValidateFull struct{}

type panoValidatePartial struct {
Admins *util.MemberType `xml:"admin"`
DeviceGroups *util.MemberType `xml:"device-group"`
Templates *util.MemberType `xml:"template"`
TemplateStacks *util.MemberType `xml:"template-stack"`
WildfireAppliances *util.MemberType `xml:"wildfire-appliance"`
WildfireClusters *util.MemberType `xml:"wildfire-appliance-cluster"`
LogCollectors *util.MemberType `xml:"log-collector"`
LogCollectorGroups *util.MemberType `xml:"log-collector-group"`
ExcludeDeviceAndNetwork string `xml:"device-and-network,omitempty"`
ExcludeSharedObjects string `xml:"shared-object,omitempty"`
}
63 changes: 63 additions & 0 deletions commit/validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package commit

import (
"encoding/xml"
"strings"
"testing"
)

func TestPanoramaValidate_ValidateElement(t *testing.T) {
testS := []string{
"<validate>",
"<partial>",
"<admin><member>admin1</member><member>admin2</member></admin>",
"<device-group><member>dg1</member></device-group>",
"<template><member>tmpl1</member></template>",
"<template-stack><member>ts1</member></template-stack>",
"<wildfire-appliance><member>wfa1</member></wildfire-appliance>",
"<wildfire-appliance-cluster><member>wfc1</member></wildfire-appliance-cluster>",
"<log-collector><member>lc1</member></log-collector>",
"<log-collector-group><member>lcg1</member></log-collector-group>",
"<device-and-network>excluded</device-and-network>",
"<shared-object>excluded</shared-object>",
"</partial>",
"</validate>",
}

expected := strings.Join(testS, "")

validateStruct := PanoramaValidate{
Admins: []string{"admin1", "admin2"},
DeviceGroups: []string{"dg1"},
LogCollectors: []string{"lc1"},
LogCollectorGroups: []string{"lcg1"},
Templates: []string{"tmpl1"},
TemplateStacks: []string{"ts1"},
WildfireAppliances: []string{"wfa1"},
WildfireApplianceClusters: []string{"wfc1"},
ExcludeDeviceAndNetwork: true,
ExcludeSharedObjects: true,
}

marshV, _ := xml.Marshal(validateStruct.Element())
if expected != string(marshV) {
t.Errorf("Expected(%s) got(%s)", expected, marshV)
}
}

func TestPanoramaValidateAll_ValidateAllElement(t *testing.T) {
testS := []string{
"<validate>",
"<full>",
"</full>",
"</validate>",
}

expected := strings.Join(testS, "")
validateStruct := PanoramaValidate{}

marshV, _ := xml.Marshal(validateStruct.Element())
if expected != string(marshV) {
t.Errorf("Expected(%s) got(%s)", expected, marshV)
}
}
Loading