diff options
Diffstat (limited to 'internal/pkg/client')
-rw-r--r-- | internal/pkg/client/client.go | 849 | ||||
-rw-r--r-- | internal/pkg/client/client_test.go | 55 |
2 files changed, 904 insertions, 0 deletions
diff --git a/internal/pkg/client/client.go b/internal/pkg/client/client.go new file mode 100644 index 00000000..28adfd62 --- /dev/null +++ b/internal/pkg/client/client.go @@ -0,0 +1,849 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 client provides a wrapper for GoBGP's gRPC API +package client + +import ( + "fmt" + "io" + "net" + "strconv" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc" + + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/pkg/packet/bgp" + "github.com/osrg/gobgp/pkg/server" +) + +type Client struct { + conn *grpc.ClientConn + cli api.GobgpApiClient +} + +func defaultGRPCOptions() []grpc.DialOption { + return []grpc.DialOption{grpc.WithTimeout(time.Second), grpc.WithBlock(), grpc.WithInsecure()} +} + +// New returns a new Client using the given target and options for dialing +// to the grpc server. If an error occurs during dialing it will be returned and +// Client will be nil. +func New(target string, opts ...grpc.DialOption) (*Client, error) { + return NewWith(context.Background(), target, opts...) +} + +// NewWith is like New, but uses the given ctx to cancel or expire the current +// attempt to connect if it becomes Done before the connection succeeds. +func NewWith(ctx context.Context, target string, opts ...grpc.DialOption) (*Client, error) { + if target == "" { + target = ":50051" + } + if len(opts) == 0 { + opts = defaultGRPCOptions() + } + conn, err := grpc.DialContext(ctx, target, opts...) + if err != nil { + return nil, err + } + cli := api.NewGobgpApiClient(conn) + return &Client{conn: conn, cli: cli}, nil +} + +// NewFrom returns a new Client, using the given conn and cli for the +// underlying connection. The given grpc.ClientConn connection is expected to be +// initialized and paired with the api client. See New to have the connection +// dialed for you. +func NewFrom(conn *grpc.ClientConn, cli api.GobgpApiClient) *Client { + return &Client{conn: conn, cli: cli} +} + +func (cli *Client) Close() error { + return cli.conn.Close() +} + +func (cli *Client) StartServer(c *config.Global) error { + _, err := cli.cli.StartServer(context.Background(), &api.StartServerRequest{ + Global: &api.Global{ + As: c.Config.As, + RouterId: c.Config.RouterId, + ListenPort: c.Config.Port, + ListenAddresses: c.Config.LocalAddressList, + UseMultiplePaths: c.UseMultiplePaths.Config.Enabled, + }, + }) + return err +} + +func (cli *Client) StopServer() error { + _, err := cli.cli.StopServer(context.Background(), &api.StopServerRequest{}) + return err +} + +func (cli *Client) GetServer() (*config.Global, error) { + ret, err := cli.cli.GetServer(context.Background(), &api.GetServerRequest{}) + if err != nil { + return nil, err + } + return &config.Global{ + Config: config.GlobalConfig{ + As: ret.Global.As, + RouterId: ret.Global.RouterId, + Port: ret.Global.ListenPort, + LocalAddressList: ret.Global.ListenAddresses, + }, + UseMultiplePaths: config.UseMultiplePaths{ + Config: config.UseMultiplePathsConfig{ + Enabled: ret.Global.UseMultiplePaths, + }, + }, + }, nil +} + +func (cli *Client) EnableZebra(c *config.Zebra) error { + req := &api.EnableZebraRequest{ + Url: c.Config.Url, + Version: uint32(c.Config.Version), + NexthopTriggerEnable: c.Config.NexthopTriggerEnable, + NexthopTriggerDelay: uint32(c.Config.NexthopTriggerDelay), + } + for _, t := range c.Config.RedistributeRouteTypeList { + req.RouteTypes = append(req.RouteTypes, string(t)) + } + _, err := cli.cli.EnableZebra(context.Background(), req) + return err +} + +func (cli *Client) getNeighbor(name string, afi int, vrf string, enableAdvertised bool) ([]*config.Neighbor, error) { + ret, err := cli.cli.GetNeighbor(context.Background(), &api.GetNeighborRequest{EnableAdvertised: enableAdvertised, Address: name}) + if err != nil { + return nil, err + } + + neighbors := make([]*config.Neighbor, 0, len(ret.Peers)) + + for _, p := range ret.Peers { + if name != "" && name != p.Info.NeighborAddress && name != p.Conf.NeighborInterface { + continue + } + if vrf != "" && name != p.Conf.Vrf { + continue + } + if afi > 0 { + v6 := net.ParseIP(p.Info.NeighborAddress).To4() == nil + if afi == bgp.AFI_IP && v6 || afi == bgp.AFI_IP6 && !v6 { + continue + } + } + n, err := server.NewNeighborFromAPIStruct(p) + if err != nil { + return nil, err + } + neighbors = append(neighbors, n) + } + return neighbors, nil +} + +func (cli *Client) ListNeighbor() ([]*config.Neighbor, error) { + return cli.getNeighbor("", 0, "", false) +} + +func (cli *Client) ListNeighborByTransport(afi int) ([]*config.Neighbor, error) { + return cli.getNeighbor("", afi, "", false) +} + +func (cli *Client) ListNeighborByVRF(vrf string) ([]*config.Neighbor, error) { + return cli.getNeighbor("", 0, vrf, false) +} + +func (cli *Client) GetNeighbor(name string, options ...bool) (*config.Neighbor, error) { + enableAdvertised := false + if len(options) > 0 && options[0] { + enableAdvertised = true + } + ns, err := cli.getNeighbor(name, 0, "", enableAdvertised) + if err != nil { + return nil, err + } + if len(ns) == 0 { + return nil, fmt.Errorf("not found neighbor %s", name) + } + return ns[0], nil +} + +func (cli *Client) AddNeighbor(c *config.Neighbor) error { + peer := server.NewPeerFromConfigStruct(c) + _, err := cli.cli.AddNeighbor(context.Background(), &api.AddNeighborRequest{Peer: peer}) + return err +} + +func (cli *Client) DeleteNeighbor(c *config.Neighbor) error { + peer := server.NewPeerFromConfigStruct(c) + _, err := cli.cli.DeleteNeighbor(context.Background(), &api.DeleteNeighborRequest{Peer: peer}) + return err +} + +func (cli *Client) UpdateNeighbor(c *config.Neighbor, doSoftResetIn bool) (bool, error) { + peer := server.NewPeerFromConfigStruct(c) + response, err := cli.cli.UpdateNeighbor(context.Background(), &api.UpdateNeighborRequest{Peer: peer, DoSoftResetIn: doSoftResetIn}) + return response.NeedsSoftResetIn, err +} + +func (cli *Client) ShutdownNeighbor(addr, communication string) error { + _, err := cli.cli.ShutdownNeighbor(context.Background(), &api.ShutdownNeighborRequest{Address: addr, Communication: communication}) + return err +} + +func (cli *Client) ResetNeighbor(addr, communication string) error { + _, err := cli.cli.ResetNeighbor(context.Background(), &api.ResetNeighborRequest{Address: addr, Communication: communication}) + return err +} + +func (cli *Client) EnableNeighbor(addr string) error { + _, err := cli.cli.EnableNeighbor(context.Background(), &api.EnableNeighborRequest{Address: addr}) + return err +} + +func (cli *Client) DisableNeighbor(addr, communication string) error { + _, err := cli.cli.DisableNeighbor(context.Background(), &api.DisableNeighborRequest{Address: addr, Communication: communication}) + return err +} + +func (cli *Client) softreset(addr string, family bgp.RouteFamily, dir api.SoftResetNeighborRequest_SoftResetDirection) error { + _, err := cli.cli.SoftResetNeighbor(context.Background(), &api.SoftResetNeighborRequest{ + Address: addr, + Direction: dir, + }) + return err +} + +func (cli *Client) SoftResetIn(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_IN) +} + +func (cli *Client) SoftResetOut(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_OUT) +} + +func (cli *Client) SoftReset(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_BOTH) +} + +func (cli *Client) getRIB(resource api.Resource, name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + stream, err := cli.cli.GetPath(context.Background(), &api.GetPathRequest{ + Type: resource, + Family: uint32(family), + Name: name, + Prefixes: prefixes, + }) + if err != nil { + return nil, err + } + pathMap := make(map[string][]*api.Path) + for { + p, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + nlri, err := p.GetNativeNlri() + if err != nil { + return nil, err + } + nlriStr := nlri.String() + pathMap[nlriStr] = append(pathMap[nlriStr], p) + } + dstList := make([]*api.Destination, 0, len(pathMap)) + for _, pathList := range pathMap { + nlri, _ := pathList[0].GetNativeNlri() + dstList = append(dstList, &api.Destination{ + Prefix: nlri.String(), + Paths: pathList, + }) + } + return &api.Table{ + Family: uint32(family), + Destinations: dstList, + }, nil +} + +func (cli *Client) GetRIB(family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + return cli.getRIB(api.Resource_GLOBAL, "", family, prefixes) +} + +func (cli *Client) GetLocalRIB(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + return cli.getRIB(api.Resource_LOCAL, name, family, prefixes) +} + +func (cli *Client) GetAdjRIBIn(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + return cli.getRIB(api.Resource_ADJ_IN, name, family, prefixes) +} + +func (cli *Client) GetAdjRIBOut(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + return cli.getRIB(api.Resource_ADJ_OUT, name, family, prefixes) +} + +func (cli *Client) GetVRFRIB(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) { + return cli.getRIB(api.Resource_VRF, name, family, prefixes) +} + +func (cli *Client) getRIBInfo(resource api.Resource, name string, family bgp.RouteFamily) (*api.TableInfo, error) { + r, err := cli.cli.GetRibInfo(context.Background(), &api.GetRibInfoRequest{ + Info: &api.TableInfo{ + Type: resource, + Name: name, + Family: uint32(family), + }, + }) + if err != nil { + return nil, err + } else { + return r.Info, nil + } +} + +func (cli *Client) GetRIBInfo(family bgp.RouteFamily) (*api.TableInfo, error) { + return cli.getRIBInfo(api.Resource_GLOBAL, "", family) +} + +func (cli *Client) GetLocalRIBInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) { + return cli.getRIBInfo(api.Resource_LOCAL, name, family) +} + +func (cli *Client) GetAdjRIBInInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) { + return cli.getRIBInfo(api.Resource_ADJ_IN, name, family) +} + +func (cli *Client) GetAdjRIBOutInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) { + return cli.getRIBInfo(api.Resource_ADJ_OUT, name, family) +} + +type AddPathByStreamClient struct { + stream api.GobgpApi_InjectMrtClient +} + +func (c *AddPathByStreamClient) Send(paths ...*api.Path) error { + return c.stream.Send(&api.InjectMrtRequest{ + Resource: api.Resource_GLOBAL, + Paths: paths, + }) +} + +func (c *AddPathByStreamClient) Close() error { + _, err := c.stream.CloseAndRecv() + return err +} + +func (cli *Client) AddPathByStream() (*AddPathByStreamClient, error) { + stream, err := cli.cli.InjectMrt(context.Background()) + if err != nil { + return nil, err + } + return &AddPathByStreamClient{stream}, nil +} + +func (cli *Client) addPath(vrfID string, pathList []*api.Path) ([]byte, error) { + resource := api.Resource_GLOBAL + if vrfID != "" { + resource = api.Resource_VRF + } + var uuid []byte + for _, path := range pathList { + r, err := cli.cli.AddPath(context.Background(), &api.AddPathRequest{ + Resource: resource, + VrfId: vrfID, + Path: path, + }) + if err != nil { + return nil, err + } + uuid = r.Uuid + } + return uuid, nil +} + +func (cli *Client) AddPath(pathList []*api.Path) ([]byte, error) { + return cli.addPath("", pathList) +} + +func (cli *Client) AddVRFPath(vrfID string, pathList []*api.Path) ([]byte, error) { + if vrfID == "" { + return nil, fmt.Errorf("VRF ID is empty") + } + return cli.addPath(vrfID, pathList) +} + +func (cli *Client) deletePath(uuid []byte, f bgp.RouteFamily, vrfID string, pathList []*api.Path) error { + var reqs []*api.DeletePathRequest + + resource := api.Resource_GLOBAL + if vrfID != "" { + resource = api.Resource_VRF + } + switch { + case len(pathList) != 0: + for _, path := range pathList { + reqs = append(reqs, &api.DeletePathRequest{ + Resource: resource, + VrfId: vrfID, + Path: path, + }) + } + default: + reqs = append(reqs, &api.DeletePathRequest{ + Resource: resource, + VrfId: vrfID, + Uuid: uuid, + Family: uint32(f), + }) + } + + for _, req := range reqs { + if _, err := cli.cli.DeletePath(context.Background(), req); err != nil { + return err + } + } + return nil +} + +func (cli *Client) DeletePath(pathList []*api.Path) error { + return cli.deletePath(nil, bgp.RouteFamily(0), "", pathList) +} + +func (cli *Client) DeleteVRFPath(vrfID string, pathList []*api.Path) error { + if vrfID == "" { + return fmt.Errorf("VRF ID is empty") + } + return cli.deletePath(nil, bgp.RouteFamily(0), vrfID, pathList) +} + +func (cli *Client) DeletePathByUUID(uuid []byte) error { + return cli.deletePath(uuid, bgp.RouteFamily(0), "", nil) +} + +func (cli *Client) DeletePathByFamily(family bgp.RouteFamily) error { + return cli.deletePath(nil, family, "", nil) +} + +func (cli *Client) GetVRF() ([]*api.Vrf, error) { + ret, err := cli.cli.GetVrf(context.Background(), &api.GetVrfRequest{}) + if err != nil { + return nil, err + } + return ret.Vrfs, nil +} + +func (cli *Client) AddVRF(name string, id int, rd bgp.RouteDistinguisherInterface, im, ex []bgp.ExtendedCommunityInterface) error { + arg := &api.AddVrfRequest{ + Vrf: &api.Vrf{ + Name: name, + Rd: api.MarshalRD(rd), + Id: uint32(id), + ImportRt: api.MarshalRTs(im), + ExportRt: api.MarshalRTs(ex), + }, + } + _, err := cli.cli.AddVrf(context.Background(), arg) + return err +} + +func (cli *Client) DeleteVRF(name string) error { + arg := &api.DeleteVrfRequest{ + Vrf: &api.Vrf{ + Name: name, + }, + } + _, err := cli.cli.DeleteVrf(context.Background(), arg) + return err +} + +func (cli *Client) getDefinedSet(typ api.DefinedType, name string) ([]*api.DefinedSet, error) { + ret, err := cli.cli.GetDefinedSet(context.Background(), &api.GetDefinedSetRequest{ + Type: api.DefinedType(typ), + Name: name, + }) + if err != nil { + return nil, err + } + return ret.Sets, nil +} + +func (cli *Client) GetDefinedSet(typ api.DefinedType) ([]*api.DefinedSet, error) { + return cli.getDefinedSet(typ, "") +} + +func (cli *Client) GetDefinedSetByName(typ api.DefinedType, name string) (*api.DefinedSet, error) { + sets, err := cli.getDefinedSet(typ, name) + if err != nil { + return nil, err + } + if len(sets) == 0 { + return nil, fmt.Errorf("not found defined set: %s", name) + } else if len(sets) > 1 { + return nil, fmt.Errorf("invalid response for GetDefinedSetByName") + } + return sets[0], nil +} + +func (cli *Client) AddDefinedSet(d *api.DefinedSet) error { + _, err := cli.cli.AddDefinedSet(context.Background(), &api.AddDefinedSetRequest{ + Set: d, + }) + return err +} + +func (cli *Client) DeleteDefinedSet(d *api.DefinedSet, all bool) error { + _, err := cli.cli.DeleteDefinedSet(context.Background(), &api.DeleteDefinedSetRequest{ + Set: d, + All: all, + }) + return err +} + +func (cli *Client) ReplaceDefinedSet(d *api.DefinedSet) error { + _, err := cli.cli.ReplaceDefinedSet(context.Background(), &api.ReplaceDefinedSetRequest{ + Set: d, + }) + return err +} + +func (cli *Client) GetStatement() ([]*api.Statement, error) { + ret, err := cli.cli.GetStatement(context.Background(), &api.GetStatementRequest{}) + if err != nil { + return nil, err + } + return ret.Statements, nil +} + +func (cli *Client) AddStatement(t *api.Statement) error { + _, err := cli.cli.AddStatement(context.Background(), &api.AddStatementRequest{ + Statement: t, + }) + return err +} + +func (cli *Client) DeleteStatement(t *api.Statement, all bool) error { + _, err := cli.cli.DeleteStatement(context.Background(), &api.DeleteStatementRequest{ + Statement: t, + All: all, + }) + return err +} + +func (cli *Client) ReplaceStatement(t *api.Statement) error { + _, err := cli.cli.ReplaceStatement(context.Background(), &api.ReplaceStatementRequest{ + Statement: t, + }) + return err +} + +func (cli *Client) GetPolicy() ([]*api.Policy, error) { + ret, err := cli.cli.GetPolicy(context.Background(), &api.GetPolicyRequest{}) + if err != nil { + return nil, err + } + return ret.Policies, nil +} + +func (cli *Client) AddPolicy(a *api.Policy, refer bool) error { + _, err := cli.cli.AddPolicy(context.Background(), &api.AddPolicyRequest{ + Policy: a, + ReferExistingStatements: refer, + }) + return err +} + +func (cli *Client) DeletePolicy(a *api.Policy, all, preserve bool) error { + _, err := cli.cli.DeletePolicy(context.Background(), &api.DeletePolicyRequest{ + Policy: a, + All: all, + PreserveStatements: preserve, + }) + return err +} + +func (cli *Client) ReplacePolicy(a *api.Policy, refer, preserve bool) error { + _, err := cli.cli.ReplacePolicy(context.Background(), &api.ReplacePolicyRequest{ + Policy: a, + ReferExistingStatements: refer, + PreserveStatements: preserve, + }) + return err +} + +func (cli *Client) getPolicyAssignment(name string, typ api.PolicyType) (*api.PolicyAssignment, error) { + resource := api.Resource_GLOBAL + if name != "" { + resource = api.Resource_LOCAL + } + + ret, err := cli.cli.GetPolicyAssignment(context.Background(), &api.GetPolicyAssignmentRequest{ + Assignment: &api.PolicyAssignment{ + Name: name, + Resource: resource, + Type: typ, + }, + }) + if err != nil { + return nil, err + } + + return &api.PolicyAssignment{ + Name: name, + Type: typ, + Policies: ret.Assignment.Policies, + Default: ret.Assignment.Default, + }, nil +} + +func (cli *Client) GetImportPolicy() (*api.PolicyAssignment, error) { + return cli.getPolicyAssignment("", api.PolicyType_IMPORT) +} + +func (cli *Client) GetExportPolicy() (*api.PolicyAssignment, error) { + return cli.getPolicyAssignment("", api.PolicyType_EXPORT) +} + +func (cli *Client) GetRouteServerImportPolicy(name string) (*api.PolicyAssignment, error) { + return cli.getPolicyAssignment(name, api.PolicyType_IMPORT) +} + +func (cli *Client) GetRouteServerExportPolicy(name string) (*api.PolicyAssignment, error) { + return cli.getPolicyAssignment(name, api.PolicyType_EXPORT) +} + +func (cli *Client) AddPolicyAssignment(assignment *api.PolicyAssignment) error { + _, err := cli.cli.AddPolicyAssignment(context.Background(), &api.AddPolicyAssignmentRequest{ + Assignment: assignment, + }) + return err +} + +func (cli *Client) DeletePolicyAssignment(assignment *api.PolicyAssignment, all bool) error { + _, err := cli.cli.DeletePolicyAssignment(context.Background(), &api.DeletePolicyAssignmentRequest{ + Assignment: assignment, + All: all}) + return err +} + +func (cli *Client) ReplacePolicyAssignment(assignment *api.PolicyAssignment) error { + _, err := cli.cli.ReplacePolicyAssignment(context.Background(), &api.ReplacePolicyAssignmentRequest{ + Assignment: assignment, + }) + return err +} + +//func (cli *Client) EnableMrt(c *config.MrtConfig) error { +//} +// +//func (cli *Client) DisableMrt(c *config.MrtConfig) error { +//} +// + +func (cli *Client) GetRPKI() ([]*config.RpkiServer, error) { + rsp, err := cli.cli.GetRpki(context.Background(), &api.GetRpkiRequest{}) + if err != nil { + return nil, err + } + servers := make([]*config.RpkiServer, 0, len(rsp.Servers)) + for _, s := range rsp.Servers { + // Note: RpkiServerConfig.Port is uint32 type, but the TCP/UDP port is + // 16-bit length. + port, err := strconv.ParseUint(s.Conf.RemotePort, 10, 16) + if err != nil { + return nil, err + } + server := &config.RpkiServer{ + Config: config.RpkiServerConfig{ + Address: s.Conf.Address, + Port: uint32(port), + }, + State: config.RpkiServerState{ + Up: s.State.Up, + SerialNumber: s.State.Serial, + RecordsV4: s.State.RecordIpv4, + RecordsV6: s.State.RecordIpv6, + PrefixesV4: s.State.PrefixIpv4, + PrefixesV6: s.State.PrefixIpv6, + Uptime: s.State.Uptime, + Downtime: s.State.Downtime, + RpkiMessages: config.RpkiMessages{ + RpkiReceived: config.RpkiReceived{ + SerialNotify: s.State.SerialNotify, + CacheReset: s.State.CacheReset, + CacheResponse: s.State.CacheResponse, + Ipv4Prefix: s.State.ReceivedIpv4, + Ipv6Prefix: s.State.ReceivedIpv6, + EndOfData: s.State.EndOfData, + Error: s.State.Error, + }, + RpkiSent: config.RpkiSent{ + SerialQuery: s.State.SerialQuery, + ResetQuery: s.State.ResetQuery, + }, + }, + }, + } + servers = append(servers, server) + } + return servers, nil +} + +func (cli *Client) GetROA(family bgp.RouteFamily) ([]*api.Roa, error) { + rsp, err := cli.cli.GetRoa(context.Background(), &api.GetRoaRequest{ + Family: uint32(family), + }) + if err != nil { + return nil, err + } + return rsp.Roas, nil +} + +func (cli *Client) AddRPKIServer(address string, port, lifetime int) error { + _, err := cli.cli.AddRpki(context.Background(), &api.AddRpkiRequest{ + Address: address, + Port: uint32(port), + Lifetime: int64(lifetime), + }) + return err +} + +func (cli *Client) DeleteRPKIServer(address string) error { + _, err := cli.cli.DeleteRpki(context.Background(), &api.DeleteRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *Client) EnableRPKIServer(address string) error { + _, err := cli.cli.EnableRpki(context.Background(), &api.EnableRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *Client) DisableRPKIServer(address string) error { + _, err := cli.cli.DisableRpki(context.Background(), &api.DisableRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *Client) ResetRPKIServer(address string) error { + _, err := cli.cli.ResetRpki(context.Background(), &api.ResetRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *Client) SoftResetRPKIServer(address string) error { + _, err := cli.cli.SoftResetRpki(context.Background(), &api.SoftResetRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *Client) ValidateRIBWithRPKI(prefixes ...string) error { + req := &api.ValidateRibRequest{} + if len(prefixes) > 1 { + return fmt.Errorf("too many prefixes: %d", len(prefixes)) + } else if len(prefixes) == 1 { + req.Prefix = prefixes[0] + } + _, err := cli.cli.ValidateRib(context.Background(), req) + return err +} + +func (cli *Client) AddBMP(c *config.BmpServerConfig) error { + _, err := cli.cli.AddBmp(context.Background(), &api.AddBmpRequest{ + Address: c.Address, + Port: c.Port, + Type: api.AddBmpRequest_MonitoringPolicy(c.RouteMonitoringPolicy.ToInt()), + }) + return err +} + +func (cli *Client) DeleteBMP(c *config.BmpServerConfig) error { + _, err := cli.cli.DeleteBmp(context.Background(), &api.DeleteBmpRequest{ + Address: c.Address, + Port: c.Port, + }) + return err +} + +type MonitorRIBClient struct { + stream api.GobgpApi_MonitorRibClient +} + +func (c *MonitorRIBClient) Recv() (*api.Destination, error) { + return c.stream.Recv() +} + +func (cli *Client) MonitorRIB(family bgp.RouteFamily, current bool) (*MonitorRIBClient, error) { + stream, err := cli.cli.MonitorRib(context.Background(), &api.MonitorRibRequest{ + Table: &api.Table{ + Type: api.Resource_GLOBAL, + Family: uint32(family), + }, + Current: current, + }) + if err != nil { + return nil, err + } + return &MonitorRIBClient{stream}, nil +} + +func (cli *Client) MonitorAdjRIBIn(name string, family bgp.RouteFamily, current bool) (*MonitorRIBClient, error) { + stream, err := cli.cli.MonitorRib(context.Background(), &api.MonitorRibRequest{ + Table: &api.Table{ + Type: api.Resource_ADJ_IN, + Name: name, + Family: uint32(family), + }, + Current: current, + }) + if err != nil { + return nil, err + } + return &MonitorRIBClient{stream}, nil +} + +type MonitorNeighborStateClient struct { + stream api.GobgpApi_MonitorPeerStateClient +} + +func (c *MonitorNeighborStateClient) Recv() (*config.Neighbor, error) { + p, err := c.stream.Recv() + if err != nil { + return nil, err + } + return server.NewNeighborFromAPIStruct(p) +} + +func (cli *Client) MonitorNeighborState(name string, current bool) (*MonitorNeighborStateClient, error) { + stream, err := cli.cli.MonitorPeerState(context.Background(), &api.Arguments{ + Name: name, + Current: current, + }) + if err != nil { + return nil, err + } + return &MonitorNeighborStateClient{stream}, nil +} diff --git a/internal/pkg/client/client_test.go b/internal/pkg/client/client_test.go new file mode 100644 index 00000000..a737172d --- /dev/null +++ b/internal/pkg/client/client_test.go @@ -0,0 +1,55 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 client + +import ( + "testing" + "time" + + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/pkg/server" + "github.com/stretchr/testify/assert" +) + +func TestGetNeighbor(test *testing.T) { + assert := assert.New(test) + s := server.NewBgpServer() + go s.Serve() + g := server.NewGrpcServer(s, ":50051") + go g.Serve() + time.Sleep(time.Second) + cli, err := New("") + assert.Nil(err) + err = cli.StartServer(&config.Global{ + Config: config.GlobalConfig{ + As: 1, + RouterId: "1.1.1.1", + Port: 1790, + }, + }) + assert.Nil(err) + err = cli.AddNeighbor(&config.Neighbor{ + Config: config.NeighborConfig{ + NeighborAddress: "10.0.0.1", + PeerAs: 2, + }, + }) + assert.Nil(err) + _, err = cli.GetNeighbor("10.0.0.1") + assert.Nil(err) + _, err = cli.GetNeighbor("10.0.0.2") + assert.Equal(err.Error(), "not found neighbor 10.0.0.2") +} |