From 8da0556f63dd3c1bb15ba089cafc44cb0b986e5b Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Tue, 10 Sep 2019 18:31:03 +0200 Subject: [PATCH] kvm/cloudstack-guest-tool: Tool to query Qemu Guest Agent (#3519) Using this tool on a hypervisor admins can query KVM Instances running on that hypervisor if they have the Qemu Guest Agent installed. All System VMs have this and they can be queried. For example: $ cloudstack-guest-tool i-2-25-VM This will print some information about network and filesystem status. root@hv-138-a05-23:~# ./cloudstack-guest-tool s-11-VM --command info|jq { "network": [ { "ip-addresses": [ { "prefix": 8, "ip-address": "127.0.0.1", "ip-address-type": "ipv4" } ], "name": "lo", "hardware-address": "00:00:00:00:00:00" }, { "ip-addresses": [ { "prefix": 16, "ip-address": "169.254.242.169", "ip-address-type": "ipv4" } ], "name": "eth0", "hardware-address": "0e:00:a9:fe:f2:a9" }, ... ... "filesystem": [ { "mountpoint": "/var", "disk": [ { "bus": 0, "bus-type": "virtio", "target": 0, "unit": 0, "pci-controller": { "slot": 7, "bus": 0, "domain": 0, "function": 0 } } ], "type": "ext4", "name": "vda6" }, Signed-off-by: Wido den Hollander --- agent/bindir/cloud-guest-tool.in | 129 +++++++++++++++++++++++++++++++ debian/rules | 1 + packaging/centos63/cloud.spec | 2 + packaging/centos7/cloud.spec | 2 + 4 files changed, 134 insertions(+) create mode 100755 agent/bindir/cloud-guest-tool.in diff --git a/agent/bindir/cloud-guest-tool.in b/agent/bindir/cloud-guest-tool.in new file mode 100755 index 00000000000..c1a0b009044 --- /dev/null +++ b/agent/bindir/cloud-guest-tool.in @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# Talk to a KVM guest through Libvirt and the Qemu Guest Agent +# to retrieve information from the guest +# +# System VMs have the Qemu Guest Agent installed by default +# and should properly respond to such commands + +# +# Talk to KVM Instances through the Qemu Guest Agent +# + +import argparse +import json +import sys +import libvirt +import libvirt_qemu + +COMMANDS = ["info", "ping", "fstrim"] + + +class Libvirt: + def __init__(self, uri=None, timeout=5): + self.timeout = timeout + self.conn = libvirt.open(uri) + if not self.conn: + raise Exception('Failed to open connection to the hypervisor') + + def get_domain(self, name): + return self.conn.lookupByName(name) + + def agent_command(self, dom, cmd, flags=0, raw=False): + ret = libvirt_qemu.qemuAgentCommand(dom, json.dumps({'execute': cmd}), + self.timeout, flags) + if raw: + return ret + + return json.loads(ret)['return'] + +class GuestCommand: + def __init__(self, domain, timeout): + self.domain = domain + self.timeout = timeout + self.virt = Libvirt(timeout=self.timeout) + self.dom = self.virt.get_domain(self.domain) + + def ping(self): + result = self.virt.agent_command(self.dom, 'guest-ping') + + res = False + code = 1 + if len(result) == 0: + res = True + code = 0 + + return {'result': res}, code + + def info(self): + info = dict() + info['filesystem'] = 'guest-get-fsinfo' + info['network'] = 'guest-network-get-interfaces' + + result = dict() + for key, cmd in info.items(): + result[key] = self.virt.agent_command(self.dom, cmd) + + return result, 0 + + def fstrim(self): + result = self.virt.agent_command(self.dom, 'guest-fstrim') + + res = False + code = 1 + if len(result) > 0: + res = True + code = 0 + + return {'result': result}, code + + +def main(args): + command = args.command + + try: + guestcmd = GuestCommand(args.instance, args.timeout) + result = {'error': 'Command not implemented'} + code = 255 + + if command == 'info': + result, code = guestcmd.info() + elif command == 'ping': + result, code = guestcmd.ping() + elif command == 'fstrim': + result, code = guestcmd.fstrim() + + print(json.dumps(result)) + sys.exit(code) + except libvirt.libvirtError as exc: + print(json.dumps({'error': str(exc)})) + sys.exit(255) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='CloudStack Guest Tool') + parser.add_argument('instance', type=str, + help='Instance Name') + parser.add_argument('--command', type=str, required=False, + help='Command to execute', default='info', + choices=COMMANDS) + parser.add_argument('--timeout', type=int, required=False, + help='timeout in seconds', default=5) + args = parser.parse_args() + main(args) diff --git a/debian/rules b/debian/rules index 07f5715cbb7..59f7e6f54eb 100755 --- a/debian/rules +++ b/debian/rules @@ -52,6 +52,7 @@ override_dh_auto_install: install -D agent/target/transformed/cloud-ssh $(DESTDIR)/usr/bin/cloudstack-ssh install -D agent/target/transformed/cloudstack-agent-profile.sh $(DESTDIR)/$(SYSCONFDIR)/profile.d/cloudstack-agent-profile.sh install -D agent/target/transformed/cloudstack-agent-upgrade $(DESTDIR)/usr/bin/cloudstack-agent-upgrade + install -D agent/target/transformed/cloud-guest-tool $(DESTDIR)/usr/bin/cloudstack-guest-tool install -D agent/target/transformed/libvirtqemuhook $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 05575e00776..ee20a05b6f1 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -319,6 +319,7 @@ install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}%{_s install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent install -D agent/target/transformed/cloudstack-agent-upgrade ${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade +install -D agent/target/transformed/cloud-guest-tool ${RPM_BUILD_ROOT}%{_bindir}/%{name}-guest-tool install -D agent/target/transformed/libvirtqemuhook ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d/%{name}-agent-profile.sh @@ -562,6 +563,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %files agent %attr(0755,root,root) %{_bindir}/%{name}-setup-agent %attr(0755,root,root) %{_bindir}/%{name}-agent-upgrade +%attr(0755,root,root) %{_bindir}/%{name}-guest-tool %attr(0755,root,root) %{_bindir}/%{name}-ssh %attr(0755,root,root) %{_sysconfdir}/init.d/%{name}-agent %attr(0644,root,root) %{_sysconfdir}/profile.d/%{name}-agent-profile.sh diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec index 0643423f2b6..1acc38d1990 100644 --- a/packaging/centos7/cloud.spec +++ b/packaging/centos7/cloud.spec @@ -294,6 +294,7 @@ install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}%{_s install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent install -D agent/target/transformed/cloudstack-agent-upgrade ${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade +install -D agent/target/transformed/cloud-guest-tool ${RPM_BUILD_ROOT}%{_bindir}/%{name}-guest-tool install -D agent/target/transformed/libvirtqemuhook ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d/%{name}-agent-profile.sh @@ -510,6 +511,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %files agent %attr(0755,root,root) %{_bindir}/%{name}-setup-agent %attr(0755,root,root) %{_bindir}/%{name}-agent-upgrade +%attr(0755,root,root) %{_bindir}/%{name}-guest-tool %attr(0755,root,root) %{_bindir}/%{name}-ssh %attr(0644,root,root) %{_unitdir}/%{name}-agent.service %config(noreplace) %{_sysconfdir}/default/%{name}-agent