From 3ecac9fd734e62003bca42d7e96ca51db3f6a442 Mon Sep 17 00:00:00 2001 From: Inho Date: Tue, 23 Jan 2024 14:52:30 +0900 Subject: [PATCH] Added BGP Peer create, delete, get command --- cmd/create/create.go | 1 + cmd/create/create_bgpneighbor.go | 118 +++++++++++++++++++++++++++++++ cmd/delete/delete.go | 2 + cmd/delete/delete_bgpneighbor.go | 97 +++++++++++++++++++++++++ cmd/get/get.go | 1 + cmd/get/get_bgpneighbor.go | 95 +++++++++++++++++++++++++ cmd/get/type.go | 1 + pkg/api/bgp.go | 67 ++++++++++++++++++ pkg/api/client.go | 14 ++++ 9 files changed, 396 insertions(+) create mode 100644 cmd/create/create_bgpneighbor.go create mode 100644 cmd/delete/delete_bgpneighbor.go create mode 100644 cmd/get/get_bgpneighbor.go create mode 100644 pkg/api/bgp.go diff --git a/cmd/create/create.go b/cmd/create/create.go index ee5d526..5c6f237 100644 --- a/cmd/create/create.go +++ b/cmd/create/create.go @@ -57,6 +57,7 @@ Create - Service type external load-balancer, Vlan, Vxlan, Qos Policies, createCmd.AddCommand(NewCreateMirrorCmd(restOptions)) createCmd.AddCommand(NewCreateFirewallCmd(restOptions)) createCmd.AddCommand(NewCreateEndPointCmd(restOptions)) + createCmd.AddCommand(NewCreateBGPNeighborCmd(restOptions)) return createCmd } diff --git a/cmd/create/create_bgpneighbor.go b/cmd/create/create_bgpneighbor.go new file mode 100644 index 0000000..63184db --- /dev/null +++ b/cmd/create/create_bgpneighbor.go @@ -0,0 +1,118 @@ +/* + * 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 create + +import ( + "context" + "errors" + "fmt" + "loxicmd/pkg/api" + "net" + "net/http" + "os" + "strconv" + "time" + + "github.com/spf13/cobra" +) + +type CreateBGPNeighborOptions struct { + SetMultiHtop bool + RemotePort uint8 +} + +func NewCreateBGPNeighborCmd(restOptions *api.RESTOptions) *cobra.Command { + o := CreateBGPNeighborOptions{} + + var createBGPNeighborCmd = &cobra.Command{ + Use: "bgpneighbor [--remotePort=] [--setMultiHtop]", + Short: "Create a BGP Neighbor", + Long: `Create a BGP Neighbor using LoxiLB. +ex) loxicmd create bgpneighbor 10.10.10.1 64512 +`, + Aliases: []string{"bgpnei", "bgpneigh"}, + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + os.Exit(0) + } + }, + Run: func(cmd *cobra.Command, args []string) { + var BGPNeighborMod api.BGPNeighborMod + // Make BGPNeighborMod + if err := ReadCreateBGPNeighborOptions(&BGPNeighborMod, args); err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + // option + if o.RemotePort != 179 { + BGPNeighborMod.RemotePort = int(o.RemotePort) + } + if o.SetMultiHtop { + BGPNeighborMod.SetMultiHop = o.SetMultiHtop + } + resp, err := BGPNeighborAPICall(restOptions, BGPNeighborMod) + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + PrintCreateResult(resp, *restOptions) + return + } + + }, + } + createBGPNeighborCmd.Flags().BoolVarP(&o.SetMultiHtop, "setMultiHtop", "", false, "Enable Multihop BGP in the load balancer") + createBGPNeighborCmd.Flags().Uint8VarP(&o.RemotePort, "remotePort", "", 179, "BGP Port number of the remote site") + + return createBGPNeighborCmd +} + +func ReadCreateBGPNeighborOptions(o *api.BGPNeighborMod, args []string) error { + if len(args) > 2 { + return errors.New("create BGPNeighbor command get so many args") + } else if len(args) <= 1 { + return errors.New("create BGPNeighbor need args") + } + + if val := net.ParseIP(args[0]); val != nil { + o.IPaddress = args[0] + } else { + return fmt.Errorf("Peer IP '%s' is invalid format", args[0]) + } + + RemoteAs, err := strconv.Atoi(args[1]) + if err != nil { + return err + } + o.RemoteAs = RemoteAs + return nil +} + +func BGPNeighborAPICall(restOptions *api.RESTOptions, BGPNeighborModel api.BGPNeighborMod) (*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.BGPNeighbor().Create(ctx, BGPNeighborModel) +} diff --git a/cmd/delete/delete.go b/cmd/delete/delete.go index 5069476..5c9b892 100644 --- a/cmd/delete/delete.go +++ b/cmd/delete/delete.go @@ -67,6 +67,8 @@ Delete - Service type external load-balancer, Vlan, Vxlan, Qos Policies, deleteCmd.AddCommand(NewDeleteMirrorCmd(restOptions)) deleteCmd.AddCommand(NewDeleteFirewallCmd(restOptions)) deleteCmd.AddCommand(NewDeleteEndPointCmd(restOptions)) + deleteCmd.AddCommand(NewDeleteBGPNeighborCmd(restOptions)) + deleteCmd.Flags().StringVarP(&NormalConfigFile, "file", "f", "", "Config file to apply as like K8s") return deleteCmd } diff --git a/cmd/delete/delete_bgpneighbor.go b/cmd/delete/delete_bgpneighbor.go new file mode 100644 index 0000000..906b854 --- /dev/null +++ b/cmd/delete/delete_bgpneighbor.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 delete + +import ( + "context" + "errors" + "fmt" + "net" + "net/http" + "os" + "strconv" + "time" + + "loxicmd/pkg/api" + + "github.com/spf13/cobra" +) + +func DeleteBGPNeighborValidation(args []string) error { + if len(args) > 3 { + fmt.Println("delete BGPNeighbor command get so many args") + } else if len(args) <= 1 { + return errors.New("delete IP Address need args") + } + if val := net.ParseIP(args[0]); val == nil { + return fmt.Errorf("Peer IP '%s' is invalid format", args[0]) + } + if val, err := strconv.Atoi(args[1]); err != nil || val > 65535 || 0 > val { + return fmt.Errorf("RemoteAS '%s' is invalid format", args[1]) + } + return nil +} + +func NewDeleteBGPNeighborCmd(restOptions *api.RESTOptions) *cobra.Command { + + var deleteBGPNeighborCmd = &cobra.Command{ + Use: "bgpneighbor ", + Short: "Delete a BGP Neighbor peer information", + Long: `Delete a BGP Neighbor peer information in the LoxiLB.`, + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + os.Exit(0) + } + }, + Aliases: []string{"bgpnei", "bgpneigh"}, + Run: func(cmd *cobra.Command, args []string) { + if err := DeleteBGPNeighborValidation(args); err != nil { + fmt.Println("not valid or ") + fmt.Println(err) + return + } + PeerIP := args[0] + RemoteAS := args[1] + client := api.NewLoxiClient(restOptions) + ctx := context.TODO() + var cancel context.CancelFunc + if restOptions.Timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, time.Duration(restOptions.Timeout)*time.Second) + defer cancel() + } + subResources := []string{ + PeerIP, + } + qmap := map[string]string{} + qmap["remoteAs"] = fmt.Sprintf("%v", RemoteAS) + resp, err := client.BGPNeighbor().SubResources(subResources).Query(qmap).Delete(ctx) + if err != nil { + fmt.Printf("Error: Failed to delete BGPNeighbor : %s", PeerIP) + return + } + defer resp.Body.Close() + fmt.Printf("Debug: response.StatusCode: %d\n", resp.StatusCode) + if resp.StatusCode == http.StatusOK { + PrintDeleteResult(resp, *restOptions) + return + } + + }, + } + + return deleteBGPNeighborCmd +} diff --git a/cmd/get/get.go b/cmd/get/get.go index 203fb71..fdbeeb8 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -66,6 +66,7 @@ func GetCmd(restOptions *api.RESTOptions) *cobra.Command { GetCmd.AddCommand(NewGetVxlanCmd(restOptions)) GetCmd.AddCommand(NewGetEndPointCmd(restOptions)) GetCmd.AddCommand(NewGetLogLevelCmd(restOptions)) + GetCmd.AddCommand(NewGetBGPNeighborCmd(restOptions)) return GetCmd } diff --git a/cmd/get/get_bgpneighbor.go b/cmd/get/get_bgpneighbor.go new file mode 100644 index 0000000..8627a8e --- /dev/null +++ b/cmd/get/get_bgpneighbor.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 get + +import ( + "context" + "encoding/json" + "fmt" + "io" + "loxicmd/pkg/api" + "net/http" + "time" + + "github.com/spf13/cobra" +) + +func NewGetBGPNeighborCmd(restOptions *api.RESTOptions) *cobra.Command { + var GetBGPNeighborCmd = &cobra.Command{ + Use: "bgpneighbor", + Short: "Get a BGP neighbor", + Long: `It shows BGP neighbor Information in the LoxiLB`, + Aliases: []string{"bgpnei", "bgpneigh"}, + 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.BGPNeighbor().SetUrl("/config/bgp/neigh/all").Get(ctx) + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + if resp.StatusCode == http.StatusOK { + PrintGetBGPNeighborResult(resp, *restOptions) + return + } + + }, + } + + return GetBGPNeighborCmd +} + +func PrintGetBGPNeighborResult(resp *http.Response, o api.RESTOptions) { + BGPNeighborresp := api.BGPNeighborModGet{} + 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, &BGPNeighborresp); 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(BGPNeighborresp, "", " ") + fmt.Println(string(resultIndent)) + return + } + + BGPNeighborresp.Sort() + + // Table Init + table := TableInit() + + // Making BGPNeighbor data + for _, BGPNeighbor := range BGPNeighborresp.BGPAttr { + + table.SetHeader(BGPNEIGHBOR_TITLE) + data = append(data, []string{BGPNeighbor.IPaddress, fmt.Sprintf("%d", BGPNeighbor.RemoteAs), BGPNeighbor.UpDownTime, BGPNeighbor.State}) + + } + // Rendering the BGPNeighbor data to table + TableShow(data, table) +} diff --git a/cmd/get/type.go b/cmd/get/type.go index 3e32381..931799e 100644 --- a/cmd/get/type.go +++ b/cmd/get/type.go @@ -43,4 +43,5 @@ var ( FIREWALL_TITLE = []string{"Source IP", "destination IP", "min SPort", "max SPort", "min DPort", "max DPort", "protocol", "port Name", "preference", "Option"} ENDPOINT_TITLE = []string{"Host", "Name", "ptype", "port", "duration", "retries", "minDelay", "avgDelay", "maxDelay", "State"} PARAM_TITLE = []string{"Param Name", "Value"} + BGPNEIGHBOR_TITLE = []string{"Peer", "AS", "UP/Down", "State"} ) diff --git a/pkg/api/bgp.go b/pkg/api/bgp.go new file mode 100644 index 0000000..2bae6f7 --- /dev/null +++ b/pkg/api/bgp.go @@ -0,0 +1,67 @@ +/* + * 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 + +import ( + "fmt" + "sort" +) + +type BGPNeighbor struct { + CommonAPI +} + +type BGPNeighborModGet struct { + BGPAttr []BGPNeighborEntry `json:"bgpNeiAttr"` +} + +type BGPNeighborMod struct { + // IPaddress - Neighbor IP address + IPaddress string `json:"ipAddress" yaml:"ipAddress"` + // RemoteAs - ASN of remote site + RemoteAs int `json:"remoteAs" yaml:"remoteAs"` + // RemotePort - BGP port + RemotePort int `json:"remotePort" yaml:"remotePort"` + // SetMultiHop - BGP Multihop enable + SetMultiHop bool `json:"setMultiHop" yaml:"setMultiHop"` +} + +type BGPNeighborEntry struct { + // IPaddress - Neighbor IP address + IPaddress string `json:"ipAddress" yaml:"ipAddress"` + // Status - BGP connection status + State string `json:"state" yaml:"state"` + // RemoteAs - ASN of remote site + RemoteAs int `json:"remoteAs" yaml:"remoteAs"` + // UpDownTime - uptime or down time based on status + UpDownTime string `json:"updowntime" yaml:"updowntime"` +} + +type ConfigurationBGPFile struct { + TypeMeta `yaml:",inline"` + ObjectMeta `yaml:"metadata,omitempty"` + Spec BGPNeighborMod `yaml:"spec"` +} + +func (nei BGPNeighborEntry) Key() string { + return fmt.Sprintf("%s|%s|%s", nei.IPaddress, nei.State, nei.UpDownTime) +} + +func (BGPsresp BGPNeighborModGet) Sort() { + sort.Slice(BGPsresp.BGPAttr, func(i, j int) bool { + return BGPsresp.BGPAttr[i].Key() < BGPsresp.BGPAttr[j].Key() + }) +} diff --git a/pkg/api/client.go b/pkg/api/client.go index ae0703d..fdd1e46 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -40,6 +40,7 @@ const ( loxiFirewallResource = "config/firewall" loxiEndPointResource = "config/endpoint" loxiParamResource = "config/params" + loxiBGPNeighResource = "config/bgp/neigh" loxiStatusResource = "status" ) @@ -291,3 +292,16 @@ func (l *LoxiClient) Param() *Param { }, } } + +func (l *LoxiClient) BGPNeighbor() *BGPNeighbor { + return &BGPNeighbor{ + CommonAPI: CommonAPI{ + restClient: &l.restClient, + requestInfo: RequestInfo{ + provider: loxiProvider, + apiVersion: loxiApiVersion, + resource: loxiBGPNeighResource, + }, + }, + } +}