diff --git a/api/go/gosdn/topology/link.pb.go b/api/go/gosdn/topology/link.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..a6ccea7d9d76de9798d2f27d4c5548b430d0b33b --- /dev/null +++ b/api/go/gosdn/topology/link.pb.go @@ -0,0 +1,221 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: gosdn/topology/link.proto + +package topology + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Link struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + SourceNode *Node `protobuf:"bytes,3,opt,name=sourceNode,proto3" json:"sourceNode,omitempty"` + TargetNode *Node `protobuf:"bytes,4,opt,name=targetNode,proto3" json:"targetNode,omitempty"` + SourcePort *Port `protobuf:"bytes,5,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"` + TargetPort *Port `protobuf:"bytes,6,opt,name=targetPort,proto3" json:"targetPort,omitempty"` +} + +func (x *Link) Reset() { + *x = Link{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_link_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Link) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Link) ProtoMessage() {} + +func (x *Link) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_link_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Link.ProtoReflect.Descriptor instead. +func (*Link) Descriptor() ([]byte, []int) { + return file_gosdn_topology_link_proto_rawDescGZIP(), []int{0} +} + +func (x *Link) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Link) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Link) GetSourceNode() *Node { + if x != nil { + return x.SourceNode + } + return nil +} + +func (x *Link) GetTargetNode() *Node { + if x != nil { + return x.TargetNode + } + return nil +} + +func (x *Link) GetSourcePort() *Port { + if x != nil { + return x.SourcePort + } + return nil +} + +func (x *Link) GetTargetPort() *Port { + if x != nil { + return x.TargetPort + } + return nil +} + +var File_gosdn_topology_link_proto protoreflect.FileDescriptor + +var file_gosdn_topology_link_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x67, 0x6f, 0x73, + 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, + 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x73, + 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x82, 0x02, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x34, + 0x0a, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, + 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x34, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x42, 0x34, 0x5a, 0x32, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, + 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74, + 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x6f, + 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gosdn_topology_link_proto_rawDescOnce sync.Once + file_gosdn_topology_link_proto_rawDescData = file_gosdn_topology_link_proto_rawDesc +) + +func file_gosdn_topology_link_proto_rawDescGZIP() []byte { + file_gosdn_topology_link_proto_rawDescOnce.Do(func() { + file_gosdn_topology_link_proto_rawDescData = protoimpl.X.CompressGZIP(file_gosdn_topology_link_proto_rawDescData) + }) + return file_gosdn_topology_link_proto_rawDescData +} + +var file_gosdn_topology_link_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_gosdn_topology_link_proto_goTypes = []interface{}{ + (*Link)(nil), // 0: gosdn.topology.Link + (*Node)(nil), // 1: gosdn.topology.Node + (*Port)(nil), // 2: gosdn.topology.Port +} +var file_gosdn_topology_link_proto_depIdxs = []int32{ + 1, // 0: gosdn.topology.Link.sourceNode:type_name -> gosdn.topology.Node + 1, // 1: gosdn.topology.Link.targetNode:type_name -> gosdn.topology.Node + 2, // 2: gosdn.topology.Link.sourcePort:type_name -> gosdn.topology.Port + 2, // 3: gosdn.topology.Link.targetPort:type_name -> gosdn.topology.Port + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_gosdn_topology_link_proto_init() } +func file_gosdn_topology_link_proto_init() { + if File_gosdn_topology_link_proto != nil { + return + } + file_gosdn_topology_node_proto_init() + file_gosdn_topology_port_proto_init() + if !protoimpl.UnsafeEnabled { + file_gosdn_topology_link_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Link); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gosdn_topology_link_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gosdn_topology_link_proto_goTypes, + DependencyIndexes: file_gosdn_topology_link_proto_depIdxs, + MessageInfos: file_gosdn_topology_link_proto_msgTypes, + }.Build() + File_gosdn_topology_link_proto = out.File + file_gosdn_topology_link_proto_rawDesc = nil + file_gosdn_topology_link_proto_goTypes = nil + file_gosdn_topology_link_proto_depIdxs = nil +} diff --git a/api/go/gosdn/topology/node.pb.go b/api/go/gosdn/topology/node.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..0c9330271dd3e7d66d7f8454d254091c32961090 --- /dev/null +++ b/api/go/gosdn/topology/node.pb.go @@ -0,0 +1,164 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: gosdn/topology/node.proto + +package topology + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Node struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Node) Reset() { + *x = Node{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_node_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Node) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Node) ProtoMessage() {} + +func (x *Node) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_node_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Node.ProtoReflect.Descriptor instead. +func (*Node) Descriptor() ([]byte, []int) { + return file_gosdn_topology_node_proto_rawDescGZIP(), []int{0} +} + +func (x *Node) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Node) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_gosdn_topology_node_proto protoreflect.FileDescriptor + +var file_gosdn_topology_node_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x67, 0x6f, 0x73, + 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, + 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2a, 0x0a, 0x04, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x63, 0x6f, 0x64, 0x65, 0x2e, + 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, + 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x67, + 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gosdn_topology_node_proto_rawDescOnce sync.Once + file_gosdn_topology_node_proto_rawDescData = file_gosdn_topology_node_proto_rawDesc +) + +func file_gosdn_topology_node_proto_rawDescGZIP() []byte { + file_gosdn_topology_node_proto_rawDescOnce.Do(func() { + file_gosdn_topology_node_proto_rawDescData = protoimpl.X.CompressGZIP(file_gosdn_topology_node_proto_rawDescData) + }) + return file_gosdn_topology_node_proto_rawDescData +} + +var file_gosdn_topology_node_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_gosdn_topology_node_proto_goTypes = []interface{}{ + (*Node)(nil), // 0: gosdn.topology.Node +} +var file_gosdn_topology_node_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_gosdn_topology_node_proto_init() } +func file_gosdn_topology_node_proto_init() { + if File_gosdn_topology_node_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_gosdn_topology_node_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Node); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gosdn_topology_node_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gosdn_topology_node_proto_goTypes, + DependencyIndexes: file_gosdn_topology_node_proto_depIdxs, + MessageInfos: file_gosdn_topology_node_proto_msgTypes, + }.Build() + File_gosdn_topology_node_proto = out.File + file_gosdn_topology_node_proto_rawDesc = nil + file_gosdn_topology_node_proto_goTypes = nil + file_gosdn_topology_node_proto_depIdxs = nil +} diff --git a/api/go/gosdn/topology/port.pb.go b/api/go/gosdn/topology/port.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..2f80df787591483a23117187bdf41992dbee030b --- /dev/null +++ b/api/go/gosdn/topology/port.pb.go @@ -0,0 +1,249 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: gosdn/topology/port.proto + +package topology + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Configuration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + PrefixLength int64 `protobuf:"varint,2,opt,name=prefixLength,proto3" json:"prefixLength,omitempty"` +} + +func (x *Configuration) Reset() { + *x = Configuration{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_port_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration) ProtoMessage() {} + +func (x *Configuration) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_port_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configuration.ProtoReflect.Descriptor instead. +func (*Configuration) Descriptor() ([]byte, []int) { + return file_gosdn_topology_port_proto_rawDescGZIP(), []int{0} +} + +func (x *Configuration) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *Configuration) GetPrefixLength() int64 { + if x != nil { + return x.PrefixLength + } + return 0 +} + +type Port struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Configuration *Configuration `protobuf:"bytes,3,opt,name=configuration,proto3" json:"configuration,omitempty"` +} + +func (x *Port) Reset() { + *x = Port{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_port_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Port) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Port) ProtoMessage() {} + +func (x *Port) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_port_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Port.ProtoReflect.Descriptor instead. +func (*Port) Descriptor() ([]byte, []int) { + return file_gosdn_topology_port_proto_rawDescGZIP(), []int{1} +} + +func (x *Port) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Port) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Port) GetConfiguration() *Configuration { + if x != nil { + return x.Configuration + } + return nil +} + +var File_gosdn_topology_port_proto protoreflect.FileDescriptor + +var file_gosdn_topology_port_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x67, 0x6f, 0x73, + 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, + 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x43, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x22, 0x0a, 0x0c, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x22, 0x6f, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x34, 0x5a, 0x32, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, + 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x64, + 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gosdn_topology_port_proto_rawDescOnce sync.Once + file_gosdn_topology_port_proto_rawDescData = file_gosdn_topology_port_proto_rawDesc +) + +func file_gosdn_topology_port_proto_rawDescGZIP() []byte { + file_gosdn_topology_port_proto_rawDescOnce.Do(func() { + file_gosdn_topology_port_proto_rawDescData = protoimpl.X.CompressGZIP(file_gosdn_topology_port_proto_rawDescData) + }) + return file_gosdn_topology_port_proto_rawDescData +} + +var file_gosdn_topology_port_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_gosdn_topology_port_proto_goTypes = []interface{}{ + (*Configuration)(nil), // 0: gosdn.topology.Configuration + (*Port)(nil), // 1: gosdn.topology.Port +} +var file_gosdn_topology_port_proto_depIdxs = []int32{ + 0, // 0: gosdn.topology.Port.configuration:type_name -> gosdn.topology.Configuration + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_gosdn_topology_port_proto_init() } +func file_gosdn_topology_port_proto_init() { + if File_gosdn_topology_port_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_gosdn_topology_port_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_port_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Port); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gosdn_topology_port_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gosdn_topology_port_proto_goTypes, + DependencyIndexes: file_gosdn_topology_port_proto_depIdxs, + MessageInfos: file_gosdn_topology_port_proto_msgTypes, + }.Build() + File_gosdn_topology_port_proto = out.File + file_gosdn_topology_port_proto_rawDesc = nil + file_gosdn_topology_port_proto_goTypes = nil + file_gosdn_topology_port_proto_depIdxs = nil +} diff --git a/api/go/gosdn/topology/topology.pb.go b/api/go/gosdn/topology/topology.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..1fd6654fca6b7af7baf4bc126bd2ecbf510c3505 --- /dev/null +++ b/api/go/gosdn/topology/topology.pb.go @@ -0,0 +1,852 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: gosdn/topology/topology.proto + +package topology + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Status int32 + +const ( + Status_STATUS_UNSPECIFIED Status = 0 + Status_STATUS_OK Status = 1 + Status_STATUS_ERROR Status = 2 +) + +// Enum value maps for Status. +var ( + Status_name = map[int32]string{ + 0: "STATUS_UNSPECIFIED", + 1: "STATUS_OK", + 2: "STATUS_ERROR", + } + Status_value = map[string]int32{ + "STATUS_UNSPECIFIED": 0, + "STATUS_OK": 1, + "STATUS_ERROR": 2, + } +) + +func (x Status) Enum() *Status { + p := new(Status) + *p = x + return p +} + +func (x Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Status) Descriptor() protoreflect.EnumDescriptor { + return file_gosdn_topology_topology_proto_enumTypes[0].Descriptor() +} + +func (Status) Type() protoreflect.EnumType { + return &file_gosdn_topology_topology_proto_enumTypes[0] +} + +func (x Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Status.Descriptor instead. +func (Status) EnumDescriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{0} +} + +type Topology struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Links []*Link `protobuf:"bytes,1,rep,name=links,proto3" json:"links,omitempty"` +} + +func (x *Topology) Reset() { + *x = Topology{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Topology) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Topology) ProtoMessage() {} + +func (x *Topology) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Topology.ProtoReflect.Descriptor instead. +func (*Topology) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{0} +} + +func (x *Topology) GetLinks() []*Link { + if x != nil { + return x.Links + } + return nil +} + +type AddLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Link *Link `protobuf:"bytes,2,opt,name=link,proto3" json:"link,omitempty"` +} + +func (x *AddLinkRequest) Reset() { + *x = AddLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddLinkRequest) ProtoMessage() {} + +func (x *AddLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddLinkRequest.ProtoReflect.Descriptor instead. +func (*AddLinkRequest) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{1} +} + +func (x *AddLinkRequest) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *AddLinkRequest) GetLink() *Link { + if x != nil { + return x.Link + } + return nil +} + +type AddLinkResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.topology.Status" json:"status,omitempty"` +} + +func (x *AddLinkResponse) Reset() { + *x = AddLinkResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddLinkResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddLinkResponse) ProtoMessage() {} + +func (x *AddLinkResponse) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddLinkResponse.ProtoReflect.Descriptor instead. +func (*AddLinkResponse) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{2} +} + +func (x *AddLinkResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *AddLinkResponse) GetStatus() Status { + if x != nil { + return x.Status + } + return Status_STATUS_UNSPECIFIED +} + +type GetTopologyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *GetTopologyRequest) Reset() { + *x = GetTopologyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTopologyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTopologyRequest) ProtoMessage() {} + +func (x *GetTopologyRequest) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTopologyRequest.ProtoReflect.Descriptor instead. +func (*GetTopologyRequest) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{3} +} + +func (x *GetTopologyRequest) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type GetTopologyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.topology.Status" json:"status,omitempty"` + Toplogy *Topology `protobuf:"bytes,3,opt,name=toplogy,proto3" json:"toplogy,omitempty"` +} + +func (x *GetTopologyResponse) Reset() { + *x = GetTopologyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTopologyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTopologyResponse) ProtoMessage() {} + +func (x *GetTopologyResponse) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTopologyResponse.ProtoReflect.Descriptor instead. +func (*GetTopologyResponse) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{4} +} + +func (x *GetTopologyResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *GetTopologyResponse) GetStatus() Status { + if x != nil { + return x.Status + } + return Status_STATUS_UNSPECIFIED +} + +func (x *GetTopologyResponse) GetToplogy() *Topology { + if x != nil { + return x.Toplogy + } + return nil +} + +type UpdateLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Link *Link `protobuf:"bytes,2,opt,name=link,proto3" json:"link,omitempty"` +} + +func (x *UpdateLinkRequest) Reset() { + *x = UpdateLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateLinkRequest) ProtoMessage() {} + +func (x *UpdateLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateLinkRequest.ProtoReflect.Descriptor instead. +func (*UpdateLinkRequest) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{5} +} + +func (x *UpdateLinkRequest) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *UpdateLinkRequest) GetLink() *Link { + if x != nil { + return x.Link + } + return nil +} + +type UpdateLinkResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.topology.Status" json:"status,omitempty"` +} + +func (x *UpdateLinkResponse) Reset() { + *x = UpdateLinkResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateLinkResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateLinkResponse) ProtoMessage() {} + +func (x *UpdateLinkResponse) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateLinkResponse.ProtoReflect.Descriptor instead. +func (*UpdateLinkResponse) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateLinkResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *UpdateLinkResponse) GetStatus() Status { + if x != nil { + return x.Status + } + return Status_STATUS_UNSPECIFIED +} + +type DeleteLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteLinkRequest) Reset() { + *x = DeleteLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteLinkRequest) ProtoMessage() {} + +func (x *DeleteLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteLinkRequest.ProtoReflect.Descriptor instead. +func (*DeleteLinkRequest) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{7} +} + +func (x *DeleteLinkRequest) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *DeleteLinkRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteLinkResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.topology.Status" json:"status,omitempty"` +} + +func (x *DeleteLinkResponse) Reset() { + *x = DeleteLinkResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gosdn_topology_topology_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteLinkResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteLinkResponse) ProtoMessage() {} + +func (x *DeleteLinkResponse) ProtoReflect() protoreflect.Message { + mi := &file_gosdn_topology_topology_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteLinkResponse.ProtoReflect.Descriptor instead. +func (*DeleteLinkResponse) Descriptor() ([]byte, []int) { + return file_gosdn_topology_topology_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteLinkResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *DeleteLinkResponse) GetStatus() Status { + if x != nil { + return x.Status + } + return Status_STATUS_UNSPECIFIED +} + +var File_gosdn_topology_topology_proto protoreflect.FileDescriptor + +var file_gosdn_topology_topology_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, + 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x19, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, + 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x08, 0x54, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x05, 0x6c, 0x69, 0x6e, + 0x6b, 0x73, 0x22, 0x58, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x5f, 0x0a, 0x0f, + 0x41, 0x64, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, + 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x22, 0x97, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, + 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x74, 0x6f, 0x70, 0x6c, 0x6f, + 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x52, 0x07, 0x74, 0x6f, 0x70, 0x6c, 0x6f, 0x67, 0x79, 0x22, 0x5b, 0x0a, 0x11, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, + 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, + 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x4c, 0x69, + 0x6e, 0x6b, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x62, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x67, + 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x11, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x62, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2a, 0x41, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, + 0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x32, 0xc6, 0x03, 0x0a, 0x0f, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x07, 0x41, 0x64, + 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x41, 0x64, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x41, 0x64, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, + 0x2a, 0x22, 0x10, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x12, 0x69, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x70, + 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x2e, 0x67, + 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, + 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x6d, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, + 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, + 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, + 0x34, 0x5a, 0x32, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, + 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gosdn_topology_topology_proto_rawDescOnce sync.Once + file_gosdn_topology_topology_proto_rawDescData = file_gosdn_topology_topology_proto_rawDesc +) + +func file_gosdn_topology_topology_proto_rawDescGZIP() []byte { + file_gosdn_topology_topology_proto_rawDescOnce.Do(func() { + file_gosdn_topology_topology_proto_rawDescData = protoimpl.X.CompressGZIP(file_gosdn_topology_topology_proto_rawDescData) + }) + return file_gosdn_topology_topology_proto_rawDescData +} + +var file_gosdn_topology_topology_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_gosdn_topology_topology_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_gosdn_topology_topology_proto_goTypes = []interface{}{ + (Status)(0), // 0: gosdn.topology.Status + (*Topology)(nil), // 1: gosdn.topology.Topology + (*AddLinkRequest)(nil), // 2: gosdn.topology.AddLinkRequest + (*AddLinkResponse)(nil), // 3: gosdn.topology.AddLinkResponse + (*GetTopologyRequest)(nil), // 4: gosdn.topology.GetTopologyRequest + (*GetTopologyResponse)(nil), // 5: gosdn.topology.GetTopologyResponse + (*UpdateLinkRequest)(nil), // 6: gosdn.topology.UpdateLinkRequest + (*UpdateLinkResponse)(nil), // 7: gosdn.topology.UpdateLinkResponse + (*DeleteLinkRequest)(nil), // 8: gosdn.topology.DeleteLinkRequest + (*DeleteLinkResponse)(nil), // 9: gosdn.topology.DeleteLinkResponse + (*Link)(nil), // 10: gosdn.topology.Link +} +var file_gosdn_topology_topology_proto_depIdxs = []int32{ + 10, // 0: gosdn.topology.Topology.links:type_name -> gosdn.topology.Link + 10, // 1: gosdn.topology.AddLinkRequest.link:type_name -> gosdn.topology.Link + 0, // 2: gosdn.topology.AddLinkResponse.status:type_name -> gosdn.topology.Status + 0, // 3: gosdn.topology.GetTopologyResponse.status:type_name -> gosdn.topology.Status + 1, // 4: gosdn.topology.GetTopologyResponse.toplogy:type_name -> gosdn.topology.Topology + 10, // 5: gosdn.topology.UpdateLinkRequest.link:type_name -> gosdn.topology.Link + 0, // 6: gosdn.topology.UpdateLinkResponse.status:type_name -> gosdn.topology.Status + 0, // 7: gosdn.topology.DeleteLinkResponse.status:type_name -> gosdn.topology.Status + 2, // 8: gosdn.topology.TopologyService.AddLink:input_type -> gosdn.topology.AddLinkRequest + 4, // 9: gosdn.topology.TopologyService.GetTopology:input_type -> gosdn.topology.GetTopologyRequest + 6, // 10: gosdn.topology.TopologyService.UpdateLink:input_type -> gosdn.topology.UpdateLinkRequest + 8, // 11: gosdn.topology.TopologyService.DeleteLink:input_type -> gosdn.topology.DeleteLinkRequest + 3, // 12: gosdn.topology.TopologyService.AddLink:output_type -> gosdn.topology.AddLinkResponse + 5, // 13: gosdn.topology.TopologyService.GetTopology:output_type -> gosdn.topology.GetTopologyResponse + 7, // 14: gosdn.topology.TopologyService.UpdateLink:output_type -> gosdn.topology.UpdateLinkResponse + 9, // 15: gosdn.topology.TopologyService.DeleteLink:output_type -> gosdn.topology.DeleteLinkResponse + 12, // [12:16] is the sub-list for method output_type + 8, // [8:12] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_gosdn_topology_topology_proto_init() } +func file_gosdn_topology_topology_proto_init() { + if File_gosdn_topology_topology_proto != nil { + return + } + file_gosdn_topology_link_proto_init() + if !protoimpl.UnsafeEnabled { + file_gosdn_topology_topology_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Topology); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddLinkResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTopologyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTopologyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateLinkResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gosdn_topology_topology_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteLinkResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gosdn_topology_topology_proto_rawDesc, + NumEnums: 1, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_gosdn_topology_topology_proto_goTypes, + DependencyIndexes: file_gosdn_topology_topology_proto_depIdxs, + EnumInfos: file_gosdn_topology_topology_proto_enumTypes, + MessageInfos: file_gosdn_topology_topology_proto_msgTypes, + }.Build() + File_gosdn_topology_topology_proto = out.File + file_gosdn_topology_topology_proto_rawDesc = nil + file_gosdn_topology_topology_proto_goTypes = nil + file_gosdn_topology_topology_proto_depIdxs = nil +} diff --git a/api/go/gosdn/topology/topology.pb.gw.go b/api/go/gosdn/topology/topology.pb.gw.go new file mode 100644 index 0000000000000000000000000000000000000000..44945a07e842b8144a5f2d64ec0b719c7853d816 --- /dev/null +++ b/api/go/gosdn/topology/topology.pb.gw.go @@ -0,0 +1,414 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: gosdn/topology/topology.proto + +/* +Package topology is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package topology + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_TopologyService_AddLink_0(ctx context.Context, marshaler runtime.Marshaler, client TopologyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddLinkRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.AddLink(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TopologyService_AddLink_0(ctx context.Context, marshaler runtime.Marshaler, server TopologyServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddLinkRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.AddLink(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_TopologyService_GetTopology_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_TopologyService_GetTopology_0(ctx context.Context, marshaler runtime.Marshaler, client TopologyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTopologyRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TopologyService_GetTopology_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetTopology(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TopologyService_GetTopology_0(ctx context.Context, marshaler runtime.Marshaler, server TopologyServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTopologyRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TopologyService_GetTopology_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetTopology(ctx, &protoReq) + return msg, metadata, err + +} + +func request_TopologyService_UpdateLink_0(ctx context.Context, marshaler runtime.Marshaler, client TopologyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateLinkRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateLink(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TopologyService_UpdateLink_0(ctx context.Context, marshaler runtime.Marshaler, server TopologyServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateLinkRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateLink(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_TopologyService_DeleteLink_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_TopologyService_DeleteLink_0(ctx context.Context, marshaler runtime.Marshaler, client TopologyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteLinkRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TopologyService_DeleteLink_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DeleteLink(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TopologyService_DeleteLink_0(ctx context.Context, marshaler runtime.Marshaler, server TopologyServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteLinkRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TopologyService_DeleteLink_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DeleteLink(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterTopologyServiceHandlerServer registers the http handlers for service TopologyService to "mux". +// UnaryRPC :call TopologyServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterTopologyServiceHandlerFromEndpoint instead. +func RegisterTopologyServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server TopologyServiceServer) error { + + mux.Handle("POST", pattern_TopologyService_AddLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.topology.TopologyService/AddLink", runtime.WithHTTPPathPattern("/topology/create")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TopologyService_AddLink_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_AddLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_TopologyService_GetTopology_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.topology.TopologyService/GetTopology", runtime.WithHTTPPathPattern("/topology")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TopologyService_GetTopology_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_GetTopology_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_TopologyService_UpdateLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.topology.TopologyService/UpdateLink", runtime.WithHTTPPathPattern("/topology/update")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TopologyService_UpdateLink_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_UpdateLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_TopologyService_DeleteLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.topology.TopologyService/DeleteLink", runtime.WithHTTPPathPattern("/topology/delete")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TopologyService_DeleteLink_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_DeleteLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterTopologyServiceHandlerFromEndpoint is same as RegisterTopologyServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterTopologyServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterTopologyServiceHandler(ctx, mux, conn) +} + +// RegisterTopologyServiceHandler registers the http handlers for service TopologyService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterTopologyServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterTopologyServiceHandlerClient(ctx, mux, NewTopologyServiceClient(conn)) +} + +// RegisterTopologyServiceHandlerClient registers the http handlers for service TopologyService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "TopologyServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "TopologyServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "TopologyServiceClient" to call the correct interceptors. +func RegisterTopologyServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client TopologyServiceClient) error { + + mux.Handle("POST", pattern_TopologyService_AddLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.topology.TopologyService/AddLink", runtime.WithHTTPPathPattern("/topology/create")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TopologyService_AddLink_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_AddLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_TopologyService_GetTopology_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.topology.TopologyService/GetTopology", runtime.WithHTTPPathPattern("/topology")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TopologyService_GetTopology_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_GetTopology_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_TopologyService_UpdateLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.topology.TopologyService/UpdateLink", runtime.WithHTTPPathPattern("/topology/update")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TopologyService_UpdateLink_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_UpdateLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_TopologyService_DeleteLink_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.topology.TopologyService/DeleteLink", runtime.WithHTTPPathPattern("/topology/delete")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TopologyService_DeleteLink_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TopologyService_DeleteLink_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_TopologyService_AddLink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"topology", "create"}, "")) + + pattern_TopologyService_GetTopology_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"topology"}, "")) + + pattern_TopologyService_UpdateLink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"topology", "update"}, "")) + + pattern_TopologyService_DeleteLink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"topology", "delete"}, "")) +) + +var ( + forward_TopologyService_AddLink_0 = runtime.ForwardResponseMessage + + forward_TopologyService_GetTopology_0 = runtime.ForwardResponseMessage + + forward_TopologyService_UpdateLink_0 = runtime.ForwardResponseMessage + + forward_TopologyService_DeleteLink_0 = runtime.ForwardResponseMessage +) diff --git a/api/go/gosdn/topology/topology_grpc.pb.go b/api/go/gosdn/topology/topology_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..804c1c406231536e5918c899b23b5a9065d06068 --- /dev/null +++ b/api/go/gosdn/topology/topology_grpc.pb.go @@ -0,0 +1,209 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package topology + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// TopologyServiceClient is the client API for TopologyService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TopologyServiceClient interface { + AddLink(ctx context.Context, in *AddLinkRequest, opts ...grpc.CallOption) (*AddLinkResponse, error) + GetTopology(ctx context.Context, in *GetTopologyRequest, opts ...grpc.CallOption) (*GetTopologyResponse, error) + UpdateLink(ctx context.Context, in *UpdateLinkRequest, opts ...grpc.CallOption) (*UpdateLinkResponse, error) + DeleteLink(ctx context.Context, in *DeleteLinkRequest, opts ...grpc.CallOption) (*DeleteLinkResponse, error) +} + +type topologyServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTopologyServiceClient(cc grpc.ClientConnInterface) TopologyServiceClient { + return &topologyServiceClient{cc} +} + +func (c *topologyServiceClient) AddLink(ctx context.Context, in *AddLinkRequest, opts ...grpc.CallOption) (*AddLinkResponse, error) { + out := new(AddLinkResponse) + err := c.cc.Invoke(ctx, "/gosdn.topology.TopologyService/AddLink", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *topologyServiceClient) GetTopology(ctx context.Context, in *GetTopologyRequest, opts ...grpc.CallOption) (*GetTopologyResponse, error) { + out := new(GetTopologyResponse) + err := c.cc.Invoke(ctx, "/gosdn.topology.TopologyService/GetTopology", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *topologyServiceClient) UpdateLink(ctx context.Context, in *UpdateLinkRequest, opts ...grpc.CallOption) (*UpdateLinkResponse, error) { + out := new(UpdateLinkResponse) + err := c.cc.Invoke(ctx, "/gosdn.topology.TopologyService/UpdateLink", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *topologyServiceClient) DeleteLink(ctx context.Context, in *DeleteLinkRequest, opts ...grpc.CallOption) (*DeleteLinkResponse, error) { + out := new(DeleteLinkResponse) + err := c.cc.Invoke(ctx, "/gosdn.topology.TopologyService/DeleteLink", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TopologyServiceServer is the server API for TopologyService service. +// All implementations must embed UnimplementedTopologyServiceServer +// for forward compatibility +type TopologyServiceServer interface { + AddLink(context.Context, *AddLinkRequest) (*AddLinkResponse, error) + GetTopology(context.Context, *GetTopologyRequest) (*GetTopologyResponse, error) + UpdateLink(context.Context, *UpdateLinkRequest) (*UpdateLinkResponse, error) + DeleteLink(context.Context, *DeleteLinkRequest) (*DeleteLinkResponse, error) + mustEmbedUnimplementedTopologyServiceServer() +} + +// UnimplementedTopologyServiceServer must be embedded to have forward compatible implementations. +type UnimplementedTopologyServiceServer struct { +} + +func (UnimplementedTopologyServiceServer) AddLink(context.Context, *AddLinkRequest) (*AddLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddLink not implemented") +} +func (UnimplementedTopologyServiceServer) GetTopology(context.Context, *GetTopologyRequest) (*GetTopologyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTopology not implemented") +} +func (UnimplementedTopologyServiceServer) UpdateLink(context.Context, *UpdateLinkRequest) (*UpdateLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateLink not implemented") +} +func (UnimplementedTopologyServiceServer) DeleteLink(context.Context, *DeleteLinkRequest) (*DeleteLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteLink not implemented") +} +func (UnimplementedTopologyServiceServer) mustEmbedUnimplementedTopologyServiceServer() {} + +// UnsafeTopologyServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TopologyServiceServer will +// result in compilation errors. +type UnsafeTopologyServiceServer interface { + mustEmbedUnimplementedTopologyServiceServer() +} + +func RegisterTopologyServiceServer(s grpc.ServiceRegistrar, srv TopologyServiceServer) { + s.RegisterService(&TopologyService_ServiceDesc, srv) +} + +func _TopologyService_AddLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TopologyServiceServer).AddLink(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gosdn.topology.TopologyService/AddLink", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TopologyServiceServer).AddLink(ctx, req.(*AddLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TopologyService_GetTopology_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTopologyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TopologyServiceServer).GetTopology(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gosdn.topology.TopologyService/GetTopology", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TopologyServiceServer).GetTopology(ctx, req.(*GetTopologyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TopologyService_UpdateLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TopologyServiceServer).UpdateLink(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gosdn.topology.TopologyService/UpdateLink", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TopologyServiceServer).UpdateLink(ctx, req.(*UpdateLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TopologyService_DeleteLink_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TopologyServiceServer).DeleteLink(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gosdn.topology.TopologyService/DeleteLink", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TopologyServiceServer).DeleteLink(ctx, req.(*DeleteLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TopologyService_ServiceDesc is the grpc.ServiceDesc for TopologyService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TopologyService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gosdn.topology.TopologyService", + HandlerType: (*TopologyServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddLink", + Handler: _TopologyService_AddLink_Handler, + }, + { + MethodName: "GetTopology", + Handler: _TopologyService_GetTopology_Handler, + }, + { + MethodName: "UpdateLink", + Handler: _TopologyService_UpdateLink_Handler, + }, + { + MethodName: "DeleteLink", + Handler: _TopologyService_DeleteLink_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "gosdn/topology/topology.proto", +} diff --git a/api/openapiv2/gosdn_northbound.swagger.json b/api/openapiv2/gosdn_northbound.swagger.json index 3fd28b3f137a29baa815120e465337fe7679c9f5..b9ded2cc7a626857f7fd65de11bd4274aa5c5c01 100644 --- a/api/openapiv2/gosdn_northbound.swagger.json +++ b/api/openapiv2/gosdn_northbound.swagger.json @@ -43,6 +43,9 @@ }, { "name": "UserService" + }, + { + "name": "TopologyService" } ], "consumes": [ @@ -1037,6 +1040,138 @@ ] } }, + "/topology": { + "get": { + "operationId": "TopologyService_GetTopology", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/topologyGetTopologyResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "timestamp", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + } + ], + "tags": [ + "TopologyService" + ] + } + }, + "/topology/create": { + "post": { + "operationId": "TopologyService_AddLink", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/topologyAddLinkResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/topologyAddLinkRequest" + } + } + ], + "tags": [ + "TopologyService" + ] + } + }, + "/topology/delete": { + "delete": { + "operationId": "TopologyService_DeleteLink", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/topologyDeleteLinkResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "timestamp", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "id", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "TopologyService" + ] + } + }, + "/topology/update": { + "post": { + "operationId": "TopologyService_UpdateLink", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/topologyUpdateLinkResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/topologyUpdateLinkRequest" + } + } + ], + "tags": [ + "TopologyService" + ] + } + }, "/users": { "get": { "summary": "Requests information about available users, requires login beforehand.\nRequires highest possible permissions.", @@ -2544,6 +2679,27 @@ "default": "TYPE_UNSPECIFIED", "title": "Changed according to style guide:\nhttps://docs.buf.build/best-practices/style-guide#enums" }, + "gosdntopologyConfiguration": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "prefixLength": { + "type": "string", + "format": "int64" + } + } + }, + "gosdntopologyStatus": { + "type": "string", + "enum": [ + "STATUS_UNSPECIFIED", + "STATUS_OK", + "STATUS_ERROR" + ], + "default": "STATUS_UNSPECIFIED" + }, "pndApiOperation": { "type": "string", "enum": [ @@ -3194,6 +3350,140 @@ } } }, + "topologyAddLinkRequest": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "link": { + "$ref": "#/definitions/topologyLink" + } + } + }, + "topologyAddLinkResponse": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "status": { + "$ref": "#/definitions/gosdntopologyStatus" + } + } + }, + "topologyDeleteLinkResponse": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "status": { + "$ref": "#/definitions/gosdntopologyStatus" + } + } + }, + "topologyGetTopologyResponse": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "status": { + "$ref": "#/definitions/gosdntopologyStatus" + }, + "toplogy": { + "$ref": "#/definitions/topologyTopology" + } + } + }, + "topologyLink": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sourceNode": { + "$ref": "#/definitions/topologyNode" + }, + "targetNode": { + "$ref": "#/definitions/topologyNode" + }, + "sourcePort": { + "$ref": "#/definitions/topologyPort" + }, + "targetPort": { + "$ref": "#/definitions/topologyPort" + } + } + }, + "topologyNode": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "topologyPort": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "configuration": { + "$ref": "#/definitions/gosdntopologyConfiguration" + } + } + }, + "topologyTopology": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/definitions/topologyLink" + } + } + } + }, + "topologyUpdateLinkRequest": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "link": { + "$ref": "#/definitions/topologyLink" + } + } + }, + "topologyUpdateLinkResponse": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64" + }, + "status": { + "$ref": "#/definitions/gosdntopologyStatus" + } + } + }, "transportGnmiTransportOption": { "type": "object", "properties": { diff --git a/api/proto/buf.lock b/api/proto/buf.lock index 91a4b401f2413cfa0b1cd65bdd506a0565853c96..ae19172c5bd7b8c97e02f094e2ead2406d5803ec 100644 --- a/api/proto/buf.lock +++ b/api/proto/buf.lock @@ -4,7 +4,7 @@ deps: - remote: buf.build owner: googleapis repository: googleapis - commit: fdc236b6d1644b29a6161156ce08d8a2 + commit: d8957b4333cc4523b3802d0af0e059e3 - remote: buf.build owner: grpc-ecosystem repository: grpc-gateway diff --git a/api/proto/gosdn/topology/link.proto b/api/proto/gosdn/topology/link.proto new file mode 100644 index 0000000000000000000000000000000000000000..70aee027733179856e95c9fc92c51670126d51fe --- /dev/null +++ b/api/proto/gosdn/topology/link.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package gosdn.topology; + +import "google/api/annotations.proto"; +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +import "gosdn/topology/node.proto"; +import "gosdn/topology/port.proto"; + +option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"; + +message Link { + string id = 1; + string name = 2; + Node sourceNode = 3; + Node targetNode = 4; + Port sourcePort = 5; + Port targetPort = 6; +} diff --git a/api/proto/gosdn/topology/node.proto b/api/proto/gosdn/topology/node.proto new file mode 100644 index 0000000000000000000000000000000000000000..ca06fab019f59cd8584b2dbe4f6b4d40b34dd267 --- /dev/null +++ b/api/proto/gosdn/topology/node.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package gosdn.topology; + +import "google/api/annotations.proto"; +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + + +option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"; + +message Node { + string id = 1; + string name = 2; +} diff --git a/api/proto/gosdn/topology/port.proto b/api/proto/gosdn/topology/port.proto new file mode 100644 index 0000000000000000000000000000000000000000..a7f623a1e899d7c237f4d2fbffc97dc0deb4bcc5 --- /dev/null +++ b/api/proto/gosdn/topology/port.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package gosdn.topology; + +import "google/api/annotations.proto"; +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + + +option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"; + +message Configuration { + string ip = 1; + int64 prefixLength = 2; +} + +message Port { + string id = 1; + string name = 2; + Configuration configuration = 3; +} diff --git a/api/proto/gosdn/topology/topology.proto b/api/proto/gosdn/topology/topology.proto new file mode 100644 index 0000000000000000000000000000000000000000..649744f6c58c93bdd82ea5040938ca1688806a0f --- /dev/null +++ b/api/proto/gosdn/topology/topology.proto @@ -0,0 +1,91 @@ +syntax = "proto3"; + +package gosdn.topology; + +import "google/api/annotations.proto"; +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +import "gosdn/topology/link.proto"; + + +option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"; + +service TopologyService { + rpc AddLink(AddLinkRequest) returns (AddLinkResponse) { + option (google.api.http) = { + post: "/topology/create" + body: "*" + }; + } + + rpc GetTopology(GetTopologyRequest) returns (GetTopologyResponse) { + option (google.api.http) = { + get: "/topology" + }; + } + + rpc UpdateLink(UpdateLinkRequest) returns (UpdateLinkResponse) { + option (google.api.http) = { + post: "/topology/update" + body: "*" + }; + } + + rpc DeleteLink(DeleteLinkRequest) returns (DeleteLinkResponse) { + option (google.api.http) = { + delete: "/topology/delete" + }; + } +} + +enum Status { + STATUS_UNSPECIFIED = 0; + STATUS_OK = 1; + STATUS_ERROR = 2; +} + +message Topology { + repeated Link links = 1; +} + +message AddLinkRequest { + int64 timestamp = 1; + Link link = 2; +} + +message AddLinkResponse { + int64 timestamp = 1; + Status status = 2; +} + +message GetTopologyRequest { + int64 timestamp = 1; +} + +message GetTopologyResponse { + int64 timestamp = 1; + Status status = 2; + Topology toplogy = 3; +} + +message UpdateLinkRequest { + int64 timestamp = 1; + Link link = 2; +} + +message UpdateLinkResponse { + int64 timestamp = 1; + Status status = 2; +} + +message DeleteLinkRequest { + int64 timestamp = 1; + string id = 2; +} + +message DeleteLinkResponse { + int64 timestamp = 1; + Status status = 2; +} + diff --git a/controller/api/initialise_test.go b/controller/api/initialise_test.go index 46726014e7acdd5493f6de98a6caea0e3b71f714..8c0dfabb393604ea5d2ed3f167710dd5f45a38b6 100644 --- a/controller/api/initialise_test.go +++ b/controller/api/initialise_test.go @@ -23,6 +23,11 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/nucleus" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/util/proto" rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -145,7 +150,24 @@ func bootstrapUnitTest() { jwtManager := rbacImpl.NewJWTManager("", (10000 * time.Hour)) - northbound := nbi.NewNBI(pndStore, userService, roleService, *jwtManager) + nodeStore := store.NewGenericStore[nodes.Node]() + nodeService := nodes.NewNodeService(nodeStore, eventService) + + portStore := store.NewGenericStore[ports.Port]() + portService := ports.NewPortService(portStore, eventService) + + topoloyStore := store.NewGenericStore[links.Link]() + topologyService := topology.NewTopologyService(topoloyStore, nodeService, portService, eventService) + + northbound := nbi.NewNBI( + pndStore, + userService, + roleService, + *jwtManager, + topologyService, + nodeService, + portService, + ) cpb.RegisterCoreServiceServer(s, northbound.Core) ppb.RegisterPndServiceServer(s, northbound.Pnd) diff --git a/controller/controller.go b/controller/controller.go index a1a8333c6a01fc30d5709ca918d05114423cbe0b..387bae4769b833b126c8a386fbf9eb067c0cf1ce 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -24,6 +24,7 @@ import ( ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" + tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" "code.fbi.h-da.de/danet/gosdn/controller/config" eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" @@ -33,6 +34,9 @@ import ( nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server" rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac" "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" @@ -44,14 +48,17 @@ var coreOnce sync.Once // Core is the representation of the controller's core type Core struct { - pndStore networkdomain.PndStore - userService rbac.UserService - roleService rbac.RoleService - httpServer *http.Server - grpcServer *grpc.Server - nbi *nbi.NorthboundInterface - eventService eventInterfaces.Service - stopChan chan os.Signal + pndStore networkdomain.PndStore + userService rbac.UserService + roleService rbac.RoleService + topologyService topology.Service + nodeService nodes.Service + portService ports.Service + httpServer *http.Server + grpcServer *grpc.Server + nbi *nbi.NorthboundInterface + eventService eventInterfaces.Service + stopChan chan os.Signal csbiClient cpb.CsbiServiceClient } @@ -70,10 +77,21 @@ func initialize() error { return err } + nodeService := nodes.NewNodeService(nodes.NewDatabaseNodeStore(), eventService) + portService := ports.NewPortService(ports.NewDatabasePortStore(), eventService) + c = &Core{ - pndStore: nucleus.NewPndStore(), - userService: rbacImpl.NewUserService(rbacImpl.NewUserStore(), eventService), - roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore(), eventService), + pndStore: nucleus.NewPndStore(), + userService: rbacImpl.NewUserService(rbacImpl.NewUserStore(), eventService), + roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore(), eventService), + topologyService: topology.NewTopologyService( + topology.NewDatabaseTopologyStore(), + nodeService, + portService, + eventService, + ), + nodeService: nodeService, + portService: portService, eventService: eventService, stopChan: make(chan os.Signal, 1), } @@ -118,7 +136,15 @@ func startGrpc() error { jwtManager := rbacImpl.NewJWTManager(config.JWTSecret, config.JWTDuration) setupGRPCServerWithCorrectSecurityLevel(jwtManager, c.userService, c.roleService) - c.nbi = nbi.NewNBI(c.pndStore, c.userService, c.roleService, *jwtManager) + c.nbi = nbi.NewNBI( + c.pndStore, + c.userService, + c.roleService, + *jwtManager, + c.topologyService, + c.nodeService, + c.portService, + ) pb.RegisterCoreServiceServer(c.grpcServer, c.nbi.Core) ppb.RegisterPndServiceServer(c.grpcServer, c.nbi.Pnd) @@ -127,6 +153,8 @@ func startGrpc() error { apb.RegisterAuthServiceServer(c.grpcServer, c.nbi.Auth) apb.RegisterUserServiceServer(c.grpcServer, c.nbi.User) apb.RegisterRoleServiceServer(c.grpcServer, c.nbi.Role) + tpb.RegisterTopologyServiceServer(c.grpcServer, c.nbi.Topology) + go func() { if err := c.grpcServer.Serve(lislisten); err != nil { log.Fatal(err) diff --git a/controller/http.go b/controller/http.go index 58d6d47d6486cecbe55e1858951fdfc7e622a164..f0a04a9b69452a61a18f45037657d68c382cd849 100644 --- a/controller/http.go +++ b/controller/http.go @@ -18,6 +18,7 @@ import ( cgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core" pgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" agw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" + tgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" ) var ( @@ -75,6 +76,11 @@ func run() error { return err } + err = tgw.RegisterTopologyServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts) + if err != nil { + return err + } + // Set the HTTP server of core to the new server c.httpServer = &http.Server{Addr: ":8080", Handler: mux} // Start HTTP server (and proxy calls to gRPC server endpoint) diff --git a/controller/mocks/Device.go b/controller/mocks/Device.go index 5cac8a61ddbd4ee56e24f840fa5ae5f825dc6249..a9158c0cd3424399ffb29ee564b07162b6b75ac3 100644 --- a/controller/mocks/Device.go +++ b/controller/mocks/Device.go @@ -58,27 +58,6 @@ func (_m *Device) GetModel() ygot.GoStruct { return r0 } -// GetModelAsString provides a mock function with given fields: -func (_m *Device) GetModelAsString() (string, error) { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ID provides a mock function with given fields: func (_m *Device) ID() uuid.UUID { ret := _m.Called() diff --git a/controller/northbound/server/nbi.go b/controller/northbound/server/nbi.go index cd4e983516f8ef4d498ebdf7fb956333e796a0be..3fb3bb65cea4d596e40aa424b1c99f1cd1338278 100644 --- a/controller/northbound/server/nbi.go +++ b/controller/northbound/server/nbi.go @@ -4,6 +4,9 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" "code.fbi.h-da.de/danet/gosdn/controller/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" "code.fbi.h-da.de/danet/gosdn/controller/metrics" "github.com/prometheus/client_golang/prometheus" @@ -15,25 +18,36 @@ import ( // NorthboundInterface is the representation of the // gRPC services used provided. type NorthboundInterface struct { - Pnd *PndServer - Core *Core - Csbi *Csbi - Sbi *SbiServer - Auth *Auth - User *User - Role *Role + Pnd *PndServer + Core *Core + Csbi *Csbi + Sbi *SbiServer + Auth *Auth + User *User + Role *Role + Topology *Topology } // NewNBI receives a PndStore and returns a new gRPC *NorthboundInterface -func NewNBI(pnds networkdomain.PndStore, users rbacInterfaces.UserService, roles rbacInterfaces.RoleService, jwt rbac.JWTManager) *NorthboundInterface { +func NewNBI( + pnds networkdomain.PndStore, + users rbacInterfaces.UserService, + roles rbacInterfaces.RoleService, + jwt rbac.JWTManager, + topologyService topology.Service, + nodeService nodes.Service, + portService ports.Service, + +) *NorthboundInterface { return &NorthboundInterface{ - Pnd: NewPndServer(pnds), - Core: NewCoreServer(pnds), - Csbi: NewCsbiServer(pnds), - Sbi: NewSbiServer(pnds), - Auth: NewAuthServer(&jwt, users), - User: NewUserServer(&jwt, users), - Role: NewRoleServer(&jwt, roles), + Pnd: NewPndServer(pnds), + Core: NewCoreServer(pnds), + Csbi: NewCsbiServer(pnds), + Sbi: NewSbiServer(pnds), + Auth: NewAuthServer(&jwt, users), + User: NewUserServer(&jwt, users), + Role: NewRoleServer(&jwt, roles), + Topology: NewTopologyServer(topologyService, nodeService, portService), } } diff --git a/controller/northbound/server/topology.go b/controller/northbound/server/topology.go new file mode 100644 index 0000000000000000000000000000000000000000..0557f2eee98a60aecca6dc5038511e9cd90b18ca --- /dev/null +++ b/controller/northbound/server/topology.go @@ -0,0 +1,191 @@ +package server + +import ( + "context" + "time" + + topopb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports/configuration" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Topology holds a topologyService and represents a TopologyServiceServer. +type Topology struct { + topopb.UnimplementedTopologyServiceServer + topologyService topology.Service + nodeService nodes.Service + portService ports.Service +} + +// NewTopologyServer receives a topologyService and returns a new TopologyServer. +func NewTopologyServer( + service topology.Service, + nodeService nodes.Service, + portService ports.Service, +) *Topology { + return &Topology{ + topologyService: service, + nodeService: nodeService, + portService: portService, + } +} + +// AddLink adds a new link to the topology +func (t *Topology) AddLink(ctx context.Context, request *topopb.AddLinkRequest) (*topopb.AddLinkResponse, error) { + sourceNode, sourcePort, err := t.ensureNodeAndPortExists(request.Link.SourceNode, request.Link.SourcePort) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + targetNode, targetPort, err := t.ensureNodeAndPortExists(request.Link.TargetNode, request.Link.TargetPort) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + link := links.Link{ + ID: uuid.New(), + Name: request.Link.Name, + SourceNode: sourceNode, + SourcePort: sourcePort, + TargetNode: targetNode, + TargetPort: targetPort, + } + err = t.topologyService.AddLink(link) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + return &topopb.AddLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_OK, + }, nil +} + +// GetTopology returns the current topology in the form of all links +func (t *Topology) GetTopology(ctx context.Context, request *topopb.GetTopologyRequest) (*topopb.GetTopologyResponse, error) { + topo, err := t.topologyService.GetAll() + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + topology := &topopb.Topology{} + + for _, link := range topo { + topology.Links = append(topology.Links, &topopb.Link{ + Id: link.ID.String(), + Name: link.Name, + SourceNode: &topopb.Node{ + Id: link.SourceNode.ID.String(), + Name: link.SourceNode.Name, + }, + SourcePort: &topopb.Port{ + Id: link.SourcePort.ID.String(), + Name: link.SourcePort.Name, + Configuration: &topopb.Configuration{ + Ip: link.SourcePort.Configuration.IP.String(), + PrefixLength: link.SourcePort.Configuration.PrefixLength, + }, + }, + TargetNode: &topopb.Node{ + Id: link.TargetNode.ID.String(), + Name: link.TargetNode.Name, + }, + TargetPort: &topopb.Port{ + Id: link.TargetPort.ID.String(), + Name: link.TargetPort.Name, + Configuration: &topopb.Configuration{ + Ip: link.TargetPort.Configuration.IP.String(), + PrefixLength: link.TargetPort.Configuration.PrefixLength, + }, + }, + }) + } + + return &topopb.GetTopologyResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_OK, + Toplogy: topology, + }, nil +} + +// DeleteLink deletes a link +func (t *Topology) DeleteLink(ctx context.Context, request *topopb.DeleteLinkRequest) (*topopb.DeleteLinkResponse, error) { + linkID, err := uuid.Parse(request.Id) + if err != nil { + return &topopb.DeleteLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_ERROR, + }, err + } + + foundLink, err := t.topologyService.Get(query.Query{ID: linkID}) + if err != nil { + return &topopb.DeleteLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_ERROR, + }, err + } + + err = t.topologyService.DeleteLink(foundLink) + if err != nil { + return &topopb.DeleteLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_ERROR, + }, err + } + + return &topopb.DeleteLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: topopb.Status_STATUS_OK, + }, nil +} + +func (t *Topology) ensureNodeAndPortExists(incomingNode *topopb.Node, incomingPort *topopb.Port) (nodes.Node, ports.Port, error) { + node, err := t.nodeService.EnsureExists( + nodes.Node{ + ID: getExistingOrCreateNilUUIDFromString(incomingNode.Id), + Name: incomingNode.Name, + }, + ) + if err != nil { + return node, ports.Port{}, status.Errorf(codes.Aborted, "%v", err) + } + + portConf, err := configuration.New(incomingPort.Configuration.Ip, incomingPort.Configuration.PrefixLength) + if err != nil { + return node, ports.Port{}, status.Errorf(codes.Aborted, "%v", err) + } + port, err := t.portService.EnsureExists( + ports.Port{ + ID: getExistingOrCreateNilUUIDFromString(incomingPort.Id), + Name: incomingPort.Name, + Configuration: portConf, + }, + ) + if err != nil { + return nodes.Node{}, port, status.Errorf(codes.Aborted, "%v", err) + } + + return node, port, nil +} + +func getExistingOrCreateNilUUIDFromString(id string) uuid.UUID { + if len(id) == 0 { + return uuid.Nil + } + + parsedID, err := uuid.Parse(id) + if err != nil { + return uuid.Nil + } + + return parsedID +} diff --git a/controller/northbound/server/topology_test.go b/controller/northbound/server/topology_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7f447baf884101e040f892f5e836eb52c86ad590 --- /dev/null +++ b/controller/northbound/server/topology_test.go @@ -0,0 +1,409 @@ +package server + +import ( + "context" + "reflect" + "testing" + + apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports/configuration" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + + "github.com/google/uuid" +) + +func getTestNodeService() nodes.Service { + eventService := eventservice.NewMockEventService() + + nodeStore := store.NewGenericStore[nodes.Node]() + nodeService := nodes.NewNodeService(nodeStore, eventService) + + return nodeService +} + +func getTestPortService() ports.Service { + eventService := eventservice.NewMockEventService() + + portStore := store.NewGenericStore[ports.Port]() + portService := ports.NewPortService(portStore, eventService) + + return portService +} + +func getTestTopologyService() topology.Service { + eventService := eventservice.NewMockEventService() + + nodeStore := store.NewGenericStore[nodes.Node]() + nodeService := nodes.NewNodeService(nodeStore, eventService) + + portStore := store.NewGenericStore[ports.Port]() + portService := ports.NewPortService(portStore, eventService) + + linkStore := store.NewGenericStore[links.Link]() + topologyService := topology.NewTopologyService(linkStore, nodeService, portService, eventService) + + return topologyService +} + +func getTestTopologyServer( + t *testing.T, + nodesToAdd []nodes.Node, + portsToAdd []ports.Port, + linksToAdd []links.Link, +) *Topology { + eventService := eventservice.NewMockEventService() + + nodeStore := getTestStoreWithNodes(t, nodesToAdd) + nodeService := nodes.NewNodeService(nodeStore, eventService) + + portStore := getTestStoreWithPorts(t, portsToAdd) + portService := ports.NewPortService(portStore, eventService) + + linkStore := getTestStoreWithLinks(t, linksToAdd) + topologyService := topology.NewTopologyService(linkStore, nodeService, portService, eventService) + + s := NewTopologyServer(topologyService, nodeService, portService) + + return s +} + +func getTestSourceNode() nodes.Node { + return nodes.Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Source-Node", + } +} + +func getTestTargetNode() nodes.Node { + return nodes.Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7612"), + Name: "Test-Target-Node", + } +} + +func getTestPortIPConfiguration() configuration.IPConfig { + config, _ := configuration.New("10.13.37.0", 24) + + return config +} + +func getTestSourcePort() ports.Port { + return ports.Port{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fce"), + Name: "Test-Source-Port", + Configuration: getTestPortIPConfiguration(), + } +} + +func getTestTargetPort() ports.Port { + return ports.Port{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"), + Name: "Test-Target-Port", + Configuration: getTestPortIPConfiguration(), + } +} + +func getTestLinkInternal() links.Link { + return links.Link{ + ID: uuid.MustParse("5eb474f1-428e-4503-ba68-dcf9bef53467"), + Name: "Test-Link", + SourceNode: getTestSourceNode(), + TargetNode: getTestTargetNode(), + SourcePort: getTestSourcePort(), + TargetPort: getTestTargetPort(), + } +} + +func getTestStoreWithLinks(t *testing.T, nodes []links.Link) topology.Store { + store := store.NewGenericStore[links.Link]() + + for _, node := range nodes { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func getTestStoreWithNodes(t *testing.T, nodesToAdd []nodes.Node) nodes.Store { + store := store.NewGenericStore[nodes.Node]() + + for _, node := range nodesToAdd { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func getTestStoreWithPorts(t *testing.T, portsToAdd []ports.Port) ports.Store { + store := store.NewGenericStore[ports.Port]() + + for _, port := range portsToAdd { + err := store.Add(port) + if err != nil { + t.Fatalf("failed to prepare test store while adding port: %v", err) + } + } + + return store +} + +func getTestLink() *apb.Link { + return &apb.Link{ + Name: "test-link", + SourceNode: &apb.Node{ + Name: "test-source-node", + }, + SourcePort: &apb.Port{ + Name: "test-source-port", + Configuration: &apb.Configuration{ + Ip: "10.13.37.0", + PrefixLength: 24, + }, + }, + TargetNode: &apb.Node{ + Name: "test-target-node", + }, + TargetPort: &apb.Port{ + Name: "test-target-port", + Configuration: &apb.Configuration{ + Ip: "10.13.38.0", + PrefixLength: 24, + }, + }, + } +} +func TestNewTopologyServer(t *testing.T) { + type args struct { + service topology.Service + nodeService nodes.Service + portService ports.Service + } + tests := []struct { + name string + args args + want *Topology + }{ + { + name: "should create a new topology service", + args: args{ + service: getTestTopologyService(), + nodeService: getTestNodeService(), + portService: getTestPortService(), + }, + want: &Topology{ + topologyService: getTestTopologyService(), + nodeService: getTestNodeService(), + portService: getTestPortService(), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewTopologyServer(tt.args.service, tt.args.nodeService, tt.args.portService); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewTopologyServer() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopology_AddLink(t *testing.T) { + type fields struct { + ports []ports.Port + nodes []nodes.Node + links []links.Link + } + type args struct { + ctx context.Context + request *apb.AddLinkRequest + } + tests := []struct { + name string + fields fields + args args + want *apb.AddLinkResponse + wantErr bool + }{ + { + name: "should add a new link", + fields: fields{ + ports: []ports.Port{}, + nodes: []nodes.Node{}, + links: []links.Link{}, + }, + args: args{ + ctx: context.TODO(), + request: &apb.AddLinkRequest{ + Link: getTestLink(), + }, + }, + want: &apb.AddLinkResponse{ + Status: apb.Status_STATUS_OK, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links) + got, err := tr.AddLink(tt.args.ctx, tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Topology.AddLink() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Status, tt.want.Status) { + t.Errorf("Topology.AddLink() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopology_GetTopology(t *testing.T) { + type fields struct { + ports []ports.Port + nodes []nodes.Node + links []links.Link + } + type args struct { + ctx context.Context + request *apb.GetTopologyRequest + } + tests := []struct { + name string + fields fields + args args + want *apb.GetTopologyResponse + wantErr bool + }{ + { + name: "should add a new link", + fields: fields{ + ports: []ports.Port{getTestSourcePort(), getTestTargetPort()}, + nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()}, + links: []links.Link{getTestLinkInternal()}, + }, + args: args{ + ctx: context.TODO(), + request: &apb.GetTopologyRequest{}, + }, + want: &apb.GetTopologyResponse{ + Status: apb.Status_STATUS_OK, + Toplogy: &apb.Topology{ + Links: []*apb.Link{ + { + Id: "5eb474f1-428e-4503-ba68-dcf9bef53467", + Name: "Test-Link", + SourceNode: &apb.Node{ + Id: "44fb4aa4-c53c-4cf9-a081-5aabc61c7610", + Name: "Test-Source-Node", + }, + SourcePort: &apb.Port{ + Id: "1fa479e7-d393-4d45-822d-485cc1f05fce", + Name: "Test-Source-Port", + Configuration: &apb.Configuration{ + Ip: "10.13.37.0", + PrefixLength: 24, + }, + }, + TargetNode: &apb.Node{ + Id: "44fb4aa4-c53c-4cf9-a081-5aabc61c7612", + Name: "Test-Target-Node", + }, + TargetPort: &apb.Port{ + Id: "1fa479e7-d393-4d45-822d-485cc1f05fc2", + Name: "Test-Target-Port", + Configuration: &apb.Configuration{ + Ip: "10.13.37.0", + PrefixLength: 24, + }, + }, + }, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links) + got, err := tr.GetTopology(tt.args.ctx, tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Toplogy, tt.want.Toplogy) { + t.Errorf("Topology.GetTopology() = %v, want %v", got.Toplogy, tt.want.Toplogy) + } + }) + } +} + +func TestTopology_DeleteLink(t *testing.T) { + type fields struct { + ports []ports.Port + nodes []nodes.Node + links []links.Link + } + type args struct { + ctx context.Context + request *apb.DeleteLinkRequest + } + tests := []struct { + name string + fields fields + args args + want *apb.DeleteLinkResponse + wantErr bool + }{ + { + name: "should add a new link", + fields: fields{ + ports: []ports.Port{getTestSourcePort(), getTestTargetPort()}, + nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()}, + links: []links.Link{getTestLinkInternal()}, + }, + args: args{ + ctx: context.TODO(), + request: &apb.DeleteLinkRequest{ + Id: "5eb474f1-428e-4503-ba68-dcf9bef53467", + }, + }, + want: &apb.DeleteLinkResponse{ + Status: apb.Status_STATUS_OK, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links) + got, err := tr.DeleteLink(tt.args.ctx, tt.args.request) + if (err != nil) != tt.wantErr { + t.Errorf("Topology.DeleteLink() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Status, tt.want.Status) { + t.Errorf("Topology.DeleteLink() = %v, want %v", got, tt.want) + } + gotAfterDelete, err := tr.GetTopology(tt.args.ctx, &apb.GetTopologyRequest{}) + if (err != nil) != tt.wantErr { + t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotAfterDelete.Toplogy, &apb.Topology{}) { + t.Errorf("Topology.GetTopology() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/nucleus/database/mongo-connection.go b/controller/nucleus/database/mongo-connection.go index 1db8205ae25d0b4d72737dd0f8c12d2bb3718e4c..c021965088d2ff365772e84a5cd701643fa9fa29 100644 --- a/controller/nucleus/database/mongo-connection.go +++ b/controller/nucleus/database/mongo-connection.go @@ -15,7 +15,6 @@ const ( connectTimeout = 5 // DatabaseName is the name of the mongoDB database used. DatabaseName = "gosdn" - //mongoConnection = "mongodb://root:example@localhost" ) // GetMongoConnection Retrieves a client to the MongoDB diff --git a/controller/topology/links/link.go b/controller/topology/links/link.go new file mode 100644 index 0000000000000000000000000000000000000000..b90faf44acb9f65ab9fe0acef0fee4a4d937da18 --- /dev/null +++ b/controller/topology/links/link.go @@ -0,0 +1,22 @@ +package links + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "github.com/google/uuid" +) + +// Link is a representation of a physical or virtual link between two nodes and their ports +type Link struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name,omitempty"` + SourceNode nodes.Node `bson:"source_node,omitempty"` + TargetNode nodes.Node `bson:"target_node,omitempty"` + SourcePort ports.Port `bson:"source_port,omitempty"` + TargetPort ports.Port `bson:"target_port,omitempty"` +} + +// GetID returns the id of a link +func (l Link) GetID() uuid.UUID { + return l.ID +} diff --git a/controller/topology/nodes/node.go b/controller/topology/nodes/node.go new file mode 100644 index 0000000000000000000000000000000000000000..02ec4ac680cd69877ab1e0fb192a12b40444f454 --- /dev/null +++ b/controller/topology/nodes/node.go @@ -0,0 +1,16 @@ +package nodes + +import ( + "github.com/google/uuid" +) + +// Node is a representation of a network element +type Node struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name"` +} + +// GetID returns the id of a node +func (n Node) GetID() uuid.UUID { + return n.ID +} diff --git a/controller/topology/nodes/nodeService.go b/controller/topology/nodes/nodeService.go new file mode 100644 index 0000000000000000000000000000000000000000..9e7f73a1db6dc65f9348d27c902608308d34647c --- /dev/null +++ b/controller/topology/nodes/nodeService.go @@ -0,0 +1,108 @@ +package nodes + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/event" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + + "github.com/google/uuid" +) + +const ( + // NodeEventTopic is the used topic for node related entity changes + NodeEventTopic = "node" +) + +// Service defines a interface for a NodeService +type Service interface { + EnsureExists(Node) (Node, error) + Update(Node) error + Delete(Node) error + Get(query.Query) (Node, error) + GetAll() ([]Node, error) +} + +// NodeService is a NodeService +type NodeService struct { + store Store + eventService eventInterfaces.Service +} + +// NewNodeService creates a NodeService +func NewNodeService(store Store, eventService eventInterfaces.Service) Service { + return &NodeService{ + store: store, + eventService: eventService, + } +} + +// EnsureExists either creates a new node or returns an already existing node +func (n *NodeService) EnsureExists(node Node) (Node, error) { + if node.ID == uuid.Nil { + node.ID = uuid.New() + return n.createNode(node) + } + + // Check if node with vaild UUID exists + existingNode, err := n.Get(query.Query{ID: node.ID}) + if err != nil { + // Create new node with that UUID + return n.createNode(node) + } + + return existingNode, nil +} + +func (n *NodeService) createNode(node Node) (Node, error) { + err := n.store.Add(node) + if err != nil { + return node, err + } + + n.eventService.PublishEvent(NodeEventTopic, event.NewAddEvent(node.ID)) + + return node, nil +} + +// Update updates an existing node +func (n *NodeService) Update(node Node) error { + err := n.store.Update(node) + if err != nil { + return err + } + + n.eventService.PublishEvent(NodeEventTopic, event.NewUpdateEvent(node.ID)) + + return nil +} + +// Delete deletes a node +func (n *NodeService) Delete(node Node) error { + err := n.store.Delete(node) + if err != nil { + return err + } + + n.eventService.PublishEvent(NodeEventTopic, event.NewDeleteEvent(node.ID)) + + return nil +} + +// Get gets a node +func (n *NodeService) Get(query query.Query) (Node, error) { + node, err := n.store.Get(query) + if err != nil { + return node, err + } + + return node, nil +} + +// GetAll gets all existing nodes +func (n *NodeService) GetAll() ([]Node, error) { + nodes, err := n.store.GetAll() + if err != nil { + return nodes, err + } + return nodes, nil +} diff --git a/controller/topology/nodes/nodeService_test.go b/controller/topology/nodes/nodeService_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a64a1e240cddd74efb9bdaefc9b69ae6c9ded7a4 --- /dev/null +++ b/controller/topology/nodes/nodeService_test.go @@ -0,0 +1,329 @@ +package nodes + +import ( + "reflect" + "testing" + + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + + "github.com/google/uuid" +) + +func getTestNode() Node { + return Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Node", + } +} + +func getEmptyNode() Node { + return Node{ + ID: uuid.Nil, + Name: "", + } +} + +func getTestStoreWithNodes(t *testing.T, nodes []Node) Store { + store := store.NewGenericStore[Node]() + + for _, node := range nodes { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func TestNewNodeService(t *testing.T) { + type args struct { + store Store + eventService event.Service + } + tests := []struct { + name string + args args + want Service + }{ + { + name: "should create a new node service", + args: args{ + store: getTestStoreWithNodes(t, []Node{}), + eventService: eventservice.NewMockEventService(), + }, + want: NewNodeService(getTestStoreWithNodes(t, []Node{}), eventservice.NewMockEventService()), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewNodeService(tt.args.store, tt.args.eventService); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNodeService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_EnsureExists(t *testing.T) { + type fields struct { + store Store + eventService event.Service + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + { + name: "should create if node with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Node](), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + node: getTestNode(), + }, + want: getTestNode(), + wantErr: false, + }, + { + name: "should return node that is in the store", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + node: getTestNode(), + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.EnsureExists(tt.args.node) + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.EnsureExists() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.EnsureExists() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_Update(t *testing.T) { + type fields struct { + store Store + eventService event.Service + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + { + name: "should update an existing node", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + node: getTestNode(), + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + if err := p.Update(tt.args.node); (err != nil) != tt.wantErr { + t.Errorf("NodeService.Update() error = %v, wantErr %v", err, tt.wantErr) + } + + updatedNode, err := p.Get(query.Query(tt.args.node)) + if err != nil { + t.Errorf("NodeService.Get() failed %v", err) + } + + if !reflect.DeepEqual(updatedNode, tt.want) { + t.Errorf("Got updated node = %v, want %v", updatedNode, tt.want) + } + }) + } +} + +func TestNodeService_Delete(t *testing.T) { + type fields struct { + store Store + eventService event.Service + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should delete an existing node", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + node: getTestNode(), + }, + wantErr: false, + }, + { + name: "should fail if a node does not exists", + fields: fields{ + store: store.NewGenericStore[Node](), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + node: getTestNode(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + if err := p.Delete(tt.args.node); (err != nil) != tt.wantErr { + t.Errorf("NodeService.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNodeService_Get(t *testing.T) { + type fields struct { + store Store + eventService event.Service + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + { + name: "should error if node with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Node](), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + query: query.Query{ + ID: getTestNode().ID, + Name: getTestNode().Name, + }, + }, + want: getEmptyNode(), + wantErr: true, + }, + { + name: "should return node that is in the store", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + query: query.Query{ + ID: getTestNode().ID, + Name: getTestNode().Name, + }, + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_GetAll(t *testing.T) { + type fields struct { + store Store + eventService event.Service + } + tests := []struct { + name string + fields fields + want []Node + wantErr bool + }{ + { + name: "should get all stored nodes", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + eventService: eventservice.NewMockEventService(), + }, + want: []Node{getTestNode()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/topology/nodes/nodeStore.go b/controller/topology/nodes/nodeStore.go new file mode 100644 index 0000000000000000000000000000000000000000..a4f6f3123d7aab5e27f1a880e00d0a8c77073ffb --- /dev/null +++ b/controller/topology/nodes/nodeStore.go @@ -0,0 +1,187 @@ +package nodes + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a NodeStore interface +type Store interface { + Add(Node) error + Update(Node) error + Delete(Node) error + Get(query.Query) (Node, error) + GetAll() ([]Node, error) +} + +// DatabaseNodeStore is a database store for nodes +type DatabaseNodeStore struct { + storeName string +} + +// NewDatabaseNodeStore returns a NodeStore +func NewDatabaseNodeStore() Store { + return &DatabaseNodeStore{ + storeName: fmt.Sprintf("node-store.json"), + } +} + +// Get takes a nodes's UUID or name and returns the nodes. +func (s *DatabaseNodeStore) Get(query query.Query) (Node, error) { + var loadedNode Node + + if query.ID.String() != "" { + loadedNode, err := s.getByID(query.ID) + if err != nil { + return loadedNode, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedNode, nil + } + + loadedNode, err := s.getByName(query.Name) + if err != nil { + return loadedNode, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedNode, nil +} + +func (s *DatabaseNodeStore) getByID(idOfNode uuid.UUID) (Node, error) { + var loadedNode Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + idAsByteArray, _ := idOfNode.MarshalBinary() + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idAsByteArray}}) + if result == nil { + return loadedNode, errors.ErrCouldNotFind{ID: idOfNode} + } + + err := result.Decode(&loadedNode) + if err != nil { + return loadedNode, errors.ErrCouldNotMarshall{Identifier: idOfNode, Type: loadedNode, Err: err} + } + + return loadedNode, nil +} + +func (s *DatabaseNodeStore) getByName(nameOfNode string) (Node, error) { + var loadedNode Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfNode}}) + if result == nil { + return loadedNode, errors.ErrCouldNotFind{Name: nameOfNode} + } + + err := result.Decode(&loadedNode) + if err != nil { + return loadedNode, errors.ErrCouldNotMarshall{Identifier: nameOfNode, Type: loadedNode, Err: err} + } + + return loadedNode, nil +} + +// GetAll returns all stored nodes. +func (s *DatabaseNodeStore) GetAll() ([]Node, error) { + var loadedNode []Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return []Node{}, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedNode) + if err != nil { + return loadedNode, errors.ErrCouldNotMarshall{Type: loadedNode, Err: err} + } + + return loadedNode, nil +} + +// Add adds a node to the node store. +func (s *DatabaseNodeStore) Add(node Node) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, node) + if err != nil { + return errors.ErrCouldNotCreate{Identifier: node.ID, Type: node, Err: err} + } + + return nil +} + +// Update updates a existing node. +func (s *DatabaseNodeStore) Update(node Node) error { + var updatedLoadedNodes Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: node}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": node.ID.String()}, update, &opt). + Decode(&updatedLoadedNodes) + if err != nil { + return errors.ErrCouldNotUpdate{Identifier: node.ID, Type: node, Err: err} + } + + return nil +} + +// Delete deletes a node from the node store. +func (s *DatabaseNodeStore) Delete(node Node) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: node.ID.String()}}) + if err != nil { + return err + } + + return nil +} diff --git a/controller/topology/ports/configuration/configuration.go b/controller/topology/ports/configuration/configuration.go new file mode 100644 index 0000000000000000000000000000000000000000..cc22dd09212a4ea8c9c751f372a1b7eb27a96715 --- /dev/null +++ b/controller/topology/ports/configuration/configuration.go @@ -0,0 +1,26 @@ +package configuration + +import ( + "fmt" + "net" +) + +// IPConfig represents an interface configuration for a cEOS instance +type IPConfig struct { + IP net.IP `bson:"ip,omitempty"` + PrefixLength int64 `bson:"prefix_length,omitempty"` +} + +// New creates a new IPConfig +func New(ip string, prefixLength int64) (IPConfig, error) { + newIPConfig := IPConfig{} + parsedIP := net.ParseIP(ip) + if parsedIP == nil { + return newIPConfig, fmt.Errorf("%s can not be parsed to an IP", ip) + } + + newIPConfig.IP = parsedIP + newIPConfig.PrefixLength = prefixLength + + return newIPConfig, nil +} diff --git a/controller/topology/ports/port.go b/controller/topology/ports/port.go new file mode 100644 index 0000000000000000000000000000000000000000..e7456af6f65a9857b78b34262a4bd524a4bc7247 --- /dev/null +++ b/controller/topology/ports/port.go @@ -0,0 +1,18 @@ +package ports + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports/configuration" + "github.com/google/uuid" +) + +// Port is a representation of physical port on a network element +type Port struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name,omitempty"` + Configuration configuration.IPConfig `bson:"configuration,omitempty"` +} + +// GetID returns the id of a port +func (p Port) GetID() uuid.UUID { + return p.ID +} diff --git a/controller/topology/ports/portService.go b/controller/topology/ports/portService.go new file mode 100644 index 0000000000000000000000000000000000000000..c092d6ada904486631eb111a9dd6b95663c1bd53 --- /dev/null +++ b/controller/topology/ports/portService.go @@ -0,0 +1,106 @@ +package ports + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/event" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + + "github.com/google/uuid" +) + +const ( + // PortEventTopic is the used topic for port related entity changes + PortEventTopic = "port" +) + +// Service defines an interface for a PortService +type Service interface { + EnsureExists(Port) (Port, error) + Update(Port) error + Delete(Port) error + Get(query.Query) (Port, error) + GetAll() ([]Port, error) +} + +// PortService is a service for ports +type PortService struct { + store Store + eventService eventInterfaces.Service +} + +// NewPortService creates a new PortService +func NewPortService(store Store, eventService eventInterfaces.Service) Service { + return &PortService{ + store: store, + eventService: eventService, + } +} + +// EnsureExists either creates a new port or returns an already existing port +func (p *PortService) EnsureExists(port Port) (Port, error) { + if port.ID == uuid.Nil { + port.ID = uuid.New() + return p.createPort(port) + } + + existingPort, err := p.Get(query.Query{ID: port.ID}) + if err != nil { + return p.createPort(port) + } + + return existingPort, nil +} + +func (p *PortService) createPort(port Port) (Port, error) { + err := p.store.Add(port) + if err != nil { + return port, err + } + + p.eventService.PublishEvent(PortEventTopic, event.NewAddEvent(port.ID)) + + return port, nil +} + +// Update updates an existing port +func (p *PortService) Update(port Port) error { + err := p.store.Update(port) + if err != nil { + return err + } + + p.eventService.PublishEvent(PortEventTopic, event.NewUpdateEvent(port.ID)) + + return nil +} + +// Delete deletes a port +func (p *PortService) Delete(port Port) error { + err := p.store.Delete(port) + if err != nil { + return err + } + + p.eventService.PublishEvent(PortEventTopic, event.NewDeleteEvent(port.ID)) + + return nil +} + +// Get gets a port +func (p *PortService) Get(query query.Query) (Port, error) { + port, err := p.store.Get(query) + if err != nil { + return port, err + } + + return port, nil +} + +// GetAll gets all existing ports +func (p *PortService) GetAll() ([]Port, error) { + nodes, err := p.store.GetAll() + if err != nil { + return nodes, err + } + return nodes, nil +} diff --git a/controller/topology/ports/portService_test.go b/controller/topology/ports/portService_test.go new file mode 100644 index 0000000000000000000000000000000000000000..951f2730ee43a6f8e5ab9180bb43c8dbab28f425 --- /dev/null +++ b/controller/topology/ports/portService_test.go @@ -0,0 +1,327 @@ +package ports + +import ( + "reflect" + "testing" + + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + + "github.com/google/uuid" +) + +func getTestPort() Port { + return Port{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Port", + } +} + +func getEmptyPort() Port { + return Port{ + ID: uuid.Nil, + Name: "", + } +} + +func getTestStoreWithPorts(t *testing.T, ports []Port) Store { + store := store.NewGenericStore[Port]() + + for _, port := range ports { + err := store.Add(port) + if err != nil { + t.Fatalf("failed to prepare test store while adding port: %v", err) + } + } + + return store +} + +func TestNewPortService(t *testing.T) { + type args struct { + store Store + eventService eventInterfaces.Service + } + tests := []struct { + name string + args args + want Service + }{ + { + name: "should create a new port service", + args: args{ + store: getTestStoreWithPorts(t, []Port{}), + eventService: eventservice.NewMockEventService(), + }, + want: NewPortService(getTestStoreWithPorts(t, []Port{}), eventservice.NewMockEventService()), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewPortService(tt.args.store, tt.args.eventService); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewPortService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPortService_EnsureExists(t *testing.T) { + type fields struct { + store Store + eventService eventInterfaces.Service + } + type args struct { + port Port + } + tests := []struct { + name string + fields fields + args args + want Port + wantErr bool + }{ + { + name: "should not error if port with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Port](), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + port: getTestPort(), + }, + want: getTestPort(), + wantErr: false, + }, + { + name: "should return port that is in the store", + fields: fields{ + store: getTestStoreWithPorts(t, []Port{getTestPort()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + port: getTestPort(), + }, + want: getTestPort(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PortService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.EnsureExists(tt.args.port) + if (err != nil) != tt.wantErr { + t.Errorf("PortService.EnsureExists() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("PortService.EnsureExists() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPortService_Update(t *testing.T) { + type fields struct { + store Store + eventService eventInterfaces.Service + } + type args struct { + port Port + } + tests := []struct { + name string + fields fields + args args + want Port + wantErr bool + }{ + { + name: "should update an existing port", + fields: fields{ + store: getTestStoreWithPorts(t, []Port{getTestPort()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + port: getTestPort(), + }, + want: getTestPort(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PortService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + if err := p.Update(tt.args.port); (err != nil) != tt.wantErr { + t.Errorf("PortService.Update() error = %v, wantErr %v", err, tt.wantErr) + } + + updatedPort, err := p.Get(query.Query{ID: tt.args.port.ID}) + if err != nil { + t.Errorf("PortService.Get() failed %v", err) + } + + if !reflect.DeepEqual(updatedPort, tt.want) { + t.Errorf("Got updated port = %v, want %v", updatedPort, tt.want) + } + }) + } +} + +func TestPortService_Delete(t *testing.T) { + type fields struct { + store Store + eventService eventInterfaces.Service + } + type args struct { + port Port + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should delete an existing port", + fields: fields{ + store: getTestStoreWithPorts(t, []Port{getTestPort()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + port: getTestPort(), + }, + wantErr: false, + }, + { + name: "should fail if a port does not exists", + fields: fields{ + store: store.NewGenericStore[Port](), + }, + args: args{ + port: getTestPort(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PortService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + if err := p.Delete(tt.args.port); (err != nil) != tt.wantErr { + t.Errorf("PortService.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPortService_Get(t *testing.T) { + type fields struct { + store Store + eventService eventInterfaces.Service + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want Port + wantErr bool + }{ + { + name: "should error if port with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Port](), + }, + args: args{ + query: query.Query{ + ID: getTestPort().ID, + Name: getTestPort().Name, + }, + }, + want: getEmptyPort(), + wantErr: true, + }, + { + name: "should return port that is in the store", + fields: fields{ + store: getTestStoreWithPorts(t, []Port{getTestPort()}), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + query: query.Query{ + ID: getTestPort().ID, + Name: getTestPort().Name, + }, + }, + want: getTestPort(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PortService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("PortService.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("PortService.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPortService_GetAll(t *testing.T) { + type fields struct { + store Store + eventService eventInterfaces.Service + } + tests := []struct { + name string + fields fields + want []Port + wantErr bool + }{ + { + name: "should get all stored ports", + fields: fields{ + store: getTestStoreWithPorts(t, []Port{getTestPort()}), + eventService: eventservice.NewMockEventService(), + }, + want: []Port{getTestPort()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PortService{ + store: tt.fields.store, + eventService: tt.fields.eventService, + } + got, err := p.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("PortService.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("PortService.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/topology/ports/portStore.go b/controller/topology/ports/portStore.go new file mode 100644 index 0000000000000000000000000000000000000000..7c7d0f0cd21e4ea07ea5c754cba6512f776a30de --- /dev/null +++ b/controller/topology/ports/portStore.go @@ -0,0 +1,188 @@ +package ports + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a PortStore interface +type Store interface { + Add(Port) error + Update(Port) error + Delete(Port) error + Get(query.Query) (Port, error) + GetAll() ([]Port, error) +} + +// DatabasePortStore is a database store for ports +type DatabasePortStore struct { + storeName string +} + +// NewDatabasePortStore returns a PortStore +func NewDatabasePortStore() Store { + return &DatabasePortStore{ + storeName: fmt.Sprintf("port-store.json"), + } +} + +// Get takes a Ports's UUID or name and returns the port. +func (s *DatabasePortStore) Get(query query.Query) (Port, error) { + var loadedPort Port + + if query.ID.String() != "" { + loadedPort, err := s.getByID(query.ID) + if err != nil { + return loadedPort, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedPort, nil + } + + loadedPort, err := s.getByName(query.Name) + if err != nil { + return loadedPort, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedPort, nil +} + +func (s *DatabasePortStore) getByID(idOfPort uuid.UUID) (Port, error) { + var loadedPort Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + idAsByteArray, _ := idOfPort.MarshalBinary() + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idAsByteArray}}) + if result == nil { + return loadedPort, errors.ErrCouldNotFind{ID: idOfPort} + } + + err := result.Decode(&loadedPort) + if err != nil { + return loadedPort, errors.ErrCouldNotMarshall{Identifier: idOfPort, Type: loadedPort, Err: err} + } + + return loadedPort, nil +} + +func (s *DatabasePortStore) getByName(nameOfPort string) (Port, error) { + var loadedPort Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfPort}}) + if result == nil { + return loadedPort, errors.ErrCouldNotFind{Name: nameOfPort} + } + + err := result.Decode(&loadedPort) + if err != nil { + return loadedPort, errors.ErrCouldNotMarshall{Identifier: nameOfPort, Type: loadedPort, Err: err} + } + + return loadedPort, nil +} + +// GetAll returns all stored ports. +func (s *DatabasePortStore) GetAll() ([]Port, error) { + var loadedPort []Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return []Port{}, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedPort) + if err != nil { + return loadedPort, errors.ErrCouldNotMarshall{Type: loadedPort, Err: err} + } + + return loadedPort, nil +} + +// Add adds a port to the port store. +func (s *DatabasePortStore) Add(port Port) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, port) + if err != nil { + return errors.ErrCouldNotCreate{Identifier: port.ID, Type: port, Err: err} + } + + return nil +} + +// Update updates a existing port. +func (s *DatabasePortStore) Update(port Port) error { + var updatedLoadedDevice device.LoadedDevice + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: port}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": port.ID.String()}, update, &opt). + Decode(&updatedLoadedDevice) + if err != nil { + return errors.ErrCouldNotUpdate{Identifier: port.ID, Type: port, Err: err} + } + + return nil +} + +// Delete deletes a port from the port store. +func (s *DatabasePortStore) Delete(port Port) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: port.ID.String()}}) + if err != nil { + return err + } + + return nil +} diff --git a/controller/topology/routing-tables/route.go b/controller/topology/routing-tables/route.go new file mode 100644 index 0000000000000000000000000000000000000000..f25292501e0264a1f8e8aa9af13e844686c4c90a --- /dev/null +++ b/controller/topology/routing-tables/route.go @@ -0,0 +1,13 @@ +package routingtables + +import ( + "github.com/google/uuid" +) + +// Route is a routing table entry on a device +type Route struct { + ID uuid.UUID `bson:"_id"` + TargetIPRange string `bson:"target_ip_range"` + PortID uuid.UUID `bson:"port_id"` + Metric string `bson:"metric"` +} diff --git a/controller/topology/routing-tables/routingTable.go b/controller/topology/routing-tables/routingTable.go new file mode 100644 index 0000000000000000000000000000000000000000..833c91df3931ffc80e39b2e0d5717ecd05a7b66d --- /dev/null +++ b/controller/topology/routing-tables/routingTable.go @@ -0,0 +1,15 @@ +package routingtables + +import "github.com/google/uuid" + +// RoutingTable is the routing table of a device +type RoutingTable struct { + ID uuid.UUID `bson:"_id"` + NodeID uuid.UUID `bson:"node_id"` + Routes Route `bson:"routes"` +} + +// GetID returns the id of a routingtable +func (r RoutingTable) GetID() uuid.UUID { + return r.ID +} diff --git a/controller/topology/routing-tables/routingTableService.go b/controller/topology/routing-tables/routingTableService.go new file mode 100644 index 0000000000000000000000000000000000000000..b5823217301eb5d92697c0154940611e2423950a --- /dev/null +++ b/controller/topology/routing-tables/routingTableService.go @@ -0,0 +1,100 @@ +package routingtables + +import ( + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "github.com/google/uuid" +) + +// Service defines a interface for a RoutingTableService +type Service interface { + EnsureExists(RoutingTable) (RoutingTable, error) + Update(RoutingTable) error + Delete(RoutingTable) error + Get(query.Query) (RoutingTable, error) + GetAll() ([]RoutingTable, error) +} + +// RoutingTableService is a RoutingTableService +type RoutingTableService struct { + store Store + nodeService nodes.Service + portService ports.Service +} + +// NewRoutingTableService creates a RoutingTableService +func NewRoutingTableService( + store Store, + nodeService nodes.Service, + portService ports.Service, +) Service { + return &RoutingTableService{ + store: store, + nodeService: nodeService, + portService: portService, + } +} + +// EnsureExists either creates a new routingTable or returns an already existing routingTable +func (r *RoutingTableService) EnsureExists(routingTable RoutingTable) (RoutingTable, error) { + if routingTable.ID == uuid.Nil { + routingTable.ID = uuid.New() + return r.createRoutingTable(routingTable) + } + + existingRoutingTable, err := r.Get(query.Query{ID: routingTable.ID}) + if err != nil { + return r.createRoutingTable(routingTable) + } + + return existingRoutingTable, nil +} + +func (r *RoutingTableService) createRoutingTable(routingTable RoutingTable) (RoutingTable, error) { + err := r.store.Add(routingTable) + if err != nil { + return routingTable, err + } + + return routingTable, err +} + +// Update updates an existing routingTable +func (r *RoutingTableService) Update(routingTable RoutingTable) error { + err := r.store.Update(routingTable) + if err != nil { + return err + } + + return nil +} + +// Delete deletes a routingTable +func (r *RoutingTableService) Delete(routingTable RoutingTable) error { + err := r.store.Delete(routingTable) + if err != nil { + return err + } + + return nil +} + +// Get gets a routingTable +func (r *RoutingTableService) Get(query query.Query) (RoutingTable, error) { + routingTable, err := r.store.Get(query) + if err != nil { + return routingTable, err + } + + return routingTable, nil +} + +// GetAll gets all existing routingTables +func (r *RoutingTableService) GetAll() ([]RoutingTable, error) { + nodes, err := r.store.GetAll() + if err != nil { + return nodes, err + } + return nodes, nil +} diff --git a/controller/topology/routing-tables/routingTableService_test.go b/controller/topology/routing-tables/routingTableService_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e2fc764524c012a12496c727f43c1c5415014cf9 --- /dev/null +++ b/controller/topology/routing-tables/routingTableService_test.go @@ -0,0 +1,409 @@ +package routingtables + +import ( + "reflect" + "testing" + + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + + "github.com/google/uuid" +) + +func getTestNode() nodes.Node { + return nodes.Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Source-Node", + } +} + +func getTestPort() ports.Port { + return ports.Port{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"), + Name: "Test-Target-Port", + } +} + +func getTestRoute() Route { + return Route{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05f12"), + TargetIPRange: "10.13.37.0/24", + PortID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"), + Metric: "1", + } +} + +func getTestRoutingTable() RoutingTable { + return RoutingTable{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05f34"), + NodeID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Routes: getTestRoute(), + } +} + +func getTestStoreWithRoutingTables(t *testing.T, routingTables []RoutingTable) Store { + store := store.NewGenericStore[RoutingTable]() + + for _, rt := range routingTables { + err := store.Add(rt) + if err != nil { + t.Fatalf("failed to prepare test store while adding routing table: %v", err) + } + } + + return store +} + +func getTestStoreWithNodes(t *testing.T, nodesToAdd []nodes.Node) nodes.Store { + store := store.NewGenericStore[nodes.Node]() + + for _, node := range nodesToAdd { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func getTestStoreWithPorts(t *testing.T, portsToAdd []ports.Port) ports.Store { + store := store.NewGenericStore[ports.Port]() + + for _, port := range portsToAdd { + err := store.Add(port) + if err != nil { + t.Fatalf("failed to prepare test store while adding port: %v", err) + } + } + + return store +} + +func TestNewRoutingTableService(t *testing.T) { + type args struct { + store Store + nodeService nodes.Service + portService ports.Service + } + tests := []struct { + name string + args args + want Service + }{ + { + name: "should create a new topology service", + args: args{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + want: NewRoutingTableService( + getTestStoreWithRoutingTables(t, []RoutingTable{}), + nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + ), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewRoutingTableService(tt.args.store, tt.args.nodeService, tt.args.portService); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNodeService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopologyService_EnsureExists(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + } + type args struct { + routingTable RoutingTable + } + tests := []struct { + name string + fields fields + args args + want RoutingTable + wantErr bool + }{ + { + name: "should add if a routing table that is not in the store", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{}), + nodeService: nodes.NewNodeService( + getTestStoreWithNodes( + t, + []nodes.Node{}, + ), + eventservice.NewMockEventService(), + ), + portService: ports.NewPortService( + getTestStoreWithPorts( + t, + []ports.Port{}, + ), + eventservice.NewMockEventService(), + ), + }, + args: args{ + routingTable: getTestRoutingTable(), + }, + want: getTestRoutingTable(), + wantErr: false, + }, + { + name: "should return routing table that is in the store", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}), + nodeService: nodes.NewNodeService( + getTestStoreWithNodes( + t, + []nodes.Node{getTestNode()}, + ), + eventservice.NewMockEventService(), + ), + portService: ports.NewPortService( + getTestStoreWithPorts( + t, + []ports.Port{getTestPort()}, + ), + eventservice.NewMockEventService(), + ), + }, + args: args{ + routingTable: getTestRoutingTable(), + }, + want: getTestRoutingTable(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &RoutingTableService{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + } + got, err := p.EnsureExists(tt.args.routingTable) + if (err != nil) != tt.wantErr { + t.Errorf("RoutingTableService.EnsureExists() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("RoutingTableService.EnsureExists() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRoutingTableService_Update(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + } + type args struct { + routingTable RoutingTable + } + tests := []struct { + name string + fields fields + args args + want RoutingTable + wantErr bool + }{ + { + name: "should update an existing routing table", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + routingTable: getTestRoutingTable(), + }, + want: getTestRoutingTable(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &RoutingTableService{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + } + if err := p.Update(tt.args.routingTable); (err != nil) != tt.wantErr { + t.Errorf("RoutingTableService.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRoutingTableService_Delete(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + } + type args struct { + routingTable RoutingTable + } + tests := []struct { + name string + fields fields + args args + want RoutingTable + wantErr bool + }{ + { + name: "should delete an existing routing table", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + routingTable: getTestRoutingTable(), + }, + wantErr: false, + }, + { + name: "should fail if a routing table does not exists", + fields: fields{ + store: store.NewGenericStore[RoutingTable](), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + routingTable: getTestRoutingTable(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &RoutingTableService{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + } + if err := p.Delete(tt.args.routingTable); (err != nil) != tt.wantErr { + t.Errorf("RoutingTableService.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRoutingTableService_Get(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want RoutingTable + wantErr bool + }{ + { + name: "should error if routing table with uuid is not in store", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + query: query.Query{ + ID: getTestRoutingTable().ID, + }, + }, + want: RoutingTable{}, + wantErr: true, + }, + { + name: "should return routing table that is in the store", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}), + }, + args: args{ + query: query.Query{ + ID: getTestRoutingTable().ID, + }, + }, + want: getTestRoutingTable(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &RoutingTableService{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + } + got, err := p.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("RoutingTableService.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("RoutingTableService.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRoutingTableService_GetAll(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + } + tests := []struct { + name string + fields fields + want []RoutingTable + wantErr bool + }{ + { + name: "should get all stored routing tables", + fields: fields{ + store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + want: []RoutingTable{getTestRoutingTable()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &RoutingTableService{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + } + got, err := p.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("RoutingTableService.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("RoutingTableService.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/topology/routing-tables/routingTableStore.go b/controller/topology/routing-tables/routingTableStore.go new file mode 100644 index 0000000000000000000000000000000000000000..b3a414575a5b61d392aebf62f81a7104002c4c2c --- /dev/null +++ b/controller/topology/routing-tables/routingTableStore.go @@ -0,0 +1,185 @@ +package routingtables + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a RoutingTable store interface +type Store interface { + Add(RoutingTable) error + Update(RoutingTable) error + Delete(RoutingTable) error + Get(query.Query) (RoutingTable, error) + GetAll() ([]RoutingTable, error) +} + +// DatabaseRoutingTableStore is a database store for routingTables +type DatabaseRoutingTableStore struct { + storeName string +} + +// NewDatabaseRoutingTableStore returns a RoutingTableStore +func NewDatabaseRoutingTableStore() Store { + return &DatabaseRoutingTableStore{ + storeName: fmt.Sprintf("routing-table-store.json"), + } +} + +// Get takes a routing-tables's UUID or name and returns the entries. +func (s *DatabaseRoutingTableStore) Get(query query.Query) (RoutingTable, error) { + var loadedRoutingTable RoutingTable + + if query.ID.String() != "" { + loadedRoutingTable, err := s.getByID(query.ID) + if err != nil { + return loadedRoutingTable, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedRoutingTable, nil + } + + loadedRoutingTable, err := s.getByName(query.Name) + if err != nil { + return loadedRoutingTable, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedRoutingTable, nil +} + +func (s *DatabaseRoutingTableStore) getByID(idOfRoutingTable uuid.UUID) (RoutingTable, error) { + var RoutingTable RoutingTable + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfRoutingTable.String()}}) + if result == nil { + return RoutingTable, errors.ErrCouldNotFind{ID: idOfRoutingTable} + } + + err := result.Decode(&RoutingTable) + if err != nil { + return RoutingTable, errors.ErrCouldNotMarshall{Identifier: idOfRoutingTable, Type: RoutingTable, Err: err} + } + + return RoutingTable, nil +} + +func (s *DatabaseRoutingTableStore) getByName(nameOfRoutingTable string) (RoutingTable, error) { + var loadedRoutingTable RoutingTable + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfRoutingTable}}) + if result == nil { + return loadedRoutingTable, errors.ErrCouldNotFind{Name: nameOfRoutingTable} + } + + err := result.Decode(&loadedRoutingTable) + if err != nil { + return loadedRoutingTable, errors.ErrCouldNotMarshall{Type: loadedRoutingTable, Err: err} + } + + return loadedRoutingTable, nil +} + +// GetAll returns all stored routingTables. +func (s *DatabaseRoutingTableStore) GetAll() ([]RoutingTable, error) { + var loadedRoutingTable []RoutingTable + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return []RoutingTable{}, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedRoutingTable) + if err != nil { + return loadedRoutingTable, errors.ErrCouldNotMarshall{Type: loadedRoutingTable, Err: err} + } + + return loadedRoutingTable, nil +} + +// Add adds a RoutingTable to the store. +func (s *DatabaseRoutingTableStore) Add(routingTable RoutingTable) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, routingTable) + if err != nil { + return errors.ErrCouldNotCreate{Identifier: routingTable.ID, Type: routingTable, Err: err} + } + + return nil +} + +// Update updates a existing routingTable. +func (s *DatabaseRoutingTableStore) Update(routingTable RoutingTable) error { + var updatedLoadedRoutingTable RoutingTable + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: routingTable}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": routingTable.ID.String()}, update, &opt). + Decode(&updatedLoadedRoutingTable) + if err != nil { + return errors.ErrCouldNotUpdate{Identifier: routingTable.ID, Type: routingTable, Err: err} + } + + return nil +} + +// Delete deletes a node from the node store. +func (s *DatabaseRoutingTableStore) Delete(routingTable RoutingTable) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: routingTable.ID.String()}}) + if err != nil { + return err + } + + return nil +} diff --git a/controller/topology/store/genericStore.go b/controller/topology/store/genericStore.go new file mode 100644 index 0000000000000000000000000000000000000000..557426c307b7dcd1a83e0ed8480e82233f5a9963 --- /dev/null +++ b/controller/topology/store/genericStore.go @@ -0,0 +1,78 @@ +package store + +import ( + "errors" + + "github.com/google/uuid" + + query "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +type storableConstraint interface { + GetID() uuid.UUID +} + +// GenericStore provides a in-memory implementation for multiple stores. +type GenericStore[T storableConstraint] struct { + store map[uuid.UUID]T +} + +// NewGenericStore returns a specific in-memory store for a type T. +func NewGenericStore[T storableConstraint]() *GenericStore[T] { + return &GenericStore[T]{ + store: make(map[uuid.UUID]T), + } +} + +func (t *GenericStore[T]) Add(item T) error { + _, ok := t.store[item.GetID()] + if ok { + return errors.New("item already exists") + } + + t.store[item.GetID()] = item + + return nil +} + +func (t *GenericStore[T]) Update(item T) error { + _, ok := t.store[item.GetID()] + if !ok { + return errors.New("item not found") + } + + t.store[item.GetID()] = item + + return nil +} + +func (t *GenericStore[T]) Delete(item T) error { + _, ok := t.store[item.GetID()] + if !ok { + return errors.New("item not found") + } + + delete(t.store, item.GetID()) + + return nil +} + +func (t *GenericStore[T]) Get(query query.Query) (T, error) { + // First search for direct hit on UUID. + item, ok := t.store[query.ID] + if !ok { + return *new(T), errors.New("item not found") + } + + return item, nil +} + +func (t *GenericStore[T]) GetAll() ([]T, error) { + var allItems []T + + for _, item := range t.store { + allItems = append(allItems, item) + } + + return allItems, nil +} diff --git a/controller/topology/store/genericStore_test.go b/controller/topology/store/genericStore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b8828653077d6527dc6c809e400404feb7d586d1 --- /dev/null +++ b/controller/topology/store/genericStore_test.go @@ -0,0 +1,314 @@ +package store + +import ( + "reflect" + "testing" + + "github.com/google/uuid" + + query "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +type testItem struct { + ID uuid.UUID +} + +func (t testItem) GetID() uuid.UUID { + return t.ID +} + +func getTestItem() testItem { + return testItem{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + } +} + +func getEmptyTestItem() testItem { + return testItem{} +} + +func TestNewGenericStore(t *testing.T) { + tests := []struct { + name string + want *GenericStore[testItem] + }{ + { + name: "should create generic store", + want: NewGenericStore[testItem](), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewGenericStore[testItem](); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewGenericStore() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGenericStore_Get(t *testing.T) { + type fields struct { + items []testItem + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want testItem + wantErr bool + }{ + { + name: "should error if item with uuid is not in store", + fields: fields{ + items: []testItem{}, + }, + args: args{ + query: query.Query{ + ID: getTestItem().ID, + }, + }, + want: getEmptyTestItem(), + wantErr: true, + }, + { + name: "should return item that is in the store", + fields: fields{ + items: []testItem{getTestItem()}, + }, + args: args{ + query: query.Query{ + ID: getTestItem().ID, + }, + }, + want: getTestItem(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewGenericStore[testItem]() + + for _, itemToAdd := range tt.fields.items { + err := s.Add(itemToAdd) + if err != nil { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + + got, err := s.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("GenericStore.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GenericStore.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGenericStore_GetAll(t *testing.T) { + type fields struct { + items []testItem + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want []testItem + wantErr bool + }{ + { + name: "should return all items that are in the store", + fields: fields{ + items: []testItem{getTestItem()}, + }, + args: args{ + query: query.Query{ + ID: getTestItem().ID, + }, + }, + want: []testItem{getTestItem()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewGenericStore[testItem]() + + for _, itemToAdd := range tt.fields.items { + err := s.Add(itemToAdd) + if err != nil { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + + got, err := s.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("GenericStore.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GenericStore.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGenericStore_Add(t *testing.T) { + type fields struct { + items []testItem + } + type args struct { + item testItem + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should add a item", + args: args{ + item: getTestItem(), + }, + wantErr: false, + }, + { + name: "should fail when adding a already existing item", + fields: fields{ + items: []testItem{getTestItem()}, + }, + args: args{ + item: getTestItem(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewGenericStore[testItem]() + + for _, itemToAdd := range tt.fields.items { + err := s.Add(itemToAdd) + if err != nil { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + + if err := s.Add(tt.args.item); (err != nil) != tt.wantErr { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGenericStore_Update(t *testing.T) { + type fields struct { + items []testItem + } + type args struct { + item testItem + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should fail when item does not exist", + args: args{ + item: getTestItem(), + }, + wantErr: true, + }, + { + name: "should update a existing a item", + fields: fields{ + items: []testItem{getTestItem()}, + }, + args: args{ + item: getTestItem(), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewGenericStore[testItem]() + + for _, itemToAdd := range tt.fields.items { + err := s.Add(itemToAdd) + if err != nil { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + + if err := s.Update(tt.args.item); (err != nil) != tt.wantErr { + t.Errorf("GenericStore.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGenericStore_Delete(t *testing.T) { + type fields struct { + items []testItem + } + type args struct { + item testItem + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should fail when item does not exist", + args: args{ + item: getTestItem(), + }, + wantErr: true, + }, + { + name: "should delete a existing a item", + fields: fields{ + items: []testItem{getTestItem()}, + }, + args: args{ + item: getTestItem(), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewGenericStore[testItem]() + + for _, itemToAdd := range tt.fields.items { + err := s.Add(itemToAdd) + if err != nil { + t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + + if err := s.Delete(tt.args.item); (err != nil) != tt.wantErr { + t.Errorf("GenericStore.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/controller/topology/topology.go b/controller/topology/topology.go new file mode 100644 index 0000000000000000000000000000000000000000..18f8a87cb9ac71f1df15f450796b8e42ba62059d --- /dev/null +++ b/controller/topology/topology.go @@ -0,0 +1 @@ +package topology diff --git a/controller/topology/topologyService.go b/controller/topology/topologyService.go new file mode 100644 index 0000000000000000000000000000000000000000..9caa065fdbcf81808682a73c072a8ac465056051 --- /dev/null +++ b/controller/topology/topologyService.go @@ -0,0 +1,124 @@ +package topology + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/event" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" +) + +const ( + // LinkEventTopic is the used topic for link related entity changes + LinkEventTopic = "link" +) + +// Service defines an interface for a Service +type Service interface { + AddLink(links.Link) error + UpdateLink(links.Link) error + DeleteLink(links.Link) error + Get(query.Query) (links.Link, error) + GetAll() ([]links.Link, error) +} + +// service is a service for ports +type service struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service +} + +// NewTopologyService creates a new TopologyService +func NewTopologyService( + store Store, + nodeService nodes.Service, + portService ports.Service, + eventService eventInterfaces.Service, +) Service { + return &service{ + store: store, + nodeService: nodeService, + portService: portService, + eventService: eventService, + } +} + +// AddLink adds a new link to the topology +func (t *service) AddLink(link links.Link) error { + // These checks are also happening in the current NBI implementation. + // This should be refactored to only to these checks here. + // _, err := t.nodeService.EnsureExists(link.SourceNode) + // if err != nil { + // return err + // } + // _, err = t.portService.EnsureExists(link.SourcePort) + // if err != nil { + // return err + // } + + // _, err = t.nodeService.EnsureExists(link.TargetNode) + // if err != nil { + // return err + // } + // _, err = t.portService.EnsureExists(link.TargetPort) + // if err != nil { + // return err + // } + + err := t.store.Add(link) + if err != nil { + return err + } + + t.eventService.PublishEvent(LinkEventTopic, event.NewAddEvent(link.ID)) + + return nil +} + +// UpdateLink updates an existing link +func (t *service) UpdateLink(link links.Link) error { + err := t.store.Update(link) + if err != nil { + return err + } + + t.eventService.PublishEvent(LinkEventTopic, event.NewUpdateEvent(link.ID)) + + return nil +} + +// DeleteLink deletes a link +func (t *service) DeleteLink(link links.Link) error { + // TODO: Delete should also check if a node or port is used somewhere else and + // if not, delete the node and its ports + err := t.store.Delete(link) + if err != nil { + return err + } + + t.eventService.PublishEvent(LinkEventTopic, event.NewDeleteEvent(link.ID)) + + return nil +} + +// GetAll returns the current topology +func (t *service) GetAll() ([]links.Link, error) { + topo, err := t.store.GetAll() + if err != nil { + return topo, err + } + return topo, nil +} + +// GetAll returns the current topology +func (t *service) Get(query query.Query) (links.Link, error) { + link, err := t.store.Get(query) + if err != nil { + return link, err + } + + return link, nil +} diff --git a/controller/topology/topologyService_test.go b/controller/topology/topologyService_test.go new file mode 100644 index 0000000000000000000000000000000000000000..899acc98c4e31b5e5b89258843efb76cf5a1be4d --- /dev/null +++ b/controller/topology/topologyService_test.go @@ -0,0 +1,439 @@ +package topology + +import ( + "reflect" + "testing" + + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + + "github.com/google/uuid" +) + +func getTestSourceNode() nodes.Node { + return nodes.Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Source-Node", + } +} + +func getTestTargetNode() nodes.Node { + return nodes.Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7612"), + Name: "Test-Target-Node", + } +} + +func getTestSourcePort() ports.Port { + return ports.Port{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fce"), + Name: "Test-Source-Port", + } +} + +func getTestTargetPort() ports.Port { + return ports.Port{ + ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"), + Name: "Test-Target-Port", + } +} + +func getTestLink() links.Link { + return links.Link{ + ID: uuid.MustParse("5eb474f1-428e-4503-ba68-dcf9bef53467"), + Name: "Test-Link", + SourceNode: getTestSourceNode(), + TargetNode: getTestTargetNode(), + SourcePort: getTestSourcePort(), + TargetPort: getTestTargetPort(), + } +} + +func getEmptyLink() links.Link { + return links.Link{} +} + +func getTestStoreWithLinks(t *testing.T, nodes []links.Link) Store { + store := store.NewGenericStore[links.Link]() + + for _, node := range nodes { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func getTestStoreWithNodes(t *testing.T, nodesToAdd []nodes.Node) nodes.Store { + store := store.NewGenericStore[nodes.Node]() + + for _, node := range nodesToAdd { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func getTestStoreWithPorts(t *testing.T, portsToAdd []ports.Port) ports.Store { + store := store.NewGenericStore[ports.Port]() + + for _, port := range portsToAdd { + err := store.Add(port) + if err != nil { + t.Fatalf("failed to prepare test store while adding port: %v", err) + } + } + + return store +} + +func TestNewTopologyService(t *testing.T) { + type args struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + tests := []struct { + name string + args args + want Service + }{ + { + name: "should create a new topology service", + args: args{ + store: getTestStoreWithLinks(t, []links.Link{}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + eventService: eventservice.NewMockEventService(), + }, + want: NewTopologyService( + getTestStoreWithLinks(t, []links.Link{}), + nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + eventservice.NewMockEventService(), + ), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewTopologyService( + tt.args.store, + tt.args.nodeService, + tt.args.portService, + tt.args.eventService, + ); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNodeService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopologyService_AddLink(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + type args struct { + link links.Link + } + tests := []struct { + name string + fields fields + args args + want links.Link + wantErr bool + }{ + { + name: "should add a link to the store", + fields: fields{ + store: store.NewGenericStore[links.Link](), + nodeService: nodes.NewNodeService( + getTestStoreWithNodes( + t, + []nodes.Node{getTestSourceNode(), getTestTargetNode()}, + ), + eventservice.NewMockEventService(), + ), + portService: ports.NewPortService( + getTestStoreWithPorts( + t, + []ports.Port{getTestSourcePort(), getTestTargetPort()}, + ), + eventservice.NewMockEventService(), + ), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + link: getTestLink(), + }, + want: getTestLink(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &service{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + eventService: tt.fields.eventService, + } + err := p.AddLink(tt.args.link) + if (err != nil) != tt.wantErr { + t.Errorf("service.AddLink() error = %v, wantErr %v", err, tt.wantErr) + return + } + + got, err := p.Get(query.Query{ID: tt.args.link.ID}) + if (err != nil) != tt.wantErr { + t.Errorf("service.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("service.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopologyService_Update(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + type args struct { + link links.Link + } + tests := []struct { + name string + fields fields + args args + want links.Link + wantErr bool + }{ + { + name: "should update an existing link", + fields: fields{ + store: getTestStoreWithLinks(t, []links.Link{getTestLink()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + link: getTestLink(), + }, + want: getTestLink(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &service{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + eventService: tt.fields.eventService, + } + err := p.UpdateLink(tt.args.link) + if (err != nil) != tt.wantErr { + t.Errorf("service.Update() error = %v, wantErr %v", err, tt.wantErr) + return + } + + got, err := p.Get(query.Query{ID: tt.args.link.ID}) + if (err != nil) != tt.wantErr { + t.Errorf("service.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("service.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_Delete(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + type args struct { + link links.Link + } + tests := []struct { + name string + fields fields + args args + want links.Link + wantErr bool + }{ + { + name: "should delete an existing link", + fields: fields{ + store: getTestStoreWithLinks(t, []links.Link{getTestLink()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + eventService: eventservice.NewMockEventService(), + }, + args: args{ + link: getTestLink(), + }, + wantErr: false, + }, + { + name: "should fail if a node does not exists", + fields: fields{ + store: store.NewGenericStore[links.Link](), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + link: getTestLink(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &service{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + eventService: tt.fields.eventService, + } + if err := p.DeleteLink(tt.args.link); (err != nil) != tt.wantErr { + t.Errorf("service.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestTopologyService_Get(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + type args struct { + query query.Query + } + tests := []struct { + name string + fields fields + args args + want links.Link + wantErr bool + }{ + { + name: "should error if link with uuid is not in store", + fields: fields{ + store: getTestStoreWithLinks(t, []links.Link{}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + }, + args: args{ + query: query.Query{ + ID: getTestLink().ID, + Name: getTestLink().Name, + }, + }, + want: getEmptyLink(), + wantErr: true, + }, + { + name: "should return link that is in the store", + fields: fields{ + store: getTestStoreWithLinks(t, []links.Link{getTestLink()}), + }, + args: args{ + query: query.Query{ + ID: getTestLink().ID, + Name: getTestLink().Name, + }, + }, + want: getTestLink(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &service{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + eventService: tt.fields.eventService, + } + got, err := p.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("service.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("service.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTopologyService_GetAll(t *testing.T) { + type fields struct { + store Store + nodeService nodes.Service + portService ports.Service + eventService eventInterfaces.Service + } + tests := []struct { + name string + fields fields + want []links.Link + wantErr bool + }{ + { + name: "should get all stored links", + fields: fields{ + store: getTestStoreWithLinks(t, []links.Link{getTestLink()}), + nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{}), eventservice.NewMockEventService()), + portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{}), eventservice.NewMockEventService()), + eventService: eventservice.NewMockEventService(), + }, + want: []links.Link{getTestLink()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &service{ + store: tt.fields.store, + nodeService: tt.fields.nodeService, + portService: tt.fields.portService, + eventService: tt.fields.eventService, + } + got, err := p.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("service.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("service.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/topology/topologyStore.go b/controller/topology/topologyStore.go new file mode 100644 index 0000000000000000000000000000000000000000..9c63aa120d89c4e6acb0fa6b27fb7894454a2f82 --- /dev/null +++ b/controller/topology/topologyStore.go @@ -0,0 +1,186 @@ +package topology + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + query "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a Topology store interface +type Store interface { + Add(links.Link) error + Update(links.Link) error + Delete(links.Link) error + Get(query.Query) (links.Link, error) + GetAll() ([]links.Link, error) +} + +// DatabaseTopologyStore is a database store for the topology +type DatabaseTopologyStore struct { + storeName string +} + +// NewDatabaseTopologyStore returns a TopologyStore +func NewDatabaseTopologyStore() Store { + return &DatabaseTopologyStore{ + storeName: fmt.Sprintf("topology-store.json"), + } +} + +// Get takes a link's UUID or name and returns the link. +func (s *DatabaseTopologyStore) Get(query query.Query) (links.Link, error) { + var loadedTopology links.Link + + if query.ID.String() != "" { + loadedTopology, err := s.getByID(query.ID) + if err != nil { + return loadedTopology, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedTopology, nil + } + + loadedTopology, err := s.getByName(query.Name) + if err != nil { + return loadedTopology, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name} + } + + return loadedTopology, nil +} + +func (s *DatabaseTopologyStore) getByID(idOfTopology uuid.UUID) (links.Link, error) { + var loadedTopology links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfTopology.String()}}) + if result == nil { + return loadedTopology, errors.ErrCouldNotFind{ID: idOfTopology} + } + + err := result.Decode(&loadedTopology) + if err != nil { + return loadedTopology, errors.ErrCouldNotMarshall{Identifier: idOfTopology, Type: loadedTopology, Err: err} + } + + return loadedTopology, nil +} + +func (s *DatabaseTopologyStore) getByName(nameOfTopology string) (links.Link, error) { + var loadedTopology links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfTopology}}) + if result == nil { + return loadedTopology, errors.ErrCouldNotFind{Name: nameOfTopology} + } + + err := result.Decode(&loadedTopology) + if err != nil { + return loadedTopology, errors.ErrCouldNotMarshall{Identifier: nameOfTopology, Type: loadedTopology, Err: err} + } + + return loadedTopology, nil +} + +// GetAll returns all stored links. +func (s *DatabaseTopologyStore) GetAll() ([]links.Link, error) { + var loadedTopology []links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return loadedTopology, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedTopology) + if err != nil { + return loadedTopology, errors.ErrCouldNotMarshall{Type: loadedTopology, Err: err} + } + + return loadedTopology, nil +} + +// Add adds a link to the link store. +func (s *DatabaseTopologyStore) Add(link links.Link) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, link) + if err != nil { + return errors.ErrCouldNotCreate{Identifier: link.ID, Type: link, Err: err} + } + + return nil +} + +// Update updates a existing link. +func (s *DatabaseTopologyStore) Update(linkToUpdate links.Link) error { + var updatedLink links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: linkToUpdate}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": linkToUpdate.ID.String()}, update, &opt). + Decode(&updatedLink) + if err != nil { + return errors.ErrCouldNotUpdate{Identifier: linkToUpdate.ID, Type: linkToUpdate, Err: err} + } + + return nil +} + +// Delete deletes a link from the link store. +func (s *DatabaseTopologyStore) Delete(linkToDelete links.Link) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: linkToDelete.ID.String()}}) + if err != nil { + return err + } + + return nil +}