Skip to content

Commit 52df264

Browse files
committed
add possibility to choose whether snapshot base volume or copy
libvirt volume resource now has key base_volume_copy which controls whether to invoke StorageVolCreateXML (defualt) or StorageVolCreateXMLFrom
1 parent e9ff32f commit 52df264

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

libvirt/resource_libvirt_volume.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ func resourceLibvirtVolume() *schema.Resource {
5858
Optional: true,
5959
ForceNew: true,
6060
},
61+
"base_volume_copy": {
62+
Type: schema.TypeBool,
63+
Optional: true,
64+
ForceNew: true,
65+
Default: false,
66+
},
6167
"xml": {
6268
Type: schema.TypeList,
6369
Optional: true,
@@ -118,6 +124,8 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error
118124
volumeDef.Target.Format.Type = givenFormat.(string)
119125
}
120126

127+
var baseVolume *libvirt.StorageVol
128+
121129
// an source image was given, this mean we can't choose size
122130
if source, ok := d.GetOk("source"); ok {
123131
// source and size conflict
@@ -167,7 +175,6 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error
167175

168176
//first handle whether it has a backing image
169177
// backing images can be specified by either (id), or by (name, pool)
170-
var baseVolume *libvirt.StorageVol
171178
if baseVolumeID, ok := d.GetOk("base_volume_id"); ok {
172179
if _, ok := d.GetOk("base_volume_name"); ok {
173180
return fmt.Errorf("'base_volume_name' can't be specified when also 'base_volume_id' is given")
@@ -192,7 +199,7 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error
192199
}
193200
}
194201

195-
if baseVolume != nil {
202+
if !d.Get("base_volume_copy").(bool) && baseVolume != nil {
196203
backingStoreFragmentDef, err := newDefBackingStoreFromLibvirt(baseVolume)
197204
if err != nil {
198205
return fmt.Errorf("Could not retrieve backing store definition: %s", err.Error())
@@ -214,6 +221,8 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error
214221
return fmt.Errorf("When 'size' is specified, it shouldn't be smaller than the backing store specified with 'base_volume_id' or 'base_volume_name/base_volume_pool'")
215222
}
216223
volumeDef.BackingStore = &backingStoreFragmentDef
224+
} else if d.Get("base_volume_copy").(bool) && baseVolume == nil {
225+
return fmt.Errorf("Can't find base volume to copy to '%s'", d.Get("name").(string))
217226
}
218227
}
219228

@@ -229,7 +238,12 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error
229238
}
230239

231240
// create the volume
232-
volume, err := pool.StorageVolCreateXML(data, 0)
241+
var volume *libvirt.StorageVol
242+
if d.Get("base_volume_copy").(bool) {
243+
volume, err = pool.StorageVolCreateXMLFrom(data, baseVolume, 0)
244+
} else {
245+
volume, err = pool.StorageVolCreateXML(data, 0)
246+
}
233247
if err != nil {
234248
return fmt.Errorf("Error creating libvirt volume: %s", err)
235249
}

libvirt/resource_libvirt_volume_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,33 @@ func testAccCheckLibvirtVolumeIsBackingStore(name string, volume *libvirt.Storag
9292
}
9393
}
9494

95+
func testAccCheckLibvirtVolumeIsNotBackingStore(name string, volume *libvirt.StorageVol) resource.TestCheckFunc {
96+
return func(state *terraform.State) error {
97+
virConn := testAccProvider.Meta().(*Client).libvirt
98+
99+
vol, err := getVolumeFromTerraformState(name, state, *virConn)
100+
if err != nil {
101+
return err
102+
}
103+
104+
volXMLDesc, err := vol.GetXMLDesc(0)
105+
if err != nil {
106+
return fmt.Errorf("Error retrieving libvirt volume XML description: %s", err)
107+
}
108+
109+
volumeDef := newDefVolume()
110+
err = xml.Unmarshal([]byte(volXMLDesc), &volumeDef)
111+
if err != nil {
112+
return fmt.Errorf("Error reading libvirt volume XML description: %s", err)
113+
}
114+
if volumeDef.BackingStore != nil {
115+
return fmt.Errorf("FAIL: the volume was not supposed to be a backingstore, but it is")
116+
}
117+
118+
return nil
119+
}
120+
}
121+
95122
func TestAccLibvirtVolume_Basic(t *testing.T) {
96123
var volume libvirt.StorageVol
97124
randomVolumeResource := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
@@ -206,6 +233,90 @@ func TestAccLibvirtVolume_BackingStoreTestByName(t *testing.T) {
206233
})
207234
}
208235

236+
func TestAccLibvirtVolume_BackingStoreCopyDir(t *testing.T) {
237+
var volume libvirt.StorageVol
238+
var volume2 libvirt.StorageVol
239+
random := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
240+
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + random
241+
// for qcow2 file images regardless of user specified size, the final size will
242+
// be the same as the backing image's size. This is so because libvirt
243+
// invokes qemu-img create followed by qemu-img convert for StorageVolCreateXMLFrom.
244+
// This behavior can be tested like this:
245+
// $ qemu-img create -f qcow2 test.qcow2 1024K
246+
// $ qemu-img info test.qcow2
247+
// $ qemu-img create -f qcow2 test-copy.qcow2 2048K
248+
// $ qemu-img info test-copy.qcow2
249+
// $ qemu-img convert -f qcow2 -O qcow2 test.qcow2 test-copy.qcow2
250+
// $ qemu-img info test-copy.qcow2
251+
resource.Test(t, resource.TestCase{
252+
PreCheck: func() { testAccPreCheck(t) },
253+
Providers: testAccProviders,
254+
CheckDestroy: resource.ComposeAggregateTestCheckFunc(
255+
testAccCheckLibvirtVolumeDestroy,
256+
testAccCheckLibvirtPoolDestroy,
257+
),
258+
Steps: []resource.TestStep{
259+
{
260+
Config: fmt.Sprintf(`
261+
resource "libvirt_pool" "%s" {
262+
name = "%s"
263+
type = "dir"
264+
path = "%s"
265+
}
266+
resource "libvirt_volume" "backing-%s" {
267+
name = "backing-%s"
268+
size = 1000448
269+
pool = "${libvirt_pool.%s.name}"
270+
}
271+
resource "libvirt_volume" "%s" {
272+
name = "%s"
273+
base_volume_copy = true
274+
base_volume_id = "${libvirt_volume.backing-%s.id}"
275+
pool = "${libvirt_pool.%s.name}"
276+
}
277+
`, random, random, randomPoolPath, random, random, random, random, random, random, random),
278+
Check: resource.ComposeTestCheckFunc(
279+
testAccCheckLibvirtVolumeExists("libvirt_volume.backing-"+random, &volume),
280+
testAccCheckLibvirtVolumeIsNotBackingStore("libvirt_volume."+random, &volume2),
281+
),
282+
},
283+
},
284+
})
285+
}
286+
287+
func TestAccLibvirtVolume_NoBackingStoreCopyError(t *testing.T) {
288+
random := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
289+
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + random
290+
expectedError := regexp.MustCompile(fmt.Sprintf(
291+
"Can't find base volume to copy to '%s'", random,
292+
))
293+
resource.Test(t, resource.TestCase{
294+
PreCheck: func() { testAccPreCheck(t) },
295+
Providers: testAccProviders,
296+
CheckDestroy: resource.ComposeAggregateTestCheckFunc(
297+
testAccCheckLibvirtVolumeDestroy,
298+
testAccCheckLibvirtPoolDestroy,
299+
),
300+
Steps: []resource.TestStep{
301+
{
302+
Config: fmt.Sprintf(`
303+
resource "libvirt_pool" "%s" {
304+
name = "%s"
305+
type = "dir"
306+
path = "%s"
307+
}
308+
resource "libvirt_volume" "%s" {
309+
name = "%s"
310+
base_volume_copy = true
311+
pool = "${libvirt_pool.%s.name}"
312+
}
313+
`, random, random, randomPoolPath, random, random, random),
314+
ExpectError: expectedError,
315+
},
316+
},
317+
})
318+
}
319+
209320
// The destroy function should always handle the case where the resource might already be destroyed
210321
// (manually, for example). If the resource is already destroyed, this should not return an error.
211322
// This allows Terraform users to manually delete resources without breaking Terraform.

website/docs/r/volume.html.markdown

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ The following arguments are supported:
6464
volume is going to be searched inside of `pool`.
6565
* `base_volume_pool` - (Optional) The name of the storage pool containing the
6666
volume defined by `base_volume_name`.
67+
* `base_volume_copy` - (Optional) If set to `true`, [virStorageVolCreateXMLFrom]
68+
is invoked rather than default [virStorageVolCreateXML] when creating from
69+
base volume. This allows to create copies of volumes not associated with base
70+
volumes in libvirt xml definitions, create copies (not snapshots) of lvm volumes
71+
which also can be larger than the base volume, etc.
6772

6873
### Altering libvirt's generated volume XML definition
6974

@@ -80,3 +85,7 @@ See the domain option with the same name for more information and examples.
8085
## Attributes Reference
8186

8287
* `id` - a unique identifier for the resource
88+
89+
90+
[virStorageVolCreateXMLFrom]: <https://libvirt.org/html/libvirt-libvirt-storage.html#virStorageVolCreateXMLFrom>
91+
[virStorageVolCreateXML]: <https://libvirt.org/html/libvirt-libvirt-storage.html#virStorageVolCreateXML>

0 commit comments

Comments
 (0)