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