github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/libvirt_domain.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 22 "github.com/golang/glog" 23 libvirt "github.com/libvirt/libvirt-go" 24 libvirtxml "github.com/libvirt/libvirt-go-xml" 25 26 "github.com/Mirantis/virtlet/pkg/virt" 27 ) 28 29 type libvirtDomainConnection struct { 30 conn libvirtConnection 31 } 32 33 var _ virt.DomainConnection = &libvirtDomainConnection{} 34 35 func newLibvirtDomainConnection(conn libvirtConnection) *libvirtDomainConnection { 36 return &libvirtDomainConnection{conn: conn} 37 } 38 39 func (dc *libvirtDomainConnection) DefineDomain(def *libvirtxml.Domain) (virt.Domain, error) { 40 xml, err := def.Marshal() 41 if err != nil { 42 return nil, err 43 } 44 glog.V(2).Infof("Defining domain:\n%s", xml) 45 d, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 46 return c.DomainDefineXML(xml) 47 }) 48 if err != nil { 49 return nil, err 50 } 51 return &libvirtDomain{d.(*libvirt.Domain)}, nil 52 } 53 54 func (dc *libvirtDomainConnection) ListDomains() ([]virt.Domain, error) { 55 domains, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 56 return c.ListAllDomains(0) 57 }) 58 if err != nil { 59 return nil, err 60 } 61 var r []virt.Domain 62 for _, d := range domains.([]libvirt.Domain) { 63 // need to make a copy here 64 curDomain := d 65 r = append(r, &libvirtDomain{&curDomain}) 66 } 67 return r, nil 68 } 69 70 func (dc *libvirtDomainConnection) LookupDomainByName(name string) (virt.Domain, error) { 71 d, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 72 return c.LookupDomainByName(name) 73 }) 74 if err != nil { 75 libvirtErr, ok := err.(libvirt.Error) 76 if ok && libvirtErr.Code == libvirt.ERR_NO_DOMAIN { 77 return nil, virt.ErrDomainNotFound 78 } 79 return nil, err 80 } 81 return &libvirtDomain{d.(*libvirt.Domain)}, nil 82 } 83 84 func (dc *libvirtDomainConnection) LookupDomainByUUIDString(uuid string) (virt.Domain, error) { 85 d, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 86 return c.LookupDomainByUUIDString(uuid) 87 }) 88 if err != nil { 89 libvirtErr, ok := err.(libvirt.Error) 90 if ok && libvirtErr.Code == libvirt.ERR_NO_DOMAIN { 91 return nil, virt.ErrDomainNotFound 92 } 93 return nil, err 94 } 95 return &libvirtDomain{d.(*libvirt.Domain)}, nil 96 } 97 98 func (dc *libvirtDomainConnection) DefineSecret(def *libvirtxml.Secret) (virt.Secret, error) { 99 xml, err := def.Marshal() 100 if err != nil { 101 return nil, err 102 } 103 secret, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 104 return c.SecretDefineXML(xml, 0) 105 }) 106 if err != nil { 107 return nil, err 108 } 109 return &libvirtSecret{secret.(*libvirt.Secret)}, nil 110 } 111 112 func (dc *libvirtDomainConnection) LookupSecretByUUIDString(uuid string) (virt.Secret, error) { 113 secret, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 114 return c.LookupSecretByUUIDString(uuid) 115 }) 116 if err != nil { 117 libvirtErr, ok := err.(libvirt.Error) 118 if ok && libvirtErr.Code == libvirt.ERR_NO_SECRET { 119 return nil, virt.ErrSecretNotFound 120 } 121 return nil, err 122 } 123 return &libvirtSecret{secret.(*libvirt.Secret)}, nil 124 } 125 126 func (dc *libvirtDomainConnection) LookupSecretByUsageName(usageType string, usageName string) (virt.Secret, error) { 127 128 if usageType != "ceph" { 129 return nil, fmt.Errorf("unsupported type %q for secret with usage name: %q", usageType, usageName) 130 } 131 132 secret, err := dc.conn.invoke(func(c *libvirt.Connect) (interface{}, error) { 133 return c.LookupSecretByUsage(libvirt.SECRET_USAGE_TYPE_CEPH, usageName) 134 }) 135 if err != nil { 136 libvirtErr, ok := err.(libvirt.Error) 137 if ok && libvirtErr.Code == libvirt.ERR_NO_SECRET { 138 return nil, virt.ErrSecretNotFound 139 } 140 return nil, err 141 } 142 return &libvirtSecret{secret.(*libvirt.Secret)}, nil 143 } 144 145 type libvirtDomain struct { 146 d *libvirt.Domain 147 } 148 149 var _ virt.Domain = &libvirtDomain{} 150 151 func (domain *libvirtDomain) Create() error { 152 return domain.d.Create() 153 } 154 155 func (domain *libvirtDomain) Destroy() error { 156 return domain.d.Destroy() 157 } 158 159 func (domain *libvirtDomain) Undefine() error { 160 return domain.d.Undefine() 161 } 162 163 func (domain *libvirtDomain) Shutdown() error { 164 return domain.d.Shutdown() 165 } 166 167 func (domain *libvirtDomain) State() (virt.DomainState, error) { 168 di, err := domain.d.GetInfo() 169 if err != nil { 170 return virt.DomainStateNoState, err 171 } 172 switch di.State { 173 case libvirt.DOMAIN_NOSTATE: 174 return virt.DomainStateNoState, nil 175 case libvirt.DOMAIN_RUNNING: 176 return virt.DomainStateRunning, nil 177 case libvirt.DOMAIN_BLOCKED: 178 return virt.DomainStateBlocked, nil 179 case libvirt.DOMAIN_PAUSED: 180 return virt.DomainStatePaused, nil 181 case libvirt.DOMAIN_SHUTDOWN: 182 return virt.DomainStateShutdown, nil 183 case libvirt.DOMAIN_CRASHED: 184 return virt.DomainStateCrashed, nil 185 case libvirt.DOMAIN_PMSUSPENDED: 186 return virt.DomainStatePMSuspended, nil 187 case libvirt.DOMAIN_SHUTOFF: 188 return virt.DomainStateShutoff, nil 189 default: 190 return virt.DomainStateNoState, fmt.Errorf("bad domain state %v", di.State) 191 } 192 } 193 194 func (domain *libvirtDomain) UUIDString() (string, error) { 195 return domain.d.GetUUIDString() 196 } 197 198 func (domain *libvirtDomain) Name() (string, error) { 199 return domain.d.GetName() 200 } 201 202 func (domain *libvirtDomain) XML() (*libvirtxml.Domain, error) { 203 desc, err := domain.d.GetXMLDesc(libvirt.DOMAIN_XML_INACTIVE) 204 if err != nil { 205 return nil, err 206 } 207 var d libvirtxml.Domain 208 if err := d.Unmarshal(desc); err != nil { 209 return nil, fmt.Errorf("error unmarshalling domain definition: %v", err) 210 } 211 return &d, nil 212 } 213 214 // GetRSS returns RSS used by VM in bytes 215 func (domain *libvirtDomain) GetRSS() (uint64, error) { 216 stats, err := domain.d.MemoryStats(uint32(libvirt.DOMAIN_MEMORY_STAT_LAST), 0) 217 if err != nil { 218 return 0, err 219 } 220 for _, stat := range stats { 221 if stat.Tag == int32(libvirt.DOMAIN_MEMORY_STAT_RSS) { 222 return stat.Val * 1024, nil 223 } 224 } 225 return 0, fmt.Errorf("rss not found in memory stats") 226 } 227 228 // GetCPUTime returns cpu time used by VM in nanoseconds per core 229 func (domain *libvirtDomain) GetCPUTime() (uint64, error) { 230 // all vcpus as a single value 231 stats, err := domain.d.GetCPUStats(-1, 1, 0) 232 if err != nil { 233 return 0, err 234 } 235 if len(stats) != 1 { 236 return 0, fmt.Errorf("domain.GetCPUStats returned %d values while single one was expected", len(stats)) 237 } 238 if !stats[0].CpuTimeSet { 239 return 0, fmt.Errorf("domain.CpuTime not found in memory stats") 240 } 241 return stats[0].CpuTime, nil 242 } 243 244 type libvirtSecret struct { 245 s *libvirt.Secret 246 } 247 248 func (secret *libvirtSecret) SetValue(value []byte) error { 249 return secret.s.SetValue(value, 0) 250 } 251 252 func (secret *libvirtSecret) Remove() error { 253 return secret.s.Undefine() 254 }