diff options
Diffstat (limited to 'weed/admin/dash/admin_data.go')
| -rw-r--r-- | weed/admin/dash/admin_data.go | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/weed/admin/dash/admin_data.go b/weed/admin/dash/admin_data.go new file mode 100644 index 000000000..ae218de75 --- /dev/null +++ b/weed/admin/dash/admin_data.go @@ -0,0 +1,280 @@ +package dash + +import ( + "context" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/seaweedfs/seaweedfs/weed/cluster" + "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/pb/master_pb" +) + +type AdminData struct { + Username string `json:"username"` + ClusterStatus string `json:"cluster_status"` + TotalVolumes int `json:"total_volumes"` + TotalFiles int64 `json:"total_files"` + TotalSize int64 `json:"total_size"` + MasterNodes []MasterNode `json:"master_nodes"` + VolumeServers []VolumeServer `json:"volume_servers"` + FilerNodes []FilerNode `json:"filer_nodes"` + DataCenters []DataCenter `json:"datacenters"` + LastUpdated time.Time `json:"last_updated"` + SystemHealth string `json:"system_health"` +} + +// Object Store Users management structures +type ObjectStoreUser struct { + Username string `json:"username"` + Email string `json:"email"` + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` + Permissions []string `json:"permissions"` +} + +type ObjectStoreUsersData struct { + Username string `json:"username"` + Users []ObjectStoreUser `json:"users"` + TotalUsers int `json:"total_users"` + LastUpdated time.Time `json:"last_updated"` +} + +// User management request structures +type CreateUserRequest struct { + Username string `json:"username" binding:"required"` + Email string `json:"email"` + Actions []string `json:"actions"` + GenerateKey bool `json:"generate_key"` +} + +type UpdateUserRequest struct { + Email string `json:"email"` + Actions []string `json:"actions"` +} + +type UpdateUserPoliciesRequest struct { + Actions []string `json:"actions" binding:"required"` +} + +type AccessKeyInfo struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` + CreatedAt time.Time `json:"created_at"` +} + +type UserDetails struct { + Username string `json:"username"` + Email string `json:"email"` + Actions []string `json:"actions"` + AccessKeys []AccessKeyInfo `json:"access_keys"` +} + +type FilerNode struct { + Address string `json:"address"` + DataCenter string `json:"datacenter"` + Rack string `json:"rack"` + Status string `json:"status"` + LastUpdated time.Time `json:"last_updated"` +} + +// GetAdminData retrieves admin data as a struct (for reuse by both JSON and HTML handlers) +func (s *AdminServer) GetAdminData(username string) (AdminData, error) { + if username == "" { + username = "admin" + } + + // Get cluster topology + topology, err := s.GetClusterTopology() + if err != nil { + glog.Errorf("Failed to get cluster topology: %v", err) + return AdminData{}, err + } + + // Get master nodes status + masterNodes := s.getMasterNodesStatus() + + // Get filer nodes status + filerNodes := s.getFilerNodesStatus() + + // Prepare admin data + adminData := AdminData{ + Username: username, + ClusterStatus: s.determineClusterStatus(topology, masterNodes), + TotalVolumes: topology.TotalVolumes, + TotalFiles: topology.TotalFiles, + TotalSize: topology.TotalSize, + MasterNodes: masterNodes, + VolumeServers: topology.VolumeServers, + FilerNodes: filerNodes, + DataCenters: topology.DataCenters, + LastUpdated: topology.UpdatedAt, + SystemHealth: s.determineSystemHealth(topology, masterNodes), + } + + return adminData, nil +} + +// ShowAdmin displays the main admin page (now uses GetAdminData) +func (s *AdminServer) ShowAdmin(c *gin.Context) { + username := c.GetString("username") + + adminData, err := s.GetAdminData(username) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get admin data: " + err.Error()}) + return + } + + // Return JSON for API calls + c.JSON(http.StatusOK, adminData) +} + +// ShowOverview displays cluster overview +func (s *AdminServer) ShowOverview(c *gin.Context) { + topology, err := s.GetClusterTopology() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, topology) +} + +// getMasterNodesStatus checks status of all master nodes +func (s *AdminServer) getMasterNodesStatus() []MasterNode { + var masterNodes []MasterNode + + // Since we have a single master address, create one entry + var isLeader bool = true // Assume leader since it's the only master we know about + var status string + + // Try to get leader info from this master + err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { + _, err := client.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{}) + if err != nil { + return err + } + // For now, assume this master is the leader since we can connect to it + isLeader = true + return nil + }) + + if err != nil { + status = "unreachable" + isLeader = false + } else { + status = "active" + } + + masterNodes = append(masterNodes, MasterNode{ + Address: s.masterAddress, + IsLeader: isLeader, + Status: status, + }) + + return masterNodes +} + +// getFilerNodesStatus checks status of all filer nodes using master's ListClusterNodes +func (s *AdminServer) getFilerNodesStatus() []FilerNode { + var filerNodes []FilerNode + + // Get filer nodes from master using ListClusterNodes + err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { + resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{ + ClientType: cluster.FilerType, + }) + if err != nil { + return err + } + + // Process each filer node + for _, node := range resp.ClusterNodes { + filerNodes = append(filerNodes, FilerNode{ + Address: node.Address, + DataCenter: node.DataCenter, + Rack: node.Rack, + Status: "active", // If it's in the cluster list, it's considered active + LastUpdated: time.Now(), + }) + } + + return nil + }) + + if err != nil { + glog.Errorf("Failed to get filer nodes from master %s: %v", s.masterAddress, err) + // Return empty list if we can't get filer info from master + return []FilerNode{} + } + + return filerNodes +} + +// determineClusterStatus analyzes cluster health +func (s *AdminServer) determineClusterStatus(topology *ClusterTopology, masters []MasterNode) string { + // Check if we have an active leader + hasActiveLeader := false + for _, master := range masters { + if master.IsLeader && master.Status == "active" { + hasActiveLeader = true + break + } + } + + if !hasActiveLeader { + return "critical" + } + + // Check volume server health + activeServers := 0 + for _, vs := range topology.VolumeServers { + if vs.Status == "active" { + activeServers++ + } + } + + if activeServers == 0 { + return "critical" + } else if activeServers < len(topology.VolumeServers) { + return "warning" + } + + return "healthy" +} + +// determineSystemHealth provides overall system health assessment +func (s *AdminServer) determineSystemHealth(topology *ClusterTopology, masters []MasterNode) string { + // Simple health calculation based on active components + totalComponents := len(masters) + len(topology.VolumeServers) + activeComponents := 0 + + for _, master := range masters { + if master.Status == "active" { + activeComponents++ + } + } + + for _, vs := range topology.VolumeServers { + if vs.Status == "active" { + activeComponents++ + } + } + + if totalComponents == 0 { + return "unknown" + } + + healthPercent := float64(activeComponents) / float64(totalComponents) * 100 + + if healthPercent >= 95 { + return "excellent" + } else if healthPercent >= 80 { + return "good" + } else if healthPercent >= 60 { + return "fair" + } else { + return "poor" + } +} |
