Skip to content

Commit

Permalink
Toggle switch port (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
ulrichSchreiner authored May 8, 2024
1 parent 0265699 commit 14952c4
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 36 deletions.
4 changes: 1 addition & 3 deletions cmd/completion/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ func (c *Completion) NetworkDestinationPrefixesCompletion(cmd *cobra.Command, ar
}
var prefixes []string
for _, n := range resp.Payload {
for _, prefix := range n.Destinationprefixes {
prefixes = append(prefixes, prefix)
}
prefixes = append(prefixes, n.Destinationprefixes...)
}
return prefixes, cobra.ShellCompDirectiveNoFileComp
}
18 changes: 18 additions & 0 deletions cmd/completion/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,21 @@ func (c *Completion) SwitchOSVersionListCompletion(cmd *cobra.Command, args []st
}
return names, cobra.ShellCompDirectiveNoFileComp
}

func (c *Completion) SwitchListPorts(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// there is no switch selected so we cannot get the list of ports
return nil, cobra.ShellCompDirectiveNoFileComp
}
resp, err := c.client.SwitchOperations().FindSwitch(switch_operations.NewFindSwitchParams().WithID(args[0]), nil)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, n := range resp.Payload.Nics {
if n != nil {
names = append(names, *n.Name)
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
104 changes: 103 additions & 1 deletion cmd/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,46 @@ Operational steps to replace a switch:
ValidArgsFunction: c.comp.SwitchListCompletion,
}

return genericcli.NewCmds(cmdsConfig, switchDetailCmd, switchMachinesCmd, switchReplaceCmd, switchSSHCmd, switchConsoleCmd)
switchPortCmd := &cobra.Command{
Use: "port",
Short: "sets the given switch port state up or down",
}
switchPortCmd.PersistentFlags().String("port", "", "the port to be changed.")
genericcli.Must(switchPortCmd.RegisterFlagCompletionFunc("port", c.comp.SwitchListPorts))

switchPortDescribeCmd := &cobra.Command{
Use: "describe <switch ID>",
Short: "gets the given switch port state",
Long: "shows the current actual and desired state of the port of the given switch.",
RunE: func(cmd *cobra.Command, args []string) error {
return w.describePort(args)
},
ValidArgsFunction: c.comp.SwitchListCompletion,
}

switchPortUpCmd := &cobra.Command{
Use: "up <machine ID>",
Short: "sets the given switch port state up",
Long: "sets the port status to UP so the connected machine will be able to connect to the switch.",
RunE: func(cmd *cobra.Command, args []string) error {
return w.togglePort(args, models.V1SwitchPortToggleRequestStatusUP)
},
ValidArgsFunction: c.comp.SwitchListCompletion,
}

switchPortDownCmd := &cobra.Command{
Use: "down <machine ID>",
Short: "sets the given switch port state down",
Long: "sets the port status to DOWN so the connected machine will not be able to connect to the switch.",
RunE: func(cmd *cobra.Command, args []string) error {
return w.togglePort(args, models.V1SwitchPortToggleRequestStatusDOWN)
},
ValidArgsFunction: c.comp.SwitchListCompletion,
}

switchPortCmd.AddCommand(switchPortUpCmd, switchPortDownCmd, switchPortDescribeCmd)

return genericcli.NewCmds(cmdsConfig, switchDetailCmd, switchMachinesCmd, switchReplaceCmd, switchSSHCmd, switchConsoleCmd, switchPortCmd)
}

func (c switchCmd) Get(id string) (*models.V1SwitchResponse, error) {
Expand Down Expand Up @@ -387,3 +426,66 @@ telnet console-server 7008`)
cmd.Stderr = os.Stdout
return cmd.Run()
}

func (c *switchCmd) describePort(args []string) error {
id, err := genericcli.GetExactlyOneArg(args)
if err != nil {
return err
}

portid := viper.GetString("port")
if portid == "" {
return fmt.Errorf("missing port")
}

resp, err := c.client.SwitchOperations().FindSwitch(switch_operations.NewFindSwitchParams().WithID(id), nil)
if err != nil {
return err
}
return c.dumpPortState(resp.Payload, portid)
}

func (c *switchCmd) togglePort(args []string, status string) error {
id, err := genericcli.GetExactlyOneArg(args)
if err != nil {
return err
}

portid := viper.GetString("port")
if portid == "" {
return fmt.Errorf("missing port")
}

resp, err := c.client.SwitchOperations().ToggleSwitchPort(switch_operations.NewToggleSwitchPortParams().WithID(id).WithBody(&models.V1SwitchPortToggleRequest{
Nic: &portid,
Status: &status,
}), nil)
if err != nil {
return err
}
return c.dumpPortState(resp.Payload, portid)
}

func (c *switchCmd) dumpPortState(rsp *models.V1SwitchResponse, portid string) error {
var state currentSwitchPortStateDump

for _, con := range rsp.Connections {
if *con.Nic.Name == portid {
state.Actual = *con
break
}
}
for _, desired := range rsp.Nics {
if *desired.Name == portid {
state.Desired = *desired
break
}
}

return c.describePrinter.Print(state)
}

type currentSwitchPortStateDump struct {
Actual models.V1SwitchConnection `json:"actual" yaml:"actual"`
Desired models.V1SwitchNic `json:"desired" yaml:"desired"`
}
113 changes: 88 additions & 25 deletions cmd/switch_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"encoding/json"
"testing"
"time"

Expand Down Expand Up @@ -28,9 +29,10 @@ var (
Cidrs: []string{"cidr"},
Vnis: []string{"vni"},
},
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Actual: pointer.Pointer("UP"),
},
},
},
Expand All @@ -54,9 +56,10 @@ var (
Cidrs: []string{"cidr"},
Vnis: []string{"vni"},
},
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Actual: pointer.Pointer("UP"),
},
},
Partition: partition1,
Expand All @@ -78,9 +81,10 @@ var (
Cidrs: []string{"cidr"},
Vnis: []string{"vni"},
},
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Actual: pointer.Pointer("DOWN"),
},
},
},
Expand All @@ -104,9 +108,10 @@ var (
Cidrs: []string{"cidr"},
Vnis: []string{"vni"},
},
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Mac: pointer.Pointer("a-mac"),
Name: pointer.Pointer("a-name"),
Vrf: "100",
Actual: pointer.Pointer("UP"),
},
},
Partition: partition1,
Expand Down Expand Up @@ -145,7 +150,7 @@ ID PARTITION RACK OS STATUS LAST SYNC
2 1 rack-1 🐢 ● 0s ago
`),
wantWideTable: pointer.Pointer(`
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST SYNC ERROR
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST ERROR
1 1 rack-1 SONiC (1) 1.2.3 1.2.3.4 operational 0s ago 1s 5m ago: error
2 1 rack-1 Cumulus (2) operational 0s ago 1s 5m ago: error
`),
Expand Down Expand Up @@ -192,7 +197,7 @@ ID PARTITION RACK OS STATUS LAST SYNC
1 1 rack-1 🦔 ● 0s ago
`),
wantWideTable: pointer.Pointer(`
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST SYNC ERROR
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST ERROR
1 1 rack-1 SONiC (1) 1.2.3 1.2.3.4 operational 0s ago 1s 5m ago: error
`),
template: pointer.Pointer("{{ .id }} {{ .name }}"),
Expand Down Expand Up @@ -317,18 +322,18 @@ func Test_SwitchCmd_ConnectedMachinesResult(t *testing.T) {
},
},
wantTable: pointer.Pointer(`
ID NIC NAME IDENTIFIER PARTITION RACK SIZE PRODUCT SERIAL
1 1 rack-1
└─╴machine-1 a-name a-mac 1 rack-1 n1-medium-x86 123
2 1 rack-1
└─╴machine-1 a-name a-mac 1 rack-1 n1-medium-x86 123
`),
wantWideTable: pointer.Pointer(`
ID NIC NAME IDENTIFIER PARTITION RACK SIZE HOSTNAME PRODUCT SERIAL
ID NIC NAME IDENTIFIER PARTITION RACK SIZE PRODUCT SERIAL
1 1 rack-1
└─╴machine-1 a-name a-mac 1 rack-1 n1-medium-x86 alloc-1 123
└─╴machine-1 a-name a-mac 1 rack-1 n1-medium-x86 123
2 1 rack-1
└─╴machine-1 ❓ a-name a-mac 1 rack-1 n1-medium-x86 alloc-1 123
└─╴machine-1 a-name (DOWN) a-mac 1 rack-1 n1-medium-x86 123
`),
wantWideTable: pointer.Pointer(`
ID NIC NAME IDENTIFIER PARTITION RACK SIZE HOSTNAME PRODUCT SERIAL
1 1 rack-1
└─╴machine-1 ❓ a-name a-mac 1 rack-1 n1-medium-x86 alloc-1 123
2 1 rack-1
└─╴machine-1 ❓ a-name (DOWN) a-mac 1 rack-1 n1-medium-x86 alloc-1 123
`),
template: pointer.Pointer(`{{ $machines := .machines }}{{ range .switches }}{{ $switch := . }}{{ range .connections }}{{ $switch.id }},{{ $switch.rack_id }},{{ .nic.name }},{{ .machine_id }},{{ (index $machines .machine_id).ipmi.fru.product_serial }}{{ printf "\n" }}{{ end }}{{ end }}`),
wantTemplate: pointer.Pointer(`
Expand Down Expand Up @@ -362,7 +367,7 @@ ID PARTITION RACK OS STATUS LAST SYNC
1 1 rack-1 🦔 ● 0s ago
`),
wantWideTable: pointer.Pointer(`
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST SYNC ERROR
ID PARTITION RACK OS METALCORE IP MODE LAST SYNC SYNC DURATION LAST ERROR
1 1 rack-1 SONiC (1) 1.2.3 1.2.3.4 operational 0s ago 1s 5m ago: error
`),
template: pointer.Pointer("{{ .id }} {{ .name }}"),
Expand Down Expand Up @@ -411,3 +416,61 @@ ID PARTITION RACK OS METALCORE IP MODE LAST S
tt.testCmd(t)
}
}

func Test_SwitchCmd_ToggleResult(t *testing.T) {
// first create a full copy of the test data so we can change some data
data, err := json.Marshal(switch1)
require.NoError(t, err)
var sw1Down models.V1SwitchResponse
err = json.Unmarshal(data, &sw1Down)
require.NoError(t, err)
sw1Down.Nics[0].Actual = pointer.Pointer("DOWN")

tests := []*test[currentSwitchPortStateDump]{
{
name: "query state",
cmd: func(want currentSwitchPortStateDump) []string {
return []string{"switch", "port", "describe", *switch1.ID, "--port", *switch1.Nics[0].Name}
},
mocks: &client.MetalMockFns{
SwitchOperations: func(mock *mock.Mock) {
mock.On("FindSwitch", testcommon.MatchIgnoreContext(t, switch_operations.NewFindSwitchParams().WithID(*switch1.ID)), nil).Return(&switch_operations.FindSwitchOK{
Payload: switch1,
}, nil)
},
},
want: currentSwitchPortStateDump{
Actual: *switch1.Connections[0],
Desired: *switch1.Nics[0],
},
},
{
name: "toggle down",
cmd: func(want currentSwitchPortStateDump) []string {
return []string{"switch", "port", "down", *switch1.ID, "--port", *switch1.Nics[0].Name}
},
mocks: &client.MetalMockFns{
SwitchOperations: func(mock *mock.Mock) {

mock.On("ToggleSwitchPort",
testcommon.MatchIgnoreContext(t,
switch_operations.NewToggleSwitchPortParams().
WithID(*switch1.ID).
WithBody(&models.V1SwitchPortToggleRequest{
Nic: switch1.Nics[0].Name,
Status: pointer.Pointer("DOWN"),
})), nil).Return(&switch_operations.ToggleSwitchPortOK{
Payload: &sw1Down,
}, nil)
},
},
want: currentSwitchPortStateDump{
Actual: *sw1Down.Connections[0],
Desired: *sw1Down.Nics[0],
},
},
}
for _, tt := range tests {
tt.testCmd(t)
}
}
Loading

0 comments on commit 14952c4

Please sign in to comment.