func DeleteVM(name string) error { gcUtil("deleteinstance", "--delete_boot_pd", "-f", name) gcUtil("deletedisk", "-f", name) dir := filepath.Join(util.HomePath(), ".capstan/instances/gce", name) c := &VMConfig{ InstanceDir: dir, ConfigFile: filepath.Join(dir, "osv.config"), } cmd := exec.Command("rm", "-f", c.ConfigFile) _, err := cmd.Output() if err != nil { fmt.Printf("rm failed: %s", c.ConfigFile) return err } cmd = exec.Command("rmdir", c.InstanceDir) _, err = cmd.Output() if err != nil { fmt.Printf("rmdir failed: %s", c.InstanceDir) return err } return nil }
func StopVM(name string) error { dir := filepath.Join(util.HomePath(), ".capstan/instances/qemu", name) c := &VMConfig{ Monitor: filepath.Join(dir, "osv.monitor"), } conn, err := net.Dial("unix", c.Monitor) if err != nil { // The instance is stopped already return nil } writer := bufio.NewWriter(conn) cmd := `{ "execute": "qmp_capabilities"}` writer.WriteString(cmd) cmd = `{ "execute": "system_powerdown" }` writer.WriteString(cmd) cmd = `{ "execute": "quit" }` writer.WriteString(cmd) writer.Flush() return nil }
func DeleteVM(name string) error { dir := filepath.Join(util.HomePath(), ".capstan/instances/vmw", name) c := &VMConfig{ VMXFile: filepath.Join(dir, "osv.vmx"), ConfigFile: filepath.Join(dir, "osv.config"), } cmd := exec.Command("rm", "-f", c.ConfigFile) _, err := cmd.Output() if err != nil { fmt.Printf("Failed to delete: %s", c.ConfigFile) return err } cmd, err = vmxRun("-T", "ws", "deleteVM", c.VMXFile) if err != nil { fmt.Printf("Failed to delete VM %s", c.VMXFile) return err } err = cmd.Wait() if err != nil { fmt.Printf("Failed to delete VM %s", c.VMXFile) return err } return nil }
func DeleteVM(name string) error { dir := filepath.Join(util.HomePath(), ".capstan/instances/vbox", name) c := &VMConfig{ ConfigFile: filepath.Join(dir, "osv.config"), } cmd := exec.Command("rm", "-f", c.ConfigFile) _, err := cmd.Output() if err != nil { fmt.Printf("Failed to delete: %s", c.ConfigFile) return err } return VBoxManage("unregistervm", name, "--delete") }
func StopVM(name string) error { dir := filepath.Join(util.HomePath(), ".capstan/instances/vmw", name) c := &VMConfig{ VMXFile: filepath.Join(dir, "osv.vmx"), } cmd, err := vmxRun("-T", "ws", "stop", c.VMXFile) if err != nil { fmt.Printf("Failed to stop VM", c.VMXFile) return err } err = cmd.Wait() if err != nil { fmt.Printf("Failed to stop VM", c.VMXFile) return err } return nil }
func LoadConfig(name string) (*VMConfig, error) { dir := filepath.Join(util.HomePath(), ".capstan/instances/gce", name) file := filepath.Join(dir, "osv.config") c := VMConfig{} data, err := ioutil.ReadFile(file) if err != nil { fmt.Printf("Failed to open: %s\n", file) return nil, err } err = yaml.Unmarshal(data, &c) if err != nil { return nil, err } return &c, nil }
func Stop(name string) error { instanceName := "" instancePlatform := "" rootDir := filepath.Join(util.HomePath(), ".capstan", "instances") platforms, _ := ioutil.ReadDir(rootDir) for _, platform := range platforms { if platform.IsDir() { platformDir := filepath.Join(rootDir, platform.Name()) instances, _ := ioutil.ReadDir(platformDir) for _, instance := range instances { if instance.IsDir() { if name == instance.Name() { instanceName = instance.Name() instancePlatform = platform.Name() } } } } } if instanceName == "" { fmt.Printf("Instance: %s not found\n", name) return nil } var err error switch instancePlatform { case "qemu": err = qemu.StopVM(name) case "vbox": err = vbox.StopVM(name) case "vmw": err = vmw.StopVM(name) } if err != nil { fmt.Printf("Failed to stop instance: %s\n", name) } fmt.Printf("Stopped instance: %s\n", name) return nil }
func Instances() error { header := fmt.Sprintf("%-20s %-10s %-10s %-15s", "Name", "Platform", "Status", "Image") fmt.Println(header) rootDir := filepath.Join(util.HomePath(), ".capstan", "instances") platforms, _ := ioutil.ReadDir(rootDir) for _, platform := range platforms { if platform.IsDir() { platformDir := filepath.Join(rootDir, platform.Name()) instances, _ := ioutil.ReadDir(platformDir) for _, instance := range instances { if instance.IsDir() { instanceDir := filepath.Join(platformDir, instance.Name()) printInstance(instance.Name(), platform.Name(), instanceDir) } } } } return nil }
func DeleteVM(name string) error { dir := filepath.Join(util.HomePath(), ".capstan/instances/qemu", name) c := &VMConfig{ InstanceDir: dir, Monitor: filepath.Join(dir, "osv.monitor"), Image: filepath.Join(dir, "disk.qcow2"), ConfigFile: filepath.Join(dir, "osv.config"), } cmd := exec.Command("rm", "-f", c.Image, " ", c.Monitor, " ", c.ConfigFile) _, err := cmd.Output() if err != nil { fmt.Printf("rm failed: %s, %s", c.Image, c.Monitor) return err } cmd = exec.Command("rmdir", c.InstanceDir) _, err = cmd.Output() if err != nil { fmt.Printf("rmdir failed: %s", c.InstanceDir) return err } return nil }
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 } }
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 } }