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  }