Skip to content
Permalink
Browse files

Fetch ip addresses with qemu guest agent if possible

  • Loading branch information...
subuk committed Sep 8, 2019
1 parent cd84e7f commit 0ab5d9b5f49b2c098593fbf154d1361e187caa91
@@ -156,6 +156,14 @@ func (service *Service) VirtualMachineGetConsoleStream(id string) (VirtualMachin
return service.virt.GetConsoleStream(id)
}

func (service *Service) VirtualMachineDisableGuestAgent(id string) error {
return service.virt.DisableGuestAgent(id)
}

func (service *Service) VirtualMachineEnableGuestAgent(id string) error {
return service.virt.EnableGuestAgent(id)
}

func (service *Service) VolumeList() ([]*Volume, error) {
return service.vol.List()
}
@@ -9,6 +9,8 @@ type VirtualMachineRepository interface {
DetachVolume(id, path string) error
AttachInterface(id, network, mac, model string, accessVlan uint, netType NetworkType) (*VirtualMachineAttachedInterface, error)
DetachInterface(id, mac string) error
EnableGuestAgent(id string) error
DisableGuestAgent(id string) error
GetConsoleStream(id string) (VirtualMachineConsoleStream, error)
Poweroff(id string) error
Reboot(id string) error
@@ -61,6 +63,7 @@ type VirtualMachine struct {
Volumes []*VirtualMachineAttachedVolume
Config *VirtualMachineConfig
Cpupin *VirtualMachineCpuPin
GuestAgent bool
}

func (vm *VirtualMachine) AttachmentInfo(path string) *VirtualMachineAttachedVolume {
@@ -124,5 +124,10 @@ func VirtualMachineFromDomainConfig(domainConfig *libvirtxml.Domain, domainInfo
volume := VirtualMachineAttachedVolumeFromDomainDiskConfig(diskConfig)
vm.Volumes = append(vm.Volumes, volume)
}
for _, channel := range domainConfig.Devices.Channels {
if channel.Target != nil && channel.Target.VirtIO != nil && channel.Target.VirtIO.Name == "org.qemu.guest_agent.0" {
vm.GuestAgent = true
}
}
return vm, nil
}
@@ -219,10 +219,15 @@ func (repo *VirtualMachineRepository) domainToVm(conn *libvirt.Connect, domain *
}
if vm.IsRunning() {
for _, attachedInterface := range vm.Interfaces {
virDomainIfaces, err := domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_ARP)
virDomainIfaces, err := domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
if err != nil {
repo.logger.Info().Err(err).Msg("cannot get interfaces addresses")
continue
repo.logger.Debug().Str("vm", vm.Id).Err(err).Msg("cannot get interfaces addresses with qemu guest agent")
virDomainIfacesLease, err := domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)
if err != nil {
repo.logger.Debug().Str("vm", vm.Id).Err(err).Msg("cannot get interfaces addresses with dhcp leases")
continue
}
virDomainIfaces = virDomainIfacesLease
}
for _, virDomainIface := range virDomainIfaces {
if virDomainIface.Hwaddr == attachedInterface.Mac {
@@ -450,7 +455,10 @@ func (repo *VirtualMachineRepository) Create(id string, arch compute.Arch, vcpus

virDomainConfig.Devices = &libvirtxml.DomainDeviceList{Emulator: domCapsConfig.Path}
virDomainConfig.Devices.Consoles = append(virDomainConfig.Devices.Consoles, libvirtxml.DomainConsole{})

virDomainConfig.Devices.Channels = append(virDomainConfig.Devices.Channels, libvirtxml.DomainChannel{
Protocol: &libvirtxml.DomainChardevProtocol{Type: "unix"},
Target: &libvirtxml.DomainChannelTarget{VirtIO: &libvirtxml.DomainChannelTargetVirtIO{Name: "org.qemu.guest_agent.0"}},
})
configVolume, err := repo.generateConfigDrive(conn, virDomainConfig, config)
if err != nil {
return nil, util.NewError(err, "cannot generate config drive")
@@ -588,6 +596,92 @@ func (repo *VirtualMachineRepository) Start(id string) error {
return domain.Create()
}

func (repo *VirtualMachineRepository) EnableGuestAgent(id string) error {
conn, err := repo.pool.Acquire()
if err != nil {
return util.NewError(err, "cannot acquire connection")
}
defer repo.pool.Release(conn)

virDomain, err := conn.LookupDomainByName(id)
if err != nil {
return util.NewError(err, "domain lookup failed")
}
running, err := virDomain.IsActive()
if err != nil {
return util.NewError(err, "cannot check if domain is running")
}
if running {
return fmt.Errorf("domain must be stopped")
}
virDomainXml, err := virDomain.GetXMLDesc(libvirt.DOMAIN_XML_MIGRATABLE)
if err != nil {
return util.NewError(err, "cannot get domain xml")
}
virDomainConfig := &libvirtxml.Domain{}
if err := virDomainConfig.Unmarshal(virDomainXml); err != nil {
return util.NewError(err, "cannot parse domain xml")
}
virDomainConfig.Devices.Channels = append(virDomainConfig.Devices.Channels, libvirtxml.DomainChannel{
Protocol: &libvirtxml.DomainChardevProtocol{Type: "unix"},
Target: &libvirtxml.DomainChannelTarget{VirtIO: &libvirtxml.DomainChannelTargetVirtIO{Name: "org.qemu.guest_agent.0"}},
Source: &libvirtxml.DomainChardevSource{UNIX: &libvirtxml.DomainChardevSourceUNIX{}},
})
virDomainXml, err = virDomainConfig.Marshal()
if err != nil {
return util.NewError(err, "cannot create domain xml")
}
if _, err := conn.DomainDefineXML(virDomainXml); err != nil {
return util.NewError(err, "cannot update domain xml")
}
return nil
}

func (repo *VirtualMachineRepository) DisableGuestAgent(id string) error {
conn, err := repo.pool.Acquire()
if err != nil {
return util.NewError(err, "cannot acquire connection")
}
defer repo.pool.Release(conn)

virDomain, err := conn.LookupDomainByName(id)
if err != nil {
return util.NewError(err, "domain lookup failed")
}
running, err := virDomain.IsActive()
if err != nil {
return util.NewError(err, "cannot check if domain is running")
}
if running {
return fmt.Errorf("domain must be stopped")
}
virDomainXml, err := virDomain.GetXMLDesc(libvirt.DOMAIN_XML_MIGRATABLE)
if err != nil {
return util.NewError(err, "cannot get domain xml")
}
virDomainConfig := &libvirtxml.Domain{}
if err := virDomainConfig.Unmarshal(virDomainXml); err != nil {
return util.NewError(err, "cannot parse domain xml")
}
newChannels := []libvirtxml.DomainChannel{}
for _, channel := range virDomainConfig.Devices.Channels {
if channel.Target != nil && channel.Target.VirtIO != nil && channel.Target.VirtIO.Name == "org.qemu.guest_agent.0" {
continue
}
newChannels = append(newChannels, channel)
}
virDomainConfig.Devices.Channels = newChannels

virDomainXml, err = virDomainConfig.Marshal()
if err != nil {
return util.NewError(err, "cannot create domain xml")
}
if _, err := conn.DomainDefineXML(virDomainXml); err != nil {
return util.NewError(err, "cannot update domain xml")
}
return nil
}

func (repo *VirtualMachineRepository) AttachVolume(id, path string, typ compute.VolumeType, format compute.VolumeFormat, device compute.DeviceType) (*compute.VirtualMachineAttachedVolume, error) {
conn, err := repo.pool.Acquire()
if err != nil {
@@ -18,6 +18,7 @@ <h1>{{ .Vm.Id }}</h1>
<div class="media-body">
<p class="text-muted">
{{ if .Vm.Config }}Configured<br>{{ end }}
{{ if .Vm.GuestAgent }}Guest agent integration enabled<br>{{ end }}
{{ .Vm.MemoryMiB }}MiB RAM, {{ .Vm.VCpus }} CPU<br>
{{ .Vm.Arch }}<br>
{{ if .Vm.Cpupin }}
@@ -33,7 +34,6 @@ <h1>{{ .Vm.Id }}</h1>

<div class="col-md-5 text-right">
<p>

{{ if .Vm.IsRunning }}
<a class="btn btn-primary" href="{{ Url "virtual-machine-console-show" "id" .Vm.Id }}">Console</a>
<a class="btn btn-primary" href="{{ Url "virtual-machine-state-form" "id" .Vm.Id "action" "poweroff" }}">Power Off</a>
@@ -44,7 +44,17 @@ <h1>{{ .Vm.Id }}</h1>
<a class="btn btn-danger" href="{{ Url "virtual-machine-delete" "id" .Vm.Id }}">Remove</a>
</p>
<p>
Status: <b>{{ .Vm.State.String | Capitalize }}</b>
{{ if not .Vm.IsRunning }}
{{ if .Vm.GuestAgent }}
<form class="float-right form-inline JS-ReactiveForm" method="post" action="{{ Url "virtual-machine-disable-guest-agent-form" "id" .Vm.Id }}">{{ CSRFField $.Request }}
<button {{ if .Vm.IsRunning }}disabled title="Please shutdown vm before"{{ end }} type="submit" class="btn btn-primary mb-2">Disable Guest Agent</button>
</form>
{{ else }}
<form class="float-right form-inline JS-ReactiveForm" method="post" action="{{ Url "virtual-machine-enable-guest-agent-form" "id" .Vm.Id }}">{{ CSRFField $.Request }}
<button {{ if .Vm.IsRunning }}disabled title="Please shutdown vm before"{{ end }} type="submit" class="btn btn-primary mb-2">Enable Guest Agent</button>
</form>
{{ end }}
{{ end }}
</p>
</div>
</div>
@@ -101,7 +111,7 @@ <h5 class="card-title">Interface {{ .Type }} {{ .Model }}</h5>
Vlan: {{ .AccessVlan }}<br>
{{ end }}
{{ if gt (len .IpAddressList) 0 }}
IP: {{ .IpAddressList | Join " " }}
IP: {{ .IpAddressList | Join "," }}
{{ end }}
</p>
<form method="post" action="{{ Url "virtual-machine-detach-interface" "id" $.Vm.Id }}">{{ CSRFField $.Request }}
@@ -180,6 +180,8 @@ func New(cfg *config.Config, logger zerolog.Logger, compute *libcompute.Service)
router.HandleFunc("/machines/{id}/detach-interface/", env.authenticated(env.VirtualMachineDetachInterfaceFormProcess)).Methods("POST").Name("virtual-machine-detach-interface")
router.HandleFunc("/machines/{id}/set-state/{action}/", env.authenticated(env.VirtualMachineStateSetFormProcess)).Name("virtual-machine-state-form").Methods("POST")
router.HandleFunc("/machines/{id}/set-state/{action}/", env.authenticated(env.VirtualMachineStateSetFormShow)).Name("virtual-machine-state-form")
router.HandleFunc("/machines/{id}/disable-guest-agent/", env.authenticated(env.VirtualMachineDisableGuestAgentFormProcess)).Methods("POST").Name("virtual-machine-disable-guest-agent-form")
router.HandleFunc("/machines/{id}/enable-guest-agent/", env.authenticated(env.VirtualMachineEnableGuestAgentFormProcess)).Methods("POST").Name("virtual-machine-enable-guest-agent-form")
router.HandleFunc("/machines/{id}/delete/", env.authenticated(env.VirtualMachineDeleteFormProcess)).Name("virtual-machine-delete").Methods("POST")
router.HandleFunc("/machines/{id}/delete/", env.authenticated(env.VirtualMachineDeleteFormShow)).Name("virtual-machine-delete")

@@ -380,3 +380,23 @@ func (env *Environ) VirtualMachineConsoleWS(rw http.ResponseWriter, req *http.Re
}
}
}

func (env *Environ) VirtualMachineEnableGuestAgentFormProcess(rw http.ResponseWriter, req *http.Request) {
urlvars := mux.Vars(req)
if err := env.compute.VirtualMachineEnableGuestAgent(urlvars["id"]); err != nil {
env.error(rw, req, err, "cannot enable guest agent", http.StatusInternalServerError)
return
}
redirectUrl := env.url("virtual-machine-detail", "id", urlvars["id"])
http.Redirect(rw, req, redirectUrl.Path, http.StatusFound)
}

func (env *Environ) VirtualMachineDisableGuestAgentFormProcess(rw http.ResponseWriter, req *http.Request) {
urlvars := mux.Vars(req)
if err := env.compute.VirtualMachineDisableGuestAgent(urlvars["id"]); err != nil {
env.error(rw, req, err, "cannot disable guest agent", http.StatusInternalServerError)
return
}
redirectUrl := env.url("virtual-machine-detail", "id", urlvars["id"])
http.Redirect(rw, req, redirectUrl.Path, http.StatusFound)
}

0 comments on commit 0ab5d9b

Please sign in to comment.
You can’t perform that action at this time.