summaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/client
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/client')
-rw-r--r--internal/pkg/client/client.go849
-rw-r--r--internal/pkg/client/client_test.go55
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")
+}