diff options
-rw-r--r-- | cmd/podman/machine/init.go | 8 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-init.1.md | 15 | ||||
-rw-r--r-- | pkg/machine/config.go | 2 | ||||
-rw-r--r-- | pkg/machine/qemu/config.go | 10 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 96 |
5 files changed, 130 insertions, 1 deletions
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index 14e87c201..ed04239c4 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -88,6 +88,14 @@ func init() { flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Machine.Image, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) + VolumeFlagName := "volume" + flags.StringArrayVarP(&initOpts.Volumes, VolumeFlagName, "v", []string{}, "Volumes to mount, source:target") + _ = initCmd.RegisterFlagCompletionFunc(VolumeFlagName, completion.AutocompleteDefault) + + VolumeDriverFlagName := "volume-driver" + flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver") + _ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault) + IgnitionPathFlagName := "ignition-path" flags.StringVar(&initOpts.IgnitionPath, IgnitionPathFlagName, "", "Path to ignition file") _ = initCmd.RegisterFlagCompletionFunc(IgnitionPathFlagName, completion.AutocompleteDefault) diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md index aead6c695..b515e8763 100644 --- a/docs/source/markdown/podman-machine-init.1.md +++ b/docs/source/markdown/podman-machine-init.1.md @@ -61,6 +61,20 @@ Set the timezone for the machine and containers. Valid values are `local` or a `timezone` such as `America/Chicago`. A value of `local`, which is the default, means to use the timezone of the machine host. +#### **--volume**, **-v**=*source:target* + +Mounts a volume from source to target. + +Create a mount. If /host-dir:/machine-dir is specified as the `*source:target*`, +Podman mounts _host-dir_ in the host to _machine-dir_ in the Podman machine. + +The root filesystem is mounted read-only in the default operating system, +so mounts must be created under the /mnt directory. + +#### **--volume-driver** + +Driver to use for mounting volumes from the host, such as `virtfs`. + #### **--help** Print usage statement. @@ -72,6 +86,7 @@ $ podman machine init $ podman machine init myvm $ podman machine init --disk-size 50 $ podman machine init --memory=1024 myvm +$ podman machine init -v /Users:/mnt/Users ``` ## SEE ALSO diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 4f2947ac0..33a352898 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -18,6 +18,8 @@ type InitOptions struct { DiskSize uint64 IgnitionPath string ImagePath string + Volumes []string + VolumeDriver string IsDefault bool Memory uint64 Name string diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 8404079a2..e76509bb1 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -11,6 +11,8 @@ type MachineVM struct { CPUs uint64 // The command line representation of the qemu command CmdLine []string + // Mounts is the list of remote filesystems to mount + Mounts []Mount // IdentityPath is the fq path to the ssh priv key IdentityPath string // IgnitionFilePath is the fq path to the .ign file @@ -33,6 +35,14 @@ type MachineVM struct { RemoteUsername string } +type Mount struct { + Type string + Tag string + Source string + Target string + ReadOnly bool +} + type Monitor struct { // Address portion of the qmp monitor (/tmp/tmp.sock) Address string diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index a80a11573..f09107c71 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -36,6 +36,11 @@ func GetQemuProvider() machine.Provider { return qemuProvider } +const ( + VolumeTypeVirtfs = "virtfs" + MountType9p = "9p" +) + // NewMachine initializes an instance of a virtual machine based on the qemu // virtualization. func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { @@ -167,6 +172,53 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { // Add arch specific options including image location v.CmdLine = append(v.CmdLine, v.addArchOptions()...) + var volumeType string + switch opts.VolumeDriver { + case "virtfs": + volumeType = VolumeTypeVirtfs + case "": // default driver + volumeType = VolumeTypeVirtfs + default: + err := fmt.Errorf("unknown volume driver: %s", opts.VolumeDriver) + return false, err + } + + mounts := []Mount{} + for i, volume := range opts.Volumes { + tag := fmt.Sprintf("vol%d", i) + paths := strings.SplitN(volume, ":", 3) + source := paths[0] + target := source + readonly := false + if len(paths) > 1 { + target = paths[1] + } + if len(paths) > 2 { + options := paths[2] + volopts := strings.Split(options, ",") + for _, o := range volopts { + switch o { + case "rw": + readonly = false + case "ro": + readonly = true + default: + fmt.Printf("Unknown option: %s\n", o) + } + } + } + switch volumeType { + case VolumeTypeVirtfs: + virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=mapped-xattr", source, tag) + if readonly { + virtfsOptions += ",readonly" + } + v.CmdLine = append(v.CmdLine, []string{"-virtfs", virtfsOptions}...) + mounts = append(mounts, Mount{Type: MountType9p, Tag: tag, Source: source, Target: target, ReadOnly: readonly}) + } + } + v.Mounts = mounts + // Add location of bootable image v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) // This kind of stinks but no other way around this r/n @@ -329,7 +381,39 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return err } _, err = bufio.NewReader(conn).ReadString('\n') - return err + if err != nil { + return err + } + + if len(v.Mounts) > 0 { + for !v.isRunning() || !v.isListening() { + time.Sleep(100 * time.Millisecond) + } + } + for _, mount := range v.Mounts { + fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target) + // create mountpoint directory if it doesn't exist + err = v.SSH(name, machine.SSHOptions{Args: []string{"-q", "--", "sudo", "mkdir", "-p", mount.Target}}) + if err != nil { + return err + } + switch mount.Type { + case MountType9p: + mountOptions := []string{"-t", "9p"} + mountOptions = append(mountOptions, []string{"-o", "trans=virtio", mount.Tag, mount.Target}...) + mountOptions = append(mountOptions, []string{"-o", "version=9p2000.L,msize=131072"}...) + if mount.ReadOnly { + mountOptions = append(mountOptions, []string{"-o", "ro"}...) + } + err = v.SSH(name, machine.SSHOptions{Args: append([]string{"-q", "--", "sudo", "mount"}, mountOptions...)}) + if err != nil { + return err + } + default: + return fmt.Errorf("unknown mount type: %s", mount.Type) + } + } + return nil } // Stop uses the qmp monitor to call a system_powerdown @@ -506,6 +590,16 @@ func (v *MachineVM) isRunning() bool { return true } +func (v *MachineVM) isListening() bool { + // Check if we can dial it + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", "localhost", v.Port), 10*time.Millisecond) + if err != nil { + return false + } + conn.Close() + return true +} + // SSH opens an interactive SSH session to the vm specified. // Added ssh function to VM interface: pkg/machine/config/go : line 58 func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { |