backup.go 10 KB

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