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

add gNMI commit extension #450

Merged
merged 2 commits into from
Jun 20, 2024
Merged
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
20 changes: 20 additions & 0 deletions docs/cmd/set.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,26 @@ It expects a file containing one or multiple CLI commands which will form the va

See [this section](#templated-set-request-file) below.

### commit-id

The `--commit-id` flag sets the commit ID when the client needs to perform a commit confirmed set request as per: https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-commit-confirmed.md

### commit-request

The `--commit-request` flag is used together with the `--commit-id` flag to set the commit action to `Request`, essentially starting a commit request.

### commit-confirm

The `--commit-confirm` flag is used together with the `--commit-id` flag to confirm an already started commit confirmed transaction.

### commit-cancel

The `--commit-cancel` flag is used together with the `--commit-id` flag to cancel an already started commit confirmed transaction.

### rollback-duration

The `--rollback-duration` flag is used together with the `--commit-id` flag to set the rollback duration of a commit confirmed transaction either at creation time or before the previous commit rollback expires.

## Update Request

There are several ways to perform an update operation with gNMI Set RPC:
Expand Down
110 changes: 110 additions & 0 deletions pkg/api/gnmi_msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
gvalue "github.com/openconfig/gnmi/value"
"github.com/openconfig/gnmic/pkg/api/path"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
)

const (
Expand Down Expand Up @@ -290,6 +291,115 @@ func Extension(ext *gnmi_ext.Extension) func(msg proto.Message) error {
}
}

func Extension_CommitRequest(id string, dur time.Duration) func(msg proto.Message) error {
return func(msg proto.Message) error {
if msg == nil {
return ErrInvalidMsgType
}
switch msg := msg.ProtoReflect().Interface().(type) {
case *gnmi.SetRequest:
fn := Extension(
&gnmi_ext.Extension{
Ext: &gnmi_ext.Extension_Commit{
Commit: &gnmi_ext.Commit{
Id: id,
Action: &gnmi_ext.Commit_Commit{
Commit: &gnmi_ext.CommitRequest{
RollbackDuration: durationpb.New(dur),
},
},
},
},
},
)
return fn(msg)
default:
return fmt.Errorf("option Extension_CommitRequest: %w: %T", ErrInvalidMsgType, msg)
}
}
}

func Extension_CommitConfirm(id string) func(msg proto.Message) error {
return func(msg proto.Message) error {
if msg == nil {
return ErrInvalidMsgType
}
switch msg := msg.ProtoReflect().Interface().(type) {
case *gnmi.SetRequest:
fn := Extension(
&gnmi_ext.Extension{
Ext: &gnmi_ext.Extension_Commit{
Commit: &gnmi_ext.Commit{
Id: id,
Action: &gnmi_ext.Commit_Confirm{
Confirm: &gnmi_ext.CommitConfirm{},
},
},
},
},
)
return fn(msg)
default:
return fmt.Errorf("option Extension_CommitConfirm: %w: %T", ErrInvalidMsgType, msg)
}
}
}

func Extension_CommitCancel(id string) func(msg proto.Message) error {
return func(msg proto.Message) error {
if msg == nil {
return ErrInvalidMsgType
}
switch msg := msg.ProtoReflect().Interface().(type) {
case *gnmi.SetRequest:
fn := Extension(
&gnmi_ext.Extension{
Ext: &gnmi_ext.Extension_Commit{
Commit: &gnmi_ext.Commit{
Id: id,
Action: &gnmi_ext.Commit_Cancel{
Cancel: &gnmi_ext.CommitCancel{},
},
},
},
},
)
return fn(msg)
default:
return fmt.Errorf("option Extension_CommitCancel: %w: %T", ErrInvalidMsgType, msg)
}
}
}

func Extension_CommitSetRollbackDuration(id string, dur time.Duration) func(msg proto.Message) error {
return func(msg proto.Message) error {
if msg == nil {
return ErrInvalidMsgType
}

switch msg := msg.ProtoReflect().Interface().(type) {
case *gnmi.SetRequest:
fn := Extension(
&gnmi_ext.Extension{
Ext: &gnmi_ext.Extension_Commit{
Commit: &gnmi_ext.Commit{
Id: id,
Action: &gnmi_ext.Commit_SetRollbackDuration{
SetRollbackDuration: &gnmi_ext.CommitSetRollbackDuration{
RollbackDuration: durationpb.New(dur),
},
},
},
},
},
)
return fn(msg)
default:
return fmt.Errorf("option Extension_CommitCancel: %w: %T", ErrInvalidMsgType, msg)
}
}
}

// Extension_HistorySnapshotTime creates a GNMIOption that adds a gNMI extension of
// type History Snapshot with the supplied snapshot time.
// the snapshot value can be nanoseconds since Unix epoch or a date in RFC3339 format
Expand Down
6 changes: 6 additions & 0 deletions pkg/app/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ func (a *App) InitSetFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayVarP(&a.Config.LocalFlags.SetUpdateCli, "update-cli", "", []string{}, "a cli command to be sent as a set update request")
cmd.Flags().StringVarP(&a.Config.LocalFlags.SetUpdateCliFile, "update-cli-file", "", "", "path to a file containing a list of commands that will be sent as a set update request")

cmd.Flags().StringVarP(&a.Config.LocalFlags.SetCommitId, "commit-id", "", "", "commit ID value")
cmd.Flags().BoolVarP(&a.Config.LocalFlags.SetCommitRequest, "commit-request", "", false, "start a commit confirmed transaction")
cmd.Flags().BoolVarP(&a.Config.LocalFlags.SetCommitConfirm, "commit-confirm", "", false, "confirm the commit ID")
cmd.Flags().BoolVarP(&a.Config.LocalFlags.SetCommitCancel, "commit-cancel", "", false, "cancel the commit")
cmd.Flags().DurationVarP(&a.Config.LocalFlags.SetCommitRollbackDuration, "rollback-duration", "", 0, "set the commit rollback duration")

cmd.LocalFlags().VisitAll(func(flag *pflag.Flag) {
a.Config.FileConfig.BindPFlag(fmt.Sprintf("%s-%s", cmd.Name(), flag.Name), flag)
})
Expand Down
82 changes: 57 additions & 25 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,30 +135,35 @@ type LocalFlags struct {
GetValuesOnly bool `mapstructure:"get-values-only,omitempty" json:"get-values-only,omitempty" yaml:"get-values-only,omitempty"`
GetProcessor []string `mapstructure:"get-processor,omitempty" json:"get-processor,omitempty" yaml:"get-processor,omitempty"`
// Set
SetPrefix string `mapstructure:"set-prefix,omitempty" json:"set-prefix,omitempty" yaml:"set-prefix,omitempty"`
SetDelete []string `mapstructure:"set-delete,omitempty" json:"set-delete,omitempty" yaml:"set-delete,omitempty"`
SetReplace []string `mapstructure:"set-replace,omitempty" json:"set-replace,omitempty" yaml:"set-replace,omitempty"`
SetUnionReplace []string `mapstructure:"set-union-replace,omitempty" json:"set-union-replace,omitempty" yaml:"set-union-replace,omitempty"`
SetUpdate []string `mapstructure:"set-update,omitempty" json:"set-update,omitempty" yaml:"set-update,omitempty"`
SetReplacePath []string `mapstructure:"set-replace-path,omitempty" json:"set-replace-path,omitempty" yaml:"set-replace-path,omitempty"`
SetUpdatePath []string `mapstructure:"set-update-path,omitempty" json:"set-update-path,omitempty" yaml:"set-update-path,omitempty"`
SetReplaceFile []string `mapstructure:"set-replace-file,omitempty" json:"set-replace-file,omitempty" yaml:"set-replace-file,omitempty"`
SetUpdateFile []string `mapstructure:"set-update-file,omitempty" json:"set-update-file,omitempty" yaml:"set-update-file,omitempty"`
SetReplaceValue []string `mapstructure:"set-replace-value,omitempty" json:"set-replace-value,omitempty" yaml:"set-replace-value,omitempty"`
SetUpdateValue []string `mapstructure:"set-update-value,omitempty" json:"set-update-value,omitempty" yaml:"set-update-value,omitempty"`
SetUnionReplacePath []string `mapstructure:"set-union-replace-path,omitempty" yaml:"set-union-replace-path,omitempty" json:"set-union-replace-path,omitempty"`
SetUnionReplaceValue []string `mapstructure:"set-union-replace-value,omitempty" yaml:"set-union-replace-value,omitempty" json:"set-union-replace-value,omitempty"`
SetUnionReplaceFile []string `mapstructure:"set-union-replace-file,omitempty" yaml:"set-union-replace-file,omitempty" json:"set-union-replace-file,omitempty"`
SetDelimiter string `mapstructure:"set-delimiter,omitempty" json:"set-delimiter,omitempty" yaml:"set-delimiter,omitempty"`
SetTarget string `mapstructure:"set-target,omitempty" json:"set-target,omitempty" yaml:"set-target,omitempty"`
SetRequestFile []string `mapstructure:"set-request-file,omitempty" json:"set-request-file,omitempty" yaml:"set-request-file,omitempty"`
SetRequestVars string `mapstructure:"set-request-vars,omitempty" json:"set-request-vars,omitempty" yaml:"set-request-vars,omitempty"`
SetRequestProtoFile []string `mapstructure:"set-proto-request-file,omitempty" yaml:"set-proto-request-file,omitempty" json:"set-proto-request-file,omitempty"`
SetDryRun bool `mapstructure:"set-dry-run,omitempty" json:"set-dry-run,omitempty" yaml:"set-dry-run,omitempty"`
SetReplaceCli []string `mapstructure:"set-replace-cli,omitempty" yaml:"set-replace-cli,omitempty" json:"set-replace-cli,omitempty"`
SetReplaceCliFile string `mapstructure:"set-replace-cli-file,omitempty" yaml:"set-replace-cli-file,omitempty" json:"set-replace-cli-file,omitempty"`
SetUpdateCli []string `mapstructure:"set-update-cli,omitempty" yaml:"set-update-cli,omitempty" json:"set-update-cli,omitempty"`
SetUpdateCliFile string `mapstructure:"set-update-cli-file,omitempty" yaml:"set-update-cli-file,omitempty" json:"set-update-cli-file,omitempty"`
SetPrefix string `mapstructure:"set-prefix,omitempty" json:"set-prefix,omitempty" yaml:"set-prefix,omitempty"`
SetDelete []string `mapstructure:"set-delete,omitempty" json:"set-delete,omitempty" yaml:"set-delete,omitempty"`
SetReplace []string `mapstructure:"set-replace,omitempty" json:"set-replace,omitempty" yaml:"set-replace,omitempty"`
SetUnionReplace []string `mapstructure:"set-union-replace,omitempty" json:"set-union-replace,omitempty" yaml:"set-union-replace,omitempty"`
SetUpdate []string `mapstructure:"set-update,omitempty" json:"set-update,omitempty" yaml:"set-update,omitempty"`
SetReplacePath []string `mapstructure:"set-replace-path,omitempty" json:"set-replace-path,omitempty" yaml:"set-replace-path,omitempty"`
SetUpdatePath []string `mapstructure:"set-update-path,omitempty" json:"set-update-path,omitempty" yaml:"set-update-path,omitempty"`
SetReplaceFile []string `mapstructure:"set-replace-file,omitempty" json:"set-replace-file,omitempty" yaml:"set-replace-file,omitempty"`
SetUpdateFile []string `mapstructure:"set-update-file,omitempty" json:"set-update-file,omitempty" yaml:"set-update-file,omitempty"`
SetReplaceValue []string `mapstructure:"set-replace-value,omitempty" json:"set-replace-value,omitempty" yaml:"set-replace-value,omitempty"`
SetUpdateValue []string `mapstructure:"set-update-value,omitempty" json:"set-update-value,omitempty" yaml:"set-update-value,omitempty"`
SetUnionReplacePath []string `mapstructure:"set-union-replace-path,omitempty" yaml:"set-union-replace-path,omitempty" json:"set-union-replace-path,omitempty"`
SetUnionReplaceValue []string `mapstructure:"set-union-replace-value,omitempty" yaml:"set-union-replace-value,omitempty" json:"set-union-replace-value,omitempty"`
SetUnionReplaceFile []string `mapstructure:"set-union-replace-file,omitempty" yaml:"set-union-replace-file,omitempty" json:"set-union-replace-file,omitempty"`
SetDelimiter string `mapstructure:"set-delimiter,omitempty" json:"set-delimiter,omitempty" yaml:"set-delimiter,omitempty"`
SetTarget string `mapstructure:"set-target,omitempty" json:"set-target,omitempty" yaml:"set-target,omitempty"`
SetRequestFile []string `mapstructure:"set-request-file,omitempty" json:"set-request-file,omitempty" yaml:"set-request-file,omitempty"`
SetRequestVars string `mapstructure:"set-request-vars,omitempty" json:"set-request-vars,omitempty" yaml:"set-request-vars,omitempty"`
SetRequestProtoFile []string `mapstructure:"set-proto-request-file,omitempty" yaml:"set-proto-request-file,omitempty" json:"set-proto-request-file,omitempty"`
SetDryRun bool `mapstructure:"set-dry-run,omitempty" json:"set-dry-run,omitempty" yaml:"set-dry-run,omitempty"`
SetReplaceCli []string `mapstructure:"set-replace-cli,omitempty" yaml:"set-replace-cli,omitempty" json:"set-replace-cli,omitempty"`
SetReplaceCliFile string `mapstructure:"set-replace-cli-file,omitempty" yaml:"set-replace-cli-file,omitempty" json:"set-replace-cli-file,omitempty"`
SetUpdateCli []string `mapstructure:"set-update-cli,omitempty" yaml:"set-update-cli,omitempty" json:"set-update-cli,omitempty"`
SetUpdateCliFile string `mapstructure:"set-update-cli-file,omitempty" yaml:"set-update-cli-file,omitempty" json:"set-update-cli-file,omitempty"`
SetCommitId string `mapstructure:"set-commit-id,omitempty" yaml:"set-commit-id,omitempty" json:"set-commit-id,omitempty"`
SetCommitRequest bool `mapstructure:"set-commit-request,omitempty" yaml:"set-commit-request,omitempty" json:"set-commit-request,omitempty"`
SetCommitRollbackDuration time.Duration `mapstructure:"set-commit-rollback-duration,omitempty" yaml:"set-commit-rollback-duration,omitempty" json:"set-commit-rollback-duration,omitempty"`
SetCommitCancel bool `mapstructure:"set-commit-cancel,omitempty" yaml:"set-commit-cancel,omitempty" json:"set-commit-cancel,omitempty"`
SetCommitConfirm bool `mapstructure:"set-commit-confirm,omitempty" yaml:"set-commit-confirm,omitempty" json:"set-commit-confirm,omitempty"`
// Sub
SubscribePrefix string `mapstructure:"subscribe-prefix,omitempty" json:"subscribe-prefix,omitempty" yaml:"subscribe-prefix,omitempty"`
SubscribePath []string `mapstructure:"subscribe-path,omitempty" json:"subscribe-path,omitempty" yaml:"subscribe-path,omitempty"`
Expand Down Expand Up @@ -776,6 +781,32 @@ func (c *Config) CreateSetRequest(targetName string) ([]*gnmi.SetRequest, error)
),
)
}

if c.LocalFlags.SetCommitId != "" {
if c.LocalFlags.SetCommitRequest {
gnmiOpts = append(gnmiOpts,
api.Extension_CommitRequest(
c.LocalFlags.SetCommitId,
c.LocalFlags.SetCommitRollbackDuration,
))
} else if c.LocalFlags.SetCommitConfirm {
gnmiOpts = append(gnmiOpts,
api.Extension_CommitConfirm(
c.LocalFlags.SetCommitId,
))
} else if c.LocalFlags.SetCommitCancel {
gnmiOpts = append(gnmiOpts,
api.Extension_CommitCancel(
c.LocalFlags.SetCommitId,
))
} else {
gnmiOpts = append(gnmiOpts,
api.Extension_CommitSetRollbackDuration(
c.LocalFlags.SetCommitId,
c.LocalFlags.SetCommitRollbackDuration,
))
}
}
//
req, err := api.NewSetRequest(gnmiOpts...)
return []*gnmi.SetRequest{req}, err
Expand Down Expand Up @@ -870,7 +901,8 @@ func (c *Config) ValidateSetInput() error {
len(c.LocalFlags.SetUpdateCli) == 0 &&
len(c.LocalFlags.SetReplaceCliFile) == 0 &&
len(c.LocalFlags.SetUpdateCliFile) == 0 &&
len(c.LocalFlags.SetRequestProtoFile) == 0 {
len(c.LocalFlags.SetRequestProtoFile) == 0 &&
c.LocalFlags.SetCommitId == "" {
return errors.New("no paths or request file provided")
}
if len(c.LocalFlags.SetUpdateFile) > 0 && len(c.LocalFlags.SetUpdateValue) > 0 {
Expand Down
50 changes: 46 additions & 4 deletions pkg/config/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"path/filepath"
"strings"
"text/template"
"time"

"google.golang.org/protobuf/encoding/prototext"
"gopkg.in/yaml.v2"
Expand All @@ -40,12 +41,24 @@ type UpdateItem struct {
}

type SetRequestFile struct {
Updates []*UpdateItem `json:"updates,omitempty" yaml:"updates,omitempty"`
Replaces []*UpdateItem `json:"replaces,omitempty" yaml:"replaces,omitempty"`
UnionReplaces []*UpdateItem `json:"union-replaces,omitempty" yaml:"union-replaces,omitempty"`
Deletes []string `json:"deletes,omitempty" yaml:"deletes,omitempty"`
Updates []*UpdateItem `json:"updates,omitempty" yaml:"updates,omitempty"`
Replaces []*UpdateItem `json:"replaces,omitempty" yaml:"replaces,omitempty"`
UnionReplaces []*UpdateItem `json:"union-replaces,omitempty" yaml:"union-replaces,omitempty"`
Deletes []string `json:"deletes,omitempty" yaml:"deletes,omitempty"`
CommitID string `yaml:"commit-id,omitempty" json:"commit-id,omitempty"`
CommitAction commitAction `yaml:"commit-action,omitempty" json:"commit-action,omitempty"`
RollbackDuration time.Duration `yaml:"rollback-duration,omitempty" json:"rollback-duration,omitempty"`
}

type commitAction string

const (
commitActionRequest commitAction = "request"
commitActionCancel commitAction = "cancel"
commitActionConfirm commitAction = "confirm"
commitActionSetRollbackDuration commitAction = "set-rollback-duration"
)

func (c *Config) ReadSetRequestTemplate() error {
if len(c.SetRequestFile) == 0 {
return nil
Expand Down Expand Up @@ -223,6 +236,35 @@ func (c *Config) CreateSetRequestFromFile(targetName string) ([]*gnmi.SetRequest
gnmiOpts = append(gnmiOpts, api.Delete(strings.TrimSpace(s)))
}

if reqFile.CommitID != "" {
switch reqFile.CommitAction {
case commitActionRequest:
gnmiOpts = append(gnmiOpts,
api.Extension_CommitRequest(
c.LocalFlags.SetCommitId,
c.LocalFlags.SetCommitRollbackDuration,
))
case commitActionCancel:
gnmiOpts = append(gnmiOpts,
api.Extension_CommitCancel(
c.LocalFlags.SetCommitId,
))
case commitActionConfirm:
gnmiOpts = append(gnmiOpts,
api.Extension_CommitConfirm(
c.LocalFlags.SetCommitId,
))
case commitActionSetRollbackDuration:
gnmiOpts = append(gnmiOpts,
api.Extension_CommitSetRollbackDuration(
c.LocalFlags.SetCommitId,
c.LocalFlags.SetCommitRollbackDuration,
))
default:
return nil, fmt.Errorf("unknown commit action %s", reqFile.CommitAction)
}
}

setReq, err := api.NewSetRequest(gnmiOpts...)
if err != nil {
return nil, err
Expand Down