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 }