github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/qcow2_flexvolume.go (about) 1 /* 2 Copyright 2017 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package libvirttools 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 24 libvirtxml "github.com/libvirt/libvirt-go-xml" 25 26 "github.com/Mirantis/virtlet/pkg/metadata/types" 27 "github.com/Mirantis/virtlet/pkg/utils" 28 "github.com/Mirantis/virtlet/pkg/virt" 29 ) 30 31 const ( 32 defaultVolumeCapacity = 1024 33 defaultVolumeCapacityUnit = "MB" 34 ) 35 36 var capacityUnits = []string{ 37 // https://libvirt.org/formatstorage.html#StorageVolFirst 38 "B", "bytes", "KB", "K", "KiB", "MB", "M", "MiB", "GB", "G", 39 "GiB", "TB", "T", "TiB", "PB", "P", "PiB", "EB", "E", "EiB", 40 } 41 42 var capacityRx = regexp.MustCompile(`^\s*(\d+)\s*(\S*)\s*$`) 43 44 type qcow2VolumeOptions struct { 45 Capacity string `json:"capacity,omitempty"` 46 UUID string `json:"uuid"` 47 } 48 49 // qcow2Volume denotes a volume in QCOW2 format 50 type qcow2Volume struct { 51 volumeBase 52 capacity int 53 capacityUnit string 54 name string 55 uuid string 56 } 57 58 var _ VMVolume = &qcow2Volume{} 59 60 func newQCOW2Volume(volumeName, configPath string, config *types.VMConfig, owner volumeOwner) (VMVolume, error) { 61 var err error 62 var opts qcow2VolumeOptions 63 if err = utils.ReadJSON(configPath, &opts); err != nil { 64 return nil, fmt.Errorf("failed to parse qcow2 volume config %q: %v", configPath, err) 65 } 66 v := &qcow2Volume{ 67 volumeBase: volumeBase{config, owner}, 68 name: volumeName, 69 uuid: opts.UUID, 70 } 71 72 v.capacity, v.capacityUnit, err = parseCapacityStr(opts.Capacity) 73 if err != nil { 74 return nil, err 75 } 76 return v, nil 77 } 78 79 func (v *qcow2Volume) volumeName() string { 80 return "virtlet-" + v.config.DomainUUID + "-" + v.name 81 } 82 83 func (v *qcow2Volume) createQCOW2Volume(capacity uint64, capacityUnit string) (virt.StorageVolume, error) { 84 storagePool, err := v.owner.StoragePool() 85 if err != nil { 86 return nil, err 87 } 88 return storagePool.CreateStorageVol(&libvirtxml.StorageVolume{ 89 Name: v.volumeName(), 90 Allocation: &libvirtxml.StorageVolumeSize{Value: 0}, 91 Capacity: &libvirtxml.StorageVolumeSize{Unit: capacityUnit, Value: capacity}, 92 Target: &libvirtxml.StorageVolumeTarget{Format: &libvirtxml.StorageVolumeTargetFormat{Type: "qcow2"}}, 93 }) 94 } 95 96 func (v *qcow2Volume) IsDisk() bool { return true } 97 98 func (v *qcow2Volume) UUID() string { 99 return v.uuid 100 } 101 102 func (v *qcow2Volume) Setup() (*libvirtxml.DomainDisk, *libvirtxml.DomainFilesystem, error) { 103 vol, err := v.createQCOW2Volume(uint64(v.capacity), v.capacityUnit) 104 if err != nil { 105 return nil, nil, fmt.Errorf("error during creation of volume '%s' with virtlet description %s: %v", v.volumeName(), v.name, err) 106 } 107 108 path, err := vol.Path() 109 if err != nil { 110 return nil, nil, err 111 } 112 113 err = vol.Format() 114 if err != nil { 115 return nil, nil, err 116 } 117 118 return &libvirtxml.DomainDisk{ 119 Device: "disk", 120 Source: &libvirtxml.DomainDiskSource{File: &libvirtxml.DomainDiskSourceFile{File: path}}, 121 Driver: &libvirtxml.DomainDiskDriver{Name: "qemu", Type: "qcow2"}, 122 }, nil, nil 123 } 124 125 func (v *qcow2Volume) Teardown() error { 126 storagePool, err := v.owner.StoragePool() 127 if err != nil { 128 return err 129 } 130 return storagePool.RemoveVolumeByName(v.volumeName()) 131 } 132 133 func parseCapacityStr(capacityStr string) (int, string, error) { 134 if capacityStr == "" { 135 return defaultVolumeCapacity, defaultVolumeCapacityUnit, nil 136 } 137 138 subs := capacityRx.FindStringSubmatch(capacityStr) 139 if subs == nil { 140 return 0, "", fmt.Errorf("invalid capacity spec: %q", capacityStr) 141 } 142 capacity, err := strconv.Atoi(subs[1]) 143 if err != nil { 144 return 0, "", fmt.Errorf("invalid capacity spec: %q", capacityStr) 145 } 146 capacityUnit := subs[2] 147 if capacityUnit == "" { 148 return capacity, defaultVolumeCapacityUnit, nil 149 } 150 for _, item := range capacityUnits { 151 if item == capacityUnit { 152 return capacity, capacityUnit, nil 153 } 154 } 155 return 0, "", fmt.Errorf("invalid capacity unit: %q", capacityUnit) 156 } 157 158 func init() { 159 addFlexvolumeSource("qcow2", newQCOW2Volume) 160 } 161 162 // TODO: this file needs a test