func (qc *qemuCluster) NewMachine(cfg string) (Machine, error) { id := uuid.NewV4() // hacky solution for cloud config ip substitution // NOTE: escaping is not supported qc.mu.Lock() netif := qc.Dnsmasq.GetInterface("br0") ip := strings.Split(netif.DHCPv4[0].String(), "/")[0] cfg = strings.Replace(cfg, "$public_ipv4", ip, -1) cfg = strings.Replace(cfg, "$private_ipv4", ip, -1) cloudConfig, err := config.NewCloudConfig(cfg) if err != nil { qc.mu.Unlock() return nil, err } if err = qc.SSHAgent.UpdateConfig(cloudConfig); err != nil { qc.mu.Unlock() return nil, err } if cloudConfig.Hostname == "" { cloudConfig.Hostname = id.String()[:8] } qc.mu.Unlock() configDrive, err := local.NewConfigDrive(cloudConfig) if err != nil { return nil, err } qm := &qemuMachine{ qc: qc, id: id.String(), configDrive: configDrive, netif: netif, } disk, err := setupDisk(qc.conf.DiskImage) if err != nil { return nil, err } defer disk.Close() qc.mu.Lock() tap, err := qc.NewTap("br0") if err != nil { qc.mu.Unlock() return nil, err } defer tap.Close() qmMac := qm.netif.HardwareAddr.String() qmCfg := qm.configDrive.Directory qm.qemu = qm.qc.NewCommand( "qemu-system-x86_64", "-machine", "accel=kvm", "-cpu", "host", "-smp", "2", "-m", "1024", "-uuid", qm.id, "-display", "none", "-add-fd", "fd=3,set=1", "-drive", "file=/dev/fdset/1,media=disk,if=virtio,format=raw", "-netdev", "tap,id=tap,fd=4", "-device", "virtio-net,netdev=tap,mac="+qmMac, "-fsdev", "local,id=cfg,security_model=none,readonly,path="+qmCfg, "-device", "virtio-9p-pci,fsdev=cfg,mount_tag=config-2") qc.mu.Unlock() cmd := qm.qemu.(*local.NsCmd) cmd.Stderr = os.Stderr cmd.ExtraFiles = append(cmd.ExtraFiles, disk) // fd=3 cmd.ExtraFiles = append(cmd.ExtraFiles, tap.File) // fd=4 if err = qm.qemu.Start(); err != nil { return nil, err } // Allow a few authentication failures in case setup is slow. sshchecker := func() error { qm.qc.mu.Lock() defer qm.qc.mu.Unlock() qm.sshClient, err = qm.qc.SSHAgent.NewClient(qm.IP()) if err != nil { return err } return nil } if err := util.Retry(sshRetries, sshTimeout, sshchecker); err != nil { return nil, err } if err != nil { qm.Destroy() return nil, err } out, err := qm.SSH("grep ^ID= /etc/os-release") if err != nil { qm.Destroy() return nil, err } if !bytes.Equal(out, []byte("ID=coreos")) { qm.Destroy() return nil, fmt.Errorf("Unexpected SSH output: %s", out) } qc.mu.Lock() qc.machines[qm.ID()] = qm qc.mu.Unlock() return Machine(qm), nil }
func (qc *QEMUCluster) NewMachine(cfg string) (Machine, error) { id := uuid.NewV4() // hacky solution for cloud config ip substitution // NOTE: escaping is not supported qc.mu.Lock() netif := qc.Dnsmasq.GetInterface("br0") ip := strings.Split(netif.DHCPv4[0].String(), "/")[0] cfg = strings.Replace(cfg, "$public_ipv4", ip, -1) cfg = strings.Replace(cfg, "$private_ipv4", ip, -1) conf, err := NewConf(cfg) if err != nil { qc.mu.Unlock() return nil, err } keys, err := qc.SSHAgent.List() if err != nil { qc.mu.Unlock() return nil, err } conf.CopyKeys(keys) qc.mu.Unlock() configDrive, err := local.NewConfigDrive(conf.String()) if err != nil { return nil, err } qm := &qemuMachine{ qc: qc, id: id.String(), configDrive: configDrive, netif: netif, } disk, err := setupDisk(qc.conf.DiskImage) if err != nil { return nil, err } defer disk.Close() qc.mu.Lock() tap, err := qc.NewTap("br0") if err != nil { qc.mu.Unlock() return nil, err } defer tap.Close() qmMac := qm.netif.HardwareAddr.String() qmCfg := qm.configDrive.Directory qm.qemu = qm.qc.NewCommand( "qemu-system-x86_64", "-machine", "accel=kvm", "-cpu", "host", "-smp", "2", "-m", "1024", "-uuid", qm.id, "-display", "none", "-add-fd", "fd=3,set=1", "-drive", "file=/dev/fdset/1,media=disk,if=virtio,format=raw", "-netdev", "tap,id=tap,fd=4", "-device", "virtio-net,netdev=tap,mac="+qmMac, "-fsdev", "local,id=cfg,security_model=none,readonly,path="+qmCfg, "-device", "virtio-9p-pci,fsdev=cfg,mount_tag=config-2") qc.mu.Unlock() cmd := qm.qemu.(*local.NsCmd) cmd.Stderr = os.Stderr cmd.ExtraFiles = append(cmd.ExtraFiles, disk) // fd=3 cmd.ExtraFiles = append(cmd.ExtraFiles, tap.File) // fd=4 if err = qm.qemu.Start(); err != nil { return nil, err } if err := commonMachineChecks(qm); err != nil { qm.Destroy() return nil, err } qc.addMach(qm) return Machine(qm), nil }