10 changed files with 2361 additions and 12 deletions
-
395weed/admin/dash/ec_shard_management.go
-
74weed/admin/dash/types.go
-
4weed/admin/handlers/admin_handlers.go
-
86weed/admin/handlers/cluster_handlers.go
-
424weed/admin/view/app/cluster_ec_shards.templ
-
706weed/admin/view/app/cluster_ec_shards_templ.go
-
223weed/admin/view/app/ec_volume_details.templ
-
432weed/admin/view/app/ec_volume_details_templ.go
-
5weed/admin/view/layout/layout.templ
-
24weed/admin/view/layout/layout_templ.go
@ -0,0 +1,395 @@ |
|||||
|
package dash |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"fmt" |
||||
|
"sort" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding" |
||||
|
) |
||||
|
|
||||
|
// GetClusterEcShards retrieves cluster EC shards data with pagination, sorting, and filtering
|
||||
|
func (s *AdminServer) GetClusterEcShards(page int, pageSize int, sortBy string, sortOrder string, collection string) (*ClusterEcShardsData, error) { |
||||
|
// Set defaults
|
||||
|
if page < 1 { |
||||
|
page = 1 |
||||
|
} |
||||
|
if pageSize < 1 || pageSize > 1000 { |
||||
|
pageSize = 100 |
||||
|
} |
||||
|
if sortBy == "" { |
||||
|
sortBy = "volume_id" |
||||
|
} |
||||
|
if sortOrder == "" { |
||||
|
sortOrder = "asc" |
||||
|
} |
||||
|
|
||||
|
var ecShards []EcShardWithInfo |
||||
|
shardsPerVolume := make(map[uint32]int) |
||||
|
volumesWithAllShards := 0 |
||||
|
volumesWithMissingShards := 0 |
||||
|
|
||||
|
// Get detailed EC shard information via gRPC
|
||||
|
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { |
||||
|
resp, err := client.VolumeList(context.Background(), &master_pb.VolumeListRequest{}) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
if resp.TopologyInfo != nil { |
||||
|
for _, dc := range resp.TopologyInfo.DataCenterInfos { |
||||
|
for _, rack := range dc.RackInfos { |
||||
|
for _, node := range rack.DataNodeInfos { |
||||
|
for _, diskInfo := range node.DiskInfos { |
||||
|
// Process EC shard information
|
||||
|
for _, ecShardInfo := range diskInfo.EcShardInfos { |
||||
|
// Count shards per volume
|
||||
|
shardsPerVolume[ecShardInfo.Id] += getShardCount(ecShardInfo.EcIndexBits) |
||||
|
|
||||
|
// Create individual shard entries for each shard this server has
|
||||
|
shardBits := ecShardInfo.EcIndexBits |
||||
|
for shardId := 0; shardId < erasure_coding.TotalShardsCount; shardId++ { |
||||
|
if (shardBits & (1 << uint(shardId))) != 0 { |
||||
|
ecShard := EcShardWithInfo{ |
||||
|
VolumeID: ecShardInfo.Id, |
||||
|
ShardID: uint32(shardId), |
||||
|
Collection: ecShardInfo.Collection, |
||||
|
Size: 0, // EC shards don't have individual size in the API response
|
||||
|
Server: node.Id, |
||||
|
DataCenter: dc.Id, |
||||
|
Rack: rack.Id, |
||||
|
DiskType: diskInfo.Type, |
||||
|
ModifiedTime: 0, // Not available in current API
|
||||
|
EcIndexBits: ecShardInfo.EcIndexBits, |
||||
|
ShardCount: getShardCount(ecShardInfo.EcIndexBits), |
||||
|
} |
||||
|
ecShards = append(ecShards, ecShard) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
// Calculate completeness statistics
|
||||
|
for volumeId, shardCount := range shardsPerVolume { |
||||
|
if shardCount == erasure_coding.TotalShardsCount { |
||||
|
volumesWithAllShards++ |
||||
|
} else { |
||||
|
volumesWithMissingShards++ |
||||
|
} |
||||
|
|
||||
|
// Update completeness info for each shard
|
||||
|
for i := range ecShards { |
||||
|
if ecShards[i].VolumeID == volumeId { |
||||
|
ecShards[i].IsComplete = (shardCount == erasure_coding.TotalShardsCount) |
||||
|
if !ecShards[i].IsComplete { |
||||
|
// Calculate missing shards
|
||||
|
ecShards[i].MissingShards = getMissingShards(ecShards[i].EcIndexBits) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Filter by collection if specified
|
||||
|
if collection != "" { |
||||
|
var filteredShards []EcShardWithInfo |
||||
|
for _, shard := range ecShards { |
||||
|
if shard.Collection == collection { |
||||
|
filteredShards = append(filteredShards, shard) |
||||
|
} |
||||
|
} |
||||
|
ecShards = filteredShards |
||||
|
} |
||||
|
|
||||
|
// Sort the results
|
||||
|
sortEcShards(ecShards, sortBy, sortOrder) |
||||
|
|
||||
|
// Calculate statistics for conditional display
|
||||
|
dataCenters := make(map[string]bool) |
||||
|
racks := make(map[string]bool) |
||||
|
collections := make(map[string]bool) |
||||
|
|
||||
|
for _, shard := range ecShards { |
||||
|
dataCenters[shard.DataCenter] = true |
||||
|
racks[shard.Rack] = true |
||||
|
if shard.Collection != "" { |
||||
|
collections[shard.Collection] = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Pagination
|
||||
|
totalShards := len(ecShards) |
||||
|
totalPages := (totalShards + pageSize - 1) / pageSize |
||||
|
startIndex := (page - 1) * pageSize |
||||
|
endIndex := startIndex + pageSize |
||||
|
if endIndex > totalShards { |
||||
|
endIndex = totalShards |
||||
|
} |
||||
|
|
||||
|
if startIndex >= totalShards { |
||||
|
startIndex = 0 |
||||
|
endIndex = 0 |
||||
|
} |
||||
|
|
||||
|
paginatedShards := ecShards[startIndex:endIndex] |
||||
|
|
||||
|
// Build response
|
||||
|
data := &ClusterEcShardsData{ |
||||
|
EcShards: paginatedShards, |
||||
|
TotalShards: totalShards, |
||||
|
TotalVolumes: len(shardsPerVolume), |
||||
|
LastUpdated: time.Now(), |
||||
|
|
||||
|
// Pagination
|
||||
|
CurrentPage: page, |
||||
|
TotalPages: totalPages, |
||||
|
PageSize: pageSize, |
||||
|
|
||||
|
// Sorting
|
||||
|
SortBy: sortBy, |
||||
|
SortOrder: sortOrder, |
||||
|
|
||||
|
// Statistics
|
||||
|
DataCenterCount: len(dataCenters), |
||||
|
RackCount: len(racks), |
||||
|
CollectionCount: len(collections), |
||||
|
|
||||
|
// Conditional display flags
|
||||
|
ShowDataCenterColumn: len(dataCenters) > 1, |
||||
|
ShowRackColumn: len(racks) > 1, |
||||
|
ShowCollectionColumn: len(collections) > 1, |
||||
|
|
||||
|
// Filtering
|
||||
|
FilterCollection: collection, |
||||
|
|
||||
|
// EC specific statistics
|
||||
|
ShardsPerVolume: shardsPerVolume, |
||||
|
VolumesWithAllShards: volumesWithAllShards, |
||||
|
VolumesWithMissingShards: volumesWithMissingShards, |
||||
|
} |
||||
|
|
||||
|
// Set single values when only one exists
|
||||
|
if len(dataCenters) == 1 { |
||||
|
for dc := range dataCenters { |
||||
|
data.SingleDataCenter = dc |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if len(racks) == 1 { |
||||
|
for rack := range racks { |
||||
|
data.SingleRack = rack |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if len(collections) == 1 { |
||||
|
for col := range collections { |
||||
|
data.SingleCollection = col |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return data, nil |
||||
|
} |
||||
|
|
||||
|
// getShardCount returns the number of shards represented by the bitmap
|
||||
|
func getShardCount(ecIndexBits uint32) int { |
||||
|
count := 0 |
||||
|
for i := 0; i < erasure_coding.TotalShardsCount; i++ { |
||||
|
if (ecIndexBits & (1 << uint(i))) != 0 { |
||||
|
count++ |
||||
|
} |
||||
|
} |
||||
|
return count |
||||
|
} |
||||
|
|
||||
|
// getMissingShards returns a slice of missing shard IDs for a volume
|
||||
|
func getMissingShards(ecIndexBits uint32) []int { |
||||
|
var missing []int |
||||
|
for i := 0; i < erasure_coding.TotalShardsCount; i++ { |
||||
|
if (ecIndexBits & (1 << uint(i))) == 0 { |
||||
|
missing = append(missing, i) |
||||
|
} |
||||
|
} |
||||
|
return missing |
||||
|
} |
||||
|
|
||||
|
// sortEcShards sorts EC shards based on the specified field and order
|
||||
|
func sortEcShards(shards []EcShardWithInfo, sortBy string, sortOrder string) { |
||||
|
sort.Slice(shards, func(i, j int) bool { |
||||
|
var less bool |
||||
|
switch sortBy { |
||||
|
case "volume_id": |
||||
|
if shards[i].VolumeID == shards[j].VolumeID { |
||||
|
less = shards[i].ShardID < shards[j].ShardID |
||||
|
} else { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} |
||||
|
case "shard_id": |
||||
|
if shards[i].ShardID == shards[j].ShardID { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} else { |
||||
|
less = shards[i].ShardID < shards[j].ShardID |
||||
|
} |
||||
|
case "collection": |
||||
|
if shards[i].Collection == shards[j].Collection { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} else { |
||||
|
less = shards[i].Collection < shards[j].Collection |
||||
|
} |
||||
|
case "server": |
||||
|
if shards[i].Server == shards[j].Server { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} else { |
||||
|
less = shards[i].Server < shards[j].Server |
||||
|
} |
||||
|
case "datacenter": |
||||
|
if shards[i].DataCenter == shards[j].DataCenter { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} else { |
||||
|
less = shards[i].DataCenter < shards[j].DataCenter |
||||
|
} |
||||
|
case "rack": |
||||
|
if shards[i].Rack == shards[j].Rack { |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} else { |
||||
|
less = shards[i].Rack < shards[j].Rack |
||||
|
} |
||||
|
default: |
||||
|
less = shards[i].VolumeID < shards[j].VolumeID |
||||
|
} |
||||
|
|
||||
|
if sortOrder == "desc" { |
||||
|
return !less |
||||
|
} |
||||
|
return less |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// GetEcVolumeDetails retrieves detailed information about a specific EC volume
|
||||
|
func (s *AdminServer) GetEcVolumeDetails(volumeID uint32) (*EcVolumeDetailsData, error) { |
||||
|
var shards []EcShardWithInfo |
||||
|
var collection string |
||||
|
dataCenters := make(map[string]bool) |
||||
|
servers := make(map[string]bool) |
||||
|
|
||||
|
// Get detailed EC shard information for the specific volume via gRPC
|
||||
|
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { |
||||
|
resp, err := client.VolumeList(context.Background(), &master_pb.VolumeListRequest{}) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
if resp.TopologyInfo != nil { |
||||
|
for _, dc := range resp.TopologyInfo.DataCenterInfos { |
||||
|
for _, rack := range dc.RackInfos { |
||||
|
for _, node := range rack.DataNodeInfos { |
||||
|
for _, diskInfo := range node.DiskInfos { |
||||
|
// Process EC shard information for this specific volume
|
||||
|
for _, ecShardInfo := range diskInfo.EcShardInfos { |
||||
|
if ecShardInfo.Id == volumeID { |
||||
|
collection = ecShardInfo.Collection |
||||
|
dataCenters[dc.Id] = true |
||||
|
servers[node.Id] = true |
||||
|
|
||||
|
// Create individual shard entries for each shard this server has
|
||||
|
shardBits := ecShardInfo.EcIndexBits |
||||
|
for shardId := 0; shardId < erasure_coding.TotalShardsCount; shardId++ { |
||||
|
if (shardBits & (1 << uint(shardId))) != 0 { |
||||
|
ecShard := EcShardWithInfo{ |
||||
|
VolumeID: ecShardInfo.Id, |
||||
|
ShardID: uint32(shardId), |
||||
|
Collection: ecShardInfo.Collection, |
||||
|
Size: 0, // EC shards don't have individual size in the API response
|
||||
|
Server: node.Id, |
||||
|
DataCenter: dc.Id, |
||||
|
Rack: rack.Id, |
||||
|
DiskType: diskInfo.Type, |
||||
|
ModifiedTime: 0, // Not available in current API
|
||||
|
EcIndexBits: ecShardInfo.EcIndexBits, |
||||
|
ShardCount: getShardCount(ecShardInfo.EcIndexBits), |
||||
|
} |
||||
|
shards = append(shards, ecShard) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if len(shards) == 0 { |
||||
|
return nil, fmt.Errorf("EC volume %d not found", volumeID) |
||||
|
} |
||||
|
|
||||
|
// Calculate completeness
|
||||
|
totalShards := len(shards) |
||||
|
isComplete := (totalShards == erasure_coding.TotalShardsCount) |
||||
|
|
||||
|
// Calculate missing shards
|
||||
|
var missingShards []int |
||||
|
foundShards := make(map[int]bool) |
||||
|
for _, shard := range shards { |
||||
|
foundShards[int(shard.ShardID)] = true |
||||
|
} |
||||
|
for i := 0; i < erasure_coding.TotalShardsCount; i++ { |
||||
|
if !foundShards[i] { |
||||
|
missingShards = append(missingShards, i) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Update completeness info for each shard
|
||||
|
for i := range shards { |
||||
|
shards[i].IsComplete = isComplete |
||||
|
shards[i].MissingShards = missingShards |
||||
|
} |
||||
|
|
||||
|
// Sort shards by ID
|
||||
|
sort.Slice(shards, func(i, j int) bool { |
||||
|
return shards[i].ShardID < shards[j].ShardID |
||||
|
}) |
||||
|
|
||||
|
// Convert maps to slices
|
||||
|
var dcList []string |
||||
|
for dc := range dataCenters { |
||||
|
dcList = append(dcList, dc) |
||||
|
} |
||||
|
var serverList []string |
||||
|
for server := range servers { |
||||
|
serverList = append(serverList, server) |
||||
|
} |
||||
|
|
||||
|
data := &EcVolumeDetailsData{ |
||||
|
VolumeID: volumeID, |
||||
|
Collection: collection, |
||||
|
Shards: shards, |
||||
|
TotalShards: totalShards, |
||||
|
IsComplete: isComplete, |
||||
|
MissingShards: missingShards, |
||||
|
DataCenters: dcList, |
||||
|
Servers: serverList, |
||||
|
LastUpdated: time.Now(), |
||||
|
} |
||||
|
|
||||
|
return data, nil |
||||
|
} |
||||
@ -0,0 +1,424 @@ |
|||||
|
package app |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash" |
||||
|
) |
||||
|
|
||||
|
templ ClusterEcShards(data dash.ClusterEcShardsData) { |
||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> |
||||
|
<div> |
||||
|
<h1 class="h2"> |
||||
|
<i class="fas fa-th-large me-2"></i>EC Shards |
||||
|
</h1> |
||||
|
if data.FilterCollection != "" { |
||||
|
<div class="d-flex align-items-center mt-2"> |
||||
|
<span class="badge bg-info me-2"> |
||||
|
<i class="fas fa-filter me-1"></i>Collection: {data.FilterCollection} |
||||
|
</span> |
||||
|
<a href="/cluster/ec-shards" class="btn btn-sm btn-outline-secondary"> |
||||
|
<i class="fas fa-times me-1"></i>Clear Filter |
||||
|
</a> |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
<div class="btn-toolbar mb-2 mb-md-0"> |
||||
|
<div class="btn-group me-2"> |
||||
|
<select class="form-select form-select-sm me-2" id="pageSizeSelect" onchange="changePageSize()" style="width: auto;"> |
||||
|
<option value="50" if data.PageSize == 50 { selected="selected" }>50 per page</option> |
||||
|
<option value="100" if data.PageSize == 100 { selected="selected" }>100 per page</option> |
||||
|
<option value="200" if data.PageSize == 200 { selected="selected" }>200 per page</option> |
||||
|
<option value="500" if data.PageSize == 500 { selected="selected" }>500 per page</option> |
||||
|
</select> |
||||
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="exportEcShards()"> |
||||
|
<i class="fas fa-download me-1"></i>Export |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Statistics Cards --> |
||||
|
<div class="row mb-4"> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="card text-bg-primary"> |
||||
|
<div class="card-body"> |
||||
|
<div class="d-flex justify-content-between"> |
||||
|
<div> |
||||
|
<h6 class="card-title">Total Shards</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.TotalShards)}</h4> |
||||
|
</div> |
||||
|
<div class="align-self-center"> |
||||
|
<i class="fas fa-puzzle-piece fa-2x"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="card text-bg-info"> |
||||
|
<div class="card-body"> |
||||
|
<div class="d-flex justify-content-between"> |
||||
|
<div> |
||||
|
<h6 class="card-title">EC Volumes</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.TotalVolumes)}</h4> |
||||
|
</div> |
||||
|
<div class="align-self-center"> |
||||
|
<i class="fas fa-database fa-2x"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="card text-bg-success"> |
||||
|
<div class="card-body"> |
||||
|
<div class="d-flex justify-content-between"> |
||||
|
<div> |
||||
|
<h6 class="card-title">Complete Volumes</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.VolumesWithAllShards)}</h4> |
||||
|
<small>All 14 shards</small> |
||||
|
</div> |
||||
|
<div class="align-self-center"> |
||||
|
<i class="fas fa-check-circle fa-2x"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="card text-bg-warning"> |
||||
|
<div class="card-body"> |
||||
|
<div class="d-flex justify-content-between"> |
||||
|
<div> |
||||
|
<h6 class="card-title">Incomplete Volumes</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.VolumesWithMissingShards)}</h4> |
||||
|
<small>Missing shards</small> |
||||
|
</div> |
||||
|
<div class="align-self-center"> |
||||
|
<i class="fas fa-exclamation-triangle fa-2x"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Shards Table --> |
||||
|
<div class="table-responsive"> |
||||
|
<table class="table table-striped table-hover" id="ecShardsTable"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('volume_id')" class="text-dark text-decoration-none"> |
||||
|
Volume ID |
||||
|
if data.SortBy == "volume_id" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('shard_id')" class="text-dark text-decoration-none"> |
||||
|
Shard ID |
||||
|
if data.SortBy == "shard_id" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
if data.ShowCollectionColumn { |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('collection')" class="text-dark text-decoration-none"> |
||||
|
Collection |
||||
|
if data.SortBy == "collection" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
} |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('server')" class="text-dark text-decoration-none"> |
||||
|
Server |
||||
|
if data.SortBy == "server" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
if data.ShowDataCenterColumn { |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('datacenter')" class="text-dark text-decoration-none"> |
||||
|
Data Center |
||||
|
if data.SortBy == "datacenter" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
} |
||||
|
if data.ShowRackColumn { |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('rack')" class="text-dark text-decoration-none"> |
||||
|
Rack |
||||
|
if data.SortBy == "rack" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
<i class="fas fa-sort-up ms-1"></i> |
||||
|
} else { |
||||
|
<i class="fas fa-sort-down ms-1"></i> |
||||
|
} |
||||
|
} else { |
||||
|
<i class="fas fa-sort ms-1 text-muted"></i> |
||||
|
} |
||||
|
</a> |
||||
|
</th> |
||||
|
} |
||||
|
<th class="text-dark">Status</th> |
||||
|
<th class="text-dark">Actions</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
for _, shard := range data.EcShards { |
||||
|
<tr> |
||||
|
<td> |
||||
|
<span class="fw-bold">{fmt.Sprintf("%d", shard.VolumeID)}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span class="badge bg-secondary">{fmt.Sprintf("%02d", shard.ShardID)}</span> |
||||
|
</td> |
||||
|
if data.ShowCollectionColumn { |
||||
|
<td> |
||||
|
if shard.Collection != "" { |
||||
|
<a href="/cluster/ec-shards?collection={shard.Collection}" class="text-decoration-none"> |
||||
|
<span class="badge bg-info">{shard.Collection}</span> |
||||
|
</a> |
||||
|
} else { |
||||
|
<span class="text-muted">default</span> |
||||
|
} |
||||
|
</td> |
||||
|
} |
||||
|
<td> |
||||
|
<code class="small">{shard.Server}</code> |
||||
|
</td> |
||||
|
if data.ShowDataCenterColumn { |
||||
|
<td> |
||||
|
<span class="badge bg-outline-primary">{shard.DataCenter}</span> |
||||
|
</td> |
||||
|
} |
||||
|
if data.ShowRackColumn { |
||||
|
<td> |
||||
|
<span class="badge bg-outline-secondary">{shard.Rack}</span> |
||||
|
</td> |
||||
|
} |
||||
|
<td> |
||||
|
if shard.IsComplete { |
||||
|
<span class="badge bg-success"> |
||||
|
<i class="fas fa-check me-1"></i>Complete |
||||
|
</span> |
||||
|
} else { |
||||
|
<span class="badge bg-warning"> |
||||
|
<i class="fas fa-exclamation-triangle me-1"></i> |
||||
|
Missing {fmt.Sprintf("%d", len(shard.MissingShards))} shards |
||||
|
</span> |
||||
|
} |
||||
|
</td> |
||||
|
<td> |
||||
|
<div class="btn-group" role="group"> |
||||
|
<button type="button" class="btn btn-sm btn-outline-primary" |
||||
|
onclick="showShardDetails(event)" |
||||
|
data-volume-id={ fmt.Sprintf("%d", shard.VolumeID) } |
||||
|
title="View EC volume details"> |
||||
|
<i class="fas fa-info-circle"></i> |
||||
|
</button> |
||||
|
if !shard.IsComplete { |
||||
|
<button type="button" class="btn btn-sm btn-outline-warning" |
||||
|
onclick="repairVolume(event)" |
||||
|
data-volume-id={ fmt.Sprintf("%d", shard.VolumeID) } |
||||
|
title="Repair missing shards"> |
||||
|
<i class="fas fa-wrench"></i> |
||||
|
</button> |
||||
|
} |
||||
|
</div> |
||||
|
</td> |
||||
|
</tr> |
||||
|
} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Pagination --> |
||||
|
if data.TotalPages > 1 { |
||||
|
<nav aria-label="EC Shards pagination"> |
||||
|
<ul class="pagination justify-content-center"> |
||||
|
if data.CurrentPage > 1 { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.CurrentPage-1) }> |
||||
|
<i class="fas fa-chevron-left"></i> |
||||
|
</a> |
||||
|
</li> |
||||
|
} |
||||
|
|
||||
|
<!-- First page --> |
||||
|
if data.CurrentPage > 3 { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(1)">1</a> |
||||
|
</li> |
||||
|
if data.CurrentPage > 4 { |
||||
|
<li class="page-item disabled"> |
||||
|
<span class="page-link">...</span> |
||||
|
</li> |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
<!-- Current page and neighbors --> |
||||
|
if data.CurrentPage > 1 && data.CurrentPage-1 >= 1 { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.CurrentPage-1) }>{fmt.Sprintf("%d", data.CurrentPage-1)}</a> |
||||
|
</li> |
||||
|
} |
||||
|
|
||||
|
<li class="page-item active"> |
||||
|
<span class="page-link">{fmt.Sprintf("%d", data.CurrentPage)}</span> |
||||
|
</li> |
||||
|
|
||||
|
if data.CurrentPage < data.TotalPages && data.CurrentPage+1 <= data.TotalPages { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.CurrentPage+1) }>{fmt.Sprintf("%d", data.CurrentPage+1)}</a> |
||||
|
</li> |
||||
|
} |
||||
|
|
||||
|
<!-- Last page --> |
||||
|
if data.CurrentPage < data.TotalPages-2 { |
||||
|
if data.CurrentPage < data.TotalPages-3 { |
||||
|
<li class="page-item disabled"> |
||||
|
<span class="page-link">...</span> |
||||
|
</li> |
||||
|
} |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.TotalPages) }>{fmt.Sprintf("%d", data.TotalPages)}</a> |
||||
|
</li> |
||||
|
} |
||||
|
|
||||
|
if data.CurrentPage < data.TotalPages { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.CurrentPage+1) }> |
||||
|
<i class="fas fa-chevron-right"></i> |
||||
|
</a> |
||||
|
</li> |
||||
|
} |
||||
|
</ul> |
||||
|
</nav> |
||||
|
} |
||||
|
|
||||
|
<!-- JavaScript --> |
||||
|
<script> |
||||
|
function sortBy(field) { |
||||
|
const currentSort = "{data.SortBy}"; |
||||
|
const currentOrder = "{data.SortOrder}"; |
||||
|
let newOrder = 'asc'; |
||||
|
|
||||
|
if (currentSort === field && currentOrder === 'asc') { |
||||
|
newOrder = 'desc'; |
||||
|
} |
||||
|
|
||||
|
updateUrl({ |
||||
|
sortBy: field, |
||||
|
sortOrder: newOrder, |
||||
|
page: 1 |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function goToPage(event) { |
||||
|
// Get data from the link element (not any child elements) |
||||
|
const link = event.target.closest('a'); |
||||
|
const page = link.getAttribute('data-page'); |
||||
|
updateUrl({ page: page }); |
||||
|
} |
||||
|
|
||||
|
function changePageSize() { |
||||
|
const pageSize = document.getElementById('pageSizeSelect').value; |
||||
|
updateUrl({ pageSize: pageSize, page: 1 }); |
||||
|
} |
||||
|
|
||||
|
function updateUrl(params) { |
||||
|
const url = new URL(window.location); |
||||
|
Object.keys(params).forEach(key => { |
||||
|
if (params[key]) { |
||||
|
url.searchParams.set(key, params[key]); |
||||
|
} else { |
||||
|
url.searchParams.delete(key); |
||||
|
} |
||||
|
}); |
||||
|
window.location.href = url.toString(); |
||||
|
} |
||||
|
|
||||
|
function exportEcShards() { |
||||
|
const url = new URL('/api/cluster/ec-shards/export', window.location.origin); |
||||
|
const params = new URLSearchParams(window.location.search); |
||||
|
params.forEach((value, key) => { |
||||
|
url.searchParams.set(key, value); |
||||
|
}); |
||||
|
window.open(url.toString(), '_blank'); |
||||
|
} |
||||
|
|
||||
|
function showShardDetails(event) { |
||||
|
// Get data from the button element (not the icon inside it) |
||||
|
const button = event.target.closest('button'); |
||||
|
const volumeId = button.getAttribute('data-volume-id'); |
||||
|
|
||||
|
// Navigate to the EC volume details page |
||||
|
window.location.href = `/cluster/ec-volumes/${volumeId}`; |
||||
|
} |
||||
|
|
||||
|
function repairVolume(event) { |
||||
|
// Get data from the button element (not the icon inside it) |
||||
|
const button = event.target.closest('button'); |
||||
|
const volumeId = button.getAttribute('data-volume-id'); |
||||
|
if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) { |
||||
|
fetch(`/api/cluster/volumes/${volumeId}/repair`, { |
||||
|
method: 'POST', |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json', |
||||
|
} |
||||
|
}) |
||||
|
.then(response => response.json()) |
||||
|
.then(data => { |
||||
|
if (data.success) { |
||||
|
alert('Repair initiated successfully'); |
||||
|
location.reload(); |
||||
|
} else { |
||||
|
alert('Failed to initiate repair: ' + data.error); |
||||
|
} |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
alert('Error: ' + error.message); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
} |
||||
@ -0,0 +1,706 @@ |
|||||
|
// Code generated by templ - DO NOT EDIT.
|
||||
|
|
||||
|
// templ: version: v0.3.906
|
||||
|
package app |
||||
|
|
||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
|
||||
|
import "github.com/a-h/templ" |
||||
|
import templruntime "github.com/a-h/templ/runtime" |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash" |
||||
|
) |
||||
|
|
||||
|
func ClusterEcShards(data dash.ClusterEcShardsData) templ.Component { |
||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context |
||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { |
||||
|
return templ_7745c5c3_CtxErr |
||||
|
} |
||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) |
||||
|
if !templ_7745c5c3_IsBuffer { |
||||
|
defer func() { |
||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) |
||||
|
if templ_7745c5c3_Err == nil { |
||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
ctx = templ.InitializeContext(ctx) |
||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx) |
||||
|
if templ_7745c5c3_Var1 == nil { |
||||
|
templ_7745c5c3_Var1 = templ.NopComponent |
||||
|
} |
||||
|
ctx = templ.ClearChildren(ctx) |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom\"><div><h1 class=\"h2\"><i class=\"fas fa-th-large me-2\"></i>EC Shards</h1>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.FilterCollection != "" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"d-flex align-items-center mt-2\"><span class=\"badge bg-info me-2\"><i class=\"fas fa-filter me-1\"></i>Collection: ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var2 string |
||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.FilterCollection) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 17, Col: 92} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span> <a href=\"/cluster/ec-shards\" class=\"btn btn-sm btn-outline-secondary\"><i class=\"fas fa-times me-1\"></i>Clear Filter</a></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div><div class=\"btn-toolbar mb-2 mb-md-0\"><div class=\"btn-group me-2\"><select class=\"form-select form-select-sm me-2\" id=\"pageSizeSelect\" onchange=\"changePageSize()\" style=\"width: auto;\"><option value=\"50\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 50 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " selected=\"selected\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, ">50 per page</option> <option value=\"100\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 100 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " selected=\"selected\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, ">100 per page</option> <option value=\"200\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 200 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " selected=\"selected\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, ">200 per page</option> <option value=\"500\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 500 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " selected=\"selected\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, ">500 per page</option></select> <button type=\"button\" class=\"btn btn-sm btn-outline-primary\" onclick=\"exportEcShards()\"><i class=\"fas fa-download me-1\"></i>Export</button></div></div></div><!-- Statistics Cards --><div class=\"row mb-4\"><div class=\"col-md-3\"><div class=\"card text-bg-primary\"><div class=\"card-body\"><div class=\"d-flex justify-content-between\"><div><h6 class=\"card-title\">Total Shards</h6><h4 class=\"mb-0\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var3 string |
||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 48, Col: 81} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</h4></div><div class=\"align-self-center\"><i class=\"fas fa-puzzle-piece fa-2x\"></i></div></div></div></div></div><div class=\"col-md-3\"><div class=\"card text-bg-info\"><div class=\"card-body\"><div class=\"d-flex justify-content-between\"><div><h6 class=\"card-title\">EC Volumes</h6><h4 class=\"mb-0\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var4 string |
||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 63, Col: 82} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</h4></div><div class=\"align-self-center\"><i class=\"fas fa-database fa-2x\"></i></div></div></div></div></div><div class=\"col-md-3\"><div class=\"card text-bg-success\"><div class=\"card-body\"><div class=\"d-flex justify-content-between\"><div><h6 class=\"card-title\">Complete Volumes</h6><h4 class=\"mb-0\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var5 string |
||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.VolumesWithAllShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 78, Col: 90} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</h4><small>All 14 shards</small></div><div class=\"align-self-center\"><i class=\"fas fa-check-circle fa-2x\"></i></div></div></div></div></div><div class=\"col-md-3\"><div class=\"card text-bg-warning\"><div class=\"card-body\"><div class=\"d-flex justify-content-between\"><div><h6 class=\"card-title\">Incomplete Volumes</h6><h4 class=\"mb-0\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var6 string |
||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.VolumesWithMissingShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 94, Col: 94} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</h4><small>Missing shards</small></div><div class=\"align-self-center\"><i class=\"fas fa-exclamation-triangle fa-2x\"></i></div></div></div></div></div></div><!-- Shards Table --><div class=\"table-responsive\"><table class=\"table table-striped table-hover\" id=\"ecShardsTable\"><thead><tr><th><a href=\"#\" onclick=\"sortBy('volume_id')\" class=\"text-dark text-decoration-none\">Volume ID ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "volume_id" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</a></th><th><a href=\"#\" onclick=\"sortBy('shard_id')\" class=\"text-dark text-decoration-none\">Shard ID ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "shard_id" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowCollectionColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<th><a href=\"#\" onclick=\"sortBy('collection')\" class=\"text-dark text-decoration-none\">Collection ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "collection" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<th><a href=\"#\" onclick=\"sortBy('server')\" class=\"text-dark text-decoration-none\">Server ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "server" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowDataCenterColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<th><a href=\"#\" onclick=\"sortBy('datacenter')\" class=\"text-dark text-decoration-none\">Data Center ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "datacenter" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
if data.ShowRackColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<th><a href=\"#\" onclick=\"sortBy('rack')\" class=\"text-dark text-decoration-none\">Rack ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "rack" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<i class=\"fas fa-sort-up ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<i class=\"fas fa-sort-down ms-1\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<i class=\"fas fa-sort ms-1 text-muted\"></i>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<th class=\"text-dark\">Status</th><th class=\"text-dark\">Actions</th></tr></thead> <tbody>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for _, shard := range data.EcShards { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<tr><td><span class=\"fw-bold\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var7 string |
||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", shard.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 209, Col: 84} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</span></td><td><span class=\"badge bg-secondary\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var8 string |
||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", shard.ShardID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 212, Col: 96} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</span></td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowCollectionColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if shard.Collection != "" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<a href=\"/cluster/ec-shards?collection={shard.Collection}\" class=\"text-decoration-none\"><span class=\"badge bg-info\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var9 string |
||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(shard.Collection) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 218, Col: 85} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</span></a>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<span class=\"text-muted\">default</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<td><code class=\"small\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var10 string |
||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(shard.Server) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 226, Col: 61} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</code></td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowDataCenterColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<td><span class=\"badge bg-outline-primary\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var11 string |
||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(shard.DataCenter) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 230, Col: 88} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</span></td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
if data.ShowRackColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<td><span class=\"badge bg-outline-secondary\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var12 string |
||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(shard.Rack) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 235, Col: 84} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</span></td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if shard.IsComplete { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<span class=\"badge bg-success\"><i class=\"fas fa-check me-1\"></i>Complete</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<span class=\"badge bg-warning\"><i class=\"fas fa-exclamation-triangle me-1\"></i> Missing ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var13 string |
||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(shard.MissingShards))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 246, Col: 88} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " shards</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</td><td><div class=\"btn-group\" role=\"group\"><button type=\"button\" class=\"btn btn-sm btn-outline-primary\" onclick=\"showShardDetails(event)\" data-volume-id=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var14 string |
||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", shard.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 254, Col: 90} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\" title=\"View EC volume details\"><i class=\"fas fa-info-circle\"></i></button> ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if !shard.IsComplete { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<button type=\"button\" class=\"btn btn-sm btn-outline-warning\" onclick=\"repairVolume(event)\" data-volume-id=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var15 string |
||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", shard.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 261, Col: 94} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" title=\"Repair missing shards\"><i class=\"fas fa-wrench\"></i></button>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "</div></td></tr>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</tbody></table></div><!-- Pagination -->") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.TotalPages > 1 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<nav aria-label=\"EC Shards pagination\"><ul class=\"pagination justify-content-center\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage > 1 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var16 string |
||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage-1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 280, Col: 129} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\"><i class=\"fas fa-chevron-left\"></i></a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<!-- First page -->") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage > 3 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(1)\">1</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage > 4 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<li class=\"page-item disabled\"><span class=\"page-link\">...</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<!-- Current page and neighbors -->") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage > 1 && data.CurrentPage-1 >= 1 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var17 string |
||||
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage-1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 301, Col: 129} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var18 string |
||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage-1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 301, Col: 170} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<li class=\"page-item active\"><span class=\"page-link\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var19 string |
||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 306, Col: 80} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage < data.TotalPages && data.CurrentPage+1 <= data.TotalPages { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var20 string |
||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage+1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 311, Col: 129} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var21 string |
||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage+1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 311, Col: 170} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<!-- Last page -->") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.CurrentPage < data.TotalPages-2 { |
||||
|
if data.CurrentPage < data.TotalPages-3 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<li class=\"page-item disabled\"><span class=\"page-link\">...</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, " <li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var22 string |
||||
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalPages)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 323, Col: 126} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var23 string |
||||
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalPages)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 323, Col: 164} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
if data.CurrentPage < data.TotalPages { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var24 string |
||||
|
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.CurrentPage+1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_shards.templ`, Line: 329, Col: 129} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "\"><i class=\"fas fa-chevron-right\"></i></a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</ul></nav>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "<!-- JavaScript --><script>\n function sortBy(field) {\n const currentSort = \"{data.SortBy}\";\n const currentOrder = \"{data.SortOrder}\";\n let newOrder = 'asc';\n \n if (currentSort === field && currentOrder === 'asc') {\n newOrder = 'desc';\n }\n \n updateUrl({\n sortBy: field,\n sortOrder: newOrder,\n page: 1\n });\n }\n\n function goToPage(event) {\n // Get data from the link element (not any child elements)\n const link = event.target.closest('a');\n const page = link.getAttribute('data-page');\n updateUrl({ page: page });\n }\n\n function changePageSize() {\n const pageSize = document.getElementById('pageSizeSelect').value;\n updateUrl({ pageSize: pageSize, page: 1 });\n }\n\n function updateUrl(params) {\n const url = new URL(window.location);\n Object.keys(params).forEach(key => {\n if (params[key]) {\n url.searchParams.set(key, params[key]);\n } else {\n url.searchParams.delete(key);\n }\n });\n window.location.href = url.toString();\n }\n\n function exportEcShards() {\n const url = new URL('/api/cluster/ec-shards/export', window.location.origin);\n const params = new URLSearchParams(window.location.search);\n params.forEach((value, key) => {\n url.searchParams.set(key, value);\n });\n window.open(url.toString(), '_blank');\n }\n\n function showShardDetails(event) {\n // Get data from the button element (not the icon inside it)\n const button = event.target.closest('button');\n const volumeId = button.getAttribute('data-volume-id');\n \n // Navigate to the EC volume details page\n window.location.href = `/cluster/ec-volumes/${volumeId}`;\n }\n\n function repairVolume(event) {\n // Get data from the button element (not the icon inside it)\n const button = event.target.closest('button');\n const volumeId = button.getAttribute('data-volume-id');\n if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) {\n fetch(`/api/cluster/volumes/${volumeId}/repair`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n }\n })\n .then(response => response.json())\n .then(data => {\n if (data.success) {\n alert('Repair initiated successfully');\n location.reload();\n } else {\n alert('Failed to initiate repair: ' + data.error);\n }\n })\n .catch(error => {\n alert('Error: ' + error.message);\n });\n }\n }\n </script>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
return nil |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
var _ = templruntime.GeneratedTemplate |
||||
@ -0,0 +1,223 @@ |
|||||
|
package app |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash" |
||||
|
) |
||||
|
|
||||
|
templ EcVolumeDetails(data dash.EcVolumeDetailsData) { |
||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> |
||||
|
<div> |
||||
|
<h1 class="h2"> |
||||
|
<i class="fas fa-th-large me-2"></i>EC Volume Details |
||||
|
</h1> |
||||
|
<nav aria-label="breadcrumb"> |
||||
|
<ol class="breadcrumb"> |
||||
|
<li class="breadcrumb-item"><a href="/admin" class="text-decoration-none">Dashboard</a></li> |
||||
|
<li class="breadcrumb-item"><a href="/cluster/ec-shards" class="text-decoration-none">EC Shards</a></li> |
||||
|
<li class="breadcrumb-item active" aria-current="page">Volume {fmt.Sprintf("%d", data.VolumeID)}</li> |
||||
|
</ol> |
||||
|
</nav> |
||||
|
</div> |
||||
|
<div class="btn-toolbar mb-2 mb-md-0"> |
||||
|
<div class="btn-group me-2"> |
||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="history.back()"> |
||||
|
<i class="fas fa-arrow-left me-1"></i>Back |
||||
|
</button> |
||||
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="window.location.reload()"> |
||||
|
<i class="fas fa-refresh me-1"></i>Refresh |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- EC Volume Summary --> |
||||
|
<div class="row mb-4"> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card"> |
||||
|
<div class="card-header"> |
||||
|
<h5 class="card-title mb-0"> |
||||
|
<i class="fas fa-info-circle me-2"></i>Volume Information |
||||
|
</h5> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<table class="table table-borderless"> |
||||
|
<tr> |
||||
|
<td><strong>Volume ID:</strong></td> |
||||
|
<td>{fmt.Sprintf("%d", data.VolumeID)}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><strong>Collection:</strong></td> |
||||
|
<td> |
||||
|
if data.Collection != "" { |
||||
|
<span class="badge bg-info">{data.Collection}</span> |
||||
|
} else { |
||||
|
<span class="text-muted">default</span> |
||||
|
} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><strong>Status:</strong></td> |
||||
|
<td> |
||||
|
if data.IsComplete { |
||||
|
<span class="badge bg-success"> |
||||
|
<i class="fas fa-check me-1"></i>Complete ({data.TotalShards}/14 shards) |
||||
|
</span> |
||||
|
} else { |
||||
|
<span class="badge bg-warning"> |
||||
|
<i class="fas fa-exclamation-triangle me-1"></i>Incomplete ({data.TotalShards}/14 shards) |
||||
|
</span> |
||||
|
} |
||||
|
</td> |
||||
|
</tr> |
||||
|
if !data.IsComplete { |
||||
|
<tr> |
||||
|
<td><strong>Missing Shards:</strong></td> |
||||
|
<td> |
||||
|
for i, shardID := range data.MissingShards { |
||||
|
if i > 0 { |
||||
|
<span>, </span> |
||||
|
} |
||||
|
<span class="badge bg-danger">{fmt.Sprintf("%02d", shardID)}</span> |
||||
|
} |
||||
|
</td> |
||||
|
</tr> |
||||
|
} |
||||
|
<tr> |
||||
|
<td><strong>Data Centers:</strong></td> |
||||
|
<td> |
||||
|
for i, dc := range data.DataCenters { |
||||
|
if i > 0 { |
||||
|
<span>, </span> |
||||
|
} |
||||
|
<span class="badge bg-primary">{dc}</span> |
||||
|
} |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><strong>Servers:</strong></td> |
||||
|
<td> |
||||
|
<span class="text-muted">{fmt.Sprintf("%d servers", len(data.Servers))}</span> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><strong>Last Updated:</strong></td> |
||||
|
<td> |
||||
|
<span class="text-muted">{data.LastUpdated.Format("2006-01-02 15:04:05")}</span> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-6"> |
||||
|
<div class="card"> |
||||
|
<div class="card-header"> |
||||
|
<h5 class="card-title mb-0"> |
||||
|
<i class="fas fa-chart-pie me-2"></i>Shard Distribution |
||||
|
</h5> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div class="row text-center"> |
||||
|
<div class="col-4"> |
||||
|
<div class="border rounded p-3"> |
||||
|
<h3 class="text-primary mb-1">{fmt.Sprintf("%d", data.TotalShards)}</h3> |
||||
|
<small class="text-muted">Total Shards</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-4"> |
||||
|
<div class="border rounded p-3"> |
||||
|
<h3 class="text-success mb-1">{fmt.Sprintf("%d", len(data.DataCenters))}</h3> |
||||
|
<small class="text-muted">Data Centers</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-4"> |
||||
|
<div class="border rounded p-3"> |
||||
|
<h3 class="text-info mb-1">{fmt.Sprintf("%d", len(data.Servers))}</h3> |
||||
|
<small class="text-muted">Servers</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Shard Distribution Visualization --> |
||||
|
<div class="mt-3"> |
||||
|
<h6>Present Shards:</h6> |
||||
|
<div class="d-flex flex-wrap gap-1"> |
||||
|
for _, shard := range data.Shards { |
||||
|
<span class="badge bg-success me-1 mb-1">{fmt.Sprintf("%02d", shard.ShardID)}</span> |
||||
|
} |
||||
|
</div> |
||||
|
if len(data.MissingShards) > 0 { |
||||
|
<h6 class="mt-2">Missing Shards:</h6> |
||||
|
<div class="d-flex flex-wrap gap-1"> |
||||
|
for _, shardID := range data.MissingShards { |
||||
|
<span class="badge bg-secondary me-1 mb-1">{fmt.Sprintf("%02d", shardID)}</span> |
||||
|
} |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Shard Details Table --> |
||||
|
<div class="card"> |
||||
|
<div class="card-header"> |
||||
|
<h5 class="card-title mb-0"> |
||||
|
<i class="fas fa-list me-2"></i>Shard Details |
||||
|
</h5> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
if len(data.Shards) > 0 { |
||||
|
<div class="table-responsive"> |
||||
|
<table class="table table-striped table-hover"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Shard ID</th> |
||||
|
<th>Server</th> |
||||
|
<th>Data Center</th> |
||||
|
<th>Rack</th> |
||||
|
<th>Disk Type</th> |
||||
|
<th>Actions</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
for _, shard := range data.Shards { |
||||
|
<tr> |
||||
|
<td> |
||||
|
<span class="badge bg-primary">{fmt.Sprintf("%02d", shard.ShardID)}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<code class="small text-dark">{shard.Server}</code> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span class="badge bg-primary text-white">{shard.DataCenter}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span class="badge bg-secondary text-white">{shard.Rack}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span class="text-dark">{shard.DiskType}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<a href={ templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", shard.Server)) } target="_blank" class="btn btn-sm btn-primary"> |
||||
|
<i class="fas fa-external-link-alt me-1"></i>Volume Server |
||||
|
</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
} else { |
||||
|
<div class="text-center py-4"> |
||||
|
<i class="fas fa-exclamation-triangle fa-3x text-warning mb-3"></i> |
||||
|
<h5>No EC shards found</h5> |
||||
|
<p class="text-muted">This volume may not be EC encoded yet.</p> |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
@ -0,0 +1,432 @@ |
|||||
|
// Code generated by templ - DO NOT EDIT.
|
||||
|
|
||||
|
// templ: version: v0.3.906
|
||||
|
package app |
||||
|
|
||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
|
||||
|
import "github.com/a-h/templ" |
||||
|
import templruntime "github.com/a-h/templ/runtime" |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash" |
||||
|
) |
||||
|
|
||||
|
func EcVolumeDetails(data dash.EcVolumeDetailsData) templ.Component { |
||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context |
||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { |
||||
|
return templ_7745c5c3_CtxErr |
||||
|
} |
||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) |
||||
|
if !templ_7745c5c3_IsBuffer { |
||||
|
defer func() { |
||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) |
||||
|
if templ_7745c5c3_Err == nil { |
||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
ctx = templ.InitializeContext(ctx) |
||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx) |
||||
|
if templ_7745c5c3_Var1 == nil { |
||||
|
templ_7745c5c3_Var1 = templ.NopComponent |
||||
|
} |
||||
|
ctx = templ.ClearChildren(ctx) |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom\"><div><h1 class=\"h2\"><i class=\"fas fa-th-large me-2\"></i>EC Volume Details</h1><nav aria-label=\"breadcrumb\"><ol class=\"breadcrumb\"><li class=\"breadcrumb-item\"><a href=\"/admin\" class=\"text-decoration-none\">Dashboard</a></li><li class=\"breadcrumb-item\"><a href=\"/cluster/ec-shards\" class=\"text-decoration-none\">EC Shards</a></li><li class=\"breadcrumb-item active\" aria-current=\"page\">Volume ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var2 string |
||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 18, Col: 115} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</li></ol></nav></div><div class=\"btn-toolbar mb-2 mb-md-0\"><div class=\"btn-group me-2\"><button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" onclick=\"history.back()\"><i class=\"fas fa-arrow-left me-1\"></i>Back</button> <button type=\"button\" class=\"btn btn-sm btn-outline-primary\" onclick=\"window.location.reload()\"><i class=\"fas fa-refresh me-1\"></i>Refresh</button></div></div></div><!-- EC Volume Summary --><div class=\"row mb-4\"><div class=\"col-md-6\"><div class=\"card\"><div class=\"card-header\"><h5 class=\"card-title mb-0\"><i class=\"fas fa-info-circle me-2\"></i>Volume Information</h5></div><div class=\"card-body\"><table class=\"table table-borderless\"><tr><td><strong>Volume ID:</strong></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var3 string |
||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 47, Col: 65} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</td></tr><tr><td><strong>Collection:</strong></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.Collection != "" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<span class=\"badge bg-info\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var4 string |
||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Collection) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 53, Col: 80} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<span class=\"text-muted\">default</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</td></tr><tr><td><strong>Status:</strong></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.IsComplete { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<span class=\"badge bg-success\"><i class=\"fas fa-check me-1\"></i>Complete (") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var5 string |
||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.TotalShards) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 64, Col: 100} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "/14 shards)</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<span class=\"badge bg-warning\"><i class=\"fas fa-exclamation-triangle me-1\"></i>Incomplete (") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var6 string |
||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.TotalShards) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 68, Col: 117} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "/14 shards)</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</td></tr>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if !data.IsComplete { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<tr><td><strong>Missing Shards:</strong></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for i, shardID := range data.MissingShards { |
||||
|
if i > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<span>, </span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " <span class=\"badge bg-danger\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var7 string |
||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", shardID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 81, Col: 99} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</td></tr>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<tr><td><strong>Data Centers:</strong></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for i, dc := range data.DataCenters { |
||||
|
if i > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<span>, </span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " <span class=\"badge bg-primary\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var8 string |
||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(dc) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 93, Col: 70} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</td></tr><tr><td><strong>Servers:</strong></td><td><span class=\"text-muted\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var9 string |
||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d servers", len(data.Servers))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 100, Col: 102} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</span></td></tr><tr><td><strong>Last Updated:</strong></td><td><span class=\"text-muted\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var10 string |
||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastUpdated.Format("2006-01-02 15:04:05")) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 106, Col: 104} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</span></td></tr></table></div></div></div><div class=\"col-md-6\"><div class=\"card\"><div class=\"card-header\"><h5 class=\"card-title mb-0\"><i class=\"fas fa-chart-pie me-2\"></i>Shard Distribution</h5></div><div class=\"card-body\"><div class=\"row text-center\"><div class=\"col-4\"><div class=\"border rounded p-3\"><h3 class=\"text-primary mb-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var11 string |
||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 125, Col: 98} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</h3><small class=\"text-muted\">Total Shards</small></div></div><div class=\"col-4\"><div class=\"border rounded p-3\"><h3 class=\"text-success mb-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var12 string |
||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(data.DataCenters))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 131, Col: 103} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</h3><small class=\"text-muted\">Data Centers</small></div></div><div class=\"col-4\"><div class=\"border rounded p-3\"><h3 class=\"text-info mb-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var13 string |
||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(data.Servers))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 137, Col: 96} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</h3><small class=\"text-muted\">Servers</small></div></div></div><!-- Shard Distribution Visualization --><div class=\"mt-3\"><h6>Present Shards:</h6><div class=\"d-flex flex-wrap gap-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for _, shard := range data.Shards { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<span class=\"badge bg-success me-1 mb-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var14 string |
||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", shard.ShardID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 148, Col: 108} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if len(data.MissingShards) > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<h6 class=\"mt-2\">Missing Shards:</h6><div class=\"d-flex flex-wrap gap-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for _, shardID := range data.MissingShards { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<span class=\"badge bg-secondary me-1 mb-1\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var15 string |
||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", shardID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 155, Col: 108} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div></div></div></div></div><!-- Shard Details Table --><div class=\"card\"><div class=\"card-header\"><h5 class=\"card-title mb-0\"><i class=\"fas fa-list me-2\"></i>Shard Details</h5></div><div class=\"card-body\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if len(data.Shards) > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<div class=\"table-responsive\"><table class=\"table table-striped table-hover\"><thead><tr><th>Shard ID</th><th>Server</th><th>Data Center</th><th>Rack</th><th>Disk Type</th><th>Actions</th></tr></thead> <tbody>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for _, shard := range data.Shards { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<tr><td><span class=\"badge bg-primary\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var16 string |
||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d", shard.ShardID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 190, Col: 110} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></td><td><code class=\"small text-dark\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var17 string |
||||
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(shard.Server) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 193, Col: 87} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</code></td><td><span class=\"badge bg-primary text-white\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var18 string |
||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(shard.DataCenter) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 196, Col: 103} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</span></td><td><span class=\"badge bg-secondary text-white\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var19 string |
||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(shard.Rack) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 199, Col: 99} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</span></td><td><span class=\"text-dark\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var20 string |
||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(shard.DiskType) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 202, Col: 83} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</span></td><td><a href=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var21 templ.SafeURL |
||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", shard.Server))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/ec_volume_details.templ`, Line: 205, Col: 121} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\" target=\"_blank\" class=\"btn btn-sm btn-primary\"><i class=\"fas fa-external-link-alt me-1\"></i>Volume Server</a></td></tr>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</tbody></table></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div class=\"text-center py-4\"><i class=\"fas fa-exclamation-triangle fa-3x text-warning mb-3\"></i><h5>No EC shards found</h5><p class=\"text-muted\">This volume may not be EC encoded yet.</p></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</div></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
return nil |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
var _ = templruntime.GeneratedTemplate |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue