Пример #1
0
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)
}
Пример #2
0
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
}
Пример #3
0
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
	}
}
Пример #4
0
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
	}
}