func (vm *KvmVM) connectQMP() (err error) { delay := QMP_CONNECT_DELAY * time.Millisecond for count := 0; count < QMP_CONNECT_RETRY; count++ { vm.q, err = qmp.Dial(vm.path("qmp")) if err == nil { log.Debug("qmp dial to %v successful", vm.ID) return } log.Info("qmp dial to %v : %v, redialing in %v", vm.ID, err, delay) time.Sleep(delay) } // Never connected successfully return fmt.Errorf("vm %v failed to connect to qmp: %v", vm.ID, err) }
func (vm *KvmVM) launch(ack chan int) (err error) { log.Info("launching vm: %v", vm.ID) // Update the state after the lock has been released defer func() { if err != nil { log.Errorln(err) vm.setState(VM_ERROR) // Only ACK for failures since, on success, launch may block ack <- vm.ID } else { vm.setState(VM_BUILDING) } }() vm.lock.Lock() defer vm.lock.Unlock() // If this is the first time launching the VM, do the final configuration // check and create a directory for it. if vm.State != VM_QUIT { if err := os.MkdirAll(vm.instancePath, os.FileMode(0700)); err != nil { teardownf("unable to create VM dir: %v", err) } // Check the disks and network interfaces are sane err = vm.checkInterfaces() if err == nil { err = vm.checkDisks() } if err != nil { return } } // write the config for this vm writeOrDie(filepath.Join(vm.instancePath, "config"), vm.Config().String()) writeOrDie(filepath.Join(vm.instancePath, "name"), vm.Name) var args []string var sOut bytes.Buffer var sErr bytes.Buffer var cmd *exec.Cmd var waitChan = make(chan int) // create and add taps if we are associated with any networks for i := range vm.Networks { net := &vm.Networks[i] log.Info("%#v", net) b, err := getBridge(net.Bridge) if err != nil { return err } net.Tap, err = b.TapCreate(net.Tap, net.VLAN, false) if err != nil { return err } updates := make(chan ipmac.IP) go func(vm *KvmVM, net *NetConfig) { defer close(updates) for { // TODO: need to acquire VM lock? select { case update := <-updates: if update.IP4 != "" { net.IP4 = update.IP4 } else if update.IP6 != "" && !strings.HasPrefix(update.IP6, "fe80") { net.IP6 = update.IP6 } case <-vm.kill: b.iml.DelMac(net.MAC) return } } }(vm, net) b.iml.AddMac(net.MAC, updates) } if len(vm.Networks) > 0 { taps := []string{} for _, net := range vm.Networks { taps = append(taps, net.Tap) } err := ioutil.WriteFile(filepath.Join(vm.instancePath, "taps"), []byte(strings.Join(taps, "\n")), 0666) if err != nil { return fmt.Errorf("write instance taps file: %v", err) } } vmConfig := VMConfig{BaseConfig: vm.BaseConfig, KVMConfig: vm.KVMConfig} args = vmConfig.qemuArgs(vm.ID, vm.instancePath) args = ParseQemuOverrides(args) log.Debug("final qemu args: %#v", args) cmd = &exec.Cmd{ Path: process("qemu"), Args: args, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } err = cmd.Start() if err != nil { return fmt.Errorf("start qemu: %v %v", err, sErr.String()) } vm.pid = cmd.Process.Pid log.Debug("vm %v has pid %v", vm.ID, vm.pid) vm.CheckAffinity() go func() { err := cmd.Wait() vm.setState(VM_QUIT) if err != nil { if err.Error() != "signal: killed" { // because we killed it log.Error("kill qemu: %v %v", err, sErr.String()) vm.setState(VM_ERROR) } } waitChan <- vm.ID }() // we can't just return on error at this point because we'll leave dangling goroutines, we have to clean up on failure sendKillAck := false // connect to qmp connected := false for count := 0; count < QMP_CONNECT_RETRY; count++ { vm.q, err = qmp.Dial(vm.qmpPath()) if err == nil { connected = true break } delay := QMP_CONNECT_DELAY * time.Millisecond log.Info("qmp dial to %v : %v, redialing in %v", vm.ID, err, delay) time.Sleep(delay) } if !connected { cmd.Process.Kill() return fmt.Errorf("vm %v failed to connect to qmp: %v", vm.ID, err) } log.Debug("qmp dial to %v successful", vm.ID) go vm.asyncLogger() ack <- vm.ID // connect cc ccPath := filepath.Join(vm.instancePath, "cc") err = ccNode.DialSerial(ccPath) if err != nil { log.Errorln(err) } go func() { select { case <-waitChan: log.Info("VM %v exited", vm.ID) case <-vm.kill: log.Info("Killing VM %v", vm.ID) cmd.Process.Kill() <-waitChan sendKillAck = true // wait to ack until we've cleaned up } if sendKillAck { killAck <- vm.ID } }() return nil }
func (vm *vmInfo) launchOne(ack chan int) { log.Info("launching vm: %v", vm.ID) s := vm.getState() // don't repeat the preamble if we're just in the quit state if s != VM_QUIT && !vm.launchPreamble(ack) { return } vm.state(VM_BUILDING) // write the config for this vm config := vm.configToString() err := ioutil.WriteFile(vm.instancePath+"config", []byte(config), 0664) if err != nil { log.Errorln(err) teardown() } err = ioutil.WriteFile(vm.instancePath+"name", []byte(vm.Name), 0664) if err != nil { log.Errorln(err) teardown() } var args []string var sOut bytes.Buffer var sErr bytes.Buffer var cmd *exec.Cmd var waitChan = make(chan int) // clear taps, we may have come from the quit state vm.Taps = []string{} // create and add taps if we are associated with any networks for i, lan := range vm.Networks { b, err := getBridge(vm.Bridges[i]) if err != nil { log.Error("get bridge: %v", err) vm.state(VM_ERROR) ack <- vm.ID return } tap, err := b.TapCreate(lan) if err != nil { log.Error("create tap: %v", err) vm.state(VM_ERROR) ack <- vm.ID return } vm.Taps = append(vm.Taps, tap) } if len(vm.Networks) > 0 { err := ioutil.WriteFile(vm.instancePath+"taps", []byte(strings.Join(vm.Taps, "\n")), 0666) if err != nil { log.Error("write instance taps file: %v", err) vm.state(VM_ERROR) ack <- vm.ID return } } args = vm.vmGetArgs(true) args = ParseQemuOverrides(args) log.Debug("final qemu args: %#v", args) cmd = &exec.Cmd{ Path: process("qemu"), Args: args, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } err = cmd.Start() if err != nil { log.Error("start qemu: %v %v", err, sErr.String()) vm.state(VM_ERROR) ack <- vm.ID return } vm.pid = cmd.Process.Pid log.Debug("vm %v has pid %v", vm.ID, vm.pid) vm.CheckAffinity() go func() { err := cmd.Wait() vm.state(VM_QUIT) if err != nil { if err.Error() != "signal: killed" { // because we killed it log.Error("kill qemu: %v %v", err, sErr.String()) vm.state(VM_ERROR) } } waitChan <- vm.ID }() // we can't just return on error at this point because we'll leave dangling goroutines, we have to clean up on failure sendKillAck := false // connect to qmp connected := false for count := 0; count < QMP_CONNECT_RETRY; count++ { vm.q, err = qmp.Dial(vm.qmpPath()) if err == nil { connected = true break } time.Sleep(QMP_CONNECT_DELAY * time.Millisecond) } if !connected { log.Error("vm %v failed to connect to qmp: %v", vm.ID, err) vm.state(VM_ERROR) cmd.Process.Kill() <-waitChan ack <- vm.ID } else { go vm.asyncLogger() ack <- vm.ID select { case <-waitChan: log.Info("VM %v exited", vm.ID) case <-vm.kill: log.Info("Killing VM %v", vm.ID) cmd.Process.Kill() <-waitChan sendKillAck = true // wait to ack until we've cleaned up } } for i, l := range vm.Networks { b, err := getBridge(vm.Bridges[i]) if err != nil { log.Error("get bridge: %v", err) } else { b.TapDestroy(l, vm.Taps[i]) } } if sendKillAck { killAck <- vm.ID } }
func (vm *vmKVM) launch(ack chan int) { log.Info("launching vm: %v", vm.id) s := vm.State() // don't repeat the preamble if we're just in the quit state if s != VM_QUIT && !vm.launchPreamble(ack) { return } vm.setState(VM_BUILDING) // write the config for this vm config := vm.String() err := ioutil.WriteFile(vm.instancePath+"config", []byte(config), 0664) if err != nil { log.Errorln(err) teardown() } err = ioutil.WriteFile(vm.instancePath+"name", []byte(vm.name), 0664) if err != nil { log.Errorln(err) teardown() } var args []string var sOut bytes.Buffer var sErr bytes.Buffer var cmd *exec.Cmd var waitChan = make(chan int) // clear taps, we may have come from the quit state for i := range vm.Networks { vm.Networks[i].Tap = "" } // create and add taps if we are associated with any networks for i := range vm.Networks { net := &vm.Networks[i] b, err := getBridge(net.Bridge) if err != nil { log.Error("get bridge: %v", err) vm.setState(VM_ERROR) ack <- vm.id return } net.Tap, err = b.TapCreate(net.VLAN) if err != nil { log.Error("create tap: %v", err) vm.setState(VM_ERROR) ack <- vm.id return } updates := make(chan ipmac.IP) go func(vm *vmKVM, net *NetConfig) { defer close(updates) for { // TODO: need to acquire VM lock? select { case update := <-updates: if update.IP4 != "" { net.IP4 = update.IP4 } else if net.IP6 != "" && strings.HasPrefix(update.IP6, "fe80") { log.Debugln("ignoring link-local over existing IPv6 address") } else if update.IP6 != "" { net.IP6 = update.IP6 } case <-vm.kill: b.iml.DelMac(net.MAC) return } } }(vm, net) b.iml.AddMac(net.MAC, updates) } if len(vm.Networks) > 0 { taps := []string{} for _, net := range vm.Networks { taps = append(taps, net.Tap) } err := ioutil.WriteFile(vm.instancePath+"taps", []byte(strings.Join(taps, "\n")), 0666) if err != nil { log.Error("write instance taps file: %v", err) vm.setState(VM_ERROR) ack <- vm.id return } } args = VMConfig{vm.BaseConfig, vm.KVMConfig}.qemuArgs(vm.ID(), vm.instancePath) args = ParseQemuOverrides(args) log.Debug("final qemu args: %#v", args) cmd = &exec.Cmd{ Path: process("qemu"), Args: args, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } err = cmd.Start() if err != nil { log.Error("start qemu: %v %v", err, sErr.String()) vm.setState(VM_ERROR) ack <- vm.id return } vm.pid = cmd.Process.Pid log.Debug("vm %v has pid %v", vm.id, vm.pid) vm.CheckAffinity() go func() { err := cmd.Wait() vm.setState(VM_QUIT) if err != nil { if err.Error() != "signal: killed" { // because we killed it log.Error("kill qemu: %v %v", err, sErr.String()) vm.setState(VM_ERROR) } } waitChan <- vm.id }() // we can't just return on error at this point because we'll leave dangling goroutines, we have to clean up on failure sendKillAck := false // connect to qmp connected := false for count := 0; count < QMP_CONNECT_RETRY; count++ { vm.q, err = qmp.Dial(vm.qmpPath()) if err == nil { connected = true break } delay := QMP_CONNECT_DELAY * time.Millisecond log.Info("qmp dial to %v : %v, redialing in %v", vm.ID(), err, delay) time.Sleep(delay) } if !connected { log.Error("vm %v failed to connect to qmp: %v", vm.id, err) vm.setState(VM_ERROR) cmd.Process.Kill() <-waitChan ack <- vm.id } else { log.Debug("qmp dial to %v successful", vm.ID()) go vm.asyncLogger() ack <- vm.id select { case <-waitChan: log.Info("VM %v exited", vm.id) case <-vm.kill: log.Info("Killing VM %v", vm.id) cmd.Process.Kill() <-waitChan sendKillAck = true // wait to ack until we've cleaned up } } for _, net := range vm.Networks { b, err := getBridge(net.Bridge) if err != nil { log.Error("get bridge: %v", err) } else { b.TapDestroy(net.VLAN, net.Tap) } } if sendKillAck { killAck <- vm.id } }