Skip to content

Commit

Permalink
ConfigureInstance - update SAPTune re-apply logic to support multiple…
Browse files Browse the repository at this point in the history
… solutions

PiperOrigin-RevId: 699154194
  • Loading branch information
JKenneson authored and copybara-github committed Nov 22, 2024
1 parent a4af96d commit 3c644b6
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 70 deletions.
58 changes: 29 additions & 29 deletions internal/onetime/configureinstance/configurex4.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,17 @@ func (c *ConfigureInstance) configureX4SLES(ctx context.Context) (bool, error) {
if err := c.saptuneService(ctx); err != nil {
return false, err
}
sapTuneReapply, err := c.checkAndRegenerateFile(ctx, "/etc/saptune/extra/google-x4.conf", googleX4Conf)
noteRegenerated, err := c.checkAndRegenerateFile(ctx, "/etc/saptune/extra/google-x4.conf", googleX4Conf)
if err != nil {
return false, err
}
if c.saptuneSolutions(ctx) {
sapTuneReapply = true
}
if err := c.saptuneReapply(ctx, sapTuneReapply); err != nil {
solutionReapply, noteReapply := c.saptuneSolutions(ctx)
noteReapply = noteReapply || noteRegenerated
if err := c.saptuneReapply(ctx, solutionReapply, noteReapply); err != nil {
return false, err
}

log.CtxLogger(ctx).Info("SLES specific configurations complete.")
return sapTuneReapply, nil
return solutionReapply || noteReapply, nil
}

// saptuneService checks if saptune service is running. If it is not running,
Expand Down Expand Up @@ -169,44 +167,46 @@ func (c *ConfigureInstance) saptuneService(ctx context.Context) error {
}

// saptuneSolutions checks if SAPTune solutions and notes are correct.
// Returns true if saptune reapply is required.
func (c *ConfigureInstance) saptuneSolutions(ctx context.Context) bool {
sapTuneReapply := false
// Returns true if saptune solution and note reapply is required.
func (c *ConfigureInstance) saptuneSolutions(ctx context.Context) (bool, bool) {
solutionReapply, noteReapply := false, false
saptuneSolutions := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "status"})
if match, _ := regexp.MatchString(`enabled Solution:\s*HANA`, saptuneSolutions.StdOut); !match {
log.CtxLogger(ctx).Info("Enabled solution is not `HANA`, SAPTune re-apply required.")
sapTuneReapply = true
if match, _ := regexp.MatchString(`enabled Solution:\s*(HANA|NETWEAVER\+HANA|S4HANA\-APP\+DB|S4HANA\-DBSERVER)`, saptuneSolutions.StdOut); !match {
log.CtxLogger(ctx).Info("Enabled solution is not `(HANA|NETWEAVER+HANA|S4HANA-APP+DB|S4HANA-DBSERVER)`, SAPTune solution re-apply required.")
solutionReapply = true
}
if match, _ := regexp.MatchString(`additional enabled Notes:\s*google-x4`, saptuneSolutions.StdOut); !match {
log.CtxLogger(ctx).Info("Enabled note is not `google-x4`, SAPTune re-apply required.")
sapTuneReapply = true
log.CtxLogger(ctx).Info("Enabled note is not `google-x4`, SAPTune note re-apply required.")
noteReapply = true
}
return sapTuneReapply
return solutionReapply, noteReapply
}

// saptuneReapply executes SAPTune re-apply by applying the
// HANA solution and the google-x4 note.
func (c *ConfigureInstance) saptuneReapply(ctx context.Context, sapTuneReapply bool) error {
if !sapTuneReapply {
func (c *ConfigureInstance) saptuneReapply(ctx context.Context, solutionReapply, noteReapply bool) error {
if !solutionReapply && !noteReapply {
log.CtxLogger(ctx).Info("SAPTune re-apply is not required.")
return nil
}
if c.Check {
log.CtxLogger(ctx).Info("Run 'configureinstance -apply' to execute SAPTune re-apply.")
return nil
}
log.CtxLogger(ctx).Info("Executing SAPTune re-apply.")
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "solution revert HANA", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune solution revert HANA' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
}
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "solution apply HANA", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune solution apply HANA' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
}
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "note revert google-x4", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune note revert google-x4' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
if solutionReapply {
log.CtxLogger(ctx).Info("Executing SAPTune solution re-apply.")
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "solution change --force HANA", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune solution change --force HANA' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
}
}
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "note apply google-x4", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune note apply google-x4' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
if noteReapply {
log.CtxLogger(ctx).Info("Executing SAPTune note re-apply.")
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "note revert google-x4", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune note revert google-x4' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
}
if res := c.ExecuteFunc(ctx, commandlineexecutor.Params{Executable: "saptune", ArgsToSplit: "note apply google-x4", Timeout: c.TimeoutSec}); res.ExitCode != 0 {
return fmt.Errorf("'saptune note apply google-x4' failed, code: %d, err: %v, stderr: %s", res.ExitCode, res.Error, res.StdErr)
}
}
return nil
}
Expand Down
88 changes: 47 additions & 41 deletions internal/onetime/configureinstance/configurex4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,110 +295,116 @@ func TestSaptuneService(t *testing.T) {

func TestSaptuneSolutions(t *testing.T) {
tests := []struct {
name string
c ConfigureInstance
want bool
name string
c ConfigureInstance
wantSolution bool
wantNote bool
}{
{
name: "BothUpdate",
c: ConfigureInstance{
ExecuteFunc: defaultExecute([]int{0}, []string{""}),
},
wantSolution: true,
wantNote: true,
},
{
name: "SolutionUpdate",
c: ConfigureInstance{
ExecuteFunc: defaultExecute([]int{0}, []string{"enabled Solution: NOT_HANA"}),
ExecuteFunc: defaultExecute([]int{0}, []string{"enabled Solution: NOT_HANA\nadditional enabled Notes: google-x4"}),
},
want: true,
wantSolution: true,
wantNote: false,
},
{
name: "NoteUpdate",
c: ConfigureInstance{
ExecuteFunc: defaultExecute([]int{0}, []string{"enabled Solution: HANA\nadditional enabled Notes: NOT_google-x4"}),
},
want: true,
wantSolution: false,
wantNote: true,
},
{
name: "NoUpdateRequired",
c: ConfigureInstance{
ExecuteFunc: defaultExecute([]int{0}, []string{"enabled Solution: HANA\nadditional enabled Notes: google-x4"}),
ExecuteFunc: defaultExecute([]int{0}, []string{"enabled Solution: NETWEAVER+HANA\nadditional enabled Notes: google-x4"}),
},
want: false,
wantSolution: false,
wantNote: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := tc.c.saptuneSolutions(context.Background())
if got != tc.want {
t.Errorf("saptuneSolutions(%#v) = %v, want: %v", tc.c, got, tc.want)
gotSolution, gotNote := tc.c.saptuneSolutions(context.Background())
if gotSolution != tc.wantSolution || gotNote != tc.wantNote {
t.Errorf("saptuneSolutions(%#v) = %v, %v, want: %v, %v", tc.c, gotSolution, gotNote, tc.wantSolution, tc.wantNote)
}
})
}
}

func TestSaptuneReapply(t *testing.T) {
tests := []struct {
name string
sapTuneReapply bool
c ConfigureInstance
wantErr error
name string
solutionReapply bool
noteReapply bool
c ConfigureInstance
wantErr error
}{
{
name: "ReapplyNotRequired",
sapTuneReapply: false,
name: "ReapplyNotRequired",
solutionReapply: false,
noteReapply: false,
},
{
name: "CheckMode",
sapTuneReapply: true,
name: "CheckMode",
solutionReapply: true,
c: ConfigureInstance{
Check: true,
},
},
{
name: "FailSolutionRevert",
sapTuneReapply: true,
name: "FailSolutionChange",
solutionReapply: true,
c: ConfigureInstance{
Apply: true,
ExecuteFunc: defaultExecute([]int{1}, []string{""}),
},
wantErr: cmpopts.AnyError,
},
{
name: "FailSolutionApply",
sapTuneReapply: true,
c: ConfigureInstance{
Apply: true,
ExecuteFunc: defaultExecute([]int{0, 1}, []string{"", ""}),
},
wantErr: cmpopts.AnyError,
},
{
name: "FailNoteRevert",
sapTuneReapply: true,
name: "FailNoteRevert",
noteReapply: true,
c: ConfigureInstance{
Apply: true,
ExecuteFunc: defaultExecute([]int{0, 0, 1}, []string{"", "", ""}),
ExecuteFunc: defaultExecute([]int{1}, []string{""}),
},
wantErr: cmpopts.AnyError,
},
{
name: "FailNoteApply",
sapTuneReapply: true,
name: "FailNoteApply",
noteReapply: true,
c: ConfigureInstance{
Apply: true,
ExecuteFunc: defaultExecute([]int{0, 0, 0, 1}, []string{"", "", "", ""}),
ExecuteFunc: defaultExecute([]int{0, 1}, []string{"", ""}),
},
wantErr: cmpopts.AnyError,
},
{
name: "Success",
sapTuneReapply: true,
name: "Success",
solutionReapply: true,
noteReapply: true,
c: ConfigureInstance{
Apply: true,
ExecuteFunc: defaultExecute([]int{0, 0, 0, 0}, []string{"", "", "", ""}),
ExecuteFunc: defaultExecute([]int{0, 0, 0}, []string{"", "", ""}),
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
gotErr := tc.c.saptuneReapply(context.Background(), tc.sapTuneReapply)
gotErr := tc.c.saptuneReapply(context.Background(), tc.solutionReapply, tc.noteReapply)
if !cmp.Equal(gotErr, tc.wantErr, cmpopts.EquateErrors()) {
t.Errorf("saptuneReapply(%v) returned error: %v, want error: %v", tc.sapTuneReapply, gotErr, tc.wantErr)
t.Errorf("saptuneReapply(%v, %v) returned error: %v, want error: %v", tc.solutionReapply, tc.noteReapply, gotErr, tc.wantErr)
}
})
}
Expand Down

0 comments on commit 3c644b6

Please sign in to comment.