diff --git a/README.md b/README.md index fd68b107..3de40c60 100644 --- a/README.md +++ b/README.md @@ -18,28 +18,53 @@ service SupernodeService { message StatusRequest {} message StatusResponse { - message CPU { - string usage = 1; - string remaining = 2; - } - - message Memory { - uint64 total = 1; - uint64 used = 2; - uint64 available = 3; - double used_perc = 4; + string version = 1; // Supernode version + uint64 uptime_seconds = 2; // Uptime in seconds + + message Resources { + message CPU { + double usage_percent = 1; // CPU usage percentage (0-100) + int32 cores = 2; // Number of CPU cores + } + + message Memory { + double total_gb = 1; // Total memory in GB + double used_gb = 2; // Used memory in GB + double available_gb = 3; // Available memory in GB + double usage_percent = 4; // Memory usage percentage (0-100) + } + + message Storage { + string path = 1; // Storage path being monitored + uint64 total_bytes = 2; + uint64 used_bytes = 3; + uint64 available_bytes = 4; + double usage_percent = 5; // Storage usage percentage (0-100) + } + + CPU cpu = 1; + Memory memory = 2; + repeated Storage storage_volumes = 3; + string hardware_summary = 4; // Formatted hardware summary (e.g., "8 cores / 32GB RAM") } - + message ServiceTasks { string service_name = 1; repeated string task_ids = 2; int32 task_count = 3; } - - CPU cpu = 1; - Memory memory = 2; - repeated ServiceTasks services = 3; - repeated string available_services = 4; + + message Network { + int32 peers_count = 1; // Number of connected peers in P2P network + repeated string peer_addresses = 2; // List of connected peer addresses (format: "ID@IP:Port") + } + + Resources resources = 3; + repeated ServiceTasks running_tasks = 4; // Services with currently running tasks + repeated string registered_services = 5; // All registered/available services + Network network = 6; // P2P network information + int32 rank = 7; // Rank in the top supernodes list (0 if not in top list) + string ip_address = 8; // Supernode IP address with port (e.g., "192.168.1.1:4445") } ``` @@ -115,42 +140,65 @@ The supernode provides an HTTP gateway that exposes the gRPC services via REST A ### Endpoints -#### GET /status -Returns the current supernode status including CPU, memory usage and active services. +#### GET /api/v1/status +Returns the current supernode status including system resources (CPU, memory, storage) and service information. ```bash -curl http://localhost:8002/status +curl http://localhost:8002/api/v1/status ``` Response: ```json { - "cpu": { - "usage": "15.2%", - "remaining": "84.8%" + "version": "1.0.0", + "uptime_seconds": "3600", + "resources": { + "cpu": { + "usage_percent": 15.2, + "cores": 8 + }, + "memory": { + "total_gb": 32.0, + "used_gb": 16.0, + "available_gb": 16.0, + "usage_percent": 50.0 + }, + "storage_volumes": [ + { + "path": "/", + "total_bytes": "500000000000", + "used_bytes": "250000000000", + "available_bytes": "250000000000", + "usage_percent": 50.0 + } + ], + "hardware_summary": "8 cores / 32GB RAM" }, - "memory": { - "total": "16777216000", - "used": "8388608000", - "available": "8388608000", - "usedPerc": 50.0 - }, - "services": [ + "running_tasks": [ { - "serviceName": "cascade", - "taskIds": ["task1", "task2"], - "taskCount": 2 + "service_name": "cascade", + "task_ids": ["task1", "task2"], + "task_count": 2 } ], - "availableServices": ["cascade", "sense"] + "registered_services": ["cascade", "sense"], + "network": { + "peers_count": 11, + "peer_addresses": [ + "lumera13z4pkmgkr587sg6lkqnmqmqkkfpsau3rmjd5kx@156.67.29.226:4445", + "lumera1s55nzsyqsuwxsl3es0v7rxux7rypsa7zpzlqg5@18.216.80.56:4445" + ] + }, + "rank": 6, + "ip_address": "192.168.1.100:4445" } ``` -#### GET /services +#### GET /api/v1/services Returns the list of available services on the supernode. ```bash -curl http://localhost:8002/services +curl http://localhost:8002/api/v1/services ``` Response: diff --git a/gen/supernode/supernode.pb.go b/gen/supernode/supernode.pb.go index 167c03bd..1ace0f23 100644 --- a/gen/supernode/supernode.pb.go +++ b/gen/supernode/supernode.pb.go @@ -199,16 +199,20 @@ func (x *ServiceInfo) GetMethods() []string { return nil } -// The StatusResponse represents system status. +// The StatusResponse represents system status with clear organization type StatusResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cpu *StatusResponse_CPU `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` - Memory *StatusResponse_Memory `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` - RunningTasks []*StatusResponse_ServiceTasks `protobuf:"bytes,3,rep,name=running_tasks,json=runningTasks,proto3" json:"running_tasks,omitempty"` // Services with currently running tasks - RegisteredServices []string `protobuf:"bytes,4,rep,name=registered_services,json=registeredServices,proto3" json:"registered_services,omitempty"` // All registered/available services + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // Supernode version + UptimeSeconds uint64 `protobuf:"varint,2,opt,name=uptime_seconds,json=uptimeSeconds,proto3" json:"uptime_seconds,omitempty"` // Uptime in seconds + Resources *StatusResponse_Resources `protobuf:"bytes,3,opt,name=resources,proto3" json:"resources,omitempty"` + RunningTasks []*StatusResponse_ServiceTasks `protobuf:"bytes,4,rep,name=running_tasks,json=runningTasks,proto3" json:"running_tasks,omitempty"` // Services with currently running tasks + RegisteredServices []string `protobuf:"bytes,5,rep,name=registered_services,json=registeredServices,proto3" json:"registered_services,omitempty"` // All registered/available services + Network *StatusResponse_Network `protobuf:"bytes,6,opt,name=network,proto3" json:"network,omitempty"` // P2P network information + Rank int32 `protobuf:"varint,7,opt,name=rank,proto3" json:"rank,omitempty"` // Rank in the top supernodes list (0 if not in top list) + IpAddress string `protobuf:"bytes,8,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,omitempty"` // Supernode IP address with port (e.g., "192.168.1.1:4445") } func (x *StatusResponse) Reset() { @@ -241,16 +245,23 @@ func (*StatusResponse) Descriptor() ([]byte, []int) { return file_supernode_supernode_proto_rawDescGZIP(), []int{4} } -func (x *StatusResponse) GetCpu() *StatusResponse_CPU { +func (x *StatusResponse) GetVersion() string { if x != nil { - return x.Cpu + return x.Version } - return nil + return "" } -func (x *StatusResponse) GetMemory() *StatusResponse_Memory { +func (x *StatusResponse) GetUptimeSeconds() uint64 { if x != nil { - return x.Memory + return x.UptimeSeconds + } + return 0 +} + +func (x *StatusResponse) GetResources() *StatusResponse_Resources { + if x != nil { + return x.Resources } return nil } @@ -269,29 +280,53 @@ func (x *StatusResponse) GetRegisteredServices() []string { return nil } -type StatusResponse_CPU struct { +func (x *StatusResponse) GetNetwork() *StatusResponse_Network { + if x != nil { + return x.Network + } + return nil +} + +func (x *StatusResponse) GetRank() int32 { + if x != nil { + return x.Rank + } + return 0 +} + +func (x *StatusResponse) GetIpAddress() string { + if x != nil { + return x.IpAddress + } + return "" +} + +// System resource information +type StatusResponse_Resources struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Usage string `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"` - Remaining string `protobuf:"bytes,2,opt,name=remaining,proto3" json:"remaining,omitempty"` + Cpu *StatusResponse_Resources_CPU `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory *StatusResponse_Resources_Memory `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` + StorageVolumes []*StatusResponse_Resources_Storage `protobuf:"bytes,3,rep,name=storage_volumes,json=storageVolumes,proto3" json:"storage_volumes,omitempty"` + HardwareSummary string `protobuf:"bytes,4,opt,name=hardware_summary,json=hardwareSummary,proto3" json:"hardware_summary,omitempty"` // Formatted hardware summary (e.g., "8 cores / 32GB RAM") } -func (x *StatusResponse_CPU) Reset() { - *x = StatusResponse_CPU{} +func (x *StatusResponse_Resources) Reset() { + *x = StatusResponse_Resources{} mi := &file_supernode_supernode_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *StatusResponse_CPU) String() string { +func (x *StatusResponse_Resources) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StatusResponse_CPU) ProtoMessage() {} +func (*StatusResponse_Resources) ProtoMessage() {} -func (x *StatusResponse_CPU) ProtoReflect() protoreflect.Message { +func (x *StatusResponse_Resources) ProtoReflect() protoreflect.Message { mi := &file_supernode_supernode_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -303,50 +338,64 @@ func (x *StatusResponse_CPU) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StatusResponse_CPU.ProtoReflect.Descriptor instead. -func (*StatusResponse_CPU) Descriptor() ([]byte, []int) { +// Deprecated: Use StatusResponse_Resources.ProtoReflect.Descriptor instead. +func (*StatusResponse_Resources) Descriptor() ([]byte, []int) { return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 0} } -func (x *StatusResponse_CPU) GetUsage() string { +func (x *StatusResponse_Resources) GetCpu() *StatusResponse_Resources_CPU { if x != nil { - return x.Usage + return x.Cpu } - return "" + return nil +} + +func (x *StatusResponse_Resources) GetMemory() *StatusResponse_Resources_Memory { + if x != nil { + return x.Memory + } + return nil } -func (x *StatusResponse_CPU) GetRemaining() string { +func (x *StatusResponse_Resources) GetStorageVolumes() []*StatusResponse_Resources_Storage { if x != nil { - return x.Remaining + return x.StorageVolumes + } + return nil +} + +func (x *StatusResponse_Resources) GetHardwareSummary() string { + if x != nil { + return x.HardwareSummary } return "" } -type StatusResponse_Memory struct { +// ServiceTasks contains task information for a specific service +type StatusResponse_ServiceTasks struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Total uint64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` - Used uint64 `protobuf:"varint,2,opt,name=used,proto3" json:"used,omitempty"` - Available uint64 `protobuf:"varint,3,opt,name=available,proto3" json:"available,omitempty"` - UsedPerc float64 `protobuf:"fixed64,4,opt,name=used_perc,json=usedPerc,proto3" json:"used_perc,omitempty"` + ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` + TaskIds []string `protobuf:"bytes,2,rep,name=task_ids,json=taskIds,proto3" json:"task_ids,omitempty"` + TaskCount int32 `protobuf:"varint,3,opt,name=task_count,json=taskCount,proto3" json:"task_count,omitempty"` } -func (x *StatusResponse_Memory) Reset() { - *x = StatusResponse_Memory{} +func (x *StatusResponse_ServiceTasks) Reset() { + *x = StatusResponse_ServiceTasks{} mi := &file_supernode_supernode_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *StatusResponse_Memory) String() string { +func (x *StatusResponse_ServiceTasks) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StatusResponse_Memory) ProtoMessage() {} +func (*StatusResponse_ServiceTasks) ProtoMessage() {} -func (x *StatusResponse_Memory) ProtoReflect() protoreflect.Message { +func (x *StatusResponse_ServiceTasks) ProtoReflect() protoreflect.Message { mi := &file_supernode_supernode_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -358,65 +407,165 @@ func (x *StatusResponse_Memory) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StatusResponse_Memory.ProtoReflect.Descriptor instead. -func (*StatusResponse_Memory) Descriptor() ([]byte, []int) { +// Deprecated: Use StatusResponse_ServiceTasks.ProtoReflect.Descriptor instead. +func (*StatusResponse_ServiceTasks) Descriptor() ([]byte, []int) { return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 1} } -func (x *StatusResponse_Memory) GetTotal() uint64 { +func (x *StatusResponse_ServiceTasks) GetServiceName() string { if x != nil { - return x.Total + return x.ServiceName + } + return "" +} + +func (x *StatusResponse_ServiceTasks) GetTaskIds() []string { + if x != nil { + return x.TaskIds + } + return nil +} + +func (x *StatusResponse_ServiceTasks) GetTaskCount() int32 { + if x != nil { + return x.TaskCount } return 0 } -func (x *StatusResponse_Memory) GetUsed() uint64 { +// Network information +type StatusResponse_Network struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PeersCount int32 `protobuf:"varint,1,opt,name=peers_count,json=peersCount,proto3" json:"peers_count,omitempty"` // Number of connected peers in P2P network + PeerAddresses []string `protobuf:"bytes,2,rep,name=peer_addresses,json=peerAddresses,proto3" json:"peer_addresses,omitempty"` // List of connected peer addresses (optional, may be empty for privacy) +} + +func (x *StatusResponse_Network) Reset() { + *x = StatusResponse_Network{} + mi := &file_supernode_supernode_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatusResponse_Network) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse_Network) ProtoMessage() {} + +func (x *StatusResponse_Network) ProtoReflect() protoreflect.Message { + mi := &file_supernode_supernode_proto_msgTypes[7] if x != nil { - return x.Used + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse_Network.ProtoReflect.Descriptor instead. +func (*StatusResponse_Network) Descriptor() ([]byte, []int) { + return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 2} +} + +func (x *StatusResponse_Network) GetPeersCount() int32 { + if x != nil { + return x.PeersCount } return 0 } -func (x *StatusResponse_Memory) GetAvailable() uint64 { +func (x *StatusResponse_Network) GetPeerAddresses() []string { if x != nil { - return x.Available + return x.PeerAddresses + } + return nil +} + +type StatusResponse_Resources_CPU struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UsagePercent float64 `protobuf:"fixed64,1,opt,name=usage_percent,json=usagePercent,proto3" json:"usage_percent,omitempty"` // CPU usage percentage (0-100) + Cores int32 `protobuf:"varint,2,opt,name=cores,proto3" json:"cores,omitempty"` // Number of CPU cores +} + +func (x *StatusResponse_Resources_CPU) Reset() { + *x = StatusResponse_Resources_CPU{} + mi := &file_supernode_supernode_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatusResponse_Resources_CPU) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse_Resources_CPU) ProtoMessage() {} + +func (x *StatusResponse_Resources_CPU) ProtoReflect() protoreflect.Message { + mi := &file_supernode_supernode_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse_Resources_CPU.ProtoReflect.Descriptor instead. +func (*StatusResponse_Resources_CPU) Descriptor() ([]byte, []int) { + return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 0, 0} +} + +func (x *StatusResponse_Resources_CPU) GetUsagePercent() float64 { + if x != nil { + return x.UsagePercent } return 0 } -func (x *StatusResponse_Memory) GetUsedPerc() float64 { +func (x *StatusResponse_Resources_CPU) GetCores() int32 { if x != nil { - return x.UsedPerc + return x.Cores } return 0 } -// ServiceTasks contains task information for a specific service -type StatusResponse_ServiceTasks struct { +type StatusResponse_Resources_Memory struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` - TaskIds []string `protobuf:"bytes,2,rep,name=task_ids,json=taskIds,proto3" json:"task_ids,omitempty"` - TaskCount int32 `protobuf:"varint,3,opt,name=task_count,json=taskCount,proto3" json:"task_count,omitempty"` + TotalGb float64 `protobuf:"fixed64,1,opt,name=total_gb,json=totalGb,proto3" json:"total_gb,omitempty"` // Total memory in GB + UsedGb float64 `protobuf:"fixed64,2,opt,name=used_gb,json=usedGb,proto3" json:"used_gb,omitempty"` // Used memory in GB + AvailableGb float64 `protobuf:"fixed64,3,opt,name=available_gb,json=availableGb,proto3" json:"available_gb,omitempty"` // Available memory in GB + UsagePercent float64 `protobuf:"fixed64,4,opt,name=usage_percent,json=usagePercent,proto3" json:"usage_percent,omitempty"` // Memory usage percentage (0-100) } -func (x *StatusResponse_ServiceTasks) Reset() { - *x = StatusResponse_ServiceTasks{} - mi := &file_supernode_supernode_proto_msgTypes[7] +func (x *StatusResponse_Resources_Memory) Reset() { + *x = StatusResponse_Resources_Memory{} + mi := &file_supernode_supernode_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *StatusResponse_ServiceTasks) String() string { +func (x *StatusResponse_Resources_Memory) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StatusResponse_ServiceTasks) ProtoMessage() {} +func (*StatusResponse_Resources_Memory) ProtoMessage() {} -func (x *StatusResponse_ServiceTasks) ProtoReflect() protoreflect.Message { - mi := &file_supernode_supernode_proto_msgTypes[7] +func (x *StatusResponse_Resources_Memory) ProtoReflect() protoreflect.Message { + mi := &file_supernode_supernode_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -427,28 +576,112 @@ func (x *StatusResponse_ServiceTasks) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StatusResponse_ServiceTasks.ProtoReflect.Descriptor instead. -func (*StatusResponse_ServiceTasks) Descriptor() ([]byte, []int) { - return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 2} +// Deprecated: Use StatusResponse_Resources_Memory.ProtoReflect.Descriptor instead. +func (*StatusResponse_Resources_Memory) Descriptor() ([]byte, []int) { + return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 0, 1} } -func (x *StatusResponse_ServiceTasks) GetServiceName() string { +func (x *StatusResponse_Resources_Memory) GetTotalGb() float64 { if x != nil { - return x.ServiceName + return x.TotalGb + } + return 0 +} + +func (x *StatusResponse_Resources_Memory) GetUsedGb() float64 { + if x != nil { + return x.UsedGb + } + return 0 +} + +func (x *StatusResponse_Resources_Memory) GetAvailableGb() float64 { + if x != nil { + return x.AvailableGb + } + return 0 +} + +func (x *StatusResponse_Resources_Memory) GetUsagePercent() float64 { + if x != nil { + return x.UsagePercent + } + return 0 +} + +type StatusResponse_Resources_Storage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // Storage path being monitored + TotalBytes uint64 `protobuf:"varint,2,opt,name=total_bytes,json=totalBytes,proto3" json:"total_bytes,omitempty"` + UsedBytes uint64 `protobuf:"varint,3,opt,name=used_bytes,json=usedBytes,proto3" json:"used_bytes,omitempty"` + AvailableBytes uint64 `protobuf:"varint,4,opt,name=available_bytes,json=availableBytes,proto3" json:"available_bytes,omitempty"` + UsagePercent float64 `protobuf:"fixed64,5,opt,name=usage_percent,json=usagePercent,proto3" json:"usage_percent,omitempty"` // Storage usage percentage (0-100) +} + +func (x *StatusResponse_Resources_Storage) Reset() { + *x = StatusResponse_Resources_Storage{} + mi := &file_supernode_supernode_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatusResponse_Resources_Storage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse_Resources_Storage) ProtoMessage() {} + +func (x *StatusResponse_Resources_Storage) ProtoReflect() protoreflect.Message { + mi := &file_supernode_supernode_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse_Resources_Storage.ProtoReflect.Descriptor instead. +func (*StatusResponse_Resources_Storage) Descriptor() ([]byte, []int) { + return file_supernode_supernode_proto_rawDescGZIP(), []int{4, 0, 2} +} + +func (x *StatusResponse_Resources_Storage) GetPath() string { + if x != nil { + return x.Path } return "" } -func (x *StatusResponse_ServiceTasks) GetTaskIds() []string { +func (x *StatusResponse_Resources_Storage) GetTotalBytes() uint64 { if x != nil { - return x.TaskIds + return x.TotalBytes } - return nil + return 0 } -func (x *StatusResponse_ServiceTasks) GetTaskCount() int32 { +func (x *StatusResponse_Resources_Storage) GetUsedBytes() uint64 { if x != nil { - return x.TaskCount + return x.UsedBytes + } + return 0 +} + +func (x *StatusResponse_Resources_Storage) GetAvailableBytes() uint64 { + if x != nil { + return x.AvailableBytes + } + return 0 +} + +func (x *StatusResponse_Resources_Storage) GetUsagePercent() float64 { + if x != nil { + return x.UsagePercent } return 0 } @@ -472,58 +705,101 @@ var file_supernode_supernode_proto_rawDesc = []byte{ 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0x90, 0x04, 0x0a, 0x0e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, - 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x75, + 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0xc7, 0x09, 0x0a, 0x0e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x70, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0d, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, + 0x41, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, + 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x52, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, + 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x12, 0x3b, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, + 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x72, 0x61, 0x6e, + 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x1a, 0x82, 0x05, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x39, + 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x50, 0x55, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, - 0x38, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x0d, 0x72, 0x75, 0x6e, - 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, - 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x03, 0x43, 0x50, 0x55, 0x12, 0x14, - 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, - 0x6e, 0x67, 0x1a, 0x6d, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x72, - 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x75, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, - 0x63, 0x1a, 0x6b, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x61, 0x73, 0x6b, - 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x73, 0x12, - 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xd7, - 0x01, 0x0a, 0x10, 0x53, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x18, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x75, 0x70, - 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x69, 0x0a, - 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2f, - 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x43, 0x50, 0x55, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x42, 0x0a, 0x06, 0x6d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x54, 0x0a, + 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, + 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x40, + 0x0a, 0x03, 0x43, 0x50, 0x55, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x70, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x72, 0x65, 0x73, + 0x1a, 0x84, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x67, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x47, 0x62, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x67, + 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x75, 0x73, 0x65, 0x64, 0x47, 0x62, 0x12, + 0x21, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x67, 0x62, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x47, 0x62, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x1a, 0xab, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x64, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x75, 0x73, + 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x75, 0x73, 0x61, 0x67, 0x65, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x1a, 0x6b, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x61, 0x73, 0x6b, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, 0x6b, + 0x49, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x1a, 0x51, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x65, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, + 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x32, 0xd7, 0x01, 0x0a, 0x10, 0x53, 0x75, 0x70, 0x65, 0x72, 0x6e, + 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, + 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x69, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, + 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x75, + 0x6d, 0x65, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x6e, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -538,31 +814,37 @@ func file_supernode_supernode_proto_rawDescGZIP() []byte { return file_supernode_supernode_proto_rawDescData } -var file_supernode_supernode_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_supernode_supernode_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_supernode_supernode_proto_goTypes = []any{ - (*StatusRequest)(nil), // 0: supernode.StatusRequest - (*ListServicesRequest)(nil), // 1: supernode.ListServicesRequest - (*ListServicesResponse)(nil), // 2: supernode.ListServicesResponse - (*ServiceInfo)(nil), // 3: supernode.ServiceInfo - (*StatusResponse)(nil), // 4: supernode.StatusResponse - (*StatusResponse_CPU)(nil), // 5: supernode.StatusResponse.CPU - (*StatusResponse_Memory)(nil), // 6: supernode.StatusResponse.Memory - (*StatusResponse_ServiceTasks)(nil), // 7: supernode.StatusResponse.ServiceTasks + (*StatusRequest)(nil), // 0: supernode.StatusRequest + (*ListServicesRequest)(nil), // 1: supernode.ListServicesRequest + (*ListServicesResponse)(nil), // 2: supernode.ListServicesResponse + (*ServiceInfo)(nil), // 3: supernode.ServiceInfo + (*StatusResponse)(nil), // 4: supernode.StatusResponse + (*StatusResponse_Resources)(nil), // 5: supernode.StatusResponse.Resources + (*StatusResponse_ServiceTasks)(nil), // 6: supernode.StatusResponse.ServiceTasks + (*StatusResponse_Network)(nil), // 7: supernode.StatusResponse.Network + (*StatusResponse_Resources_CPU)(nil), // 8: supernode.StatusResponse.Resources.CPU + (*StatusResponse_Resources_Memory)(nil), // 9: supernode.StatusResponse.Resources.Memory + (*StatusResponse_Resources_Storage)(nil), // 10: supernode.StatusResponse.Resources.Storage } var file_supernode_supernode_proto_depIdxs = []int32{ - 3, // 0: supernode.ListServicesResponse.services:type_name -> supernode.ServiceInfo - 5, // 1: supernode.StatusResponse.cpu:type_name -> supernode.StatusResponse.CPU - 6, // 2: supernode.StatusResponse.memory:type_name -> supernode.StatusResponse.Memory - 7, // 3: supernode.StatusResponse.running_tasks:type_name -> supernode.StatusResponse.ServiceTasks - 0, // 4: supernode.SupernodeService.GetStatus:input_type -> supernode.StatusRequest - 1, // 5: supernode.SupernodeService.ListServices:input_type -> supernode.ListServicesRequest - 4, // 6: supernode.SupernodeService.GetStatus:output_type -> supernode.StatusResponse - 2, // 7: supernode.SupernodeService.ListServices:output_type -> supernode.ListServicesResponse - 6, // [6:8] is the sub-list for method output_type - 4, // [4:6] 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 + 3, // 0: supernode.ListServicesResponse.services:type_name -> supernode.ServiceInfo + 5, // 1: supernode.StatusResponse.resources:type_name -> supernode.StatusResponse.Resources + 6, // 2: supernode.StatusResponse.running_tasks:type_name -> supernode.StatusResponse.ServiceTasks + 7, // 3: supernode.StatusResponse.network:type_name -> supernode.StatusResponse.Network + 8, // 4: supernode.StatusResponse.Resources.cpu:type_name -> supernode.StatusResponse.Resources.CPU + 9, // 5: supernode.StatusResponse.Resources.memory:type_name -> supernode.StatusResponse.Resources.Memory + 10, // 6: supernode.StatusResponse.Resources.storage_volumes:type_name -> supernode.StatusResponse.Resources.Storage + 0, // 7: supernode.SupernodeService.GetStatus:input_type -> supernode.StatusRequest + 1, // 8: supernode.SupernodeService.ListServices:input_type -> supernode.ListServicesRequest + 4, // 9: supernode.SupernodeService.GetStatus:output_type -> supernode.StatusResponse + 2, // 10: supernode.SupernodeService.ListServices:output_type -> supernode.ListServicesResponse + 9, // [9:11] is the sub-list for method output_type + 7, // [7:9] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_supernode_supernode_proto_init() } @@ -576,7 +858,7 @@ func file_supernode_supernode_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_supernode_supernode_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/supernode/supernode.swagger.json b/gen/supernode/supernode.swagger.json index 779075de..af023816 100644 --- a/gen/supernode/supernode.swagger.json +++ b/gen/supernode/supernode.swagger.json @@ -62,38 +62,113 @@ } }, "definitions": { - "StatusResponseCPU": { + "ResourcesCPU": { "type": "object", "properties": { - "usage": { - "type": "string" + "usagePercent": { + "type": "number", + "format": "double", + "title": "CPU usage percentage (0-100)" }, - "remaining": { - "type": "string" + "cores": { + "type": "integer", + "format": "int32", + "title": "Number of CPU cores" } } }, - "StatusResponseMemory": { + "ResourcesMemory": { "type": "object", "properties": { - "total": { + "totalGb": { + "type": "number", + "format": "double", + "title": "Total memory in GB" + }, + "usedGb": { + "type": "number", + "format": "double", + "title": "Used memory in GB" + }, + "availableGb": { + "type": "number", + "format": "double", + "title": "Available memory in GB" + }, + "usagePercent": { + "type": "number", + "format": "double", + "title": "Memory usage percentage (0-100)" + } + } + }, + "ResourcesStorage": { + "type": "object", + "properties": { + "path": { + "type": "string", + "title": "Storage path being monitored" + }, + "totalBytes": { "type": "string", "format": "uint64" }, - "used": { + "usedBytes": { "type": "string", "format": "uint64" }, - "available": { + "availableBytes": { "type": "string", "format": "uint64" }, - "usedPerc": { + "usagePercent": { "type": "number", - "format": "double" + "format": "double", + "title": "Storage usage percentage (0-100)" } } }, + "StatusResponseNetwork": { + "type": "object", + "properties": { + "peersCount": { + "type": "integer", + "format": "int32", + "title": "Number of connected peers in P2P network" + }, + "peerAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "title": "List of connected peer addresses (optional, may be empty for privacy)" + } + }, + "title": "Network information" + }, + "StatusResponseResources": { + "type": "object", + "properties": { + "cpu": { + "$ref": "#/definitions/ResourcesCPU" + }, + "memory": { + "$ref": "#/definitions/ResourcesMemory" + }, + "storageVolumes": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/ResourcesStorage" + } + }, + "hardwareSummary": { + "type": "string", + "title": "Formatted hardware summary (e.g., \"8 cores / 32GB RAM\")" + } + }, + "title": "System resource information" + }, "StatusResponseServiceTasks": { "type": "object", "properties": { @@ -174,11 +249,17 @@ "supernodeStatusResponse": { "type": "object", "properties": { - "cpu": { - "$ref": "#/definitions/StatusResponseCPU" + "version": { + "type": "string", + "title": "Supernode version" }, - "memory": { - "$ref": "#/definitions/StatusResponseMemory" + "uptimeSeconds": { + "type": "string", + "format": "uint64", + "title": "Uptime in seconds" + }, + "resources": { + "$ref": "#/definitions/StatusResponseResources" }, "runningTasks": { "type": "array", @@ -194,9 +275,22 @@ "type": "string" }, "title": "All registered/available services" + }, + "network": { + "$ref": "#/definitions/StatusResponseNetwork", + "title": "P2P network information" + }, + "rank": { + "type": "integer", + "format": "int32", + "title": "Rank in the top supernodes list (0 if not in top list)" + }, + "ipAddress": { + "type": "string", + "title": "Supernode IP address with port (e.g., \"192.168.1.1:4445\")" } }, - "description": "The StatusResponse represents system status." + "title": "The StatusResponse represents system status with clear organization" } } } diff --git a/proto/supernode/supernode.proto b/proto/supernode/supernode.proto index 9e7dfb09..d0582156 100644 --- a/proto/supernode/supernode.proto +++ b/proto/supernode/supernode.proto @@ -33,29 +33,56 @@ message ServiceInfo { repeated string methods = 2; } -// The StatusResponse represents system status. +// The StatusResponse represents system status with clear organization message StatusResponse { - message CPU { - string usage = 1; - string remaining = 2; - } - - message Memory { - uint64 total = 1; - uint64 used = 2; - uint64 available = 3; - double used_perc = 4; + string version = 1; // Supernode version + uint64 uptime_seconds = 2; // Uptime in seconds + + // System resource information + message Resources { + message CPU { + double usage_percent = 1; // CPU usage percentage (0-100) + int32 cores = 2; // Number of CPU cores + } + + message Memory { + double total_gb = 1; // Total memory in GB + double used_gb = 2; // Used memory in GB + double available_gb = 3; // Available memory in GB + double usage_percent = 4; // Memory usage percentage (0-100) + } + + message Storage { + string path = 1; // Storage path being monitored + uint64 total_bytes = 2; + uint64 used_bytes = 3; + uint64 available_bytes = 4; + double usage_percent = 5; // Storage usage percentage (0-100) + } + + CPU cpu = 1; + Memory memory = 2; + repeated Storage storage_volumes = 3; + string hardware_summary = 4; // Formatted hardware summary (e.g., "8 cores / 32GB RAM") } - + // ServiceTasks contains task information for a specific service message ServiceTasks { string service_name = 1; repeated string task_ids = 2; int32 task_count = 3; } - - CPU cpu = 1; - Memory memory = 2; - repeated ServiceTasks running_tasks = 3; // Services with currently running tasks - repeated string registered_services = 4; // All registered/available services + + // Network information + message Network { + int32 peers_count = 1; // Number of connected peers in P2P network + repeated string peer_addresses = 2; // List of connected peer addresses (optional, may be empty for privacy) + } + + Resources resources = 3; + repeated ServiceTasks running_tasks = 4; // Services with currently running tasks + repeated string registered_services = 5; // All registered/available services + Network network = 6; // P2P network information + int32 rank = 7; // Rank in the top supernodes list (0 if not in top list) + string ip_address = 8; // Supernode IP address with port (e.g., "192.168.1.1:4445") } \ No newline at end of file diff --git a/sdk/adapters/supernodeservice/adapter.go b/sdk/adapters/supernodeservice/adapter.go index 39000f37..0c2d12f3 100644 --- a/sdk/adapters/supernodeservice/adapter.go +++ b/sdk/adapters/supernodeservice/adapter.go @@ -343,25 +343,45 @@ func toSdkEvent(e cascade.SupernodeEventType) event.EventType { func toSdkSupernodeStatus(resp *supernode.StatusResponse) *SupernodeStatusresponse { result := &SupernodeStatusresponse{} + result.Version = resp.Version + result.UptimeSeconds = resp.UptimeSeconds + + // Convert Resources data + if resp.Resources != nil { + // Convert CPU data + if resp.Resources.Cpu != nil { + result.Resources.CPU.UsagePercent = resp.Resources.Cpu.UsagePercent + result.Resources.CPU.Cores = resp.Resources.Cpu.Cores + } - // Convert CPU data - if resp.Cpu != nil { - result.CPU.Usage = resp.Cpu.Usage - result.CPU.Remaining = resp.Cpu.Remaining - } + // Convert Memory data + if resp.Resources.Memory != nil { + result.Resources.Memory.TotalGB = resp.Resources.Memory.TotalGb + result.Resources.Memory.UsedGB = resp.Resources.Memory.UsedGb + result.Resources.Memory.AvailableGB = resp.Resources.Memory.AvailableGb + result.Resources.Memory.UsagePercent = resp.Resources.Memory.UsagePercent + } - // Convert Memory data - if resp.Memory != nil { - result.Memory.Total = resp.Memory.Total - result.Memory.Used = resp.Memory.Used - result.Memory.Available = resp.Memory.Available - result.Memory.UsedPerc = resp.Memory.UsedPerc + // Convert Storage data + result.Resources.Storage = make([]StorageInfo, 0, len(resp.Resources.StorageVolumes)) + for _, storage := range resp.Resources.StorageVolumes { + result.Resources.Storage = append(result.Resources.Storage, StorageInfo{ + Path: storage.Path, + TotalBytes: storage.TotalBytes, + UsedBytes: storage.UsedBytes, + AvailableBytes: storage.AvailableBytes, + UsagePercent: storage.UsagePercent, + }) + } + + // Copy hardware summary + result.Resources.HardwareSummary = resp.Resources.HardwareSummary } // Convert RunningTasks data - result.Services = make([]ServiceTasks, 0, len(resp.RunningTasks)) + result.RunningTasks = make([]ServiceTasks, 0, len(resp.RunningTasks)) for _, service := range resp.RunningTasks { - result.Services = append(result.Services, ServiceTasks{ + result.RunningTasks = append(result.RunningTasks, ServiceTasks{ ServiceName: service.ServiceName, TaskIDs: service.TaskIds, TaskCount: service.TaskCount, @@ -369,8 +389,19 @@ func toSdkSupernodeStatus(resp *supernode.StatusResponse) *SupernodeStatusrespon } // Convert RegisteredServices data - result.AvailableServices = make([]string, len(resp.RegisteredServices)) - copy(result.AvailableServices, resp.RegisteredServices) + result.RegisteredServices = make([]string, len(resp.RegisteredServices)) + copy(result.RegisteredServices, resp.RegisteredServices) + + // Convert Network data + if resp.Network != nil { + result.Network.PeersCount = resp.Network.PeersCount + result.Network.PeerAddresses = make([]string, len(resp.Network.PeerAddresses)) + copy(result.Network.PeerAddresses, resp.Network.PeerAddresses) + } + + // Copy rank and IP address + result.Rank = resp.Rank + result.IPAddress = resp.IpAddress return result } diff --git a/sdk/adapters/supernodeservice/types.go b/sdk/adapters/supernodeservice/types.go index c6b88131..0b2921b6 100644 --- a/sdk/adapters/supernodeservice/types.go +++ b/sdk/adapters/supernodeservice/types.go @@ -35,19 +35,40 @@ type ServiceTasks struct { TaskCount int32 } +// StorageInfo contains storage metrics for a specific path +type StorageInfo struct { + Path string + TotalBytes uint64 + UsedBytes uint64 + AvailableBytes uint64 + UsagePercent float64 +} + type SupernodeStatusresponse struct { - CPU struct { - Usage string - Remaining string + Version string // Supernode version + UptimeSeconds uint64 // Uptime in seconds + Resources struct { + CPU struct { + UsagePercent float64 + Cores int32 + } + Memory struct { + TotalGB float64 + UsedGB float64 + AvailableGB float64 + UsagePercent float64 + } + Storage []StorageInfo + HardwareSummary string // Formatted hardware summary } - Memory struct { - Total uint64 - Used uint64 - Available uint64 - UsedPerc float64 + RunningTasks []ServiceTasks // Services with running tasks + RegisteredServices []string // All available service names + Network struct { + PeersCount int32 // Number of connected peers + PeerAddresses []string // List of peer addresses } - Services []ServiceTasks - AvailableServices []string + Rank int32 // Rank in top supernodes list (0 if not in top list) + IPAddress string // Supernode IP address with port } type CascadeSupernodeDownloadRequest struct { ActionID string diff --git a/supernode/cmd/start.go b/supernode/cmd/start.go index 7a991a40..bc9e207d 100644 --- a/supernode/cmd/start.go +++ b/supernode/cmd/start.go @@ -101,8 +101,11 @@ The supernode will connect to the Lumera network and begin participating in the // Create cascade action server cascadeActionServer := cascade.NewCascadeActionServer(cService) + // Set the version in the status service package + supernodeService.Version = Version + // Create supernode status service - statusService := supernodeService.NewSupernodeStatusService() + statusService := supernodeService.NewSupernodeStatusService(*p2pService, lumeraClient, appConfig) statusService.RegisterTaskProvider(cService) // Create supernode server diff --git a/supernode/config/config.go b/supernode/config/config.go index 1f236df7..1ae876d0 100644 --- a/supernode/config/config.go +++ b/supernode/config/config.go @@ -21,9 +21,9 @@ type SupernodeConfig struct { type KeyringConfig struct { Backend string `yaml:"backend"` Dir string `yaml:"dir"` - PassPlain string `yaml:"passphrase_plain"` - PassEnv string `yaml:"passphrase_env"` - PassFile string `yaml:"passphrase_file"` + PassPlain string `yaml:"passphrase_plain,omitempty"` + PassEnv string `yaml:"passphrase_env,omitempty"` + PassFile string `yaml:"passphrase_file,omitempty"` } type P2PConfig struct { diff --git a/supernode/node/supernode/gateway/swagger.json b/supernode/node/supernode/gateway/swagger.json index 779075de..af023816 100644 --- a/supernode/node/supernode/gateway/swagger.json +++ b/supernode/node/supernode/gateway/swagger.json @@ -62,38 +62,113 @@ } }, "definitions": { - "StatusResponseCPU": { + "ResourcesCPU": { "type": "object", "properties": { - "usage": { - "type": "string" + "usagePercent": { + "type": "number", + "format": "double", + "title": "CPU usage percentage (0-100)" }, - "remaining": { - "type": "string" + "cores": { + "type": "integer", + "format": "int32", + "title": "Number of CPU cores" } } }, - "StatusResponseMemory": { + "ResourcesMemory": { "type": "object", "properties": { - "total": { + "totalGb": { + "type": "number", + "format": "double", + "title": "Total memory in GB" + }, + "usedGb": { + "type": "number", + "format": "double", + "title": "Used memory in GB" + }, + "availableGb": { + "type": "number", + "format": "double", + "title": "Available memory in GB" + }, + "usagePercent": { + "type": "number", + "format": "double", + "title": "Memory usage percentage (0-100)" + } + } + }, + "ResourcesStorage": { + "type": "object", + "properties": { + "path": { + "type": "string", + "title": "Storage path being monitored" + }, + "totalBytes": { "type": "string", "format": "uint64" }, - "used": { + "usedBytes": { "type": "string", "format": "uint64" }, - "available": { + "availableBytes": { "type": "string", "format": "uint64" }, - "usedPerc": { + "usagePercent": { "type": "number", - "format": "double" + "format": "double", + "title": "Storage usage percentage (0-100)" } } }, + "StatusResponseNetwork": { + "type": "object", + "properties": { + "peersCount": { + "type": "integer", + "format": "int32", + "title": "Number of connected peers in P2P network" + }, + "peerAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "title": "List of connected peer addresses (optional, may be empty for privacy)" + } + }, + "title": "Network information" + }, + "StatusResponseResources": { + "type": "object", + "properties": { + "cpu": { + "$ref": "#/definitions/ResourcesCPU" + }, + "memory": { + "$ref": "#/definitions/ResourcesMemory" + }, + "storageVolumes": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/ResourcesStorage" + } + }, + "hardwareSummary": { + "type": "string", + "title": "Formatted hardware summary (e.g., \"8 cores / 32GB RAM\")" + } + }, + "title": "System resource information" + }, "StatusResponseServiceTasks": { "type": "object", "properties": { @@ -174,11 +249,17 @@ "supernodeStatusResponse": { "type": "object", "properties": { - "cpu": { - "$ref": "#/definitions/StatusResponseCPU" + "version": { + "type": "string", + "title": "Supernode version" }, - "memory": { - "$ref": "#/definitions/StatusResponseMemory" + "uptimeSeconds": { + "type": "string", + "format": "uint64", + "title": "Uptime in seconds" + }, + "resources": { + "$ref": "#/definitions/StatusResponseResources" }, "runningTasks": { "type": "array", @@ -194,9 +275,22 @@ "type": "string" }, "title": "All registered/available services" + }, + "network": { + "$ref": "#/definitions/StatusResponseNetwork", + "title": "P2P network information" + }, + "rank": { + "type": "integer", + "format": "int32", + "title": "Rank in the top supernodes list (0 if not in top list)" + }, + "ipAddress": { + "type": "string", + "title": "Supernode IP address with port (e.g., \"192.168.1.1:4445\")" } }, - "description": "The StatusResponse represents system status." + "title": "The StatusResponse represents system status with clear organization" } } } diff --git a/supernode/node/supernode/server/status_server.go b/supernode/node/supernode/server/status_server.go index e46add36..37f22b37 100644 --- a/supernode/node/supernode/server/status_server.go +++ b/supernode/node/supernode/server/status_server.go @@ -60,18 +60,42 @@ func (s *SupernodeServer) GetStatus(ctx context.Context, req *pb.StatusRequest) // Convert to protobuf response response := &pb.StatusResponse{ - Cpu: &pb.StatusResponse_CPU{ - Usage: status.CPU.Usage, - Remaining: status.CPU.Remaining, - }, - Memory: &pb.StatusResponse_Memory{ - Total: status.Memory.Total, - Used: status.Memory.Used, - Available: status.Memory.Available, - UsedPerc: status.Memory.UsedPerc, + Version: status.Version, + UptimeSeconds: status.UptimeSeconds, + Resources: &pb.StatusResponse_Resources{ + Cpu: &pb.StatusResponse_Resources_CPU{ + UsagePercent: status.Resources.CPU.UsagePercent, + Cores: status.Resources.CPU.Cores, + }, + Memory: &pb.StatusResponse_Resources_Memory{ + TotalGb: status.Resources.Memory.TotalGB, + UsedGb: status.Resources.Memory.UsedGB, + AvailableGb: status.Resources.Memory.AvailableGB, + UsagePercent: status.Resources.Memory.UsagePercent, + }, + StorageVolumes: make([]*pb.StatusResponse_Resources_Storage, 0, len(status.Resources.Storage)), + HardwareSummary: status.Resources.HardwareSummary, }, RunningTasks: make([]*pb.StatusResponse_ServiceTasks, 0, len(status.RunningTasks)), RegisteredServices: status.RegisteredServices, + Network: &pb.StatusResponse_Network{ + PeersCount: status.Network.PeersCount, + PeerAddresses: status.Network.PeerAddresses, + }, + Rank: status.Rank, + IpAddress: status.IPAddress, + } + + // Convert storage information + for _, storage := range status.Resources.Storage { + storageInfo := &pb.StatusResponse_Resources_Storage{ + Path: storage.Path, + TotalBytes: storage.TotalBytes, + UsedBytes: storage.UsedBytes, + AvailableBytes: storage.AvailableBytes, + UsagePercent: storage.UsagePercent, + } + response.Resources.StorageVolumes = append(response.Resources.StorageVolumes, storageInfo) } // Convert service tasks diff --git a/supernode/node/supernode/server/status_server_test.go b/supernode/node/supernode/server/status_server_test.go index 712d9a1a..908e0753 100644 --- a/supernode/node/supernode/server/status_server_test.go +++ b/supernode/node/supernode/server/status_server_test.go @@ -16,7 +16,7 @@ func TestSupernodeServer_GetStatus(t *testing.T) { ctx := context.Background() // Create status service - statusService := supernode.NewSupernodeStatusService() + statusService := supernode.NewSupernodeStatusService(nil, nil, nil) // Create server server := NewSupernodeServer(statusService) @@ -27,22 +27,54 @@ func TestSupernodeServer_GetStatus(t *testing.T) { assert.NotNil(t, resp) // Check basic structure - assert.NotNil(t, resp.Cpu) - assert.NotNil(t, resp.Memory) - assert.NotEmpty(t, resp.Cpu.Usage) - assert.NotEmpty(t, resp.Cpu.Remaining) - assert.True(t, resp.Memory.Total > 0) + assert.NotNil(t, resp.Resources) + assert.NotNil(t, resp.Resources.Cpu) + assert.NotNil(t, resp.Resources.Memory) + assert.NotNil(t, resp.RunningTasks) + assert.NotNil(t, resp.RegisteredServices) + + // Check version field + assert.NotEmpty(t, resp.Version) + + // Check uptime field + assert.True(t, resp.UptimeSeconds >= 0) + + // Check CPU metrics + assert.True(t, resp.Resources.Cpu.UsagePercent >= 0) + assert.True(t, resp.Resources.Cpu.UsagePercent <= 100) + assert.True(t, resp.Resources.Cpu.Cores >= 0) + + // Check Memory metrics (now in GB) + assert.True(t, resp.Resources.Memory.TotalGb > 0) + assert.True(t, resp.Resources.Memory.UsagePercent >= 0) + assert.True(t, resp.Resources.Memory.UsagePercent <= 100) + + // Check hardware summary + if resp.Resources.Cpu.Cores > 0 && resp.Resources.Memory.TotalGb > 0 { + assert.NotEmpty(t, resp.Resources.HardwareSummary) + } + + // Check Storage (should have default root filesystem) + assert.NotEmpty(t, resp.Resources.StorageVolumes) + assert.Equal(t, "/", resp.Resources.StorageVolumes[0].Path) // Should have no services initially assert.Empty(t, resp.RunningTasks) assert.Empty(t, resp.RegisteredServices) + + // Check new fields have default values + assert.NotNil(t, resp.Network) + assert.Equal(t, int32(0), resp.Network.PeersCount) + assert.Empty(t, resp.Network.PeerAddresses) + assert.Equal(t, int32(0), resp.Rank) + assert.Empty(t, resp.IpAddress) } func TestSupernodeServer_GetStatusWithService(t *testing.T) { ctx := context.Background() // Create status service - statusService := supernode.NewSupernodeStatusService() + statusService := supernode.NewSupernodeStatusService(nil, nil, nil) // Add a mock task provider mockProvider := &common.MockTaskProvider{ @@ -72,7 +104,7 @@ func TestSupernodeServer_GetStatusWithService(t *testing.T) { } func TestSupernodeServer_Desc(t *testing.T) { - statusService := supernode.NewSupernodeStatusService() + statusService := supernode.NewSupernodeStatusService(nil, nil, nil) server := NewSupernodeServer(statusService) desc := server.Desc() diff --git a/supernode/services/cascade/download.go b/supernode/services/cascade/download.go index ddd80516..f9823a00 100644 --- a/supernode/services/cascade/download.go +++ b/supernode/services/cascade/download.go @@ -188,8 +188,6 @@ func (task *CascadeRegistrationTask) streamDownloadEvent(eventType SupernodeEven FilePath: filePath, DownloadedDir: tmpDir, }) - - return } // parseIndexFile parses compressed index file to extract IndexFile structure diff --git a/supernode/services/cascade/interfaces.go b/supernode/services/cascade/interfaces.go index 4885648f..e782bc23 100644 --- a/supernode/services/cascade/interfaces.go +++ b/supernode/services/cascade/interfaces.go @@ -2,8 +2,6 @@ package cascade import ( "context" - - "github.com/LumeraProtocol/supernode/supernode/services/common/supernode" ) // CascadeServiceFactory defines an interface to create cascade tasks @@ -16,7 +14,6 @@ type CascadeServiceFactory interface { // CascadeTask interface defines operations for cascade registration and data management type CascadeTask interface { Register(ctx context.Context, req *RegisterRequest, send func(resp *RegisterResponse) error) error - GetStatus(ctx context.Context) (supernode.StatusResponse, error) Download(ctx context.Context, req *DownloadRequest, send func(resp *DownloadResponse) error) error CleanupDownload(ctx context.Context, actionID string) error } diff --git a/supernode/services/cascade/mocks/cascade_interfaces_mock.go b/supernode/services/cascade/mocks/cascade_interfaces_mock.go index eae5b750..3552e517 100644 --- a/supernode/services/cascade/mocks/cascade_interfaces_mock.go +++ b/supernode/services/cascade/mocks/cascade_interfaces_mock.go @@ -14,7 +14,6 @@ import ( reflect "reflect" cascade "github.com/LumeraProtocol/supernode/supernode/services/cascade" - supernode "github.com/LumeraProtocol/supernode/supernode/services/common/supernode" gomock "go.uber.org/mock/gomock" ) @@ -108,21 +107,6 @@ func (mr *MockCascadeTaskMockRecorder) Download(ctx, req, send any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Download", reflect.TypeOf((*MockCascadeTask)(nil).Download), ctx, req, send) } -// GetStatus mocks base method. -func (m *MockCascadeTask) GetStatus(ctx context.Context) (supernode.StatusResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStatus", ctx) - ret0, _ := ret[0].(supernode.StatusResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetStatus indicates an expected call of GetStatus. -func (mr *MockCascadeTaskMockRecorder) GetStatus(ctx any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatus", reflect.TypeOf((*MockCascadeTask)(nil).GetStatus), ctx) -} - // Register mocks base method. func (m *MockCascadeTask) Register(ctx context.Context, req *cascade.RegisterRequest, send func(*cascade.RegisterResponse) error) error { m.ctrl.T.Helper() diff --git a/supernode/services/cascade/status.go b/supernode/services/cascade/status.go index f974ba36..677e3efb 100644 --- a/supernode/services/cascade/status.go +++ b/supernode/services/cascade/status.go @@ -12,14 +12,11 @@ type StatusResponse = supernode.StatusResponse // GetStatus delegates to the common supernode status service func (service *CascadeService) GetStatus(ctx context.Context) (StatusResponse, error) { // Create a status service and register the cascade service as a task provider - statusService := supernode.NewSupernodeStatusService() + // Pass nil for optional dependencies (P2P, lumera client, and config) + // as cascade service doesn't have access to them in this context + statusService := supernode.NewSupernodeStatusService(nil, nil, nil) statusService.RegisterTaskProvider(service) // Get the status from the common service return statusService.GetStatus(ctx) } - -// GetStatus method for task interface compatibility -func (task *CascadeRegistrationTask) GetStatus(ctx context.Context) (StatusResponse, error) { - return task.CascadeService.GetStatus(ctx) -} diff --git a/supernode/services/cascade/status_test.go b/supernode/services/cascade/status_test.go index 543bdf90..b12d5bb4 100644 --- a/supernode/services/cascade/status_test.go +++ b/supernode/services/cascade/status_test.go @@ -64,17 +64,39 @@ func TestGetStatus(t *testing.T) { assert.NoError(t, err) + // Version check + assert.NotEmpty(t, resp.Version) + + // Uptime check + assert.True(t, resp.UptimeSeconds >= 0) + // CPU checks - assert.NotEmpty(t, resp.CPU.Usage) - assert.NotEmpty(t, resp.CPU.Remaining) + assert.True(t, resp.Resources.CPU.UsagePercent >= 0) + assert.True(t, resp.Resources.CPU.UsagePercent <= 100) + assert.True(t, resp.Resources.CPU.Cores >= 0) + + // Memory checks (now in GB) + assert.True(t, resp.Resources.Memory.TotalGB > 0) + assert.True(t, resp.Resources.Memory.UsedGB <= resp.Resources.Memory.TotalGB) + assert.True(t, resp.Resources.Memory.UsagePercent >= 0 && resp.Resources.Memory.UsagePercent <= 100) + + // Hardware summary check + if resp.Resources.CPU.Cores > 0 && resp.Resources.Memory.TotalGB > 0 { + assert.NotEmpty(t, resp.Resources.HardwareSummary) + } - // Memory checks - assert.True(t, resp.Memory.Total > 0) - assert.True(t, resp.Memory.Used <= resp.Memory.Total) - assert.True(t, resp.Memory.UsedPerc >= 0 && resp.Memory.UsedPerc <= 100) + // Storage checks - should have default root filesystem + assert.NotEmpty(t, resp.Resources.Storage) + assert.Equal(t, "/", resp.Resources.Storage[0].Path) // Registered services check assert.Contains(t, resp.RegisteredServices, "cascade") + + // Check new fields have default values (since service doesn't have access to P2P/lumera/config) + assert.Equal(t, int32(0), resp.Network.PeersCount) + assert.Empty(t, resp.Network.PeerAddresses) + assert.Equal(t, int32(0), resp.Rank) + assert.Empty(t, resp.IPAddress) // Task count check - look for cascade service in the running tasks list var cascadeService *supernode.ServiceTasks diff --git a/supernode/services/cascade/task.go b/supernode/services/cascade/task.go index 538214cf..66fe663f 100644 --- a/supernode/services/cascade/task.go +++ b/supernode/services/cascade/task.go @@ -2,6 +2,7 @@ package cascade import ( "context" + "github.com/LumeraProtocol/supernode/pkg/storage/files" "github.com/LumeraProtocol/supernode/supernode/services/common/base" "github.com/LumeraProtocol/supernode/supernode/services/common/storage" diff --git a/supernode/services/common/supernode/metrics.go b/supernode/services/common/supernode/metrics.go index 180dd8f4..09384948 100644 --- a/supernode/services/common/supernode/metrics.go +++ b/supernode/services/common/supernode/metrics.go @@ -2,11 +2,11 @@ package supernode import ( "context" - "fmt" "time" "github.com/LumeraProtocol/supernode/pkg/logtrace" "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/mem" ) @@ -19,18 +19,26 @@ func NewMetricsCollector() *MetricsCollector { } // CollectCPUMetrics gathers CPU usage information -// Returns usage and remaining percentages as formatted strings -func (m *MetricsCollector) CollectCPUMetrics(ctx context.Context) (usage, remaining string, err error) { +// Returns usage percentage as a float64 +func (m *MetricsCollector) CollectCPUMetrics(ctx context.Context) (float64, error) { percentages, err := cpu.Percent(time.Second, false) if err != nil { logtrace.Error(ctx, "failed to get cpu info", logtrace.Fields{logtrace.FieldError: err.Error()}) - return "", "", err + return 0, err } - usageFloat := percentages[0] - remainingFloat := 100 - usageFloat + return percentages[0], nil +} - return fmt.Sprintf("%.2f", usageFloat), fmt.Sprintf("%.2f", remainingFloat), nil +// GetCPUCores returns the number of CPU cores +func (m *MetricsCollector) GetCPUCores(ctx context.Context) (int32, error) { + cores, err := cpu.Counts(true) + if err != nil { + logtrace.Error(ctx, "failed to get cpu core count", logtrace.Fields{logtrace.FieldError: err.Error()}) + return 0, err + } + + return int32(cores), nil } // CollectMemoryMetrics gathers memory usage information @@ -44,3 +52,34 @@ func (m *MetricsCollector) CollectMemoryMetrics(ctx context.Context) (total, use return vmem.Total, vmem.Used, vmem.Available, vmem.UsedPercent, nil } + +// CollectStorageMetrics gathers storage usage information for specified paths +// If paths is empty, it will collect metrics for the root filesystem +func (m *MetricsCollector) CollectStorageMetrics(ctx context.Context, paths []string) []StorageInfo { + if len(paths) == 0 { + // Default to root filesystem + paths = []string{"/"} + } + + var storageInfos []StorageInfo + for _, path := range paths { + usage, err := disk.Usage(path) + if err != nil { + logtrace.Error(ctx, "failed to get storage info", logtrace.Fields{ + logtrace.FieldError: err.Error(), + "path": path, + }) + continue // Skip this path but continue with others + } + + storageInfos = append(storageInfos, StorageInfo{ + Path: path, + TotalBytes: usage.Total, + UsedBytes: usage.Used, + AvailableBytes: usage.Free, + UsagePercent: usage.UsedPercent, + }) + } + + return storageInfos +} diff --git a/supernode/services/common/supernode/service.go b/supernode/services/common/supernode/service.go index 4484e688..9a150d1e 100644 --- a/supernode/services/common/supernode/service.go +++ b/supernode/services/common/supernode/service.go @@ -2,22 +2,41 @@ package supernode import ( "context" + "fmt" + "time" + "github.com/LumeraProtocol/supernode/p2p" + "github.com/LumeraProtocol/supernode/p2p/kademlia" "github.com/LumeraProtocol/supernode/pkg/logtrace" + "github.com/LumeraProtocol/supernode/pkg/lumera" + "github.com/LumeraProtocol/supernode/supernode/config" ) +// Version is the supernode version, set by the main application +var Version = "dev" + // SupernodeStatusService provides centralized status information // by collecting system metrics and aggregating task information from registered services type SupernodeStatusService struct { taskProviders []TaskProvider // List of registered services that provide task information metrics *MetricsCollector // System metrics collector for CPU and memory stats + storagePaths []string // Paths to monitor for storage metrics + startTime time.Time // Service start time for uptime calculation + p2pService p2p.Client // P2P service for network information + lumeraClient lumera.Client // Lumera client for blockchain queries + config *config.Config // Supernode configuration } // NewSupernodeStatusService creates a new supernode status service instance -func NewSupernodeStatusService() *SupernodeStatusService { +func NewSupernodeStatusService(p2pService p2p.Client, lumeraClient lumera.Client, cfg *config.Config) *SupernodeStatusService { return &SupernodeStatusService{ taskProviders: make([]TaskProvider, 0), metrics: NewMetricsCollector(), + storagePaths: []string{"/"}, // Default to monitoring root filesystem + startTime: time.Now(), + p2pService: p2pService, + lumeraClient: lumeraClient, + config: cfg, } } @@ -37,24 +56,47 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, logtrace.Info(ctx, "status request received", fields) var resp StatusResponse + resp.Version = Version + + // Calculate uptime + resp.UptimeSeconds = uint64(time.Since(s.startTime).Seconds()) // Collect CPU metrics - cpuUsage, cpuRemaining, err := s.metrics.CollectCPUMetrics(ctx) + cpuUsage, err := s.metrics.CollectCPUMetrics(ctx) if err != nil { return resp, err } - resp.CPU.Usage = cpuUsage - resp.CPU.Remaining = cpuRemaining + resp.Resources.CPU.UsagePercent = cpuUsage + + // Get CPU cores + cpuCores, err := s.metrics.GetCPUCores(ctx) + if err != nil { + // Log error but continue - non-critical + logtrace.Error(ctx, "failed to get cpu cores", logtrace.Fields{logtrace.FieldError: err.Error()}) + cpuCores = 0 + } + resp.Resources.CPU.Cores = cpuCores // Collect memory metrics memTotal, memUsed, memAvailable, memUsedPerc, err := s.metrics.CollectMemoryMetrics(ctx) if err != nil { return resp, err } - resp.Memory.Total = memTotal - resp.Memory.Used = memUsed - resp.Memory.Available = memAvailable - resp.Memory.UsedPerc = memUsedPerc + + // Convert to GB + const bytesToGB = 1024 * 1024 * 1024 + resp.Resources.Memory.TotalGB = float64(memTotal) / bytesToGB + resp.Resources.Memory.UsedGB = float64(memUsed) / bytesToGB + resp.Resources.Memory.AvailableGB = float64(memAvailable) / bytesToGB + resp.Resources.Memory.UsagePercent = memUsedPerc + + // Generate hardware summary + if cpuCores > 0 && resp.Resources.Memory.TotalGB > 0 { + resp.Resources.HardwareSummary = fmt.Sprintf("%d cores / %.0fGB RAM", cpuCores, resp.Resources.Memory.TotalGB) + } + + // Collect storage metrics + resp.Resources.Storage = s.metrics.CollectStorageMetrics(ctx, s.storagePaths) // Collect service information from all registered providers resp.RunningTasks = make([]ServiceTasks, 0, len(s.taskProviders)) @@ -76,6 +118,69 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, resp.RunningTasks = append(resp.RunningTasks, serviceTask) } + // Initialize network info + resp.Network = NetworkInfo{ + PeersCount: 0, + PeerAddresses: []string{}, + } + + // Collect P2P network information + if s.p2pService != nil { + p2pStats, err := s.p2pService.Stats(ctx) + if err != nil { + // Log error but continue - non-critical + logtrace.Error(ctx, "failed to get p2p stats", logtrace.Fields{logtrace.FieldError: err.Error()}) + } else { + if dhtStats, ok := p2pStats["dht"].(map[string]interface{}); ok { + if peersCount, ok := dhtStats["peers_count"].(int); ok { + resp.Network.PeersCount = int32(peersCount) + } + + // Extract peer addresses + if peers, ok := dhtStats["peers"].([]*kademlia.Node); ok { + resp.Network.PeerAddresses = make([]string, 0, len(peers)) + for _, peer := range peers { + // Format peer address as "ID@IP:Port" + peerAddr := fmt.Sprintf("%s@%s:%d", string(peer.ID), peer.IP, peer.Port) + resp.Network.PeerAddresses = append(resp.Network.PeerAddresses, peerAddr) + } + } else { + resp.Network.PeerAddresses = []string{} + } + } + } + } + + // Calculate rank from top supernodes + if s.lumeraClient != nil && s.config != nil { + // Get current block height + blockInfo, err := s.lumeraClient.Node().GetLatestBlock(ctx) + if err != nil { + // Log error but continue - non-critical + logtrace.Error(ctx, "failed to get latest block", logtrace.Fields{logtrace.FieldError: err.Error()}) + } else { + // Get top supernodes for current block + topNodes, err := s.lumeraClient.SuperNode().GetTopSuperNodesForBlock(ctx, uint64(blockInfo.SdkBlock.Header.Height)) + if err != nil { + // Log error but continue - non-critical + logtrace.Error(ctx, "failed to get top supernodes", logtrace.Fields{logtrace.FieldError: err.Error()}) + } else { + // Find our rank + for idx, node := range topNodes.Supernodes { + if node.SupernodeAccount == s.config.SupernodeConfig.Identity { + resp.Rank = int32(idx + 1) // Rank starts from 1 + break + } + } + } + } + } + + // Set IP address from config + if s.config != nil { + resp.IPAddress = fmt.Sprintf("%s:%d", s.config.SupernodeConfig.IpAddress, s.config.P2PConfig.Port) + } + // Log summary statistics totalTasks := 0 for _, service := range resp.RunningTasks { @@ -83,13 +188,18 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, } logtrace.Info(ctx, "status data collected", logtrace.Fields{ - "cpu_usage": cpuUsage, - "cpu_remaining": cpuRemaining, - "mem_total": memTotal, - "mem_used": memUsed, - "mem_used%": memUsedPerc, - "service_count": len(resp.RunningTasks), - "total_tasks": totalTasks, + "cpu_usage%": cpuUsage, + "cpu_cores": cpuCores, + "mem_total_gb": resp.Resources.Memory.TotalGB, + "mem_used_gb": resp.Resources.Memory.UsedGB, + "mem_usage%": memUsedPerc, + "uptime_seconds": resp.UptimeSeconds, + "storage_volumes": len(resp.Resources.Storage), + "service_count": len(resp.RunningTasks), + "total_tasks": totalTasks, + "network_peers": resp.Network.PeersCount, + "rank": resp.Rank, + "ip_address": resp.IPAddress, }) return resp, nil diff --git a/supernode/services/common/supernode/service_test.go b/supernode/services/common/supernode/service_test.go index 5bc55cdc..5a3197ca 100644 --- a/supernode/services/common/supernode/service_test.go +++ b/supernode/services/common/supernode/service_test.go @@ -12,23 +12,47 @@ func TestSupernodeStatusService(t *testing.T) { ctx := context.Background() t.Run("empty service", func(t *testing.T) { - statusService := NewSupernodeStatusService() + statusService := NewSupernodeStatusService(nil, nil, nil) resp, err := statusService.GetStatus(ctx) assert.NoError(t, err) + + // Should have version info + assert.NotEmpty(t, resp.Version) + + // Should have uptime + assert.True(t, resp.UptimeSeconds >= 0) // Should have CPU and Memory info - assert.NotEmpty(t, resp.CPU.Usage) - assert.NotEmpty(t, resp.CPU.Remaining) - assert.True(t, resp.Memory.Total > 0) + assert.True(t, resp.Resources.CPU.UsagePercent >= 0) + assert.True(t, resp.Resources.CPU.UsagePercent <= 100) + assert.True(t, resp.Resources.CPU.Cores >= 0) + assert.True(t, resp.Resources.Memory.TotalGB > 0) + assert.True(t, resp.Resources.Memory.UsagePercent >= 0) + assert.True(t, resp.Resources.Memory.UsagePercent <= 100) + + // Should have hardware summary if cores and memory are available + if resp.Resources.CPU.Cores > 0 && resp.Resources.Memory.TotalGB > 0 { + assert.NotEmpty(t, resp.Resources.HardwareSummary) + } + + // Should have storage info (default root filesystem) + assert.NotEmpty(t, resp.Resources.Storage) + assert.Equal(t, "/", resp.Resources.Storage[0].Path) // Should have empty services list assert.Empty(t, resp.RunningTasks) assert.Empty(t, resp.RegisteredServices) + + // Should have default values for new fields + assert.Equal(t, int32(0), resp.Network.PeersCount) + assert.Empty(t, resp.Network.PeerAddresses) + assert.Equal(t, int32(0), resp.Rank) + assert.Empty(t, resp.IPAddress) }) t.Run("single service with tasks", func(t *testing.T) { - statusService := NewSupernodeStatusService() + statusService := NewSupernodeStatusService(nil, nil, nil) // Register a mock task provider mockProvider := &common.MockTaskProvider{ @@ -52,7 +76,7 @@ func TestSupernodeStatusService(t *testing.T) { }) t.Run("multiple services", func(t *testing.T) { - statusService := NewSupernodeStatusService() + statusService := NewSupernodeStatusService(nil, nil, nil) // Register multiple mock task providers cascadeProvider := &common.MockTaskProvider{ @@ -94,7 +118,7 @@ func TestSupernodeStatusService(t *testing.T) { }) t.Run("service with no tasks", func(t *testing.T) { - statusService := NewSupernodeStatusService() + statusService := NewSupernodeStatusService(nil, nil, nil) // Register a mock task provider with no tasks mockProvider := &common.MockTaskProvider{ diff --git a/supernode/services/common/supernode/types.go b/supernode/services/common/supernode/types.go index 77e71d35..fffeba8b 100644 --- a/supernode/services/common/supernode/types.go +++ b/supernode/services/common/supernode/types.go @@ -1,20 +1,47 @@ package supernode // StatusResponse represents the complete system status information -// including CPU usage, memory statistics, and service details +// with clear organization of resources and services type StatusResponse struct { - CPU struct { - Usage string // CPU usage percentage as string (e.g., "45.32") - Remaining string // Remaining CPU capacity as string (e.g., "54.68") - } - Memory struct { - Total uint64 // Total memory in bytes - Used uint64 // Used memory in bytes - Available uint64 // Available memory in bytes - UsedPerc float64 // Memory usage percentage (0-100) - } - RunningTasks []ServiceTasks // List of services with their running task counts - RegisteredServices []string // Names of all registered/available services + Version string // Supernode version + UptimeSeconds uint64 // Uptime in seconds + Resources Resources // System resource information + RunningTasks []ServiceTasks // Services with currently running tasks + RegisteredServices []string // All registered/available services + Network NetworkInfo // P2P network information + Rank int32 // Rank in the top supernodes list (0 if not in top list) + IPAddress string // Supernode IP address with port (e.g., "192.168.1.1:4445") +} + +// Resources contains system resource metrics +type Resources struct { + CPU CPUInfo // CPU usage information + Memory MemoryInfo // Memory usage information + Storage []StorageInfo // Storage volumes information + HardwareSummary string // Formatted hardware summary (e.g., "8 cores / 32GB RAM") +} + +// CPUInfo contains CPU usage metrics +type CPUInfo struct { + UsagePercent float64 // CPU usage percentage (0-100) + Cores int32 // Number of CPU cores +} + +// MemoryInfo contains memory usage metrics +type MemoryInfo struct { + TotalGB float64 // Total memory in GB + UsedGB float64 // Used memory in GB + AvailableGB float64 // Available memory in GB + UsagePercent float64 // Memory usage percentage (0-100) +} + +// StorageInfo contains storage metrics for a specific path +type StorageInfo struct { + Path string // Storage path being monitored + TotalBytes uint64 // Total storage in bytes + UsedBytes uint64 // Used storage in bytes + AvailableBytes uint64 // Available storage in bytes + UsagePercent float64 // Storage usage percentage (0-100) } // ServiceTasks contains task information for a specific service @@ -24,6 +51,12 @@ type ServiceTasks struct { TaskCount int32 // Total number of running tasks } +// NetworkInfo contains P2P network information +type NetworkInfo struct { + PeersCount int32 // Number of connected peers in P2P network + PeerAddresses []string // List of connected peer addresses (optional, may be empty for privacy) +} + // TaskProvider interface defines the contract for services to provide // their running task information to the status service type TaskProvider interface {