func SendLocalToRemote(stream libvirt.VirStream, volume libvirt.VirStorageVol, data []byte) error { err := libvirt.StorageVolUpload(volume, stream, 0, uint64(len(data))) if err != nil { return err } //transfter volume remain := len(data) sent := 0 offset := 0 DATALEN := 16384 for remain > 0 { if remain > DATALEN { sent = stream.Send(data[offset:], DATALEN) } else { sent = stream.Send(data[offset:], remain) } if sent < 0 { stream.Abort() return errors.New("Stream Send return 0") } if sent == 0 { break } remain -= sent offset += sent } err = stream.Finish() if err != nil { return err } return nil }
func VmInstall(conn libvirt.VirConnection, vmname string, url string, autoyast string, imageSize uint64, ch chan string) { //check input if len(vmname) <= 0 { reportFail(ch, "Name too short") return } if imageSize == 0 { reportFail(ch, "disk size too short") } //sender close the channel if ch != nil { defer close(ch) } pool, err := createRemoteBootPool(conn) defer pool.Free() //url := "http://mirror.bej.suse.com/dist/install/SLP/SLE-12-Server-Beta10/x86_64/DVD1" linuxSurfix := "/boot/x86_64/loader/linux" initrdSurfix := "/boot/x86_64/loader/initrd" // Download linux and initrd image from remote reportStatus(ch, "Downloading linux image") m := DownloadManager{} m.Regsiter(HTTPDownloader{}) linuxContent, err := m.Download(url + linuxSurfix) if err != nil { reportFail(ch, err.Error()) return } reportStatus(ch, "Downloading initrd image") initrdContent, err := m.Download(url + initrdSurfix) if err != nil { reportFail(ch, err.Error()) return } //prepare two temporary name for linux;initrd image temp := generateFourRandom() bootImageName := vmname + temp + ".image" bootInitrdName := vmname + temp + ".initrd" // create remote boot linux storage from temp pool linuxVolume, err := createVolume(pool, Storage{Name: bootImageName, Size: uint64(len(linuxContent)), Type: "raw"}) if err != nil { reportFail(ch, err.Error()) return } defer linuxVolume.Free() defer linuxVolume.Delete() linuxPath, _ := linuxVolume.GetPath() // create remote boot initrd storage from temp pool initrdVolume, err := createVolume(pool, Storage{Name: bootInitrdName, Size: uint64(len(initrdContent)), Type: "raw"}) if err != nil { reportFail(ch, err.Error()) return } defer initrdVolume.Free() defer initrdVolume.Delete() initrdPath, _ := initrdVolume.GetPath() var stream libvirt.VirStream stream, err = conn.StreamNew() if err != nil { reportFail(ch, err.Error()) return } defer stream.Free() //Upload to remote reportStatus(ch, "sending linuxVolume") if err := SendLocalToRemote(stream, linuxVolume, linuxContent); err != nil { reportFail(ch, err.Error()) return } reportStatus(ch, "sending initrd") if err := SendLocalToRemote(stream, initrdVolume, initrdContent); err != nil { reportFail(ch, err.Error()) return } // create image reportStatus(ch, "creating remote imaging...") dataPool, err := conn.StoragePoolLookupByName("default") if err != nil { reportFail(ch, err.Error()) return } defer dataPool.Free() //var imageSize uint64 = 8589934592 //8G realImageName := vmname + ".img" imageVolume, err := createVolume(dataPool, Storage{Name: realImageName, Size: imageSize, Type: "qcow2"}) if err != nil { reportFail(ch, err.Error()) return } defer imageVolume.Free() imagePath, _ := imageVolume.GetPath() log.Println("Create remote VirtualMachine") reportStatus(ch, "Create remote VirtualMachine") // create boot xml var xml string // add autoyast var installArg string if len(autoyast) > 0 { installArg = url + " autoyast=" + autoyast } else { installArg = url } domain := Domain{Name: vmname, Kernel: linuxPath, Initrd: initrdPath, Image: imagePath, Install: installArg} if xml, err = domain.Encode(); err != nil { reportFail(ch, err.Error()) return } // create booting vm bootingDomain, err := conn.CreateXML(xml) if err != nil { reportFail(ch, err.Error()) return } defer bootingDomain.Free() // get xml from remote // create new defined xml if xml, err = bootingDomain.GetXMLDesc(); err != nil { reportFail(ch, err.Error()) return } /* change xml a bit using regex lines, I do not want to parse the xml file * 1. change os section to boot from hd * 2. change destory section */ /* (?s) is used to let . match newline(\n) */ osSection := regexp.MustCompile("(?s)<os>.*</os>") onBoot := regexp.MustCompile("(?s)<on_reboot>.*</on_reboot>") onCrash := regexp.MustCompile("(?s)<on_crash>.*</on_crash>") xml = osSection.ReplaceAllString(xml, OSSECTION) xml = onBoot.ReplaceAllString(xml, ONBOOT) xml = onCrash.ReplaceAllString(xml, ONCRASH) newPersistentDomain, err := conn.DefineXML(xml) if err != nil { reportFail(ch, err.Error()) return } log.Println(newPersistentDomain) defer newPersistentDomain.Free() reportSuccess(ch) }