nfs.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package nfs
  15. import (
  16. "context"
  17. "fmt"
  18. "io"
  19. "os"
  20. "path"
  21. "sync"
  22. "time"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. api "yunion.io/x/onecloud/pkg/apis/compute"
  26. "yunion.io/x/onecloud/pkg/hostman/options"
  27. "yunion.io/x/onecloud/pkg/util/fileutils2"
  28. "yunion.io/x/onecloud/pkg/util/procutils"
  29. )
  30. var ErrorBackupStorageOffline error = errors.Error(api.BackupStorageOffline)
  31. type SNFSBackupStorage struct {
  32. BackupStorageId string
  33. Path string
  34. NfsHost string
  35. NfsSharedDir string
  36. lock *sync.Mutex
  37. userNumber int
  38. }
  39. func newNFSBackupStorage(backupStorageId, nfsHost, nfsSharedDir string) *SNFSBackupStorage {
  40. return &SNFSBackupStorage{
  41. BackupStorageId: backupStorageId,
  42. NfsHost: nfsHost,
  43. NfsSharedDir: nfsSharedDir,
  44. Path: path.Join(options.HostOptions.LocalBackupStoragePath, backupStorageId),
  45. lock: &sync.Mutex{},
  46. }
  47. }
  48. func (s *SNFSBackupStorage) getBackupDir() string {
  49. return path.Join(s.Path, "backups")
  50. }
  51. func (s *SNFSBackupStorage) getBackupDiskPath(backupId string) string {
  52. return path.Join(s.getBackupDir(), backupId)
  53. }
  54. func (s *SNFSBackupStorage) getPackageDir() string {
  55. return path.Join(s.Path, "backuppacks")
  56. }
  57. func (s *SNFSBackupStorage) getBackupInstancePath(backupInstanceId string) string {
  58. return path.Join(s.getPackageDir(), backupInstanceId)
  59. }
  60. func (s *SNFSBackupStorage) checkAndMount() error {
  61. s.lock.Lock()
  62. defer s.lock.Unlock()
  63. if !fileutils2.Exists(s.Path) {
  64. output, err := procutils.NewCommand("mkdir", "-p", s.Path).Output()
  65. if err != nil {
  66. log.Errorf("mkdir %s failed: %s", s.Path, output)
  67. return errors.Wrapf(err, "mkdir %s failed: %s", s.Path, output)
  68. }
  69. }
  70. if err := procutils.NewRemoteCommandAsFarAsPossible("mountpoint", s.Path).Run(); err == nil {
  71. s.userNumber++
  72. return nil
  73. }
  74. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  75. defer cancel()
  76. err := procutils.NewRemoteCommandContextAsFarAsPossible(ctx,
  77. "mount", "-t", "nfs", fmt.Sprintf("%s:%s", s.NfsHost, s.NfsSharedDir), s.Path).Run()
  78. if err != nil {
  79. return errors.Wrap(ErrorBackupStorageOffline, err.Error())
  80. }
  81. backupDir := s.getBackupDir()
  82. if !fileutils2.Exists(backupDir) {
  83. output, err := procutils.NewCommand("mkdir", "-p", backupDir).Output()
  84. if err != nil {
  85. log.Errorf("mkdir %s failed: %s", backupDir, output)
  86. return errors.Wrapf(err, "mkdir %s failed: %s", backupDir, output)
  87. }
  88. }
  89. packageDir := s.getPackageDir()
  90. if !fileutils2.Exists(packageDir) {
  91. output, err := procutils.NewCommand("mkdir", "-p", packageDir).Output()
  92. if err != nil {
  93. log.Errorf("mkdir %s failed: %s", packageDir, output)
  94. return errors.Wrapf(err, "mkdir %s failed: %s", packageDir, output)
  95. }
  96. }
  97. s.userNumber++
  98. return nil
  99. }
  100. func (s *SNFSBackupStorage) unMount() error {
  101. s.lock.Lock()
  102. defer s.lock.Unlock()
  103. s.userNumber--
  104. if s.userNumber > 0 {
  105. return nil
  106. }
  107. out, err := procutils.NewRemoteCommandAsFarAsPossible("umount", s.Path).Output()
  108. if err != nil {
  109. return errors.Wrapf(err, "umount %s failed %s", s.Path, out)
  110. }
  111. return nil
  112. }
  113. func (s *SNFSBackupStorage) SaveBackupFrom(ctx context.Context, srcFile io.Reader, fileSize int64, backupId string) error {
  114. return s.saveFile(ctx, srcFile, fileSize, backupId, s.getBackupDiskPath)
  115. }
  116. func (s *SNFSBackupStorage) SaveBackupInstanceFrom(ctx context.Context, srcFile io.Reader, fileSize int64, backupId string) error {
  117. return s.saveFile(ctx, srcFile, fileSize, backupId, s.getBackupDiskPath)
  118. }
  119. func (s *SNFSBackupStorage) saveFile(ctx context.Context, srcFile io.Reader, fileSize int64, id string, getPathFunc func(string) string) error {
  120. err := s.checkAndMount()
  121. if err != nil {
  122. return errors.Wrap(err, "unable to checkAndMount")
  123. }
  124. defer s.unMount()
  125. targetFilename := getPathFunc(id)
  126. targetFile, err := os.Create(targetFilename)
  127. if err != nil {
  128. return errors.Wrap(err, "os.Create")
  129. }
  130. defer targetFile.Close()
  131. _, err = io.CopyN(targetFile, srcFile, fileSize)
  132. if err != nil {
  133. log.Errorf("unable to io.Copy to %s: %s", targetFilename, err)
  134. return errors.Wrap(err, "io.Copy")
  135. }
  136. return nil
  137. }
  138. func (s *SNFSBackupStorage) RestoreBackupTo(ctx context.Context, targetFilename string, backupId string) error {
  139. return s.restoreFile(ctx, targetFilename, backupId, s.getBackupDiskPath)
  140. }
  141. func (s *SNFSBackupStorage) RestoreBackupInstanceTo(ctx context.Context, targetFilename string, backupId string) error {
  142. return s.restoreFile(ctx, targetFilename, backupId, s.getBackupInstancePath)
  143. }
  144. func (s *SNFSBackupStorage) restoreFile(ctx context.Context, targetFilename string, id string, getPathFunc func(string) string) error {
  145. err := s.checkAndMount()
  146. if err != nil {
  147. return errors.Wrap(err, "unable to checkAndMount")
  148. }
  149. defer s.unMount()
  150. srcFilename := getPathFunc(id)
  151. if output, err := procutils.NewCommand("cp", srcFilename, targetFilename).Output(); err != nil {
  152. log.Errorf("unable to cp %s to %s: %s", srcFilename, targetFilename, output)
  153. return errors.Wrapf(err, "cp %s to %s failed and output is %q", srcFilename, targetFilename, output)
  154. }
  155. return nil
  156. }
  157. func (s *SNFSBackupStorage) RemoveBackup(ctx context.Context, backupId string) error {
  158. return s.removeFile(ctx, backupId, s.getBackupDiskPath)
  159. }
  160. func (s *SNFSBackupStorage) RemoveBackupInstance(ctx context.Context, backupId string) error {
  161. return s.removeFile(ctx, backupId, s.getBackupInstancePath)
  162. }
  163. func (s *SNFSBackupStorage) removeFile(ctx context.Context, id string, getPathFunc func(id string) string) error {
  164. err := s.checkAndMount()
  165. if err != nil {
  166. return errors.Wrap(err, "unable to checkAndMount")
  167. }
  168. defer s.unMount()
  169. filename := getPathFunc(id)
  170. if !fileutils2.Exists(filename) {
  171. return nil
  172. }
  173. if output, err := procutils.NewCommand("rm", filename).Output(); err != nil {
  174. log.Errorf("unable to rm %s: %s", filename, output)
  175. return errors.Wrapf(err, "rm %s failed and output is %q", filename, output)
  176. }
  177. return nil
  178. }
  179. func (s *SNFSBackupStorage) IsBackupExists(backupId string) (bool, string, error) {
  180. return s.isFileExists(backupId, s.getBackupDiskPath)
  181. }
  182. func (s *SNFSBackupStorage) IsBackupInstanceExists(backupId string) (bool, string, error) {
  183. return s.isFileExists(backupId, s.getBackupInstancePath)
  184. }
  185. func (s *SNFSBackupStorage) isFileExists(id string, getPathFunc func(id string) string) (bool, string, error) {
  186. err := s.checkAndMount()
  187. if err != nil {
  188. if errors.Cause(err) == ErrorBackupStorageOffline {
  189. return false, err.Error(), nil
  190. }
  191. return false, "", errors.Wrap(err, "unable to checkAndMount")
  192. }
  193. defer s.unMount()
  194. filename := getPathFunc(id)
  195. return fileutils2.Exists(filename), "", nil
  196. }
  197. func (s *SNFSBackupStorage) IsOnline() (bool, string, error) {
  198. err := s.checkAndMount()
  199. if err != nil {
  200. if errors.Cause(err) == ErrorBackupStorageOffline {
  201. return false, err.Error(), nil
  202. }
  203. return false, "", err
  204. }
  205. s.unMount()
  206. return true, "", nil
  207. }
  208. func (s *SNFSBackupStorage) GetExternalAccessUrl(backupId string) (string, error) {
  209. return "", errors.ErrNotSupported
  210. }