| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- 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"`
- TotalVolumes int `json:"total_volumes"`
- TotalFiles int64 `json:"total_files"`
- TotalSize int64 `json:"total_size"`
- VolumeSizeLimitMB uint64 `json:"volume_size_limit_mb"`
- MasterNodes []MasterNode `json:"master_nodes"`
- VolumeServers []VolumeServer `json:"volume_servers"`
- FilerNodes []FilerNode `json:"filer_nodes"`
- MessageBrokers []MessageBrokerNode `json:"message_brokers"`
- DataCenters []DataCenter `json:"datacenters"`
- LastUpdated time.Time `json:"last_updated"`
- // EC shard totals for dashboard
- TotalEcVolumes int `json:"total_ec_volumes"` // Total number of EC volumes across all servers
- TotalEcShards int `json:"total_ec_shards"` // Total number of EC shards across all servers
- }
- // 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"`
- LastUpdated time.Time `json:"last_updated"`
- }
- type MessageBrokerNode struct {
- Address string `json:"address"`
- DataCenter string `json:"datacenter"`
- Rack string `json:"rack"`
- 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 volume servers data with EC shard information
- volumeServersData, err := s.GetClusterVolumeServers()
- if err != nil {
- glog.Errorf("Failed to get cluster volume servers: %v", err)
- return AdminData{}, err
- }
- // Get master nodes status
- masterNodes := s.getMasterNodesStatus()
- // Get filer nodes status
- filerNodes := s.getFilerNodesStatus()
- // Get message broker nodes status
- messageBrokers := s.getMessageBrokerNodesStatus()
- // Get volume size limit from master configuration
- var volumeSizeLimitMB uint64 = 30000 // Default to 30GB
- err = s.WithMasterClient(func(client master_pb.SeaweedClient) error {
- resp, err := client.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{})
- if err != nil {
- return err
- }
- volumeSizeLimitMB = uint64(resp.VolumeSizeLimitMB)
- return nil
- })
- if err != nil {
- glog.Warningf("Failed to get volume size limit from master: %v", err)
- // Keep default value on error
- }
- // Calculate EC shard totals
- var totalEcVolumes, totalEcShards int
- ecVolumeSet := make(map[uint32]bool) // To avoid counting the same EC volume multiple times
- for _, vs := range volumeServersData.VolumeServers {
- totalEcShards += vs.EcShards
- // Count unique EC volumes across all servers
- for _, ecInfo := range vs.EcShardDetails {
- ecVolumeSet[ecInfo.VolumeID] = true
- }
- }
- totalEcVolumes = len(ecVolumeSet)
- // Prepare admin data
- adminData := AdminData{
- Username: username,
- TotalVolumes: topology.TotalVolumes,
- TotalFiles: topology.TotalFiles,
- TotalSize: topology.TotalSize,
- VolumeSizeLimitMB: volumeSizeLimitMB,
- MasterNodes: masterNodes,
- VolumeServers: volumeServersData.VolumeServers,
- FilerNodes: filerNodes,
- MessageBrokers: messageBrokers,
- DataCenters: topology.DataCenters,
- LastUpdated: topology.UpdatedAt,
- TotalEcVolumes: totalEcVolumes,
- TotalEcShards: totalEcShards,
- }
- 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
- // 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 {
- isLeader = false
- }
- currentMaster := s.masterClient.GetMaster(context.Background())
- if currentMaster != "" {
- masterNodes = append(masterNodes, MasterNode{
- Address: string(currentMaster),
- IsLeader: isLeader,
- })
- }
- 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,
- LastUpdated: time.Now(),
- })
- }
- return nil
- })
- if err != nil {
- currentMaster := s.masterClient.GetMaster(context.Background())
- glog.Errorf("Failed to get filer nodes from master %s: %v", currentMaster, err)
- // Return empty list if we can't get filer info from master
- return []FilerNode{}
- }
- return filerNodes
- }
- // getMessageBrokerNodesStatus checks status of all message broker nodes using master's ListClusterNodes
- func (s *AdminServer) getMessageBrokerNodesStatus() []MessageBrokerNode {
- var messageBrokers []MessageBrokerNode
- // Get message broker 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.BrokerType,
- })
- if err != nil {
- return err
- }
- // Process each message broker node
- for _, node := range resp.ClusterNodes {
- messageBrokers = append(messageBrokers, MessageBrokerNode{
- Address: node.Address,
- DataCenter: node.DataCenter,
- Rack: node.Rack,
- LastUpdated: time.Now(),
- })
- }
- return nil
- })
- if err != nil {
- currentMaster := s.masterClient.GetMaster(context.Background())
- glog.Errorf("Failed to get message broker nodes from master %s: %v", currentMaster, err)
- // Return empty list if we can't get broker info from master
- return []MessageBrokerNode{}
- }
- return messageBrokers
- }
|