Skip to content

Commit

Permalink
[#21357] yugabyted: Display the list of backup and restore tasks run …
Browse files Browse the repository at this point in the history
…in the YugabyteDB cluster.

Summary:
Created `/backup` and `/restore` APIs for getting the list of previously run backup and restore tasks.
Jira: DB-10254

Test Plan: Manual Testing.

Reviewers: nikhil, sgarg-yb, djiang

Reviewed By: nikhil, djiang

Subscribers: djiang, shikhar.sahay

Differential Revision: https://phorge.dev.yugabyte.com/D33372
  • Loading branch information
ShikharSahay committed Mar 25, 2024
1 parent b4f4b80 commit 5e5b564
Show file tree
Hide file tree
Showing 24 changed files with 925 additions and 12 deletions.
2 changes: 2 additions & 0 deletions bin/yugabyted
Original file line number Diff line number Diff line change
Expand Up @@ -2364,6 +2364,8 @@ class ControlScript(object):
get("master_webserver_port")),
"-tserver_ui_port={}".format(self.configs.saved_data.
get("tserver_webserver_port")),
"-data_dir={}".format(self.configs.saved_data.
get("data_dir")),
"-warnings={}".format("|".join(warnings_for_ui))
]
if self.configs.saved_data.get("secure"):
Expand Down
166 changes: 166 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/.docs/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ servers:
security:
- BearerAuthToken: []
tags:
- description: APIs for getting backup Details
name: backup-info
- description: APIs for cluster CRUD
name: cluster
- description: APIs for getting information about an existing cluster
name: cluster-info
- description: APIs for getting Point-in-Time Recovery (PITR) schedules
name: pitr-info
- description: APIs for getting restore Details
name: restore-info
- description: APIs for getting information about Voyager migrations
name: voyager-info
- description: APIs for getting Voyager data migrations metrics
Expand Down Expand Up @@ -835,6 +839,34 @@ paths:
summary: Get PITR Schedules
tags:
- pitr-info
/restore:
get:
description: Retrieve the list of databases on which restore is run in the YugabyteDB
cluster.
operationId: getRestoreDetails
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/RestoreDetails'
description: Retrieve the list of databases on which restore is run in the
YugabyteDB cluster.
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
description: API Error
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
description: API Error
summary: Get Restore Details
tags:
- restore-info
components:
requestBodies:
ClusterSpec:
Expand Down Expand Up @@ -970,6 +1002,13 @@ components:
schema:
$ref: '#/components/schemas/PITRSchedule'
description: List of PITR Schedules in the cluster
RestoreDetailsListResponse:
content:
application/json:
schema:
$ref: '#/components/schemas/RestoreDetails'
description: Retrieve the list of databases on which restore is run in the YugabyteDB
cluster.
schemas:
VoyagerMigrationDetails:
description: Information regarding a specific voyager migration task
Expand Down Expand Up @@ -2641,6 +2680,101 @@ components:
- retention
title: List of PITR Schedules in the cluster
type: object
BackupDetails:
description: Details of backup enabled on databases in YugabyteDB cluster
example:
bytes_transferred: bytes_transferred
actual_size: actual_size
user_operation: user_operation
task_status: task_status
database_keyspace: database_keyspace
ybdb_api: ybdb_api
ybc_task_id: ybc_task_id
tserver_ip: tserver_ip
time_taken: time_taken
task_start_time: task_start_time
properties:
ybc_task_id:
type: string
tserver_ip:
type: string
user_operation:
type: string
ybdb_api:
type: string
database_keyspace:
type: string
task_start_time:
type: string
task_status:
type: string
time_taken:
type: string
bytes_transferred:
type: string
actual_size:
type: string
required:
- actual_size
- bytes_transferred
- database_keyspace
- task_start_time
- task_status
- time_taken
- tserver_ip
- user_operation
- ybc_task_id
- ybdb_api
title: Get Backup Details
type: object
RestoreDetails:
description: Details of databases in YugabyteDB cluster on which restore operation
in run
example:
bytes_transferred: bytes_transferred
actual_size: actual_size
user_operation: user_operation
task_status: task_status
database_keyspace: database_keyspace
ybdb_api: ybdb_api
ybc_task_id: ybc_task_id
tserver_ip: tserver_ip
time_taken: time_taken
task_start_time: task_start_time
properties:
ybc_task_id:
type: string
tserver_ip:
type: string
user_operation:
type: string
ybdb_api:
type: string
database_keyspace:
type: string
task_start_time:
type: string
task_status:
type: string
time_taken:
type: string
bytes_transferred:
type: string
actual_size:
type: string
required:
- actual_size
- bytes_transferred
- database_keyspace
- task_start_time
- task_status
- time_taken
- tserver_ip
- user_operation
- ybc_task_id
- ybdb_api
title: Get Restore Details
type: object
API_for_Plan_and_Asses_page:
example:
data:
Expand Down Expand Up @@ -2985,6 +3119,38 @@ components:
- data
title: Alerts Response
type: object
RestoreDetails:
example:
schedules:
- bytes_transferred: bytes_transferred
actual_size: actual_size
user_operation: user_operation
task_status: task_status
database_keyspace: database_keyspace
ybdb_api: ybdb_api
ybc_task_id: ybc_task_id
tserver_ip: tserver_ip
time_taken: time_taken
task_start_time: task_start_time
- bytes_transferred: bytes_transferred
actual_size: actual_size
user_operation: user_operation
task_status: task_status
database_keyspace: database_keyspace
ybdb_api: ybdb_api
ybc_task_id: ybc_task_id
tserver_ip: tserver_ip
time_taken: time_taken
task_start_time: task_start_time
properties:
schedules:
items:
$ref: '#/components/schemas/RestoreDetails'
type: array
uniqueItems: true
required:
- restore
type: object
ApiError_error:
properties:
detail:
Expand Down
4 changes: 4 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ models/model_api_error.go
models/model_api_error_error.go
models/model_api_for_plan_and_asses_page.go
models/model_assesment_complexity_info.go
models/model_backup_details.go
models/model_cloud_enum.go
models/model_cloud_info.go
models/model_cluster_data.go
Expand Down Expand Up @@ -35,6 +36,8 @@ models/model_health_check_info.go
models/model_health_check_response.go
models/model_individual_migration_task_info.go
models/model_schedules_response.go
models/model_backup_response.go
models/model_restore_response.go
models/model_is_load_balancer_idle.go
models/model_live_query_response_data.go
models/model_live_query_response_schema.go
Expand All @@ -55,6 +58,7 @@ models/model_placement_block.go
models/model_placement_cloud_info.go
models/model_placement_info.go
models/model_raft_config.go
models/model_restore_details.go
models/model_slow_query_response_data.go
models/model_slow_query_response_schema.go
models/model_slow_query_response_ysql_data.go
Expand Down
70 changes: 70 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/handlers/api_backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package handlers

import (
"apiserver/cmd/server/helpers"
"apiserver/cmd/server/models"
"fmt"
"net/http"
"github.com/labstack/echo/v4"
)

func (c *Container) GetBackupDetails(ctx echo.Context) error {
var operationType = "backup"
lockfilelistFuture := make(chan helpers.LockFileListFuture, 1)
c.helper.ListLockFilesFuture(helpers.DataDir, operationType, lockfilelistFuture)
lockfilelistResult := <-lockfilelistFuture
if lockfilelistResult.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to list lock files: %v", lockfilelistResult.Error))
}
if len(lockfilelistResult.Result) == 0 {
return ctx.JSON(http.StatusOK, []interface{}{})
}
var lockFileDetails helpers.LockFileDetails
lockFileInfoFutures := make([]chan helpers.LockFileInfoFuture,
len(lockfilelistResult.Result))
taskProgressInfoFutures := make([]chan helpers.TaskProgressInfoFuture,
len(lockfilelistResult.Result))

for i, lockFilePath := range lockfilelistResult.Result {
lockFileInfoFutures[i] = make(chan helpers.LockFileInfoFuture)
// Fetch lock file info asynchronously
go c.helper.GetLockFileInfoFuture(lockFilePath, lockFileInfoFutures[i])
}
// Asynchronously start task progress
for i := range lockfilelistResult.Result {
lockFileInfo := <-lockFileInfoFutures[i]
if lockFileInfo.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to parse lock files: %v", lockFileInfo.Error))
}
c.logger.Infof("Successfully read lock file for database/keyspace: %s",
lockFileInfo.DatabaseKeyspace)
lockFileDetails.Data = append(lockFileDetails.Data, lockFileInfo)
taskProgressInfoFutures[i] = make(chan helpers.TaskProgressInfoFuture)
go c.helper.GetYBCTaskProgress(lockFileInfo.YbcTaskID, lockFileInfo.TserverIP,
taskProgressInfoFutures[i])
}

var response models.BackupResponse
for i, lockFileInfo := range lockFileDetails.Data {
taskProgressInfo := <-taskProgressInfoFutures[i]
if taskProgressInfo.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to get task progress: %v", taskProgressInfo.Error))
}
response.Backup = append(response.Backup, models.BackupDetails{
YbcTaskId: lockFileInfo.YbcTaskID,
TserverIp: lockFileInfo.TserverIP,
UserOperation: lockFileInfo.UserOperation,
YbdbApi: lockFileInfo.YbdbAPI,
DatabaseKeyspace: lockFileInfo.DatabaseKeyspace,
TaskStartTime: lockFileInfo.TaskStartTime,
TaskStatus: taskProgressInfo.FinalStatus,
TimeTaken: taskProgressInfo.TimeTaken,
BytesTransferred: taskProgressInfo.BytesTransferred,
ActualSize: taskProgressInfo.ActualSize,
})
}
return ctx.JSON(http.StatusOK, response)
}
1 change: 1 addition & 0 deletions yugabyted-ui/apiserver/cmd/server/handlers/api_pitr.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func (c *Container) GetPITRConfigurations(ctx echo.Context) error {
pitrConfigResult.Error)
return ctx.String(http.StatusInternalServerError, errorMessage)
}
c.logger.Infof("Successfully fetched and parsed PITR configuration")
var response models.PITRScheduleInfo
activeSchedules := false
for i := len(pitrConfigResult.Config.Schedules) - 1; i >= 0; i-- {
Expand Down
70 changes: 70 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/handlers/api_restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package handlers

import (
"apiserver/cmd/server/helpers"
"apiserver/cmd/server/models"
"fmt"
"net/http"
"github.com/labstack/echo/v4"
)

func (c *Container) GetRestoreDetails(ctx echo.Context) error {
var operationType = "restore"
lockfilelistFuture := make(chan helpers.LockFileListFuture, 1)
c.helper.ListLockFilesFuture(helpers.DataDir, operationType, lockfilelistFuture)
lockfilelistResult := <-lockfilelistFuture
if lockfilelistResult.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to list lock files: %v", lockfilelistResult.Error))
}
if len(lockfilelistResult.Result) == 0 {
return ctx.JSON(http.StatusOK, []interface{}{})
}
var lockFileDetails helpers.LockFileDetails
lockFileInfoFutures := make([]chan helpers.LockFileInfoFuture,
len(lockfilelistResult.Result))
taskProgressInfoFutures := make([]chan helpers.TaskProgressInfoFuture,
len(lockfilelistResult.Result))

for i, lockFilePath := range lockfilelistResult.Result {
lockFileInfoFutures[i] = make(chan helpers.LockFileInfoFuture)
// Fetch lock file info asynchronously
go c.helper.GetLockFileInfoFuture(lockFilePath, lockFileInfoFutures[i])
}
// Asynchronously start task progress
for i := range lockfilelistResult.Result {
lockFileInfo := <-lockFileInfoFutures[i]
if lockFileInfo.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to parse lock files: %v", lockFileInfo.Error))
}
c.logger.Infof("Successfully read lock file for database/keyspace: %s",
lockFileInfo.DatabaseKeyspace)
lockFileDetails.Data = append(lockFileDetails.Data, lockFileInfo)
taskProgressInfoFutures[i] = make(chan helpers.TaskProgressInfoFuture)
go c.helper.GetYBCTaskProgress(lockFileInfo.YbcTaskID, lockFileInfo.TserverIP,
taskProgressInfoFutures[i])
}

var response models.RestoreResponse
for i, lockFileInfo := range lockFileDetails.Data {
taskProgressInfo := <-taskProgressInfoFutures[i]
if taskProgressInfo.Error != nil {
return ctx.String(http.StatusInternalServerError,
fmt.Sprintf("Failed to get task progress: %v", taskProgressInfo.Error))
}
response.Restore = append(response.Restore, models.RestoreDetails{
YbcTaskId: lockFileInfo.YbcTaskID,
TserverIp: lockFileInfo.TserverIP,
UserOperation: lockFileInfo.UserOperation,
YbdbApi: lockFileInfo.YbdbAPI,
DatabaseKeyspace: lockFileInfo.DatabaseKeyspace,
TaskStartTime: lockFileInfo.TaskStartTime,
TaskStatus: taskProgressInfo.FinalStatus,
TimeTaken: taskProgressInfo.TimeTaken,
BytesTransferred: taskProgressInfo.BytesTransferred,
ActualSize: taskProgressInfo.ActualSize,
})
}
return ctx.JSON(http.StatusOK, response)
}

0 comments on commit 5e5b564

Please sign in to comment.