Beispiel #1
0
func UploadRPM(r *util.Repo, hypervisor string, image string, config *util.Config, verbose bool) error {
	file := r.ImagePath(hypervisor, image)
	vmconfig := &VMConfig{
		Image:       file,
		Verbose:     verbose,
		Memory:      64,
		Networking:  "nat",
		NatRules:    []nat.Rule{nat.Rule{GuestPort: "10000", HostPort: "10000"}},
		BackingFile: false,
	}
	qemu, err := LaunchVM(vmconfig)
	if err != nil {
		return err
	}
	defer qemu.Process.Kill()

	conn, err := util.ConnectAndWait("tcp", "localhost:10000")
	if err != nil {
		return err
	}

	cmd := exec.Command("rpm2cpio", config.RpmBase.Filename())
	cmd.Stdout = conn
	err = cmd.Start()
	if err != nil {
		return err
	}
	defer cmd.Wait()

	err = qemu.Wait()

	conn.Close()

	return err
}
Beispiel #2
0
func checkConfig(t *core.Template, r *util.Repo, hypervisor string) error {
	if _, err := os.Stat(r.ImagePath(hypervisor, t.Base)); os.IsNotExist(err) {
		if err := Pull(r, hypervisor, t.Base); err != nil {
			return err
		}
	}
	for _, value := range t.Files {
		if _, err := os.Stat(value); os.IsNotExist(err) {
			return errors.New(fmt.Sprintf("%s: no such file or directory", value))
		}
	}
	return nil
}
Beispiel #3
0
func SetArgs(r *util.Repo, hypervisor, image string, args string) error {
	file := r.ImagePath(hypervisor, image)
	cmd := exec.Command("qemu-nbd", file)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	err = cmd.Start()
	if err != nil {
		return err
	}
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)

	conn, err := util.ConnectAndWait("tcp", "localhost:10809")
	if err != nil {
		return err
	}

	session := &nbd.NbdSession{
		Conn:   conn,
		Handle: 0,
	}
	if err := session.Handshake(); err != nil {
		return err
	}

	padding := 512 - (len(args) % 512)

	data := append([]byte(args), make([]byte, padding)...)

	if err := session.Write(512, data); err != nil {
		return err
	}
	if err := session.Flush(); err != nil {
		return err
	}
	if err := session.Disconnect(); err != nil {
		return err
	}
	conn.Close()
	cmd.Wait()

	return nil
}
Beispiel #4
0
func UploadFiles(r *util.Repo, hypervisor string, image string, config *util.Config, verbose bool) error {
	file := r.ImagePath(hypervisor, image)
	vmconfig := &VMConfig{
		Image:       file,
		Verbose:     verbose,
		Memory:      64,
		Networking:  "nat",
		NatRules:    []nat.Rule{nat.Rule{GuestPort: "10000", HostPort: "10000"}},
		BackingFile: false,
	}
	cmd, err := LaunchVM(vmconfig)
	if err != nil {
		return err
	}
	defer cmd.Process.Kill()

	conn, err := util.ConnectAndWait("tcp", "localhost:10000")
	if err != nil {
		return err
	}

	if _, err = os.Stat(config.Rootfs); !os.IsNotExist(err) {
		err = filepath.Walk(config.Rootfs, func(src string, info os.FileInfo, _ error) error {
			if info.IsDir() {
				return nil
			}
			dst := strings.Replace(src, config.Rootfs, "", -1)
			if verbose {
				fmt.Println(src + "  --> " + dst)
			}
			return copyFile(conn, src, dst)
		})
	}

	for dst, src := range config.Files {
		err = copyFile(conn, src, dst)
		if verbose {
			fmt.Println(src + "  --> " + dst)
		}
		if err != nil {
			return err
		}
	}

	cpio.WritePadded(conn, cpio.ToWireFormat("TRAILER!!!", 0, 0))

	conn.Close()
	return cmd.Wait()
}
Beispiel #5
0
func UploadRPM(r *util.Repo, hypervisor string, image string, template *core.Template, verbose bool, mem string) error {
	file := r.ImagePath(hypervisor, image)
	size, err := util.ParseMemSize(mem)
	if err != nil {
		return err
	}
	vmconfig := &qemu.VMConfig{
		Image:       file,
		Verbose:     verbose,
		Memory:      size,
		Networking:  "nat",
		NatRules:    []nat.Rule{nat.Rule{GuestPort: "10000", HostPort: "10000"}},
		BackingFile: false,
	}
	vm, err := qemu.LaunchVM(vmconfig)
	if err != nil {
		return err
	}
	defer vm.Process.Kill()

	conn, err := util.ConnectAndWait("tcp", "localhost:10000")
	if err != nil {
		return err
	}

	cmd := exec.Command("rpm2cpio", template.RpmBase.Filename())
	cmd.Stdout = conn
	err = cmd.Start()
	if err != nil {
		return err
	}
	defer cmd.Wait()

	err = vm.Wait()

	conn.Close()

	return err
}
Beispiel #6
0
func UploadFiles(r *util.Repo, hypervisor string, image string, config *util.Config, verbose bool) error {
	file := r.ImagePath(hypervisor, image)
	vmconfig := &VMConfig{
		Image:       file,
		Verbose:     verbose,
		Memory:      64,
		NatRules:    []nat.Rule{nat.Rule{GuestPort: "10000", HostPort: "10000"}},
		BackingFile: false,
	}
	cmd, err := LaunchVM(vmconfig)
	if err != nil {
		return err
	}
	defer cmd.Process.Kill()

	time.Sleep(1 * time.Second)

	conn, err := net.Dial("tcp", "localhost:10000")
	if err != nil {
		return err
	}

	for key, value := range config.Files {
		fi, err := os.Stat(value)
		if err != nil {
			return err
		}
		cpio.WritePadded(conn, cpio.ToWireFormat(key, cpio.C_ISREG, fi.Size()))
		b, err := ioutil.ReadFile(value)
		cpio.WritePadded(conn, b)
	}

	cpio.WritePadded(conn, cpio.ToWireFormat("TRAILER!!!", 0, 0))

	conn.Close()
	return cmd.Wait()
}
Beispiel #7
0
func Build(r *util.Repo, hypervisor string, image string, verbose bool) error {
	config, err := util.ReadConfig("Capstanfile")
	if err != nil {
		return err
	}
	fmt.Printf("Building %s...\n", image)
	err = os.MkdirAll(filepath.Dir(r.ImagePath(hypervisor, image)), 0777)
	if err != nil {
		return err
	}
	if config.RpmBase != nil {
		config.RpmBase.Download()
	}
	if config.Build != "" {
		args := strings.Fields(config.Build)
		cmd := exec.Command(args[0], args[1:]...)
		out, err := cmd.CombinedOutput()
		if err != nil {
			fmt.Println(string(out))
			return err
		}
	}
	err = checkConfig(config, r, hypervisor)
	if err != nil {
		return err
	}
	cmd := exec.Command("cp", r.ImagePath(hypervisor, config.Base), r.ImagePath(hypervisor, image))
	_, err = cmd.Output()
	if err != nil {
		return err
	}
	err = qemu.SetArgs(r, hypervisor, image, "/tools/cpiod.so")
	if err != nil {
		return err
	}
	if config.RpmBase != nil {
		err = qemu.UploadRPM(r, hypervisor, image, config, verbose)
		if err != nil {
			return err
		}
	}
	err = qemu.UploadFiles(r, hypervisor, image, config, verbose)
	if err != nil {
		return err
	}
	err = qemu.SetArgs(r, hypervisor, image, config.Cmdline)
	if err != nil {
		return err
	}
	return nil
}
Beispiel #8
0
func Build(r *util.Repo, image *core.Image, template *core.Template, verbose bool, mem string) error {
	if err := os.MkdirAll(filepath.Dir(r.ImagePath(image.Hypervisor, image.Name)), 0777); err != nil {
		return err
	}
	fmt.Printf("Building %s...\n", image.Name)
	if template.Build != "" {
		args := strings.Fields(template.Build)
		cmd := exec.Command(args[0], args[1:]...)
		out, err := cmd.CombinedOutput()
		if err != nil {
			fmt.Println(string(out))
			return err
		}
	}
	if err := checkConfig(template, r, image.Hypervisor); err != nil {
		return err
	}
	if template.RpmBase != nil {
		template.RpmBase.Download()
	}
	cmd := util.CopyFile(r.ImagePath(image.Hypervisor, template.Base), r.ImagePath(image.Hypervisor, image.Name))
	_, err := cmd.Output()
	if err != nil {
		return err
	}
	cmdline := "/tools/cpiod.so"
	if verbose {
		cmdline = "--verbose" + cmdline
	}
	if err := SetArgs(r, image.Hypervisor, image.Name, "/tools/cpiod.so"); err != nil {
		return err
	}
	if template.RpmBase != nil {
		if err := UploadRPM(r, image.Hypervisor, image.Name, template, verbose, mem); err != nil {
			return err
		}
	}
	if err := UploadFiles(r, image.Hypervisor, image.Name, template, verbose, mem); err != nil {
		return err
	}
	return SetArgs(r, image.Hypervisor, image.Name, template.Cmdline)
}
Beispiel #9
0
func Run(repo *util.Repo, config *RunConfig) error {
	var path string
	var cmd *exec.Cmd

	// Start an existing instance
	if config.ImageName == "" && config.InstanceName != "" {
		instanceName, instancePlatform := util.SearchInstance(config.InstanceName)
		if instanceName != "" {
			defer fmt.Println("")

			fmt.Printf("Created instance: %s\n", instanceName)
			// Do not set RawTerm for gce
			if instancePlatform != "gce" {
				util.RawTerm()
				defer util.ResetTerm()
			}

			var err error
			switch instancePlatform {
			case "qemu":
				c, _ := qemu.LoadConfig(instanceName)
				cmd, err = qemu.LaunchVM(c)
			case "vbox":
				c, _ := vbox.LoadConfig(instanceName)
				cmd, err = vbox.LaunchVM(c)
			case "vmw":
				c, _ := vmw.LoadConfig(instanceName)
				cmd, err = vmw.LaunchVM(c)
			case "gce":
				c, _ := gce.LoadConfig(instanceName)
				cmd, err = gce.LaunchVM(c)
			}

			if err != nil {
				return err
			}
			if cmd != nil {
				return cmd.Wait()
			}
			return nil
		} else {
			// The InstanceName is actually a ImageName
			// so, cmd like "capstan run cloudius/osv" will work
			config.ImageName = config.InstanceName
			config.InstanceName = strings.Replace(config.InstanceName, "/", "-", -1)
			return Run(repo, config)
		}
		// Both ImageName and InstanceName are specified
	} else if config.ImageName != "" && config.InstanceName != "" {
		if _, err := os.Stat(config.ImageName); os.IsNotExist(err) {
			if repo.ImageExists(config.Hypervisor, config.ImageName) {
				path = repo.ImagePath(config.Hypervisor, config.ImageName)
			} else if image.IsCloudImage(config.ImageName) {
				path = config.ImageName
			} else if util.IsRemoteImage(config.ImageName) {
				err := Pull(repo, config.Hypervisor, config.ImageName)
				if err != nil {
					return err
				}
				path = repo.ImagePath(config.Hypervisor, config.ImageName)
			} else {
				return fmt.Errorf("%s: no such image", config.ImageName)
			}
			if config.Hypervisor == "gce" && !image.IsCloudImage(config.ImageName) {
				str, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				path = string(str)
			}
		} else {
			path = config.ImageName
		}
		deleteInstance(config.InstanceName)

		// Valid only when Capstanfile is present
	} else if config.ImageName == "" && config.InstanceName == "" {
		config.ImageName = repo.DefaultImage()
		config.InstanceName = config.ImageName
		if config.ImageName == "" {
			return fmt.Errorf("No Capstanfile found, unable to run.")
		}
		if !repo.ImageExists(config.Hypervisor, config.ImageName) {
			if !util.ConfigExists("Capstanfile") {
				return fmt.Errorf("%s: no such image", config.ImageName)
			}
			err := Build(repo, config.Hypervisor, config.ImageName, config.Verbose)
			if err != nil {
				return err
			}
		}
		path = repo.ImagePath(config.Hypervisor, config.ImageName)
		deleteInstance(config.InstanceName)

		// Cmdline option is not valid
	} else {
		usage()
		return nil
	}

	format, err := image.Probe(path)
	if err != nil {
		return err
	}
	if format == image.Unknown {
		return fmt.Errorf("%s: image format not recognized, unable to run it.", path)
	}
	size, err := util.ParseMemSize(config.Memory)
	if err != nil {
		return err
	}
	defer fmt.Println("")

	id := config.InstanceName
	fmt.Printf("Created instance: %s\n", id)
	// Do not set RawTerm for gce
	if config.Hypervisor != "gce" {
		util.RawTerm()
		defer util.ResetTerm()
	}

	switch config.Hypervisor {
	case "qemu":
		dir := filepath.Join(os.Getenv("HOME"), ".capstan/instances/qemu", id)
		bridge := config.Bridge
		if bridge == "" {
			bridge = "virbr0"
		}
		config := &qemu.VMConfig{
			Name:        id,
			Image:       path,
			Verbose:     true,
			Memory:      size,
			Cpus:        config.Cpus,
			Networking:  config.Networking,
			Bridge:      bridge,
			NatRules:    config.NatRules,
			BackingFile: true,
			InstanceDir: dir,
			Monitor:     filepath.Join(dir, "osv.monitor"),
			ConfigFile:  filepath.Join(dir, "osv.config"),
		}
		cmd, err = qemu.LaunchVM(config)
	case "vbox":
		if format != image.VDI && format != image.VMDK {
			return fmt.Errorf("%s: image format of %s is not supported, unable to run it.", config.Hypervisor, path)
		}
		dir := filepath.Join(util.HomePath(), ".capstan/instances/vbox", id)
		bridge := config.Bridge
		if bridge == "" {
			bridge = "vboxnet0"
		}
		config := &vbox.VMConfig{
			Name:       id,
			Dir:        filepath.Join(util.HomePath(), ".capstan/instances/vbox"),
			Image:      path,
			Memory:     size,
			Cpus:       config.Cpus,
			Networking: config.Networking,
			Bridge:     bridge,
			NatRules:   config.NatRules,
			ConfigFile: filepath.Join(dir, "osv.config"),
		}
		cmd, err = vbox.LaunchVM(config)
	case "gce":
		if format != image.GCE_TARBALL && format != image.GCE_GS {
			return fmt.Errorf("%s: image format of %s is not supported, unable to run it.", config.Hypervisor, path)
		}
		dir := filepath.Join(util.HomePath(), ".capstan/instances/gce", id)
		c := &gce.VMConfig{
			Name:        id,
			Image:       id,
			Network:     "default",
			MachineType: "n1-standard-1",
			Zone:        "us-central1-a",
			ConfigFile:  filepath.Join(dir, "osv.config"),
			InstanceDir: dir,
		}
		if format == image.GCE_TARBALL {
			c.CloudStoragePath = strings.TrimSuffix(config.GCEUploadDir, "/") + "/" + id + ".tar.gz"
			c.Tarball = path
		} else {
			c.CloudStoragePath = path
			c.Tarball = ""
		}
		cmd, err = gce.LaunchVM(c)
	case "vmw":
		if format != image.VMDK {
			return fmt.Errorf("%s: image format of %s is not supported, unable to run it.", config.Hypervisor, path)
		}
		dir := filepath.Join(util.HomePath(), ".capstan/instances/vmw", id)
		config := &vmw.VMConfig{
			Name:         id,
			Dir:          dir,
			Image:        filepath.Join(dir, "osv.vmdk"),
			Memory:       size,
			Cpus:         config.Cpus,
			NatRules:     config.NatRules,
			VMXFile:      filepath.Join(dir, "osv.vmx"),
			InstanceDir:  dir,
			OriginalVMDK: path,
			ConfigFile:   filepath.Join(dir, "osv.config"),
		}
		cmd, err = vmw.LaunchVM(config)
	default:
		err = fmt.Errorf("%s: is not a supported hypervisor", config.Hypervisor)
	}
	if err != nil {
		return err
	}
	if cmd != nil {
		return cmd.Wait()
	} else {
		return nil
	}
}
Beispiel #10
0
func UploadFiles(r *util.Repo, hypervisor string, image string, t *core.Template, verbose bool, mem string) error {
	file := r.ImagePath(hypervisor, image)
	size, err := util.ParseMemSize(mem)
	if err != nil {
		return err
	}
	vmconfig := &qemu.VMConfig{
		Image:       file,
		Verbose:     verbose,
		Memory:      size,
		Networking:  "nat",
		NatRules:    []nat.Rule{nat.Rule{GuestPort: "10000", HostPort: "10000"}},
		BackingFile: false,
	}
	cmd, err := qemu.VMCommand(vmconfig)
	if err != nil {
		return err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	defer cmd.Process.Kill()
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		text := scanner.Text()
		if verbose {
			fmt.Println(text)
		}
		if text == "Waiting for connection from host..." {
			break
		}
	}
	if verbose {
		go io.Copy(os.Stdout, stdout)
	}
	conn, err := util.ConnectAndWait("tcp", "localhost:10000")
	if err != nil {
		return err
	}

	rootfsFiles := make(map[string]string)
	if _, err = os.Stat(t.Rootfs); !os.IsNotExist(err) {
		err = filepath.Walk(t.Rootfs, func(src string, info os.FileInfo, _ error) error {
			dst := strings.Replace(src, t.Rootfs, "", 1)
			if dst != "" {
				rootfsFiles[dst] = src
			}
			return nil
		})
	}

	fmt.Println("Uploading files...")

	bar := pb.New(len(rootfsFiles) + len(t.Files))

	if !verbose {
		bar.Start()
	}

	for dst, src := range rootfsFiles {
		err = copyFile(conn, src, dst)
		if verbose {
			fmt.Println(src + "  --> " + dst)
		} else {
			bar.Increment()
		}
		if err != nil {
			return err
		}
	}

	for dst, src := range t.Files {
		err = copyFile(conn, src, dst)
		if verbose {
			fmt.Println(src + "  --> " + dst)
		} else {
			bar.Increment()
		}
		if err != nil {
			return err
		}
	}

	cpio.WritePadded(conn, cpio.ToWireFormat("TRAILER!!!", 0, 0))

	conn.Close()
	return cmd.Wait()
}
Beispiel #11
0
func Run(repo *util.Repo, config *RunConfig) error {
	var path string
	if config.ImageName != "" {
		if _, err := os.Stat(config.ImageName); os.IsNotExist(err) {
			if repo.ImageExists(config.Hypervisor, config.ImageName) {
				path = repo.ImagePath(config.Hypervisor, config.ImageName)
			} else if util.IsRemoteImage(config.ImageName) {
				err := Pull(repo, config.Hypervisor, config.ImageName)
				if err != nil {
					return err
				}
				path = repo.ImagePath(config.Hypervisor, config.ImageName)
			} else {
				return fmt.Errorf("%s: no such image", config.ImageName)
			}
		} else {
			path = config.ImageName
		}
	} else {
		config.ImageName = repo.DefaultImage()
		if config.ImageName == "" {
			return fmt.Errorf("No Capstanfile found, unable to run.")
		}
		if !repo.ImageExists(config.Hypervisor, config.ImageName) {
			if !util.ConfigExists("Capstanfile") {
				return fmt.Errorf("%s: no such image", config.ImageName)
			}
			err := Build(repo, config.Hypervisor, config.ImageName, config.Verbose)
			if err != nil {
				return err
			}
		}
		path = repo.ImagePath(config.Hypervisor, config.ImageName)
	}
	format, err := image.Probe(path)
	if err != nil {
		return err
	}
	if format == image.Unknown {
		return fmt.Errorf("%s: image format not recognized, unable to run it.", path)
	}
	size, err := util.ParseMemSize(config.Memory)
	if err != nil {
		return err
	}
	var cmd *exec.Cmd
	switch config.Hypervisor {
	case "qemu":
		id := util.ID()
		dir := filepath.Join(os.Getenv("HOME"), ".capstan/instances/qemu", id)
		config := &qemu.VMConfig{
			Name:        id,
			Image:       path,
			Verbose:     true,
			Memory:      size,
			Cpus:        config.Cpus,
			NatRules:    config.NatRules,
			BackingFile: true,
			InstanceDir: dir,
			Monitor:     filepath.Join(dir, "osv.monitor"),
		}
		fmt.Printf("Created instance: %s\n", id)
		tio, _ := util.RawTerm()
		defer util.ResetTerm(tio)
		cmd, err = qemu.LaunchVM(config)
		defer qemu.DeleteVM(config)
	case "vbox":
		if format != image.VDI && format != image.VMDK {
			return fmt.Errorf("%s: image format of %s is not supported, unable to run it.", config.Hypervisor, path)
		}
		id := util.ID()
		config := &vbox.VMConfig{
			Name:     id,
			Dir:      filepath.Join(util.HomePath(), ".capstan/instances/vbox"),
			Image:    path,
			Memory:   size,
			Cpus:     config.Cpus,
			NatRules: config.NatRules,
		}
		fmt.Printf("Created instance: %s\n", id)
		tio, _ := util.RawTerm()
		defer util.ResetTerm(tio)
		cmd, err = vbox.LaunchVM(config)
		defer vbox.DeleteVM(config)
	case "gce":
		id := util.ID()
		bucket := "osvimg"
		config := &gce.VMConfig{
			Name:             "osv-capstan-" + id,
			Image:            "osv-capstan-" + id,
			Network:          "default",
			MachineType:      "n1-standard-1",
			Zone:             "us-central1-a",
			CloudStoragePath: "gs://" + bucket + "/osv-capstan-" + id + ".tar.gz",
			Tarball:          path,
		}
		cmd, err = gce.LaunchVM(config)
	case "vmw":
		id := util.ID()
		if format != image.VMDK {
			return fmt.Errorf("%s: image format of %s is not supported, unable to run it.", config.Hypervisor, path)
		}
		dir := filepath.Join(util.HomePath(), ".capstan/instances/vmw", id)
		config := &vmw.VMConfig{
			Name:         id,
			Dir:          dir,
			Image:        filepath.Join(dir, "osv.vmdk"),
			Memory:       size,
			Cpus:         config.Cpus,
			NatRules:     config.NatRules,
			VMXFile:      filepath.Join(dir, "osv.vmx"),
			InstanceDir:  dir,
			OriginalVMDK: path,
		}
		fmt.Printf("Created instance: %s\n", id)
		tio, _ := util.RawTerm()
		defer util.ResetTerm(tio)
		cmd, err = vmw.LaunchVM(config)
		defer vmw.DeleteVM(config)
	default:
		err = fmt.Errorf("%s: is not a supported hypervisor", config.Hypervisor)
	}
	if err != nil {
		return err
	}
	if cmd != nil {
		return cmd.Wait()
	} else {
		return nil
	}
}