driver.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 libguestfs
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "math/rand"
  19. "path"
  20. "path/filepath"
  21. "strings"
  22. "time"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/sortedmap"
  26. "yunion.io/x/onecloud/pkg/hostman/diskutils/fsutils"
  27. "yunion.io/x/onecloud/pkg/hostman/diskutils/libguestfs/guestfish"
  28. "yunion.io/x/onecloud/pkg/hostman/diskutils/nbd"
  29. "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
  30. "yunion.io/x/onecloud/pkg/hostman/guestfs/guestfishpart"
  31. "yunion.io/x/onecloud/pkg/hostman/guestfs/kvmpart"
  32. "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
  33. "yunion.io/x/onecloud/pkg/util/fileutils2"
  34. "yunion.io/x/onecloud/pkg/util/qemuimg"
  35. )
  36. const (
  37. DiskLabelLength = 6
  38. letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  39. )
  40. func RandStringBytes(n int) string {
  41. b := make([]byte, n)
  42. for i := range b {
  43. b[i] = letterBytes[rand.Intn(len(letterBytes))]
  44. }
  45. return string(b)
  46. }
  47. func init() {
  48. rand.Seed(time.Now().UnixNano())
  49. }
  50. type SLibguestfsDriver struct {
  51. imageInfo qemuimg.SImageInfo
  52. nbddev string
  53. diskLabel string
  54. lvmParts []string
  55. fsmap *sortedmap.SSortedMap
  56. fish *guestfish.Guestfish
  57. device string
  58. parts []fsdriver.IDiskPartition
  59. }
  60. func NewLibguestfsDriver(imageInfo qemuimg.SImageInfo) *SLibguestfsDriver {
  61. return &SLibguestfsDriver{
  62. imageInfo: imageInfo,
  63. }
  64. }
  65. func (d *SLibguestfsDriver) Connect(*apis.GuestDesc, string) error {
  66. fish, err := guestfsManager.AcquireFish()
  67. if err != nil {
  68. return err
  69. }
  70. d.fish = fish
  71. d.nbddev = nbd.GetNBDManager().AcquireNbddev()
  72. if err != nil {
  73. return errors.Errorf("Cannot get nbd device")
  74. }
  75. log.Debugf("acquired device %s", d.nbddev)
  76. err = nbd.QemuNbdConnect(d.imageInfo, d.nbddev)
  77. if err != nil {
  78. return err
  79. }
  80. lable := RandStringBytes(DiskLabelLength)
  81. err = fish.AddDrive(d.nbddev, lable, false)
  82. if err != nil {
  83. return err
  84. }
  85. d.diskLabel = lable
  86. if err = fish.LvmClearFilter(); err != nil {
  87. return err
  88. }
  89. devices, err := fish.ListDevices()
  90. if err != nil {
  91. return err
  92. }
  93. if len(devices) == 0 {
  94. return errors.Errorf("fish list devices no device found")
  95. }
  96. d.device = devices[0]
  97. fsmap, err := fish.ListFilesystems()
  98. if err != nil {
  99. return err
  100. }
  101. d.fsmap = fsmap
  102. log.Debugf("fsmap output %#v", d.fsmap)
  103. lvs, err := fish.Lvs()
  104. if err != nil {
  105. return err
  106. }
  107. d.lvmParts = lvs
  108. keys := d.fsmap.Keys()
  109. for i := 0; i < len(keys); i++ {
  110. partDev := keys[i]
  111. ifs, _ := d.fsmap.Get(keys[i])
  112. fs := ifs.(string)
  113. log.Debugf("new partition %s %s %s", d.device, partDev, fs)
  114. /* guestfish run ntfs mount to host is too slow
  115. * use host nbd partition replace */
  116. if fs == "ntfs" && len(d.lvmParts) == 0 {
  117. log.Infof("has ntfs, use nbd parts")
  118. d.parts, err = d.findNbdPartitions()
  119. if err != nil {
  120. return err
  121. }
  122. if err = guestfsManager.ReleaseFish(d.fish); err != nil {
  123. log.Errorf("release fish failed %s", err)
  124. }
  125. d.diskLabel = ""
  126. d.fish = nil
  127. break
  128. }
  129. part := guestfishpart.NewGuestfishDiskPartition(d.device, partDev, fs, fish)
  130. d.parts = append(d.parts, part)
  131. }
  132. return nil
  133. }
  134. func (d *SLibguestfsDriver) findNbdPartitions() ([]fsdriver.IDiskPartition, error) {
  135. if len(d.nbddev) == 0 {
  136. return nil, fmt.Errorf("Want find partitions but dosen't have nbd dev")
  137. }
  138. dev := filepath.Base(d.nbddev)
  139. devpath := filepath.Dir(d.nbddev)
  140. files, err := ioutil.ReadDir(devpath)
  141. if err != nil {
  142. return nil, errors.Wrapf(err, "read dir %s", devpath)
  143. }
  144. parts := make([]fsdriver.IDiskPartition, 0)
  145. for i := 0; i < len(files); i++ {
  146. if files[i].Name() != dev && strings.HasPrefix(files[i].Name(), dev+"p") {
  147. var part = kvmpart.NewKVMGuestDiskPartition(path.Join(devpath, files[i].Name()), "", false)
  148. parts = append(parts, part)
  149. }
  150. }
  151. return parts, nil
  152. }
  153. func (d *SLibguestfsDriver) Disconnect() error {
  154. if len(d.diskLabel) > 0 {
  155. if err := guestfsManager.ReleaseFish(d.fish); err != nil {
  156. log.Errorf("release fish failed %s", err)
  157. }
  158. d.diskLabel = ""
  159. d.fish = nil
  160. }
  161. if len(d.nbddev) > 0 {
  162. if err := nbd.QemuNbdDisconnect(d.nbddev); err != nil {
  163. return err
  164. }
  165. nbd.GetNBDManager().ReleaseNbddev(d.nbddev)
  166. }
  167. return nil
  168. }
  169. func (d *SLibguestfsDriver) GetPartitions() []fsdriver.IDiskPartition {
  170. return d.parts
  171. }
  172. func (d *SLibguestfsDriver) IsLVMPartition() bool {
  173. return len(d.lvmParts) > 0
  174. }
  175. func (d *SLibguestfsDriver) Zerofree() {
  176. startTime := time.Now()
  177. for _, part := range d.parts {
  178. part.Zerofree()
  179. }
  180. log.Infof("libguestfs zerofree %d partitions takes %f seconds",
  181. len(d.parts), time.Now().Sub(startTime).Seconds())
  182. }
  183. func (d *SLibguestfsDriver) ResizePartition(string, string) error {
  184. if d.IsLVMPartition() {
  185. // do not try to resize LVM partition
  186. return nil
  187. }
  188. return fsutils.ResizeDiskFs(d.nbddev, 0, false)
  189. }
  190. func (d *SLibguestfsDriver) FormatPartition(fs, uuid string, features *apis.FsFeatures) error {
  191. return fsutils.FormatPartition(fmt.Sprintf("%sp1", d.nbddev), fs, uuid, features)
  192. }
  193. func (d *SLibguestfsDriver) MakePartition(fsFormat string) error {
  194. return fsutils.Mkpartition(d.nbddev, fsFormat)
  195. }
  196. func (d *SLibguestfsDriver) FormatPartition2(fs, uuid string) error {
  197. partDev := fmt.Sprintf("%s1", d.device)
  198. switch fs {
  199. case "swap":
  200. return d.fish.Mkswap(partDev, uuid, "")
  201. case "ext2", "ext3", "ext4", "xfs", "fat":
  202. return d.fish.Mkfs(partDev, fs)
  203. }
  204. return errors.Errorf("Unknown fs %s", fs)
  205. }
  206. func (d *SLibguestfsDriver) MakePartition2(fsFormat string) error {
  207. var (
  208. labelType = "gpt"
  209. diskType = fileutils2.FsFormatToDiskType(fsFormat)
  210. )
  211. if len(diskType) == 0 {
  212. return errors.Errorf("Unknown fsFormat %s", fsFormat)
  213. }
  214. err := d.fish.PartDisk(d.device, labelType)
  215. if err != nil {
  216. return err
  217. }
  218. return nil
  219. }
  220. func (d *SLibguestfsDriver) DetectIsUEFISupport(rootfs fsdriver.IRootFsDriver) bool {
  221. return fsutils.DetectIsUEFISupport(rootfs, d.GetPartitions())
  222. }
  223. func (d *SLibguestfsDriver) DetectIsBIOSSupport(rootfs fsdriver.IRootFsDriver) bool {
  224. return fsutils.DetectIsBIOSSupport(d.nbddev, rootfs)
  225. }
  226. func (d *SLibguestfsDriver) MountRootfs(readonly bool) (fsdriver.IRootFsDriver, error) {
  227. return fsutils.MountRootfs(readonly, d.GetPartitions())
  228. }
  229. func (d *SLibguestfsDriver) UmountRootfs(fd fsdriver.IRootFsDriver) error {
  230. if part := fd.GetPartition(); part != nil {
  231. return part.Umount()
  232. }
  233. return nil
  234. }
  235. func (d *SLibguestfsDriver) DeployGuestfs(req *apis.DeployParams) (res *apis.DeployGuestFsResponse, err error) {
  236. return fsutils.DeployGuestfs(d, req)
  237. }
  238. func (d *SLibguestfsDriver) ResizeFs(*apis.ResizeFsParams) (*apis.Empty, error) {
  239. return fsutils.ResizeFs(d, "")
  240. }
  241. func (d *SLibguestfsDriver) SaveToGlance(req *apis.SaveToGlanceParams) (*apis.SaveToGlanceResponse, error) {
  242. return fsutils.SaveToGlance(d, req)
  243. }
  244. func (d *SLibguestfsDriver) FormatFs(req *apis.FormatFsParams) (*apis.Empty, error) {
  245. return fsutils.FormatFs(d, req)
  246. }
  247. func (d *SLibguestfsDriver) ProbeImageInfo(req *apis.ProbeImageInfoPramas) (*apis.ImageInfo, error) {
  248. return fsutils.ProbeImageInfo(d)
  249. }