diff --git a/lxc/storage_volume.go b/lxc/storage_volume.go index f2ddf1746dff..32ada6fc5e35 100644 --- a/lxc/storage_volume.go +++ b/lxc/storage_volume.go @@ -51,6 +51,17 @@ func parseVolume(defaultType string, name string) (volName string, volType strin return volName, volType } +func parseVolumeSnapshot(defaultType string, name string) (volName string, volType string, snapshot string) { + volName, volType = parseVolume(defaultType, name) + + parts := strings.SplitN(volName, "/", 2) + if len(parts) == 2 { + volName, snapshot = parts[0], parts[1] + } + + return volName, volType, snapshot +} + func (c *cmdStorageVolume) command() *cobra.Command { cmd := &cobra.Command{} cmd.Use = usage("volume") @@ -164,7 +175,7 @@ type cmdStorageVolumeAttach struct { func (c *cmdStorageVolumeAttach) command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = usage("attach", i18n.G("[:] [/] [] []")) + cmd.Use = usage("attach", i18n.G("[:] [/][/] [] []")) cmd.Short = i18n.G("Attach new storage volumes to instances") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( `Attach new storage volumes to instances @@ -211,7 +222,7 @@ func (c *cmdStorageVolumeAttach) run(cmd *cobra.Command, args []string) error { return errors.New(i18n.G("Missing pool name")) } - volName, volType := parseVolume("custom", args[1]) + volName, volType, snapshot := parseVolumeSnapshot("custom", args[1]) if volType != "custom" && volType != "virtual-machine" { return errors.New(i18n.G(`Only "custom" and "virtual-machine" volumes can be attached to instances`)) } @@ -268,6 +279,10 @@ func (c *cmdStorageVolumeAttach) run(cmd *cobra.Command, args []string) error { device["source-type"] = volType } + if snapshot != "" { + device["snapshot"] = snapshot + } + // Add the device to the instance err = instanceDeviceAdd(resource.server, args[2], devName, device) if err != nil { @@ -286,7 +301,7 @@ type cmdStorageVolumeAttachProfile struct { func (c *cmdStorageVolumeAttachProfile) command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = usage("attach-profile", i18n.G("[] [/] [] []")) + cmd.Use = usage("attach-profile", i18n.G("[] [/][/] [] []")) cmd.Short = i18n.G("Attach new storage volumes to profiles") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( `Attach new storage volumes to profiles @@ -348,7 +363,7 @@ func (c *cmdStorageVolumeAttachProfile) run(cmd *cobra.Command, args []string) e devPath = args[4] } - volName, volType := parseVolume("custom", args[1]) + volName, volType, snapshot := parseVolumeSnapshot("custom", args[1]) if volType != "custom" && volType != "virtual-machine" { return errors.New(i18n.G(`Only "custom" and "virtual-machine" volumes can be attached to profiles`)) } @@ -376,6 +391,10 @@ func (c *cmdStorageVolumeAttachProfile) run(cmd *cobra.Command, args []string) e device["source-type"] = volType } + if snapshot != "" { + device["snapshot"] = snapshot + } + // Add the device to the instance err = profileDeviceAdd(resource.server, args[2], devName, device) if err != nil { @@ -817,7 +836,7 @@ type cmdStorageVolumeDetach struct { func (c *cmdStorageVolumeDetach) command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = usage("detach", i18n.G("[:] [/] []")) + cmd.Use = usage("detach", i18n.G("[:] [/][/] []")) cmd.Short = i18n.G("Detach storage volumes from instances") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( `Detach storage volumes from instances`)) @@ -874,7 +893,7 @@ func (c *cmdStorageVolumeDetach) run(cmd *cobra.Command, args []string) error { return err } - volName, volType := parseVolume("custom", args[1]) + volName, volType, snapshot := parseVolumeSnapshot("custom", args[1]) // Find the device if devName == "" { @@ -884,7 +903,7 @@ func (c *cmdStorageVolumeDetach) run(cmd *cobra.Command, args []string) error { sourceType = d["source-type"] } - if d["type"] == "disk" && d["pool"] == resource.name && volType == sourceType && volName == d["source"] { + if d["type"] == "disk" && d["pool"] == resource.name && volType == sourceType && volName == d["source"] && snapshot == d["snapshot"] { if devName != "" { return errors.New(i18n.G("More than one device matches, specify the device name")) } @@ -922,7 +941,7 @@ type cmdStorageVolumeDetachProfile struct { func (c *cmdStorageVolumeDetachProfile) command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = usage("detach-profile", i18n.G("[] [/] []")) + cmd.Use = usage("detach-profile", i18n.G("[] [/][/] []")) cmd.Short = i18n.G("Detach storage volumes from profiles") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( `Detach storage volumes from profiles`)) @@ -978,7 +997,7 @@ func (c *cmdStorageVolumeDetachProfile) run(cmd *cobra.Command, args []string) e return err } - volName, volType := parseVolume("custom", args[1]) + volName, volType, snapshot := parseVolumeSnapshot("custom", args[1]) // Find the device if devName == "" { @@ -988,7 +1007,7 @@ func (c *cmdStorageVolumeDetachProfile) run(cmd *cobra.Command, args []string) e sourceType = d["source-type"] } - if d["type"] == "disk" && d["pool"] == resource.name && volType == sourceType && volName == d["source"] { + if d["type"] == "disk" && d["pool"] == resource.name && volType == sourceType && volName == d["source"] && snapshot == d["snapshot"] { if devName != "" { return errors.New(i18n.G("More than one device matches, specify the device name")) }