diff --git a/cmd/create/create_loadbalancer.go b/cmd/create/create_loadbalancer.go index 3d59e66..2ff2659 100644 --- a/cmd/create/create_loadbalancer.go +++ b/cmd/create/create_loadbalancer.go @@ -66,7 +66,7 @@ func ReadCreateLoadBalancerOptions(o *CreateLoadBalancerOptions, args []string) if val := net.ParseIP(args[0]); val != nil { o.ExternalIP = args[0] } else { - return fmt.Errorf("Externel IP '%s' is invalid format", args[0]) + return fmt.Errorf("externel IP '%s' is invalid format", args[0]) } return nil } diff --git a/cmd/get/get.go b/cmd/get/get.go index 470b73e..85af116 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -68,6 +68,7 @@ func GetCmd(restOptions *api.RESTOptions) *cobra.Command { GetCmd.AddCommand(NewGetLogLevelCmd(restOptions)) GetCmd.AddCommand(NewGetBGPNeighborCmd(restOptions)) GetCmd.AddCommand(NewGetHaStateCmd(restOptions)) + GetCmd.AddCommand(NewGetBFDCmd(restOptions)) return GetCmd } diff --git a/cmd/get/get_bfd.go b/cmd/get/get_bfd.go new file mode 100644 index 0000000..12021d7 --- /dev/null +++ b/cmd/get/get_bfd.go @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 NetLOX Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package get + +import ( + "context" + "encoding/json" + "fmt" + "io" + "loxicmd/pkg/api" + "net/http" + "time" + + "github.com/spf13/cobra" +) + +func NewGetBFDCmd(restOptions *api.RESTOptions) *cobra.Command { + var GetBFDCmd = &cobra.Command{ + Use: "bfd", + Short: "Get all BFD sessions", + Long: `It shows BFD Sessions in the LoxiLB`, + + Run: func(cmd *cobra.Command, args []string) { + client := api.NewLoxiClient(restOptions) + ctx := context.TODO() + var cancel context.CancelFunc + if restOptions.Timeout > 0 { + ctx, cancel = context.WithTimeout(context.TODO(), time.Duration(restOptions.Timeout)*time.Second) + defer cancel() + } + resp, err := client.Status().SetUrl("config/bfd/all").Get(ctx) + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + if resp.StatusCode == http.StatusOK { + PrintGetBFDResult(resp, *restOptions) + return + } + + }, + } + + return GetBFDCmd +} + +func PrintGetBFDResult(resp *http.Response, o api.RESTOptions) { + BFDresp := api.BFDSessionGet{} + var data [][]string + resultByte, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Printf("Error: Failed to read HTTP response: (%s)\n", err.Error()) + return + } + + if err := json.Unmarshal(resultByte, &BFDresp); err != nil { + fmt.Printf("Error: Failed to unmarshal HTTP response: (%s)\n", err.Error()) + return + } + + // if json options enable, it print as a json format. + if o.PrintOption == "json" { + resultIndent, _ := json.MarshalIndent(BFDresp, "", " ") + fmt.Println(string(resultIndent)) + return + } + + // Table Init + table := TableInit() + + // Making load balance data + for _, bfd := range BFDresp.BFDSessionAttr { + if (o.PrintOption == "wide") { + table.SetHeader(BFD_WIDE_TITLE) + data = append(data, []string{bfd.Instance, bfd.RemoteIP, bfd.SourceIP, + fmt.Sprintf("%d",bfd.Port), fmt.Sprintf("%d us",bfd.Interval), fmt.Sprintf("%d",bfd.RetryCount), bfd.State}) + } else { + table.SetHeader(BFD_TITLE) + data = append(data, []string{bfd.Instance, bfd.RemoteIP, bfd.State}) + } + } + // Rendering the load balance data to table + TableShow(data, table) +} \ No newline at end of file diff --git a/cmd/get/type.go b/cmd/get/type.go index 8f673d5..edfaeed 100644 --- a/cmd/get/type.go +++ b/cmd/get/type.go @@ -45,4 +45,6 @@ var ( PARAM_TITLE = []string{"Param Name", "Value"} BGPNEIGHBOR_TITLE = []string{"Peer", "AS", "UP/Down", "State"} HASTATE_TITLE = []string{"Instance", "HAState"} + BFD_TITLE = []string{"Instance", "RemoteIP", "State"} + BFD_WIDE_TITLE = []string{"Instance", "RemoteIP", "SourceIP", "Port", "Interval","Retry Count", "State"} ) diff --git a/cmd/set/set.go b/cmd/set/set.go index b05c95a..b0d133e 100644 --- a/cmd/set/set.go +++ b/cmd/set/set.go @@ -34,14 +34,14 @@ func SetParamCmd(restOptions *api.RESTOptions) *cobra.Command { SetParamCmd := &cobra.Command{ Use: "set", Short: "Set configurations", - Long: `Set the configuration like log-level`, + Long: `Set the configuration like log-level or bfd session`, Run: func(cmd *cobra.Command, args []string) { _ = cmd _ = args - }, } SetParamCmd.AddCommand(NewSetLogLevelCmd(restOptions)) + SetParamCmd.AddCommand(NewSetBFDCmd(restOptions)) return SetParamCmd } diff --git a/cmd/set/set_bfd.go b/cmd/set/set_bfd.go new file mode 100644 index 0000000..f529024 --- /dev/null +++ b/cmd/set/set_bfd.go @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 NetLOX Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package set + +import ( + "context" + "errors" + "fmt" + "loxicmd/pkg/api" + "net/http" + "time" + "net" + + "github.com/spf13/cobra" +) + +// NewLogLevelCmd represents the save command +func NewSetBFDCmd(restOptions *api.RESTOptions) *cobra.Command { + o := api.BFDSessionInfo{} + SetBFDCmd := &cobra.Command{ + Use: "bfd remoteIP [--instance=] [--interval=] [--retryCount=]", + Short: "bfd session configuration", + Long: `bfd session congfigration +--instance - Cluster Instance name +--interval - BFD packet Tx interval value in microseconds +--retryCount - Maximum number of retry to detect failure`, + + Aliases: []string{"bfd-session"}, + Run: func(cmd *cobra.Command, args []string) { + + // Make paramMod + if err := ReadSetBfdOptions(&o, args); err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + resp, err := SetBFDAPICall(restOptions, o) + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + if resp.StatusCode == http.StatusOK { + PrintSetResult(resp, *restOptions) + return + } + + }, + } + SetBFDCmd.Flags().StringVarP(&o.Instance, "instance", "", "default", "Specify the cluster instance name") + SetBFDCmd.Flags().Uint64VarP(&o.Interval, "interval", "", 0, "Specify the BFD packet tx interval (in microseconds)") + SetBFDCmd.Flags().Uint8VarP(&o.RetryCount, "retryCount", "", 0, "Specify the mark num to segregate a load-balancer VIP service") + + return SetBFDCmd +} + +func ReadSetBfdOptions(o *api.BFDSessionInfo, args []string) error { + if len(args) > 1 { + return errors.New("set bfd command get so many args") + } else if len(args) < 1 { + return errors.New("set bfd need args") + } + + if val := net.ParseIP(args[0]); val != nil { + o.RemoteIP = args[0] + } else { + return fmt.Errorf("remote IP '%s' is invalid format", args[0]) + } + + return nil +} + + +func SetBFDAPICall(restOptions *api.RESTOptions, bfdModel api.BFDSessionInfo) (*http.Response, error) { + client := api.NewLoxiClient(restOptions) + ctx := context.TODO() + var cancel context.CancelFunc + if restOptions.Timeout > 0 { + ctx, cancel = context.WithTimeout(context.TODO(), time.Duration(restOptions.Timeout)*time.Second) + defer cancel() + } + + return client.BFDSession().Create(ctx, bfdModel) +} diff --git a/pkg/api/bfd.go b/pkg/api/bfd.go new file mode 100644 index 0000000..51af8d7 --- /dev/null +++ b/pkg/api/bfd.go @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 NetLOX Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package api + +type BFDSession struct { + CommonAPI +} + +// HAStateGetEntry c i status get entry +// +// swagger:model CIStatusGetEntry +type BFDSessionGet struct { + BFDSessionAttr []BFDSessionInfo `json:"Attr"` +} + +type BFDSessionInfo struct { + // Instance name + Instance string `json:"instance,omitempty"` + + // RemoteIP - Remote IP for BFD session + RemoteIP string `json:"remoteIp,omitempty"` + + // Interval - Tx Interval between BFD packets + SourceIP string `json:"sourceIp,omitempty"` + + // Port - BFD session port + Port uint16 `json:"port,omitempty"` + + // Interval - Tx Interval between BFD packets + Interval uint64 `json:"interval,omitempty"` + + // RetryCount - Retry Count for detecting failure + RetryCount uint8 `json:"retryCount,omitempty"` + + // Current BFD State + State string `json:"state,omitempty"` +} + diff --git a/pkg/api/client.go b/pkg/api/client.go index fdd1e46..ef6af34 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -42,6 +42,7 @@ const ( loxiParamResource = "config/params" loxiBGPNeighResource = "config/bgp/neigh" loxiStatusResource = "status" + loxiBFDSessionResource = "config/bfd" ) type LoxiClient struct { @@ -305,3 +306,16 @@ func (l *LoxiClient) BGPNeighbor() *BGPNeighbor { }, } } + +func (l *LoxiClient) BFDSession() *BFDSession { + return &BFDSession{ + CommonAPI: CommonAPI{ + restClient: &l.restClient, + requestInfo: RequestInfo{ + provider: loxiProvider, + apiVersion: loxiApiVersion, + resource: loxiBFDSessionResource, + }, + }, + } +} \ No newline at end of file