github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/libvirt_storage.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  	"sync"
    22  
    23  	"github.com/golang/glog"
    24  	libvirt "github.com/libvirt/libvirt-go"
    25  	libvirtxml "github.com/libvirt/libvirt-go-xml"
    26  
    27  	"github.com/Mirantis/virtlet/pkg/diskimage"
    28  	"github.com/Mirantis/virtlet/pkg/virt"
    29  )
    30  
    31  type libvirtStorageConnection struct {
    32  	// Trying to do several storage-related operations at the same time
    33  	// may cause a race condition.
    34  	// As right now Virtlet uses just one storage pool, a single sync.Mutex
    35  	// is enough for handling the storage.
    36  	sync.Mutex
    37  	conn libvirtConnection
    38  }
    39  
    40  var _ virt.StorageConnection = &libvirtStorageConnection{}
    41  
    42  func newLibvirtStorageConnection(conn libvirtConnection) *libvirtStorageConnection {
    43  	return &libvirtStorageConnection{conn: conn}
    44  }
    45  
    46  func (sc *libvirtStorageConnection) CreateStoragePool(def *libvirtxml.StoragePool) (virt.StoragePool, error) {
    47  	xml, err := def.Marshal()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	glog.V(2).Infof("Creating storage pool:\n%s", xml)
    52  	p, err := sc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) {
    53  		return c.StoragePoolCreateXML(xml, 0)
    54  	})
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return &libvirtStoragePool{Mutex: &sc.Mutex, conn: sc.conn, p: p.(*libvirt.StoragePool)}, nil
    59  }
    60  
    61  func (sc *libvirtStorageConnection) LookupStoragePoolByName(name string) (virt.StoragePool, error) {
    62  	p, err := sc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) {
    63  		return c.LookupStoragePoolByName(name)
    64  	})
    65  	if err != nil {
    66  		libvirtErr, ok := err.(libvirt.Error)
    67  		if ok && libvirtErr.Code == libvirt.ERR_NO_STORAGE_POOL {
    68  			return nil, virt.ErrStoragePoolNotFound
    69  		}
    70  		return nil, err
    71  	}
    72  	return &libvirtStoragePool{Mutex: &sc.Mutex, conn: sc.conn, p: p.(*libvirt.StoragePool)}, nil
    73  }
    74  
    75  func (sc *libvirtStorageConnection) ListPools() ([]virt.StoragePool, error) {
    76  	pools, err := sc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) {
    77  		return c.ListAllStoragePools(0)
    78  	})
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	var r []virt.StoragePool
    83  	for _, p := range pools.([]libvirt.StoragePool) {
    84  		r = append(r, &libvirtStoragePool{Mutex: &sc.Mutex, conn: sc.conn, p: &p})
    85  	}
    86  	return r, nil
    87  }
    88  
    89  func (sc *libvirtStorageConnection) PutFiles(imagePath string, files map[string][]byte) error {
    90  	return diskimage.Put(imagePath, files)
    91  }
    92  
    93  type libvirtStoragePool struct {
    94  	*sync.Mutex
    95  	conn libvirtConnection
    96  	p    *libvirt.StoragePool
    97  }
    98  
    99  var _ virt.StoragePool = &libvirtStoragePool{}
   100  
   101  func (pool *libvirtStoragePool) CreateStorageVol(def *libvirtxml.StorageVolume) (virt.StorageVolume, error) {
   102  	pool.Lock()
   103  	defer pool.Unlock()
   104  	xml, err := def.Marshal()
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	glog.V(2).Infof("Creating storage volume:\n%s", xml)
   109  	v, err := pool.p.StorageVolCreateXML(xml, 0)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	// libvirt may report qcow2 file size as 'capacity' for
   114  	// qcow2-based volumes for some time after creating them.
   115  	// Here we work around this problem by refreshing the pool
   116  	// which invokes acquiring volume info.
   117  	if err := pool.p.Refresh(0); err != nil {
   118  		v.Delete(0)
   119  		return nil, fmt.Errorf("failed to refresh the storage pool: %v", err)
   120  	}
   121  	return &libvirtStorageVolume{Mutex: pool.Mutex, name: def.Name, v: v}, nil
   122  }
   123  
   124  func (pool *libvirtStoragePool) ListVolumes() ([]virt.StorageVolume, error) {
   125  	pool.Lock()
   126  	defer pool.Unlock()
   127  	volumes, err := pool.p.ListAllStorageVolumes(0)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	r := make([]virt.StorageVolume, len(volumes))
   132  	for n, v := range volumes {
   133  		name, err := v.GetName()
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  		// need to make a copy here
   138  		curVolume := v
   139  		r[n] = &libvirtStorageVolume{Mutex: pool.Mutex, name: name, v: &curVolume}
   140  	}
   141  	return r, nil
   142  }
   143  
   144  func (pool *libvirtStoragePool) LookupVolumeByName(name string) (virt.StorageVolume, error) {
   145  	pool.Lock()
   146  	defer pool.Unlock()
   147  	v, err := pool.p.LookupStorageVolByName(name)
   148  	if err != nil {
   149  		libvirtErr, ok := err.(libvirt.Error)
   150  		if ok && libvirtErr.Code == libvirt.ERR_NO_STORAGE_VOL {
   151  			return nil, virt.ErrStorageVolumeNotFound
   152  		}
   153  		return nil, err
   154  	}
   155  	return &libvirtStorageVolume{Mutex: pool.Mutex, name: name, v: v}, nil
   156  }
   157  
   158  func (pool *libvirtStoragePool) RemoveVolumeByName(name string) error {
   159  	vol, err := pool.LookupVolumeByName(name)
   160  	switch {
   161  	case err == virt.ErrStorageVolumeNotFound:
   162  		return nil
   163  	case err != nil:
   164  		return err
   165  	default:
   166  		return vol.Remove()
   167  	}
   168  }
   169  
   170  func (pool *libvirtStoragePool) XML() (*libvirtxml.StoragePool, error) {
   171  	desc, err := pool.p.GetXMLDesc(libvirt.STORAGE_XML_INACTIVE)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	var p libvirtxml.StoragePool
   176  	if err := p.Unmarshal(desc); err != nil {
   177  		return nil, fmt.Errorf("error unmarshalling storage pool definition: %v", err)
   178  	}
   179  	return &p, nil
   180  }
   181  
   182  type libvirtStorageVolume struct {
   183  	*sync.Mutex
   184  	name string
   185  	v    *libvirt.StorageVol
   186  }
   187  
   188  var _ virt.StorageVolume = &libvirtStorageVolume{}
   189  
   190  func (volume *libvirtStorageVolume) Name() string {
   191  	return volume.name
   192  }
   193  
   194  func (volume *libvirtStorageVolume) Size() (uint64, error) {
   195  	volume.Lock()
   196  	defer volume.Unlock()
   197  	info, err := volume.v.GetInfo()
   198  	if err != nil {
   199  		return 0, err
   200  	}
   201  	return info.Capacity, nil
   202  }
   203  
   204  func (volume *libvirtStorageVolume) Path() (string, error) {
   205  	volume.Lock()
   206  	defer volume.Unlock()
   207  	return volume.v.GetPath()
   208  }
   209  
   210  func (volume *libvirtStorageVolume) Remove() error {
   211  	volume.Lock()
   212  	defer volume.Unlock()
   213  	return volume.v.Delete(0)
   214  }
   215  
   216  func (volume *libvirtStorageVolume) Format() error {
   217  	volume.Lock()
   218  	defer volume.Unlock()
   219  	volPath, err := volume.v.GetPath()
   220  	if err != nil {
   221  		return fmt.Errorf("can't get volume path: %v", err)
   222  	}
   223  	return diskimage.FormatDisk(volPath)
   224  }
   225  
   226  func (volume *libvirtStorageVolume) XML() (*libvirtxml.StorageVolume, error) {
   227  	desc, err := volume.v.GetXMLDesc(0)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	var v libvirtxml.StorageVolume
   232  	if err := v.Unmarshal(desc); err != nil {
   233  		return nil, fmt.Errorf("error unmarshalling storage volume definition: %v", err)
   234  	}
   235  	return &v, nil
   236  }