backup.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "path/filepath"
  7. "strings"
  8. "imuslab.com/arozos/mod/disk/hybridBackup"
  9. user "imuslab.com/arozos/mod/user"
  10. prout "imuslab.com/arozos/mod/prouter"
  11. )
  12. func backup_init() {
  13. //Register HybridBackup storage restore endpoints
  14. router := prout.NewModuleRouter(prout.RouterOption{
  15. AdminOnly: false,
  16. UserHandler: userHandler,
  17. DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
  18. sendErrorResponse(w, "Permission Denied")
  19. },
  20. })
  21. //Register API endpoints
  22. router.HandleFunc("/system/backup/listRestorable", backup_listRestorable)
  23. router.HandleFunc("/system/backup/restoreFile", backup_restoreSelected)
  24. router.HandleFunc("/system/backup/snapshotSummary", backup_renderSnapshotSummary)
  25. router.HandleFunc("/system/backup/listAll", backup_listAllBackupDisk)
  26. //Register settings
  27. registerSetting(settingModule{
  28. Name: "Backup Disks",
  29. Desc: "All backup disk in the system",
  30. IconPath: "img/system/backup.svg",
  31. Group: "Disk",
  32. StartDir: "SystemAO/disk/backup/backups.html",
  33. RequireAdmin: true,
  34. })
  35. }
  36. //List all backup disk info
  37. func backup_listAllBackupDisk(w http.ResponseWriter, r *http.Request) {
  38. //Get all fsh from the system
  39. runningBackupTasks := []*hybridBackup.BackupTask{}
  40. //Render base storage pool
  41. for _, fsh := range baseStoragePool.Storages {
  42. if fsh.Hierarchy == "backup" {
  43. task, err := baseStoragePool.HyperBackupManager.GetTaskByBackupDiskID(fsh.UUID)
  44. if err != nil {
  45. continue
  46. }
  47. runningBackupTasks = append(runningBackupTasks, task)
  48. }
  49. }
  50. //Render group storage pool
  51. for _, pg := range permissionHandler.PermissionGroups {
  52. for _, fsh := range pg.StoragePool.Storages {
  53. task, err := pg.StoragePool.HyperBackupManager.GetTaskByBackupDiskID(fsh.UUID)
  54. if err != nil {
  55. continue
  56. }
  57. runningBackupTasks = append(runningBackupTasks, task)
  58. }
  59. }
  60. type backupDrive struct {
  61. DiskUID string //The backup disk UUID
  62. DiskName string // The Backup disk name
  63. ParentUID string //Parent disk UID
  64. ParentName string //Parent disk name
  65. BackupMode string //The backup mode of the drive
  66. LastBackupCycleTime int64 //Last backup timestamp
  67. BackupCycleCount int64 //How many backup cycle has proceeded since the system startup
  68. Error bool //If there are error occured in the last cycle
  69. ErrorMessage string //If there are any error msg
  70. }
  71. backupDrives := []*backupDrive{}
  72. for _, task := range runningBackupTasks {
  73. diskFsh, diskErr := GetFsHandlerByUUID(task.DiskUID)
  74. parentFsh, parentErr := GetFsHandlerByUUID(task.ParentUID)
  75. //Check for error in getting FS Handler
  76. if diskErr != nil || parentErr != nil {
  77. sendErrorResponse(w, "Unable to get backup task info from backup disk: "+task.DiskUID)
  78. return
  79. }
  80. thisBackupDrive := backupDrive{
  81. DiskUID: diskFsh.UUID,
  82. DiskName: diskFsh.Name,
  83. ParentUID: parentFsh.UUID,
  84. ParentName: parentFsh.Name,
  85. BackupMode: task.Mode,
  86. LastBackupCycleTime: task.LastCycleTime,
  87. BackupCycleCount: task.CycleCounter,
  88. Error: task.PanicStopped,
  89. ErrorMessage: task.ErrorMessage,
  90. }
  91. backupDrives = append(backupDrives, &thisBackupDrive)
  92. }
  93. js, _ := json.Marshal(backupDrives)
  94. sendJSONResponse(w, string(js))
  95. }
  96. //Generate a snapshot summary for vroot
  97. func backup_renderSnapshotSummary(w http.ResponseWriter, r *http.Request) {
  98. //Get user accessiable storage pools
  99. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  100. if err != nil {
  101. sendErrorResponse(w, "User not logged in")
  102. return
  103. }
  104. //Get Backup disk ID from request
  105. bdid, err := mv(r, "bdid", true)
  106. if err != nil {
  107. sendErrorResponse(w, "Invalid backup disk ID given")
  108. return
  109. }
  110. //Get target snapshot name from request
  111. snapshot, err := mv(r, "snapshot", true)
  112. if err != nil {
  113. sendErrorResponse(w, "Invalid snapshot name given")
  114. return
  115. }
  116. //Get fsh from the id
  117. fsh, err := GetFsHandlerByUUID(bdid)
  118. if err != nil {
  119. sendErrorResponse(w, err.Error())
  120. return
  121. }
  122. //Get parent disk hierarcy
  123. parentDiskID, err := userinfo.HomeDirectories.HyperBackupManager.GetParentDiskIDByRestoreDiskID(fsh.UUID)
  124. if err != nil {
  125. sendErrorResponse(w, err.Error())
  126. return
  127. }
  128. parentFsh, err := GetFsHandlerByUUID(parentDiskID)
  129. if err != nil {
  130. sendErrorResponse(w, err.Error())
  131. return
  132. }
  133. //Get task by the backup disk id
  134. task, err := userinfo.HomeDirectories.HyperBackupManager.GetTaskByBackupDiskID(fsh.UUID)
  135. if err != nil {
  136. sendErrorResponse(w, err.Error())
  137. return
  138. }
  139. if task.Mode == "version" {
  140. //Generate snapshot summary
  141. var summary *hybridBackup.SnapshotSummary
  142. if parentFsh.Hierarchy == "user" {
  143. s, err := task.GenerateSnapshotSummary(snapshot, &userinfo.Username)
  144. if err != nil {
  145. sendErrorResponse(w, err.Error())
  146. return
  147. }
  148. summary = s
  149. } else {
  150. s, err := task.GenerateSnapshotSummary(snapshot, nil)
  151. if err != nil {
  152. sendErrorResponse(w, err.Error())
  153. return
  154. }
  155. summary = s
  156. }
  157. js, _ := json.Marshal(summary)
  158. sendJSONResponse(w, string(js))
  159. } else {
  160. sendErrorResponse(w, "Unable to genreate snapshot summary: Backup mode is not snapshot")
  161. return
  162. }
  163. }
  164. //Restore a given file
  165. func backup_restoreSelected(w http.ResponseWriter, r *http.Request) {
  166. //Get user accessiable storage pools
  167. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  168. if err != nil {
  169. sendErrorResponse(w, "User not logged in")
  170. return
  171. }
  172. //Get Backup disk ID from request
  173. bdid, err := mv(r, "bdid", true)
  174. if err != nil {
  175. sendErrorResponse(w, "Invalid backup disk ID given")
  176. return
  177. }
  178. //Get fsh from the id
  179. fsh, err := GetFsHandlerByUUID(bdid)
  180. if err != nil {
  181. sendErrorResponse(w, err.Error())
  182. return
  183. }
  184. //Get the relative path for the restorable file
  185. relpath, err := mv(r, "relpath", true)
  186. if err != nil {
  187. sendErrorResponse(w, "Invalid relative path given")
  188. return
  189. }
  190. //Pick the correct HybridBackup Manager
  191. targetHybridBackupManager, err := backup_pickHybridBackupManager(userinfo, fsh.UUID)
  192. if err != nil {
  193. sendErrorResponse(w, err.Error())
  194. return
  195. }
  196. //Handle restore of the file
  197. err = targetHybridBackupManager.HandleRestore(fsh.UUID, relpath, &userinfo.Username)
  198. if err != nil {
  199. sendErrorResponse(w, err.Error())
  200. return
  201. }
  202. type RestoreResult struct {
  203. RestoreDiskID string
  204. TargetDiskID string
  205. RestoredVirtualPath string
  206. }
  207. result := RestoreResult{
  208. RestoreDiskID: fsh.UUID,
  209. }
  210. //Get access path for this file
  211. parentDiskId, err := targetHybridBackupManager.GetParentDiskIDByRestoreDiskID(fsh.UUID)
  212. if err != nil {
  213. //Unable to get parent disk ID???
  214. } else {
  215. //Get the path of the parent disk
  216. parentDiskHandler, err := GetFsHandlerByUUID(parentDiskId)
  217. if err == nil {
  218. //Join the result to create a virtual path
  219. assumedRestoreRealPath := filepath.ToSlash(filepath.Join(parentDiskHandler.Path, relpath))
  220. restoreVpath, err := userinfo.RealPathToVirtualPath(assumedRestoreRealPath)
  221. if err == nil {
  222. result.RestoredVirtualPath = restoreVpath
  223. }
  224. result.TargetDiskID = parentDiskId
  225. }
  226. }
  227. js, _ := json.Marshal(result)
  228. sendJSONResponse(w, string(js))
  229. }
  230. //As one user might be belongs to multiple groups, check which storage pool is this disk ID owned by and return its corect backup maanger
  231. func backup_pickHybridBackupManager(userinfo *user.User, diskID string) (*hybridBackup.Manager, error) {
  232. //Filter out the :/ if it exists in the disk ID
  233. if strings.Contains(diskID, ":") {
  234. diskID = strings.Split(diskID, ":")[0]
  235. }
  236. //Get all backup managers that this user ac can access
  237. userpg := userinfo.GetUserPermissionGroup()
  238. if userinfo.HomeDirectories.ContainDiskID(diskID) {
  239. return userinfo.HomeDirectories.HyperBackupManager, nil
  240. }
  241. //Extract the backup Managers
  242. for _, pg := range userpg {
  243. if pg.StoragePool.ContainDiskID(diskID) {
  244. return pg.StoragePool.HyperBackupManager, nil
  245. }
  246. }
  247. return nil, errors.New("Disk ID not found in any storage pool this user can access")
  248. }
  249. //Generate and return a restorable report
  250. func backup_listRestorable(w http.ResponseWriter, r *http.Request) {
  251. //Get user accessiable storage pools
  252. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  253. if err != nil {
  254. sendErrorResponse(w, "User not logged in")
  255. return
  256. }
  257. //Get Vroot ID from request
  258. vroot, err := mv(r, "vroot", true)
  259. if err != nil {
  260. sendErrorResponse(w, "Invalid vroot given")
  261. return
  262. }
  263. //Get fsh from the id
  264. fsh, err := GetFsHandlerByUUID(vroot)
  265. if err != nil {
  266. sendErrorResponse(w, err.Error())
  267. return
  268. }
  269. //Get all backup managers that this user ac can access
  270. targetBackupManager, err := backup_pickHybridBackupManager(userinfo, vroot)
  271. if err != nil {
  272. sendErrorResponse(w, err.Error())
  273. return
  274. }
  275. //Get the user's storage pool and list restorable by the user's storage pool access
  276. restorableReport, err := targetBackupManager.ListRestorable(fsh.UUID)
  277. if err != nil {
  278. sendErrorResponse(w, err.Error())
  279. return
  280. }
  281. //Get and check if the parent disk has a user Hierarcy
  282. paretnfsh, err := GetFsHandlerByUUID(restorableReport.ParentUID)
  283. if err != nil {
  284. sendErrorResponse(w, err.Error())
  285. return
  286. }
  287. result := hybridBackup.RestorableReport{
  288. ParentUID: restorableReport.ParentUID,
  289. RestorableFiles: []*hybridBackup.RestorableFile{},
  290. }
  291. if paretnfsh.Hierarchy == "user" {
  292. //The file system is user based. Filter out those file that is not belong to this user
  293. for _, restorableFile := range restorableReport.RestorableFiles {
  294. if restorableFile.IsSnapshot {
  295. //Is snapshot. Always allow access
  296. result.RestorableFiles = append(result.RestorableFiles, restorableFile)
  297. } else {
  298. //Is file
  299. fileAbsPath := filepath.Join(fsh.Path, restorableFile.RelpathOnDisk)
  300. _, err := userinfo.RealPathToVirtualPath(fileAbsPath)
  301. if err != nil {
  302. //Cannot translate this file. That means the file is not owned by this user
  303. } else {
  304. //Can translate the path.
  305. result.RestorableFiles = append(result.RestorableFiles, restorableFile)
  306. }
  307. }
  308. }
  309. } else {
  310. result = restorableReport
  311. }
  312. js, _ := json.Marshal(result)
  313. sendJSONResponse(w, string(js))
  314. }