213 lines
5.3 KiB
Go
213 lines
5.3 KiB
Go
package nfs
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/sirupsen/logrus"
|
||
)
|
||
|
||
// Manager управляет NFS монтированием
|
||
type Manager struct {
|
||
mu sync.RWMutex
|
||
enabled bool
|
||
server string
|
||
exportPath string
|
||
mountPoint string
|
||
options string
|
||
mounted bool
|
||
mountTime time.Time
|
||
}
|
||
|
||
var (
|
||
// DefaultManager - глобальный менеджер NFS
|
||
DefaultManager *Manager
|
||
initOnce sync.Once
|
||
)
|
||
|
||
// Config конфигурация NFS
|
||
type Config struct {
|
||
Enabled bool
|
||
Server string
|
||
ExportPath string
|
||
MountPoint string
|
||
Options string
|
||
}
|
||
|
||
// Init инициализирует глобальный менеджер NFS
|
||
func Init(cfg Config) {
|
||
initOnce.Do(func() {
|
||
DefaultManager = &Manager{
|
||
enabled: cfg.Enabled,
|
||
server: cfg.Server,
|
||
exportPath: cfg.ExportPath,
|
||
mountPoint: cfg.MountPoint,
|
||
options: cfg.Options,
|
||
mounted: false,
|
||
}
|
||
|
||
if cfg.Enabled {
|
||
logrus.Infof("NFS manager initialized: %s:%s -> %s",
|
||
cfg.Server, cfg.ExportPath, cfg.MountPoint)
|
||
} else {
|
||
logrus.Info("NFS manager initialized but disabled")
|
||
}
|
||
})
|
||
}
|
||
|
||
// IsEnabled возвращает статус включения NFS
|
||
func (m *Manager) IsEnabled() bool {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
return m.enabled
|
||
}
|
||
|
||
// IsMounted проверяет, смонтирован ли NFS
|
||
func (m *Manager) IsMounted() bool {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
if !m.mounted {
|
||
return false
|
||
}
|
||
|
||
// Проверяем актуальность монтирования через filesystem
|
||
return checkMountPoint(m.mountPoint)
|
||
}
|
||
|
||
// Mount монтирует NFS share
|
||
func (m *Manager) Mount() error {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
if !m.enabled {
|
||
logrus.Debug("NFS mounting skipped: disabled")
|
||
return nil
|
||
}
|
||
|
||
// Проверяем, уже ли смонтировано
|
||
if checkMountPoint(m.mountPoint) {
|
||
logrus.Debugf("NFS already mounted at %s", m.mountPoint)
|
||
m.mounted = true
|
||
m.mountTime = time.Now()
|
||
return nil
|
||
}
|
||
|
||
logrus.Infof("Attempting to mount NFS: %s:%s -> %s",
|
||
m.server, m.exportPath, m.mountPoint)
|
||
|
||
// Создаем точку монтирования если не существует
|
||
if err := os.MkdirAll(m.mountPoint, 0755); err != nil {
|
||
logrus.Errorf("Failed to create mount point %s: %v", m.mountPoint, err)
|
||
return fmt.Errorf("failed to create mount point: %w", err)
|
||
}
|
||
|
||
// Формируем опции монтирования
|
||
mountOptions := m.options
|
||
if mountOptions == "" {
|
||
mountOptions = "rw,vers=4.1,timeo=50,retrans=2"
|
||
}
|
||
|
||
// Выполняем команду mount
|
||
cmd := exec.Command("mount", "-t", "nfs",
|
||
fmt.Sprintf("%s:%s", m.server, m.exportPath),
|
||
m.mountPoint,
|
||
"-o", mountOptions)
|
||
|
||
output, err := cmd.CombinedOutput()
|
||
if err != nil {
|
||
logrus.Errorf("Failed to mount NFS: %v, output: %s", err, string(output))
|
||
m.mounted = false
|
||
return fmt.Errorf("failed to mount NFS: %w, output: %s", err, string(output))
|
||
}
|
||
|
||
m.mounted = true
|
||
m.mountTime = time.Now()
|
||
logrus.Infof("NFS mounted successfully: %s:%s -> %s",
|
||
m.server, m.exportPath, m.mountPoint)
|
||
|
||
return nil
|
||
}
|
||
|
||
// Unmount размонтирует NFS share
|
||
func (m *Manager) Unmount() error {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
if !m.enabled {
|
||
logrus.Debug("NFS unmounting skipped: disabled")
|
||
return nil
|
||
}
|
||
|
||
logrus.Infof("Unmounting NFS from %s", m.mountPoint)
|
||
|
||
cmd := exec.Command("umount", m.mountPoint)
|
||
output, err := cmd.CombinedOutput()
|
||
if err != nil {
|
||
logrus.Errorf("Failed to unmount NFS: %v, output: %s", err, string(output))
|
||
return fmt.Errorf("failed to unmount NFS: %w, output: %s", err, string(output))
|
||
}
|
||
|
||
m.mounted = false
|
||
logrus.Infof("NFS unmounted successfully from %s", m.mountPoint)
|
||
|
||
return nil
|
||
}
|
||
|
||
// CheckHealth проверяет доступность NFS mount point
|
||
func (m *Manager) CheckHealth() error {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
if !m.enabled {
|
||
return nil
|
||
}
|
||
|
||
return checkMountPointWithDetails(m.mountPoint)
|
||
}
|
||
|
||
// Remount пытается перемонтировать NFS если он размонтирован
|
||
func (m *Manager) Remount() error {
|
||
if !m.IsMounted() {
|
||
logrus.Warn("NFS not mounted, attempting remount...")
|
||
return m.Mount()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetMountPoint возвращает точку монтирования
|
||
func (m *Manager) GetMountPoint() string {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
return m.mountPoint
|
||
}
|
||
|
||
// checkMountPoint проверяет, смонтирована ли точка
|
||
func checkMountPoint(mountPoint string) bool {
|
||
// Проверяем через statfs (на Linux)
|
||
// Если точка смонтирована, то она будет иметь другой device ID чем родитель
|
||
cmd := exec.Command("mountpoint", "-q", mountPoint)
|
||
err := cmd.Run()
|
||
return err == nil
|
||
}
|
||
|
||
// checkMountPointWithDetails проверяет с логированием
|
||
func checkMountPointWithDetails(mountPoint string) error {
|
||
if !checkMountPoint(mountPoint) {
|
||
return fmt.Errorf("mount point %s is not mounted", mountPoint)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// EnsureMounted гарантирует что NFS смонтирован, монтирует если нужно
|
||
func EnsureMounted() error {
|
||
if DefaultManager == nil {
|
||
logrus.Debug("NFS manager not initialized")
|
||
return nil
|
||
}
|
||
return DefaultManager.Remount()
|
||
}
|