package common import ( "errors" "fmt" "github.com/nilshell/xmlrpc" "log" ) type XenAPIClient struct { Session interface{} Host string Url string Username string Password string RPC *xmlrpc.Client } type APIResult struct { Status string Value interface{} ErrorDescription string } type XenAPIObject struct { Ref string Client *XenAPIClient } type VM XenAPIObject type SR XenAPIObject type VDI XenAPIObject type Network XenAPIObject type VBD XenAPIObject type VIF XenAPIObject type PIF XenAPIObject type Pool XenAPIObject type Task XenAPIObject type VDIType int const ( _ VDIType = iota Disk CD Floppy ) type TaskStatusType int const ( _ TaskStatusType = iota Pending Success Failure Cancelling Cancelled ) func (c *XenAPIClient) RPCCall(result interface{}, method string, params []interface{}) (err error) { fmt.Println(params) p := new(xmlrpc.Params) p.Params = params err = c.RPC.Call(method, *p, result) return err } func (client *XenAPIClient) Login() (err error) { //Do loging call result := xmlrpc.Struct{} params := make([]interface{}, 2) params[0] = client.Username params[1] = client.Password err = client.RPCCall(&result, "session.login_with_password", params) client.Session = result["Value"] return err } func (client *XenAPIClient) APICall(result *APIResult, method string, params ...interface{}) (err error) { if client.Session == nil { fmt.Println("Error: no session") return fmt.Errorf("No session. Unable to make call") } //Make a params slice which will include the session p := make([]interface{}, len(params)+1) p[0] = client.Session if params != nil { for idx, element := range params { p[idx+1] = element } } res := xmlrpc.Struct{} err = client.RPCCall(&res, method, p) if err != nil { return err } result.Status = res["Status"].(string) if result.Status != "Success" { fmt.Println("Encountered an API error: ", result.Status) fmt.Println(res["ErrorDescription"]) return fmt.Errorf("API Error: %s", res["ErrorDescription"]) } else { result.Value = res["Value"] } return } func (client *XenAPIClient) GetHosts() (err error) { result := APIResult{} _ = client.APICall(&result, "host.get_all") hosts := result.Value fmt.Println(hosts) return nil } func (client *XenAPIClient) GetPools() (pools []*Pool, err error) { pools = make([]*Pool, 0) result := APIResult{} err = client.APICall(&result, "pool.get_all") if err != nil { return pools, err } for _, elem := range result.Value.([]interface{}) { pool := new(Pool) pool.Ref = elem.(string) pool.Client = client pools = append(pools, pool) } return pools, nil } func (client *XenAPIClient) GetDefaultSR() (sr *SR, err error) { pools, err := client.GetPools() if err != nil { return nil, err } pool_rec, err := pools[0].GetRecord() if err != nil { return nil, err } if pool_rec["default_SR"] == "" { return nil, errors.New("No default_SR specified for the pool.") } sr = new(SR) sr.Ref = pool_rec["default_SR"].(string) sr.Client = client return sr, nil } func (client *XenAPIClient) GetVMByUuid(vm_uuid string) (vm *VM, err error) { vm = new(VM) result := APIResult{} err = client.APICall(&result, "VM.get_by_uuid", vm_uuid) if err != nil { return nil, err } vm.Ref = result.Value.(string) vm.Client = client return } func (client *XenAPIClient) GetVMByNameLabel(name_label string) (vms []*VM, err error) { vms = make([]*VM, 0) result := APIResult{} err = client.APICall(&result, "VM.get_by_name_label", name_label) if err != nil { return vms, err } for _, elem := range result.Value.([]interface{}) { vm := new(VM) vm.Ref = elem.(string) vm.Client = client vms = append(vms, vm) } return vms, nil } func (client *XenAPIClient) GetSRByNameLabel(name_label string) (srs []*SR, err error) { srs = make([]*SR, 0) result := APIResult{} err = client.APICall(&result, "SR.get_by_name_label", name_label) if err != nil { return srs, err } for _, elem := range result.Value.([]interface{}) { sr := new(SR) sr.Ref = elem.(string) sr.Client = client srs = append(srs, sr) } return srs, nil } func (client *XenAPIClient) GetNetworkByUuid(network_uuid string) (network *Network, err error) { network = new(Network) result := APIResult{} err = client.APICall(&result, "network.get_by_uuid", network_uuid) if err != nil { return nil, err } network.Ref = result.Value.(string) network.Client = client return } func (client *XenAPIClient) GetNetworkByNameLabel(name_label string) (networks []*Network, err error) { networks = make([]*Network, 0) result := APIResult{} err = client.APICall(&result, "network.get_by_name_label", name_label) if err != nil { return networks, err } for _, elem := range result.Value.([]interface{}) { network := new(Network) network.Ref = elem.(string) network.Client = client networks = append(networks, network) } return networks, nil } func (client *XenAPIClient) GetVdiByNameLabel(name_label string) (vdis []*VDI, err error) { vdis = make([]*VDI, 0) result := APIResult{} err = client.APICall(&result, "VDI.get_by_name_label", name_label) if err != nil { return vdis, err } for _, elem := range result.Value.([]interface{}) { vdi := new(VDI) vdi.Ref = elem.(string) vdi.Client = client vdis = append(vdis, vdi) } return vdis, nil } func (client *XenAPIClient) GetSRByUuid(sr_uuid string) (sr *SR, err error) { sr = new(SR) result := APIResult{} err = client.APICall(&result, "SR.get_by_uuid", sr_uuid) if err != nil { return nil, err } sr.Ref = result.Value.(string) sr.Client = client return } func (client *XenAPIClient) GetVdiByUuid(vdi_uuid string) (vdi *VDI, err error) { vdi = new(VDI) result := APIResult{} err = client.APICall(&result, "VDI.get_by_uuid", vdi_uuid) if err != nil { return nil, err } vdi.Ref = result.Value.(string) vdi.Client = client return } func (client *XenAPIClient) GetPIFs() (pifs []*PIF, err error) { pifs = make([]*PIF, 0) result := APIResult{} err = client.APICall(&result, "PIF.get_all") if err != nil { return pifs, err } for _, elem := range result.Value.([]interface{}) { pif := new(PIF) pif.Ref = elem.(string) pif.Client = client pifs = append(pifs, pif) } return pifs, nil } func (client *XenAPIClient) CreateTask() (task *Task, err error) { result := APIResult{} err = client.APICall(&result, "task.create", "packer-task", "Packer task") if err != nil { return } task = new(Task) task.Ref = result.Value.(string) task.Client = client return } // VM associated functions func (self *VM) Clone(label string) (new_instance *VM, err error) { new_instance = new(VM) result := APIResult{} err = self.Client.APICall(&result, "VM.clone", self.Ref, label) if err != nil { return nil, err } new_instance.Ref = result.Value.(string) new_instance.Client = self.Client return } func (self *VM) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.destroy", self.Ref) if err != nil { return err } return } func (self *VM) Start(paused, force bool) (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.start", self.Ref, paused, force) if err != nil { return err } return } func (self *VM) CleanShutdown() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.clean_shutdown", self.Ref) if err != nil { return err } return } func (self *VM) HardShutdown() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.hard_shutdown", self.Ref) if err != nil { return err } return } func (self *VM) Unpause() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.unpause", self.Ref) if err != nil { return err } return } func (self *VM) SetPVBootloader(pv_bootloader, pv_args string) (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.set_PV_bootloader", self.Ref, pv_bootloader) if err != nil { return err } result = APIResult{} err = self.Client.APICall(&result, "VM.set_PV_bootloader_args", self.Ref, pv_args) if err != nil { return err } return } func (self *VM) GetDomainId() (domid string, err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.get_domid", self.Ref) if err != nil { return "", err } domid = result.Value.(string) return domid, nil } func (self *VM) GetPowerState() (state string, err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.get_power_state", self.Ref) if err != nil { return "", err } state = result.Value.(string) return state, nil } func (self *VM) GetUuid() (uuid string, err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.get_uuid", self.Ref) if err != nil { return "", err } uuid = result.Value.(string) return uuid, nil } func (self *VM) GetVBDs() (vbds []VBD, err error) { vbds = make([]VBD, 0) result := APIResult{} err = self.Client.APICall(&result, "VM.get_VBDs", self.Ref) if err != nil { return vbds, err } for _, elem := range result.Value.([]interface{}) { vbd := VBD{} vbd.Ref = elem.(string) vbd.Client = self.Client vbds = append(vbds, vbd) } return vbds, nil } func (self *VM) GetVIFs() (vifs []VIF, err error) { vifs = make([]VIF, 0) result := APIResult{} err = self.Client.APICall(&result, "VM.get_VIFs", self.Ref) if err != nil { return vifs, err } for _, elem := range result.Value.([]interface{}) { vif := VIF{} vif.Ref = elem.(string) vif.Client = self.Client vifs = append(vifs, vif) } return vifs, nil } func (self *VM) GetDisks() (vdis []*VDI, err error) { // Return just data disks (non-isos) vdis = make([]*VDI, 0) vbds, err := self.GetVBDs() if err != nil { return nil, err } for _, vbd := range vbds { rec, err := vbd.GetRecord() if err != nil { return nil, err } if rec["type"] == "Disk" { vdi, err := vbd.GetVDI() if err != nil { return nil, err } vdis = append(vdis, vdi) } } return vdis, nil } func (self *VM) GetGuestMetricsRef() (ref string, err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.get_guest_metrics", self.Ref) if err != nil { return "", nil } ref = result.Value.(string) return ref, err } func (self *VM) GetGuestMetrics() (metrics map[string]interface{}, err error) { metrics = make(map[string]interface{}) metrics_ref, err := self.GetGuestMetricsRef() if err != nil { return metrics, err } result := APIResult{} err = self.Client.APICall(&result, "VM_guest_metrics.get_record", metrics_ref) if err != nil { return metrics, nil } for k, v := range result.Value.(xmlrpc.Struct) { metrics[k] = v } return metrics, nil } func (self *VM) SetStaticMemoryRange(min, max uint) (err error) { result := APIResult{} strMin := fmt.Sprintf("%d", min) strMax := fmt.Sprintf("%d", max) err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, strMin, strMax, strMin, strMax) if err != nil { return err } return } func (self *VM) ConnectVdi(vdi *VDI, vdiType VDIType) (err error) { // 1. Create a VBD vbd_rec := make(xmlrpc.Struct) vbd_rec["VM"] = self.Ref vbd_rec["VDI"] = vdi.Ref vbd_rec["userdevice"] = "autodetect" vbd_rec["empty"] = false vbd_rec["other_config"] = make(xmlrpc.Struct) vbd_rec["qos_algorithm_type"] = "" vbd_rec["qos_algorithm_params"] = make(xmlrpc.Struct) switch vdiType { case CD: vbd_rec["mode"] = "RO" vbd_rec["bootable"] = true vbd_rec["unpluggable"] = false vbd_rec["type"] = "CD" case Disk: vbd_rec["mode"] = "RW" vbd_rec["bootable"] = false vbd_rec["unpluggable"] = false vbd_rec["type"] = "Disk" case Floppy: vbd_rec["mode"] = "RW" vbd_rec["bootable"] = false vbd_rec["unpluggable"] = true vbd_rec["type"] = "Floppy" } result := APIResult{} err = self.Client.APICall(&result, "VBD.create", vbd_rec) if err != nil { return err } vbd_ref := result.Value.(string) fmt.Println("VBD Ref:", vbd_ref) result = APIResult{} err = self.Client.APICall(&result, "VBD.get_uuid", vbd_ref) fmt.Println("VBD UUID: ", result.Value.(string)) /* // 2. Plug VBD (Non need - the VM hasn't booted. // @todo - check VM state result = APIResult{} err = self.Client.APICall(&result, "VBD.plug", vbd_ref) if err != nil { return err } */ return } func (self *VM) DisconnectVdi(vdi *VDI) error { vbds, err := self.GetVBDs() if err != nil { return fmt.Errorf("Unable to get VM VBDs: %s", err.Error()) } for _, vbd := range vbds { rec, err := vbd.GetRecord() if err != nil { return fmt.Errorf("Could not get record for VBD '%s': %s", vbd.Ref, err.Error()) } if recVdi, ok := rec["VDI"].(string); ok { if recVdi == vdi.Ref { _ = vbd.Unplug() err = vbd.Destroy() if err != nil { return fmt.Errorf("Could not destroy VBD '%s': %s", vbd.Ref, err.Error()) } return nil } } else { log.Printf("Could not find VDI record in VBD '%s'", vbd.Ref) } } return fmt.Errorf("Could not find VBD for VDI '%s'", vdi.Ref) } func (self *VM) SetPlatform(params map[string]string) (err error) { result := APIResult{} platform_rec := make(xmlrpc.Struct) for key, value := range params { platform_rec[key] = value } err = self.Client.APICall(&result, "VM.set_platform", self.Ref, platform_rec) if err != nil { return err } return } func (self *VM) ConnectNetwork(network *Network, device string) (vif *VIF, err error) { // Create the VIF vif_rec := make(xmlrpc.Struct) vif_rec["network"] = network.Ref vif_rec["VM"] = self.Ref vif_rec["MAC"] = "" vif_rec["device"] = device vif_rec["MTU"] = "1504" vif_rec["other_config"] = make(xmlrpc.Struct) vif_rec["qos_algorithm_type"] = "" vif_rec["qos_algorithm_params"] = make(xmlrpc.Struct) result := APIResult{} err = self.Client.APICall(&result, "VIF.create", vif_rec) if err != nil { return nil, err } vif = new(VIF) vif.Ref = result.Value.(string) vif.Client = self.Client return vif, nil } // Setters func (self *VM) SetIsATemplate(is_a_template bool) (err error) { result := APIResult{} err = self.Client.APICall(&result, "VM.set_is_a_template", self.Ref, is_a_template) if err != nil { return err } return } // SR associated functions func (self *SR) CreateVdi(name_label string, size int64) (vdi *VDI, err error) { vdi = new(VDI) vdi_rec := make(xmlrpc.Struct) vdi_rec["name_label"] = name_label vdi_rec["SR"] = self.Ref vdi_rec["virtual_size"] = fmt.Sprintf("%d", size) vdi_rec["type"] = "user" vdi_rec["sharable"] = false vdi_rec["read_only"] = false oc := make(xmlrpc.Struct) oc["temp"] = "temp" vdi_rec["other_config"] = oc result := APIResult{} err = self.Client.APICall(&result, "VDI.create", vdi_rec) if err != nil { return nil, err } vdi.Ref = result.Value.(string) vdi.Client = self.Client return } // Network associated functions func (self *Network) GetAssignedIPs() (ip_map map[string]string, err error) { ip_map = make(map[string]string, 0) result := APIResult{} err = self.Client.APICall(&result, "network.get_assigned_ips", self.Ref) if err != nil { return ip_map, err } for k, v := range result.Value.(xmlrpc.Struct) { ip_map[k] = v.(string) } return ip_map, nil } // PIF associated functions func (self *PIF) GetRecord() (record map[string]interface{}, err error) { record = make(map[string]interface{}) result := APIResult{} err = self.Client.APICall(&result, "PIF.get_record", self.Ref) if err != nil { return record, err } for k, v := range result.Value.(xmlrpc.Struct) { record[k] = v } return record, nil } // Pool associated functions func (self *Pool) GetRecord() (record map[string]interface{}, err error) { record = make(map[string]interface{}) result := APIResult{} err = self.Client.APICall(&result, "pool.get_record", self.Ref) if err != nil { return record, err } for k, v := range result.Value.(xmlrpc.Struct) { record[k] = v } return record, nil } // VBD associated functions func (self *VBD) GetRecord() (record map[string]interface{}, err error) { record = make(map[string]interface{}) result := APIResult{} err = self.Client.APICall(&result, "VBD.get_record", self.Ref) if err != nil { return record, err } for k, v := range result.Value.(xmlrpc.Struct) { record[k] = v } return record, nil } func (self *VBD) GetVDI() (vdi *VDI, err error) { vbd_rec, err := self.GetRecord() if err != nil { return nil, err } vdi = new(VDI) vdi.Ref = vbd_rec["VDI"].(string) vdi.Client = self.Client return vdi, nil } func (self *VBD) Eject() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VBD.eject", self.Ref) if err != nil { return err } return nil } func (self *VBD) Unplug() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VBD.unplug", self.Ref) if err != nil { return err } return nil } func (self *VBD) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VBD.destroy", self.Ref) if err != nil { return err } return nil } // VIF associated functions func (self *VIF) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VIF.destroy", self.Ref) if err != nil { return err } return nil } // VDI associated functions func (self *VDI) GetUuid() (vdi_uuid string, err error) { result := APIResult{} err = self.Client.APICall(&result, "VDI.get_uuid", self.Ref) if err != nil { return "", err } vdi_uuid = result.Value.(string) return vdi_uuid, nil } func (self *VDI) GetVBDs() (vbds []VBD, err error) { vbds = make([]VBD, 0) result := APIResult{} err = self.Client.APICall(&result, "VDI.get_VBDs", self.Ref) if err != nil { return vbds, err } for _, elem := range result.Value.([]interface{}) { vbd := VBD{} vbd.Ref = elem.(string) vbd.Client = self.Client vbds = append(vbds, vbd) } return vbds, nil } func (self *VDI) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "VDI.destroy", self.Ref) if err != nil { return err } return } // Task associated functions func (self *Task) GetStatus() (status TaskStatusType, err error) { result := APIResult{} err = self.Client.APICall(&result, "task.get_status", self.Ref) if err != nil { return } rawStatus := result.Value.(string) switch rawStatus { case "pending": status = Pending case "success": status = Success case "failure": status = Failure case "cancelling": status = Cancelling case "cancelled": status = Cancelled default: panic(fmt.Sprintf("Task.get_status: Unknown status '%s'", rawStatus)) } return } func (self *Task) GetProgress() (progress float64, err error) { result := APIResult{} err = self.Client.APICall(&result, "task.get_progress", self.Ref) if err != nil { return } progress = result.Value.(float64) return } func (self *Task) GetResult() (object *XenAPIObject, err error) { result := APIResult{} err = self.Client.APICall(&result, "task.get_result", self.Ref) if err != nil { return } switch ref := result.Value.(type) { case string: object = &XenAPIObject{ Ref: ref.(string), Client: self.Client, } case nil: object = nil default: err = fmt.Errorf("task.get_result: unknown value type %T (expected string or nil)", ref) } return } func (self *Task) GetErrorInfo() (errorInfo []string, err error) { result := APIResult{} err = self.Client.APICall(&result, "task.get_error_info", self.Ref) if err != nil { return } errorInfo = make([]string, 0) for _, infoRaw := range result.Value.([]interface{}) { errorInfo = append(errorInfo, fmt.Sprintf("%v", infoRaw)) } return } func (self *Task) Destroy() (err error) { result := APIResult{} err = self.Client.APICall(&result, "task.destroy", self.Ref) return } // Client Initiator func NewXenAPIClient(host, username, password string) (client XenAPIClient) { client.Host = host client.Url = "http://" + host client.Username = username client.Password = password client.RPC, _ = xmlrpc.NewClient(client.Url, nil) return }