working on bacula engine

This commit is contained in:
2026-01-15 17:16:04 +00:00
parent 70b7841d1a
commit 7343ea65d5
7 changed files with 1143 additions and 555 deletions

View File

@@ -1694,14 +1694,12 @@ func (s *Service) ListStorageDaemons(ctx context.Context) ([]StorageDaemon, erro
return nil, fmt.Errorf("Bacula database connection not configured")
}
// Query only columns that exist in Storage table
query := `
SELECT
s.StorageId,
s.Name,
s.Address,
s.Port,
s.DeviceName,
s.MediaType
s.Autochanger
FROM Storage s
ORDER BY s.Name
`
@@ -1715,33 +1713,29 @@ func (s *Service) ListStorageDaemons(ctx context.Context) ([]StorageDaemon, erro
var daemons []StorageDaemon
for rows.Next() {
var daemon StorageDaemon
var address, deviceName, mediaType sql.NullString
var port sql.NullInt64
var autochanger sql.NullInt64
err := rows.Scan(
&daemon.StorageID, &daemon.Name, &address, &port,
&deviceName, &mediaType,
&daemon.StorageID, &daemon.Name, &autochanger,
)
if err != nil {
s.logger.Error("Failed to scan storage daemon row", "error", err)
continue
}
if address.Valid {
daemon.Address = address.String
// Get detailed information from bconsole
details, err := s.getStorageDaemonDetails(ctx, daemon.Name)
if err != nil {
s.logger.Warn("Failed to get storage daemon details from bconsole", "name", daemon.Name, "error", err)
// Continue with defaults
daemon.Status = "Unknown"
} else {
daemon.Address = details.Address
daemon.Port = details.Port
daemon.DeviceName = details.DeviceName
daemon.MediaType = details.MediaType
daemon.Status = details.Status
}
if port.Valid {
daemon.Port = int(port.Int64)
}
if deviceName.Valid {
daemon.DeviceName = deviceName.String
}
if mediaType.Valid {
daemon.MediaType = mediaType.String
}
// Default status to Online (could be enhanced with actual connection check)
daemon.Status = "Online"
daemons = append(daemons, daemon)
}
@@ -1753,6 +1747,77 @@ func (s *Service) ListStorageDaemons(ctx context.Context) ([]StorageDaemon, erro
return daemons, nil
}
// getStorageDaemonDetails retrieves detailed information about a storage daemon using bconsole
func (s *Service) getStorageDaemonDetails(ctx context.Context, storageName string) (*StorageDaemon, error) {
bconsoleConfig := "/opt/calypso/conf/bacula/bconsole.conf"
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo -e 'show storage=%s\nquit' | bconsole -c %s", storageName, bconsoleConfig))
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("failed to execute bconsole: %w", err)
}
// Parse bconsole output
// Example output:
// Autochanger: name=File1 address=localhost SDport=9103 MaxJobs=10 NumJobs=0
// DeviceName=FileChgr1 MediaType=File1 StorageId=1 Autochanger=1
outputStr := string(output)
daemon := &StorageDaemon{
Name: storageName,
Status: "Online", // Default, could check connection status
}
// Parse address
if idx := strings.Index(outputStr, "address="); idx != -1 {
addrStart := idx + len("address=")
addrEnd := strings.IndexAny(outputStr[addrStart:], " \n")
if addrEnd == -1 {
addrEnd = len(outputStr) - addrStart
}
daemon.Address = strings.TrimSpace(outputStr[addrStart : addrStart+addrEnd])
}
// Parse port (SDport)
if idx := strings.Index(outputStr, "SDport="); idx != -1 {
portStart := idx + len("SDport=")
portEnd := strings.IndexAny(outputStr[portStart:], " \n")
if portEnd == -1 {
portEnd = len(outputStr) - portStart
}
if port, err := strconv.Atoi(strings.TrimSpace(outputStr[portStart : portStart+portEnd])); err == nil {
daemon.Port = port
}
}
// Parse DeviceName
if idx := strings.Index(outputStr, "DeviceName="); idx != -1 {
devStart := idx + len("DeviceName=")
devEnd := strings.IndexAny(outputStr[devStart:], " \n")
if devEnd == -1 {
devEnd = len(outputStr) - devStart
}
daemon.DeviceName = strings.TrimSpace(outputStr[devStart : devStart+devEnd])
}
// Parse MediaType
if idx := strings.Index(outputStr, "MediaType="); idx != -1 {
mediaStart := idx + len("MediaType=")
mediaEnd := strings.IndexAny(outputStr[mediaStart:], " \n")
if mediaEnd == -1 {
mediaEnd = len(outputStr) - mediaStart
}
daemon.MediaType = strings.TrimSpace(outputStr[mediaStart : mediaStart+mediaEnd])
}
// Check if storage is online by checking for connection errors in output
if strings.Contains(outputStr, "ERR") || strings.Contains(outputStr, "Error") {
daemon.Status = "Offline"
}
return daemon, nil
}
// CreatePoolRequest represents a request to create a new storage pool
type CreatePoolRequest struct {
Name string `json:"name" binding:"required"`