/vbox.go

https://github.com/uruddarraju/virtualbox-go · Go · 208 lines · 160 code · 37 blank · 11 comment · 23 complexity · 811eba9c6f51dc6ac8004d04b36cb326 MD5 · raw file

  1. package virtualbox
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "os/exec"
  7. "os/user"
  8. "path/filepath"
  9. "regexp"
  10. "strings"
  11. "github.com/golang/glog"
  12. )
  13. const (
  14. VBoxManage = "VBoxManage"
  15. NatPortBase = 10900
  16. )
  17. var DefaultVBBasePath = GetDefaultVBBasePath()
  18. var reColonLine = regexp.MustCompile(`([^:]+):\s+(.*)`)
  19. // parses lines like the following
  20. // foo="bar"
  21. var reKeyEqVal = regexp.MustCompile(`([^=]+)=\s*(.*)`)
  22. // Config for the Manager
  23. type Config struct {
  24. // BasePath is the base filesystem location for managing this provider's configuration
  25. // Defaults to $HOME/.vbm/VBox BasePath string
  26. BasePath string
  27. // VirtualBoxPath is where the VirtualBox cmd is available on the local machine
  28. VirtualBoxPath string
  29. Groups []string
  30. // expected to be managed by this tool
  31. Networks []Network
  32. }
  33. // VBox uses the VBoxManage command for its functionality
  34. type VBox struct {
  35. Config Config
  36. Verbose bool
  37. // as discovered and includes networks created out of band (not through this api)
  38. // TODO: Merge them to a single map and provide accessors for specific filtering
  39. HostOnlyNws map[string]*Network
  40. BridgedNws map[string]*Network
  41. InternalNws map[string]*Network
  42. NatNws map[string]*Network
  43. }
  44. func NewVBox(config Config) *VBox {
  45. if config.BasePath == "" {
  46. config.BasePath = DefaultVBBasePath
  47. }
  48. return &VBox{
  49. Config: config,
  50. HostOnlyNws: make(map[string]*Network),
  51. BridgedNws: make(map[string]*Network),
  52. InternalNws: make(map[string]*Network),
  53. NatNws: make(map[string]*Network),
  54. }
  55. }
  56. func GetDefaultVBBasePath() string {
  57. user, err := user.Current()
  58. if err != nil {
  59. panic(fmt.Errorf("basepath not supplied and default location cannot be determined %v", err))
  60. }
  61. return fmt.Sprintf("%s/VirtualBox VMs", user.HomeDir)
  62. }
  63. func IsVBoxError(err error) bool {
  64. _, ok := err.(VBoxError)
  65. return ok
  66. }
  67. //VBoxError are errors that are returned as error by Virtualbox cli on stderr
  68. type VBoxError string
  69. func (ve VBoxError) Error() string {
  70. return string(ve)
  71. }
  72. func (vb *VBox) getVMBaseDir(vm *VirtualMachine) string {
  73. var group string
  74. if vm.Spec.Group != "" {
  75. group = vm.Spec.Group
  76. }
  77. return filepath.Join(vb.Config.BasePath, group, vm.Spec.Name)
  78. }
  79. func (vb *VBox) getVMSettingsFile(vm *VirtualMachine) string {
  80. return filepath.Join(vb.getVMBaseDir(vm), vm.Spec.Name+".vbox")
  81. }
  82. func (vb *VBox) manage(args ...string) (string, error) {
  83. vboxManage := vboxManagePath()
  84. cmd := exec.Command(vboxManage, args...)
  85. glog.V(4).Infof("COMMAND: %v %v", vboxManage, strings.Join(args, " "))
  86. var stdout bytes.Buffer
  87. var stderr bytes.Buffer
  88. cmd.Stdout = &stdout
  89. cmd.Stderr = &stderr
  90. err := cmd.Run()
  91. stderrStr := stderr.String()
  92. if err != nil {
  93. if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
  94. return "", errors.New("unable to find VBoxManage command in path")
  95. }
  96. return "", VBoxError(stderrStr)
  97. }
  98. glog.V(10).Infof("STDOUT:\n{\n%v}", stdout.String())
  99. glog.V(10).Infof("STDERR:\n{\n%v}", stderrStr)
  100. return string(stdout.Bytes()), err
  101. }
  102. func (vb *VBox) modify(vm *VirtualMachine, args ...string) (string, error) {
  103. return vb.manage(append([]string{"modifyvm", vm.UUIDOrName()}, args...)...)
  104. }
  105. func (vb *VBox) control(vm *VirtualMachine, args ...string) (string, error) {
  106. return vb.manage(append([]string{"controlvm", vm.UUIDOrName()}, args...)...)
  107. }
  108. func (vb *VBox) ListDHCPServers() (map[string]*DHCPServer, error) {
  109. listOutput, err := vb.manage("list", "dhcpservers")
  110. if err != nil {
  111. return nil, err
  112. }
  113. m := make(map[string]*DHCPServer)
  114. var dhcpServer *DHCPServer
  115. err = parseKeyValues(listOutput, reColonLine, func(key, val string) error {
  116. switch key {
  117. case "NetworkName":
  118. dhcpServer = &DHCPServer{}
  119. m[val] = dhcpServer
  120. dhcpServer.NetworkName = val
  121. case "IP":
  122. dhcpServer.IPAddress = val
  123. case "upperIPAddress":
  124. dhcpServer.UpperIPAddress = val
  125. case "lowerIPAddress":
  126. dhcpServer.LowerIPAddress = val
  127. case "NetworkMask":
  128. dhcpServer.NetworkMask = val
  129. case "Enabled":
  130. dhcpServer.Enabled = val == "Yes"
  131. }
  132. return nil
  133. })
  134. if err != nil {
  135. return nil, err
  136. }
  137. return m, nil
  138. }
  139. func (vb *VBox) ListOSTypes() (map[string]*OSType, error) {
  140. listOutput, err := vb.manage("list", "ostypes")
  141. if err != nil {
  142. return nil, err
  143. }
  144. m := make(map[string]*OSType)
  145. var osType *OSType
  146. err = parseKeyValues(listOutput, reColonLine, func(key, val string) error {
  147. switch key {
  148. case "ID":
  149. osType = &OSType{}
  150. m[val] = osType
  151. osType.ID = val
  152. case "Description":
  153. osType.Description = val
  154. case "Family ID":
  155. osType.FamilyID = val
  156. case "Family Desc":
  157. osType.FamilyDescription = val
  158. case "64 bit":
  159. osType.Bit64 = val == "true"
  160. }
  161. return nil
  162. })
  163. if err != nil {
  164. return nil, err
  165. }
  166. return m, nil
  167. }
  168. func (vb *VBox) MarkHDImmutable(hdPath string) error {
  169. vb.manage("modifyhd", hdPath, "--type", "immutable")
  170. return nil
  171. }