diff options
-rw-r--r-- | api/gobgp.pb.go | 853 | ||||
-rw-r--r-- | api/gobgp.proto | 145 | ||||
-rw-r--r-- | api/grpc.go | 318 | ||||
-rw-r--r-- | api/rest.go | 293 | ||||
-rw-r--r-- | gobgp/main.go | 713 | ||||
-rw-r--r-- | gobgpd/main.go | 6 | ||||
-rw-r--r-- | packet/bgp.go | 2 | ||||
-rw-r--r-- | server/peer.go | 243 | ||||
-rw-r--r-- | server/peer_test.go | 101 | ||||
-rw-r--r-- | server/server.go | 60 | ||||
-rw-r--r-- | table/destination.go | 8 | ||||
-rw-r--r-- | table/path.go | 16 | ||||
-rw-r--r-- | test/scenario_test/bgp_router_test.py | 22 | ||||
-rw-r--r-- | test/scenario_test/constant.py | 27 | ||||
-rw-r--r-- | test/scenario_test/docker_control.py | 70 | ||||
-rw-r--r-- | test/scenario_test/gobgp_test.py | 140 | ||||
-rw-r--r-- | test/scenario_test/peer_info.py | 19 | ||||
-rw-r--r-- | test/scenario_test/route_server_ipv4_v6_test.py | 13 | ||||
-rw-r--r-- | test/scenario_test/route_server_malformed_test.py | 12 | ||||
-rw-r--r-- | test/scenario_test/route_server_policy_test.py | 133 | ||||
-rw-r--r-- | test/scenario_test/route_server_test.py | 34 | ||||
-rw-r--r-- | test/scenario_test/scenario_test_util.py | 201 |
22 files changed, 2221 insertions, 1208 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go new file mode 100644 index 00000000..3b91d6ea --- /dev/null +++ b/api/gobgp.pb.go @@ -0,0 +1,853 @@ +// Code generated by protoc-gen-go. +// source: gobgp.proto +// DO NOT EDIT! + +/* +Package api is a generated protocol buffer package. + +It is generated from these files: + gobgp.proto + +It has these top-level messages: + Error + Arguments + PathAttr + Path + Destination + PeerConf + PeerInfo + Peer +*/ +package api + +import proto "github.com/golang/protobuf/proto" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal + +type Resource int32 + +const ( + Resource_GLOBAL Resource = 0 + Resource_LOCAL Resource = 1 + Resource_ADJ_IN Resource = 2 + Resource_ADJ_OUT Resource = 3 +) + +var Resource_name = map[int32]string{ + 0: "GLOBAL", + 1: "LOCAL", + 2: "ADJ_IN", + 3: "ADJ_OUT", +} +var Resource_value = map[string]int32{ + "GLOBAL": 0, + "LOCAL": 1, + "ADJ_IN": 2, + "ADJ_OUT": 3, +} + +func (x Resource) String() string { + return proto.EnumName(Resource_name, int32(x)) +} + +type AddressFamily int32 + +const ( + AddressFamily_IPV4 AddressFamily = 0 + AddressFamily_IPV6 AddressFamily = 1 + AddressFamily_EVPN AddressFamily = 2 +) + +var AddressFamily_name = map[int32]string{ + 0: "IPV4", + 1: "IPV6", + 2: "EVPN", +} +var AddressFamily_value = map[string]int32{ + "IPV4": 0, + "IPV6": 1, + "EVPN": 2, +} + +func (x AddressFamily) String() string { + return proto.EnumName(AddressFamily_name, int32(x)) +} + +type Error_ErrorCode int32 + +const ( + Error_SUCCESS Error_ErrorCode = 0 + Error_FAIL Error_ErrorCode = 1 +) + +var Error_ErrorCode_name = map[int32]string{ + 0: "SUCCESS", + 1: "FAIL", +} +var Error_ErrorCode_value = map[string]int32{ + "SUCCESS": 0, + "FAIL": 1, +} + +func (x Error_ErrorCode) String() string { + return proto.EnumName(Error_ErrorCode_name, int32(x)) +} + +type PathAttr_Origin int32 + +const ( + PathAttr_IGP PathAttr_Origin = 0 + PathAttr_EGP PathAttr_Origin = 1 + PathAttr_INCOMPLETE PathAttr_Origin = 2 +) + +var PathAttr_Origin_name = map[int32]string{ + 0: "IGP", + 1: "EGP", + 2: "INCOMPLETE", +} +var PathAttr_Origin_value = map[string]int32{ + "IGP": 0, + "EGP": 1, + "INCOMPLETE": 2, +} + +func (x PathAttr_Origin) String() string { + return proto.EnumName(PathAttr_Origin_name, int32(x)) +} + +type Error struct { + Code Error_ErrorCode `protobuf:"varint,1,opt,name=code,enum=api.Error_ErrorCode" json:"code,omitempty"` + Msg string `protobuf:"bytes,2,opt,name=msg" json:"msg,omitempty"` +} + +func (m *Error) Reset() { *m = Error{} } +func (m *Error) String() string { return proto.CompactTextString(m) } +func (*Error) ProtoMessage() {} + +type Arguments struct { + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=api.Resource" json:"resource,omitempty"` + Af AddressFamily `protobuf:"varint,2,opt,name=af,enum=api.AddressFamily" json:"af,omitempty"` + RouterId string `protobuf:"bytes,3,opt,name=router_id" json:"router_id,omitempty"` + Prefix string `protobuf:"bytes,4,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *Arguments) Reset() { *m = Arguments{} } +func (m *Arguments) String() string { return proto.CompactTextString(m) } +func (*Arguments) ProtoMessage() {} + +type PathAttr struct { + Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"` + Origin PathAttr_Origin `protobuf:"varint,2,opt,name=origin,enum=api.PathAttr_Origin" json:"origin,omitempty"` + AsPath []uint32 `protobuf:"varint,3,rep,name=as_path" json:"as_path,omitempty"` + Metric uint32 `protobuf:"varint,4,opt,name=metric" json:"metric,omitempty"` + Pref uint32 `protobuf:"varint,5,opt,name=pref" json:"pref,omitempty"` + Aggregator *PathAttr_Aggregator `protobuf:"bytes,6,opt,name=aggregator" json:"aggregator,omitempty"` + Communites []uint32 `protobuf:"varint,7,rep,name=communites" json:"communites,omitempty"` + Originator string `protobuf:"bytes,8,opt,name=originator" json:"originator,omitempty"` + Cluster string `protobuf:"bytes,9,opt,name=cluster" json:"cluster,omitempty"` + Value uint32 `protobuf:"varint,10,opt,name=value" json:"value,omitempty"` +} + +func (m *PathAttr) Reset() { *m = PathAttr{} } +func (m *PathAttr) String() string { return proto.CompactTextString(m) } +func (*PathAttr) ProtoMessage() {} + +func (m *PathAttr) GetAggregator() *PathAttr_Aggregator { + if m != nil { + return m.Aggregator + } + return nil +} + +type PathAttr_Aggregator struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address" json:"address,omitempty"` +} + +func (m *PathAttr_Aggregator) Reset() { *m = PathAttr_Aggregator{} } +func (m *PathAttr_Aggregator) String() string { return proto.CompactTextString(m) } +func (*PathAttr_Aggregator) ProtoMessage() {} + +type Path struct { + Network string `protobuf:"bytes,1,opt,name=network" json:"network,omitempty"` + Nexthop string `protobuf:"bytes,2,opt,name=nexthop" json:"nexthop,omitempty"` + Age int64 `protobuf:"varint,3,opt,name=age" json:"age,omitempty"` + Attrs []*PathAttr `protobuf:"bytes,4,rep,name=attrs" json:"attrs,omitempty"` + Best bool `protobuf:"varint,5,opt,name=best" json:"best,omitempty"` +} + +func (m *Path) Reset() { *m = Path{} } +func (m *Path) String() string { return proto.CompactTextString(m) } +func (*Path) ProtoMessage() {} + +func (m *Path) GetAttrs() []*PathAttr { + if m != nil { + return m.Attrs + } + return nil +} + +type Destination struct { + Prefix string `protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` + Paths []*Path `protobuf:"bytes,2,rep,name=paths" json:"paths,omitempty"` + BestPathIdx int32 `protobuf:"varint,3,opt,name=best_path_idx" json:"best_path_idx,omitempty"` +} + +func (m *Destination) Reset() { *m = Destination{} } +func (m *Destination) String() string { return proto.CompactTextString(m) } +func (*Destination) ProtoMessage() {} + +func (m *Destination) GetPaths() []*Path { + if m != nil { + return m.Paths + } + return nil +} + +type PeerConf struct { + RemoteIp string `protobuf:"bytes,1,opt,name=remote_ip" json:"remote_ip,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id" json:"id,omitempty"` + RemoteAs uint32 `protobuf:"varint,3,opt,name=remote_as" json:"remote_as,omitempty"` + CapRefresh bool `protobuf:"varint,4,opt,name=cap_refresh" json:"cap_refresh,omitempty"` + CapEnhancedRefresh bool `protobuf:"varint,5,opt,name=cap_enhanced_refresh" json:"cap_enhanced_refresh,omitempty"` + RemoteCap []int32 `protobuf:"varint,6,rep,name=remote_cap" json:"remote_cap,omitempty"` + LocalCap []int32 `protobuf:"varint,7,rep,name=local_cap" json:"local_cap,omitempty"` +} + +func (m *PeerConf) Reset() { *m = PeerConf{} } +func (m *PeerConf) String() string { return proto.CompactTextString(m) } +func (*PeerConf) ProtoMessage() {} + +type PeerInfo struct { + BgpState string `protobuf:"bytes,1,opt,name=bgp_state" json:"bgp_state,omitempty"` + AdminState string `protobuf:"bytes,2,opt,name=admin_state" json:"admin_state,omitempty"` + FsmEstablishedTransitions uint32 `protobuf:"varint,3,opt,name=fsm_established_transitions" json:"fsm_established_transitions,omitempty"` + TotalMessageOut uint32 `protobuf:"varint,4,opt,name=total_message_out" json:"total_message_out,omitempty"` + TotalMessageIn uint32 `protobuf:"varint,5,opt,name=total_message_in" json:"total_message_in,omitempty"` + UpdateMessageOut uint32 `protobuf:"varint,6,opt,name=update_message_out" json:"update_message_out,omitempty"` + UpdateMessageIn uint32 `protobuf:"varint,7,opt,name=update_message_in" json:"update_message_in,omitempty"` + KeepAliveMessageOut uint32 `protobuf:"varint,8,opt,name=keep_alive_message_out" json:"keep_alive_message_out,omitempty"` + KeepAliveMessageIn uint32 `protobuf:"varint,9,opt,name=keep_alive_message_in" json:"keep_alive_message_in,omitempty"` + OpenMessageOut uint32 `protobuf:"varint,10,opt,name=open_message_out" json:"open_message_out,omitempty"` + OpenMessageIn uint32 `protobuf:"varint,11,opt,name=open_message_in" json:"open_message_in,omitempty"` + NotificationOut uint32 `protobuf:"varint,12,opt,name=notification_out" json:"notification_out,omitempty"` + NotificationIn uint32 `protobuf:"varint,13,opt,name=notification_in" json:"notification_in,omitempty"` + RefreshMessageOut uint32 `protobuf:"varint,14,opt,name=refresh_message_out" json:"refresh_message_out,omitempty"` + RefreshMessageIn uint32 `protobuf:"varint,15,opt,name=refresh_message_in" json:"refresh_message_in,omitempty"` + DiscardedOut uint32 `protobuf:"varint,16,opt,name=discarded_out" json:"discarded_out,omitempty"` + DiscardedIn uint32 `protobuf:"varint,17,opt,name=discarded_in" json:"discarded_in,omitempty"` + Uptime int64 `protobuf:"varint,18,opt,name=uptime" json:"uptime,omitempty"` + Downtime int64 `protobuf:"varint,19,opt,name=downtime" json:"downtime,omitempty"` + LastError string `protobuf:"bytes,20,opt,name=last_error" json:"last_error,omitempty"` + Received uint32 `protobuf:"varint,21,opt,name=received" json:"received,omitempty"` + Accepted uint32 `protobuf:"varint,22,opt,name=accepted" json:"accepted,omitempty"` + Advertized uint32 `protobuf:"varint,23,opt,name=advertized" json:"advertized,omitempty"` + OutQ uint32 `protobuf:"varint,24,opt,name=out_q" json:"out_q,omitempty"` + Flops uint32 `protobuf:"varint,25,opt,name=flops" json:"flops,omitempty"` +} + +func (m *PeerInfo) Reset() { *m = PeerInfo{} } +func (m *PeerInfo) String() string { return proto.CompactTextString(m) } +func (*PeerInfo) ProtoMessage() {} + +type Peer struct { + Conf *PeerConf `protobuf:"bytes,1,opt,name=conf" json:"conf,omitempty"` + Info *PeerInfo `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"` +} + +func (m *Peer) Reset() { *m = Peer{} } +func (m *Peer) String() string { return proto.CompactTextString(m) } +func (*Peer) ProtoMessage() {} + +func (m *Peer) GetConf() *PeerConf { + if m != nil { + return m.Conf + } + return nil +} + +func (m *Peer) GetInfo() *PeerInfo { + if m != nil { + return m.Info + } + return nil +} + +func init() { + proto.RegisterEnum("api.Resource", Resource_name, Resource_value) + proto.RegisterEnum("api.AddressFamily", AddressFamily_name, AddressFamily_value) + proto.RegisterEnum("api.Error_ErrorCode", Error_ErrorCode_name, Error_ErrorCode_value) + proto.RegisterEnum("api.PathAttr_Origin", PathAttr_Origin_name, PathAttr_Origin_value) +} + +// Client API for Grpc service + +type GrpcClient interface { + GetNeighbors(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetNeighborsClient, error) + GetNeighbor(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Peer, error) + GetRib(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetRibClient, error) + GetAdjRib(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetAdjRibClient, error) + Reset(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + SoftReset(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + SoftResetIn(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + SoftResetOut(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + Shutdown(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + Enable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + Disable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) + AddPath(ctx context.Context, opts ...grpc.CallOption) (Grpc_AddPathClient, error) + DeletePath(ctx context.Context, opts ...grpc.CallOption) (Grpc_DeletePathClient, error) +} + +type grpcClient struct { + cc *grpc.ClientConn +} + +func NewGrpcClient(cc *grpc.ClientConn) GrpcClient { + return &grpcClient{cc} +} + +func (c *grpcClient) GetNeighbors(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetNeighborsClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[0], c.cc, "/api.Grpc/GetNeighbors", opts...) + if err != nil { + return nil, err + } + x := &grpcGetNeighborsClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Grpc_GetNeighborsClient interface { + Recv() (*Peer, error) + grpc.ClientStream +} + +type grpcGetNeighborsClient struct { + grpc.ClientStream +} + +func (x *grpcGetNeighborsClient) Recv() (*Peer, error) { + m := new(Peer) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcClient) GetNeighbor(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Peer, error) { + out := new(Peer) + err := grpc.Invoke(ctx, "/api.Grpc/GetNeighbor", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) GetRib(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetRibClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[1], c.cc, "/api.Grpc/GetRib", opts...) + if err != nil { + return nil, err + } + x := &grpcGetRibClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Grpc_GetRibClient interface { + Recv() (*Destination, error) + grpc.ClientStream +} + +type grpcGetRibClient struct { + grpc.ClientStream +} + +func (x *grpcGetRibClient) Recv() (*Destination, error) { + m := new(Destination) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcClient) GetAdjRib(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (Grpc_GetAdjRibClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[2], c.cc, "/api.Grpc/GetAdjRib", opts...) + if err != nil { + return nil, err + } + x := &grpcGetAdjRibClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Grpc_GetAdjRibClient interface { + Recv() (*Path, error) + grpc.ClientStream +} + +type grpcGetAdjRibClient struct { + grpc.ClientStream +} + +func (x *grpcGetAdjRibClient) Recv() (*Path, error) { + m := new(Path) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcClient) Reset(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/Reset", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) SoftReset(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/SoftReset", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) SoftResetIn(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/SoftResetIn", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) SoftResetOut(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/SoftResetOut", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) Shutdown(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/Shutdown", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) Enable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/Enable", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) Disable(ctx context.Context, in *Arguments, opts ...grpc.CallOption) (*Error, error) { + out := new(Error) + err := grpc.Invoke(ctx, "/api.Grpc/Disable", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcClient) AddPath(ctx context.Context, opts ...grpc.CallOption) (Grpc_AddPathClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[3], c.cc, "/api.Grpc/AddPath", opts...) + if err != nil { + return nil, err + } + x := &grpcAddPathClient{stream} + return x, nil +} + +type Grpc_AddPathClient interface { + Send(*Arguments) error + CloseAndRecv() (*Error, error) + grpc.ClientStream +} + +type grpcAddPathClient struct { + grpc.ClientStream +} + +func (x *grpcAddPathClient) Send(m *Arguments) error { + return x.ClientStream.SendMsg(m) +} + +func (x *grpcAddPathClient) CloseAndRecv() (*Error, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(Error) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcClient) DeletePath(ctx context.Context, opts ...grpc.CallOption) (Grpc_DeletePathClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Grpc_serviceDesc.Streams[4], c.cc, "/api.Grpc/DeletePath", opts...) + if err != nil { + return nil, err + } + x := &grpcDeletePathClient{stream} + return x, nil +} + +type Grpc_DeletePathClient interface { + Send(*Arguments) error + CloseAndRecv() (*Error, error) + grpc.ClientStream +} + +type grpcDeletePathClient struct { + grpc.ClientStream +} + +func (x *grpcDeletePathClient) Send(m *Arguments) error { + return x.ClientStream.SendMsg(m) +} + +func (x *grpcDeletePathClient) CloseAndRecv() (*Error, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(Error) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// Server API for Grpc service + +type GrpcServer interface { + GetNeighbors(*Arguments, Grpc_GetNeighborsServer) error + GetNeighbor(context.Context, *Arguments) (*Peer, error) + GetRib(*Arguments, Grpc_GetRibServer) error + GetAdjRib(*Arguments, Grpc_GetAdjRibServer) error + Reset(context.Context, *Arguments) (*Error, error) + SoftReset(context.Context, *Arguments) (*Error, error) + SoftResetIn(context.Context, *Arguments) (*Error, error) + SoftResetOut(context.Context, *Arguments) (*Error, error) + Shutdown(context.Context, *Arguments) (*Error, error) + Enable(context.Context, *Arguments) (*Error, error) + Disable(context.Context, *Arguments) (*Error, error) + AddPath(Grpc_AddPathServer) error + DeletePath(Grpc_DeletePathServer) error +} + +func RegisterGrpcServer(s *grpc.Server, srv GrpcServer) { + s.RegisterService(&_Grpc_serviceDesc, srv) +} + +func _Grpc_GetNeighbors_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Arguments) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcServer).GetNeighbors(m, &grpcGetNeighborsServer{stream}) +} + +type Grpc_GetNeighborsServer interface { + Send(*Peer) error + grpc.ServerStream +} + +type grpcGetNeighborsServer struct { + grpc.ServerStream +} + +func (x *grpcGetNeighborsServer) Send(m *Peer) error { + return x.ServerStream.SendMsg(m) +} + +func _Grpc_GetNeighbor_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).GetNeighbor(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_GetRib_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Arguments) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcServer).GetRib(m, &grpcGetRibServer{stream}) +} + +type Grpc_GetRibServer interface { + Send(*Destination) error + grpc.ServerStream +} + +type grpcGetRibServer struct { + grpc.ServerStream +} + +func (x *grpcGetRibServer) Send(m *Destination) error { + return x.ServerStream.SendMsg(m) +} + +func _Grpc_GetAdjRib_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Arguments) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcServer).GetAdjRib(m, &grpcGetAdjRibServer{stream}) +} + +type Grpc_GetAdjRibServer interface { + Send(*Path) error + grpc.ServerStream +} + +type grpcGetAdjRibServer struct { + grpc.ServerStream +} + +func (x *grpcGetAdjRibServer) Send(m *Path) error { + return x.ServerStream.SendMsg(m) +} + +func _Grpc_Reset_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).Reset(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_SoftReset_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).SoftReset(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_SoftResetIn_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).SoftResetIn(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_SoftResetOut_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).SoftResetOut(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_Shutdown_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).Shutdown(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_Enable_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).Enable(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_Disable_Handler(srv interface{}, ctx context.Context, buf []byte) (interface{}, error) { + in := new(Arguments) + if err := proto.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GrpcServer).Disable(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Grpc_AddPath_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GrpcServer).AddPath(&grpcAddPathServer{stream}) +} + +type Grpc_AddPathServer interface { + SendAndClose(*Error) error + Recv() (*Arguments, error) + grpc.ServerStream +} + +type grpcAddPathServer struct { + grpc.ServerStream +} + +func (x *grpcAddPathServer) SendAndClose(m *Error) error { + return x.ServerStream.SendMsg(m) +} + +func (x *grpcAddPathServer) Recv() (*Arguments, error) { + m := new(Arguments) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Grpc_DeletePath_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GrpcServer).DeletePath(&grpcDeletePathServer{stream}) +} + +type Grpc_DeletePathServer interface { + SendAndClose(*Error) error + Recv() (*Arguments, error) + grpc.ServerStream +} + +type grpcDeletePathServer struct { + grpc.ServerStream +} + +func (x *grpcDeletePathServer) SendAndClose(m *Error) error { + return x.ServerStream.SendMsg(m) +} + +func (x *grpcDeletePathServer) Recv() (*Arguments, error) { + m := new(Arguments) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _Grpc_serviceDesc = grpc.ServiceDesc{ + ServiceName: "api.Grpc", + HandlerType: (*GrpcServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetNeighbor", + Handler: _Grpc_GetNeighbor_Handler, + }, + { + MethodName: "Reset", + Handler: _Grpc_Reset_Handler, + }, + { + MethodName: "SoftReset", + Handler: _Grpc_SoftReset_Handler, + }, + { + MethodName: "SoftResetIn", + Handler: _Grpc_SoftResetIn_Handler, + }, + { + MethodName: "SoftResetOut", + Handler: _Grpc_SoftResetOut_Handler, + }, + { + MethodName: "Shutdown", + Handler: _Grpc_Shutdown_Handler, + }, + { + MethodName: "Enable", + Handler: _Grpc_Enable_Handler, + }, + { + MethodName: "Disable", + Handler: _Grpc_Disable_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetNeighbors", + Handler: _Grpc_GetNeighbors_Handler, + ServerStreams: true, + }, + { + StreamName: "GetRib", + Handler: _Grpc_GetRib_Handler, + ServerStreams: true, + }, + { + StreamName: "GetAdjRib", + Handler: _Grpc_GetAdjRib_Handler, + ServerStreams: true, + }, + { + StreamName: "AddPath", + Handler: _Grpc_AddPath_Handler, + ClientStreams: true, + }, + { + StreamName: "DeletePath", + Handler: _Grpc_DeletePath_Handler, + ClientStreams: true, + }, + }, +} diff --git a/api/gobgp.proto b/api/gobgp.proto new file mode 100644 index 00000000..41832653 --- /dev/null +++ b/api/gobgp.proto @@ -0,0 +1,145 @@ +// Copyright (C) 2015 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. + +syntax = "proto3"; + +package api; + +// Interface exported by the server. + +service Grpc { + rpc GetNeighbors(Arguments) returns (stream Peer) {} + rpc GetNeighbor(Arguments) returns (Peer) {} + rpc GetRib(Arguments) returns (stream Destination) {} + rpc GetAdjRib(Arguments) returns (stream Path) {} + rpc Reset(Arguments) returns (Error) {} + rpc SoftReset(Arguments) returns (Error) {} + rpc SoftResetIn(Arguments) returns (Error) {} + rpc SoftResetOut(Arguments) returns (Error) {} + rpc Shutdown(Arguments) returns (Error) {} + rpc Enable(Arguments) returns (Error) {} + rpc Disable(Arguments) returns (Error) {} + rpc AddPath(stream Arguments) returns (Error) {} + rpc DeletePath(stream Arguments) returns (Error) {} +} + +message Error { + enum ErrorCode { + SUCCESS = 0; + FAIL = 1; + } + ErrorCode code = 1; + string msg = 2; +} + +message Arguments { + Resource resource = 1; + AddressFamily af = 2; + string router_id = 3; + string prefix = 4; +} + +enum Resource { + GLOBAL = 0; + LOCAL = 1; + ADJ_IN = 2; + ADJ_OUT = 3; +} + +enum AddressFamily { + IPV4 = 0; + IPV6 = 1; + EVPN = 2; +} + +message PathAttr { + + string type = 1; + enum Origin { + IGP = 0; + EGP = 1; + INCOMPLETE = 2; + } + message Aggregator { + uint32 as = 1; + string address = 2; + } + Origin origin = 2; + repeated uint32 as_path = 3; + uint32 metric = 4; + uint32 pref = 5; + Aggregator aggregator = 6; + repeated uint32 communites = 7; + string originator = 8; + string cluster = 9; + uint32 value = 10; +} + +message Path { + string network = 1; + string nexthop = 2; + int64 age = 3; + repeated PathAttr attrs = 4; + bool best = 5; +} + +message Destination { + string prefix = 1; + repeated Path paths = 2; + int32 best_path_idx = 3; +} + +message PeerConf { + string remote_ip = 1; + string id = 2; + uint32 remote_as = 3; + bool cap_refresh = 4; + bool cap_enhanced_refresh = 5; + repeated int32 remote_cap = 6; + repeated int32 local_cap = 7; +} + +message PeerInfo { + string bgp_state = 1; + string admin_state = 2; + uint32 fsm_established_transitions = 3; + uint32 total_message_out = 4; + uint32 total_message_in = 5; + uint32 update_message_out = 6; + uint32 update_message_in = 7; + uint32 keep_alive_message_out = 8; + uint32 keep_alive_message_in = 9; + uint32 open_message_out = 10; + uint32 open_message_in = 11; + uint32 notification_out = 12; + uint32 notification_in = 13; + uint32 refresh_message_out = 14; + uint32 refresh_message_in = 15; + uint32 discarded_out = 16; + uint32 discarded_in = 17; + int64 uptime = 18; + int64 downtime = 19; + string last_error = 20; + uint32 received = 21; + uint32 accepted = 22; + uint32 advertized = 23; + uint32 out_q = 24; + uint32 flops = 25; +} + +message Peer { + PeerConf conf = 1; + PeerInfo info = 2; +} diff --git a/api/grpc.go b/api/grpc.go new file mode 100644 index 00000000..69e8d159 --- /dev/null +++ b/api/grpc.go @@ -0,0 +1,318 @@ +// Copyright (C) 2014,2015 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 api + +import ( + "fmt" + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/packet" + "golang.org/x/net/context" + "google.golang.org/grpc" + "io" + "net" +) + +const ( + _ = iota + REQ_NEIGHBOR + REQ_NEIGHBORS + REQ_ADJ_RIB_IN + REQ_ADJ_RIB_OUT + REQ_LOCAL_RIB + REQ_NEIGHBOR_SHUTDOWN + REQ_NEIGHBOR_RESET + REQ_NEIGHBOR_SOFT_RESET + REQ_NEIGHBOR_SOFT_RESET_IN + REQ_NEIGHBOR_SOFT_RESET_OUT + REQ_NEIGHBOR_ENABLE + REQ_NEIGHBOR_DISABLE + REQ_GLOBAL_RIB + REQ_GLOBAL_ADD + REQ_GLOBAL_DELETE +) + +const GRPC_PORT = 8080 + +type Server struct { + grpcServer *grpc.Server + bgpServerCh chan *GrpcRequest +} + +func (s *Server) Serve() error { + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", GRPC_PORT)) + if err != nil { + return fmt.Errorf("failed to listen: %v", err) + } + s.grpcServer.Serve(lis) + return nil +} + +func (s *Server) GetNeighbor(ctx context.Context, arg *Arguments) (*Peer, error) { + var rf bgp.RouteFamily + req := NewGrpcRequest(REQ_NEIGHBOR, arg.RouterId, rf, nil) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return nil, err + } + + return res.Data.(*Peer), nil +} + +func (s *Server) GetNeighbors(_ *Arguments, stream Grpc_GetNeighborsServer) error { + var rf bgp.RouteFamily + req := NewGrpcRequest(REQ_NEIGHBORS, "", rf, nil) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*Peer)); err != nil { + return err + } + } + + return nil +} + +func (s *Server) GetAdjRib(arg *Arguments, stream Grpc_GetAdjRibServer) error { + var reqType int + switch arg.Resource { + case Resource_ADJ_IN: + reqType = REQ_ADJ_RIB_IN + case Resource_ADJ_OUT: + reqType = REQ_ADJ_RIB_OUT + default: + return fmt.Errorf("unsupported resource type: %v", arg.Resource) + } + + var rf bgp.RouteFamily + switch arg.Af { + case AddressFamily_IPV4: + rf = bgp.RF_IPv4_UC + case AddressFamily_IPV6: + rf = bgp.RF_IPv6_UC + case AddressFamily_EVPN: + rf = bgp.RF_EVPN + default: + return fmt.Errorf("unsupported resource type: %v", arg.Af) + } + + req := NewGrpcRequest(reqType, arg.RouterId, rf, nil) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*Path)); err != nil { + return err + } + } + + return nil +} + +func (s *Server) GetRib(arg *Arguments, stream Grpc_GetRibServer) error { + var reqType int + switch arg.Resource { + case Resource_LOCAL: + reqType = REQ_LOCAL_RIB + case Resource_GLOBAL: + reqType = REQ_GLOBAL_RIB + default: + return fmt.Errorf("unsupported resource type: %v", arg.Resource) + } + + var rf bgp.RouteFamily + switch arg.Af { + case AddressFamily_IPV4: + rf = bgp.RF_IPv4_UC + case AddressFamily_IPV6: + rf = bgp.RF_IPv6_UC + case AddressFamily_EVPN: + rf = bgp.RF_EVPN + default: + return fmt.Errorf("unsupported resource type: %v", arg.Af) + } + + req := NewGrpcRequest(reqType, arg.RouterId, rf, nil) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*Destination)); err != nil { + return err + } + } + return nil +} + +func (s *Server) neighbor(reqType int, arg *Arguments) (*Error, error) { + var rf bgp.RouteFamily + switch arg.Af { + case AddressFamily_IPV4: + rf = bgp.RF_IPv4_UC + case AddressFamily_IPV6: + rf = bgp.RF_IPv6_UC + case AddressFamily_EVPN: + rf = bgp.RF_EVPN + default: + return nil, fmt.Errorf("unsupported resource type: %v", arg.Af) + } + + none := &Error{} + req := NewGrpcRequest(reqType, arg.RouterId, rf, nil) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return nil, err + } + return none, nil +} + +func (s *Server) Reset(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_RESET, arg) +} + +func (s *Server) SoftReset(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_SOFT_RESET, arg) +} + +func (s *Server) SoftResetIn(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_SOFT_RESET_IN, arg) +} + +func (s *Server) SoftResetOut(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_SOFT_RESET_OUT, arg) +} + +func (s *Server) Shutdown(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_SHUTDOWN, arg) +} + +func (s *Server) Enable(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_ENABLE, arg) +} + +func (s *Server) Disable(ctx context.Context, arg *Arguments) (*Error, error) { + return s.neighbor(REQ_NEIGHBOR_DISABLE, arg) +} + +func (s *Server) modPath(reqType int, stream grpc.ServerStream) error { + for { + var err error + var arg *Arguments + + if reqType == REQ_GLOBAL_ADD { + arg, err = stream.(Grpc_AddPathServer).Recv() + } else if reqType == REQ_GLOBAL_DELETE { + arg, err = stream.(Grpc_DeletePathServer).Recv() + } else { + return fmt.Errorf("unsupportd req: %d", reqType) + } + + if err == io.EOF { + return nil + } else if err != nil { + return err + } + + if arg.Resource != Resource_GLOBAL { + return fmt.Errorf("unsupported resource: %s", arg.Resource) + } + prefix := make(map[string]interface{}, 1) + prefix["prefix"] = arg.Prefix + + var rf bgp.RouteFamily + switch arg.Af { + case AddressFamily_IPV4: + rf = bgp.RF_IPv4_UC + case AddressFamily_IPV6: + rf = bgp.RF_IPv6_UC + case AddressFamily_EVPN: + rf = bgp.RF_EVPN + default: + return fmt.Errorf("unsupported resource type: %v", arg.Af) + } + + req := NewGrpcRequest(reqType, arg.RouterId, rf, prefix) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + } +} + +func (s *Server) AddPath(stream Grpc_AddPathServer) error { + return s.modPath(REQ_GLOBAL_ADD, stream) +} + +func (s *Server) DeletePath(stream Grpc_DeletePathServer) error { + return s.modPath(REQ_GLOBAL_DELETE, stream) +} + +type GrpcRequest struct { + RequestType int + RemoteAddr string + RouteFamily bgp.RouteFamily + ResponseCh chan *GrpcResponse + Err error + Data map[string]interface{} +} + +func NewGrpcRequest(reqType int, remoteAddr string, rf bgp.RouteFamily, d map[string]interface{}) *GrpcRequest { + r := &GrpcRequest{ + RequestType: reqType, + RouteFamily: rf, + RemoteAddr: remoteAddr, + ResponseCh: make(chan *GrpcResponse), + Data: d, + } + return r +} + +type GrpcResponse struct { + ResponseErr error + Data interface{} +} + +func (r *GrpcResponse) Err() error { + return r.ResponseErr +} + +func NewGrpcServer(port int, bgpServerCh chan *GrpcRequest) *Server { + grpcServer := grpc.NewServer() + server := &Server{ + grpcServer: grpcServer, + bgpServerCh: bgpServerCh, + } + RegisterGrpcServer(grpcServer, server) + return server +} diff --git a/api/rest.go b/api/rest.go deleted file mode 100644 index fd57253d..00000000 --- a/api/rest.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (C) 2014,2015 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 api - -import ( - "bytes" - log "github.com/Sirupsen/logrus" - "github.com/fukata/golang-stats-api-handler" - "github.com/gorilla/mux" - "github.com/osrg/gobgp/packet" - "net/http" - "net/url" - "strconv" -) - -const ( - _ = iota - REQ_NEIGHBOR - REQ_NEIGHBORS - REQ_ADJ_RIB_IN - REQ_ADJ_RIB_OUT - REQ_LOCAL_RIB - REQ_NEIGHBOR_SHUTDOWN - REQ_NEIGHBOR_RESET - REQ_NEIGHBOR_SOFT_RESET - REQ_NEIGHBOR_SOFT_RESET_IN - REQ_NEIGHBOR_SOFT_RESET_OUT - REQ_NEIGHBOR_ENABLE - REQ_NEIGHBOR_DISABLE - REQ_GLOBAL_RIB - REQ_GLOBAL_ADD - REQ_GLOBAL_DELETE -) - -const ( - BASE_VERSION = "/v1" - GLOBAL = "/bgp/global" - NEIGHBOR = "/bgp/neighbor" - NEIGHBORS = "/bgp/neighbors" - - PARAM_REMOTE_PEER_ADDR = "remotePeerAddr" - PARAM_SHOW_OBJECT = "showObject" - PARAM_OPERATION = "operation" - PARAM_ROUTE_FAMILY = "routeFamily" - - STATS = "/stats" -) - -const REST_PORT = 8080 - -// trigger struct for exchanging information in the rest and peer. -// rest and peer operated at different thread. - -type RestRequest struct { - RequestType int - RemoteAddr string - RouteFamily bgp.RouteFamily - ResponseCh chan *RestResponse - Err error - Data map[string]interface{} -} - -func NewRestRequest(reqType int, remoteAddr string, rf bgp.RouteFamily, d map[string]interface{}) *RestRequest { - r := &RestRequest{ - RequestType: reqType, - RouteFamily: rf, - RemoteAddr: remoteAddr, - ResponseCh: make(chan *RestResponse), - Data: d, - } - return r -} - -type RestResponse struct { - ResponseErr error - Data []byte -} - -func (r *RestResponse) Err() error { - return r.ResponseErr -} - -type RestServer struct { - port int - bgpServerCh chan *RestRequest -} - -func NewRestServer(port int, bgpServerCh chan *RestRequest) *RestServer { - rs := &RestServer{ - port: port, - bgpServerCh: bgpServerCh} - return rs -} - -// Main thread of rest service. -// URL than can receive. -// get state of neighbors. -// -- curl -i -X GET http://<ownIP>:8080/v1/bgp/neighbors -// get state of neighbor. -// -- curl -i -X GET http://<ownIP>:8080/v1/bgp/neighbor/<remote address of target neighbor> -// get adj-rib-in of each neighbor. -// -- curl -i -X GET http://<ownIP>:8080/v1/bgp/neighbor/<remote address of target neighbor>/adj-rib-in/<rf> -// get adj-rib-out of each neighbor. -// -- curl -i -X GET http://<ownIP>:8080/v1/bgp/neighbor/<remote address of target neighbor>/adj-rib-out/<rf> -// get local-rib of each neighbor. -// -- curl -i -X GET http://<ownIP>:8080/v1/bgp/neighbor/<remote address of target neighbor>/local-rib/<rf> -func (rs *RestServer) Serve() { - global := BASE_VERSION + GLOBAL - neighbor := BASE_VERSION + NEIGHBOR - neighbors := BASE_VERSION + NEIGHBORS - - r := mux.NewRouter() - perPeerURL := "/{" + PARAM_REMOTE_PEER_ADDR + "}" - showObjectURL := "/{" + PARAM_SHOW_OBJECT + "}" - operationURL := "/{" + PARAM_OPERATION + "}" - routeFamilyURL := "/{" + PARAM_ROUTE_FAMILY + "}" - r.HandleFunc(global+showObjectURL+routeFamilyURL, rs.GlobalGET).Methods("GET") - r.HandleFunc(global+routeFamilyURL, rs.GlobalPOST).Methods("POST") - r.HandleFunc(global+routeFamilyURL, rs.GlobalDELETE).Methods("DELETE") - r.HandleFunc(neighbors, rs.NeighborGET).Methods("GET") - r.HandleFunc(neighbor+perPeerURL, rs.NeighborGET).Methods("GET") - r.HandleFunc(neighbor+perPeerURL+showObjectURL+routeFamilyURL, rs.NeighborGET).Methods("GET") - r.HandleFunc(neighbor+perPeerURL+operationURL, rs.NeighborPOST).Methods("POST") - r.HandleFunc(neighbor+perPeerURL+operationURL+routeFamilyURL, rs.NeighborPOST).Methods("POST") - - // stats - r.HandleFunc(STATS, stats_api.Handler).Methods("GET") - - // Handler when not found url - r.NotFoundHandler = http.HandlerFunc(NotFoundHandler) - http.Handle("/", r) - - http.ListenAndServe(":"+strconv.Itoa(rs.port), nil) - -} - -func (rs *RestServer) neighbor(w http.ResponseWriter, r *http.Request, reqType int) { - params := mux.Vars(r) - remoteAddr, _ := params[PARAM_REMOTE_PEER_ADDR] - log.Debugf("Look up neighbor with the remote address : %v", remoteAddr) - var rf bgp.RouteFamily - routeFamily, ok := params[PARAM_ROUTE_FAMILY] - if ok { - switch routeFamily { - case "ipv4": - rf = bgp.RF_IPv4_UC - case "ipv6": - rf = bgp.RF_IPv6_UC - case "evpn": - rf = bgp.RF_EVPN - default: - NotFoundHandler(w, r) - return - } - } - - //Send channel of request parameter. - req := NewRestRequest(reqType, remoteAddr, rf, nil) - rs.bgpServerCh <- req - - //Wait response - res := <-req.ResponseCh - if e := res.Err(); e != nil { - log.Debug(e.Error()) - http.Error(w, e.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Write(res.Data) -} - -func (rs *RestServer) NeighborPOST(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - switch params[PARAM_OPERATION] { - case "shutdown": - rs.neighbor(w, r, REQ_NEIGHBOR_SHUTDOWN) - case "reset": - rs.neighbor(w, r, REQ_NEIGHBOR_RESET) - case "softreset": - rs.neighbor(w, r, REQ_NEIGHBOR_SOFT_RESET) - case "softresetin": - rs.neighbor(w, r, REQ_NEIGHBOR_SOFT_RESET_IN) - case "softresetout": - rs.neighbor(w, r, REQ_NEIGHBOR_SOFT_RESET_OUT) - case "enable": - rs.neighbor(w, r, REQ_NEIGHBOR_ENABLE) - case "disable": - rs.neighbor(w, r, REQ_NEIGHBOR_DISABLE) - default: - NotFoundHandler(w, r) - } -} - -func (rs *RestServer) NeighborGET(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - if _, ok := params[PARAM_REMOTE_PEER_ADDR]; !ok { - rs.neighbor(w, r, REQ_NEIGHBORS) - return - } - - if showObject, ok := params[PARAM_SHOW_OBJECT]; ok { - switch showObject { - case "local-rib": - rs.neighbor(w, r, REQ_LOCAL_RIB) - case "adj-rib-in": - rs.neighbor(w, r, REQ_ADJ_RIB_IN) - case "adj-rib-out": - rs.neighbor(w, r, REQ_ADJ_RIB_OUT) - default: - NotFoundHandler(w, r) - } - } else { - rs.neighbor(w, r, REQ_NEIGHBOR) - } -} - -func (rs *RestServer) GlobalGET(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - if showObject, ok := params[PARAM_SHOW_OBJECT]; ok { - switch showObject { - case "rib": - rs.neighbor(w, r, REQ_GLOBAL_RIB) - default: - NotFoundHandler(w, r) - } - } - -} - -func (rs *RestServer) global(w http.ResponseWriter, r *http.Request, reqType int) { - params := mux.Vars(r) - var rf bgp.RouteFamily - routeFamily, ok := params[PARAM_ROUTE_FAMILY] - if ok { - switch routeFamily { - case "ipv4": - rf = bgp.RF_IPv4_UC - case "ipv6": - rf = bgp.RF_IPv6_UC - case "evpn": - rf = bgp.RF_EVPN - default: - NotFoundHandler(w, r) - return - } - } - var buf bytes.Buffer - buf.ReadFrom(r.Body) - query, _ := url.ParseQuery(buf.String()) - d := make(map[string]interface{}) - for k, v := range query { - d[k] = v - } - req := NewRestRequest(reqType, "", rf, d) - rs.bgpServerCh <- req - - //Wait response - res := <-req.ResponseCh - if e := res.Err(); e != nil { - log.Debug(e.Error()) - http.Error(w, e.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Write(res.Data) -} - -func (rs *RestServer) GlobalPOST(w http.ResponseWriter, r *http.Request) { - rs.global(w, r, REQ_GLOBAL_ADD) -} - -func (rs *RestServer) GlobalDELETE(w http.ResponseWriter, r *http.Request) { - rs.global(w, r, REQ_GLOBAL_DELETE) -} - -func NotFoundHandler(w http.ResponseWriter, r *http.Request) { - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) -} diff --git a/gobgp/main.go b/gobgp/main.go index fd761da2..3f6f4e81 100644 --- a/gobgp/main.go +++ b/gobgp/main.go @@ -16,58 +16,21 @@ package main import ( + "bytes" "encoding/json" "fmt" "github.com/jessevdk/go-flags" - "github.com/parnurzeal/gorequest" + "github.com/osrg/gobgp/api" + "golang.org/x/net/context" + "google.golang.org/grpc" + "io" "net" - "net/http" "os" "sort" + "strings" + "time" ) -func isError(resp *http.Response) bool { - return resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated -} - -func execute(resource string, callback func(url string, r *gorequest.SuperAgent) *gorequest.SuperAgent) []byte { - r := gorequest.New() - url := globalOpts.URL + ":" + fmt.Sprint(globalOpts.Port) + "/v1/bgp/" + resource - if globalOpts.Debug { - fmt.Println(url) - } - r = callback(url, r) - resp, body, err := r.End() - if err != nil { - fmt.Print("Failed to connect to gobgpd. It runs?\n") - if globalOpts.Debug { - fmt.Println(err) - } - os.Exit(1) - } - if globalOpts.Debug || isError(resp) { - fmt.Println(body) - if isError(resp) { - os.Exit(1) - } - } - return []byte(body) -} - -func post(resource string) []byte { - f := func(url string, r *gorequest.SuperAgent) *gorequest.SuperAgent { - return r.Post(url) - } - return execute(resource, f) -} - -func get(resource string) []byte { - f := func(url string, r *gorequest.SuperAgent) *gorequest.SuperAgent { - return r.Get(url) - } - return execute(resource, f) -} - func formatTimedelta(d int64) string { u := uint64(d) neg := d < 0 @@ -89,115 +52,92 @@ func formatTimedelta(d int64) string { } } -type PeerConf struct { - RemoteIP string `json:"remote_ip"` - Id string `json:"id"` - RemoteAS uint32 `json:"remote_as"` - CapRefresh bool `json:"cap_refresh"` - CapEnhancedRefresh bool `json:"cap_enhanced_refresh"` - RemoteCap []int - LocalCap []int -} - -type PeerInfo struct { - BgpState string `json:"bgp_state"` - AdminState string - FsmEstablishedTransitions uint32 `json:"fsm_established_transitions"` - TotalMessageOut uint32 `json:"total_message_out"` - TotalMessageIn uint32 `json:"total_message_in"` - UpdateMessageOut uint32 `json:"update_message_out"` - UpdateMessageIn uint32 `json:"update_message_in"` - KeepAliveMessageOut uint32 `json:"keepalive_message_out"` - KeepAliveMessageIn uint32 `json:"keepalive_message_in"` - OpenMessageOut uint32 `json:"open_message_out"` - OpenMessageIn uint32 `json:"open_message_in"` - NotificationOut uint32 `json:"notification_out"` - NotificationIn uint32 `json:"notification_in"` - RefreshMessageOut uint32 `json:"refresh_message_out"` - RefreshMessageIn uint32 `json:"refresh_message_in"` - DiscardedOut uint32 - DiscardedIn uint32 - Uptime int64 `json:"uptime"` - Downtime int64 `json:"downtime"` - LastError string `json:"last_error"` - Received uint32 - Accepted uint32 - Advertized uint32 - OutQ int - Flops uint32 -} - -type peer struct { - Conf PeerConf - Info PeerInfo -} +var client api.GrpcClient type ShowNeighborCommand struct { } -func showNeighbor(args []string) { - p := peer{} - b := get("neighbor/" + args[0]) - e := json.Unmarshal(b, &p) +func showNeighbor(args []string) error { + id := &api.Arguments{ + RouterId: args[0], + } + p, e := client.GetNeighbor(context.Background(), id) if e != nil { fmt.Println(e) - } else { - fmt.Printf("BGP neighbor is %s, remote AS %d\n", p.Conf.RemoteIP, p.Conf.RemoteAS) - fmt.Printf(" BGP version 4, remote router ID %s\n", p.Conf.Id) - fmt.Printf(" BGP state = %s, up for %s\n", p.Info.BgpState, formatTimedelta(p.Info.Uptime)) - fmt.Printf(" BGP OutQ = %d, Flops = %d\n", p.Info.OutQ, p.Info.Flops) - fmt.Printf(" Neighbor capabilities:\n") - caps := []int{} - lookup := func(val int, l []int) bool { - for _, v := range l { - if v == val { - return true - } + return e + } + + if globalOpts.Json { + j, _ := json.Marshal(p) + fmt.Println(string(j)) + return nil + } + + fmt.Printf("BGP neighbor is %s, remote AS %d\n", p.Conf.RemoteIp, p.Conf.RemoteAs) + fmt.Printf(" BGP version 4, remote router ID %s\n", p.Conf.Id) + fmt.Printf(" BGP state = %s, up for %s\n", p.Info.BgpState, formatTimedelta(p.Info.Uptime)) + fmt.Printf(" BGP OutQ = %d, Flops = %d\n", p.Info.OutQ, p.Info.Flops) + fmt.Printf(" Neighbor capabilities:\n") + caps := []int32{} + lookup := func(val int32, l []int32) bool { + for _, v := range l { + if v == val { + return true } - return false } - caps = append(caps, p.Conf.LocalCap...) - for _, v := range p.Conf.RemoteCap { - if !lookup(v, caps) { - caps = append(caps, v) - } + return false + } + caps = append(caps, p.Conf.LocalCap...) + for _, v := range p.Conf.RemoteCap { + if !lookup(v, caps) { + caps = append(caps, v) } + } - sort.Sort(sort.IntSlice(caps)) - capdict := map[int]string{1: "MULTIPROTOCOL", - 2: "ROUTE_REFRESH", - 4: "CARRYING_LABEL_INFO", - 64: "GRACEFUL_RESTART", - 65: "FOUR_OCTET_AS_NUMBER", - 70: "ENHANCED_ROUTE_REFRESH", - 128: "ROUTE_REFRESH_CISCO"} - for _, c := range caps { - k, found := capdict[c] - if !found { - k = "UNKNOWN (" + fmt.Sprint(c) + ")" - } - support := "" - if lookup(c, p.Conf.LocalCap) { - support += "advertised" - } - if lookup(c, p.Conf.RemoteCap) { - if len(support) != 0 { - support += " and " - } - support += "received" + toInt := func(arg []int32) []int { + ret := make([]int, 0, len(arg)) + for _, v := range arg { + ret = append(ret, int(v)) + } + return ret + } + + sort.Sort(sort.IntSlice(toInt(caps))) + capdict := map[int]string{1: "MULTIPROTOCOL", + 2: "ROUTE_REFRESH", + 4: "CARRYING_LABEL_INFO", + 64: "GRACEFUL_RESTART", + 65: "FOUR_OCTET_AS_NUMBER", + 70: "ENHANCED_ROUTE_REFRESH", + 128: "ROUTE_REFRESH_CISCO"} + for _, c := range caps { + k, found := capdict[int(c)] + if !found { + k = "UNKNOWN (" + fmt.Sprint(c) + ")" + } + support := "" + if lookup(c, p.Conf.LocalCap) { + support += "advertised" + } + if lookup(c, p.Conf.RemoteCap) { + if len(support) != 0 { + support += " and " } - fmt.Printf(" %s: %s\n", k, support) - } - fmt.Print(" Message statistics:\n") - fmt.Print(" Sent Rcvd\n") - fmt.Printf(" Opens: %10d %10d\n", p.Info.OpenMessageOut, p.Info.OpenMessageIn) - fmt.Printf(" Notifications: %10d %10d\n", p.Info.NotificationOut, p.Info.NotificationIn) - fmt.Printf(" Updates: %10d %10d\n", p.Info.UpdateMessageOut, p.Info.UpdateMessageIn) - fmt.Printf(" Keepalives: %10d %10d\n", p.Info.KeepAliveMessageOut, p.Info.KeepAliveMessageIn) - fmt.Printf(" Route Refesh: %10d %10d\n", p.Info.RefreshMessageOut, p.Info.RefreshMessageIn) - fmt.Printf(" Discarded: %10d %10d\n", p.Info.DiscardedOut, p.Info.DiscardedIn) - fmt.Printf(" Total: %10d %10d\n", p.Info.TotalMessageOut, p.Info.TotalMessageIn) + support += "received" + } + fmt.Printf(" %s: %s\n", k, support) } + fmt.Print(" Message statistics:\n") + fmt.Print(" Sent Rcvd\n") + fmt.Printf(" Opens: %10d %10d\n", p.Info.OpenMessageOut, p.Info.OpenMessageIn) + fmt.Printf(" Notifications: %10d %10d\n", p.Info.NotificationOut, p.Info.NotificationIn) + fmt.Printf(" Updates: %10d %10d\n", p.Info.UpdateMessageOut, p.Info.UpdateMessageIn) + fmt.Printf(" Keepalives: %10d %10d\n", p.Info.KeepAliveMessageOut, p.Info.KeepAliveMessageIn) + fmt.Printf(" Route Refesh: %10d %10d\n", p.Info.RefreshMessageOut, p.Info.RefreshMessageIn) + fmt.Printf(" Discarded: %10d %10d\n", p.Info.DiscardedOut, p.Info.DiscardedIn) + fmt.Printf(" Total: %10d %10d\n", p.Info.TotalMessageOut, p.Info.TotalMessageIn) + + return nil } func (x *ShowNeighborCommand) Execute(args []string) error { @@ -211,9 +151,9 @@ func (x *ShowNeighborCommand) Execute(args []string) error { showNeighbor(args) } else { parser := flags.NewParser(nil, flags.Default) - parser.AddCommand("local", "", "", NewShowNeighborRibCommand(args[0], "local-rib")) - parser.AddCommand("adj-in", "", "", NewShowNeighborRibCommand(args[0], "adj-rib-in")) - parser.AddCommand("adj-out", "", "", NewShowNeighborRibCommand(args[0], "adj-rib-out")) + parser.AddCommand("local", "", "", NewShowNeighborRibCommand(args[0], api.Resource_LOCAL)) + parser.AddCommand("adj-in", "", "", NewShowNeighborRibCommand(args[0], api.Resource_ADJ_IN)) + parser.AddCommand("adj-out", "", "", NewShowNeighborRibCommand(args[0], api.Resource_ADJ_OUT)) if _, err := parser.ParseArgs(args[1:]); err != nil { os.Exit(1) } @@ -223,18 +163,10 @@ func (x *ShowNeighborCommand) Execute(args []string) error { type ShowNeighborRibCommand struct { remoteIP net.IP - resource string + resource api.Resource } -type path struct { - Network string - Nexthop string - Age float64 - Attrs []map[string]interface{} - best bool -} - -func showRoute(pathList []path, showAge bool, showBest bool) { +func showRoute(pathList []*api.Path, showAge bool, showBest bool) { var format string if showAge { format = "%-2s %-18s %-15s %-10s %-10s %-s\n" @@ -245,28 +177,35 @@ func showRoute(pathList []path, showAge bool, showBest bool) { } for _, p := range pathList { - aspath := func(attrs []map[string]interface{}) string { + aspath := func(attrs []*api.PathAttr) string { + s := bytes.NewBuffer(make([]byte, 0, 64)) + s.WriteString("[") for _, a := range attrs { - if a["Type"] == "BGP_ATTR_TYPE_AS_PATH" { - return fmt.Sprint(a["AsPath"]) + if a.Type == "BGP_ATTR_TYPE_AS_PATH" { + var ss []string + for _, as := range a.AsPath { + ss = append(ss, fmt.Sprintf("%d", as)) + } + s.WriteString(strings.Join(ss, " ")) } } - return "" + s.WriteString("]") + return s.String() } - formatAttrs := func(attrs []map[string]interface{}) string { + formatAttrs := func(attrs []*api.PathAttr) string { s := []string{} for _, a := range attrs { - switch a["Type"] { + switch a.Type { case "BGP_ATTR_TYPE_ORIGIN": - s = append(s, fmt.Sprintf("{Origin: %v}", a["Value"])) + s = append(s, fmt.Sprintf("{Origin: %s}", a.Origin)) case "BGP_ATTR_TYPE_MULTI_EXIT_DISC": - s = append(s, fmt.Sprintf("{Med: %v}", a["Metric"])) + s = append(s, fmt.Sprintf("{Med: %d}", a.Metric)) case "BGP_ATTR_TYPE_LOCAL_PREF": - s = append(s, fmt.Sprintf("{LocalPref: %v}", a["Pref"])) + s = append(s, fmt.Sprintf("{LocalPref: %v}", a.Pref)) case "BGP_ATTR_TYPE_ATOMIC_AGGREGATE": s = append(s, "AtomicAggregate") - case "BGP_ATTR_TYPE_AGGREGATE": - s = append(s, fmt.Sprintf("{Aggregate: {AS: %v, Address: %v}", a["AS"], a["Address"])) + case "BGP_ATTR_TYPE_AGGREGATOR": + s = append(s, fmt.Sprintf("{Aggregate: {AS: %d, Address: %s}", a.GetAggregator().As, a.GetAggregator().Address)) case "BGP_ATTR_TYPE_COMMUNITIES": l := []string{} known := map[uint32]string{ @@ -283,8 +222,7 @@ func showRoute(pathList []path, showAge bool, showBest bool) { 0xFFFFFF03: "NO_EXPORT_SUBCONFED", 0xFFFFFF04: "NOPEER"} - for _, vv := range a["Value"].([]interface{}) { - v := uint32(vv.(float64)) + for _, v := range a.Communites { k, found := known[v] if found { l = append(l, fmt.Sprint(k)) @@ -294,96 +232,123 @@ func showRoute(pathList []path, showAge bool, showBest bool) { } s = append(s, fmt.Sprintf("{Cummunity: %v}", l)) case "BGP_ATTR_TYPE_ORIGINATOR_ID": - s = append(s, fmt.Sprintf("{Originator: %v|", a["Address"])) + s = append(s, fmt.Sprintf("{Originator: %v|", a.Originator)) case "BGP_ATTR_TYPE_CLUSTER_LIST": - s = append(s, fmt.Sprintf("{Cluster: %v|", a["Address"])) + s = append(s, fmt.Sprintf("{Cluster: %v|", a.Cluster)) case "BGP_ATTR_TYPE_AS4_PATH", "BGP_ATTR_TYPE_MP_UNREACH_NLRI", "BGP_ATTR_TYPE_MP_REACH_NLRI", "BGP_ATTR_TYPE_NEXT_HOP", "BGP_ATTR_TYPE_AS_PATH": default: - s = append(s, fmt.Sprintf("{%v: %v}", a["Type"], a["Value"])) + s = append(s, fmt.Sprintf("{%v: %v}", a.Type, a.Value)) } } return fmt.Sprint(s) } best := "" if showBest { - if p.best { + if p.Best { best = "*>" } else { best = "* " } } if showAge { - fmt.Printf(format, best, p.Network, p.Nexthop, aspath(p.Attrs), formatTimedelta(int64(p.Age)), formatAttrs(p.Attrs)) + fmt.Printf(format, best, p.Network, p.Nexthop, aspath(p.Attrs), formatTimedelta(p.Age), formatAttrs(p.Attrs)) } else { fmt.Printf(format, best, p.Network, p.Nexthop, aspath(p.Attrs), formatAttrs(p.Attrs)) } } } -func showRibCommand(isAdj, showAge, showBest bool, b []byte) { - type dest struct { - Prefix string - Paths []path - BestPathIdx int - } - type local struct { - Destinations []dest - } - - m := []path{} - var e error - if isAdj == false { - l := local{} - e = json.Unmarshal(b, &l) - if e == nil { - for _, d := range l.Destinations { - for i, p := range d.Paths { - if i == d.BestPathIdx { - p.best = true - } - m = append(m, p) - } - } - } - } else { - e = json.Unmarshal(b, &m) - } - if e != nil { - return - } - showRoute(m, showAge, showBest) -} - func (x *ShowNeighborRibCommand) Execute(args []string) error { - var rt string + var rt api.AddressFamily if len(args) == 0 { if x.remoteIP.To4() != nil { - rt = "ipv4" + rt = api.AddressFamily_IPV4 } else { - rt = "ipv6" + rt = api.AddressFamily_IPV6 } } else { - rt = args[0] + switch args[0] { + case "ipv4": + rt = api.AddressFamily_IPV4 + case "ipv6": + rt = api.AddressFamily_IPV6 + case "evpn": + rt = api.AddressFamily_EVPN + } + } + + arg := &api.Arguments{ + Resource: x.resource, + Af: rt, + RouterId: x.remoteIP.String(), } - b := get("neighbor/" + x.remoteIP.String() + "/" + x.resource + "/" + rt) - isAdj := false + ps := []*api.Path{} showBest := false showAge := true - if x.resource == "adj-rib-out" || x.resource == "adj-rib-in" { - isAdj = true - if x.resource == "adj-rib-out" { - showAge = false - } - } - if x.resource == "local-rib" { + + switch x.resource { + case api.Resource_LOCAL: showBest = true + stream, e := client.GetRib(context.Background(), arg) + if e != nil { + return e + } + + ds := []*api.Destination{} + for { + d, e := stream.Recv() + if e == io.EOF { + break + } else if e != nil { + return e + } + ds = append(ds, d) + } + + if globalOpts.Json { + j, _ := json.Marshal(ds) + fmt.Println(string(j)) + return nil + } + + for _, d := range ds { + for idx, p := range d.Paths { + if idx == int(d.BestPathIdx) { + p.Best = true + } + ps = append(ps, p) + } + } + case api.Resource_ADJ_OUT: + showAge = false + fallthrough + case api.Resource_ADJ_IN: + stream, e := client.GetAdjRib(context.Background(), arg) + if e != nil { + return e + } + for { + p, e := stream.Recv() + if e == io.EOF { + break + } else if e != nil { + return e + } + ps = append(ps, p) + } + if globalOpts.Json { + j, _ := json.Marshal(ps) + fmt.Println(string(j)) + return nil + } } - showRibCommand(isAdj, showAge, showBest, b) + + showRoute(ps, showAge, showBest) return nil } -func NewShowNeighborRibCommand(addr, resource string) *ShowNeighborRibCommand { +func NewShowNeighborRibCommand(addr string, resource api.Resource) *ShowNeighborRibCommand { return &ShowNeighborRibCommand{ remoteIP: net.ParseIP(addr), resource: resource, @@ -394,70 +359,86 @@ type ShowNeighborsCommand struct { } func (x *ShowNeighborsCommand) Execute(args []string) error { - m := []peer{} - b := get("neighbors") - e := json.Unmarshal(b, &m) + arg := &api.Arguments{} + stream, e := client.GetNeighbors(context.Background(), arg) if e != nil { fmt.Println(e) - } else { - if globalOpts.Quiet { - for _, p := range m { - fmt.Println(p.Conf.RemoteIP) - } - return nil + return e + } + m := []*api.Peer{} + for { + p, e := stream.Recv() + if e == io.EOF { + break + } else if e != nil { + return e } - maxaddrlen := 0 - maxaslen := 0 - maxtimelen := len("Up/Down") - timedelta := []string{} - for _, p := range m { - if len(p.Conf.RemoteIP) > maxaddrlen { - maxaddrlen = len(p.Conf.RemoteIP) - } + m = append(m, p) + } - if len(fmt.Sprint(p.Conf.RemoteAS)) > maxaslen { - maxaslen = len(fmt.Sprint(p.Conf.RemoteAS)) - } - var t string - if p.Info.Uptime == 0 { - t = "never" - } else if p.Info.BgpState == "BGP_FSM_ESTABLISHED" { - t = formatTimedelta(p.Info.Uptime) - } else { - t = formatTimedelta(p.Info.Downtime) - } - if len(t) > maxtimelen { - maxtimelen = len(t) - } - timedelta = append(timedelta, t) - } - var format string - format = "%-" + fmt.Sprint(maxaddrlen) + "s" + " %" + fmt.Sprint(maxaslen) + "s" + " %" + fmt.Sprint(maxtimelen) + "s" - format += " %-11s |%11s %8s %8s\n" - fmt.Printf(format, "Peer", "AS", "Up/Down", "State", "#Advertised", "Received", "Accepted") - format_fsm := func(admin, fsm string) string { - if admin == "ADMIN_STATE_DOWN" { - return "Idle(Admin)" - } + if globalOpts.Json { + j, _ := json.Marshal(m) + fmt.Println(string(j)) + return nil + } - if fsm == "BGP_FSM_IDLE" { - return "Idle" - } else if fsm == "BGP_FSM_CONNECT" { - return "Connect" - } else if fsm == "BGP_FSM_ACTIVE" { - return "Active" - } else if fsm == "BGP_FSM_OPENSENT" { - return "Sent" - } else if fsm == "BGP_FSM_OPENCONFIRM" { - return "Confirm" - } else { - return "Establ" - } + if globalOpts.Quiet { + for _, p := range m { + fmt.Println(p.Conf.RemoteIp) + } + return nil + } + maxaddrlen := 0 + maxaslen := 0 + maxtimelen := len("Up/Down") + timedelta := []string{} + for _, p := range m { + if len(p.Conf.RemoteIp) > maxaddrlen { + maxaddrlen = len(p.Conf.RemoteIp) } - for i, p := range m { - fmt.Printf(format, p.Conf.RemoteIP, fmt.Sprint(p.Conf.RemoteAS), timedelta[i], format_fsm(p.Info.AdminState, p.Info.BgpState), fmt.Sprint(p.Info.Advertized), fmt.Sprint(p.Info.Received), fmt.Sprint(p.Info.Accepted)) + if len(fmt.Sprint(p.Conf.RemoteAs)) > maxaslen { + maxaslen = len(fmt.Sprint(p.Conf.RemoteAs)) } + var t string + if p.Info.Uptime == 0 { + t = "never" + } else if p.Info.BgpState == "BGP_FSM_ESTABLISHED" { + t = formatTimedelta(p.Info.Uptime) + } else { + t = formatTimedelta(p.Info.Downtime) + } + if len(t) > maxtimelen { + maxtimelen = len(t) + } + timedelta = append(timedelta, t) + } + var format string + format = "%-" + fmt.Sprint(maxaddrlen) + "s" + " %" + fmt.Sprint(maxaslen) + "s" + " %" + fmt.Sprint(maxtimelen) + "s" + format += " %-11s |%11s %8s %8s\n" + fmt.Printf(format, "Peer", "AS", "Up/Down", "State", "#Advertised", "Received", "Accepted") + format_fsm := func(admin, fsm string) string { + if admin == "ADMIN_STATE_DOWN" { + return "Idle(Admin)" + } + + if fsm == "BGP_FSM_IDLE" { + return "Idle" + } else if fsm == "BGP_FSM_CONNECT" { + return "Connect" + } else if fsm == "BGP_FSM_ACTIVE" { + return "Active" + } else if fsm == "BGP_FSM_OPENSENT" { + return "Sent" + } else if fsm == "BGP_FSM_OPENCONFIRM" { + return "Confirm" + } else { + return "Establ" + } + } + + for i, p := range m { + fmt.Printf(format, p.Conf.RemoteIp, fmt.Sprint(p.Conf.RemoteAs), timedelta[i], format_fsm(p.Info.AdminState, p.Info.BgpState), fmt.Sprint(p.Info.Advertized), fmt.Sprint(p.Info.Received), fmt.Sprint(p.Info.Accepted)) } return nil @@ -467,14 +448,56 @@ type ShowGlobalCommand struct { } func (x *ShowGlobalCommand) Execute(args []string) error { - var rt string + var rt api.AddressFamily if len(args) == 0 { - rt = "ipv4" + rt = api.AddressFamily_IPV4 } else { - rt = args[0] + switch args[0] { + case "ipv4": + rt = api.AddressFamily_IPV4 + case "ipv6": + rt = api.AddressFamily_IPV6 + case "evpn": + rt = api.AddressFamily_EVPN + } + } + + arg := &api.Arguments{ + Resource: api.Resource_GLOBAL, + Af: rt, + } + stream, e := client.GetRib(context.Background(), arg) + if e != nil { + return e + } + ds := []*api.Destination{} + for { + d, e := stream.Recv() + if e == io.EOF { + break + } else if e != nil { + return e + } + ds = append(ds, d) + } + + if globalOpts.Json { + j, _ := json.Marshal(ds) + fmt.Println(string(j)) + return nil } - b := get("global/rib/" + rt) - showRibCommand(false, true, true, b) + + ps := []*api.Path{} + for _, d := range ds { + for idx, p := range d.Paths { + if idx == int(d.BestPathIdx) { + p.Best = true + } + ps = append(ps, p) + } + } + + showRoute(ps, true, true) return nil } @@ -497,10 +520,54 @@ type ResetCommand struct { } func (x *ResetCommand) Execute(args []string) error { - if len(args) != 2 { - return nil + if len(args) < 2 { + return fmt.Errorf("usage: %s neighbor <router_id> [ipv4|ipv6]", x.resource) + } + + var rt api.AddressFamily + switch x.resource { + case "softreset", "softresetin", "softresetout": + if len(args) == 2 { + rt = api.AddressFamily_IPV4 + } else { + switch args[2] { + case "ipv4": + rt = api.AddressFamily_IPV4 + case "ipv6": + rt = api.AddressFamily_IPV6 + case "evpn": + rt = api.AddressFamily_EVPN + default: + return fmt.Errorf("unsupported rf: %s", args[2]) + } + } + } + + arg := &api.Arguments{ + RouterId: args[1], + Af: rt, + } + + fmt.Println(arg) + + switch x.resource { + case "reset": + client.Reset(context.Background(), arg) + case "softreset": + client.SoftReset(context.Background(), arg) + case "softresetin": + client.SoftResetIn(context.Background(), arg) + case "softresetout": + client.SoftResetOut(context.Background(), arg) + case "shutdown": + client.Shutdown(context.Background(), arg) + case "enable": + client.Enable(context.Background(), arg) + case "disable": + client.Disable(context.Background(), arg) + default: + return fmt.Errorf("unsupported command: %s", x.resource) } - post("neighbor/" + args[1] + "/" + x.resource) return nil } @@ -510,15 +577,87 @@ func NewResetCommand(resource string) *ResetCommand { } } +type PathCommand struct { + modtype string +} + +func (x *PathCommand) Execute(args []string) error { + if len(args) != 3 { + return fmt.Errorf("usage: %s global <af> <prefix>", x.modtype) + } + var rt api.AddressFamily + switch args[1] { + case "ipv4": + rt = api.AddressFamily_IPV4 + case "ipv6": + rt = api.AddressFamily_IPV6 + case "evpn": + rt = api.AddressFamily_EVPN + } + + arg := &api.Arguments{ + Resource: api.Resource_GLOBAL, + Af: rt, + Prefix: args[2], + } + + switch x.modtype { + case "add": + stream, err := client.AddPath(context.Background()) + if err != nil { + return err + } + err = stream.Send(arg) + if err != nil { + return err + } + _, err = stream.CloseAndRecv() + if err != nil { + return err + } + case "delete": + stream, err := client.DeletePath(context.Background()) + if err != nil { + return err + } + err = stream.Send(arg) + if err != nil { + return err + } + _, err = stream.CloseAndRecv() + if err != nil { + return err + } + } + return nil +} + +func NewPathCommand(modtype string) *PathCommand { + return &PathCommand{ + modtype: modtype, + } +} + var globalOpts struct { - URL string `short:"u" long:"url" description:"specifying an url" default:"http://127.0.0.1"` + Host string `short:"u" long:"url" description:"specifying an url" default:"127.0.0.1"` Port int `short:"p" long:"port" description:"specifying a port" default:"8080"` Debug bool `short:"d" long:"debug"` Quiet bool `short:"q" long:"quiet"` + Json bool `short:"j" long:"json"` } func main() { parser := flags.NewParser(&globalOpts, flags.Default) + parser.Parse() + timeout := grpc.WithTimeout(time.Second) + conn, err := grpc.Dial(fmt.Sprintf("%s:%d", globalOpts.Host, globalOpts.Port), timeout) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + defer conn.Close() + client = api.NewGrpcClient(conn) + parser.AddCommand("show", "show stuff", "get information", &ShowCommand{}) parser.AddCommand("reset", "show stuff", "get information", NewResetCommand("reset")) parser.AddCommand("softreset", "show stuff", "get information", NewResetCommand("softreset")) @@ -527,6 +666,8 @@ func main() { parser.AddCommand("shutdown", "show stuff", "get information", NewResetCommand("shutdown")) parser.AddCommand("enable", "show stuff", "get information", NewResetCommand("enable")) parser.AddCommand("disable", "show stuff", "get information", NewResetCommand("disable")) + parser.AddCommand("add", "show stuff", "get information", NewPathCommand("add")) + parser.AddCommand("delete", "show stuff", "get information", NewPathCommand("delete")) if _, err := parser.Parse(); err != nil { fmt.Println(err) diff --git a/gobgpd/main.go b/gobgpd/main.go index 090828eb..e8922226 100644 --- a/gobgpd/main.go +++ b/gobgpd/main.go @@ -143,9 +143,9 @@ func main() { bgpServer := server.NewBgpServer(bgp.BGP_PORT) go bgpServer.Serve() - // start Rest Server - restServer := api.NewRestServer(api.REST_PORT, bgpServer.RestReqCh) - go restServer.Serve() + // start grpc Server + grpcServer := api.NewGrpcServer(api.GRPC_PORT, bgpServer.GrpcReqCh) + go grpcServer.Serve() var bgpConfig *config.Bgp = nil var policyConfig *config.RoutingPolicy = nil diff --git a/packet/bgp.go b/packet/bgp.go index 464ec018..84169bcc 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -1894,7 +1894,7 @@ func (p *PathAttributeAsPath) MarshalJSON() ([]byte, error) { } return json.Marshal(struct { Type string - AsPath []uint32 + AsPath []uint32 `json:"as_path,omitempty"` }{ Type: p.Type.String(), AsPath: aslist, diff --git a/server/peer.go b/server/peer.go index 8c62d105..194998fb 100644 --- a/server/peer.go +++ b/server/peer.go @@ -288,79 +288,93 @@ func (peer *Peer) sendMessages(msgs []*bgp.BGPMessage) { } } -func (peer *Peer) handleREST(restReq *api.RestRequest) { - result := &api.RestResponse{} - switch restReq.RequestType { +func (peer *Peer) handleGrpc(grpcReq *api.GrpcRequest) { + result := &api.GrpcResponse{} + switch grpcReq.RequestType { case api.REQ_GLOBAL_ADD, api.REQ_GLOBAL_DELETE: - rf := restReq.RouteFamily - prefixes := restReq.Data["prefix"].([]string) + rf := grpcReq.RouteFamily + prefix := grpcReq.Data["prefix"].(string) var isWithdraw bool - if restReq.RequestType == api.REQ_GLOBAL_DELETE { + if grpcReq.RequestType == api.REQ_GLOBAL_DELETE { isWithdraw = true } - pList := make([]table.Path, 0, len(prefixes)) - for _, prefix := range prefixes { - var nlri bgp.AddrPrefixInterface - pattr := make([]bgp.PathAttributeInterface, 0) - pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)) - asparam := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{peer.peerInfo.AS}) - pattr = append(pattr, bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{asparam})) - - if rf == bgp.RF_IPv4_UC { - ip, net, _ := net.ParseCIDR(prefix) - if ip.To4() == nil { - result.ResponseErr = fmt.Errorf("Invalid ipv4 prefix: %s", prefix) - restReq.ResponseCh <- result - close(restReq.ResponseCh) - return - } - ones, _ := net.Mask.Size() - nlri = &bgp.NLRInfo{ - IPAddrPrefix: *bgp.NewIPAddrPrefix(uint8(ones), ip.String()), - } + var nlri bgp.AddrPrefixInterface + pattr := make([]bgp.PathAttributeInterface, 0) + pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)) + asparam := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{peer.peerInfo.AS}) + pattr = append(pattr, bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{asparam})) - pattr = append(pattr, bgp.NewPathAttributeNextHop("0.0.0.0")) - - } else if rf == bgp.RF_IPv6_UC { - ip, net, _ := net.ParseCIDR(prefix) - if ip.To16() == nil { - result.ResponseErr = fmt.Errorf("Invalid ipv6 prefix: %s", prefix) - restReq.ResponseCh <- result - close(restReq.ResponseCh) - return - } - ones, _ := net.Mask.Size() - nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String()) + if rf == bgp.RF_IPv4_UC { + ip, net, _ := net.ParseCIDR(prefix) + if ip.To4() == nil { + result.ResponseErr = fmt.Errorf("Invalid ipv4 prefix: %s", prefix) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + return + } + ones, _ := net.Mask.Size() + nlri = &bgp.NLRInfo{ + IPAddrPrefix: *bgp.NewIPAddrPrefix(uint8(ones), ip.String()), + } - pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("::", []bgp.AddrPrefixInterface{nlri})) + pattr = append(pattr, bgp.NewPathAttributeNextHop("0.0.0.0")) - } else { - result.ResponseErr = fmt.Errorf("Unsupported address family: %s", rf) - restReq.ResponseCh <- result - close(restReq.ResponseCh) + } else if rf == bgp.RF_IPv6_UC { + ip, net, _ := net.ParseCIDR(prefix) + if ip.To16() == nil { + result.ResponseErr = fmt.Errorf("Invalid ipv6 prefix: %s", prefix) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) return } + ones, _ := net.Mask.Size() + nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String()) - p := table.CreatePath(peer.peerInfo, nlri, pattr, isWithdraw, time.Now()) - pList = append(pList, p) + pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("::", []bgp.AddrPrefixInterface{nlri})) + + } else { + result.ResponseErr = fmt.Errorf("Unsupported address family: %s", rf) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + return } + p := table.CreatePath(peer.peerInfo, nlri, pattr, isWithdraw, time.Now()) + pm := &peerMsg{ msgType: PEER_MSG_PATH, - msgData: pList, + msgData: []table.Path{p}, } peer.peerMsgCh <- pm case api.REQ_LOCAL_RIB, api.REQ_GLOBAL_RIB: - // just empty so we use ipv4 for any route family - j, _ := json.Marshal(table.NewIPv4Table(0)) - if peer.fsm.adminState != ADMIN_STATE_DOWN { - if t, ok := peer.rib.Tables[restReq.RouteFamily]; ok { - j, _ = json.Marshal(t) + if peer.fsm.adminState == ADMIN_STATE_DOWN { + close(grpcReq.ResponseCh) + return + } + if t, ok := peer.rib.Tables[grpcReq.RouteFamily]; ok { + type table struct { + Destinations []*api.Destination + } + var tt table + j, _ := json.Marshal(t) + err := json.Unmarshal(j, &tt) + if err != nil { + result := &api.GrpcResponse{} + result.ResponseErr = err + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) + return + } + for _, dst := range tt.Destinations { + result := &api.GrpcResponse{} + result.Data = dst + grpcReq.ResponseCh <- result } + close(grpcReq.ResponseCh) + return } - result.Data = j case api.REQ_NEIGHBOR_SHUTDOWN: peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil) case api.REQ_NEIGHBOR_RESET: @@ -368,40 +382,55 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, nil) case api.REQ_NEIGHBOR_SOFT_RESET, api.REQ_NEIGHBOR_SOFT_RESET_IN: // soft-reconfiguration inbound - peer.sendPathsToSiblings(peer.adjRib.GetInPathList(restReq.RouteFamily)) - if restReq.RequestType == api.REQ_NEIGHBOR_SOFT_RESET_IN { + peer.sendPathsToSiblings(peer.adjRib.GetInPathList(grpcReq.RouteFamily)) + if grpcReq.RequestType == api.REQ_NEIGHBOR_SOFT_RESET_IN { break } fallthrough case api.REQ_NEIGHBOR_SOFT_RESET_OUT: - pathList := peer.adjRib.GetOutPathList(restReq.RouteFamily) + pathList := peer.adjRib.GetOutPathList(grpcReq.RouteFamily) peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList)) case api.REQ_ADJ_RIB_IN, api.REQ_ADJ_RIB_OUT: - rf := restReq.RouteFamily - if restReq.RequestType == api.REQ_ADJ_RIB_IN { - paths := peer.adjRib.GetInPathList(rf) - j, _ := json.Marshal(paths) - result.Data = j + rf := grpcReq.RouteFamily + var paths []table.Path + + if grpcReq.RequestType == api.REQ_ADJ_RIB_IN { + paths = peer.adjRib.GetInPathList(rf) log.Debugf("RouteFamily=%v adj-rib-in found : %d", rf.String(), len(paths)) } else { - paths := peer.adjRib.GetOutPathList(rf) - j, _ := json.Marshal(paths) - result.Data = j + paths = peer.adjRib.GetOutPathList(rf) log.Debugf("RouteFamily=%v adj-rib-out found : %d", rf.String(), len(paths)) } + + for _, p := range paths { + result := &api.GrpcResponse{} + path := &api.Path{} + j, _ := json.Marshal(p) + err := json.Unmarshal(j, path) + if err != nil { + result.ResponseErr = err + } else { + result.Data = path + } + grpcReq.ResponseCh <- result + } + close(grpcReq.ResponseCh) + return case api.REQ_NEIGHBOR_ENABLE, api.REQ_NEIGHBOR_DISABLE: - r := make(map[string]string) - if restReq.RequestType == api.REQ_NEIGHBOR_ENABLE { + var err api.Error + if grpcReq.RequestType == api.REQ_NEIGHBOR_ENABLE { select { case peer.fsm.adminStateCh <- ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.peerConfig.NeighborAddress, }).Debug("ADMIN_STATE_UP requested") - r["result"] = "ADMIN_STATE_UP" + err.Code = api.Error_SUCCESS + err.Msg = "ADMIN_STATE_UP" default: log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) - r["result"] = "previous request is still remaining" + err.Code = api.Error_FAIL + err.Msg = "previous request is still remaining" } } else { select { @@ -410,17 +439,18 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { "Topic": "Peer", "Key": peer.peerConfig.NeighborAddress, }).Debug("ADMIN_STATE_DOWN requested") - r["result"] = "ADMIN_STATE_DOWN" + err.Code = api.Error_SUCCESS + err.Msg = "ADMIN_STATE_DOWN" default: log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) - r["result"] = "previous request is still remaining" + err.Code = api.Error_FAIL + err.Msg = "previous request is still remaining" } } - j, _ := json.Marshal(r) - result.Data = j + result.Data = err } - restReq.ResponseCh <- result - close(restReq.ResponseCh) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) } func (peer *Peer) sendUpdateMsgFromPaths(pList []table.Path) { @@ -626,7 +656,7 @@ func (peer *Peer) handleServerMsg(m *serverMsg) { log.Warning("can not find peer: ", d.Address.String()) } case SRV_MSG_API: - peer.handleREST(m.msgData.(*api.RestRequest)) + peer.handleGrpc(m.msgData.(*api.GrpcRequest)) case SRV_MSG_POLICY_UPDATED: log.Debug("policy updated") d := m.msgData.(map[string]*policy.Policy) @@ -797,30 +827,29 @@ func (peer *Peer) PassConn(conn *net.TCPConn) { } func (peer *Peer) MarshalJSON() ([]byte, error) { + p, err := peer.ToGrpc() + if err != nil { + return nil, err + } + return json.Marshal(p) +} + +func (peer *Peer) ToGrpc() (*api.Peer, error) { f := peer.fsm c := f.peerConfig - p := make(map[string]interface{}) - capList := make([]int, 0) + capList := make([]int32, 0, len(peer.capMap)) for k, _ := range peer.capMap { - capList = append(capList, int(k)) + capList = append(capList, int32(k)) } - p["conf"] = struct { - RemoteIP string `json:"remote_ip"` - Id string `json:"id"` - RemoteAS uint32 `json:"remote_as"` - CapRefresh bool `json:"cap_refresh"` - CapEnhancedRefresh bool `json:"cap_enhanced_refresh"` - RemoteCap []int - LocalCap []int - }{ - RemoteIP: c.NeighborAddress.String(), + conf := &api.PeerConf{ + RemoteIp: c.NeighborAddress.String(), Id: peer.peerInfo.ID.To4().String(), - RemoteAS: c.PeerAs, + RemoteAs: c.PeerAs, RemoteCap: capList, - LocalCap: []int{int(bgp.BGP_CAP_MULTIPROTOCOL), int(bgp.BGP_CAP_ROUTE_REFRESH), int(bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER)}, + LocalCap: []int32{int32(bgp.BGP_CAP_MULTIPROTOCOL), int32(bgp.BGP_CAP_ROUTE_REFRESH), int32(bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER)}, } s := c.BgpNeighborCommonState @@ -845,34 +874,7 @@ func (peer *Peer) MarshalJSON() ([]byte, error) { } } - p["info"] = struct { - BgpState string `json:"bgp_state"` - AdminState string - FsmEstablishedTransitions uint32 `json:"fsm_established_transitions"` - TotalMessageOut uint32 `json:"total_message_out"` - TotalMessageIn uint32 `json:"total_message_in"` - UpdateMessageOut uint32 `json:"update_message_out"` - UpdateMessageIn uint32 `json:"update_message_in"` - KeepAliveMessageOut uint32 `json:"keepalive_message_out"` - KeepAliveMessageIn uint32 `json:"keepalive_message_in"` - OpenMessageOut uint32 `json:"open_message_out"` - OpenMessageIn uint32 `json:"open_message_in"` - NotificationOut uint32 `json:"notification_out"` - NotificationIn uint32 `json:"notification_in"` - RefreshMessageOut uint32 `json:"refresh_message_out"` - RefreshMessageIn uint32 `json:"refresh_message_in"` - DiscardedOut uint32 - DiscardedIn uint32 - Uptime int64 `json:"uptime"` - Downtime int64 `json:"downtime"` - LastError string `json:"last_error"` - Received uint32 - Accepted uint32 - Advertized uint32 - OutQ int - Flops uint32 - }{ - + info := &api.PeerInfo{ BgpState: f.state.String(), AdminState: f.adminState.String(), FsmEstablishedTransitions: s.EstablishedCount, @@ -895,9 +897,12 @@ func (peer *Peer) MarshalJSON() ([]byte, error) { Received: received, Accepted: accepted, Advertized: advertized, - OutQ: len(peer.outgoing), + OutQ: uint32(len(peer.outgoing)), Flops: s.Flops, } - return json.Marshal(p) + return &api.Peer{ + Conf: conf, + Info: info, + }, nil } diff --git a/server/peer_test.go b/server/peer_test.go index 90c64960..cae3b0c7 100644 --- a/server/peer_test.go +++ b/server/peer_test.go @@ -17,8 +17,6 @@ package server import ( "fmt" - //"encoding/json" - "encoding/json" log "github.com/Sirupsen/logrus" "github.com/osrg/gobgp/api" "github.com/osrg/gobgp/config" @@ -123,17 +121,16 @@ func TestPeerAdminShutdownWhileEstablished(t *testing.T) { peer.connCh <- m waitUntil(assert, bgp.BGP_FSM_ESTABLISHED, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) @@ -166,17 +163,16 @@ func TestPeerAdminShutdownWhileIdle(t *testing.T) { waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) @@ -201,17 +197,16 @@ func TestPeerAdminShutdownWhileActive(t *testing.T) { waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) @@ -238,17 +233,16 @@ func TestPeerAdminShutdownWhileOpensent(t *testing.T) { peer.connCh <- m waitUntil(assert, bgp.BGP_FSM_OPENSENT, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) @@ -283,17 +277,16 @@ func TestPeerAdminShutdownWhileOpenconfirm(t *testing.T) { peer.connCh <- m waitUntil(assert, bgp.BGP_FSM_OPENCONFIRM, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) @@ -334,32 +327,30 @@ func TestPeerAdminEnable(t *testing.T) { waitUntil(assert, bgp.BGP_FSM_ESTABLISHED, peer, 1000) // shutdown peer at first - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_DOWN", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) // enable peer - restReq = api.NewRestRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq = api.NewGrpcRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg = &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result = <-restReq.ResponseCh - res = make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("ADMIN_STATE_UP", res["result"]) + result = <-grpcReq.ResponseCh + err = result.Data.(api.Error) + assert.Equal(err.Code, api.Error_SUCCESS) waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, (HOLDTIME_IDLE+1)*1000) assert.Equal(bgp.BGP_FSM_ACTIVE, peer.fsm.state) @@ -397,31 +388,29 @@ func TestPeerAdminShutdownReject(t *testing.T) { peer.connCh <- m waitUntil(assert, bgp.BGP_FSM_OPENSENT, peer, 1000) - restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq := api.NewGrpcRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.fsm.adminStateCh <- ADMIN_STATE_DOWN peer.serverMsgCh <- msg - result := <-restReq.ResponseCh - res := make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("previous request is still remaining", res["result"]) + result := <-grpcReq.ResponseCh + err := result.Data.(api.Error) + assert.Equal(err.Code, api.Error_FAIL) - restReq = api.NewRestRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) + grpcReq = api.NewGrpcRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0", bgp.RF_IPv4_UC, nil) msg = &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } peer.serverMsgCh <- msg - result = <-restReq.ResponseCh - res = make(map[string]string) - json.Unmarshal(result.Data, &res) - assert.Equal("previous request is still remaining", res["result"]) + result = <-grpcReq.ResponseCh + err = result.Data.(api.Error) + assert.Equal(err.Code, api.Error_FAIL) waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) diff --git a/server/server.go b/server/server.go index a47f9177..8212a1a0 100644 --- a/server/server.go +++ b/server/server.go @@ -16,7 +16,6 @@ package server import ( - "encoding/json" "fmt" log "github.com/Sirupsen/logrus" "github.com/osrg/gobgp/api" @@ -61,7 +60,7 @@ type BgpServer struct { globalTypeCh chan config.Global addedPeerCh chan config.Neighbor deletedPeerCh chan config.Neighbor - RestReqCh chan *api.RestRequest + GrpcReqCh chan *api.GrpcRequest listenPort int peerMap map[string]peerMapInfo globalRib *Peer @@ -74,7 +73,7 @@ func NewBgpServer(port int) *BgpServer { b.globalTypeCh = make(chan config.Global) b.addedPeerCh = make(chan config.Neighbor) b.deletedPeerCh = make(chan config.Neighbor) - b.RestReqCh = make(chan *api.RestRequest, 1) + b.GrpcReqCh = make(chan *api.GrpcRequest, 1) b.policyUpdateCh = make(chan config.RoutingPolicy) b.listenPort = port return &b @@ -217,8 +216,8 @@ func (server *BgpServer) Serve() { } else { log.Info("Can't delete a peer configuration for ", addr) } - case restReq := <-server.RestReqCh: - server.handleRest(restReq) + case grpcReq := <-server.GrpcReqCh: + server.handleGrpc(grpcReq) case pl := <-server.policyUpdateCh: server.SetPolicy(pl) msg := &serverMsg{ @@ -294,37 +293,44 @@ func (p peers) Less(i, j int) bool { return strings.Less(0, 1) } -func (server *BgpServer) handleRest(restReq *api.RestRequest) { - switch restReq.RequestType { +func (server *BgpServer) handleGrpc(grpcReq *api.GrpcRequest) { + switch grpcReq.RequestType { case api.REQ_NEIGHBORS: - result := &api.RestResponse{} peerList := peers{} for _, info := range server.peerMap { peerList = append(peerList, info.peer) } sort.Sort(peerList) - j, _ := json.Marshal(peerList) - result.Data = j - restReq.ResponseCh <- result - close(restReq.ResponseCh) - + for _, peer := range peerList { + data, err := peer.ToGrpc() + result := &api.GrpcResponse{ + ResponseErr: err, + Data: data, + } + grpcReq.ResponseCh <- result + } + close(grpcReq.ResponseCh) case api.REQ_NEIGHBOR: - - remoteAddr := restReq.RemoteAddr - result := &api.RestResponse{} + remoteAddr := grpcReq.RemoteAddr + var result *api.GrpcResponse info, found := server.peerMap[remoteAddr] if found { - j, _ := json.Marshal(info.peer) - result.Data = j + data, err := info.peer.ToGrpc() + result = &api.GrpcResponse{ + ResponseErr: err, + Data: data, + } } else { - result.ResponseErr = fmt.Errorf("Neighbor that has %v does not exist.", remoteAddr) + result = &api.GrpcResponse{ + ResponseErr: fmt.Errorf("Neighbor that has %v does not exist.", remoteAddr), + } } - restReq.ResponseCh <- result - close(restReq.ResponseCh) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) case api.REQ_GLOBAL_RIB, api.REQ_GLOBAL_ADD, api.REQ_GLOBAL_DELETE: msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } server.globalRib.serverMsgCh <- msg case api.REQ_LOCAL_RIB, api.REQ_NEIGHBOR_SHUTDOWN, api.REQ_NEIGHBOR_RESET, @@ -332,19 +338,19 @@ func (server *BgpServer) handleRest(restReq *api.RestRequest) { api.REQ_ADJ_RIB_IN, api.REQ_ADJ_RIB_OUT, api.REQ_NEIGHBOR_ENABLE, api.REQ_NEIGHBOR_DISABLE: - remoteAddr := restReq.RemoteAddr - result := &api.RestResponse{} + remoteAddr := grpcReq.RemoteAddr + result := &api.GrpcResponse{} info, found := server.peerMap[remoteAddr] if found { msg := &serverMsg{ msgType: SRV_MSG_API, - msgData: restReq, + msgData: grpcReq, } info.peer.serverMsgCh <- msg } else { result.ResponseErr = fmt.Errorf("Neighbor that has %v does not exist.", remoteAddr) - restReq.ResponseCh <- result - close(restReq.ResponseCh) + grpcReq.ResponseCh <- result + close(grpcReq.ResponseCh) } } } diff --git a/table/destination.go b/table/destination.go index 72350d8a..aae02822 100644 --- a/table/destination.go +++ b/table/destination.go @@ -111,7 +111,7 @@ func (dd *DestinationDefault) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Prefix string Paths []Path - BestPathIdx int + BestPathIdx int `json:"best_path_idx"` }{ Prefix: prefix.String(), Paths: dd.knownPathList, @@ -929,7 +929,7 @@ func (ipv6d *IPv6Destination) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Prefix string Paths []Path - BestPathIdx int + BestPathIdx int `json:"best_path_idx"` }{ Prefix: prefix.String(), Paths: ipv6d.knownPathList, @@ -985,7 +985,7 @@ func (ipv4vpnd *IPv4VPNDestination) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Prefix string Paths []Path - BestPathIdx int + BestPathIdx int `json:"best_path_idx"` }{ Prefix: prefix.String(), Paths: ipv4vpnd.knownPathList, @@ -1023,7 +1023,7 @@ func (evpnd *EVPNDestination) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Prefix string Paths []Path - BestPathIdx int + BestPathIdx int `json:"best_path_idx"` }{ Prefix: nlri.String(), Paths: evpnd.knownPathList, diff --git a/table/path.go b/table/path.go index c7329587..fddde7d3 100644 --- a/table/path.go +++ b/table/path.go @@ -175,12 +175,12 @@ func (pd *PathDefault) MarshalJSON() ([]byte, error) { Network string Nexthop string Attrs []bgp.PathAttributeInterface - Age float64 + Age int64 }{ Network: pd.getPrefix(), Nexthop: pd.GetNexthop().String(), Attrs: pd.getPathAttrs(), - Age: time.Now().Sub(pd.timestamp).Seconds(), + Age: int64(time.Now().Sub(pd.timestamp).Seconds()), }) } @@ -399,12 +399,12 @@ func (ipv6p *IPv6Path) MarshalJSON() ([]byte, error) { Network string Nexthop string Attrs []bgp.PathAttributeInterface - Age float64 + Age int64 }{ Network: ipv6p.getPrefix(), Nexthop: ipv6p.PathDefault.GetNexthop().String(), Attrs: ipv6p.PathDefault.getPathAttrs(), - Age: time.Now().Sub(ipv6p.PathDefault.timestamp).Seconds(), + Age: int64(time.Now().Sub(ipv6p.PathDefault.timestamp).Seconds()), }) } @@ -451,12 +451,12 @@ func (ipv4vpnp *IPv4VPNPath) MarshalJSON() ([]byte, error) { Network string Nexthop string Attrs []bgp.PathAttributeInterface - Age float64 + Age int64 }{ Network: ipv4vpnp.getPrefix(), Nexthop: ipv4vpnp.PathDefault.GetNexthop().String(), Attrs: ipv4vpnp.PathDefault.getPathAttrs(), - Age: time.Now().Sub(ipv4vpnp.PathDefault.timestamp).Seconds(), + Age: int64(time.Now().Sub(ipv4vpnp.PathDefault.timestamp).Seconds()), }) } @@ -503,11 +503,11 @@ func (evpnp *EVPNPath) MarshalJSON() ([]byte, error) { Network string Nexthop string Attrs []bgp.PathAttributeInterface - Age float64 + Age int64 }{ Network: evpnp.getPrefix(), Nexthop: evpnp.PathDefault.GetNexthop().String(), Attrs: evpnp.PathDefault.getPathAttrs(), - Age: time.Now().Sub(evpnp.PathDefault.timestamp).Seconds(), + Age: int64(time.Now().Sub(evpnp.PathDefault.timestamp).Seconds()), }) } diff --git a/test/scenario_test/bgp_router_test.py b/test/scenario_test/bgp_router_test.py index f2d3ce16..62ff9b5f 100644 --- a/test/scenario_test/bgp_router_test.py +++ b/test/scenario_test/bgp_router_test.py @@ -73,12 +73,12 @@ class GoBGPTest(GoBGPTestBase): for p in d.paths: print "check of %s's route %s existance in gobgp global rib" % (peer_ip, p.network) exist = False - for dst in rib['Destinations']: - for path in dst['Paths']: - if path['Network'] == p.network: + for dst in rib: + for path in dst['paths']: + if path['network'] == p.network: exist = True if exist: - self.assertEqual(path['Nexthop'] == p.nexthop, True) + self.assertEqual(path['nexthop'] == p.nexthop, True) self.assertEqual(exist, True) # Test of advertising route to each quagga form gobgp @@ -150,12 +150,12 @@ class GoBGPTest(GoBGPTestBase): for p in d.paths: print "check of %s's route %s existance in gobgp global rib" % (peer_ip, p.network) exist = False - for dst in rib['Destinations']: - for path in dst['Paths']: - if path['Network'] == p.network: + for dst in rib: + for path in dst['paths']: + if path['network'] == p.network: exist = True if exist: - self.assertEqual(path['Nexthop'] == p.nexthop, True) + self.assertEqual(path['nexthop'] == p.nexthop, True) self.assertEqual(exist, True) # Test of advertising route to each quagga form gobgp when append quagga container @@ -215,9 +215,9 @@ class GoBGPTest(GoBGPTestBase): removed_prefix = "10.0.0.%d/24" % self.remove_quagga still_exists = False - for dst in rib['Destinations']: - for path in dst['Paths']: - if path['Network'] == removed_prefix: + for dst in rib: + for path in dst['paths']: + if path['network'] == removed_prefix: still_exists = True self.assertEqual(still_exists, False) diff --git a/test/scenario_test/constant.py b/test/scenario_test/constant.py index 5e672a79..131cfd04 100644 --- a/test/scenario_test/constant.py +++ b/test/scenario_test/constant.py @@ -1,3 +1,19 @@ +# Copyright (C) 2015 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. + + IPv4 = 'ipv4' IPv6 = 'ipv6' GOBGP_IP = "10.0.255.1" @@ -12,12 +28,15 @@ GOBGP_CONFIG_FILE = "gobgpd.conf" CONFIG_DIR = "/tmp/gobgp" CONFIG_DIRR = "/tmp/gobgp/" SHARE_VOLUME = "/root/share_volume" +CLI_CMD = "gobgpcli" EXABGP_CONTAINER_NAME = "exabgp" EXABGP_ADDRESS = "10.0.0.100/16" EXABGP_CONFDIR = SHARE_VOLUME + "/exabgp_test_conf" EXABGP_LOG_FILE = "exabgpd.log" STARTUP_FILE_NAME = "gobgp_startup.sh" STARTUP_FILE = SHARE_VOLUME + "/" + STARTUP_FILE_NAME +INSTALL_FILE_NAME = "gobgp_install.sh" +INSTALL_FILE = SHARE_VOLUME + "/" + INSTALL_FILE_NAME IP_VERSION = IPv4 IF_CONFIG_OPTION = {IPv4: "inet", IPv6: "inet6"} @@ -40,8 +59,8 @@ BASE_MASK = {IPv4: "/16", IPv6: "/64"} A_PART_OF_CURRENT_DIR = "/test/scenario_test" -ADJ_RIB_IN = "adj-rib-in" -ADJ_RIB_OUT = "adj-rib-out" -LOCAL_RIB = "local-rib" -GLOBAL_RIB = "global/rib" +ADJ_RIB_IN = "adj-in" +ADJ_RIB_OUT = "adj-out" +LOCAL_RIB = "local" +GLOBAL_RIB = "global" NEIGHBOR = "neighbor" diff --git a/test/scenario_test/docker_control.py b/test/scenario_test/docker_control.py index 64446111..008b346d 100644 --- a/test/scenario_test/docker_control.py +++ b/test/scenario_test/docker_control.py @@ -154,35 +154,38 @@ def create_config_dir(): cmd = "mkdir " + CONFIG_DIR local(cmd, capture=True) - def make_startup_file(log_opt=""): - file_buff = '#!/bin/bash' + '\n' - file_buff += 'cd /go/src/github.com/osrg/gobgp' + '\n' - file_buff += 'git pull origin master' + '\n' - file_buff += 'cd gobgpd' + '\n' - file_buff += 'go get -v' + '\n' - file_buff += 'go build' + '\n' - file_buff += './gobgpd -f ' + SHARE_VOLUME + '/gobgpd.conf ' + log_opt + ' > ' + SHARE_VOLUME + '/gobgpd.log' + file_buff += "cd /go/src/github.com/osrg/gobgp/gobgpd" + '\n' + file_buff += "./gobgpd -f " + SHARE_VOLUME + "/gobgpd.conf " + log_opt + " > " + SHARE_VOLUME + "/gobgpd.log" + cmd = "echo \"" + file_buff + "\" > " + CONFIG_DIR + "/" + STARTUP_FILE_NAME local(cmd, capture=True) cmd = "chmod 755 " + CONFIG_DIRR + STARTUP_FILE_NAME local(cmd, capture=True) -def make_startup_file_use_local_gobgp(log_opt=""): - +def make_install_file(use_local=False): file_buff = '#!/bin/bash' + '\n' - file_buff += 'rm -rf /go/src/github.com/osrg/gobgp' + '\n' - file_buff += 'cp -r ' + SHARE_VOLUME + '/gobgp /go/src/github.com/osrg/' + '\n' - file_buff += 'cd /go/src/github.com/osrg/gobgp' + '\n' - file_buff += 'cd gobgpd' + '\n' + + if use_local: + file_buff += 'rm -rf /go/src/github.com/osrg/gobgp' + '\n' + file_buff += 'cp -r ' + SHARE_VOLUME + '/gobgp /go/src/github.com/osrg/' + '\n' + file_buff += 'cd /go/src/github.com/osrg/gobgp' + '\n' + else: + file_buff += 'cd /go/src/github.com/osrg/gobgp' + '\n' + file_buff += 'git pull origin master' + '\n' + + file_buff += 'cd gobgp' + '\n' file_buff += 'go get -v' + '\n' file_buff += 'go build' + '\n' - file_buff += './gobgpd -f ' + SHARE_VOLUME + '/gobgpd.conf ' + log_opt + ' > ' + SHARE_VOLUME + '/gobgpd.log' - cmd = "echo \"" + file_buff + "\" > " + CONFIG_DIR + "/" + STARTUP_FILE_NAME + file_buff += 'cp gobgp ' + SHARE_VOLUME + '/' + CLI_CMD + '\n' + file_buff += 'cd ../gobgpd' + '\n' + file_buff += 'go get -v' + '\n' + file_buff += 'go build' + cmd = "echo \"" + file_buff + "\" > " + CONFIG_DIR + "/" + INSTALL_FILE_NAME local(cmd, capture=True) - cmd = "chmod 755 " + CONFIG_DIRR + STARTUP_FILE_NAME + cmd = "chmod 755 " + CONFIG_DIRR + INSTALL_FILE_NAME local(cmd, capture=True) @@ -255,7 +258,9 @@ def bridge_unsetting_for_docker_connection(): def start_gobgp(): - cmd = "docker exec gobgp " + STARTUP_FILE + " > /dev/null 2>&1 &" + cmd = "docker exec -it gobgp " + INSTALL_FILE + local(cmd, capture=True) + cmd = "docker exec -it gobgp " + STARTUP_FILE + "&" local(cmd, capture=True) @@ -334,7 +339,7 @@ def change_exabgp_version(): def reload_config(): - cmd = "docker exec gobgp /usr/bin/pkill gobgp -SIGHUP" + cmd = "docker exec gobgp /usr/bin/pkill gobgpd -SIGHUP" local(cmd, capture=True) print "complete append docker container." @@ -359,6 +364,7 @@ def init_test_env_executor(quagga_num, use_local, go_path, log_debug=False, is_r opt = "-l debug" if log_debug else "" # execute local gobgp program in the docker container if the input option is local + make_startup_file(log_opt=opt) if use_local: print "execute gobgp program in local machine." pwd = local("pwd", capture=True) @@ -366,14 +372,14 @@ def init_test_env_executor(quagga_num, use_local, go_path, log_debug=False, is_r gobgp_path = re.sub(A_PART_OF_CURRENT_DIR, "", pwd) cmd = "cp -r " + gobgp_path + " " + CONFIG_DIRR local(cmd, capture=True) - make_startup_file_use_local_gobgp(log_opt=opt) + make_install_file(use_local=True) else: print "scenario_test directory is not." print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() else: print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() change_owner_to_root(CONFIG_DIR) start_gobgp() @@ -405,6 +411,7 @@ def init_policy_test_env_executor(quagga_num, use_local, go_path, log_debug=Fals opt = "-l debug" if log_debug else "" # execute local gobgp program in the docker container if the input option is local + make_startup_file(log_opt=opt) if use_local: print "execute gobgp program in local machine." pwd = local("pwd", capture=True) @@ -412,14 +419,14 @@ def init_policy_test_env_executor(quagga_num, use_local, go_path, log_debug=Fals gobgp_path = re.sub(A_PART_OF_CURRENT_DIR, "", pwd) cmd = "cp -r " + gobgp_path + " " + CONFIG_DIRR local(cmd, capture=True) - make_startup_file_use_local_gobgp(log_opt=opt) + make_install_file(use_local=True) else: print "scenario_test directory is not." print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() else: print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() change_owner_to_root(CONFIG_DIR) start_gobgp() @@ -451,6 +458,7 @@ def init_ipv6_test_env_executor(quagga_num, use_local, go_path, log_debug=False) opt = "-l debug" if log_debug else "" # execute local gobgp program in the docker container if the input option is local + make_startup_file(log_opt=opt) if use_local: print "execute gobgp program in local machine." pwd = local("pwd", capture=True) @@ -458,14 +466,14 @@ def init_ipv6_test_env_executor(quagga_num, use_local, go_path, log_debug=False) gobgp_path = re.sub(A_PART_OF_CURRENT_DIR, "", pwd) cmd = "cp -r " + gobgp_path + " " + CONFIG_DIRR local(cmd, capture=True) - make_startup_file_use_local_gobgp(log_opt=opt) + make_install_file(use_local=True) else: print "scenario_test directory is not." print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() else: print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() change_owner_to_root(CONFIG_DIR) start_gobgp() @@ -508,14 +516,14 @@ def init_malformed_test_env_executor(conf_file, use_local, go_path, exabgp_path gobgp_path = re.sub(A_PART_OF_CURRENT_DIR, "", pwd) cmd = "cp -r " + gobgp_path + " " + CONFIG_DIRR local(cmd, capture=True) - make_startup_file_use_local_gobgp(log_opt=opt) + make_install_file(use_local=True) else: print "scenario_test directory is not." print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() else: print "execute gobgp program of osrg/master in github." - make_startup_file(log_opt=opt) + make_install_file() change_owner_to_root(CONFIG_DIR) diff --git a/test/scenario_test/gobgp_test.py b/test/scenario_test/gobgp_test.py index 0cf42199..aafc41ed 100644 --- a/test/scenario_test/gobgp_test.py +++ b/test/scenario_test/gobgp_test.py @@ -14,7 +14,7 @@ # limitations under the License. import unittest -import requests +from fabric.api import local import json import toml import os @@ -24,6 +24,7 @@ from peer_info import Peer from peer_info import Destination from peer_info import Path from constant import * +import quagga_access as qaccess class GoBGPTestBase(unittest.TestCase): @@ -42,6 +43,19 @@ class GoBGPTestBase(unittest.TestCase): def setUp(self): self.quagga_configs = [] + def get_neighbor_state(self, neighbor_address): + print "check neighbor state for %s" % (neighbor_address) + state = None + try: + neighbor = self.ask_gobgp(NEIGHBOR, neighbor_address) + state = neighbor['info']['bgp_state'] + remote_ip = neighbor['conf']['remote_ip'] + assert remote_ip == neighbor_address + return state + except Exception as e: + print e + return state + def retry_routine_for_state(self, addresses, allow_state): in_prepare_quagga = True retry_count = 0 @@ -56,15 +70,8 @@ class GoBGPTestBase(unittest.TestCase): success_count = 0 for address in addresses: # get neighbor state and remote ip from gobgp connections - try: - neighbor = self.ask_gobgp(NEIGHBOR, address) - except Exception: - continue - if neighbor is None: - continue - state = neighbor['info']['bgp_state'] - remote_ip = neighbor['conf']['remote_ip'] - if address == remote_ip and state == allow_state: + state = self.get_neighbor_state(address) + if state == allow_state: success_count += 1 if success_count == len(addresses): in_prepare_quagga = False @@ -77,12 +84,11 @@ class GoBGPTestBase(unittest.TestCase): rib = self.ask_gobgp(LOCAL_RIB, check_address) target_exist = False - g_dests = rib['Destinations'] - for g_dest in g_dests: - best_path_idx = g_dest['BestPathIdx'] - if target_network == g_dest['Prefix']: + for g_dest in rib: + best_path_idx = g_dest['best_path_idx'] if 'best_path_idx' in g_dest else 0 + if target_network == g_dest['prefix']: target_exist = True - g_paths = g_dest['Paths'] + g_paths = g_dest['paths'] idx = 0 if len(g_paths) < 2: print "target path has not been bestpath selected yet." @@ -91,10 +97,11 @@ class GoBGPTestBase(unittest.TestCase): self.retry_routine_for_bestpath(check_address, target_network, ans_nexthop) return for g_path in g_paths: - print "best_path_Idx: " + str(best_path_idx) + "idx: " + str(idx) - print "pre: ", g_dest['Prefix'], "net: ", g_path['Network'], "next: ", g_path['Nexthop'] + print "best_path_Idx: " + str(best_path_idx) + ", idx: " + str(idx) + print g_dest + print "pre: ", g_dest['prefix'], "net: ", g_path['network'], "next: ", g_path['nexthop'] if str(best_path_idx) == str(idx): - rep_nexthop = g_path['Nexthop'] + rep_nexthop = g_path['nexthop'] idx += 1 if target_exist is False: print "target path has not been receive yet." @@ -179,13 +186,98 @@ class GoBGPTestBase(unittest.TestCase): return True def ask_gobgp(self, what, who="", af="ipv4"): - url = "http://" + self.gobgp_ip + ":" + self.gobgp_port + "/v1/bgp/" + cmd = "%s/%s -j -u %s -p %s show " % (CONFIG_DIR, CLI_CMD, self.gobgp_ip, self.gobgp_port) if what == GLOBAL_RIB: - url += "/".join([what, af]) + cmd += " ".join([what, af]) elif what == NEIGHBOR: - url += "/".join([NEIGHBOR, who]) + cmd += " ".join([NEIGHBOR, who]) else: - url += "/".join([NEIGHBOR, who, what, af]) - r = requests.get(url) - result = json.loads(r.text) + cmd += " ".join([NEIGHBOR, who, what, af]) + j = local(cmd, capture=True) + result = json.loads(j) return result + + def soft_reset(self, neighbor_address, route_family, type="in"): + cmd = "%s/%s -j -u %s -p %s softreset%s " % (CONFIG_DIR, CLI_CMD, self.gobgp_ip, self.gobgp_port, type) + cmd += "neighbor %s %s" % (neighbor_address, route_family) + local(cmd) + + def get_paths_in_localrib(self, neighbor_address, target_prefix, retry=3, interval=5): + retry_count = 0 + while True: + local_rib = self.ask_gobgp(LOCAL_RIB, neighbor_address) + g_dest = [dest for dest in local_rib if dest['prefix'] == target_prefix] + if len(g_dest) > 0: + assert len(g_dest) == 1 + d = g_dest[0] + return d['paths'] + else: + retry_count += 1 + if retry_count > retry: + break + else: + print "destination is none : %s" % neighbor_address + print "please wait more (" + str(interval) + " second)" + time.sleep(interval) + + print "destination is none" + return None + + def get_adj_rib_in(self, neighbor_address, target_prefix, retry=3, interval=-1): + if interval < 0: + interval = self.wait_per_retry + return self.get_adj_rib(neighbor_address, target_prefix, retry, interval, type=ADJ_RIB_IN) + + + def get_adj_rib_out(self, neighbor_address, target_prefix, retry=3, interval=-1): + if interval < 0: + interval = self.wait_per_retry + return self.get_adj_rib(neighbor_address, target_prefix, retry, interval, type=ADJ_RIB_OUT) + + + def get_adj_rib(self, neighbor_address, target_prefix, retry, interval, type=ADJ_RIB_IN): + retry_count = 0 + while True: + rib = self.ask_gobgp(type, neighbor_address) + paths = [p for p in rib if p['network'] == target_prefix] + + if len(paths) > 0: + assert len(paths) == 1 + return paths[0] + else: + retry_count += 1 + if retry_count > retry: + break + else: + print "adj_rib_%s is none" % type + print "wait (" + str(interval) + " seconds)" + time.sleep(interval) + + print "adj_rib_%s is none" % type + return None + + + # get route information on quagga + def get_routing_table(self, neighbor_address, target_prefix, retry=3, interval=-1): + if interval < 0: + interval = self.wait_per_retry + print "check route %s on quagga : %s" % (target_prefix, neighbor_address) + retry_count = 0 + while True: + tn = qaccess.login(neighbor_address) + q_rib = qaccess.show_rib(tn) + qaccess.logout(tn) + for q_path in q_rib: + if target_prefix == q_path['Network']: + return q_path + + retry_count += 1 + if retry_count > retry: + break + else: + print "target_prefix %s is none" % target_prefix + print "wait (" + str(interval) + " seconds)" + time.sleep(interval) + + print "route : %s is none" % target_prefix + return None diff --git a/test/scenario_test/peer_info.py b/test/scenario_test/peer_info.py index 00490d09..5a965f7b 100644 --- a/test/scenario_test/peer_info.py +++ b/test/scenario_test/peer_info.py @@ -1,6 +1,21 @@ +# Copyright (C) 2015 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. + + class Peer: def __init__(self, peer_ip, peer_id, peer_as, ip_version): - # def __init__(self, peer_ip, peer_id, peer_as): self.peer_ip = peer_ip self.peer_id = peer_id self.peer_as = peer_as @@ -21,4 +36,4 @@ class Path: self.nexthop = nexthop self.origin = None self.as_path = [] - self.metric = None
\ No newline at end of file + self.metric = None diff --git a/test/scenario_test/route_server_ipv4_v6_test.py b/test/scenario_test/route_server_ipv4_v6_test.py index a171536b..4233988f 100644 --- a/test/scenario_test/route_server_ipv4_v6_test.py +++ b/test/scenario_test/route_server_ipv4_v6_test.py @@ -76,21 +76,16 @@ class GoBGPIPv6Test(GoBGPTestBase): if quagga_config.peer_ip == address or quagga_config.ip_version != af: for c_dest in quagga_config.destinations.itervalues(): # print "config : ", c_dest.prefix, "my ip or different ip version!!!" - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 0) else: for c_dest in quagga_config.destinations.itervalues(): - # print "config : ", c_dest.prefix" - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 1) diff --git a/test/scenario_test/route_server_malformed_test.py b/test/scenario_test/route_server_malformed_test.py index 4c45a9f7..ab8993ae 100644 --- a/test/scenario_test/route_server_malformed_test.py +++ b/test/scenario_test/route_server_malformed_test.py @@ -20,12 +20,13 @@ import sys import nose import collections import docker_control as fab +from fabric.api import local import requests import json import toml from noseplugin import OptionParser from noseplugin import parser_option - +from constant import CONFIG_DIR, CLI_CMD initial_wait_time = 10 wait_per_retry = 5 @@ -98,13 +99,13 @@ def check_func(exabgp_conf, result): retry_count = 0 # get neighbor addresses from gobgpd.conf addresses = get_neighbor_address() - url = "http://" + gobgp_ip + ":" + gobgp_port + "/v1/bgp/neighbors" neighbors = None q_address = "" e_address = "" q_transitions = 0 q_state = "" notification = "" + while in_prepare_quagga or in_prepare_exabgp: if retry_count != 0: print "please wait more (" + str(wait_per_retry) + " second)" @@ -115,8 +116,9 @@ def check_func(exabgp_conf, result): retry_count += 1 # check whether the service of gobgp is normally try: - r = requests.get(url) - neighbors = json.loads(r.text) + cmd = "%s/%s -j -u %s -p %s show neighbors" % (CONFIG_DIR, CLI_CMD, gobgp_ip, gobgp_port) + j = local(cmd, capture=True) + neighbors = json.loads(j) except Exception: continue if neighbors is None: @@ -173,4 +175,4 @@ if __name__ == '__main__': if fab.docker_pkg_check() is False: print "not install docker package." sys.exit(1) - nose.main(argv=sys.argv, addplugins=[OptionParser()], defaultTest=sys.argv[0])
\ No newline at end of file + nose.main(argv=sys.argv, addplugins=[OptionParser()], defaultTest=sys.argv[0]) diff --git a/test/scenario_test/route_server_policy_test.py b/test/scenario_test/route_server_policy_test.py index 42d74d9e..9ae29a2b 100644 --- a/test/scenario_test/route_server_policy_test.py +++ b/test/scenario_test/route_server_policy_test.py @@ -14,42 +14,25 @@ # limitations under the License. import unittest -import requests -import json -import toml import os import time import sys import nose import quagga_access as qaccess -from peer_info import Peer -from peer_info import Destination -from peer_info import Path -from ciscoconfparse import CiscoConfParse import docker_control as fab from noseplugin import OptionParser from noseplugin import parser_option -import scenario_test_util as util +from gobgp_test import GoBGPTestBase +from constant import * -class GoBGPTest(unittest.TestCase): +class GoBGPTest(GoBGPTestBase): - gobgp_ip = "10.0.255.1" - gobgp_port = "8080" - rest_url_neighbor = "http://" + gobgp_ip + ":" + gobgp_port + "/v1/bgp/neighbor/" - base_dir = "/tmp/gobgp/" - gobgp_config_file = "/tmp/gobgp/gobgpd.conf" - gobgp_config = None quagga_num = 3 - initial_wait_time = 10 - wait_per_retry = 5 def __init__(self, *args, **kwargs): super(GoBGPTest, self).__init__(*args, **kwargs) - def setUp(self): - self.quagga_configs = [] - def initialize(self, policy_pattern=None): use_local = parser_option.use_local go_path = parser_option.go_path @@ -60,12 +43,6 @@ class GoBGPTest(unittest.TestCase): self.assertTrue(self.check_load_config()) - def check_established(self, addresses): - for address in addresses: - result = self.retry_until(address, target_state="BGP_FSM_ESTABLISHED",retry=10) - self.assertEqual(result, True) - - """ import-policy test --------------------------------------- @@ -80,33 +57,32 @@ class GoBGPTest(unittest.TestCase): # policy_pattern:p1 attaches a policy to reject route 192.168.0.0/16 (16...24) # coming from peer2(10.0.0.2) to peer3(10.0.0.3)'s import-policy. self.initialize(policy_pattern="p1") - self.check_established(util.get_neighbor_address(self.gobgp_config)) + addresses = self.get_neighbor_address(self.gobgp_config) + self.retry_routine_for_state(addresses, "BGP_FSM_ESTABLISHED") peer1 = "10.0.0.1" peer2 = "10.0.0.2" peer3 = "10.0.0.3" - base_url = self.rest_url_neighbor - w = self.wait_per_retry - path = util.get_paths_in_localrib(base_url, peer1, "192.168.2.0", retry=3, interval=w) + path = self.get_paths_in_localrib(peer1, "192.168.2.0", retry=3) self.assertIsNotNone(path) # check show ip bgp on peer1(quagga1) - qpath = util.get_routing_table(peer1,"192.168.2.0", retry=3, interval=w) + qpath = self.get_routing_table(peer1,"192.168.2.0", retry=3) print qpath self.assertIsNotNone(qpath) # check adj-rib-out in peer2 - path = util.get_adj_rib_in(base_url, peer2, "192.168.2.0/24", retry=3, interval=w) + path = self.get_adj_rib_in(peer2, "192.168.2.0/24", retry=3) # print path self.assertIsNotNone(path) - path = util.get_paths_in_localrib(base_url, peer3, "192.168.2.0",retry=0, interval=w) + path = self.get_paths_in_localrib(peer3, "192.168.2.0",retry=0) # print path self.assertIsNone(path) # check show ip bgp on peer1(quagga3) - qpath = util.get_routing_table(peer3,"192.168.2.0", retry=3, interval=w) + qpath = self.get_routing_table(peer3,"192.168.2.0", retry=3) # print qpath self.assertIsNone(qpath) @@ -125,38 +101,37 @@ class GoBGPTest(unittest.TestCase): # policy_pattern:p1 attaches a policy to reject route 192.168.0.0/16 (16...24) # coming from peer2(10.0.0.2) to peer3(10.0.0.3)'s export-policy. self.initialize(policy_pattern="p2") - self.check_established(util.get_neighbor_address(self.gobgp_config)) + addresses = self.get_neighbor_address(self.gobgp_config) + self.retry_routine_for_state(addresses, "BGP_FSM_ESTABLISHED") peer1 = "10.0.0.1" peer2 = "10.0.0.2" peer3 = "10.0.0.3" - base_url = self.rest_url_neighbor - w = self.wait_per_retry - paths = util.get_paths_in_localrib(base_url, peer1, "192.168.2.0", retry=3, interval=w) + paths = self.get_paths_in_localrib(peer1, "192.168.2.0", retry=3) # print paths self.assertIsNotNone(paths) # check show ip bgp on peer1(quagga1) - qpath = util.get_routing_table(peer1, "192.168.2.0", retry=3, interval=w) + qpath = self.get_routing_table(peer1, "192.168.2.0", retry=3) # print qpath self.assertIsNotNone(qpath) # check adj-rib-out in peer2 - path = util.get_adj_rib_in(base_url, peer2, "192.168.2.0/24", retry=1, interval=w) + path = self.get_adj_rib_in(peer2, "192.168.2.0/24", retry=1) # print path self.assertIsNotNone(path) - path = util.get_paths_in_localrib(base_url, peer3, "192.168.2.0") + path = self.get_paths_in_localrib(peer3, "192.168.2.0") # print path self.assertIsNotNone(path) - path = util.get_adj_rib_out(base_url, peer3, "192.168.2.0", retry=1, interval=w) + path = self.get_adj_rib_out(peer3, "192.168.2.0", retry=1) # print path self.assertIsNone(path) # check show ip bgp on peer1(quagga3) - qpath = util.get_routing_table(peer3,"192.168.2.0", retry=3, interval=w) + qpath = self.get_routing_table(peer3,"192.168.2.0", retry=3) # print qpath self.assertIsNone(qpath) @@ -195,8 +170,6 @@ class GoBGPTest(unittest.TestCase): peer1 = "10.0.0.1" peer2 = "10.0.0.2" peer3 = "10.0.0.3" - base_url = self.rest_url_neighbor - w = self.wait_per_retry # add other network tn = qaccess.login(peer2) @@ -206,20 +179,21 @@ class GoBGPTest(unittest.TestCase): qaccess.add_network(tn, 65002, "192.168.200.0/24") qaccess.logout(tn) - self.check_established(util.get_neighbor_address(self.gobgp_config)) + addresses = self.get_neighbor_address(self.gobgp_config) + self.retry_routine_for_state(addresses, "BGP_FSM_ESTABLISHED") time.sleep(self.initial_wait_time) def path_exists_in_localrib(peer, prefix,r=10): - paths = util.get_paths_in_localrib(base_url, peer, prefix, retry=r, interval=w) + paths = self.get_paths_in_localrib(peer, prefix, retry=r) return paths is not None def path_exists_in_routing_table(peer, prefix,r=10): - qpath = util.get_routing_table(peer, prefix, retry=r, interval=w) + qpath = self.get_routing_table(peer, prefix, retry=r) return qpath is not None def path_exists_in_adj_rib_in(peer, prefix,r=10): - path = util.get_adj_rib_in(base_url, peer, prefix, retry=r, interval=w) + path = self.get_adj_rib_in(peer, prefix, retry=r) return path is not None @@ -253,7 +227,7 @@ class GoBGPTest(unittest.TestCase): # soft reset print "soft_reset" - self.soft_reset(peer2, "ipv4") + self.soft_reset(peer2, IPv4) # check local-rib self.assertTrue(path_exists_in_localrib(peer3,"192.168.2.0")) @@ -301,8 +275,6 @@ class GoBGPTest(unittest.TestCase): peer1 = "10.0.0.1" peer2 = "10.0.0.2" peer3 = "10.0.0.3" - base_url = self.rest_url_neighbor - w = self.wait_per_retry # add other network tn = qaccess.login(peer2) @@ -312,25 +284,25 @@ class GoBGPTest(unittest.TestCase): qaccess.add_network(tn, 65002, "192.168.200.0/24") qaccess.logout(tn) - self.check_established(self.get_neighbor_address(self.gobgp_config)) + addresses = self.get_neighbor_address(self.gobgp_config) + self.retry_routine_for_state(addresses, "BGP_FSM_ESTABLISHED") time.sleep(self.initial_wait_time) - def path_exists_in_localrib(peer, prefix,r=10): - paths = util.get_paths_in_localrib(base_url, peer, prefix, retry=r, interval=w) + paths = self.get_paths_in_localrib(peer, prefix, retry=r) return paths is not None def path_exists_in_routing_table(peer, prefix,r=10): - qpath = util.get_routing_table(peer, prefix, retry=r, interval=w) + qpath = self.get_routing_table(peer, prefix, retry=r) return qpath is not None def path_exists_in_adj_rib_in(peer, prefix,r=10): - path = util.get_adj_rib_in(base_url, peer, prefix, retry=r, interval=w) + path = self.get_adj_rib_in(peer, prefix, retry=r) return path is not None def path_exists_in_adj_rib_out(peer, prefix,r=10): - path = util.get_adj_rib_out(base_url, peer, prefix, retry=r, interval=w) + path = self.get_adj_rib_out(peer, prefix, retry=r) return path is not None @@ -371,7 +343,7 @@ class GoBGPTest(unittest.TestCase): # soft reset print "soft_reset" - self.soft_reset(peer2, "ipv4") + self.soft_reset(peer2, IPv4) # check local-rib self.assertTrue(path_exists_in_localrib(peer3,"192.168.2.0")) @@ -389,49 +361,6 @@ class GoBGPTest(unittest.TestCase): self.assertTrue(path_exists_in_routing_table(peer3, "192.168.200.0")) - def retry_until(self, neighbor_address, target_state="BGP_FSM_ESTABLISHED", retry=3): - retry_count = 0 - - while True: - - current_state = util.get_neighbor_state(self.rest_url_neighbor, neighbor_address) - if current_state == target_state: - print "state changed to %s : %s" % (current_state, neighbor_address) - return True - else: - retry_count += 1 - if retry_count > retry: - break - else: - print "current state is %s" % current_state - print "please wait more (" + str(self.wait_per_retry) + " second)" - time.sleep(self.wait_per_retry) - - print "exceeded retry count : %s" % neighbor_address - return False - - - def soft_reset(self, neighbor_address, route_family, type="in"): - url = self.rest_url_neighbor + neighbor_address + "/softreset"+type+"/" + route_family - r = requests.post(url) - if r.status_code == requests.codes.ok: - print "Succeed" - else: - print "Failed" - - - def check_load_config(self): - self.gobgp_config = util.load_gobgp_config(self.gobgp_config_file) - self.quagga_configs = util.load_quagga_config(self.base_dir) - if self.gobgp_config is None: - print "Failed to read the gobgp configuration file" - return False - if len(self.quagga_configs) == 0: - print "Failed to read the quagga configuration file" - return False - return True - - if __name__ == '__main__': if fab.test_user_check() is False: print "you are not root." diff --git a/test/scenario_test/route_server_test.py b/test/scenario_test/route_server_test.py index 7621d628..36ea0b20 100644 --- a/test/scenario_test/route_server_test.py +++ b/test/scenario_test/route_server_test.py @@ -73,18 +73,16 @@ class GoBGPTest(GoBGPTestBase): for quagga_config in self.quagga_configs: if quagga_config.peer_ip == address: for c_dest in quagga_config.destinations.itervalues(): - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 0) else: for c_dest in quagga_config.destinations.itervalues(): - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 1) @@ -152,22 +150,17 @@ class GoBGPTest(GoBGPTestBase): for quagga_config in self.quagga_configs: if quagga_config.peer_ip == address: for c_dest in quagga_config.destinations.itervalues(): - # print "config : ", c_dest.prefix, "my ip !!!" - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 0) else: for c_dest in quagga_config.destinations.itervalues(): # print "config : ", c_dest.prefix," - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 1) @@ -241,21 +234,18 @@ class GoBGPTest(GoBGPTestBase): if quagga_config.peer_ip == address: for c_dest in quagga_config.destinations.itervalues(): # print "config : ", c_dest.prefix, "my ip !!!" - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: - # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + for g_dest in local_rib: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 0) else: for c_dest in quagga_config.destinations.itervalues(): # print "config : ", c_dest.prefix - g_dests = local_rib['Destinations'] exist_n = 0 - for g_dest in g_dests: + for g_dest in local_rib: # print "gobgp : ", g_dest['Prefix'] - if c_dest.prefix == g_dest['Prefix']: + if c_dest.prefix == g_dest['prefix']: exist_n += 1 self.assertEqual(exist_n, 1) diff --git a/test/scenario_test/scenario_test_util.py b/test/scenario_test/scenario_test_util.py deleted file mode 100644 index a889c2db..00000000 --- a/test/scenario_test/scenario_test_util.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright (C) 2014,2015 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. - -import os -import time -import json -import requests -import quagga_access as qaccess -import toml -from peer_info import Peer -from peer_info import Destination -from peer_info import Path -from ciscoconfparse import CiscoConfParse - - -# get address of each neighbor from gobpg configration -def get_neighbor_address(config): - address = [] - neighbors_config = config['NeighborList'] - for neighbor_config in neighbors_config: - neighbor_ip = neighbor_config['NeighborAddress'] - address.append(neighbor_ip) - return address - - -# get route information on quagga -def get_routing_table(neighbor_address, target_prefix, retry=3, interval=5): - print "check route %s on quagga : %s" % (target_prefix, neighbor_address) - retry_count = 0 - while True: - - tn = qaccess.login(neighbor_address) - q_rib = qaccess.show_rib(tn) - qaccess.logout(tn) - for q_path in q_rib: - if target_prefix == q_path['Network']: - return q_path - - retry_count += 1 - if retry_count > retry: - break - else: - print "target_prefix %s is none" % target_prefix - print "wait (" + str(interval) + " seconds)" - time.sleep(interval) - - print "route : %s is none" % target_prefix - return None - - -def get_adj_rib_in(url, neighbor_address, target_prefix, retry=3, interval=5): - return get_adj_rib(url, neighbor_address, target_prefix, retry, interval, type="in") - - -def get_adj_rib_out(url, neighbor_address, target_prefix, retry=3, interval=5): - return get_adj_rib(url, neighbor_address, target_prefix, retry, interval, type="out") - - -def get_adj_rib(base_url, neighbor_address, target_prefix, retry, interval, type="in"): - url = base_url + neighbor_address + "/adj-rib-" +type +"/ipv4" - - retry_count = 0 - while True: - - r = requests.get(url) - in_rib = json.loads(r.text) - print in_rib - paths = [p for p in in_rib if p['Network'] == target_prefix] - - if len(paths) > 0: - assert len(paths) == 1 - return paths[0] - else: - retry_count += 1 - if retry_count > retry: - break - else: - print "adj_rib_%s is none" % type - print "wait (" + str(interval) + " seconds)" - time.sleep(interval) - - print "adj_rib_%s is none" % type - return None - - -def get_neighbor_state(base_url, neighbor_address): - print "check neighbor state for %s" % (neighbor_address) - state = None - url = base_url + neighbor_address - try: - r = requests.get(url) - neighbor = json.loads(r.text) - state = neighbor['info']['bgp_state'] - remote_ip = neighbor['conf']['remote_ip'] - assert remote_ip == neighbor_address - return state - except Exception as e: - print e - return state - - -def get_paths_in_localrib(base_url, neighbor_address, target_prefix, retry=3, interval=5): - url = base_url + neighbor_address + "/local-rib" + "/ipv4" - - retry_count = 0 - while True: - - r = requests.get(url) - local_rib = json.loads(r.text) - g_dests = local_rib['Destinations'] - g_dest = [dest for dest in g_dests if dest['Prefix'] == target_prefix] - if len(g_dest) > 0: - assert len(g_dest) == 1 - d = g_dest[0] - return d['Paths'] - else: - retry_count += 1 - if retry_count > retry: - break - else: - print "destination is none : %s" % neighbor_address - print "please wait more (" + str(interval) + " second)" - time.sleep(interval) - - print "destination is none" - return None - - -def load_gobgp_config(gobgp_config_file): - - config = None - try: - config = toml.loads(open(gobgp_config_file).read()) - except IOError, (errno, strerror): - print "I/O error(%s): %s" % (errno, strerror) - - return config - - -# load configration from quagga(bgpd.conf) -def load_quagga_config(base_dir): - configs = [] - dirs = [] - try: - content = os.listdir(base_dir) - for item in content: - if "q" != item[0]: - continue - if os.path.isdir(os.path.join(base_dir, item)): - dirs.append(item) - except OSError, (errno, strerror): - print "I/O error(%s): %s" % (errno, strerror) - - for dir in dirs: - config_path = base_dir + dir + "/bgpd.conf" - config = CiscoConfParse(config_path) - - peer_ip = config.find_objects(r"^!\smy\saddress")[0].text.split(" ")[3] - peer_ip_version = config.find_objects(r"^!\smy\sip_version")[0].text.split(" ")[3] - peer_id = config.find_objects(r"^bgp\srouter-id")[0].text.split(" ")[2] - peer_as = config.find_objects(r"^router\sbgp")[0].text.split(" ")[2] - quagga_config = Peer(peer_ip, peer_id, peer_as, peer_ip_version) - - networks = config.find_objects(r"^network") - if len(networks) == 0: - continue - for network in networks: - elems = network.text.split(" ") - prefix = elems[1].split("/")[0] - network = elems[1] - nexthop = peer_ip - path = Path(network, nexthop) - dest = Destination(prefix) - dest.paths.append(path) - quagga_config.destinations[prefix] = dest - # print "prefix: " + prefix - # print "network: " + network - # print "nexthop: " + nexthop - - neighbors = config.find_objects(r"^neighbor\s.*\sremote-as") - if len(neighbors) == 0: - continue - for neighbor in neighbors: - elems = neighbor.text.split(" ") - neighbor = Peer(elems[1], None, elems[3], None) - quagga_config.neighbors.append(neighbor) - configs.append(quagga_config) - - return configs
\ No newline at end of file |