Browse Source
ec is mostly working now
ec is mostly working now
* distribution of ec shards needs improvement * need configuration to enable ecworker-execute-ec-tasks
17 changed files with 3056 additions and 688 deletions
-
339weed/admin/dash/ec_shard_management.go
-
45weed/admin/dash/types.go
-
50weed/admin/handlers/cluster_handlers.go
-
10weed/admin/view/app/cluster_ec_shards.templ
-
165weed/admin/view/app/cluster_ec_shards_templ.go
-
490weed/admin/view/app/cluster_ec_volumes.templ
-
845weed/admin/view/app/cluster_ec_volumes_templ.go
-
2weed/admin/view/app/ec_volume_details.templ
-
2weed/admin/view/app/ec_volume_details_templ.go
-
2weed/admin/view/layout/layout.templ
-
2weed/admin/view/layout/layout_templ.go
-
23weed/pb/volume_server.proto
-
966weed/pb/volume_server_pb/volume_server.pb.go
-
43weed/pb/volume_server_pb/volume_server_grpc.pb.go
-
117weed/server/volume_grpc_copy.go
-
5weed/server/volume_server_handlers_admin.go
-
638weed/worker/tasks/erasure_coding/ec.go
@ -0,0 +1,490 @@ |
|||||
|
package app |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"strings" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash" |
||||
|
) |
||||
|
|
||||
|
templ ClusterEcVolumes(data dash.ClusterEcVolumesData) { |
||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<title>EC Volumes - SeaweedFS</title> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container-fluid"> |
||||
|
<div class="row"> |
||||
|
<div class="col-12"> |
||||
|
<h2 class="mb-4"> |
||||
|
<i class="fas fa-database me-2"></i>EC Volumes |
||||
|
<small class="text-muted">({fmt.Sprintf("%d", data.TotalVolumes)} volumes)</small> |
||||
|
</h2> |
||||
|
</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 Volumes</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.TotalVolumes)}</h4> |
||||
|
<small>EC encoded volumes</small> |
||||
|
</div> |
||||
|
<div class="align-self-center"> |
||||
|
<i class="fas fa-cubes 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">Total Shards</h6> |
||||
|
<h4 class="mb-0">{fmt.Sprintf("%d", data.TotalShards)}</h4> |
||||
|
<small>Distributed shards</small> |
||||
|
</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-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.CompleteVolumes)}</h4> |
||||
|
<small>All shards present</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.IncompleteVolumes)}</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> |
||||
|
|
||||
|
<!-- Volumes Table --> |
||||
|
<div class="d-flex justify-content-between align-items-center mb-3"> |
||||
|
<div class="d-flex align-items-center"> |
||||
|
<span class="me-3"> |
||||
|
Showing {fmt.Sprintf("%d", (data.Page-1)*data.PageSize + 1)} to {fmt.Sprintf("%d", func() int { |
||||
|
end := data.Page * data.PageSize |
||||
|
if end > data.TotalVolumes { |
||||
|
return data.TotalVolumes |
||||
|
} |
||||
|
return end |
||||
|
}())} of {fmt.Sprintf("%d", data.TotalVolumes)} volumes |
||||
|
</span> |
||||
|
|
||||
|
<div class="d-flex align-items-center"> |
||||
|
<label for="pageSize" class="form-label me-2 mb-0">Show:</label> |
||||
|
<select id="pageSize" class="form-select form-select-sm" style="width: auto;" onchange="changePageSize(this.value)"> |
||||
|
<option value="5" if data.PageSize == 5 { selected }>5</option> |
||||
|
<option value="10" if data.PageSize == 10 { selected }>10</option> |
||||
|
<option value="25" if data.PageSize == 25 { selected }>25</option> |
||||
|
<option value="50" if data.PageSize == 50 { selected }>50</option> |
||||
|
<option value="100" if data.PageSize == 100 { selected }>100</option> |
||||
|
</select> |
||||
|
<span class="ms-2">per page</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
if data.Collection != "" { |
||||
|
<div> |
||||
|
<span class="badge bg-info">Collection: {data.Collection}</span> |
||||
|
<a href="/cluster/ec-shards" class="btn btn-sm btn-outline-secondary ms-2">Clear Filter</a> |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
|
||||
|
<div class="table-responsive"> |
||||
|
<table class="table table-striped table-hover" id="ecVolumesTable"> |
||||
|
<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> |
||||
|
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('total_shards')" class="text-dark text-decoration-none"> |
||||
|
Shard Count |
||||
|
if data.SortBy == "total_shards" { |
||||
|
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">Shard Locations</th> |
||||
|
<th> |
||||
|
<a href="#" onclick="sortBy('completeness')" class="text-dark text-decoration-none"> |
||||
|
Status |
||||
|
if data.SortBy == "completeness" { |
||||
|
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 class="text-dark">Data Centers</th> |
||||
|
} |
||||
|
<th class="text-dark">Actions</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
for _, volume := range data.EcVolumes { |
||||
|
<tr> |
||||
|
<td> |
||||
|
<strong>{fmt.Sprintf("%d", volume.VolumeID)}</strong> |
||||
|
</td> |
||||
|
if data.ShowCollectionColumn { |
||||
|
<td> |
||||
|
if volume.Collection != "" { |
||||
|
<span class="badge bg-outline-info">{volume.Collection}</span> |
||||
|
} else { |
||||
|
<span class="text-muted">default</span> |
||||
|
} |
||||
|
</td> |
||||
|
} |
||||
|
<td> |
||||
|
<span class="badge bg-primary">{fmt.Sprintf("%d/14", volume.TotalShards)}</span> |
||||
|
</td> |
||||
|
<td> |
||||
|
<div class="shard-locations" style="max-width: 400px;"> |
||||
|
@displayShardLocationsHTML(volume.ShardLocations) |
||||
|
</div> |
||||
|
</td> |
||||
|
<td> |
||||
|
if volume.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(volume.MissingShards))} shards |
||||
|
</span> |
||||
|
if len(volume.MissingShards) > 0 { |
||||
|
<br/> |
||||
|
<small class="text-muted"> |
||||
|
Missing: {formatMissingShards(volume.MissingShards)} |
||||
|
</small> |
||||
|
} |
||||
|
} |
||||
|
</td> |
||||
|
if data.ShowDataCenterColumn { |
||||
|
<td> |
||||
|
for i, dc := range volume.DataCenters { |
||||
|
if i > 0 { |
||||
|
<span>, </span> |
||||
|
} |
||||
|
<span class="badge bg-primary text-white">{dc}</span> |
||||
|
} |
||||
|
</td> |
||||
|
} |
||||
|
<td> |
||||
|
<div class="btn-group" role="group"> |
||||
|
<button type="button" class="btn btn-sm btn-outline-primary" |
||||
|
onclick="showVolumeDetails(event)" |
||||
|
data-volume-id={ fmt.Sprintf("%d", volume.VolumeID) } |
||||
|
title="View EC volume details"> |
||||
|
<i class="fas fa-info-circle"></i> |
||||
|
</button> |
||||
|
if !volume.IsComplete { |
||||
|
<button type="button" class="btn btn-sm btn-outline-warning" |
||||
|
onclick="repairVolume(event)" |
||||
|
data-volume-id={ fmt.Sprintf("%d", volume.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 Volumes pagination"> |
||||
|
<ul class="pagination justify-content-center"> |
||||
|
if data.Page > 1 { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page="1">First</a> |
||||
|
</li> |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.Page-1) }>Previous</a> |
||||
|
</li> |
||||
|
} |
||||
|
|
||||
|
for i := 1; i <= data.TotalPages; i++ { |
||||
|
if i == data.Page { |
||||
|
<li class="page-item active"> |
||||
|
<span class="page-link">{fmt.Sprintf("%d", i)}</span> |
||||
|
</li> |
||||
|
} else if i <= 3 || i > data.TotalPages-3 || (i >= data.Page-2 && i <= data.Page+2) { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", i) }>{fmt.Sprintf("%d", i)}</a> |
||||
|
</li> |
||||
|
} else if i == 4 && data.Page > 6 { |
||||
|
<li class="page-item disabled"> |
||||
|
<span class="page-link">...</span> |
||||
|
</li> |
||||
|
} else if i == data.TotalPages-3 && data.Page < data.TotalPages-5 { |
||||
|
<li class="page-item disabled"> |
||||
|
<span class="page-link">...</span> |
||||
|
</li> |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if data.Page < data.TotalPages { |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.Page+1) }>Next</a> |
||||
|
</li> |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="#" onclick="goToPage(event)" data-page={ fmt.Sprintf("%d", data.TotalPages) }>Last</a> |
||||
|
</li> |
||||
|
} |
||||
|
</ul> |
||||
|
</nav> |
||||
|
} |
||||
|
</div> |
||||
|
|
||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> |
||||
|
<script> |
||||
|
// Sorting functionality |
||||
|
function sortBy(field) { |
||||
|
const currentSort = new URLSearchParams(window.location.search).get('sort_by'); |
||||
|
const currentOrder = new URLSearchParams(window.location.search).get('sort_order') || 'asc'; |
||||
|
|
||||
|
let newOrder = 'asc'; |
||||
|
if (currentSort === field && currentOrder === 'asc') { |
||||
|
newOrder = 'desc'; |
||||
|
} |
||||
|
|
||||
|
const url = new URL(window.location); |
||||
|
url.searchParams.set('sort_by', field); |
||||
|
url.searchParams.set('sort_order', newOrder); |
||||
|
url.searchParams.set('page', '1'); // Reset to first page |
||||
|
window.location.href = url.toString(); |
||||
|
} |
||||
|
|
||||
|
// Pagination functionality |
||||
|
function goToPage(event) { |
||||
|
event.preventDefault(); |
||||
|
const page = event.target.closest('a').getAttribute('data-page'); |
||||
|
const url = new URL(window.location); |
||||
|
url.searchParams.set('page', page); |
||||
|
window.location.href = url.toString(); |
||||
|
} |
||||
|
|
||||
|
// Page size functionality |
||||
|
function changePageSize(newPageSize) { |
||||
|
const url = new URL(window.location); |
||||
|
url.searchParams.set('page_size', newPageSize); |
||||
|
url.searchParams.set('page', '1'); // Reset to first page when changing page size |
||||
|
window.location.href = url.toString(); |
||||
|
} |
||||
|
|
||||
|
// Volume details |
||||
|
function showVolumeDetails(event) { |
||||
|
const volumeId = event.target.closest('button').getAttribute('data-volume-id'); |
||||
|
window.location.href = `/cluster/ec-volumes/${volumeId}`; |
||||
|
} |
||||
|
|
||||
|
// Repair volume |
||||
|
function repairVolume(event) { |
||||
|
const volumeId = event.target.closest('button').getAttribute('data-volume-id'); |
||||
|
if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) { |
||||
|
// TODO: Implement repair functionality |
||||
|
alert('Repair functionality will be implemented soon.'); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
|
} |
||||
|
|
||||
|
// displayShardLocationsHTML renders shard locations as proper HTML |
||||
|
templ displayShardLocationsHTML(shardLocations map[int]string) { |
||||
|
if len(shardLocations) == 0 { |
||||
|
<span class="text-muted">No shards</span> |
||||
|
} else { |
||||
|
for i, serverInfo := range groupShardsByServer(shardLocations) { |
||||
|
if i > 0 { |
||||
|
<br/> |
||||
|
} |
||||
|
<strong> |
||||
|
<a href={ templ.URL("/cluster/volume-servers/" + serverInfo.Server) } class="text-primary text-decoration-none"> |
||||
|
{ serverInfo.Server } |
||||
|
</a>: |
||||
|
</strong> { serverInfo.ShardRanges } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ServerShardInfo represents server and its shard ranges |
||||
|
type ServerShardInfo struct { |
||||
|
Server string |
||||
|
ShardRanges string |
||||
|
} |
||||
|
|
||||
|
// groupShardsByServer groups shards by server and formats ranges |
||||
|
func groupShardsByServer(shardLocations map[int]string) []ServerShardInfo { |
||||
|
if len(shardLocations) == 0 { |
||||
|
return []ServerShardInfo{} |
||||
|
} |
||||
|
|
||||
|
// Group shards by server |
||||
|
serverShards := make(map[string][]int) |
||||
|
for shardId, server := range shardLocations { |
||||
|
serverShards[server] = append(serverShards[server], shardId) |
||||
|
} |
||||
|
|
||||
|
var serverInfos []ServerShardInfo |
||||
|
for server, shards := range serverShards { |
||||
|
// Sort shards for each server |
||||
|
for i := 0; i < len(shards); i++ { |
||||
|
for j := i + 1; j < len(shards); j++ { |
||||
|
if shards[i] > shards[j] { |
||||
|
shards[i], shards[j] = shards[j], shards[i] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Format shard ranges compactly |
||||
|
shardRanges := formatShardRanges(shards) |
||||
|
serverInfos = append(serverInfos, ServerShardInfo{ |
||||
|
Server: server, |
||||
|
ShardRanges: shardRanges, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Sort by server name |
||||
|
for i := 0; i < len(serverInfos); i++ { |
||||
|
for j := i + 1; j < len(serverInfos); j++ { |
||||
|
if serverInfos[i].Server > serverInfos[j].Server { |
||||
|
serverInfos[i], serverInfos[j] = serverInfos[j], serverInfos[i] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return serverInfos |
||||
|
} |
||||
|
|
||||
|
// Helper function to format shard ranges compactly (e.g., "0-3,7,9-11") |
||||
|
func formatShardRanges(shards []int) string { |
||||
|
if len(shards) == 0 { |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
var ranges []string |
||||
|
start := shards[0] |
||||
|
end := shards[0] |
||||
|
|
||||
|
for i := 1; i < len(shards); i++ { |
||||
|
if shards[i] == end+1 { |
||||
|
end = shards[i] |
||||
|
} else { |
||||
|
if start == end { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d", start)) |
||||
|
} else { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d-%d", start, end)) |
||||
|
} |
||||
|
start = shards[i] |
||||
|
end = shards[i] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Add the last range |
||||
|
if start == end { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d", start)) |
||||
|
} else { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d-%d", start, end)) |
||||
|
} |
||||
|
|
||||
|
return strings.Join(ranges, ",") |
||||
|
} |
||||
|
|
||||
|
// Helper function to format missing shards |
||||
|
func formatMissingShards(missingShards []int) string { |
||||
|
if len(missingShards) == 0 { |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
var shardStrs []string |
||||
|
for _, shard := range missingShards { |
||||
|
shardStrs = append(shardStrs, fmt.Sprintf("%d", shard)) |
||||
|
} |
||||
|
|
||||
|
return strings.Join(shardStrs, ", ") |
||||
|
} |
||||
@ -0,0 +1,845 @@ |
|||||
|
// 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" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
func ClusterEcVolumes(data dash.ClusterEcVolumesData) 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, "<!doctype html><html lang=\"en\"><head><title>EC Volumes - SeaweedFS</title><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\"><link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\"></head><body><div class=\"container-fluid\"><div class=\"row\"><div class=\"col-12\"><h2 class=\"mb-4\"><i class=\"fas fa-database me-2\"></i>EC Volumes <small class=\"text-muted\">(") |
||||
|
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.TotalVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 25, Col: 84} |
||||
|
} |
||||
|
_, 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, " volumes)</small></h2></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 Volumes</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.TotalVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 38, Col: 86} |
||||
|
} |
||||
|
_, 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, "</h4><small>EC encoded volumes</small></div><div class=\"align-self-center\"><i class=\"fas fa-cubes 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\">Total Shards</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.TotalShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 54, Col: 85} |
||||
|
} |
||||
|
_, 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, 4, "</h4><small>Distributed shards</small></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-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.CompleteVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 70, Col: 89} |
||||
|
} |
||||
|
_, 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, 5, "</h4><small>All shards present</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.IncompleteVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 86, Col: 91} |
||||
|
} |
||||
|
_, 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, 6, "</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><!-- Volumes Table --><div class=\"d-flex justify-content-between align-items-center mb-3\"><div class=\"d-flex align-items-center\"><span class=\"me-3\">Showing ") |
||||
|
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", (data.Page-1)*data.PageSize+1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 102, Col: 79} |
||||
|
} |
||||
|
_, 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, 7, " to ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var8 string |
||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", func() int { |
||||
|
end := data.Page * data.PageSize |
||||
|
if end > data.TotalVolumes { |
||||
|
return data.TotalVolumes |
||||
|
} |
||||
|
return end |
||||
|
}())) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 108, Col: 24} |
||||
|
} |
||||
|
_, 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, 8, " of ") |
||||
|
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", data.TotalVolumes)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 108, Col: 66} |
||||
|
} |
||||
|
_, 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, 9, " volumes</span><div class=\"d-flex align-items-center\"><label for=\"pageSize\" class=\"form-label me-2 mb-0\">Show:</label> <select id=\"pageSize\" class=\"form-select form-select-sm\" style=\"width: auto;\" onchange=\"changePageSize(this.value)\"><option value=\"5\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 5 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " selected") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">5</option> <option value=\"10\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 10 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " selected") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ">10</option> <option value=\"25\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 25 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " selected") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, ">25</option> <option value=\"50\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.PageSize == 50 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " selected") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, ">50</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, 18, " selected") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, ">100</option></select> <span class=\"ms-2\">per page</span></div></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.Collection != "" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div><span class=\"badge bg-info\">Collection: ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var10 string |
||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.Collection) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 126, Col: 76} |
||||
|
} |
||||
|
_, 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, 21, "</span> <a href=\"/cluster/ec-shards\" class=\"btn btn-sm btn-outline-secondary ms-2\">Clear Filter</a></div>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</div><div class=\"table-responsive\"><table class=\"table table-striped table-hover\" id=\"ecVolumesTable\"><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, 23, "<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, 24, "<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, 25, "<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, 26, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowCollectionColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<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, 28, "<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, 29, "<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, 30, "<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, 31, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<th><a href=\"#\" onclick=\"sortBy('total_shards')\" class=\"text-dark text-decoration-none\">Shard Count ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "total_shards" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<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, 34, "<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, 35, "<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, 36, "</a></th><th class=\"text-dark\">Shard Locations</th><th><a href=\"#\" onclick=\"sortBy('completeness')\" class=\"text-dark text-decoration-none\">Status ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.SortBy == "completeness" { |
||||
|
if data.SortOrder == "asc" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<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, 38, "<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, 39, "<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, 40, "</a></th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowDataCenterColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<th class=\"text-dark\">Data Centers</th>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<th class=\"text-dark\">Actions</th></tr></thead> <tbody>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for _, volume := range data.EcVolumes { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<tr><td><strong>") |
||||
|
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", volume.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 205, Col: 75} |
||||
|
} |
||||
|
_, 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, 44, "</strong></td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowCollectionColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if volume.Collection != "" { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<span class=\"badge bg-outline-info\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var12 string |
||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(volume.Collection) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 210, Col: 94} |
||||
|
} |
||||
|
_, 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, 47, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<span class=\"text-muted\">default</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<td><span class=\"badge bg-primary\">") |
||||
|
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/14", volume.TotalShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 217, Col: 104} |
||||
|
} |
||||
|
_, 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, 51, "</span></td><td><div class=\"shard-locations\" style=\"max-width: 400px;\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = displayShardLocationsHTML(volume.ShardLocations).Render(ctx, templ_7745c5c3_Buffer) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</div></td><td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if volume.IsComplete { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<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, 54, "<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_Var14 string |
||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(volume.MissingShards))) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 232, Col: 93} |
||||
|
} |
||||
|
_, 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, 55, " shards</span> ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if len(volume.MissingShards) > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<br><small class=\"text-muted\">Missing: ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var15 string |
||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(formatMissingShards(volume.MissingShards)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 237, Col: 95} |
||||
|
} |
||||
|
_, 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, 57, "</small>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.ShowDataCenterColumn { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
for i, dc := range volume.DataCenters { |
||||
|
if i > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<span>, </span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " <span class=\"badge bg-primary text-white\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var16 string |
||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(dc) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 248, Col: 85} |
||||
|
} |
||||
|
_, 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, 62, "</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</td>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<td><div class=\"btn-group\" role=\"group\"><button type=\"button\" class=\"btn btn-sm btn-outline-primary\" onclick=\"showVolumeDetails(event)\" data-volume-id=\"") |
||||
|
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", volume.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 256, Col: 95} |
||||
|
} |
||||
|
_, 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, 65, "\" title=\"View EC volume details\"><i class=\"fas fa-info-circle\"></i></button> ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if !volume.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_Var18 string |
||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", volume.VolumeID)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 263, Col: 99} |
||||
|
} |
||||
|
_, 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, 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 Volumes pagination\"><ul class=\"pagination justify-content-center\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
if data.Page > 1 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<li class=\"page-item\"><a class=\"page-link\" href=\"#\" onclick=\"goToPage(event)\" data-page=\"1\">First</a></li><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_Var19 string |
||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.Page-1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 285, Col: 126} |
||||
|
} |
||||
|
_, 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, 72, "\">Previous</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
for i := 1; i <= data.TotalPages; i++ { |
||||
|
if i == data.Page { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<li class=\"page-item active\"><span class=\"page-link\">") |
||||
|
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", i)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 292, Col: 77} |
||||
|
} |
||||
|
_, 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, 74, "</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else if i <= 3 || i > data.TotalPages-3 || (i >= data.Page-2 && i <= data.Page+2) { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<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_Var21 string |
||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 296, Col: 120} |
||||
|
} |
||||
|
_, 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, 76, "\">") |
||||
|
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", i)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 296, Col: 144} |
||||
|
} |
||||
|
_, 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, 77, "</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else if i == 4 && data.Page > 6 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "<li class=\"page-item disabled\"><span class=\"page-link\">...</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else if i == data.TotalPages-3 && data.Page < data.TotalPages-5 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<li class=\"page-item disabled\"><span class=\"page-link\">...</span></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if data.Page < data.TotalPages { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<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_Var23 string |
||||
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.Page+1)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 311, Col: 126} |
||||
|
} |
||||
|
_, 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, 81, "\">Next</a></li><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.TotalPages)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 314, Col: 130} |
||||
|
} |
||||
|
_, 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, 82, "\">Last</a></li>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</ul></nav>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</div><script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script><script>\n // Sorting functionality\n function sortBy(field) {\n const currentSort = new URLSearchParams(window.location.search).get('sort_by');\n const currentOrder = new URLSearchParams(window.location.search).get('sort_order') || 'asc';\n \n let newOrder = 'asc';\n if (currentSort === field && currentOrder === 'asc') {\n newOrder = 'desc';\n }\n \n const url = new URL(window.location);\n url.searchParams.set('sort_by', field);\n url.searchParams.set('sort_order', newOrder);\n url.searchParams.set('page', '1'); // Reset to first page\n window.location.href = url.toString();\n }\n\n // Pagination functionality\n function goToPage(event) {\n event.preventDefault();\n const page = event.target.closest('a').getAttribute('data-page');\n const url = new URL(window.location);\n url.searchParams.set('page', page);\n window.location.href = url.toString();\n }\n\n // Page size functionality\n function changePageSize(newPageSize) {\n const url = new URL(window.location);\n url.searchParams.set('page_size', newPageSize);\n url.searchParams.set('page', '1'); // Reset to first page when changing page size\n window.location.href = url.toString();\n }\n\n // Volume details\n function showVolumeDetails(event) {\n const volumeId = event.target.closest('button').getAttribute('data-volume-id');\n window.location.href = `/cluster/ec-volumes/${volumeId}`;\n }\n\n // Repair volume\n function repairVolume(event) {\n const volumeId = event.target.closest('button').getAttribute('data-volume-id');\n if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) {\n // TODO: Implement repair functionality\n alert('Repair functionality will be implemented soon.');\n }\n }\n </script></body></html>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
return nil |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// displayShardLocationsHTML renders shard locations as proper HTML
|
||||
|
func displayShardLocationsHTML(shardLocations map[int]string) 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_Var25 := templ.GetChildren(ctx) |
||||
|
if templ_7745c5c3_Var25 == nil { |
||||
|
templ_7745c5c3_Var25 = templ.NopComponent |
||||
|
} |
||||
|
ctx = templ.ClearChildren(ctx) |
||||
|
if len(shardLocations) == 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<span class=\"text-muted\">No shards</span>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} else { |
||||
|
for i, serverInfo := range groupShardsByServer(shardLocations) { |
||||
|
if i > 0 { |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<br>") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, " <strong><a href=\"") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var26 templ.SafeURL |
||||
|
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(templ.URL("/cluster/volume-servers/" + serverInfo.Server)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 387, Col: 71} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "\" class=\"text-primary text-decoration-none\">") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var27 string |
||||
|
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(serverInfo.Server) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 388, Col: 24} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</a>:</strong> ") |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
var templ_7745c5c3_Var28 string |
||||
|
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(serverInfo.ShardRanges) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/cluster_ec_volumes.templ`, Line: 390, Col: 37} |
||||
|
} |
||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) |
||||
|
if templ_7745c5c3_Err != nil { |
||||
|
return templ_7745c5c3_Err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// ServerShardInfo represents server and its shard ranges
|
||||
|
type ServerShardInfo struct { |
||||
|
Server string |
||||
|
ShardRanges string |
||||
|
} |
||||
|
|
||||
|
// groupShardsByServer groups shards by server and formats ranges
|
||||
|
func groupShardsByServer(shardLocations map[int]string) []ServerShardInfo { |
||||
|
if len(shardLocations) == 0 { |
||||
|
return []ServerShardInfo{} |
||||
|
} |
||||
|
|
||||
|
// Group shards by server
|
||||
|
serverShards := make(map[string][]int) |
||||
|
for shardId, server := range shardLocations { |
||||
|
serverShards[server] = append(serverShards[server], shardId) |
||||
|
} |
||||
|
|
||||
|
var serverInfos []ServerShardInfo |
||||
|
for server, shards := range serverShards { |
||||
|
// Sort shards for each server
|
||||
|
for i := 0; i < len(shards); i++ { |
||||
|
for j := i + 1; j < len(shards); j++ { |
||||
|
if shards[i] > shards[j] { |
||||
|
shards[i], shards[j] = shards[j], shards[i] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Format shard ranges compactly
|
||||
|
shardRanges := formatShardRanges(shards) |
||||
|
serverInfos = append(serverInfos, ServerShardInfo{ |
||||
|
Server: server, |
||||
|
ShardRanges: shardRanges, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Sort by server name
|
||||
|
for i := 0; i < len(serverInfos); i++ { |
||||
|
for j := i + 1; j < len(serverInfos); j++ { |
||||
|
if serverInfos[i].Server > serverInfos[j].Server { |
||||
|
serverInfos[i], serverInfos[j] = serverInfos[j], serverInfos[i] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return serverInfos |
||||
|
} |
||||
|
|
||||
|
// Helper function to format shard ranges compactly (e.g., "0-3,7,9-11")
|
||||
|
func formatShardRanges(shards []int) string { |
||||
|
if len(shards) == 0 { |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
var ranges []string |
||||
|
start := shards[0] |
||||
|
end := shards[0] |
||||
|
|
||||
|
for i := 1; i < len(shards); i++ { |
||||
|
if shards[i] == end+1 { |
||||
|
end = shards[i] |
||||
|
} else { |
||||
|
if start == end { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d", start)) |
||||
|
} else { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d-%d", start, end)) |
||||
|
} |
||||
|
start = shards[i] |
||||
|
end = shards[i] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Add the last range
|
||||
|
if start == end { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d", start)) |
||||
|
} else { |
||||
|
ranges = append(ranges, fmt.Sprintf("%d-%d", start, end)) |
||||
|
} |
||||
|
|
||||
|
return strings.Join(ranges, ",") |
||||
|
} |
||||
|
|
||||
|
// Helper function to format missing shards
|
||||
|
func formatMissingShards(missingShards []int) string { |
||||
|
if len(missingShards) == 0 { |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
var shardStrs []string |
||||
|
for _, shard := range missingShards { |
||||
|
shardStrs = append(shardStrs, fmt.Sprintf("%d", shard)) |
||||
|
} |
||||
|
|
||||
|
return strings.Join(shardStrs, ", ") |
||||
|
} |
||||
|
|
||||
|
var _ = templruntime.GeneratedTemplate |
||||
966
weed/pb/volume_server_pb/volume_server.pb.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue