diff --git a/vboxwebsrv/vboxwebsrv.go b/vboxwebsrv/vboxwebsrv.go index 93ad76b..f5c2bf6 100644 --- a/vboxwebsrv/vboxwebsrv.go +++ b/vboxwebsrv/vboxwebsrv.go @@ -15800,7 +15800,7 @@ type IAdditionsFacility struct { } type IMediumAttachment struct { - XMLName xml.Name `xml:"http://www.virtualbox.org/ IMediumAttachment"` + // XMLName xml.Name `xml:"http://www.virtualbox.org/ IMediumAttachment"` Medium string `xml:"medium,omitempty"` Controller string `xml:"controller,omitempty"` @@ -20347,6 +20347,8 @@ func (service *VboxPortType) IMachinegetMedium(request *IMachinegetMedium) (*IMa // - InvalidObjectFault // - RuntimeFault +type Returnval *IMediumAttachment + func (service *VboxPortType) IMachinegetMediumAttachmentsOfController(request *IMachinegetMediumAttachmentsOfController) (*IMachinegetMediumAttachmentsOfControllerResponse, error) { response := new(IMachinegetMediumAttachmentsOfControllerResponse) err := service.client.Call("", request, response) @@ -33330,17 +33332,20 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro encoder := xml.NewEncoder(buffer) //encoder.Indent(" ", " ") - err := encoder.Encode(envelope) - if err == nil { - err = encoder.Flush() + if err := encoder.Encode(envelope); err != nil { + return err } - log.Println(buffer.String()) - if err != nil { + if err := encoder.Flush(); err != nil { return err } + // log.Println(buffer.String()) + req, err := http.NewRequest("POST", s.url, buffer) + if err != nil { + return err + } if s.auth != nil { req.SetBasicAuth(s.auth.Login, s.auth.Password) } @@ -33368,12 +33373,16 @@ func (s *SOAPClient) Call(soapAction string, request, response interface{}) erro defer res.Body.Close() rawbody, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + if len(rawbody) == 0 { log.Println("empty response") return nil } - log.Println(string(rawbody)) + // log.Println(string(rawbody)) respEnvelope := new(SOAPEnvelope) respEnvelope.Body = SOAPBody{Content: response} err = xml.Unmarshal(rawbody, respEnvelope) diff --git a/virtualboxclient/hard_disk.go b/virtualboxclient/hard_disk.go new file mode 100644 index 0000000..5fad1d8 --- /dev/null +++ b/virtualboxclient/hard_disk.go @@ -0,0 +1,49 @@ +package virtualboxclient + +type HardDisk struct { + virtualbox *VirtualBox + managedObjectId string +} + +type HardDisks struct { + disks []*HardDisk +} + +func (h *HardDisk) getMedium() *Medium { + return &Medium{virtualbox: h.virtualbox, managedObjectId: h.managedObjectId} +} + +func isSet(value string) bool { + return value != "" +} + +func (hs *HardDisks) GetMedium(objectID, name string) ([]*Medium, error) { + var ms []*Medium + for _, hardDisk := range hs.disks { + om := hardDisk.getMedium() + var m *Medium + if isSet(name) || isSet(objectID) { + var err error + m, err = om.GetIDName() + if err != nil { + return nil, err + } + } + + if isSet(name) && m.Name != name { + continue + } + + if isSet(objectID) && m.ID != objectID { + continue + } + + medium, err := om.Get() + if err != nil { + return nil, err + } + ms = append(ms, medium) + } + + return ms, nil +} diff --git a/virtualboxclient/machine.go b/virtualboxclient/machine.go index 7bb2bb2..c1fab22 100644 --- a/virtualboxclient/machine.go +++ b/virtualboxclient/machine.go @@ -1,12 +1,16 @@ package virtualboxclient import ( + "errors" + "github.com/appropriate/go-virtualboxclient/vboxwebsrv" ) type Machine struct { virtualbox *VirtualBox managedObjectId string + ID string + Name string } func (m *Machine) GetChipsetType() (*vboxwebsrv.ChipsetType, error) { @@ -28,6 +32,18 @@ func (m *Machine) GetMediumAttachments() ([]*vboxwebsrv.IMediumAttachment, error return nil, err // TODO: Wrap the error } + ret := response.Returnval + return ret, nil +} + +func (m *Machine) GetMediumAttachmentsOfController(cName string) ([]*vboxwebsrv.IMediumAttachment, error) { + request := vboxwebsrv.IMachinegetMediumAttachmentsOfController{This: m.managedObjectId, Name: cName} + + response, err := m.virtualbox.IMachinegetMediumAttachmentsOfController(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + return response.Returnval, nil } @@ -53,6 +69,29 @@ func (m *Machine) GetSettingsFilePath() (string, error) { return response.Returnval, nil } +func (m *Machine) SaveSettings() error { + request := vboxwebsrv.IMachinesaveSettings{This: m.managedObjectId} + + _, err := m.virtualbox.IMachinesaveSettings(&request) + if err != nil { + defer m.DiscardSettings() + return err // TODO: Wrap the error + } + + return nil +} + +func (m *Machine) DiscardSettings() error { + request := vboxwebsrv.IMachinediscardSettings{This: m.managedObjectId} + + _, err := m.virtualbox.IMachinediscardSettings(&request) + if err != nil { + return err // TODO: Wrap the error + } + + return nil +} + func (m *Machine) GetStorageControllers() ([]*StorageController, error) { request := vboxwebsrv.IMachinegetStorageControllers{This: m.managedObjectId} @@ -63,8 +102,209 @@ func (m *Machine) GetStorageControllers() ([]*StorageController, error) { storageControllers := make([]*StorageController, len(response.Returnval)) for i, oid := range response.Returnval { - storageControllers[i] = &StorageController{m.virtualbox, oid} + storageControllers[i] = &StorageController{virtualbox: m.virtualbox, managedObjectId: oid} } return storageControllers, nil } + +func (m *Machine) GetStorageController(name string) (*StorageController, error) { + if name == "" { + return nil, errors.New("storage controller name not specified") + } + scs, err := m.GetStorageControllers() + if err != nil { + return nil, err + } + + for _, sc := range scs { + scName, err := sc.GetName() + if err != nil { + return nil, err + } + if scName == name { + sc.Name = scName + return sc, nil + } + } + return nil, errors.New("storage controller not found") +} + +func (m *Machine) AttachDevice(medium *Medium) error { + session, err := m.virtualbox.GetSession() + if err != nil { + return err + } + // defer session.Release() + + if err := m.Lock(session, vboxwebsrv.LockTypeShared); err != nil { + return err + } + defer m.Unlock(session) + + sm, err := session.GetMachine() + if err != nil { + return err + } + defer sm.Release() + + if m.virtualbox.controllerName == "" { + return errors.New("missing controllerName") + } + + sc, err := sm.GetStorageController(m.virtualbox.controllerName) + if err != nil { + return err + } + + pn, err := sc.GetNextAvailablePort(m) + if err != nil { + return err + } + + pc, err := sc.GetPortCount() + if err != nil { + return err + } + + spc := uint32(pn) + 1 + if spc > pc { + if err := sc.SetPortCount(spc); err != nil { + return err + } + } + + request := vboxwebsrv.IMachineattachDevice{ + This: sm.managedObjectId, + Name: sc.Name, + ControllerPort: pn, + Device: 0, + Type_: &medium.DeviceType, + Medium: medium.managedObjectId, + } + + _, err = m.virtualbox.IMachineattachDevice(&request) + if err != nil { + return err + } + + if err := sm.SaveSettings(); err != nil { + return err + } + + if err := m.Unlock(session); err != nil { + return err + } + + return nil +} + +func (m *Machine) DetachDevice(medium *Medium) error { + + session, err := m.virtualbox.GetSession() + if err != nil { + return err + } + // defer session.Release() + + if err := m.Lock(session, vboxwebsrv.LockTypeShared); err != nil { + return err + } + defer m.Unlock(session) + + sm, err := session.GetMachine() + if err != nil { + return err + } + defer sm.Release() + + mediumAttachments, err := m.GetMediumAttachments() + if err != nil { + return err + } + + var request *vboxwebsrv.IMachinedetachDevice + for _, ma := range mediumAttachments { + am := &Medium{virtualbox: m.virtualbox, managedObjectId: ma.Medium} + defer am.Release() + amID, err := am.GetID() + if err != nil { + return err + } + + if amID != medium.ID { + continue + } + request = &vboxwebsrv.IMachinedetachDevice{ + This: sm.managedObjectId, + Name: ma.Controller, + ControllerPort: ma.Port, + Device: 0, + } + } + if request == nil { + return errors.New("couldn't find attached medium") + } + + _, err = m.virtualbox.IMachinedetachDevice(request) + if err != nil { + return err + } + + if err := sm.SaveSettings(); err != nil { + return err + } + + return nil +} + +func (m *Machine) Unlock(session *Session) error { + if err := session.UnlockMachine(); err != nil { + return err + } + return nil +} + +func (m *Machine) Lock(session *Session, lockType vboxwebsrv.LockType) error { + if err := session.LockMachine(m, lockType); err != nil { + return err + } + return nil +} + +func (m *Machine) GetID() (string, error) { + request := vboxwebsrv.IMachinegetId{This: m.managedObjectId} + + response, err := m.virtualbox.IMachinegetId(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Machine) GetName() (string, error) { + request := vboxwebsrv.IMachinegetName{This: m.managedObjectId} + + response, err := m.virtualbox.IMachinegetName(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Machine) Release() error { + return m.virtualbox.Release(m.managedObjectId) +} + +func (m *Machine) Refresh() error { + if mr, err := m.virtualbox.FindMachine(m.ID); err != nil { + return err + } else { + m.managedObjectId = mr.managedObjectId + } + return nil +} diff --git a/virtualboxclient/medium.go b/virtualboxclient/medium.go index ffe0104..370e447 100644 --- a/virtualboxclient/medium.go +++ b/virtualboxclient/medium.go @@ -1,12 +1,24 @@ package virtualboxclient -import ( - "github.com/appropriate/go-virtualboxclient/vboxwebsrv" -) +import "github.com/appropriate/go-virtualboxclient/vboxwebsrv" type Medium struct { virtualbox *VirtualBox managedObjectId string + Location string + Name string + DeviceType vboxwebsrv.DeviceType + Description string + LogicalSize int64 + Size int64 + Format string + MediumFormat string + HostDrive bool + Children []string + Parent string + ID string + MachineIDs []string + SnapshotIDs []string } func (m *Medium) CreateBaseStorage(logicalSize int64, variant []*vboxwebsrv.MediumVariant) (*Progress, error) { @@ -18,7 +30,7 @@ func (m *Medium) CreateBaseStorage(logicalSize int64, variant []*vboxwebsrv.Medi } // TODO: See if we need to do anything with the response - return &Progress{managedObjectId: response.Returnval}, nil + return &Progress{virtualbox: m.virtualbox, managedObjectId: response.Returnval}, nil } func (m *Medium) DeleteStorage() (*Progress, error) { @@ -30,5 +42,293 @@ func (m *Medium) DeleteStorage() (*Progress, error) { } // TODO: See if we need to do anything with the response - return &Progress{managedObjectId: response.Returnval}, nil + return &Progress{virtualbox: m.virtualbox, managedObjectId: response.Returnval}, nil +} + +func (m *Medium) Release() error { + return m.virtualbox.Release(m.managedObjectId) +} + +func (m *Medium) GetLocation() (string, error) { + request := vboxwebsrv.IMediumgetLocation{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetLocation(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetName() (string, error) { + request := vboxwebsrv.IMediumgetName{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetName(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetDeviceType() (*vboxwebsrv.DeviceType, error) { + request := vboxwebsrv.IMediumgetDeviceType{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetDeviceType(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetDescription() (string, error) { + request := vboxwebsrv.IMediumgetDescription{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetDescription(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetSize() (int64, error) { + request := vboxwebsrv.IMediumgetSize{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetSize(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetLogicalSize() (int64, error) { + request := vboxwebsrv.IMediumgetLogicalSize{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetLogicalSize(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetState() (*vboxwebsrv.MediumState, error) { + request := vboxwebsrv.IMediumgetState{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetState(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetFormat() (string, error) { + request := vboxwebsrv.IMediumgetFormat{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetFormat(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetMediumFormat() (string, error) { + request := vboxwebsrv.IMediumgetMediumFormat{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetMediumFormat(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetHostDrive() (bool, error) { + request := vboxwebsrv.IMediumgetHostDrive{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetHostDrive(&request) + if err != nil { + return false, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetParent() (string, error) { + request := vboxwebsrv.IMediumgetParent{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetParent(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetChildren() ([]string, error) { + request := vboxwebsrv.IMediumgetChildren{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetChildren(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) DetachMachines() error { + for _, mid := range m.MachineIDs { + machine, err := m.virtualbox.FindMachine(mid) + if err != nil { + return err + } + defer machine.Release() + + if err := machine.DetachDevice(m); err != nil { + return err + } + } + return nil +} + +func (m *Medium) GetID() (string, error) { + request := vboxwebsrv.IMediumgetId{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetId(&request) + if err != nil { + return "", err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetSnapshotIDs() ([]string, error) { + request := vboxwebsrv.IMediumgetSnapshotIds{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetSnapshotIds(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) GetMachineIDs() ([]string, error) { + request := vboxwebsrv.IMediumgetMachineIds{This: m.managedObjectId} + + response, err := m.virtualbox.IMediumgetMachineIds(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (m *Medium) Get() (*Medium, error) { + var err error + m.Location, err = m.GetLocation() + if err != nil { + return nil, err + } + m.Name, err = m.GetName() + if err != nil { + return nil, err + } + + m.Description, err = m.GetDescription() + if err != nil { + return nil, err + } + + m.Size, err = m.GetSize() + if err != nil { + return nil, err + } + + m.LogicalSize, err = m.GetLogicalSize() + if err != nil { + return nil, err + } + + dt, err := m.GetDeviceType() + if err != nil { + return nil, err + } + m.DeviceType = *dt + + m.Format, err = m.GetFormat() + if err != nil { + return nil, err + } + + m.MediumFormat, err = m.GetMediumFormat() + if err != nil { + return nil, err + } + + m.HostDrive, err = m.GetHostDrive() + if err != nil { + return nil, err + } + + m.Children, err = m.GetChildren() + if err != nil { + return nil, err + } + + m.Parent, err = m.GetParent() + if err != nil { + return nil, err + } + + m.ID, err = m.GetID() + if err != nil { + return nil, err + } + + m.MachineIDs, err = m.GetMachineIDs() + if err != nil { + return nil, err + } + + m.SnapshotIDs, err = m.GetSnapshotIDs() + if err != nil { + return nil, err + } + + return m, nil + +} + +func (m *Medium) GetIDName() (*Medium, error) { + var err error + m.ID, err = m.GetID() + if err != nil { + return nil, err + } + m.Name, err = m.GetName() + if err != nil { + return nil, err + } + return m, nil } diff --git a/virtualboxclient/medium_attachment.go b/virtualboxclient/medium_attachment.go new file mode 100644 index 0000000..b39c606 --- /dev/null +++ b/virtualboxclient/medium_attachment.go @@ -0,0 +1,13 @@ +package virtualboxclient + +import "github.com/appropriate/go-virtualboxclient/vboxwebsrv" + +type MediumAttachment struct { + *vboxwebsrv.IMediumAttachment + virtualbox *VirtualBox + managedObjectId string +} + +func (m *MediumAttachment) GetMedium() (*Medium, error) { + return &Medium{virtualbox: m.virtualbox, managedObjectId: m.Medium}, nil +} diff --git a/virtualboxclient/progress.go b/virtualboxclient/progress.go index dbac7a7..fd44fea 100644 --- a/virtualboxclient/progress.go +++ b/virtualboxclient/progress.go @@ -1,7 +1,38 @@ package virtualboxclient +import "github.com/appropriate/go-virtualboxclient/vboxwebsrv" + type Progress struct { virtualbox *VirtualBox managedObjectId string } + +func (p *Progress) WaitForCompletion(timeout int32) error { + request := vboxwebsrv.IProgresswaitForCompletion{This: p.managedObjectId} + request.Timeout = timeout + + _, err := p.virtualbox.IProgresswaitForCompletion(&request) + if err != nil { + return err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return nil +} + +func (p *Progress) GetPercent() (uint32, error) { + request := vboxwebsrv.IProgressgetPercent{This: p.managedObjectId} + + response, err := p.virtualbox.IProgressgetPercent(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return response.Returnval, nil +} + +func (p *Progress) Release() error { + return p.virtualbox.Release(p.managedObjectId) +} diff --git a/virtualboxclient/session.go b/virtualboxclient/session.go new file mode 100644 index 0000000..dddea09 --- /dev/null +++ b/virtualboxclient/session.go @@ -0,0 +1,49 @@ +package virtualboxclient + +import "github.com/appropriate/go-virtualboxclient/vboxwebsrv" + +type Session struct { + virtualbox *VirtualBox + managedObjectId string +} + +func (s *Session) UnlockMachine() error { + request := vboxwebsrv.ISessionunlockMachine{This: s.managedObjectId} + _, err := s.virtualbox.ISessionunlockMachine(&request) + if err != nil { + return err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return nil +} + +func (s *Session) LockMachine(m *Machine, l vboxwebsrv.LockType) error { + request := vboxwebsrv.IMachinelockMachine{ + This: m.managedObjectId, + Session: s.managedObjectId, + LockType: &l, + } + _, err := s.virtualbox.IMachinelockMachine(&request) + if err != nil { + return err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return nil +} + +func (s *Session) GetMachine() (*Machine, error) { + request := vboxwebsrv.ISessiongetMachine{This: s.managedObjectId} + response, err := s.virtualbox.ISessiongetMachine(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return &Machine{managedObjectId: response.Returnval, virtualbox: s.virtualbox}, nil +} + +func (s *Session) Release() error { + return s.virtualbox.Release(s.managedObjectId) +} diff --git a/virtualboxclient/state.go b/virtualboxclient/state.go new file mode 100644 index 0000000..485cc90 --- /dev/null +++ b/virtualboxclient/state.go @@ -0,0 +1,7 @@ +package virtualboxclient + +type State struct { + virtualbox *VirtualBox + + managedObjectId string +} diff --git a/virtualboxclient/storage_controller.go b/virtualboxclient/storage_controller.go index a68a293..da7f8a3 100644 --- a/virtualboxclient/storage_controller.go +++ b/virtualboxclient/storage_controller.go @@ -1,12 +1,19 @@ package virtualboxclient import ( + "errors" + "github.com/appropriate/go-virtualboxclient/vboxwebsrv" ) type StorageController struct { virtualbox *VirtualBox managedObjectId string + Name string +} + +type StorageControllers struct { + storageControllers []*StorageController } func (sc *StorageController) GetName() (string, error) { @@ -30,3 +37,73 @@ func (sc *StorageController) GetPortCount() (uint32, error) { return response.Returnval, nil } + +func (sc *StorageController) GetStorageBus() (vboxwebsrv.StorageBus, error) { + mapStorageBus := make(map[string]vboxwebsrv.StorageBus) + mapStorageBus["SATA Controller"] = vboxwebsrv.StorageBusSATA + mapStorageBus["IDE Controller"] = vboxwebsrv.StorageBusIDE + mapStorageBus["SCSI"] = vboxwebsrv.StorageBusSCSI + mapStorageBus["SAS"] = vboxwebsrv.StorageBusSAS + + scName, err := sc.GetName() + if err != nil { + return vboxwebsrv.StorageBusNull, err + } + + if bus, ok := mapStorageBus[scName]; ok { + return bus, nil + } + + return vboxwebsrv.StorageBusNull, errors.New("bad controller controller specified") +} + +func (sc *StorageController) GetMaxPortCount() (uint32, error) { + request := vboxwebsrv.IStorageControllergetMaxPortCount{This: sc.managedObjectId} + + response, err := sc.virtualbox.IStorageControllergetMaxPortCount(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + return response.Returnval, nil +} + +func (sc *StorageController) SetPortCount(count uint32) error { + request := vboxwebsrv.IStorageControllersetPortCount{This: sc.managedObjectId, PortCount: count} + + _, err := sc.virtualbox.IStorageControllersetPortCount(&request) + if err != nil { + return err // TODO: Wrap the error + } + + return nil +} + +func (sc *StorageController) GetNextAvailablePort(m *Machine) (int32, error) { + c, err := sc.GetMaxPortCount() + if err != nil { + return 0, err + } + + ams, err := m.GetMediumAttachmentsOfController(sc.Name) + if err != nil { + return 0, nil + } + + portMap := make(map[int32]bool) + for _, am := range ams { + portMap[am.Port] = true + } + + intArr := make([]int32, c) + for i := range intArr { + if _, isUsed := portMap[int32(i)]; !isUsed { + return int32(i), nil + } + } + return 0, errors.New("no available ports") +} + +func (sc *StorageController) Release() error { + return sc.virtualbox.Release(sc.managedObjectId) +} diff --git a/virtualboxclient/system_properties.go b/virtualboxclient/system_properties.go index aded96c..4d154bf 100644 --- a/virtualboxclient/system_properties.go +++ b/virtualboxclient/system_properties.go @@ -19,3 +19,27 @@ func (sp *SystemProperties) GetMaxNetworkAdapters(chipset *vboxwebsrv.ChipsetTyp return response.Returnval, nil } + +func (sp *SystemProperties) GetMaxDevicesPerPortForStorageBus(bus vboxwebsrv.StorageBus) (uint32, error) { + request := vboxwebsrv.ISystemPropertiesgetMaxDevicesPerPortForStorageBus{This: sp.managedObjectId, Bus: &bus} + response, err := sp.virtualbox.ISystemPropertiesgetMaxDevicesPerPortForStorageBus(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + return response.Returnval, nil +} + +func (sp *SystemProperties) GetMinPortCountForStorageBus(bus vboxwebsrv.StorageBus) (uint32, error) { + request := vboxwebsrv.ISystemPropertiesgetMinPortCountForStorageBus{This: sp.managedObjectId, Bus: &bus} + response, err := sp.virtualbox.ISystemPropertiesgetMinPortCountForStorageBus(&request) + if err != nil { + return 0, err // TODO: Wrap the error + } + + return response.Returnval, nil +} + +func (sp *SystemProperties) Release() error { + return sp.virtualbox.Release(sp.managedObjectId) +} diff --git a/virtualboxclient/virtualbox.go b/virtualboxclient/virtualbox.go index ef90922..6c3a0f6 100644 --- a/virtualboxclient/virtualbox.go +++ b/virtualboxclient/virtualbox.go @@ -1,30 +1,31 @@ package virtualboxclient import ( + "errors" + "github.com/appropriate/go-virtualboxclient/vboxwebsrv" ) type VirtualBox struct { *vboxwebsrv.VboxPortType - - username string - password string - managedObjectId string + basicAuth *vboxwebsrv.BasicAuth + controllerName string } -func New(username, password, url string) *VirtualBox { +func New(username, password, url string, tls bool, controllerName string) *VirtualBox { + basicAuth := &vboxwebsrv.BasicAuth{ + Login: username, + Password: password, + } return &VirtualBox{ - VboxPortType: vboxwebsrv.NewVboxPortType(url, false, nil), - - username: username, - password: password, + VboxPortType: vboxwebsrv.NewVboxPortType(url, tls, basicAuth), + basicAuth: basicAuth, + controllerName: controllerName, } } func (vb *VirtualBox) CreateHardDisk(format, location string) (*Medium, error) { - vb.Logon() - request := vboxwebsrv.IVirtualBoxcreateHardDisk{This: vb.managedObjectId, Format: format, Location: location} response, err := vb.IVirtualBoxcreateHardDisk(&request) @@ -36,8 +37,6 @@ func (vb *VirtualBox) CreateHardDisk(format, location string) (*Medium, error) { } func (vb *VirtualBox) GetMachines() ([]*Machine, error) { - vb.Logon() - request := vboxwebsrv.IVirtualBoxgetMachines{This: vb.managedObjectId} response, err := vb.IVirtualBoxgetMachines(&request) @@ -47,15 +46,13 @@ func (vb *VirtualBox) GetMachines() ([]*Machine, error) { machines := make([]*Machine, len(response.Returnval)) for n, oid := range response.Returnval { - machines[n] = &Machine{vb, oid} + machines[n] = &Machine{virtualbox: vb, managedObjectId: oid} } return machines, nil } func (vb *VirtualBox) GetSystemProperties() (*SystemProperties, error) { - vb.Logon() - request := vboxwebsrv.IVirtualBoxgetSystemProperties{This: vb.managedObjectId} response, err := vb.IVirtualBoxgetSystemProperties(&request) @@ -67,14 +64,9 @@ func (vb *VirtualBox) GetSystemProperties() (*SystemProperties, error) { } func (vb *VirtualBox) Logon() error { - if vb.managedObjectId != "" { - // Already logged in - return nil - } - request := vboxwebsrv.IWebsessionManagerlogon{ - Username: vb.username, - Password: vb.password, + Username: vb.basicAuth.Login, + Password: vb.basicAuth.Password, } response, err := vb.IWebsessionManagerlogon(&request) @@ -86,3 +78,130 @@ func (vb *VirtualBox) Logon() error { return nil } + +func (vb *VirtualBox) GetHardDisk(objectID string) (*HardDisks, error) { + request := vboxwebsrv.IVirtualBoxgetHardDisks{This: vb.managedObjectId} + + response, err := vb.IVirtualBoxgetHardDisks(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + var hardDisks []*HardDisk + for _, oid := range response.Returnval { + if objectID == "" || objectID == oid { + hardDisks = append(hardDisks, &HardDisk{vb, oid}) + } + } + + return &HardDisks{disks: hardDisks}, nil +} + +func (vb *VirtualBox) CreateMedium(format string, location string, size int64) (*Medium, error) { + + medium, err := vb.CreateHardDisk(format, location) + if err != nil { + return nil, err + } + defer medium.Release() + + progress, err := medium.CreateBaseStorage(size, nil) + if err != nil { + return nil, err + } + + if err := progress.WaitForCompletion(-1); err != nil { + return nil, err + } + + if p, err := progress.GetPercent(); err != nil { + return nil, err + } else if p != 100 { + return nil, errors.New("failed to create medium") + } + + return medium.Get() +} + +func (vb *VirtualBox) GetMedium(mediumID, mediumName string) ([]*Medium, error) { + hardDisks, err := vb.GetHardDisk("") + if err != nil { + return nil, err + } + + return hardDisks.GetMedium(mediumID, mediumName) +} + +func (vb *VirtualBox) RemoveMedium(mediumID string) error { + if mediumID == "" { + return errors.New("mediumID is empty") + } + + mediums, err := vb.GetMedium(mediumID, "") + if err != nil { + return err + } + + if len(mediums) == 0 { + return errors.New("no mediums returned") + } + + progress, err := mediums[0].DeleteStorage() + if err != nil { + return err + } + + if err := progress.WaitForCompletion(-1); err != nil { + return err + } + + if p, err := progress.GetPercent(); err != nil { + return err + } else if p != 100 { + return errors.New("failed to remove medium") + } + + return nil +} + +func (vb *VirtualBox) GetSession() (*Session, error) { + request := vboxwebsrv.IWebsessionManagergetSessionObject{RefIVirtualBox: vb.managedObjectId} + response, err := vb.IWebsessionManagergetSessionObject(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + + return &Session{managedObjectId: response.Returnval, virtualbox: vb}, nil +} + +func (vb *VirtualBox) FindMachine(nameOrID string) (*Machine, error) { + request := vboxwebsrv.IVirtualBoxfindMachine{This: vb.managedObjectId, NameOrId: nameOrID} + response, err := vb.IVirtualBoxfindMachine(&request) + if err != nil { + return nil, err // TODO: Wrap the error + } + + return &Machine{managedObjectId: response.Returnval, virtualbox: vb}, nil +} + +func (vb *VirtualBox) Release(managedObjectId string) error { + request := vboxwebsrv.IManagedObjectRefrelease{This: managedObjectId} + + _, err := vb.IManagedObjectRefrelease(&request) + if err != nil { + return err // TODO: Wrap the error + } + + // TODO: See if we need to do anything with the response + return nil +} + +func (vb *VirtualBox) GetMOID() string { + return vb.managedObjectId +} + +func (vb *VirtualBox) NewMedium(moid string) *Medium { + return &Medium{virtualbox: vb, managedObjectId: moid} +}