handler.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. package raid
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "imuslab.com/arozos/mod/disk/diskfs"
  11. "imuslab.com/arozos/mod/utils"
  12. )
  13. /*
  14. Handler.go
  15. This module handle api call to the raid module
  16. */
  17. // Handle stopping a RAID array for maintaince
  18. func (m *Manager) HandleStopRAIDArray(w http.ResponseWriter, r *http.Request) {
  19. }
  20. // Handle remove a member disk (sdX) from RAID volume (mdX)
  21. func (m *Manager) HandleRemoveDiskFromRAIDVol(w http.ResponseWriter, r *http.Request) {
  22. //mdadm --remove /dev/md0 /dev/sdb1
  23. mdDev, err := utils.PostPara(r, "raidDev")
  24. if err != nil {
  25. utils.SendErrorResponse(w, "invalid raid device given")
  26. return
  27. }
  28. sdXDev, err := utils.PostPara(r, "memDev")
  29. if err != nil {
  30. utils.SendErrorResponse(w, "invalid member device given")
  31. return
  32. }
  33. //Check if target array exists
  34. if !m.RAIDDeviceExists(mdDev) {
  35. utils.SendErrorResponse(w, "target RAID array not exists")
  36. return
  37. }
  38. //Check if this is the only disk in the array
  39. if !m.IsSafeToRemove(mdDev, sdXDev) {
  40. utils.SendErrorResponse(w, "removal of this device will cause data loss")
  41. return
  42. }
  43. //Check if the disk is already failed
  44. diskAlreadyFailed, err := m.DiskIsFailed(mdDev, sdXDev)
  45. if err != nil {
  46. log.Println("[RAID] Unable to validate if disk failed: " + err.Error())
  47. utils.SendErrorResponse(w, err.Error())
  48. return
  49. }
  50. //Disk not failed. Mark it as failed
  51. if !diskAlreadyFailed {
  52. err = m.FailDisk(mdDev, sdXDev)
  53. if err != nil {
  54. utils.SendErrorResponse(w, err.Error())
  55. return
  56. }
  57. }
  58. //Add some delay for OS level to handle IO closing
  59. time.Sleep(300 * time.Millisecond)
  60. //Done. Remove the device from array
  61. err = m.RemoveDisk(mdDev, sdXDev)
  62. if err != nil {
  63. utils.SendErrorResponse(w, err.Error())
  64. return
  65. }
  66. log.Println("[RAID] Memeber disk " + sdXDev + " removed from RAID volume " + mdDev)
  67. utils.SendOK(w)
  68. }
  69. // Handle adding a disk (mdX) to RAID volume (mdX)
  70. func (m *Manager) HandleAddDiskToRAIDVol(w http.ResponseWriter, r *http.Request) {
  71. //mdadm --add /dev/md0 /dev/sdb1
  72. mdDev, err := utils.PostPara(r, "raidDev")
  73. if err != nil {
  74. utils.SendErrorResponse(w, "invalid raid device given")
  75. return
  76. }
  77. sdXDev, err := utils.PostPara(r, "memDev")
  78. if err != nil {
  79. utils.SendErrorResponse(w, "invalid member device given")
  80. return
  81. }
  82. //Check if target array exists
  83. if !m.RAIDDeviceExists(mdDev) {
  84. utils.SendErrorResponse(w, "target RAID array not exists")
  85. return
  86. }
  87. //Check if disk already in another RAID array or mounted
  88. isMounted, err := diskfs.DeviceIsMounted(sdXDev)
  89. if err != nil {
  90. utils.SendErrorResponse(w, "unable to read device state")
  91. return
  92. }
  93. if isMounted {
  94. utils.SendErrorResponse(w, "target device is mounted")
  95. return
  96. }
  97. diskUsedByAnotherRAID, err := m.DiskIsUsedInAnotherRAIDVol(sdXDev)
  98. if err != nil {
  99. utils.SendErrorResponse(w, err.Error())
  100. }
  101. if diskUsedByAnotherRAID {
  102. utils.SendErrorResponse(w, "target device already been used by another RAID volume")
  103. return
  104. }
  105. isOSDisk, err := m.DiskIsRoot(sdXDev)
  106. if err != nil {
  107. utils.SendErrorResponse(w, err.Error())
  108. }
  109. if isOSDisk {
  110. utils.SendErrorResponse(w, "OS disk cannot be used as RAID member")
  111. return
  112. }
  113. //OK! Clear the disk
  114. err = m.ClearSuperblock(sdXDev)
  115. if err != nil {
  116. utils.SendErrorResponse(w, "unable to clear superblock of device")
  117. return
  118. }
  119. //Add it to the target RAID array
  120. err = m.AddDisk(mdDev, sdXDev)
  121. if err != nil {
  122. utils.SendErrorResponse(w, "adding disk to RAID volume failed")
  123. return
  124. }
  125. log.Println("[RAID] Device " + sdXDev + " added to RAID volume " + mdDev)
  126. utils.SendOK(w)
  127. }
  128. // Handle force flush reloading mdadm to solve the md0 become md127 problem
  129. func (m *Manager) HandleMdadmFlushReload(w http.ResponseWriter, r *http.Request) {
  130. err := m.FlushReload()
  131. if err != nil {
  132. utils.SendErrorResponse(w, "reload failed: "+strings.ReplaceAll(err.Error(), "\n", " "))
  133. return
  134. }
  135. utils.SendOK(w)
  136. }
  137. // Handle resolving the disk model label, might return null
  138. func (m *Manager) HandleResolveDiskModelLabel(w http.ResponseWriter, r *http.Request) {
  139. devName, err := utils.GetPara(r, "devName")
  140. if err != nil {
  141. utils.SendErrorResponse(w, "invalid device name given")
  142. return
  143. }
  144. //Function only accept sdX not /dev/sdX
  145. devName = filepath.Base(devName)
  146. labelSize, labelModel, err := diskfs.GetDiskModelByName(devName)
  147. if err != nil {
  148. utils.SendErrorResponse(w, err.Error())
  149. return
  150. }
  151. js, _ := json.Marshal([]string{labelModel, labelSize})
  152. utils.SendJSONResponse(w, string(js))
  153. }
  154. // Handle force flush reloading mdadm to solve the md0 become md127 problem
  155. func (m *Manager) HandlListChildrenDeviceInfo(w http.ResponseWriter, r *http.Request) {
  156. devName, err := utils.GetPara(r, "devName")
  157. if err != nil {
  158. utils.SendErrorResponse(w, "invalid device name given")
  159. return
  160. }
  161. if !strings.HasPrefix(devName, "/dev/") {
  162. devName = "/dev/" + devName
  163. }
  164. //Get the children devices for this RAID
  165. raidDevice, err := m.GetRAIDDeviceByDevicePath(devName)
  166. if err != nil {
  167. utils.SendErrorResponse(w, err.Error())
  168. return
  169. }
  170. //Merge the child devices info into one array
  171. results := map[string]*diskfs.BlockDeviceMeta{}
  172. for _, blockdevice := range raidDevice.Members {
  173. bdm, err := diskfs.GetBlockDeviceMeta("/dev/" + blockdevice.Name)
  174. if err != nil {
  175. log.Println("[RAID] Unable to load block device info: " + err.Error())
  176. results[blockdevice.Name] = &diskfs.BlockDeviceMeta{
  177. Name: blockdevice.Name,
  178. Size: -1,
  179. }
  180. continue
  181. }
  182. results[blockdevice.Name] = bdm
  183. }
  184. js, _ := json.Marshal(results)
  185. utils.SendJSONResponse(w, string(js))
  186. }
  187. // Handle list all the disks that is usable
  188. func (m *Manager) HandleListUsableDevices(w http.ResponseWriter, r *http.Request) {
  189. storageDevices, err := diskfs.ListAllStorageDevices()
  190. if err != nil {
  191. utils.SendErrorResponse(w, err.Error())
  192. return
  193. }
  194. //Filter out the block devices that are disks
  195. usableDisks := []diskfs.BlockDeviceMeta{}
  196. for _, device := range storageDevices.Blockdevices {
  197. if device.Type == "disk" {
  198. usableDisks = append(usableDisks, device)
  199. }
  200. }
  201. js, _ := json.Marshal(usableDisks)
  202. utils.SendJSONResponse(w, string(js))
  203. }
  204. // Handle loading the detail of a given RAID array
  205. func (m *Manager) HandleLoadArrayDetail(w http.ResponseWriter, r *http.Request) {
  206. devName, err := utils.GetPara(r, "devName")
  207. if err != nil {
  208. utils.SendErrorResponse(w, "invalid device name given")
  209. return
  210. }
  211. if !strings.HasPrefix(devName, "/dev/") {
  212. devName = "/dev/" + devName
  213. }
  214. //Check device exists
  215. if !utils.FileExists(devName) {
  216. utils.SendErrorResponse(w, "target device not exists")
  217. return
  218. }
  219. //Get status of the array
  220. targetRAIDInfo, err := m.GetRAIDInfo(devName)
  221. if err != nil {
  222. utils.SendErrorResponse(w, err.Error())
  223. return
  224. }
  225. js, _ := json.Marshal(targetRAIDInfo)
  226. utils.SendJSONResponse(w, string(js))
  227. }
  228. // Handle formating a device
  229. func (m *Manager) HandleFormatRaidDevice(w http.ResponseWriter, r *http.Request) {
  230. devName, err := utils.GetPara(r, "devName")
  231. if err != nil {
  232. utils.SendErrorResponse(w, "invalid device name given")
  233. return
  234. }
  235. format, err := utils.GetPara(r, "format")
  236. if err != nil {
  237. utils.SendErrorResponse(w, "invalid device name given")
  238. return
  239. }
  240. if !strings.HasPrefix(devName, "/dev/") {
  241. devName = "/dev/" + devName
  242. }
  243. //Check if the target device exists
  244. if !m.RAIDDeviceExists(devName) {
  245. utils.SendErrorResponse(w, "target not exists or not a valid RAID device")
  246. return
  247. }
  248. //Format the drive
  249. err = diskfs.FormatStorageDevice(format, devName)
  250. if err != nil {
  251. utils.SendErrorResponse(w, err.Error())
  252. return
  253. }
  254. utils.SendOK(w)
  255. }
  256. // List all the raid device in this system
  257. func (m *Manager) HandleListRaidDevices(w http.ResponseWriter, r *http.Request) {
  258. rdevs, err := m.GetRAIDDevicesFromProcMDStat()
  259. if err != nil {
  260. utils.SendErrorResponse(w, err.Error())
  261. return
  262. }
  263. results := []*RAIDInfo{}
  264. for _, rdev := range rdevs {
  265. arrayInfo, err := m.GetRAIDInfo("/dev/" + rdev.Name)
  266. if err != nil {
  267. continue
  268. }
  269. results = append(results, arrayInfo)
  270. }
  271. js, _ := json.Marshal(results)
  272. utils.SendJSONResponse(w, string(js))
  273. }
  274. // Create a RAID storage pool
  275. func (m *Manager) HandleCreateRAIDDevice(w http.ResponseWriter, r *http.Request) {
  276. devName, err := utils.PostPara(r, "devName")
  277. if err != nil || devName == "" {
  278. //Use auto generated one
  279. devName, err = GetNextAvailableMDDevice()
  280. if err != nil {
  281. utils.SendErrorResponse(w, err.Error())
  282. return
  283. }
  284. }
  285. raidName, err := utils.PostPara(r, "raidName")
  286. if err != nil {
  287. utils.SendErrorResponse(w, "invalid raid storage name given")
  288. return
  289. }
  290. raidLevelStr, err := utils.PostPara(r, "level")
  291. if err != nil {
  292. utils.SendErrorResponse(w, "invalid raid level given")
  293. return
  294. }
  295. raidDevicesJSON, err := utils.PostPara(r, "raidDev")
  296. if err != nil {
  297. utils.SendErrorResponse(w, "invalid raid device array given")
  298. return
  299. }
  300. spareDevicesJSON, err := utils.PostPara(r, "spareDev")
  301. if err != nil {
  302. utils.SendErrorResponse(w, "invalid spare device array given")
  303. return
  304. }
  305. //Get if superblock require all zeroed (will also do formating after raid constructed)
  306. zerosuperblock, err := utils.PostBool(r, "zerosuperblock")
  307. if err != nil {
  308. zerosuperblock = false
  309. }
  310. //Convert raidDevices and spareDevices ID into string slice
  311. raidDevices := []string{}
  312. spareDevices := []string{}
  313. err = json.Unmarshal([]byte(raidDevicesJSON), &raidDevices)
  314. if err != nil {
  315. utils.SendErrorResponse(w, "unable to parse raid device into array")
  316. return
  317. }
  318. err = json.Unmarshal([]byte(spareDevicesJSON), &spareDevices)
  319. if err != nil {
  320. utils.SendErrorResponse(w, "unable to parse spare devices into array")
  321. return
  322. }
  323. //Make sure RAID Name do not contain spaces or werid charcters
  324. if strings.Contains(raidName, " ") {
  325. utils.SendErrorResponse(w, "raid name cannot contain space")
  326. return
  327. }
  328. //Convert raidLevel to int
  329. raidLevelStr = strings.TrimPrefix(raidLevelStr, "raid")
  330. raidLevel, err := strconv.Atoi(raidLevelStr)
  331. if err != nil {
  332. utils.SendErrorResponse(w, "invalid raid level given")
  333. return
  334. }
  335. if zerosuperblock {
  336. //Format each drives
  337. drivesToZeroblocks := []string{}
  338. for _, raidDev := range raidDevices {
  339. if !strings.HasPrefix(raidDev, "/dev/") {
  340. //Prepend /dev/ to it if not set
  341. raidDev = filepath.Join("/dev/", raidDev)
  342. }
  343. if !utils.FileExists(raidDev) {
  344. //This disk not found
  345. utils.SendErrorResponse(w, raidDev+" not found")
  346. return
  347. }
  348. thisDisk := raidDev
  349. drivesToZeroblocks = append(drivesToZeroblocks, thisDisk)
  350. }
  351. for _, spareDev := range spareDevices {
  352. if !strings.HasPrefix(spareDev, "/dev/") {
  353. //Prepend /dev/ to it if not set
  354. spareDev = filepath.Join("/dev/", spareDev)
  355. }
  356. if !utils.FileExists(spareDev) {
  357. //This disk not found
  358. utils.SendErrorResponse(w, spareDev+" not found")
  359. return
  360. }
  361. thisDisk := spareDev
  362. drivesToZeroblocks = append(drivesToZeroblocks, thisDisk)
  363. }
  364. for _, clearPendingDisk := range drivesToZeroblocks {
  365. //Format all drives
  366. m.Options.Logger.PrintAndLog("RAID", "Clearning superblock for disk "+clearPendingDisk, nil)
  367. err = m.ClearSuperblock(clearPendingDisk)
  368. if err != nil {
  369. m.Options.Logger.PrintAndLog("RAID", "Unable to format "+clearPendingDisk+": "+err.Error(), err)
  370. utils.SendErrorResponse(w, err.Error())
  371. return
  372. }
  373. }
  374. }
  375. //Create the RAID device
  376. err = m.CreateRAIDDevice(devName, raidName, raidLevel, raidDevices, spareDevices)
  377. if err != nil {
  378. utils.SendErrorResponse(w, err.Error())
  379. return
  380. }
  381. //Update the mdadm config
  382. err = m.UpdateMDADMConfig()
  383. if err != nil {
  384. utils.SendErrorResponse(w, err.Error())
  385. return
  386. }
  387. utils.SendOK(w)
  388. }
  389. // Request to reload the RAID manager and scan new / fix missing raid pools
  390. func (m *Manager) HandleRaidDevicesAssemble(w http.ResponseWriter, r *http.Request) {
  391. err := m.RestartRAIDService()
  392. if err != nil {
  393. utils.SendErrorResponse(w, err.Error())
  394. return
  395. }
  396. utils.SendOK(w)
  397. }
  398. // Remove a given raid device with its name, USE WITH CAUTION
  399. func (m *Manager) HandleRemoveRaideDevice(w http.ResponseWriter, r *http.Request) {
  400. //TODO: Add protection and switch to POST
  401. targetDevice, err := utils.PostPara(r, "raidDev")
  402. if err != nil {
  403. utils.SendErrorResponse(w, "target device not given")
  404. return
  405. }
  406. //Check if the raid device exists
  407. if !m.RAIDDeviceExists(targetDevice) {
  408. utils.SendErrorResponse(w, "target device not exists")
  409. return
  410. }
  411. //Get the RAID device memeber disks
  412. targetRAIDDevice, err := m.GetRAIDDeviceByDevicePath(targetDevice)
  413. if err != nil {
  414. utils.SendErrorResponse(w, "error occured when trying to load target RAID device info")
  415. return
  416. }
  417. //Check if it is mounted. If yes, unmount it
  418. if !strings.HasPrefix(targetDevice, "/dev/") {
  419. targetDevice = filepath.Join("/dev/", targetDevice)
  420. }
  421. mounted, err := diskfs.DeviceIsMounted(targetDevice)
  422. if err != nil {
  423. m.Options.Logger.PrintAndLog("RAID", "Unmount failed: "+err.Error(), err)
  424. utils.SendErrorResponse(w, err.Error())
  425. return
  426. }
  427. if mounted {
  428. m.Options.Logger.PrintAndLog("RAID", targetDevice+" is mounted. Trying to unmount...", nil)
  429. err = diskfs.UnmountDevice(targetDevice)
  430. if err != nil {
  431. log.Println("[RAID] Unmount failed: " + err.Error())
  432. utils.SendErrorResponse(w, err.Error())
  433. return
  434. }
  435. //Wait for 3 seconds to check if it is still mounted
  436. counter := 0
  437. for counter < 3 {
  438. mounted, _ := diskfs.DeviceIsMounted(targetDevice)
  439. if mounted {
  440. //Still not unmounted. Wait for it
  441. m.Options.Logger.PrintAndLog("RAID", "Device still mounted. Retrying in 1 second", nil)
  442. counter++
  443. time.Sleep(1 * time.Second)
  444. } else {
  445. break
  446. }
  447. }
  448. //Check if it is still mounted
  449. mounted, _ = diskfs.DeviceIsMounted(targetDevice)
  450. if mounted {
  451. utils.SendErrorResponse(w, "unmount RAID partition failed: device is busy")
  452. return
  453. }
  454. }
  455. //Give it some time for the raid device to finish umount
  456. time.Sleep(300 * time.Millisecond)
  457. //Stop & Remove RAID service on the target device
  458. err = m.StopRAIDDevice(targetDevice)
  459. if err != nil {
  460. m.Options.Logger.PrintAndLog("RAID", "Stop RAID partition failed: "+err.Error(), err)
  461. utils.SendErrorResponse(w, err.Error())
  462. return
  463. }
  464. //Zeroblock the RAID device member disks
  465. for _, memberDisk := range targetRAIDDevice.Members {
  466. //Member disk name do not contain full path
  467. name := memberDisk.Name
  468. if !strings.HasPrefix(name, "/dev/") {
  469. name = filepath.Join("/dev/", name)
  470. }
  471. err = m.ClearSuperblock(name)
  472. if err != nil {
  473. m.Options.Logger.PrintAndLog("RAID", "Unable to clear superblock on device "+name, err)
  474. continue
  475. }
  476. }
  477. //Update the mdadm config
  478. err = m.UpdateMDADMConfig()
  479. if err != nil {
  480. utils.SendErrorResponse(w, err.Error())
  481. return
  482. }
  483. //Done
  484. utils.SendOK(w)
  485. }