From 7fbe405ecb15e4ab22da4a74368aafcc6b0c2ea8 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 10 Aug 2017 12:48:55 +0200 Subject: use libvirt-go-xml for volumes Signed-off-by: Thomas Hipp --- libvirt/cloudinit_def.go | 2 +- libvirt/coreos_ignition_def.go | 4 +- libvirt/resource_libvirt_volume.go | 6 +- libvirt/utils_volume.go | 31 +++++++---- libvirt/utils_volume_test.go | 36 +++++++++--- libvirt/volume_def.go | 110 +++++++++++-------------------------- 6 files changed, 85 insertions(+), 104 deletions(-) diff --git a/libvirt/cloudinit_def.go b/libvirt/cloudinit_def.go index 4fc05507..37f4afb4 100644 --- a/libvirt/cloudinit_def.go +++ b/libvirt/cloudinit_def.go @@ -92,7 +92,7 @@ func (ci *defCloudInit) CreateAndUpload(virConn *libvirt.Connect) (string, error } volumeDef.Capacity.Unit = "B" - volumeDef.Capacity.Amount = size + volumeDef.Capacity.Value = size volumeDef.Target.Format.Type = "raw" volumeDefXml, err := xml.Marshal(volumeDef) diff --git a/libvirt/coreos_ignition_def.go b/libvirt/coreos_ignition_def.go index 1ac7def9..084979fc 100644 --- a/libvirt/coreos_ignition_def.go +++ b/libvirt/coreos_ignition_def.go @@ -72,7 +72,7 @@ func (ign *defIgnition) CreateAndUpload(virConn *libvirt.Connect) (string, error } volumeDef.Capacity.Unit = "B" - volumeDef.Capacity.Amount = size + volumeDef.Capacity.Value = size volumeDef.Target.Format.Type = "raw" volumeDefXml, err := xml.Marshal(volumeDef) @@ -88,7 +88,7 @@ func (ign *defIgnition) CreateAndUpload(virConn *libvirt.Connect) (string, error defer volume.Free() // upload ignition file - err = img.Import(newCopier(virConn, volume, volumeDef.Capacity.Amount), volumeDef) + err = img.Import(newCopier(virConn, volume, volumeDef.Capacity.Value), volumeDef) if err != nil { return "", fmt.Errorf("Error while uploading ignition file %s: %s", img.String(), err) } diff --git a/libvirt/resource_libvirt_volume.go b/libvirt/resource_libvirt_volume.go index 918981e4..234772db 100644 --- a/libvirt/resource_libvirt_volume.go +++ b/libvirt/resource_libvirt_volume.go @@ -159,7 +159,7 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error } else { log.Printf("Image %s image is: %d bytes", img, size) volumeDef.Capacity.Unit = "B" - volumeDef.Capacity.Amount = size + volumeDef.Capacity.Value = size } } else { _, noSize := d.GetOk("size") @@ -168,7 +168,7 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error if noSize && noBaseVol { return fmt.Errorf("'size' needs to be specified if no 'source' or 'base_volume_id' is given.") } - volumeDef.Capacity.Amount = uint64(d.Get("size").(int)) + volumeDef.Capacity.Value = uint64(d.Get("size").(int)) } if baseVolumeId, ok := d.GetOk("base_volume_id"); ok { @@ -250,7 +250,7 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error // upload source if present if _, ok := d.GetOk("source"); ok { - err = img.Import(newCopier(virConn, volume, volumeDef.Capacity.Amount), volumeDef) + err = img.Import(newCopier(virConn, volume, volumeDef.Capacity.Value), volumeDef) if err != nil { return fmt.Errorf("Error while uploading source %s: %s", img.String(), err) } diff --git a/libvirt/utils_volume.go b/libvirt/utils_volume.go index 83596465..289ded4e 100644 --- a/libvirt/utils_volume.go +++ b/libvirt/utils_volume.go @@ -9,14 +9,16 @@ import ( "os" "strconv" "strings" + "time" libvirt "github.com/libvirt/libvirt-go" + "github.com/libvirt/libvirt-go-xml" ) // network transparent image type image interface { Size() (uint64, error) - Import(func(io.Reader) error, defVolume) error + Import(func(io.Reader) error, libvirtxml.StorageVolume) error String() string } @@ -41,8 +43,7 @@ func (i *localImage) Size() (uint64, error) { return uint64(fi.Size()), nil } -func (i *localImage) Import(copier func(io.Reader) error, vol defVolume) error { - +func (i *localImage) Import(copier func(io.Reader) error, vol libvirtxml.StorageVolume) error { file, err := os.Open(i.path) defer file.Close() if err != nil { @@ -53,9 +54,8 @@ func (i *localImage) Import(copier func(io.Reader) error, vol defVolume) error { return err } else { // we can skip the upload if the modification times are the same - if vol.Target.Timestamps != nil && vol.Target.Timestamps.Modification != nil { - modTime := UnixTimestamp{fi.ModTime()} - if modTime == *vol.Target.Timestamps.Modification { + if vol.Target.Timestamps != nil && vol.Target.Timestamps.Mtime != "" { + if fi.ModTime() == timeFromEpoch(vol.Target.Timestamps.Mtime) { log.Printf("Modification time is the same: skipping image copy") return nil } @@ -98,13 +98,12 @@ func (i *httpImage) Size() (uint64, error) { return uint64(length), nil } -func (i *httpImage) Import(copier func(io.Reader) error, vol defVolume) error { +func (i *httpImage) Import(copier func(io.Reader) error, vol libvirtxml.StorageVolume) error { client := &http.Client{} req, _ := http.NewRequest("GET", i.url.String(), nil) - if vol.Target.Timestamps != nil && vol.Target.Timestamps.Modification != nil { - t := vol.Target.Timestamps.Modification.UTC().Format(http.TimeFormat) - req.Header.Set("If-Modified-Since", t) + if vol.Target.Timestamps != nil && vol.Target.Timestamps.Mtime != "" { + req.Header.Set("If-Modified-Since", timeFromEpoch(vol.Target.Timestamps.Mtime).UTC().Format(http.TimeFormat)) } response, err := client.Do(req) defer response.Body.Close() @@ -155,3 +154,15 @@ func newCopier(virConn *libvirt.Connect, volume *libvirt.StorageVol, size uint64 } return copier } + +func timeFromEpoch(str string) time.Time { + var s, ns int + + ts := strings.Split(str, ".") + if len(ts) == 2 { + ns, _ = strconv.Atoi(ts[1]) + } + s, _ = strconv.Atoi(ts[0]) + + return time.Unix(int64(s), int64(ns)) +} diff --git a/libvirt/utils_volume_test.go b/libvirt/utils_volume_test.go index 5d8188fb..577d6057 100644 --- a/libvirt/utils_volume_test.go +++ b/libvirt/utils_volume_test.go @@ -6,6 +6,9 @@ import ( "io/ioutil" "os" "testing" + "time" + + "github.com/libvirt/libvirt-go-xml" ) func TestLocalImageDownload(t *testing.T) { @@ -24,7 +27,6 @@ func TestLocalImageDownload(t *testing.T) { if err != nil { t.Fatal(err) } - url := fmt.Sprintf("file://%s", tmpfile.Name()) image, err := newImage(url) if err != nil { @@ -33,9 +35,8 @@ func TestLocalImageDownload(t *testing.T) { t.Logf("Importing %s", url) vol := newDefVolume() - modTime := UnixTimestamp{tmpfileStat.ModTime()} - vol.Target.Timestamps = &defTimestamps{ - Modification: &modTime, + vol.Target.Timestamps = &libvirtxml.StorageVolumeTargetTimestamps{ + Mtime: fmt.Sprintf("%d.%d", tmpfileStat.ModTime().Unix(), tmpfileStat.ModTime().Nanosecond()), } copier := func(r io.Reader) error { @@ -43,7 +44,7 @@ func TestLocalImageDownload(t *testing.T) { return nil } if err = image.Import(copier, vol); err != nil { - t.Fatal("Could not copy image from %s: %v", url, err) + t.Fatalf("Could not copy image from %s: %v", url, err) } t.Log("File not copied because modification time was the same") } @@ -72,17 +73,34 @@ func TestRemoteImageDownload(t *testing.T) { t.Logf("Importing %s", url) vol := newDefVolume() - modTime := UnixTimestamp{tmpfileStat.ModTime()} - vol.Target.Timestamps = &defTimestamps{ - Modification: &modTime, + vol.Target.Timestamps = &libvirtxml.StorageVolumeTargetTimestamps{ + Mtime: fmt.Sprintf("%d.%d", tmpfileStat.ModTime().Unix(), tmpfileStat.ModTime().Nanosecond()), } copier := func(r io.Reader) error { t.Fatalf("ERROR: starting copy of %s... but the file is the same!", url) return nil } if err = image.Import(copier, vol); err != nil { - t.Fatal("Could not copy image from %s: %v", url, err) + t.Fatalf("Could not copy image from %s: %v", url, err) } t.Log("File not copied because modification time was the same") } + +func TestTimeFromEpoch(t *testing.T) { + if ts := timeFromEpoch(""); ts.UnixNano() > 0 { + t.Fatalf("expected timestamp '0.0', got %v.%v", ts.Unix(), ts.Nanosecond()) + } + + if ts := timeFromEpoch("abc"); ts.UnixNano() > 0 { + t.Fatalf("expected timestamp '0.0', got %v.%v", ts.Unix(), ts.Nanosecond()) + } + + if ts := timeFromEpoch("123"); ts.UnixNano() != time.Unix(123, 0).UnixNano() { + t.Fatalf("expected timestamp '123.0', got %v.%v", ts.Unix(), ts.Nanosecond()) + } + + if ts := timeFromEpoch("123.456"); ts.UnixNano() != time.Unix(123, 456).UnixNano() { + t.Fatalf("expected timestamp '123.456', got %v.%v", ts.Unix(), ts.Nanosecond()) + } +} diff --git a/libvirt/volume_def.go b/libvirt/volume_def.go index f667fe10..6d867240 100644 --- a/libvirt/volume_def.go +++ b/libvirt/volume_def.go @@ -3,116 +3,68 @@ package libvirt import ( "encoding/xml" "fmt" - "math" - "strconv" - "time" libvirt "github.com/libvirt/libvirt-go" + "github.com/libvirt/libvirt-go-xml" ) -type UnixTimestamp struct{ time.Time } - -func (t *UnixTimestamp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - var content string - if err := d.DecodeElement(&content, &start); err != nil { - return err - } - - ts, err := strconv.ParseFloat(content, 64) - if err != nil { - return err +func newDefVolume() libvirtxml.StorageVolume { + return libvirtxml.StorageVolume{ + Target: &libvirtxml.StorageVolumeTarget{ + Format: &libvirtxml.StorageVolumeTargetFormat{ + Type: "qcow2", + }, + Permissions: &libvirtxml.StorageVolumeTargetPermissions{ + Mode: "644", + }, + }, + Capacity: &libvirtxml.StorageVolumeSize{ + Unit: "bytes", + Value: 1, + }, } - s, ns := math.Modf(ts) - *t = UnixTimestamp{time.Time(time.Unix(int64(s), int64(ns)))} - return nil -} - -func (t *UnixTimestamp) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - s := t.UTC().Unix() - ns := t.UTC().UnixNano() - return e.EncodeElement(fmt.Sprintf("%d.%d", s, ns), start) -} - -type defTimestamps struct { - Change *UnixTimestamp `xml:"ctime,omitempty"` - Modification *UnixTimestamp `xml:"mtime,omitempty"` - Access *UnixTimestamp `xml:"atime,omitempty"` -} - -type defBackingStore struct { - Path string `xml:"path"` - Format struct { - Type string `xml:"type,attr"` - } `xml:"format"` - Timestamps *defTimestamps `xml:"timestamps,omitempty"` -} - -type defVolume struct { - XMLName xml.Name `xml:"volume"` - Name string `xml:"name"` - Target struct { - Format struct { - Type string `xml:"type,attr"` - } `xml:"format"` - Permissions struct { - Mode int `xml:"mode,omitempty"` - } `xml:"permissions,omitempty"` - Timestamps *defTimestamps `xml:"timestamps,omitempty"` - } `xml:"target"` - Allocation int `xml:"allocation"` - Capacity struct { - Unit string `xml:"unit,attr"` - Amount uint64 `xml:"chardata"` - } `xml:"capacity"` - BackingStore *defBackingStore `xml:"backingStore,omitempty"` -} - -func newDefVolume() defVolume { - volumeDef := defVolume{} - volumeDef.Target.Format.Type = "qcow2" - volumeDef.Target.Permissions.Mode = 644 - volumeDef.Capacity.Unit = "bytes" - volumeDef.Capacity.Amount = 1 - return volumeDef } // Creates a volume definition from a XML -func newDefVolumeFromXML(s string) (defVolume, error) { - var volumeDef defVolume +func newDefVolumeFromXML(s string) (libvirtxml.StorageVolume, error) { + var volumeDef libvirtxml.StorageVolume err := xml.Unmarshal([]byte(s), &volumeDef) if err != nil { - return defVolume{}, err + return libvirtxml.StorageVolume{}, err } return volumeDef, nil } -func newDefVolumeFromLibvirt(volume *libvirt.StorageVol) (defVolume, error) { +func newDefVolumeFromLibvirt(volume *libvirt.StorageVol) (libvirtxml.StorageVolume, error) { name, err := volume.GetName() if err != nil { - return defVolume{}, fmt.Errorf("could not get name for volume: %s.", err) + return libvirtxml.StorageVolume{}, fmt.Errorf("could not get name for volume: %s.", err) } volumeDefXml, err := volume.GetXMLDesc(0) if err != nil { - return defVolume{}, fmt.Errorf("could not get XML description for volume %s: %s.", name, err) + return libvirtxml.StorageVolume{}, fmt.Errorf("could not get XML description for volume %s: %s.", name, err) } volumeDef, err := newDefVolumeFromXML(volumeDefXml) if err != nil { - return defVolume{}, fmt.Errorf("could not get a volume definition from XML for %s: %s.", volumeDef.Name, err) + return libvirtxml.StorageVolume{}, fmt.Errorf("could not get a volume definition from XML for %s: %s.", volumeDef.Name, err) } return volumeDef, nil } -func newDefBackingStoreFromLibvirt(baseVolume *libvirt.StorageVol) (defBackingStore, error) { +func newDefBackingStoreFromLibvirt(baseVolume *libvirt.StorageVol) (libvirtxml.StorageVolumeBackingStore, error) { baseVolumeDef, err := newDefVolumeFromLibvirt(baseVolume) if err != nil { - return defBackingStore{}, fmt.Errorf("could not get volume: %s.", err) + return libvirtxml.StorageVolumeBackingStore{}, fmt.Errorf("could not get volume: %s.", err) } baseVolPath, err := baseVolume.GetPath() if err != nil { - return defBackingStore{}, fmt.Errorf("could not get base image path: %s", err) + return libvirtxml.StorageVolumeBackingStore{}, fmt.Errorf("could not get base image path: %s", err) + } + backingStoreDef := libvirtxml.StorageVolumeBackingStore{ + Path: baseVolPath, + Format: &libvirtxml.StorageVolumeTargetFormat{ + Type: baseVolumeDef.Target.Format.Type, + }, } - var backingStoreDef defBackingStore - backingStoreDef.Path = baseVolPath - backingStoreDef.Format.Type = baseVolumeDef.Target.Format.Type return backingStoreDef, nil } -- cgit v1.2.3