. * * @package LibreNMS * @link http://librenms.org * @copyright 2023 Tony Murray * @author Tony Murray */ namespace LibreNMS\OS\Traits; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use LibreNMS\Config; use LibreNMS\Enum\PowerState; trait VminfoLibvirt { public function discoverVminfo(): Collection { echo 'LibVirt VM: '; if (! Config::get('enable_libvirt')) { echo 'not configured'; return new Collection; } $vms = new Collection; $ssh_ok = 0; $userHostname = $this->getDevice()->hostname; if (Config::has('libvirt_username')) { $userHostname = Config::get('libvirt_username') . '@' . $userHostname; } foreach (Config::get('libvirt_protocols') as $method) { if (Str::contains($method, 'qemu')) { $uri = $method . '://' . $userHostname . '/system'; } else { $uri = $method . '://' . $userHostname; } if (Str::contains($method, 'ssh') && ! $ssh_ok) { // Check if we are using SSH if we can log in without password - without blocking the discovery // Also automatically add the host key so discovery doesn't block on the yes/no question, and run echo so we don't get stuck in a remote shell ;-) exec('ssh -o "StrictHostKeyChecking no" -o "PreferredAuthentications publickey" -o "IdentitiesOnly yes" ' . $userHostname . ' echo -e', $out, $ret); if ($ret != 255) { $ssh_ok = 1; } } if ($ssh_ok || ! Str::contains($method, 'ssh')) { // Fetch virtual machine list unset($domlist); exec(Config::get('virsh') . ' -rc ' . $uri . ' list', $domlist); foreach ($domlist as $dom) { [$dom_id] = explode(' ', trim($dom), 2); if (is_numeric($dom_id)) { // Fetch the Virtual Machine information. unset($vm_info_array); exec(Config::get('virsh') . ' -rc ' . $uri . ' dumpxml ' . $dom_id, $vm_info_array); // Example xml: // // moo.example.com // 48cf6378-6fd5-4610-0611-63dd4b31cfd6 // 1048576 // 1048576 // 8 // // hvm // // // // // (...) // See spec at https://libvirt.org/formatdomain.html // Convert array to string $vm_info_xml = implode($vm_info_array); $xml = simplexml_load_string(' ' . $vm_info_xml); Log::debug($xml); // libvirt does not supply this exec(Config::get('virsh') . ' -rc ' . $uri . ' domstate ' . $dom_id, $vm_state); $vmwVmState = PowerState::STATES[strtolower($vm_state[0])] ?? PowerState::UNKNOWN; $vmwVmMemSize = $xml->memory; // Convert memory size to MiB switch ($xml->memory['unit']) { case 'T': case 'TiB': $vmwVmMemSize = $xml->memory * 1048576; break; case 'TB': $vmwVmMemSize = $xml->memory * 1000000; break; case 'G': case 'GiB': $vmwVmMemSize = $xml->memory * 1024; break; case 'GB': $vmwVmMemSize = $xml->memory * 1000; break; case 'M': case 'MiB': break; case 'MB': $vmwVmMemSize = $xml->memory * 1000000 / 1048576; break; case 'KB': $vmwVmMemSize = $xml->memory / 1000; break; case 'b': case 'bytes': $vmwVmMemSize = $xml->memory / 1048576; break; default: // KiB or k or no value $vmwVmMemSize = $xml->memory / 1024; break; } // Save the discovered Virtual Machine. $vms->push(new \App\Models\Vminfo([ 'vmtype' => 'libvirt', 'vmwVmVMID' => $dom_id, 'vmwVmState' => $vmwVmState, 'vmwVmGuestOS' => '', 'vmwVmDisplayName' => $xml->name, 'vmwVmMemSize' => $vmwVmMemSize, 'vmwVmCpus' => $xml->vcpu['current'] ?? $xml->vcpu, ])); } } } // If we found VMs, don't cycle the other protocols anymore. if ($vms->isNotEmpty()) { break; } } return $vms; } }