From 62ff96fbd72a1cac345d8fa6a4d7f9f5c8c5b9ea Mon Sep 17 00:00:00 2001 From: Francois Menabe Date: Mon, 12 Jan 2015 18:44:10 +0100 Subject: [PATCH 001/167] Correct a bug in 'list_domains' method for old virsh version. --- kvm.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/kvm.py b/kvm.py index 5da5aec..463b4f6 100644 --- a/kvm.py +++ b/kvm.py @@ -266,13 +266,17 @@ def list_domains(self, **kwargs): domains = {} with self.set_controls(parse=True): stdout = self.virsh('list', **virsh_opts) - elts = [elt.lower() for elt in stdout[0].split()] + for line in stdout[2:]: - values = [elt.strip() for elt in line.split(' ') if elt] - domain = dict(zip(elts, values)) - if states and domain['state'] not in states: - continue - domains.setdefault(domain.pop('name'), domain) + domid, name, state, *params = line.split() + # Manage state in two words. + if state == 'shut': + state += ' %s' % params.pop(0) + domain = {'id': int(domid) if domid != '-' else -1, + 'state': state} + if 'title' in kwargs: + domain['title'] = ' '.join(params) if params else '' + domains[name] = domain return domains From aac05b3799430c9de6c0c9944d716b5d29adb895 Mon Sep 17 00:00:00 2001 From: Francois Menabe Date: Mon, 12 Jan 2015 18:46:59 +0100 Subject: [PATCH 002/167] Rename the function for generating a dict from a XML document and orderize the result. --- kvm.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/kvm.py b/kvm.py index 463b4f6..fce0759 100644 --- a/kvm.py +++ b/kvm.py @@ -6,7 +6,8 @@ import string import weakref import unix -from lxml import etree +import lxml.etree as etree +from collections import OrderedDict import sys SELF = sys.modules[__name__] @@ -97,7 +98,7 @@ def gen_mac(): ''.join([random.choice(_CHOICES) for _ in range(0, 2)]))) -def _xml_to_dict(elt): +def from_xml(elt): """Recursive function that transform an XML element to a dictionnary. **elt** must be of type ``lxml.etree.Element``.""" tag = elt.tag @@ -106,18 +107,19 @@ def _xml_to_dict(elt): childs = elt.getchildren() if not attrs and not childs and not text: - return {tag: True} + value = True elif not attrs and not childs and text: - return {tag: text} + value = text elif attrs and not childs: child = {'@%s' % attr: value for attr, value in attrs} if text: child['#text'] = text - return {tag: child} + value = child elif childs: - elts = {'@%s' % attr: value for attr, value in attrs} if attrs else {} + elts = (OrderedDict(('@%s' % attr, value) for attr, value in attrs) + if attrs else OrderedDict()) for child in childs: - child = _xml_to_dict(child) + child = from_xml(child) child_tag = list(child.keys())[0] if child_tag in elts: if not isinstance(elts[child_tag], list): @@ -125,7 +127,11 @@ def _xml_to_dict(elt): elts[child_tag].append(child[child_tag]) else: elts.update(child) - return {tag: elts} + value = elts + + result = OrderedDict() + result[tag] = value + return result def __str_to_dict(string): From 3e56fe3542e6bb444851b1d32572f6f03f646787 Mon Sep 17 00:00:00 2001 From: Francois Menabe Date: Mon, 12 Jan 2015 18:47:55 +0100 Subject: [PATCH 003/167] Add a function for converting a dict to an XML document. --- kvm.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kvm.py b/kvm.py index fce0759..63afa4f 100644 --- a/kvm.py +++ b/kvm.py @@ -134,6 +134,29 @@ def from_xml(elt): return result +def to_xml(tag_name, conf): + tag = etree.Element(tag_name) + for elt, value in conf.items(): + if elt.startswith('@'): + tag.attrib[elt[1:]] = value + elif elt == '#text': + tag.text = value + elif isinstance(value, dict): + tag.append(to_xml(elt, value)) + elif isinstance(value, list): + print(tag_name, elt, value) + for child in value: + tag.append(to_xml(elt, child)) + elif isinstance(value, bool): + tag.append(etree.Element(elt)) + continue + else: + child = etree.Element(elt) + child.text = value + tag.append(child) + return tag + + def __str_to_dict(string): def format_key(key): return (key.strip().lower() From 01454ac1dd4b19598f180705fbeba3d1a475dfa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 6 Mar 2015 16:07:02 +0100 Subject: [PATCH 004/167] Release version 0.1 with initial version before merging branch 'rewrite'. --- README.md | 5 ++++- setup.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a09e944..67e3747 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -The purpose of this module is to manage KVM hosts (starting/stopping/destroying VM, createing/resizing disks, ...). It is just the definition of many basic commands of 'virsh' and 'qemu'. It use the module 'python-unix' and class decorators for flexibility and simplicity. +The purpose of this module is to manage KVM hosts (starting/stopping/destroying +VM, createing/resizing disks, ...). It is just the definition of many basic +commands of 'virsh' and 'qemu'. It use the module 'python-unix' and class +decorators for flexibility and simplicity. Better explications are examples ^^: ``` diff --git a/setup.py b/setup.py index a1898dd..a3d44db 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from distutils.core import setup setup ( - name='Python remote KVM manager', + name='kvm', version='0.1', author='François Ménabé', author_email='francois.menabe@gmail.com', From 47ca6720dfddc75e56e2bdbb657de0f815b43190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:25:42 +0100 Subject: [PATCH 005/167] Change visibility of some constants and functions. --- kvm.py | 110 +++++++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/kvm.py b/kvm.py index 63afa4f..671d39c 100644 --- a/kvm.py +++ b/kvm.py @@ -14,12 +14,54 @@ # Controls. -CONTROLS = {'parse': False} -unix.CONTROLS.update(CONTROLS) +_CONTROLS = {'parse': False} +unix._CONTROLS.update(_CONTROLS) # Characters in generating strings. _CHOICES = string.ascii_letters[:6] + string.digits +_ITEM_RE = re.compile('^.IX (?P\w+) "(?P.*)"$') + +_MAPPING = {'hypervisor': {'version': {'type': 'dict'}, + 'sysinfo': {'type': 'dict'}, + 'nodeinfo': {'type': 'dict'}, + 'nodecpumap': {'type': 'dict'}, + 'nodecpustats': {'type': 'dict'}, + 'nodememstats': {'type': 'dict'}, + 'nodesuspend': {'type': 'none'}, + 'node_memory_tune': {'type': 'none'}, + 'capabilities': {'type': 'xml', + 'key': 'capabilities'}, + 'domcapabilities': {'type': 'xml', + 'key': 'domainCapabilities'}, + 'freecell': {'type': 'dict'}, + 'freepages': {'type': 'dict'}, + 'allocpages': {'type': 'none'}}, + 'domain': {'autostart': {'type': 'none'}, + 'inject-nmi': {'type': 'none'}, + 'desc': {'type': 'dict'}, + 'destroy': {'type': 'none'}, + 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, + 'display': {'cmd': 'domdisplay', 'type': 'str'}, + 'info': {'cmd': 'dominfo', 'type': 'dict'}, + 'uuid': {'cmd': 'domuuid', 'type': 'str'}, + 'id': {'cmd': 'domid', 'type': 'str'}, + 'name': {'cmd': 'domname', 'type': 'str'}, + 'state': {'cmd': 'domstate', 'type': 'str'}, + 'control': {'cmd': 'domcontrol', 'type': 'str'}, + 'dumpxml': {'type': 'xml', 'key': 'domain'}, + 'reboot': {'type': 'none'}, + 'reset': {'type': 'none'}, + 'screenshot': {'type': 'none'}, + 'shutdown': {'type': 'none'}, + 'start': {'type': 'none'}, + 'suspend': {'type': 'none'}, + 'resume': {'type': 'none'}, + 'ttyconsole': {'type': 'str'}, + 'undefine': {'type': 'none'} + }} + + RUNNING = 'running' IDLE = 'idle' PAUSED = 'paused' @@ -29,54 +71,6 @@ DYING = 'dying' SUSPENDED = 'pmsuspended' -MAPPING = {'hypervisor': {'version': {'type': 'dict'}, - 'sysinfo': {'type': 'dict'}, - 'nodeinfo': {'type': 'dict'}, - 'nodecpumap': {'type': 'dict'}, - 'nodecpustats': {'type': 'dict'}, - 'nodememstats': {'type': 'dict'}, - 'nodesuspend': {'type': 'none'}, - 'node_memory_tune': {'type': 'none'}, - 'capabilities': {'type': 'xml', - 'key': 'capabilities'}, - 'domcapabilities': {'type': 'xml', - 'key': 'domainCapabilities'}, - 'freecell': {'type': 'dict'}, - 'freepages': {'type': 'dict'}, - 'allocpages': {'type': 'none'}}, - 'domain': {'autostart': {'type': 'none'}, - 'inject-nmi': {'type': 'none'}, - 'desc': {'type': 'dict'}, - 'destroy': {'type': 'none'}, - 'blkinfo': {'type': 'dict', - 'cmd': 'domblkinfo'}, - 'display': {'type': 'str', - 'cmd': 'domdisplay'}, - 'info': {'type': 'dict', - 'cmd': 'dominfo'}, - 'uuid': {'type': 'str', - 'cmd': 'domuuid'}, - 'id': {'type': 'str', - 'cmd': 'domid'}, - 'name': {'type': 'str', - 'cmd': 'domname'}, - 'state': {'type': 'str', - 'cmd': 'domstate'}, - 'control': {'type': 'str', - 'cmd': 'domcontrol'}, - 'dumpxml': {'type': 'xml', - 'key': 'domain'}, - 'reboot': {'type': 'none'}, - 'reset': {'type': 'none'}, - 'screenshot': {'type': 'none'}, - 'shutdown': {'type': 'none'}, - 'start': {'type': 'none'}, - 'suspend': {'type': 'none'}, - 'resume': {'type': 'none'}, - 'ttyconsole': {'type': 'str'}, - 'undefine': {'type': 'none'}, - }} - # # Functions for generating datas. @@ -157,7 +151,7 @@ def to_xml(tag_name, conf): return tag -def __str_to_dict(string): +def _str_to_dict(string): def format_key(key): return (key.strip().lower() .replace(' ', '_').replace('(', '').replace(')', '')) @@ -174,7 +168,7 @@ def str_method(self, *args, **kwargs): def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True): - return __str_to_dict(self._host.virsh(cmd, *args, **kwargs)) + return _str_to_dict(self._host.virsh(cmd, *args, **kwargs)) def none_method(self, *args, **kwargs): return self._host.virsh(method, *args, **kwargs) @@ -182,9 +176,9 @@ def none_method(self, *args, **kwargs): def xml_method(self, *args, **kwargs): with self._host.set_controls(parse=True): xml = '\n'.join(self._host.virsh(cmd, *args, **kwargs)) - return _xml_to_dict(etree.fromstring(xml))[conf['key']] + return from_xml(etree.fromstring(xml), conf['lists'])[conf['key']] - setattr(obj, method, locals()['%s_method' % conf['type']]) + setattr(obj, method.replace('-', '_'), locals()['%s_method' % conf['type']]) # @@ -214,7 +208,7 @@ class Hypervisor(host.__class__): def __init__(self): host.__class__.__init__(self) self.__dict__.update(host.__dict__) - for control, value in CONTROLS.items(): + for control, value in _CONTROLS.items(): setattr(self, '_%s' % control, value) @@ -229,7 +223,7 @@ def virsh(self, command, *args, **kwargs): command, *args, **kwargs) - # Clean stdout and stderr. + # Clean stdout and stderr. if stdout: stdout = stdout.rstrip('\n') if stderr: @@ -316,7 +310,7 @@ class _Hypervisor(object): def __init__(self, host): self._host = host -for mname, mconf in MAPPING['hypervisor'].items(): +for mname, mconf in _MAPPING['hypervisor'].items(): __add_method(_Hypervisor, mname, mconf) @@ -358,5 +352,5 @@ def timeout_handler(signum, frame): signal.signal(signal.SIGALRM, old_handler) signal.alarm(0) -for mname, mconf in MAPPING['domain'].items(): +for mname, mconf in _MAPPING['domain'].items(): __add_method(_Domain, mname, mconf) From 0480498bc8ebb5dc9398c0da7c7f27d240e6bcc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:28:53 +0100 Subject: [PATCH 006/167] Add functions 'create', 'define', 'attach-disk' and rename function 'dumpxml' to 'conf' in the domains' mapping. --- kvm.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kvm.py b/kvm.py index 671d39c..dfe3d3f 100644 --- a/kvm.py +++ b/kvm.py @@ -39,6 +39,8 @@ 'allocpages': {'type': 'none'}}, 'domain': {'autostart': {'type': 'none'}, 'inject-nmi': {'type': 'none'}, + 'create': {'type': 'none'}, + 'define': {'type': 'none'}, 'desc': {'type': 'dict'}, 'destroy': {'type': 'none'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, @@ -49,7 +51,10 @@ 'name': {'cmd': 'domname', 'type': 'str'}, 'state': {'cmd': 'domstate', 'type': 'str'}, 'control': {'cmd': 'domcontrol', 'type': 'str'}, - 'dumpxml': {'type': 'xml', 'key': 'domain'}, + 'conf': {'cmd': 'dumpxml', + 'type': 'xml', + 'key': 'domain', + 'lists': ['disk', 'interface']}, 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, 'screenshot': {'type': 'none'}, @@ -58,7 +63,8 @@ 'suspend': {'type': 'none'}, 'resume': {'type': 'none'}, 'ttyconsole': {'type': 'str'}, - 'undefine': {'type': 'none'} + 'undefine': {'type': 'none'}, + 'attach-disk': {'type': 'none'}, }} From 7990c21cb0983da5974e8050f6c3230b67d9dbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:32:12 +0100 Subject: [PATCH 007/167] Force list values when generating a dictionnary from XML. --- kvm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kvm.py b/kvm.py index dfe3d3f..e184641 100644 --- a/kvm.py +++ b/kvm.py @@ -98,7 +98,7 @@ def gen_mac(): ''.join([random.choice(_CHOICES) for _ in range(0, 2)]))) -def from_xml(elt): +def from_xml(elt, force_lists=[]): """Recursive function that transform an XML element to a dictionnary. **elt** must be of type ``lxml.etree.Element``.""" tag = elt.tag @@ -119,8 +119,10 @@ def from_xml(elt): elts = (OrderedDict(('@%s' % attr, value) for attr, value in attrs) if attrs else OrderedDict()) for child in childs: - child = from_xml(child) + child = from_xml(child, force_lists) child_tag = list(child.keys())[0] + if child_tag in force_lists: + elts[child_tag] = [] if child_tag in elts: if not isinstance(elts[child_tag], list): elts[child_tag] = [elts[child_tag]] From 8e688fe090cb5f79463e40910fffb16316bbc39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:33:55 +0100 Subject: [PATCH 008/167] Force str in XML tags in 'to_xml' function. --- kvm.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kvm.py b/kvm.py index e184641..678a0c8 100644 --- a/kvm.py +++ b/kvm.py @@ -140,13 +140,12 @@ def to_xml(tag_name, conf): tag = etree.Element(tag_name) for elt, value in conf.items(): if elt.startswith('@'): - tag.attrib[elt[1:]] = value + tag.attrib[elt[1:]] = str(value) elif elt == '#text': - tag.text = value + tag.text = str(value) elif isinstance(value, dict): tag.append(to_xml(elt, value)) elif isinstance(value, list): - print(tag_name, elt, value) for child in value: tag.append(to_xml(elt, child)) elif isinstance(value, bool): From 89bb108e8ce955491d0896e6e5972b6146d16db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:34:23 +0100 Subject: [PATCH 009/167] Correct a bug when using python2. --- kvm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index 678a0c8..f75b2ab 100644 --- a/kvm.py +++ b/kvm.py @@ -298,7 +298,8 @@ def list_domains(self, **kwargs): stdout = self.virsh('list', **virsh_opts) for line in stdout[2:]: - domid, name, state, *params = line.split() + line = line.split() + (domid, name, state), params = line[:3], line[3:] # Manage state in two words. if state == 'shut': state += ' %s' % params.pop(0) From d0c079a934dd3e01a419a67ab800e0d2c4bd546a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:34:55 +0100 Subject: [PATCH 010/167] Add an 'Image' object accessible via the 'image' property which allow to manage qemu images (create, resize, ...). --- kvm.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/kvm.py b/kvm.py index f75b2ab..dd8a66d 100644 --- a/kvm.py +++ b/kvm.py @@ -311,6 +311,12 @@ def list_domains(self, **kwargs): return domains + + @property + def image(self): + return _Image(weakref.ref(self)()) + + return Hypervisor() @@ -360,5 +366,71 @@ def timeout_handler(signum, frame): signal.signal(signal.SIGALRM, old_handler) signal.alarm(0) + +class _Image(object): + def __init__(self, host): + self._host = host + + + def check(self, path, **kwargs): + return self._host.execute('qemu-img check', path, **kwargs) + + + def create(self, path, size, **kwargs): + return self._host.execute('qemu-img create', path, size, **kwargs) + + + def commit(self, path, **kwargs): + return self._host.execute('qemu-img commit', path, **kwargs) + + + def compare(self, *paths, **kwargs): + return self._host.execute('qemu-img compare', *paths, **kwargs) + + + def convert(self, src_path, dst_path, **kwargs): + with self._host.set_controls(options_place='after'): + return self._host.execute('qemu-img convert', src_path, dst_path, **kwargs) + + + def info(self, path, **kwargs): + status, stdout, stderr = self._host.execute('qemu-img info', path, **kwargs) + if not status: + raise OSError(stderr) + return _str_to_dict(stdout.splitlines()) + + + def map(self, path, **kwargs): + return self._host.execute('qemu-img map', path, **kwargs) + + + def snapshot(self, path, **kwargs): + return self._host.execute('qemu-img snapshot', path, **kwargs) + + + def rebase(self, path, **kwargs): + return self._host.execute('qemu-img rebase', path, **kwargs) + + + def resize(self, path, size): + return self._host.execute('qemu-img resize', path, size) + + + def amend(self, path, **kwargs): + return self._host.execute('qemu-img amend', path, **kwargs) + + + def load(self, path, device='nbd0', **kwargs): + kwargs['c'] = '/dev/%s' % device + kwargs['d'] = False + return self._host.execute('qemu-nbd', path, **kwargs) + + + def unload(self, device='nbd0', **kwargs): + kwargs['c'] = False + kwargs['d'] = '/dev/%s' % device + return self._host.execute('qemu-nbd', **kwargs) + + for mname, mconf in _MAPPING['domain'].items(): __add_method(_Domain, mname, mconf) From 678cb98eb4118129470c65e30e5163d1ac13379b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:37:04 +0100 Subject: [PATCH 011/167] Add a function for generating the configuration of a domain. --- kvm.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kvm.py b/kvm.py index dd8a66d..fe0ef6d 100644 --- a/kvm.py +++ b/kvm.py @@ -333,12 +333,10 @@ def __init__(self, host): self._host = host - def create(self, conf, *kwargs): - pass + def gen_conf(self, conf): + return etree.tostring(to_xml('domain', conf), pretty_print=True) - def define(self, conf): - pass def stop(self, domain, timeout=30, force=False): From 47f431c53baa0bc55bc8f990b964535238731d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 15 Mar 2015 10:37:21 +0100 Subject: [PATCH 012/167] 'domain.stop' method check if the guest exists and now return a list of three elements ([status, stdout, stderr]). --- kvm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kvm.py b/kvm.py index fe0ef6d..485a0c4 100644 --- a/kvm.py +++ b/kvm.py @@ -345,6 +345,10 @@ def stop(self, domain, timeout=30, force=False): def timeout_handler(signum, frame): raise TimeoutException() + # Check guest exists. + if domain not in self._host.list_domains(all=True): + return [False, '', 'Domain not found'] + self.shutdown(domain) old_handler = signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(timeout) @@ -363,6 +367,7 @@ def timeout_handler(signum, frame): finally: signal.signal(signal.SIGALRM, old_handler) signal.alarm(0) + return [True, '', ''] class _Image(object): From b578ff62b033833596398800d67337a2f9e446d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 13:58:31 +0100 Subject: [PATCH 013/167] Correct a bug when forcing list values of XML outputs. Rename the parameter of '_str_to_dict' function. --- kvm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kvm.py b/kvm.py index 485a0c4..7340754 100644 --- a/kvm.py +++ b/kvm.py @@ -158,12 +158,12 @@ def to_xml(tag_name, conf): return tag -def _str_to_dict(string): +def _str_to_dict(lines): def format_key(key): return (key.strip().lower() .replace(' ', '_').replace('(', '').replace(')', '')) return {format_key(key): (value or '').strip() - for line in string if line + for line in lines if line for key, value in [line.split(':')]} @@ -183,7 +183,7 @@ def none_method(self, *args, **kwargs): def xml_method(self, *args, **kwargs): with self._host.set_controls(parse=True): xml = '\n'.join(self._host.virsh(cmd, *args, **kwargs)) - return from_xml(etree.fromstring(xml), conf['lists'])[conf['key']] + return from_xml(etree.fromstring(xml), conf.get('lists', []))[conf['key']] setattr(obj, method.replace('-', '_'), locals()['%s_method' % conf['type']]) From 2e2c4c52c5286ccf797539eab917bc096fa13b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:00:42 +0100 Subject: [PATCH 014/167] Check at the initialization that the 'virsh' command can be found. --- kvm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index 7340754..1460f51 100644 --- a/kvm.py +++ b/kvm.py @@ -207,6 +207,11 @@ class TimeoutException(Exception): def Hypervisor(host): unix.isvalid(host) + try: + host.which('virsh') + except unix.UnixError: + raise KvmError("unable to find 'virsh' command, is this a KVM host?") + class Hypervisor(host.__class__): """This object represent an Hypervisor. **host** must be an object of type ``unix.Local`` or ``unix.Remote`` (or an object inheriting from @@ -295,7 +300,7 @@ def list_domains(self, **kwargs): # Get domains (filtered on state). domains = {} with self.set_controls(parse=True): - stdout = self.virsh('list', **virsh_opts) + stdout = self.virsh('list', **virsh_opts) for line in stdout[2:]: line = line.split() From 0f2afdfb57c4290800bbef43efb5b0e9994fba45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:02:54 +0100 Subject: [PATCH 015/167] Add a mapping method for parsing stats ouputs. --- kvm.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index 1460f51..87e1b47 100644 --- a/kvm.py +++ b/kvm.py @@ -65,7 +65,6 @@ 'ttyconsole': {'type': 'str'}, 'undefine': {'type': 'none'}, 'attach-disk': {'type': 'none'}, - }} RUNNING = 'running' @@ -167,6 +166,12 @@ def format_key(key): for key, value in [line.split(':')]} +def _stats(lines, ignore): + return {elts[1 if ignore else 0]: elts[2 if ignore else 1] + for line in lines if line + for elts in [line.split()]} + + def __add_method(obj, method, conf): cmd = conf.get('cmd', method) def str_method(self, *args, **kwargs): @@ -177,6 +182,11 @@ def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True): return _str_to_dict(self._host.virsh(cmd, *args, **kwargs)) + def stat_method(self, *args, **kwargs): + with self._host.set_controls(parse=True): + return _stats(self._host.virsh(cmd, *args, **kwargs), + conf.get('ignore', False)) + def none_method(self, *args, **kwargs): return self._host.virsh(method, *args, **kwargs) From ccda037a77ff57b865e7c171ce5ae79a9dffeee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:05:09 +0100 Subject: [PATCH 016/167] Add the function 'domain.blkstat' (virsh domblkstat) for getting the statistics of a domain's disk. --- kvm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kvm.py b/kvm.py index 87e1b47..f0e1506 100644 --- a/kvm.py +++ b/kvm.py @@ -43,6 +43,9 @@ 'define': {'type': 'none'}, 'desc': {'type': 'dict'}, 'destroy': {'type': 'none'}, + 'blkstat': {'cmd': 'domblkstat', + 'type': 'stat', + 'ignore': True}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, From 51e1f2e0ff1e4f4d93e69c22ab471abdb601cb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:06:50 +0100 Subject: [PATCH 017/167] Add the function 'domain.ifstat' (virsh domifstat) for getting statistics of a domain's interface. --- kvm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kvm.py b/kvm.py index f0e1506..e7a737a 100644 --- a/kvm.py +++ b/kvm.py @@ -46,6 +46,9 @@ 'blkstat': {'cmd': 'domblkstat', 'type': 'stat', 'ignore': True}, + 'ifstat': {'cmd': 'domifstat', + 'type': 'stat', + 'ignore': True}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, From 4d3a2c94beb0a577b3bb21b04852bf8534c364ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:08:17 +0100 Subject: [PATCH 018/167] Add functions 'domain.if_getlink' and 'domain.if_setlink' for getting/setting the state of a domain's interface. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index e7a737a..a32a5ca 100644 --- a/kvm.py +++ b/kvm.py @@ -49,6 +49,8 @@ 'ifstat': {'cmd': 'domifstat', 'type': 'stat', 'ignore': True}, + 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, + 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, From 61c6cd80386aa7b88b1c5af0bae7a40996653e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:10:25 +0100 Subject: [PATCH 019/167] Add the function 'domain.iftune' for limitting interface's bandwidth of a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index a32a5ca..5f9e0a2 100644 --- a/kvm.py +++ b/kvm.py @@ -51,6 +51,7 @@ 'ignore': True}, 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, + 'iftune': {'type': 'none'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, From f46ff4f4ed11a07ed6bfc4220bcd72171914a6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:12:29 +0100 Subject: [PATCH 020/167] Add the function 'domain.memstat' for getting memory statistics of a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 5f9e0a2..59b38ec 100644 --- a/kvm.py +++ b/kvm.py @@ -52,6 +52,7 @@ 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, 'iftune': {'type': 'none'}, + 'memstat': {'cmd': 'dommemstat', 'type': 'stat'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, From 716361f35a9d7a0522a673a1a5fbce2a431c0f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:26:26 +0100 Subject: [PATCH 021/167] Rename mapping function 'stat' to '_stats' and add the keyword 'disable' for this parsing allowing to ignore some options (like 'human' for 'domblkstat'). --- kvm.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/kvm.py b/kvm.py index 59b38ec..38c03af 100644 --- a/kvm.py +++ b/kvm.py @@ -44,15 +44,16 @@ 'desc': {'type': 'dict'}, 'destroy': {'type': 'none'}, 'blkstat': {'cmd': 'domblkstat', - 'type': 'stat', - 'ignore': True}, + 'type': 'stats', + 'ignore': True, + 'disable': ['human']}, 'ifstat': {'cmd': 'domifstat', - 'type': 'stat', + 'type': 'stats', 'ignore': True}, 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, 'iftune': {'type': 'none'}, - 'memstat': {'cmd': 'dommemstat', 'type': 'stat'}, + 'memstat': {'cmd': 'dommemstat', 'type': 'stats'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, @@ -176,7 +177,7 @@ def format_key(key): for key, value in [line.split(':')]} -def _stats(lines, ignore): +def _stats(lines, ignore=False): return {elts[1 if ignore else 0]: elts[2 if ignore else 1] for line in lines if line for elts in [line.split()]} @@ -192,8 +193,10 @@ def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True): return _str_to_dict(self._host.virsh(cmd, *args, **kwargs)) - def stat_method(self, *args, **kwargs): + def stats_method(self, *args, **kwargs): with self._host.set_controls(parse=True): + for opt in conf.get('disable', []): + kwargs[opt] = False return _stats(self._host.virsh(cmd, *args, **kwargs), conf.get('ignore', False)) From 5fdb3d279af5b17195888ca7f02824c4c58c674e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:24:07 +0100 Subject: [PATCH 022/167] Add a mapping type 'list' that parse listing outputs. --- kvm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kvm.py b/kvm.py index 38c03af..b97c7b6 100644 --- a/kvm.py +++ b/kvm.py @@ -183,6 +183,11 @@ def _stats(lines, ignore=False): for elts in [line.split()]} +def _list(lines): + params = [param.lower() for param in re.split('\s+', lines[0])] + return [dict(zip(params, re.split('\s+', line))) for line in lines[2:]] + + def __add_method(obj, method, conf): cmd = conf.get('cmd', method) def str_method(self, *args, **kwargs): @@ -200,6 +205,10 @@ def stats_method(self, *args, **kwargs): return _stats(self._host.virsh(cmd, *args, **kwargs), conf.get('ignore', False)) + def list_method(self, *args, **kwargs): + with self._host.set_controls(parse=True): + return _list(self._host.virsh(cmd, *args, **kwargs)) + def none_method(self, *args, **kwargs): return self._host.virsh(method, *args, **kwargs) From 376eb01552cd55951802551c4836aaf653b3a04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 14:36:42 +0100 Subject: [PATCH 023/167] Add the function 'domain.blklist' that list domain's disks. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index b97c7b6..3e1b690 100644 --- a/kvm.py +++ b/kvm.py @@ -55,6 +55,7 @@ 'iftune': {'type': 'none'}, 'memstat': {'cmd': 'dommemstat', 'type': 'stats'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, + 'blklist': {'cmd': 'domblklist', 'type': 'list'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, 'uuid': {'cmd': 'domuuid', 'type': 'str'}, From 921438541105f18b50aa15bf95d997d786862f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:29:39 +0100 Subject: [PATCH 024/167] Add a mapping type 'tune' that manage *tune virsh commands. When the command is executed with no options or only one of the --config, --live, --current options, the current settings are returned. Othersize the settings are applied based of the given options. --- kvm.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index 3e1b690..748bf13 100644 --- a/kvm.py +++ b/kvm.py @@ -210,8 +210,16 @@ def list_method(self, *args, **kwargs): with self._host.set_controls(parse=True): return _list(self._host.virsh(cmd, *args, **kwargs)) + def tune_method(self, *args, **kwargs): + ignore_opts = ('config', 'live', 'current') + if (not kwargs + or (len(kwargs) == 1 and any(opt in kwargs for opt in ignore_opts))): + return dict_method(self, *args, **kwargs) + else: + return none_method(self, *args, **kwargs) + def none_method(self, *args, **kwargs): - return self._host.virsh(method, *args, **kwargs) + return self._host.virsh(cmd, *args, **kwargs) def xml_method(self, *args, **kwargs): with self._host.set_controls(parse=True): From 7f1d88fbfbf5987b5a0bb3e5dd0506fbf5c27f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:31:14 +0100 Subject: [PATCH 025/167] Add a function 'domain.iftune' for getting/setting the limits of a domain's interface. --- kvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index 748bf13..5f4ffa1 100644 --- a/kvm.py +++ b/kvm.py @@ -52,7 +52,7 @@ 'ignore': True}, 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, - 'iftune': {'type': 'none'}, + 'iftune': {'cmd': 'domiftune', 'type': 'tune'}, 'memstat': {'cmd': 'dommemstat', 'type': 'stats'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'blklist': {'cmd': 'domblklist', 'type': 'list'}, From 99cf89ca5929b9c9554eb55e46ddbc76114d5653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 16:46:37 +0100 Subject: [PATCH 026/167] Add the function 'domain.iflist' that list domain's interfaces. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 5f4ffa1..21a8831 100644 --- a/kvm.py +++ b/kvm.py @@ -56,6 +56,7 @@ 'memstat': {'cmd': 'dommemstat', 'type': 'stats'}, 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'blklist': {'cmd': 'domblklist', 'type': 'list'}, + 'iflist': {'cmd': 'domiflist', 'type': 'list'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, 'uuid': {'cmd': 'domuuid', 'type': 'str'}, From 69932bd3a3f7b951d576055e472cc10d639fedaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 16:49:54 +0100 Subject: [PATCH 027/167] Add the function 'domain.blkdeviotune' for getting/setting the limits of a domain's block device. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 21a8831..44e4a63 100644 --- a/kvm.py +++ b/kvm.py @@ -57,6 +57,7 @@ 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'blklist': {'cmd': 'domblklist', 'type': 'list'}, 'iflist': {'cmd': 'domiflist', 'type': 'list'}, + 'blkdeviotune': {'type': 'tune'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, 'uuid': {'cmd': 'domuuid', 'type': 'str'}, From e6c608741c376f49e8f31474c19cf9714a4fdc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 18:20:59 +0100 Subject: [PATCH 028/167] Add the function 'domain.time' that returns the date (a datetime object) of the domain if no options are passed. Otherwise set the date of the domain according to the given options. --- kvm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kvm.py b/kvm.py index 44e4a63..0cd9015 100644 --- a/kvm.py +++ b/kvm.py @@ -385,6 +385,15 @@ def gen_conf(self, conf): return etree.tostring(to_xml('domain', conf), pretty_print=True) + def time(self, domain, **kwargs): + kwargs.pop('pretty', None) + if not kwargs: + from datetime import datetime + with self._host.set_controls(parse=True): + time = self._host.virsh('domtime', domain, **kwargs)[0] + return datetime.fromtimestamp(int(time.split(':')[1])) + else: + return self._host.virsh('domtime', domain, **kwargs) def stop(self, domain, timeout=30, force=False): From 9a0d09f300f3912a77aed716c963513511fe7a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 18:26:02 +0100 Subject: [PATCH 029/167] Add the function 'domain.coredump' for dumping the core of domain for analysis. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 0cd9015..fe78124 100644 --- a/kvm.py +++ b/kvm.py @@ -65,6 +65,7 @@ 'name': {'cmd': 'domname', 'type': 'str'}, 'state': {'cmd': 'domstate', 'type': 'str'}, 'control': {'cmd': 'domcontrol', 'type': 'str'}, + 'coredump': {'cmd': 'dump', 'type': 'none'}, 'conf': {'cmd': 'dumpxml', 'type': 'xml', 'key': 'domain', From e1788fe7eb91a59b3d3126a1dd33af9d8de89155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 17 Mar 2015 18:27:31 +0100 Subject: [PATCH 030/167] Add the function 'domain.blockresize' allowing to resize the disk of a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index fe78124..494c8e3 100644 --- a/kvm.py +++ b/kvm.py @@ -58,6 +58,7 @@ 'blklist': {'cmd': 'domblklist', 'type': 'list'}, 'iflist': {'cmd': 'domiflist', 'type': 'list'}, 'blkdeviotune': {'type': 'tune'}, + 'blockresize': {'type': 'none'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, 'uuid': {'cmd': 'domuuid', 'type': 'str'}, From 5e0316e97958439dbb82741483dd2d30bc03a82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 10:41:02 +0100 Subject: [PATCH 031/167] Add the function 'domain.managedsave' that save and stop a running domain, so it can be restarted from the same state at a later time. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 494c8e3..103dc4f 100644 --- a/kvm.py +++ b/kvm.py @@ -71,6 +71,7 @@ 'type': 'xml', 'key': 'domain', 'lists': ['disk', 'interface']}, + 'managedsave': {'type': 'none'}, 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, 'screenshot': {'type': 'none'}, From 70d3680c578983deb88c1e3a1ff7376745d218dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 10:43:30 +0100 Subject: [PATCH 032/167] Add the function 'domain.managedsave_remove' that remove the managedsave state file for a domain and so, ensures the domain will do a full boot the next time it is started. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index 103dc4f..d3076f2 100644 --- a/kvm.py +++ b/kvm.py @@ -72,6 +72,8 @@ 'key': 'domain', 'lists': ['disk', 'interface']}, 'managedsave': {'type': 'none'}, + 'managedsave_remove': {'cmd': 'managedsave-remove', + 'type': 'none'}, 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, 'screenshot': {'type': 'none'}, From 0bd33b3b76447b189af4675c0117267a02cde30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:03:13 +0100 Subject: [PATCH 033/167] Add a parameter 'convert' for mapping type 'str' allowing to convert the resulted value. --- kvm.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kvm.py b/kvm.py index d3076f2..fb815e4 100644 --- a/kvm.py +++ b/kvm.py @@ -10,7 +10,10 @@ from collections import OrderedDict import sys -SELF = sys.modules[__name__] +#_SELF = sys.modules[__name__] +_BUILTINS = sys.modules['builtins' + if sys.version_info.major == 3 + else '__builtin__'] # Controls. @@ -200,7 +203,12 @@ def __add_method(obj, method, conf): cmd = conf.get('cmd', method) def str_method(self, *args, **kwargs): with self._host.set_controls(parse=True): - return self._host.virsh(cmd, *args, **kwargs)[0] + result = self._host.virsh(cmd, *args, **kwargs)[0] + if 'convert' in conf: + try: + return getattr(_BUILTINS, conf['convert'])(result) + except ValueError: + return -1 if conf['convert'] == 'int' else result def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True): From 24717f1f904158b33d41b34ff9f9c9d043407840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:37:39 +0100 Subject: [PATCH 034/167] Update the mapping of the function 'domain.id' for returning an interger (it will return -1 if the domain is not started). --- kvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index fb815e4..6cb9677 100644 --- a/kvm.py +++ b/kvm.py @@ -65,7 +65,7 @@ 'display': {'cmd': 'domdisplay', 'type': 'str'}, 'info': {'cmd': 'dominfo', 'type': 'dict'}, 'uuid': {'cmd': 'domuuid', 'type': 'str'}, - 'id': {'cmd': 'domid', 'type': 'str'}, + 'id': {'cmd': 'domid', 'type': 'str', 'convert': 'int'}, 'name': {'cmd': 'domname', 'type': 'str'}, 'state': {'cmd': 'domstate', 'type': 'str'}, 'control': {'cmd': 'domcontrol', 'type': 'str'}, From 08ab1bcac0d6df3b3d6fff1b12b5f101be5f15ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:39:47 +0100 Subject: [PATCH 035/167] Add the function 'hypervisor.maxvcpu' returning the maximum vcpu a domain can have. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index 6cb9677..aeff746 100644 --- a/kvm.py +++ b/kvm.py @@ -27,6 +27,8 @@ _MAPPING = {'hypervisor': {'version': {'type': 'dict'}, 'sysinfo': {'type': 'dict'}, + 'maxvcpus': {'type': 'str', + 'convert': 'int'}, 'nodeinfo': {'type': 'dict'}, 'nodecpumap': {'type': 'dict'}, 'nodecpustats': {'type': 'dict'}, From 476340a5b356c91044672e2cb75e5aa2d69bfbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 11:54:23 +0100 Subject: [PATCH 036/167] Add the function 'domain.cpustats' that returns domain's vcpu statistics. --- kvm.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kvm.py b/kvm.py index aeff746..0c9f7a2 100644 --- a/kvm.py +++ b/kvm.py @@ -411,6 +411,21 @@ def time(self, domain, **kwargs): return self._host.virsh('domtime', domain, **kwargs) + def cpustats(self, domain, **kwargs): + with self._host.set_controls(parse=True): + lines = self._host.virsh('cpu-stats', domain, **kwargs) + stats = {} + cur_cpu = '' + for line in lines: + if not line.startswith('\t'): + cur_cpu = line[:-1].lower() + stats.setdefault(cur_cpu, {}) + else: + param, value, unit = line[1:].split() + stats[cur_cpu][param] = '%s %s' % (value, unit) + return stats + + def stop(self, domain, timeout=30, force=False): import signal, time From cb3e607b069311535ccc940fe70d993af826e07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 18 Mar 2015 12:11:27 +0100 Subject: [PATCH 037/167] Add the function 'domain.numatune' allowing to get/set NUMA limits of a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 0c9f7a2..db4bb54 100644 --- a/kvm.py +++ b/kvm.py @@ -79,6 +79,7 @@ 'managedsave': {'type': 'none'}, 'managedsave_remove': {'cmd': 'managedsave-remove', 'type': 'none'}, + 'numatune': {'type': 'tune'}, 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, 'screenshot': {'type': 'none'}, From 55b54152be4e044a81f8c925f58ed007d001842f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 11:16:24 +0100 Subject: [PATCH 038/167] Add the function 'domain.save' that saves a running domain (RAM, but not disk state) to a state file so that it can be restored later. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index db4bb54..edc9016 100644 --- a/kvm.py +++ b/kvm.py @@ -82,6 +82,7 @@ 'numatune': {'type': 'tune'}, 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, + 'save': {'type': 'none'}, 'screenshot': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, From 142dcb1ea9204a3371048256579e9d81ed4b2c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 11:32:54 +0100 Subject: [PATCH 039/167] Add the function 'domain.save_conf' that extracts the domain's XML configuration that was in effect at the time the saved state file was created. --- kvm.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kvm.py b/kvm.py index edc9016..06ea41c 100644 --- a/kvm.py +++ b/kvm.py @@ -83,6 +83,10 @@ 'reboot': {'type': 'none'}, 'reset': {'type': 'none'}, 'save': {'type': 'none'}, + 'save_conf': {'cmd': 'save-image-dumpxml', + 'type': 'xml', + 'key': 'domain', + 'lists': ['disk', 'interface']}, 'screenshot': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, From 2ada5e9cfc05d54190d2f9b8df4d31546453d536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 11:35:00 +0100 Subject: [PATCH 040/167] Add the function 'domain.save_define' that updates the domain's XML configuration of the state file. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index 06ea41c..e6443b6 100644 --- a/kvm.py +++ b/kvm.py @@ -87,6 +87,8 @@ 'type': 'xml', 'key': 'domain', 'lists': ['disk', 'interface']}, + 'save_define': {'cmd': 'save-image-define', + 'type': 'none'}, 'screenshot': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, From 2fc5a69cab660f7e03a446ed075f05857a059365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 11:36:18 +0100 Subject: [PATCH 041/167] Add the function 'domain.restore' that restores a domain from a state file. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index e6443b6..93f0e05 100644 --- a/kvm.py +++ b/kvm.py @@ -89,6 +89,7 @@ 'lists': ['disk', 'interface']}, 'save_define': {'cmd': 'save-image-define', 'type': 'none'}, + 'restore': {'type': 'none'}, 'screenshot': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, From d8c525f09d8f8aade1c36f16f3e2619991d22abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 14:56:36 +0100 Subject: [PATCH 042/167] Add the function 'domain.schedinfo' that allows to show and set the domain scheduler parameters --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 93f0e05..388ff4e 100644 --- a/kvm.py +++ b/kvm.py @@ -90,6 +90,7 @@ 'save_define': {'cmd': 'save-image-define', 'type': 'none'}, 'restore': {'type': 'none'}, + 'schedinfo': {'type': 'dict'}, 'screenshot': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, From 21270eb8f30c15b78d00d17150f362592865963e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 15:13:16 +0100 Subject: [PATCH 043/167] Add the command 'domain.send_key' allowing to send keycodes to a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 388ff4e..dfe7951 100644 --- a/kvm.py +++ b/kvm.py @@ -92,6 +92,7 @@ 'restore': {'type': 'none'}, 'schedinfo': {'type': 'dict'}, 'screenshot': {'type': 'none'}, + 'send_key': {'cmd': 'send-key', 'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, 'suspend': {'type': 'none'}, From ded6793b85a047e518e3aa47787578385d655656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 15:58:15 +0100 Subject: [PATCH 044/167] Add the function 'domain.setmem' that change the memory allocation for a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index dfe7951..91e33c5 100644 --- a/kvm.py +++ b/kvm.py @@ -93,6 +93,7 @@ 'schedinfo': {'type': 'dict'}, 'screenshot': {'type': 'none'}, 'send_key': {'cmd': 'send-key', 'type': 'none'}, + 'setmem': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, 'suspend': {'type': 'none'}, From 2525b430a1bdf776128279ba6b538e6942e05260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 15:59:09 +0100 Subject: [PATCH 045/167] Add the function 'domain.setmaxmem' that change the maximum memory allocation for a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 91e33c5..d594395 100644 --- a/kvm.py +++ b/kvm.py @@ -94,6 +94,7 @@ 'screenshot': {'type': 'none'}, 'send_key': {'cmd': 'send-key', 'type': 'none'}, 'setmem': {'type': 'none'}, + 'setmaxmem': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, 'suspend': {'type': 'none'}, From aa18b46dd2909d9b9a2a14545559cf0fcf010a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 16:01:58 +0100 Subject: [PATCH 046/167] Add the function 'domain.memtune' for getting/setting a domain's memory limits. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index d594395..2769717 100644 --- a/kvm.py +++ b/kvm.py @@ -95,6 +95,7 @@ 'send_key': {'cmd': 'send-key', 'type': 'none'}, 'setmem': {'type': 'none'}, 'setmaxmem': {'type': 'none'}, + 'memtune': {'type': 'tune'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, 'suspend': {'type': 'none'}, From 2918c033eb20d3e068cb2bb3c3aa75ce7db693ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 16:14:28 +0100 Subject: [PATCH 047/167] Add a function 'domain.blkiotune' that get/set limits on a block devices for a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 2769717..f8f514a 100644 --- a/kvm.py +++ b/kvm.py @@ -62,6 +62,7 @@ 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, 'blklist': {'cmd': 'domblklist', 'type': 'list'}, 'iflist': {'cmd': 'domiflist', 'type': 'list'}, + 'blkiotune': {'type': 'tune'}, 'blkdeviotune': {'type': 'tune'}, 'blockresize': {'type': 'none'}, 'display': {'cmd': 'domdisplay', 'type': 'str'}, From 783ac40e2956a6cdbf4b1fd9d3a84bc7c2eefe1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 17:24:03 +0100 Subject: [PATCH 048/167] Add the function 'domain.setvcpus' that change the number of virtual CPUs active in a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index f8f514a..75a72b2 100644 --- a/kvm.py +++ b/kvm.py @@ -97,6 +97,7 @@ 'setmem': {'type': 'none'}, 'setmaxmem': {'type': 'none'}, 'memtune': {'type': 'tune'}, + 'setvcpus': {'type': 'none'}, 'shutdown': {'type': 'none'}, 'start': {'type': 'none'}, 'suspend': {'type': 'none'}, From 202f447f60aab627256663f17b3e40caaa88b8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 17:29:09 +0100 Subject: [PATCH 049/167] Add the function 'domain.pmsuspend' that suspend a running domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 75a72b2..8984390 100644 --- a/kvm.py +++ b/kvm.py @@ -104,6 +104,7 @@ 'resume': {'type': 'none'}, 'ttyconsole': {'type': 'str'}, 'undefine': {'type': 'none'}, + 'pmsuspend': {'cmd': 'dompmsuspend', 'type': 'none'}, 'attach-disk': {'type': 'none'}, From 8694c696a10441387701fcb960fbf388d16a3548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 17:29:56 +0100 Subject: [PATCH 050/167] Add the function 'domain.pmwakeup' that wakeup a pmsuspended domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 8984390..c34afc7 100644 --- a/kvm.py +++ b/kvm.py @@ -105,6 +105,7 @@ 'ttyconsole': {'type': 'str'}, 'undefine': {'type': 'none'}, 'pmsuspend': {'cmd': 'dompmsuspend', 'type': 'none'}, + 'pmwakeup': {'cmd': 'dompmwakeup', 'type': 'none'}, 'attach-disk': {'type': 'none'}, From fd7f3a8b638f2f4a49f91a5dc5b86cbe8bcfd332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:28:31 +0100 Subject: [PATCH 051/167] Add the function 'domain.attach_disk' for adding a disk to a domain. --- kvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm.py b/kvm.py index c34afc7..551cbef 100644 --- a/kvm.py +++ b/kvm.py @@ -106,7 +106,7 @@ 'undefine': {'type': 'none'}, 'pmsuspend': {'cmd': 'dompmsuspend', 'type': 'none'}, 'pmwakeup': {'cmd': 'dompmwakeup', 'type': 'none'}, - 'attach-disk': {'type': 'none'}, + 'attach_disk': {'cmd': 'attach-disk', 'type': 'none'}, RUNNING = 'running' From ae1b3f199d3e06db5415d3ec78f2ca49cbeb70a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:29:20 +0100 Subject: [PATCH 052/167] Add the function 'domain.attach_device' for adding a new device to a domain from an XML file. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 551cbef..d13a64f 100644 --- a/kvm.py +++ b/kvm.py @@ -106,6 +106,7 @@ 'undefine': {'type': 'none'}, 'pmsuspend': {'cmd': 'dompmsuspend', 'type': 'none'}, 'pmwakeup': {'cmd': 'dompmwakeup', 'type': 'none'}, + 'attach_device': {'cmd': 'attach-device', 'type': 'none'}, 'attach_disk': {'cmd': 'attach-disk', 'type': 'none'}, From c3246407189990962dfc348d6570485cee9391fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:29:59 +0100 Subject: [PATCH 053/167] Add the function 'domain.attach_interface' for adding a new network interface to a domain. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index d13a64f..a9df05e 100644 --- a/kvm.py +++ b/kvm.py @@ -108,6 +108,8 @@ 'pmwakeup': {'cmd': 'dompmwakeup', 'type': 'none'}, 'attach_device': {'cmd': 'attach-device', 'type': 'none'}, 'attach_disk': {'cmd': 'attach-disk', 'type': 'none'}, + 'attach_interface': {'cmd': 'attach-interface', + 'type': 'none'}, RUNNING = 'running' From a8fedfc6aad89512d5a23514fd30e411da5ccae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:30:48 +0100 Subject: [PATCH 054/167] Add the function 'domain.detach_device' for removing a device to a domain based on a XML file. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index a9df05e..64d2484 100644 --- a/kvm.py +++ b/kvm.py @@ -110,6 +110,7 @@ 'attach_disk': {'cmd': 'attach-disk', 'type': 'none'}, 'attach_interface': {'cmd': 'attach-interface', 'type': 'none'}, + 'detach_device': {'cmd': 'detach-device', 'type': 'none'}, RUNNING = 'running' From d093d27c4b8510eb8b2f2b4d201de22e33c39465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:31:57 +0100 Subject: [PATCH 055/167] Add the function 'domain.detach_disk' for removing a disk to a domain. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 64d2484..a2508dc 100644 --- a/kvm.py +++ b/kvm.py @@ -111,6 +111,7 @@ 'attach_interface': {'cmd': 'attach-interface', 'type': 'none'}, 'detach_device': {'cmd': 'detach-device', 'type': 'none'}, + 'detach_disk': {'cmd': 'detach-disk', 'type': 'none'}, RUNNING = 'running' From 6a2e47f7ea49e2b338c2f3f01e2f7d6e3b09fc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:32:35 +0100 Subject: [PATCH 056/167] Add the function 'domain.detach_interface' for removing a network interface to a domain. --- kvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.py b/kvm.py index a2508dc..d8d2155 100644 --- a/kvm.py +++ b/kvm.py @@ -112,6 +112,8 @@ 'type': 'none'}, 'detach_device': {'cmd': 'detach-device', 'type': 'none'}, 'detach_disk': {'cmd': 'detach-disk', 'type': 'none'}, + 'detach_interface': {'cmd': 'detach-interface', + 'type': 'none'}, RUNNING = 'running' From 9ed353fc6768eef8b60f1102ec25b782dc6cb76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:33:32 +0100 Subject: [PATCH 057/167] Add the function 'domain.update_device' for updating a domain's device based of a XML file. --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index d8d2155..5773752 100644 --- a/kvm.py +++ b/kvm.py @@ -114,6 +114,7 @@ 'detach_disk': {'cmd': 'detach-disk', 'type': 'none'}, 'detach_interface': {'cmd': 'detach-interface', 'type': 'none'}, + 'update_device': {'cmd': 'update-device', 'type': 'none'}, RUNNING = 'running' From e7ab046d7b9b2bbe3cbf8583b5f6fb685023531e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 19 Mar 2015 18:34:32 +0100 Subject: [PATCH 058/167] Update the function 'to_xml' for returning a decoded string rather than an 'etree.Element' object. Remove the function 'domain.gen_conf'. --- kvm.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/kvm.py b/kvm.py index 5773752..a002102 100644 --- a/kvm.py +++ b/kvm.py @@ -186,25 +186,27 @@ def from_xml(elt, force_lists=[]): def to_xml(tag_name, conf): - tag = etree.Element(tag_name) - for elt, value in conf.items(): - if elt.startswith('@'): - tag.attrib[elt[1:]] = str(value) - elif elt == '#text': - tag.text = str(value) - elif isinstance(value, dict): - tag.append(to_xml(elt, value)) - elif isinstance(value, list): - for child in value: - tag.append(to_xml(elt, child)) - elif isinstance(value, bool): - tag.append(etree.Element(elt)) - continue - else: - child = etree.Element(elt) - child.text = value - tag.append(child) - return tag + def parse(tag_name, conf): + tag = etree.Element(tag_name) + for elt, value in conf.items(): + if elt.startswith('@'): + tag.attrib[elt[1:]] = str(value) + elif elt == '#text': + tag.text = str(value) + elif isinstance(value, dict): + tag.append(parse(elt, value)) + elif isinstance(value, list): + for child in value: + tag.append(parse(elt, child)) + elif isinstance(value, bool): + tag.append(etree.Element(elt)) + continue + else: + child = etree.Element(elt) + child.text = value + tag.append(child) + return tag + return etree.tostring(parse(tag_name, conf), pretty_print=True).decode() def _str_to_dict(lines): @@ -422,8 +424,8 @@ def __init__(self, host): self._host = host - def gen_conf(self, conf): - return etree.tostring(to_xml('domain', conf), pretty_print=True) +# def gen_conf(self, conf): +# return etree.tostring(to_xml('domain', conf), pretty_print=True) def time(self, domain, **kwargs): From df9c3bfdc7768130201dea664fab21339d3cbf80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 15:21:46 +0100 Subject: [PATCH 059/167] Add a control 'ignore_opts' for ignoring certains options. All mapping methods can have the keyword 'disable' for ignoring some options. --- kvm.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/kvm.py b/kvm.py index a002102..0de180e 100644 --- a/kvm.py +++ b/kvm.py @@ -17,7 +17,7 @@ # Controls. -_CONTROLS = {'parse': False} +_CONTROLS = {'parse': False, 'ignore_opts': []} unix._CONTROLS.update(_CONTROLS) # Characters in generating strings. @@ -231,8 +231,9 @@ def _list(lines): def __add_method(obj, method, conf): cmd = conf.get('cmd', method) + ignore_opts = conf.pop('disable', []) def str_method(self, *args, **kwargs): - with self._host.set_controls(parse=True): + with self._host.set_controls(parse=True, ignore_opts=ignore_opts): result = self._host.virsh(cmd, *args, **kwargs)[0] if 'convert' in conf: try: @@ -241,18 +242,17 @@ def str_method(self, *args, **kwargs): return -1 if conf['convert'] == 'int' else result def dict_method(self, *args, **kwargs): - with self._host.set_controls(parse=True): + with self._host.set_controls(parse=True, ignore_opts=ignore_opts): return _str_to_dict(self._host.virsh(cmd, *args, **kwargs)) def stats_method(self, *args, **kwargs): - with self._host.set_controls(parse=True): - for opt in conf.get('disable', []): - kwargs[opt] = False + with self._host.set_controls(parse=True, ignore_opts=ignore_opts): + return _stats(self._host.virsh(cmd, *args, **kwargs), conf.get('ignore', False)) def list_method(self, *args, **kwargs): - with self._host.set_controls(parse=True): + with self._host.set_controls(parse=True, ignore_opts=ignore_opts): return _list(self._host.virsh(cmd, *args, **kwargs)) def tune_method(self, *args, **kwargs): @@ -267,7 +267,7 @@ def none_method(self, *args, **kwargs): return self._host.virsh(cmd, *args, **kwargs) def xml_method(self, *args, **kwargs): - with self._host.set_controls(parse=True): + with self._host.set_controls(parse=True, ignore_opts=ignore_opts): xml = '\n'.join(self._host.virsh(cmd, *args, **kwargs)) return from_xml(etree.fromstring(xml), conf.get('lists', []))[conf['key']] @@ -316,6 +316,9 @@ def virsh(self, command, *args, **kwargs): is activated, the value of ``stdout`` is returned or **KvmError** exception is raised. """ + if self._ignore_opts: + for opt in self._ignore_opts: + kwargs.update({opt: False}) with self.set_controls(options_place='after', decode='utf-8'): status, stdout, stderr = self.execute('virsh', command, From 100586dcb96e9919c98747f4018c5dd18f8b56bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 18:09:20 +0100 Subject: [PATCH 060/167] Move commands mapping to a json file. --- kvm.json | 74 +++++++++++++++++++++++++++++++++++++++++++ kvm.py | 96 +++----------------------------------------------------- setup.py | 5 +++ 3 files changed, 84 insertions(+), 91 deletions(-) create mode 100644 kvm.json diff --git a/kvm.json b/kvm.json new file mode 100644 index 0000000..8e61d2c --- /dev/null +++ b/kvm.json @@ -0,0 +1,74 @@ +{"hypervisor": { + "version": {"cmd": "version", "type": "dict"}, + "sysinfo": {"cmd": "sysinfo", "type": "dict"}, + "maxvcpus": {"cmd": "maxvcpus", "type": "str", "convert": "int"}, + "nodeinfo": {"cmd": "nodeinfo", "type": "dict"}, + "nodecpumap": {"cmd": "nodecpumap", "type": "dict"}, + "nodecpustats": {"cmd": "nodecpustats", "type": "dict"}, + "nodememstats": {"cmd": "nodememstats", "type": "dict"}, + "nodesuspend": {"cmd": "nodesuspend", "type": "none"}, + "node_memory_tune": {"cmd": "node-memory-tune", "type": "none"}, + "capabilities": {"cmd": "capabilities", "type": "xml", "key": "capabilities"}, + "domcapabilities": {"cmd": "domcapabilities", "type": "xml", "key": "domainCapabilities"}, + "freecell": {"cmd": "freecell", "type": "dict"}, + "freepages": {"cmd": "freepages", "type": "dict"}, + "allocpages": {"cmd": "allocpages", "type": "none"}}, + "domain": { + "autostart": {"cmd": "autostart", "type": "none"}, + "inject_nmi": {"cmd": "inject_nmi", "type": "none"}, + "create": {"cmd": "create", "type": "none"}, + "define": {"cmd": "define", "type": "none"}, + "desc": {"cmd": "desc", "type": "dict"}, + "destroy": {"cmd": "destroy", "type": "none"}, + "blkstat": {"cmd": "domblkstat", "type": "stats", "ignore": true, "disable": ["human"]}, + "ifstat": {"cmd": "domifstat", "type": "stats", "ignore": true}, + "if_setlink": {"cmd": "domif-setlink", "type": "none"}, + "if_getlink": {"cmd": "domif-getlink", "type": "none"}, + "iftune": {"cmd": "domiftune", "type": "tune"}, + "memstat": {"cmd": "dommemstat", "type": "stats"}, + "blkinfo": {"cmd": "domblkinfo", "type": "dict"}, + "blklist": {"cmd": "domblklist", "type": "list"}, + "iflist": {"cmd": "domiflist", "type": "list"}, + "blkiotune": {"type": "tune"}, + "blkdeviotune": {"type": "tune"}, + "blockresize": {"type": "none"}, + "display": {"cmd": "domdisplay", "type": "str"}, + "info": {"cmd": "dominfo", "type": "dict"}, + "uuid": {"cmd": "domuuid", "type": "str"}, + "id": {"cmd": "domid", "type": "str", "convert": "int"}, + "name": {"cmd": "domname", "type": "str"}, + "state": {"cmd": "domstate", "type": "str"}, + "control": {"cmd": "domcontrol", "type": "str"}, + "coredump": {"cmd": "dump", "type": "none"}, + "conf": {"cmd": "dumpxml", "type": "xml", "key": "domain", "lists": ["disk", "interface"]}, + "managedsave": {"type": "none"}, + "managedsave_remove": {"cmd": "managedsave-remove", "type": "none"}, + "numatune": {"type": "tune"}, + "reboot": {"type": "none"}, + "reset": {"type": "none"}, + "save": {"type": "none"}, + "save_conf": {"cmd": "save-image-dumpxml", "type": "xml", "key": "domain", "lists": ["disk", "interface"]}, + "save_define": {"cmd": "save-image-define", "type": "none"}, + "restore": {"type": "none"}, + "schedinfo": {"type": "dict"}, + "screenshot": {"type": "none"}, + "send_key": {"cmd": "send-key", "type": "none"}, + "setmem": {"type": "none"}, + "setmaxmem": {"type": "none"}, + "memtune": {"type": "tune"}, + "setvcpus": {"type": "none"}, + "shutdown": {"type": "none"}, + "start": {"type": "none"}, + "suspend": {"type": "none"}, + "resume": {"type": "none"}, + "ttyconsole": {"type": "str"}, + "undefine": {"type": "none"}, + "pmsuspend": {"cmd": "dompmsuspend", "type": "none"}, + "pmwakeup": {"cmd": "dompmwakeup", "type": "none"}, + "attach_device": {"cmd": "attach-device", "type": "none"}, + "attach_disk": {"cmd": "attach-disk", "type": "none"}, + "attach_interface": {"cmd": "attach-interface", "type": "none"}, + "detach_device": {"cmd": "detach-device", "type": "none"}, + "detach_disk": {"cmd": "detach-disk", "type": "none"}, + "detach_interface": {"cmd": "detach-interface", "type": "none"}, + "update_device": {"cmd": "update-device", "type": "none"}}} diff --git a/kvm.py b/kvm.py index 0de180e..09ad6df 100644 --- a/kvm.py +++ b/kvm.py @@ -2,6 +2,7 @@ import os import re +import json import random import string import weakref @@ -25,97 +26,10 @@ _ITEM_RE = re.compile('^.IX (?P\w+) "(?P.*)"$') -_MAPPING = {'hypervisor': {'version': {'type': 'dict'}, - 'sysinfo': {'type': 'dict'}, - 'maxvcpus': {'type': 'str', - 'convert': 'int'}, - 'nodeinfo': {'type': 'dict'}, - 'nodecpumap': {'type': 'dict'}, - 'nodecpustats': {'type': 'dict'}, - 'nodememstats': {'type': 'dict'}, - 'nodesuspend': {'type': 'none'}, - 'node_memory_tune': {'type': 'none'}, - 'capabilities': {'type': 'xml', - 'key': 'capabilities'}, - 'domcapabilities': {'type': 'xml', - 'key': 'domainCapabilities'}, - 'freecell': {'type': 'dict'}, - 'freepages': {'type': 'dict'}, - 'allocpages': {'type': 'none'}}, - 'domain': {'autostart': {'type': 'none'}, - 'inject-nmi': {'type': 'none'}, - 'create': {'type': 'none'}, - 'define': {'type': 'none'}, - 'desc': {'type': 'dict'}, - 'destroy': {'type': 'none'}, - 'blkstat': {'cmd': 'domblkstat', - 'type': 'stats', - 'ignore': True, - 'disable': ['human']}, - 'ifstat': {'cmd': 'domifstat', - 'type': 'stats', - 'ignore': True}, - 'if_setlink': {'cmd': 'domif-setlink', 'type': 'none'}, - 'if_getlink': {'cmd': 'domif-getlink', 'type': 'none'}, - 'iftune': {'cmd': 'domiftune', 'type': 'tune'}, - 'memstat': {'cmd': 'dommemstat', 'type': 'stats'}, - 'blkinfo': {'cmd': 'domblkinfo', 'type': 'dict'}, - 'blklist': {'cmd': 'domblklist', 'type': 'list'}, - 'iflist': {'cmd': 'domiflist', 'type': 'list'}, - 'blkiotune': {'type': 'tune'}, - 'blkdeviotune': {'type': 'tune'}, - 'blockresize': {'type': 'none'}, - 'display': {'cmd': 'domdisplay', 'type': 'str'}, - 'info': {'cmd': 'dominfo', 'type': 'dict'}, - 'uuid': {'cmd': 'domuuid', 'type': 'str'}, - 'id': {'cmd': 'domid', 'type': 'str', 'convert': 'int'}, - 'name': {'cmd': 'domname', 'type': 'str'}, - 'state': {'cmd': 'domstate', 'type': 'str'}, - 'control': {'cmd': 'domcontrol', 'type': 'str'}, - 'coredump': {'cmd': 'dump', 'type': 'none'}, - 'conf': {'cmd': 'dumpxml', - 'type': 'xml', - 'key': 'domain', - 'lists': ['disk', 'interface']}, - 'managedsave': {'type': 'none'}, - 'managedsave_remove': {'cmd': 'managedsave-remove', - 'type': 'none'}, - 'numatune': {'type': 'tune'}, - 'reboot': {'type': 'none'}, - 'reset': {'type': 'none'}, - 'save': {'type': 'none'}, - 'save_conf': {'cmd': 'save-image-dumpxml', - 'type': 'xml', - 'key': 'domain', - 'lists': ['disk', 'interface']}, - 'save_define': {'cmd': 'save-image-define', - 'type': 'none'}, - 'restore': {'type': 'none'}, - 'schedinfo': {'type': 'dict'}, - 'screenshot': {'type': 'none'}, - 'send_key': {'cmd': 'send-key', 'type': 'none'}, - 'setmem': {'type': 'none'}, - 'setmaxmem': {'type': 'none'}, - 'memtune': {'type': 'tune'}, - 'setvcpus': {'type': 'none'}, - 'shutdown': {'type': 'none'}, - 'start': {'type': 'none'}, - 'suspend': {'type': 'none'}, - 'resume': {'type': 'none'}, - 'ttyconsole': {'type': 'str'}, - 'undefine': {'type': 'none'}, - 'pmsuspend': {'cmd': 'dompmsuspend', 'type': 'none'}, - 'pmwakeup': {'cmd': 'dompmwakeup', 'type': 'none'}, - 'attach_device': {'cmd': 'attach-device', 'type': 'none'}, - 'attach_disk': {'cmd': 'attach-disk', 'type': 'none'}, - 'attach_interface': {'cmd': 'attach-interface', - 'type': 'none'}, - 'detach_device': {'cmd': 'detach-device', 'type': 'none'}, - 'detach_disk': {'cmd': 'detach-disk', 'type': 'none'}, - 'detach_interface': {'cmd': 'detach-interface', - 'type': 'none'}, - 'update_device': {'cmd': 'update-device', 'type': 'none'}, - +__MAPFILE = os.path.join(os.path.dirname(__file__), 'kvm.json') +_MAPPING = json.loads(''.join([line + for line in open(__MAPFILE).readlines() + if not line.startswith('#')])) RUNNING = 'running' IDLE = 'idle' diff --git a/setup.py b/setup.py index a3d44db..1dd747f 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- from distutils.core import setup +from distutils.command.install import INSTALL_SCHEMES + +for scheme in INSTALL_SCHEMES.values(): + scheme['data'] = scheme['purelib'] setup ( name='kvm', @@ -8,6 +12,7 @@ author_email='francois.menabe@gmail.com', py_modules=['kvm'], licence='LICENCE.txt', + data_files=[('', ['kvm.json'])], description='An API for managing KVM host.', long_description=open('README.md').read(), install_requires=[ From 8c60cf63031923872ed53134a57f7ec3fe838264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 18:20:04 +0100 Subject: [PATCH 061/167] Change the way to generate properties objects. --- kvm.py | 148 +++++++++++++++++++++++++++------------------------------ 1 file changed, 70 insertions(+), 78 deletions(-) diff --git a/kvm.py b/kvm.py index 09ad6df..946531b 100644 --- a/kvm.py +++ b/kvm.py @@ -11,7 +11,7 @@ from collections import OrderedDict import sys -#_SELF = sys.modules[__name__] +_SELF = sys.modules[__name__] _BUILTINS = sys.modules['builtins' if sys.version_info.major == 3 else '__builtin__'] @@ -253,16 +253,6 @@ def virsh(self, command, *args, **kwargs): return stdout[:-1] if not stdout[-1] else stdout - @property - def hypervisor(self): - return _Hypervisor(weakref.ref(self)()) - - - @property - def domain(self): - return _Domain(weakref.ref(self)()) - - def list_domains(self, **kwargs): """List domains. **kwargs** can contains any option supported by the virsh command. It can also contains a **state** argument which is a @@ -325,81 +315,87 @@ def image(self): return _Image(weakref.ref(self)()) + + for property_name, property_methods in _MAPPING.items(): + property_obj = type('_%s' % property_name.capitalize(), + (object,), + dict(__init__=__init)) + + for method_name, method_conf in property_methods.items(): + __add_method(property_obj, method_name, method_conf) + # getattr(Hypervisor, method['name']).__doc__ = '\n'.join(method['doc']) + + for method_name in dir(_SELF): + if method_name.startswith('__%s' % property_name): + method = method_name.replace('__%s_' % property_name, '') + setattr(property_obj, method, getattr(_SELF, method_name)) + setattr(Hypervisor, property_name, property(property_obj)) + + return Hypervisor() -class _Hypervisor(object): - def __init__(self, host): - self._host = host +def __init(self, host): + self._host = host -for mname, mconf in _MAPPING['hypervisor'].items(): - __add_method(_Hypervisor, mname, mconf) +def __domain_time(self, domain, **kwargs): + kwargs.pop('pretty', None) + if not kwargs: + from datetime import datetime + with self._host.set_controls(parse=True): + time = self._host.virsh('domtime', domain, **kwargs)[0] + return datetime.fromtimestamp(int(time.split(':')[1])) + else: + return self._host.virsh('domtime', domain, **kwargs) + + +def __domain_cpustats(self, domain, **kwargs): + with self._host.set_controls(parse=True): + lines = self._host.virsh('cpu-stats', domain, **kwargs) + stats = {} + cur_cpu = '' + for line in lines: + if not line.startswith('\t'): + cur_cpu = line[:-1].lower() + stats.setdefault(cur_cpu, {}) + else: + param, value, unit = line[1:].split() + stats[cur_cpu][param] = '%s %s' % (value, unit) + return stats -class _Domain(object): - def __init__(self, host): - self._host = host +def __domain_stop(self, domain, timeout=30, force=False): + import signal, time -# def gen_conf(self, conf): -# return etree.tostring(to_xml('domain', conf), pretty_print=True) + def timeout_handler(signum, frame): + raise TimeoutException() + # Check guest exists. + if domain not in self._host.list_domains(all=True): + return [False, '', 'Domain not found'] - def time(self, domain, **kwargs): - kwargs.pop('pretty', None) - if not kwargs: - from datetime import datetime - with self._host.set_controls(parse=True): - time = self._host.virsh('domtime', domain, **kwargs)[0] - return datetime.fromtimestamp(int(time.split(':')[1])) - else: - return self._host.virsh('domtime', domain, **kwargs) + self.shutdown(domain) + old_handler = signal.signal(signal.SIGALRM, timeout_handler) + signal.alarm(timeout) + try: + while self.state(domain) != SHUTOFF: + print(self.state(domain), SHUTOFF) + time.sleep(1) + except TimeoutException: + if force: + status, stdout, stderr = self.destroy(domain) + if status: + stderr = 'VM has been destroyed after %ss' % timeout + return (status, stdout, stderr) + else: + return (False, '', 'VM not stopped after %ss' % timeout) + finally: + signal.signal(signal.SIGALRM, old_handler) + signal.alarm(0) + return [True, '', ''] - def cpustats(self, domain, **kwargs): - with self._host.set_controls(parse=True): - lines = self._host.virsh('cpu-stats', domain, **kwargs) - stats = {} - cur_cpu = '' - for line in lines: - if not line.startswith('\t'): - cur_cpu = line[:-1].lower() - stats.setdefault(cur_cpu, {}) - else: - param, value, unit = line[1:].split() - stats[cur_cpu][param] = '%s %s' % (value, unit) - return stats - - - def stop(self, domain, timeout=30, force=False): - import signal, time - - def timeout_handler(signum, frame): - raise TimeoutException() - - # Check guest exists. - if domain not in self._host.list_domains(all=True): - return [False, '', 'Domain not found'] - - self.shutdown(domain) - old_handler = signal.signal(signal.SIGALRM, timeout_handler) - signal.alarm(timeout) - - try: - while self.state(domain) != SHUTOFF: - time.sleep(1) - except TimeoutException: - if force: - status, stdout, stderr = self.destroy(domain) - if status: - stderr = 'VM has been destroyed after %ss' % timeout - return (status, stdout, stderr) - else: - return (False, '', 'VM not stopped after %ss' % timeout) - finally: - signal.signal(signal.SIGALRM, old_handler) - signal.alarm(0) - return [True, '', ''] class _Image(object): @@ -465,7 +461,3 @@ def unload(self, device='nbd0', **kwargs): kwargs['c'] = False kwargs['d'] = '/dev/%s' % device return self._host.execute('qemu-nbd', **kwargs) - - -for mname, mconf in _MAPPING['domain'].items(): - __add_method(_Domain, mname, mconf) From 92d72c7e2110d155c5e6b688d8048aacd367dbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 18:21:39 +0100 Subject: [PATCH 062/167] Forget to add MANIFEST file in previous commit (100586dcb96e9919c98747f4018c5dd18f8b56bc). --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..35d1ee2 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include kvm.json From 2b89268bdfd6cfc2ea59cc2227479cd63d509f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 18:23:02 +0100 Subject: [PATCH 063/167] Correct a bug with 'str' mapping with no conversion (nothing was returned). --- kvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.py b/kvm.py index 946531b..613b8a4 100644 --- a/kvm.py +++ b/kvm.py @@ -154,6 +154,7 @@ def str_method(self, *args, **kwargs): return getattr(_BUILTINS, conf['convert'])(result) except ValueError: return -1 if conf['convert'] == 'int' else result + return result def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True, ignore_opts=ignore_opts): From 60e3c02fe7d5849d54aac7d59e6db090e8401285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 20 Mar 2015 18:24:13 +0100 Subject: [PATCH 064/167] Correct the mapping for the function 'hypervisor.sysinfo'. --- kvm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 8e61d2c..d611f57 100644 --- a/kvm.json +++ b/kvm.json @@ -1,6 +1,6 @@ {"hypervisor": { "version": {"cmd": "version", "type": "dict"}, - "sysinfo": {"cmd": "sysinfo", "type": "dict"}, + "sysinfo": {"cmd": "sysinfo", "type": "xml", "key": "sysinfo"}, "maxvcpus": {"cmd": "maxvcpus", "type": "str", "convert": "int"}, "nodeinfo": {"cmd": "nodeinfo", "type": "dict"}, "nodecpumap": {"cmd": "nodecpumap", "type": "dict"}, From 049db075e946ca0fa2b4954e81c21f0fd68536cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:14:19 +0100 Subject: [PATCH 065/167] Add a method for converting values. --- kvm.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/kvm.py b/kvm.py index 613b8a4..c05901b 100644 --- a/kvm.py +++ b/kvm.py @@ -127,7 +127,8 @@ def _str_to_dict(lines): def format_key(key): return (key.strip().lower() .replace(' ', '_').replace('(', '').replace(')', '')) - return {format_key(key): (value or '').strip() + + return {format_key(key): _convert((value or '').strip()) for line in lines if line for key, value in [line.split(':')]} @@ -139,8 +140,8 @@ def _stats(lines, ignore=False): def _list(lines): - params = [param.lower() for param in re.split('\s+', lines[0])] - return [dict(zip(params, re.split('\s+', line))) for line in lines[2:]] + params = [param.lower() for param in re.split('\s+', lines[0])][1:] + return [dict(zip(params, re.split('\s+', line)[1:])) for line in lines[2:]] def __add_method(obj, method, conf): @@ -189,6 +190,16 @@ def xml_method(self, *args, **kwargs): setattr(obj, method.replace('-', '_'), locals()['%s_method' % conf['type']]) +def _convert(value): + value = value.strip() + if value.isdigit(): + return int(value) + for val, map_val in (('yes', True), ('no', False)): + if value == val: + return map_val + return value + + # # Exceptions # From 4d3a0c3120cfa390515c7eaa8052dd618f2dd276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:15:13 +0100 Subject: [PATCH 066/167] Add the function 'list_networks' allowing to list networks defined on the hypervisor. --- kvm.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kvm.py b/kvm.py index c05901b..ec61b30 100644 --- a/kvm.py +++ b/kvm.py @@ -322,6 +322,20 @@ def list_domains(self, **kwargs): return domains + def list_networks(self, **kwargs): + with self.set_controls(parse=True): + stdout = self.virsh('net-list', **kwargs) + networks = {} + for line in stdout[2:]: + line = line.split() + name, state, autostart = line[:3] + net = dict(state=state, autostart=_convert(autostart)) + if len(line) == 4: + net.update(persistent=_convert(line[3])) + networks.setdefault(name, net) + return networks + + @property def image(self): return _Image(weakref.ref(self)()) From d3857fbb991f6625fe37f50e91813d1cb90ebaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:16:57 +0100 Subject: [PATCH 067/167] Add the function 'net.autostart' that set/unset autostart of a network. --- kvm.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvm.json b/kvm.json index d611f57..188d910 100644 --- a/kvm.json +++ b/kvm.json @@ -72,3 +72,5 @@ "detach_disk": {"cmd": "detach-disk", "type": "none"}, "detach_interface": {"cmd": "detach-interface", "type": "none"}, "update_device": {"cmd": "update-device", "type": "none"}}} + "net": { + "autostart": {"cmd": "net-autostart", "type": "none"}} From a3f105ab304d5ad69ed44d5d1e8f2eca77447aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:18:14 +0100 Subject: [PATCH 068/167] Add the function 'net.create' that define and start a network from an XML file. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 188d910..083cd28 100644 --- a/kvm.json +++ b/kvm.json @@ -73,4 +73,5 @@ "detach_interface": {"cmd": "detach-interface", "type": "none"}, "update_device": {"cmd": "update-device", "type": "none"}}} "net": { - "autostart": {"cmd": "net-autostart", "type": "none"}} + "autostart": {"cmd": "net-autostart", "type": "none"}, + "create": {"cmd": "net-create", "type": "none"}} From 82e0e1987482fcd1fbedbe94e27d82351039ade1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:19:17 +0100 Subject: [PATCH 069/167] Add the function 'net.define' that define a network from an XML file. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 083cd28..264af11 100644 --- a/kvm.json +++ b/kvm.json @@ -74,4 +74,5 @@ "update_device": {"cmd": "update-device", "type": "none"}}} "net": { "autostart": {"cmd": "net-autostart", "type": "none"}, - "create": {"cmd": "net-create", "type": "none"}} + "create": {"cmd": "net-create", "type": "none"}, + "define": {"cmd": "net-define", "type": "none"}} From c5e1d7fe56c06ee67bfd27e00bf3250a1b2b9436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:20:10 +0100 Subject: [PATCH 070/167] Add the function 'net.destroy' that destroy (stop) a network. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 264af11..997d9a9 100644 --- a/kvm.json +++ b/kvm.json @@ -75,4 +75,5 @@ "net": { "autostart": {"cmd": "net-autostart", "type": "none"}, "create": {"cmd": "net-create", "type": "none"}, - "define": {"cmd": "net-define", "type": "none"}} + "define": {"cmd": "net-define", "type": "none"}, + "destroy": {"cmd": "net-destroy", "type": "none"}} From 7c6f59286605c065fe02f42f78dafa08f436bf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:21:00 +0100 Subject: [PATCH 071/167] Add the function 'net.conf' that return the configuration of a network. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 997d9a9..41ae242 100644 --- a/kvm.json +++ b/kvm.json @@ -76,4 +76,5 @@ "autostart": {"cmd": "net-autostart", "type": "none"}, "create": {"cmd": "net-create", "type": "none"}, "define": {"cmd": "net-define", "type": "none"}, - "destroy": {"cmd": "net-destroy", "type": "none"}} + "destroy": {"cmd": "net-destroy", "type": "none"}, + "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}} From ec970c3c515c906b32650ee1dac4e7fa8142268e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:22:00 +0100 Subject: [PATCH 072/167] Add the function 'net.info' that return network's parameters. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 41ae242..c984bcc 100644 --- a/kvm.json +++ b/kvm.json @@ -77,4 +77,5 @@ "create": {"cmd": "net-create", "type": "none"}, "define": {"cmd": "net-define", "type": "none"}, "destroy": {"cmd": "net-destroy", "type": "none"}, - "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}} + "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, + "info": {"cmd": "net-info", "type": "dict"}} From 8b31ca46527d3aa92f2c3d942247f3dcda55e91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:22:42 +0100 Subject: [PATCH 073/167] Add the function 'net.name' that return the name of a network based on an uuid. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index c984bcc..61bc63b 100644 --- a/kvm.json +++ b/kvm.json @@ -78,4 +78,5 @@ "define": {"cmd": "net-define", "type": "none"}, "destroy": {"cmd": "net-destroy", "type": "none"}, "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, - "info": {"cmd": "net-info", "type": "dict"}} + "info": {"cmd": "net-info", "type": "dict"}, + "name": {"cmd": "net-name", "type": "str"}} From f2d6356ca5819fe6c7eab5bfdb960e505bdaf4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:23:39 +0100 Subject: [PATCH 074/167] Add the function 'net.start' that start a network. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 61bc63b..21d4781 100644 --- a/kvm.json +++ b/kvm.json @@ -79,4 +79,5 @@ "destroy": {"cmd": "net-destroy", "type": "none"}, "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, "info": {"cmd": "net-info", "type": "dict"}, - "name": {"cmd": "net-name", "type": "str"}} + "name": {"cmd": "net-name", "type": "str"}, + "start": {"cmd": "net-start", "type": "none"}} From ea98ce4626ce23a0cbd048e6233b3f82e4830a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:24:18 +0100 Subject: [PATCH 075/167] Add the function 'net.undefine' that undefine a network. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 21d4781..b2e1cf8 100644 --- a/kvm.json +++ b/kvm.json @@ -80,4 +80,5 @@ "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, "info": {"cmd": "net-info", "type": "dict"}, "name": {"cmd": "net-name", "type": "str"}, - "start": {"cmd": "net-start", "type": "none"}} + "start": {"cmd": "net-start", "type": "none"}, + "undefine": {"cmd": "net-undefine", "type": "none"}} From b3bc2a046c65946bb394a7d15aacbe1ae16c9d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:26:20 +0100 Subject: [PATCH 076/167] Add the function 'net.uuid' that return the uuid of a network based of is name. --- kvm.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kvm.json b/kvm.json index b2e1cf8..6ddeec3 100644 --- a/kvm.json +++ b/kvm.json @@ -71,7 +71,7 @@ "detach_device": {"cmd": "detach-device", "type": "none"}, "detach_disk": {"cmd": "detach-disk", "type": "none"}, "detach_interface": {"cmd": "detach-interface", "type": "none"}, - "update_device": {"cmd": "update-device", "type": "none"}}} + "update_device": {"cmd": "update-device", "type": "none"}}, "net": { "autostart": {"cmd": "net-autostart", "type": "none"}, "create": {"cmd": "net-create", "type": "none"}, @@ -81,4 +81,5 @@ "info": {"cmd": "net-info", "type": "dict"}, "name": {"cmd": "net-name", "type": "str"}, "start": {"cmd": "net-start", "type": "none"}, - "undefine": {"cmd": "net-undefine", "type": "none"}} + "undefine": {"cmd": "net-undefine", "type": "none"}, + "uuid": {"cmd": "net-uuid", "type": "str"}}} From fcc08cc1edc51557cde612df7fef08a897af6106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sat, 21 Mar 2015 12:27:31 +0100 Subject: [PATCH 077/167] Add the function 'net.update' that update a network. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 6ddeec3..667a255 100644 --- a/kvm.json +++ b/kvm.json @@ -82,4 +82,5 @@ "name": {"cmd": "net-name", "type": "str"}, "start": {"cmd": "net-start", "type": "none"}, "undefine": {"cmd": "net-undefine", "type": "none"}, - "uuid": {"cmd": "net-uuid", "type": "str"}}} + "uuid": {"cmd": "net-uuid", "type": "str"}, + "update": {"cmd": "net-update", "type": "none"}}} From fc1e85c69a69d043a66e97a474420d5e5cd03f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:15:08 +0100 Subject: [PATCH 078/167] Add the function 'list_interfaces' allowing to list hypervisor's interfaces. --- kvm.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kvm.py b/kvm.py index ec61b30..6855785 100644 --- a/kvm.py +++ b/kvm.py @@ -336,6 +336,14 @@ def list_networks(self, **kwargs): return networks + def list_interfaces(self, **kwargs): + with self.set_controls(parse=True): + stdout = self.virsh('iface-list', **kwargs) + return {name: {'state': state, 'mac': mac} + for line in self.virsh('iface-list', **kwargs)[2:] + for name, state, mac in [line.split()]} + + @property def image(self): return _Image(weakref.ref(self)()) From b0bf5b10a88318d9b3e9c07f7c964f17089d39e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:16:45 +0100 Subject: [PATCH 079/167] Add the function 'iface.bridge' that create a new bridge. --- kvm.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 667a255..026ce59 100644 --- a/kvm.json +++ b/kvm.json @@ -78,9 +78,12 @@ "define": {"cmd": "net-define", "type": "none"}, "destroy": {"cmd": "net-destroy", "type": "none"}, "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, +# "event": {}, "info": {"cmd": "net-info", "type": "dict"}, "name": {"cmd": "net-name", "type": "str"}, "start": {"cmd": "net-start", "type": "none"}, "undefine": {"cmd": "net-undefine", "type": "none"}, "uuid": {"cmd": "net-uuid", "type": "str"}, - "update": {"cmd": "net-update", "type": "none"}}} + "update": {"cmd": "net-update", "type": "none"}} + "iface": { + "bridge": {"cmd": "iface-bridge", "type": "none"}}} From 6a16a09442294140eaad53834a4bf05fff3f0af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:23:02 +0100 Subject: [PATCH 080/167] Add the function 'iface.define' that define an interface from a XML file. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 026ce59..1af9ec4 100644 --- a/kvm.json +++ b/kvm.json @@ -86,4 +86,5 @@ "uuid": {"cmd": "net-uuid", "type": "str"}, "update": {"cmd": "net-update", "type": "none"}} "iface": { - "bridge": {"cmd": "iface-bridge", "type": "none"}}} + "bridge": {"cmd": "iface-bridge", "type": "none"}, + "define": {"cmd": "iface-define", "type": "none"}}} From b799efa420e9bbb81ff02afda372d21536a81ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:24:00 +0100 Subject: [PATCH 081/167] Add the function 'iface.destroy' that destroy (stop) an interface. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 1af9ec4..0156aa0 100644 --- a/kvm.json +++ b/kvm.json @@ -87,4 +87,5 @@ "update": {"cmd": "net-update", "type": "none"}} "iface": { "bridge": {"cmd": "iface-bridge", "type": "none"}, - "define": {"cmd": "iface-define", "type": "none"}}} + "define": {"cmd": "iface-define", "type": "none"}, + "destroy": {"cmd": "iface-destroy", "type": "none"}}} From 5a44e2243d05e9bf860f468b97913d7d9fdcb639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:24:42 +0100 Subject: [PATCH 082/167] Add the function 'iface.conf' that return the configuration of an interface. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 0156aa0..4a9765d 100644 --- a/kvm.json +++ b/kvm.json @@ -88,4 +88,5 @@ "iface": { "bridge": {"cmd": "iface-bridge", "type": "none"}, "define": {"cmd": "iface-define", "type": "none"}, - "destroy": {"cmd": "iface-destroy", "type": "none"}}} + "destroy": {"cmd": "iface-destroy", "type": "none"}, + "conf": {"cmd": "iface-dumpxml", "type": "xml", "key": "interface"}, From 35428ac63504a4ddcb965047ee4050da78f11e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:28:01 +0100 Subject: [PATCH 083/167] Add the function 'iface.name' that return the name of an interface from a MAC address. --- kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm.json b/kvm.json index 4a9765d..0365a38 100644 --- a/kvm.json +++ b/kvm.json @@ -90,3 +90,4 @@ "define": {"cmd": "iface-define", "type": "none"}, "destroy": {"cmd": "iface-destroy", "type": "none"}, "conf": {"cmd": "iface-dumpxml", "type": "xml", "key": "interface"}, + "name": {"cmd": "iface-name", "type": "str"}}} From c6a5f98adf496bb2874943874934988251abadd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:28:51 +0100 Subject: [PATCH 084/167] Add the function 'iface.mac' that return the MAC address of an interface. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 0365a38..461f45b 100644 --- a/kvm.json +++ b/kvm.json @@ -90,4 +90,5 @@ "define": {"cmd": "iface-define", "type": "none"}, "destroy": {"cmd": "iface-destroy", "type": "none"}, "conf": {"cmd": "iface-dumpxml", "type": "xml", "key": "interface"}, - "name": {"cmd": "iface-name", "type": "str"}}} + "name": {"cmd": "iface-name", "type": "str"}, + "mac": {"cmd": "iface-mac", "type": "str"}}} From 1e6001019e4846e9c81e48e718f40bb26134a223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:30:00 +0100 Subject: [PATCH 085/167] Add the function 'iface.start' that start an interface. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 461f45b..3706669 100644 --- a/kvm.json +++ b/kvm.json @@ -91,4 +91,5 @@ "destroy": {"cmd": "iface-destroy", "type": "none"}, "conf": {"cmd": "iface-dumpxml", "type": "xml", "key": "interface"}, "name": {"cmd": "iface-name", "type": "str"}, - "mac": {"cmd": "iface-mac", "type": "str"}}} + "mac": {"cmd": "iface-mac", "type": "str"}, + "start": {"cmd": "iface-start", "type": "none"}}} From 339d78c3305d392070a374f9d6b3656b5c58bd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:31:10 +0100 Subject: [PATCH 086/167] Add the function 'iface.unbridge' that destroy a bridge. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 3706669..16c4502 100644 --- a/kvm.json +++ b/kvm.json @@ -92,4 +92,5 @@ "conf": {"cmd": "iface-dumpxml", "type": "xml", "key": "interface"}, "name": {"cmd": "iface-name", "type": "str"}, "mac": {"cmd": "iface-mac", "type": "str"}, - "start": {"cmd": "iface-start", "type": "none"}}} + "start": {"cmd": "iface-start", "type": "none"}, + "unbridge": {"cmd": "iface-unbridge", "type": "none"}}} From 7cb6d2e176072c678e8d8dbc4dd0867b37773920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:32:16 +0100 Subject: [PATCH 087/167] Add the function 'iface.undefine' that undefine an interface. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 16c4502..c9ade69 100644 --- a/kvm.json +++ b/kvm.json @@ -93,4 +93,5 @@ "name": {"cmd": "iface-name", "type": "str"}, "mac": {"cmd": "iface-mac", "type": "str"}, "start": {"cmd": "iface-start", "type": "none"}, - "unbridge": {"cmd": "iface-unbridge", "type": "none"}}} + "unbridge": {"cmd": "iface-unbridge", "type": "none"}, + "undefine": {"cmd": "iface-undefine", "type": "none"}}} From 2b64e32ef432fde76de3f5eb683df92bf6c31b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:32:55 +0100 Subject: [PATCH 088/167] Add the function 'iface.begin' that create a snapshot of current host interface settings. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index c9ade69..aa999e6 100644 --- a/kvm.json +++ b/kvm.json @@ -94,4 +94,5 @@ "mac": {"cmd": "iface-mac", "type": "str"}, "start": {"cmd": "iface-start", "type": "none"}, "unbridge": {"cmd": "iface-unbridge", "type": "none"}, - "undefine": {"cmd": "iface-undefine", "type": "none"}}} + "undefine": {"cmd": "iface-undefine", "type": "none"}, + "begin": {"cmd": "iface-begin", "type": "none"}}} From fd258da57df3e6622b54df7c2b1c2716a6687a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:34:12 +0100 Subject: [PATCH 089/167] Add the function 'iface.commit' that declare all changes since the last 'iface-begin' as working, and delete the rollback point. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index aa999e6..9c6db95 100644 --- a/kvm.json +++ b/kvm.json @@ -95,4 +95,5 @@ "start": {"cmd": "iface-start", "type": "none"}, "unbridge": {"cmd": "iface-unbridge", "type": "none"}, "undefine": {"cmd": "iface-undefine", "type": "none"}, - "begin": {"cmd": "iface-begin", "type": "none"}}} + "begin": {"cmd": "iface-begin", "type": "none"}, + "commit": {"cmd": "iface-commit", "type": "none"}}} From d2b27568d905e9809ab923902d4d63e99aa462ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 22 Mar 2015 11:35:31 +0100 Subject: [PATCH 090/167] Add the function 'iface.rollback' that revert all host interface settings back to the state recorded in the last iface-begin. --- kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 9c6db95..d4f202f 100644 --- a/kvm.json +++ b/kvm.json @@ -96,4 +96,5 @@ "unbridge": {"cmd": "iface-unbridge", "type": "none"}, "undefine": {"cmd": "iface-undefine", "type": "none"}, "begin": {"cmd": "iface-begin", "type": "none"}, - "commit": {"cmd": "iface-commit", "type": "none"}}} + "commit": {"cmd": "iface-commit", "type": "none"}, + "rollback": {"cmd": "iface-rollback", "type": "none"}}} From 9afb48172d4fae621f768d88b4eb18c877c9df17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 23 Mar 2015 17:55:40 +0100 Subject: [PATCH 091/167] Rename the mapping function '_str_to_dict' to '_dict' and update it for manage some bugs with old versions of virsh. --- kvm.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/kvm.py b/kvm.py index 6855785..8cb5c77 100644 --- a/kvm.py +++ b/kvm.py @@ -123,14 +123,21 @@ def parse(tag_name, conf): return etree.tostring(parse(tag_name, conf), pretty_print=True).decode() -def _str_to_dict(lines): +def _dict(lines): def format_key(key): return (key.strip().lower() .replace(' ', '_').replace('(', '').replace(')', '')) - return {format_key(key): _convert((value or '').strip()) - for line in lines if line - for key, value in [line.split(':')]} + elts = {} + for line in lines: + if not line: + continue + try: + key, value = line.split(':') + except ValueError: + key, value = line.split() + elts[format_key(key)] = _convert(value or '') + return elts def _stats(lines, ignore=False): @@ -159,7 +166,7 @@ def str_method(self, *args, **kwargs): def dict_method(self, *args, **kwargs): with self._host.set_controls(parse=True, ignore_opts=ignore_opts): - return _str_to_dict(self._host.virsh(cmd, *args, **kwargs)) + return _dict(self._host.virsh(cmd, *args, **kwargs)) def stats_method(self, *args, **kwargs): with self._host.set_controls(parse=True, ignore_opts=ignore_opts): @@ -415,7 +422,6 @@ def timeout_handler(signum, frame): try: while self.state(domain) != SHUTOFF: - print(self.state(domain), SHUTOFF) time.sleep(1) except TimeoutException: if force: @@ -462,7 +468,7 @@ def info(self, path, **kwargs): status, stdout, stderr = self._host.execute('qemu-img info', path, **kwargs) if not status: raise OSError(stderr) - return _str_to_dict(stdout.splitlines()) + return _dict(stdout.splitlines()) def map(self, path, **kwargs): From 3d075314ee3a03629ee7578bc24722ad819b96a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 23 Mar 2015 17:58:48 +0100 Subject: [PATCH 092/167] Update the method 'hypervisor.sysinfo' for parsing 'entry' elements. --- kvm.json | 1 - kvm.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index d4f202f..689b135 100644 --- a/kvm.json +++ b/kvm.json @@ -1,6 +1,5 @@ {"hypervisor": { "version": {"cmd": "version", "type": "dict"}, - "sysinfo": {"cmd": "sysinfo", "type": "xml", "key": "sysinfo"}, "maxvcpus": {"cmd": "maxvcpus", "type": "str", "convert": "int"}, "nodeinfo": {"cmd": "nodeinfo", "type": "dict"}, "nodecpumap": {"cmd": "nodecpumap", "type": "dict"}, diff --git a/kvm.py b/kvm.py index 8cb5c77..cd1a618 100644 --- a/kvm.py +++ b/kvm.py @@ -380,6 +380,23 @@ def __init(self, host): self._host = host +def __hypervisor_sysinfo(self): + entry = lambda value: {elt['@name']: elt['#text'] for elt in value} + + with self._host.set_controls(parse=True): + xml = '\n'.join(self._host.virsh('sysinfo')) + sysinfo = from_xml(etree.fromstring(xml))['sysinfo'] + result = {} + for elt, elt_entries in sysinfo.items(): + if elt.startswith('@'): + result[elt[1:]] = elt_entries + continue + result.update({elt: [entry(value['entry']) for value in elt_entries] + if isinstance(elt_entries, list) + else entry(elt_entries['entry'])}) + return result + + def __domain_time(self, domain, **kwargs): kwargs.pop('pretty', None) if not kwargs: From 5bda5f0287cff1eb18f2f557e614285a3d1f7918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 12 Jun 2015 12:37:06 +0200 Subject: [PATCH 093/167] Correct setup.py. --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 1dd747f..954a217 100644 --- a/setup.py +++ b/setup.py @@ -11,11 +11,12 @@ author='François Ménabé', author_email='francois.menabe@gmail.com', py_modules=['kvm'], - licence='LICENCE.txt', data_files=[('', ['kvm.json'])], + license='MIT License', description='An API for managing KVM host.', - long_description=open('README.md').read(), + long_description=open('README.rst').read(), install_requires=[ - 'unix' + 'unix', + 'lxml' ], ) From 4946ae2b987891cd394646b5b1fef9871d1dadc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 12 Jun 2015 12:48:58 +0200 Subject: [PATCH 094/167] Correct the JSON file which was invalid. --- kvm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm.json b/kvm.json index 689b135..ef47d99 100644 --- a/kvm.json +++ b/kvm.json @@ -83,7 +83,7 @@ "start": {"cmd": "net-start", "type": "none"}, "undefine": {"cmd": "net-undefine", "type": "none"}, "uuid": {"cmd": "net-uuid", "type": "str"}, - "update": {"cmd": "net-update", "type": "none"}} + "update": {"cmd": "net-update", "type": "none"}}, "iface": { "bridge": {"cmd": "iface-bridge", "type": "none"}, "define": {"cmd": "iface-define", "type": "none"}, From 5e8ddf7b1849bb62c8483dfda0abe97fd3a219ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 2 Jul 2015 12:30:01 +0200 Subject: [PATCH 095/167] Correct invalid JSON file. --- kvm.json | 1 - 1 file changed, 1 deletion(-) diff --git a/kvm.json b/kvm.json index ef47d99..037a3ef 100644 --- a/kvm.json +++ b/kvm.json @@ -77,7 +77,6 @@ "define": {"cmd": "net-define", "type": "none"}, "destroy": {"cmd": "net-destroy", "type": "none"}, "conf": {"cmd": "net-dumpxml", "type": "xml", "key": "network"}, -# "event": {}, "info": {"cmd": "net-info", "type": "dict"}, "name": {"cmd": "net-name", "type": "str"}, "start": {"cmd": "net-start", "type": "none"}, From 43c3ce21e6e9104c29c00a9a2764a2b567070ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 2 Jul 2015 13:59:08 +0200 Subject: [PATCH 096/167] Update README and set version 1.0. --- MANIFEST.in | 1 + README.rst | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-- setup.py | 14 ++++++++---- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 35d1ee2..dcdc8f6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ include kvm.json +include README.rst diff --git a/README.rst b/README.rst index f2ddecc..6225d8b 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ Otherwise sources are on github: https://github.com/fmenabe/python-kvm Usage ----- You need to import the necessary classes from ``unix`` module. An hypervisor is -represented by the **Hypervisor** class and must wrap an object of type +represented by the **Hypervisor** object and must wrap an object of type ``unix.Local`` or ``unix.Remote``. It theorically support any Unix system, but disks manipulations need *nbd* module to be loaded so it is better to use an ``unix.linux.Linux`` host. @@ -36,8 +36,9 @@ disks manipulations need *nbd* module to be loaded so it is better to use an >>> from unix import Local, Remote, UnixError >>> from unix.linux import Linux >>> import kvm + >>> import json >>> localhost = kvm.Hypervisor(Linux(Local())) - >>> localhost.generic.nodeinfo() + >>> localhost.hypervisor.nodeinfo() {'nb_cpu': 1, 'nb_threads_per_core': 2, 'memory': 16331936, @@ -46,3 +47,62 @@ disks manipulations need *nbd* module to be loaded so it is better to use an 'nb_cores_per_cpu': 4, 'nb_cores': 8, 'cpu_freq': 1340} + >>> localhost.list_domains(all=True) + {'guest1': {'id': -1, 'state': 'shut off'}} + {'guest2': {'id': 1, 'state': 'running'}} + >>> localhost.domain.start('guest1') + # Wait a few seconds for the domain to start. + >>> localhost.domain.state('guest1') + 'running' + >>> localhost.domain.id('guest1') + 2 + >>> print(json.dumps(localhost.domain.conf('guest1'), indent=2)) + # json is use for pretty printing the dictionnary containing the + # configuration. + { + "@type": "kvm", + "name": "guest1", + "uuid": "ed68d942-5d4b-7bba-4d74-7d44d73779d3", + "memory": { + "@unit": "KiB", + "#text": "2097152" + }, + ... + } + >>> localhost.list_networks() + {'default': {'autostart': True, 'persistent': True, 'state': 'active'}} + + >>> host = unix.Remote() + >>> host.connect('hypervisor1') + >>> host = kvm.Hypervisor(Linux(host) + >>> host.hypervisor.nodeinfo() + {'cores_per_socket': 12, + 'cpu_frequency': '2200 MHz', + 'cpu_model': 'x86_64', + 'cpu_sockets': 2, + 'cpus': 24, + 'memory_size': '98974432 kB', + 'numa_cells': 1, + 'threads_per_core': 1} + >>> host.list_domains(all=True) + {'guest1': {'id': 1, 'state': 'running'}} + {'guest2': {'id': 2, 'state': 'running'}} + >>> host.domain.shutdown('guest2') + # Wait for the domain to stop. + >>> host.domain.state('guest1') + 'shut off' + + +Releases notes +-------------- +1.0 (2015-07-02) +~~~~~~~~~~~~~~~~ + * Wrapper to ''virsh'' command. + * Each type (domain, nodedev, net, ...) has one command for listing and a property regrouping commands to apply to one element. + * Properties: + * ``hypervisor``: generic commands (``nodeinfo``, ``capabilities``, ...) + * ``domains``: commands for managing a domain + * ``nodedev``: commands for managing a node device + * ``net``: commands for managing a vritual network + * ``iface``: commands for manage an interface + * Transfrom XML outputs to dictionnaries. diff --git a/setup.py b/setup.py index 954a217..145f61d 100644 --- a/setup.py +++ b/setup.py @@ -2,12 +2,9 @@ from distutils.core import setup from distutils.command.install import INSTALL_SCHEMES -for scheme in INSTALL_SCHEMES.values(): - scheme['data'] = scheme['purelib'] - setup ( name='kvm', - version='0.1', + version='1.0', author='François Ménabé', author_email='francois.menabe@gmail.com', py_modules=['kvm'], @@ -19,4 +16,13 @@ 'unix', 'lxml' ], + classifiers=[ + 'License :: OSI Approved :: MIT License', + 'Development Status :: 3 - Alpha', + 'Intended Audience :: System Administrators', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Operating System :: Unix', + 'Topic :: System :: Systems Administration'] ) From 80798943a61f31633ecda5a17ced2a6217bd1bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 2 Jul 2015 15:19:49 +0200 Subject: [PATCH 097/167] Correct setup problems with PyPi and set version to 1.0.4. --- .gitignore | 2 ++ MANIFEST.in | 2 +- README.rst | 4 ++-- kvm.py => kvm/__init__.py | 0 kvm.json => kvm/kvm.json | 0 setup.py | 8 +++++--- 6 files changed, 10 insertions(+), 6 deletions(-) rename kvm.py => kvm/__init__.py (100%) rename kvm.json => kvm/kvm.json (100%) diff --git a/.gitignore b/.gitignore index acbc4c8..02d5bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ # directory generated by python installer build/* +dist +MANIFEST # doc build directory doc/build/* diff --git a/MANIFEST.in b/MANIFEST.in index dcdc8f6..559e591 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include kvm.json +include kvm/kvm.json include README.rst diff --git a/README.rst b/README.rst index 6225d8b..8fa341d 100644 --- a/README.rst +++ b/README.rst @@ -95,8 +95,8 @@ disks manipulations need *nbd* module to be loaded so it is better to use an Releases notes -------------- -1.0 (2015-07-02) -~~~~~~~~~~~~~~~~ +1.0.4 (2015-07-02) +~~~~~~~~~~~~~~~~~~ * Wrapper to ''virsh'' command. * Each type (domain, nodedev, net, ...) has one command for listing and a property regrouping commands to apply to one element. * Properties: diff --git a/kvm.py b/kvm/__init__.py similarity index 100% rename from kvm.py rename to kvm/__init__.py diff --git a/kvm.json b/kvm/kvm.json similarity index 100% rename from kvm.json rename to kvm/kvm.json diff --git a/setup.py b/setup.py index 145f61d..0937dc5 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,15 @@ from distutils.core import setup from distutils.command.install import INSTALL_SCHEMES + setup ( name='kvm', - version='1.0', + version='1.0.4', author='François Ménabé', author_email='francois.menabe@gmail.com', - py_modules=['kvm'], - data_files=[('', ['kvm.json'])], + packages=['kvm'], + package_dir={'kvm': 'kvm'}, + package_data={'kvm': ['kvm.json']}, license='MIT License', description='An API for managing KVM host.', long_description=open('README.rst').read(), From 8fad97e4528ca47af198cc107b9c59d8735c712d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 3 Dec 2015 14:13:49 +0100 Subject: [PATCH 098/167] Correct a bug when parsing XML files if there are multiple elements (like interfaces) and 'force_lists' is set. --- kvm/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index cd1a618..e3de558 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -84,7 +84,7 @@ def from_xml(elt, force_lists=[]): for child in childs: child = from_xml(child, force_lists) child_tag = list(child.keys())[0] - if child_tag in force_lists: + if child_tag not in elts and child_tag in force_lists: elts[child_tag] = [] if child_tag in elts: if not isinstance(elts[child_tag], list): From 2f4a2c95beb45256ee149161aa63e111617224f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 3 Dec 2015 14:21:37 +0100 Subject: [PATCH 099/167] Update README and increment version. --- README.rst | 4 ++++ setup.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8fa341d..f1a5cfe 100644 --- a/README.rst +++ b/README.rst @@ -95,6 +95,10 @@ disks manipulations need *nbd* module to be loaded so it is better to use an Releases notes -------------- +1.0.5 (2015-12-03) +~~~~~~~~~~~~~~~~~~ + * Correct a bug when parsing XML (https://github.com/fmenabe/python-kvm/commit/8fad97e4528ca47af198cc107b9c59d8735c712d) + 1.0.4 (2015-07-02) ~~~~~~~~~~~~~~~~~~ * Wrapper to ''virsh'' command. diff --git a/setup.py b/setup.py index 0937dc5..7035c77 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup ( name='kvm', - version='1.0.4', + version='1.0.5', author='François Ménabé', author_email='francois.menabe@gmail.com', packages=['kvm'], @@ -25,6 +25,7 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Operating System :: Unix', 'Topic :: System :: Systems Administration'] ) From 9fcd15264e6745b0da82bed1614274ee9d9d57ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 12 Jan 2016 18:33:07 +0100 Subject: [PATCH 100/167] Correct a bug that raised an error with python 2.7. --- kvm/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index e3de558..b14abb5 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -356,9 +356,8 @@ def image(self): return _Image(weakref.ref(self)()) - for property_name, property_methods in _MAPPING.items(): - property_obj = type('_%s' % property_name.capitalize(), + property_obj = type('_%s' % str(property_name).capitalize(), (object,), dict(__init__=__init)) From 667456f739a7c4a9d3309be93b66fec26706e4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 19 Jan 2016 19:15:43 +0100 Subject: [PATCH 101/167] Add the function 'hypervisor.sysinfo' that print hypervisor system information. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 037a3ef..8a237aa 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -11,7 +11,8 @@ "domcapabilities": {"cmd": "domcapabilities", "type": "xml", "key": "domainCapabilities"}, "freecell": {"cmd": "freecell", "type": "dict"}, "freepages": {"cmd": "freepages", "type": "dict"}, - "allocpages": {"cmd": "allocpages", "type": "none"}}, + "allocpages": {"cmd": "allocpages", "type": "none"}, + "sysinfo": {"cmd": "sysinfo", "type": "xml", "key": "sysinfo"}}, "domain": { "autostart": {"cmd": "autostart", "type": "none"}, "inject_nmi": {"cmd": "inject_nmi", "type": "none"}, From 196d26c10ca00628440507873cf96ba2565b75cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:32:19 +0100 Subject: [PATCH 102/167] Revert "Add the function 'hypervisor.sysinfo' that print hypervisor system information." This reverts commit 667456f739a7c4a9d3309be93b66fec26706e4a9. --- kvm/kvm.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 8a237aa..037a3ef 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -11,8 +11,7 @@ "domcapabilities": {"cmd": "domcapabilities", "type": "xml", "key": "domainCapabilities"}, "freecell": {"cmd": "freecell", "type": "dict"}, "freepages": {"cmd": "freepages", "type": "dict"}, - "allocpages": {"cmd": "allocpages", "type": "none"}, - "sysinfo": {"cmd": "sysinfo", "type": "xml", "key": "sysinfo"}}, + "allocpages": {"cmd": "allocpages", "type": "none"}}, "domain": { "autostart": {"cmd": "autostart", "type": "none"}, "inject_nmi": {"cmd": "inject_nmi", "type": "none"}, From 55b0a2be25c885c9455938c71ca95ccf5d0abab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:33:19 +0100 Subject: [PATCH 103/167] Add a 'pprint' function allowing to pretty printing OrderedDict. --- kvm/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index b14abb5..8e78e7b 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -41,6 +41,13 @@ SUSPENDED = 'pmsuspended' +def pprint(value): + return {key: pprint(val) if isinstance(val, (OrderedDict, dict)) + else [pprint(elt) for elt in val] + if isinstance(val, list) + else val + for key, val in value.items()} + # # Functions for generating datas. # From 0b219e9c3fb08387e3e49a90e2d3230506aa32ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:35:08 +0100 Subject: [PATCH 104/167] Add the function 'hypervisor.uri' that print the current URI. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 037a3ef..a310d05 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -1,8 +1,9 @@ {"hypervisor": { "version": {"cmd": "version", "type": "dict"}, - "maxvcpus": {"cmd": "maxvcpus", "type": "str", "convert": "int"}, + "uri": {"cmd": "uri", "type": "str"}, "nodeinfo": {"cmd": "nodeinfo", "type": "dict"}, "nodecpumap": {"cmd": "nodecpumap", "type": "dict"}, + "maxvcpus": {"cmd": "maxvcpus", "type": "str", "convert": "int"}, "nodecpustats": {"cmd": "nodecpustats", "type": "dict"}, "nodememstats": {"cmd": "nodememstats", "type": "dict"}, "nodesuspend": {"cmd": "nodesuspend", "type": "none"}, From 93f7a192b8b9899184e3d705ad610e9b8d6ffae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:38:17 +0100 Subject: [PATCH 105/167] Improve function 'hypervisor.node_memory_tune'. --- kvm/__init__.py | 6 ++++++ kvm/kvm.json | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index 8e78e7b..472c2ee 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -402,6 +402,12 @@ def __hypervisor_sysinfo(self): else entry(elt_entries['entry'])}) return result +def __hypervisor_node_memory_tune(self, **kwargs): + if not kwargs: + with self._host.set_controls(parse=True): + return _dict(self._host.virsh('node-memory-tune')[1:]) + else: + return self._host.virsh('node-memory-tune', **kwargs) def __domain_time(self, domain, **kwargs): kwargs.pop('pretty', None) diff --git a/kvm/kvm.json b/kvm/kvm.json index a310d05..99c7ecf 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -7,7 +7,6 @@ "nodecpustats": {"cmd": "nodecpustats", "type": "dict"}, "nodememstats": {"cmd": "nodememstats", "type": "dict"}, "nodesuspend": {"cmd": "nodesuspend", "type": "none"}, - "node_memory_tune": {"cmd": "node-memory-tune", "type": "none"}, "capabilities": {"cmd": "capabilities", "type": "xml", "key": "capabilities"}, "domcapabilities": {"cmd": "domcapabilities", "type": "xml", "key": "domainCapabilities"}, "freecell": {"cmd": "freecell", "type": "dict"}, From 4e0520f91b61f8eba244434251176a99ca04009b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:40:15 +0100 Subject: [PATCH 106/167] Some coding style changes. --- kvm/__init__.py | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index 472c2ee..e4aa040 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -59,7 +59,6 @@ def gen_uuid(): ''.join([random.choice(_CHOICES) for _ in range(0, 4)]), ''.join([random.choice(_CHOICES) for _ in range(0, 12)]))) - def gen_mac(): """Generate a random mac address.""" return ':'.join(('54', '52', '00', @@ -67,7 +66,6 @@ def gen_mac(): ''.join([random.choice(_CHOICES) for _ in range(0, 2)]), ''.join([random.choice(_CHOICES) for _ in range(0, 2)]))) - def from_xml(elt, force_lists=[]): """Recursive function that transform an XML element to a dictionnary. **elt** must be of type ``lxml.etree.Element``.""" @@ -105,7 +103,6 @@ def from_xml(elt, force_lists=[]): result[tag] = value return result - def to_xml(tag_name, conf): def parse(tag_name, conf): tag = etree.Element(tag_name) @@ -129,7 +126,6 @@ def parse(tag_name, conf): return tag return etree.tostring(parse(tag_name, conf), pretty_print=True).decode() - def _dict(lines): def format_key(key): return (key.strip().lower() @@ -146,18 +142,15 @@ def format_key(key): elts[format_key(key)] = _convert(value or '') return elts - def _stats(lines, ignore=False): return {elts[1 if ignore else 0]: elts[2 if ignore else 1] for line in lines if line for elts in [line.split()]} - def _list(lines): params = [param.lower() for param in re.split('\s+', lines[0])][1:] return [dict(zip(params, re.split('\s+', line)[1:])) for line in lines[2:]] - def __add_method(obj, method, conf): cmd = conf.get('cmd', method) ignore_opts = conf.pop('disable', []) @@ -203,7 +196,6 @@ def xml_method(self, *args, **kwargs): setattr(obj, method.replace('-', '_'), locals()['%s_method' % conf['type']]) - def _convert(value): value = value.strip() if value.isdigit(): @@ -221,7 +213,6 @@ class KvmError(Exception): """Main exception for this module.""" pass - class TimeoutException(Exception): """Exception raise when a timeout is exceeded.""" pass @@ -249,7 +240,6 @@ def __init__(self): for control, value in _CONTROLS.items(): setattr(self, '_%s' % control, value) - def virsh(self, command, *args, **kwargs): """Wrap the execution of the virsh command. It set a control for putting options after the virsh **command**. If **parse** control @@ -259,11 +249,9 @@ def virsh(self, command, *args, **kwargs): if self._ignore_opts: for opt in self._ignore_opts: kwargs.update({opt: False}) + with self.set_controls(options_place='after', decode='utf-8'): - status, stdout, stderr = self.execute('virsh', - command, - *args, - **kwargs) + status, stdout, stderr = self.execute('virsh', command, *args, **kwargs) # Clean stdout and stderr. if stdout: stdout = stdout.rstrip('\n') @@ -278,7 +266,6 @@ def virsh(self, command, *args, **kwargs): stdout = stdout.splitlines() return stdout[:-1] if not stdout[-1] else stdout - def list_domains(self, **kwargs): """List domains. **kwargs** can contains any option supported by the virsh command. It can also contains a **state** argument which is a @@ -335,7 +322,6 @@ def list_domains(self, **kwargs): return domains - def list_networks(self, **kwargs): with self.set_controls(parse=True): stdout = self.virsh('net-list', **kwargs) @@ -349,7 +335,6 @@ def list_networks(self, **kwargs): networks.setdefault(name, net) return networks - def list_interfaces(self, **kwargs): with self.set_controls(parse=True): stdout = self.virsh('iface-list', **kwargs) @@ -362,7 +347,6 @@ def list_interfaces(self, **kwargs): def image(self): return _Image(weakref.ref(self)()) - for property_name, property_methods in _MAPPING.items(): property_obj = type('_%s' % str(property_name).capitalize(), (object,), @@ -378,14 +362,12 @@ def image(self): setattr(property_obj, method, getattr(_SELF, method_name)) setattr(Hypervisor, property_name, property(property_obj)) - return Hypervisor() def __init(self, host): self._host = host - def __hypervisor_sysinfo(self): entry = lambda value: {elt['@name']: elt['#text'] for elt in value} @@ -419,7 +401,6 @@ def __domain_time(self, domain, **kwargs): else: return self._host.virsh('domtime', domain, **kwargs) - def __domain_cpustats(self, domain, **kwargs): with self._host.set_controls(parse=True): lines = self._host.virsh('cpu-stats', domain, **kwargs) @@ -434,7 +415,6 @@ def __domain_cpustats(self, domain, **kwargs): stats[cur_cpu][param] = '%s %s' % (value, unit) return stats - def __domain_stop(self, domain, timeout=30, force=False): import signal, time @@ -466,66 +446,52 @@ def timeout_handler(signum, frame): return [True, '', ''] - class _Image(object): def __init__(self, host): self._host = host - def check(self, path, **kwargs): return self._host.execute('qemu-img check', path, **kwargs) - def create(self, path, size, **kwargs): return self._host.execute('qemu-img create', path, size, **kwargs) - def commit(self, path, **kwargs): return self._host.execute('qemu-img commit', path, **kwargs) - def compare(self, *paths, **kwargs): return self._host.execute('qemu-img compare', *paths, **kwargs) - def convert(self, src_path, dst_path, **kwargs): with self._host.set_controls(options_place='after'): return self._host.execute('qemu-img convert', src_path, dst_path, **kwargs) - def info(self, path, **kwargs): status, stdout, stderr = self._host.execute('qemu-img info', path, **kwargs) if not status: raise OSError(stderr) return _dict(stdout.splitlines()) - def map(self, path, **kwargs): return self._host.execute('qemu-img map', path, **kwargs) - def snapshot(self, path, **kwargs): return self._host.execute('qemu-img snapshot', path, **kwargs) - def rebase(self, path, **kwargs): return self._host.execute('qemu-img rebase', path, **kwargs) - def resize(self, path, size): return self._host.execute('qemu-img resize', path, size) - def amend(self, path, **kwargs): return self._host.execute('qemu-img amend', path, **kwargs) - def load(self, path, device='nbd0', **kwargs): kwargs['c'] = '/dev/%s' % device kwargs['d'] = False return self._host.execute('qemu-nbd', path, **kwargs) - def unload(self, device='nbd0', **kwargs): kwargs['c'] = False kwargs['d'] = '/dev/%s' % device From 06fe75f67c3e20b083c04830259eae1d7b53bfa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 24 Jan 2016 11:43:20 +0100 Subject: [PATCH 107/167] Correct a bug in dict outputs where there is an invalid line. --- kvm/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index e4aa040..3f84a68 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -138,7 +138,10 @@ def format_key(key): try: key, value = line.split(':') except ValueError: - key, value = line.split() + try: + key, value = line.split() + except ValueError: + continue elts[format_key(key)] = _convert(value or '') return elts From 21c594e66e984701cc1c2e79eb39f2592af06ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:16:39 +0100 Subject: [PATCH 108/167] Add the function 'list_pools' allowing to list hypervisor's pools. --- kvm/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index 3f84a68..3c93054 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -345,6 +345,21 @@ def list_interfaces(self, **kwargs): for line in self.virsh('iface-list', **kwargs)[2:] for name, state, mac in [line.split()]} + def list_pools(self, **kwargs): + with self.set_controls(parse=True): + stdout = self.virsh('pool-list', **kwargs) + pools = {} + for line in stdout[2:]: + line = line.split() + name, state, autostart = line[:3] + pool = dict(state=state, autostart=_convert(autostart)) + if len(line) > 3: + pool.update(persistent=_convert(line[3]), + capacity=' '.join(line[4:6]), + allocation=' '.join(line[6:8]), + available=' '.join(line[8:10])) + pools.setdefault(line[0], pool) + return pools @property def image(self): From 12000aa64b4fc6868b5c2b0e0c79282e9a5e9c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:18:44 +0100 Subject: [PATCH 109/167] Add the function 'pool.define' that define a pool from an XML file. --- kvm/kvm.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 99c7ecf..7cffc79 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -95,4 +95,6 @@ "undefine": {"cmd": "iface-undefine", "type": "none"}, "begin": {"cmd": "iface-begin", "type": "none"}, "commit": {"cmd": "iface-commit", "type": "none"}, - "rollback": {"cmd": "iface-rollback", "type": "none"}}} + "rollback": {"cmd": "iface-rollback", "type": "none"}}, + "pool": { + "define": {"cmd": "pool-define", "type": "none"}}} From 676e6014868c89de76caf8352bcf0e434a72d5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:26:16 +0100 Subject: [PATCH 110/167] Add the function 'pool.autostart' that set/unset autostart of a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 7cffc79..17cf452 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -97,4 +97,5 @@ "commit": {"cmd": "iface-commit", "type": "none"}, "rollback": {"cmd": "iface-rollback", "type": "none"}}, "pool": { + "autostart": {"cmd": "pool-autostart", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}}} From b43a68322f6b9b36e15b7b6c34e7ee9fa7e79b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:30:31 +0100 Subject: [PATCH 111/167] Add the function 'pool.build' that build a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 17cf452..3de117d 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -98,4 +98,5 @@ "rollback": {"cmd": "iface-rollback", "type": "none"}}, "pool": { "autostart": {"cmd": "pool-autostart", "type": "none"}, + "build": {"cmd": "pool-build", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}}} From d6decc491e1ad6fd36baea6400e9a999976491d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:32:29 +0100 Subject: [PATCH 112/167] Add the function 'pool.start' that start a pool. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 3de117d..398f5dd 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -99,4 +99,5 @@ "pool": { "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, - "define": {"cmd": "pool-define", "type": "none"}}} + "define": {"cmd": "pool-define", "type": "none"}, + "start": {"cmd": "pool-start", "type": "none"}}} From cd74e3eb871de5e6c272c1558eb836dd8c708bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:34:11 +0100 Subject: [PATCH 113/167] Add the function 'pool.info' that returns pool's parameters. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 398f5dd..9939f9a 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -100,4 +100,5 @@ "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, + "info": {"cmd": "pool-info", "type": "dict"}, "start": {"cmd": "pool-start", "type": "none"}}} From a453ad1849e0ba4bafc567784e27a0e9362a4bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:35:17 +0100 Subject: [PATCH 114/167] Add the function 'pool.conf' that returns the configuration of a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 9939f9a..b8d86d6 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -100,5 +100,6 @@ "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, + "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, "start": {"cmd": "pool-start", "type": "none"}}} From 068dc91b2759f005f44e50c9d4ed08d7cc6073ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:37:36 +0100 Subject: [PATCH 115/167] Add the function 'pool.uuid' that returns the uuid of a pool based of its name. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index b8d86d6..a2bfc8d 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -102,4 +102,5 @@ "define": {"cmd": "pool-define", "type": "none"}, "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, - "start": {"cmd": "pool-start", "type": "none"}}} + "start": {"cmd": "pool-start", "type": "none"}, + "uuid": {"cmd": "pool-uuid", "type": "str"}}} From ab216ead86c9762ea8469af682d893272003eaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:38:31 +0100 Subject: [PATCH 116/167] Add the function 'pool.name' that returns the name of a pool based of its uuid. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index a2bfc8d..15a2e41 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -102,5 +102,6 @@ "define": {"cmd": "pool-define", "type": "none"}, "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, + "name": {"cmd": "pool-name", "type": "str"}, "start": {"cmd": "pool-start", "type": "none"}, "uuid": {"cmd": "pool-uuid", "type": "str"}}} From 31454645449852d74422a7d141d382133ea4c76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:40:28 +0100 Subject: [PATCH 117/167] Add the function 'pool.refresh' that refresh informations and volumes of a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 15a2e41..9db3c09 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -103,5 +103,6 @@ "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, "name": {"cmd": "pool-name", "type": "str"}, + "refresh": {"cmd": "pool-refresh", "type": "none"}, "start": {"cmd": "pool-start", "type": "none"}, "uuid": {"cmd": "pool-uuid", "type": "str"}}} From b8eb81874927f97b70049f3b3f6380c59bcd0d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:41:45 +0100 Subject: [PATCH 118/167] Add the function 'pool.destroy' that stop a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 9db3c09..92c9c6c 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -100,6 +100,7 @@ "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, + "destroy": {"cmd": "pool-destroy", "type": "none"}, "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, "name": {"cmd": "pool-name", "type": "str"}, From a3cfb2f4891b58c5c92de1916d609db1cd047552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:42:55 +0100 Subject: [PATCH 119/167] Add the function 'pool.undefine' that undefine the configuration of an inactive pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 92c9c6c..5fefcd2 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -106,4 +106,5 @@ "name": {"cmd": "pool-name", "type": "str"}, "refresh": {"cmd": "pool-refresh", "type": "none"}, "start": {"cmd": "pool-start", "type": "none"}, + "undefine": {"cmd": "pool-undefine", "type": "none"}, "uuid": {"cmd": "pool-uuid", "type": "str"}}} From 9209f79cd581a1c689c57878c4fe6764164e882b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:48:33 +0100 Subject: [PATCH 120/167] Add the function 'pool.define_as' that define a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 5fefcd2..bb6c63c 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -100,6 +100,7 @@ "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, + "define_as": {"cmd": "pool-define-as", "type": "none"}, "destroy": {"cmd": "pool-destroy", "type": "none"}, "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, From 6feea0f59df303f76c4a2b4e062d2462b6662b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:57:19 +0100 Subject: [PATCH 121/167] Add the function 'pool.create' that create and start a pool from an XML file. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index bb6c63c..72405d6 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -99,6 +99,7 @@ "pool": { "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, + "create": {"cmd": "pool-create", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, "define_as": {"cmd": "pool-define-as", "type": "none"}, "destroy": {"cmd": "pool-destroy", "type": "none"}, From 4bbde53affd26bc04de9318ac19fd312b27a9a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:57:37 +0100 Subject: [PATCH 122/167] Add the function 'pool.delete' that delete a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 72405d6..93557e9 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -103,6 +103,7 @@ "define": {"cmd": "pool-define", "type": "none"}, "define_as": {"cmd": "pool-define-as", "type": "none"}, "destroy": {"cmd": "pool-destroy", "type": "none"}, + "delete": {"cmd": "pool-delete", "type": "none"}, "conf": {"cmd": "pool-dumpxml", "type": "xml", "key": "pool"}, "info": {"cmd": "pool-info", "type": "dict"}, "name": {"cmd": "pool-name", "type": "str"}, From e61bdbe889949affdaf1b08325fcf88dd4d26443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Mon, 25 Jan 2016 22:58:05 +0100 Subject: [PATCH 123/167] Add the function 'pool.create_as' that create and start a pool. --- kvm/kvm.json | 1 + 1 file changed, 1 insertion(+) diff --git a/kvm/kvm.json b/kvm/kvm.json index 93557e9..ff87e10 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -100,6 +100,7 @@ "autostart": {"cmd": "pool-autostart", "type": "none"}, "build": {"cmd": "pool-build", "type": "none"}, "create": {"cmd": "pool-create", "type": "none"}, + "create_as": {"cmd": "pool-create-as", "type": "none"}, "define": {"cmd": "pool-define", "type": "none"}, "define_as": {"cmd": "pool-define-as", "type": "none"}, "destroy": {"cmd": "pool-destroy", "type": "none"}, From 1f0ba1010f22806ad5aaa5c2fc249fc3b71b99c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:35:33 +0100 Subject: [PATCH 124/167] Add the function 'list_volumes' allowing to list the volumes of a pool. --- kvm/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index 3c93054..5855e49 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -361,6 +361,21 @@ def list_pools(self, **kwargs): pools.setdefault(line[0], pool) return pools + def list_volumes(self, pool, **kwargs): + with self.set_controls(parse=True): + stdout = self.virsh('vol-list', pool, **kwargs) + volumes = {} + for line in stdout[2:]: + line = line.split() + name, path = line[:2] + volume = dict(path=path) + if len(line) > 2: + volume.update(type=line[2], + capacity=' '.join(line[3:5]), + allocation=' '.join(line[5:7])) + volumes.setdefault(name, volume) + return volumes + @property def image(self): return _Image(weakref.ref(self)()) From 2d2ed737fffd364ef3e9dccb063b0cf452f09ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:36:58 +0100 Subject: [PATCH 125/167] Add the function 'volume.create' that create a volume from an XML file. --- kvm/kvm.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index ff87e10..ee2eb0b 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -111,4 +111,6 @@ "refresh": {"cmd": "pool-refresh", "type": "none"}, "start": {"cmd": "pool-start", "type": "none"}, "undefine": {"cmd": "pool-undefine", "type": "none"}, - "uuid": {"cmd": "pool-uuid", "type": "str"}}} + "uuid": {"cmd": "pool-uuid", "type": "str"}}, + "volume": { + "create": {"cmd": "vol-create", "type": "none"}}} From d77856c59bb43f5c3c43021280a7bef3786de0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:38:42 +0100 Subject: [PATCH 126/167] Add the function 'volume.create_from' that create a volume from an XML file and based on another volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index ee2eb0b..902f286 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -113,4 +113,5 @@ "undefine": {"cmd": "pool-undefine", "type": "none"}, "uuid": {"cmd": "pool-uuid", "type": "str"}}, "volume": { - "create": {"cmd": "vol-create", "type": "none"}}} + "create": {"cmd": "vol-create", "type": "none"}, + "create_from": {"cmd": "vol-create-from", "type": "none"}}} From 60697ea8b20831a6947c79c47d0143b8197717a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:39:10 +0100 Subject: [PATCH 127/167] Add the function 'volume.create_as' that create a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 902f286..f2a5900 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -114,4 +114,5 @@ "uuid": {"cmd": "pool-uuid", "type": "str"}}, "volume": { "create": {"cmd": "vol-create", "type": "none"}, - "create_from": {"cmd": "vol-create-from", "type": "none"}}} + "create_from": {"cmd": "vol-create-from", "type": "none"}, + "create_as": {"cmd": "vol-create-as", "type": "none"}}} From 7e869b75f6b61bae1424d150d0cf069db142c82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:40:40 +0100 Subject: [PATCH 128/167] Add the function 'volume.clone' that clone an existing volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index f2a5900..2e94cd4 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -115,4 +115,5 @@ "volume": { "create": {"cmd": "vol-create", "type": "none"}, "create_from": {"cmd": "vol-create-from", "type": "none"}, - "create_as": {"cmd": "vol-create-as", "type": "none"}}} + "create_as": {"cmd": "vol-create-as", "type": "none"}, + "clone": {"cmd": "vol-clone", "type": "none"}}} From a59b19c98185d81c35799d57f729a1583b5b2134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:42:26 +0100 Subject: [PATCH 129/167] Add the function 'volume.delete' that delete a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 2e94cd4..339b73a 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -116,4 +116,5 @@ "create": {"cmd": "vol-create", "type": "none"}, "create_from": {"cmd": "vol-create-from", "type": "none"}, "create_as": {"cmd": "vol-create-as", "type": "none"}, - "clone": {"cmd": "vol-clone", "type": "none"}}} + "clone": {"cmd": "vol-clone", "type": "none"}, + "delete": {"cmd": "vol-delete", "type": "none"}}} From ec91ed314dd795c287ce128ff2f398537b8aba23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:43:30 +0100 Subject: [PATCH 130/167] Add the function 'volume.upload' that upload the content of a file to a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 339b73a..1c8c15e 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -117,4 +117,5 @@ "create_from": {"cmd": "vol-create-from", "type": "none"}, "create_as": {"cmd": "vol-create-as", "type": "none"}, "clone": {"cmd": "vol-clone", "type": "none"}, - "delete": {"cmd": "vol-delete", "type": "none"}}} + "delete": {"cmd": "vol-delete", "type": "none"}, + "upload": {"cmd": "vol-upload", "type": "none"}}} From 139be88851f62f6efb13ff1419d3fdb8ad2fcfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:44:37 +0100 Subject: [PATCH 131/167] Add the function 'volume.download' that download the content of a volume to a file. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 1c8c15e..ac2b63b 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -118,4 +118,5 @@ "create_as": {"cmd": "vol-create-as", "type": "none"}, "clone": {"cmd": "vol-clone", "type": "none"}, "delete": {"cmd": "vol-delete", "type": "none"}, - "upload": {"cmd": "vol-upload", "type": "none"}}} + "upload": {"cmd": "vol-upload", "type": "none"}, + "download": {"cmd": "vol-download", "type": "none"}}} From 0f402015ccd952767d40b92ceb223174ab18edbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:46:02 +0100 Subject: [PATCH 132/167] Add the function 'volume.wipe' that (securely) wipe the content of a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index ac2b63b..8bc8610 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -119,4 +119,5 @@ "clone": {"cmd": "vol-clone", "type": "none"}, "delete": {"cmd": "vol-delete", "type": "none"}, "upload": {"cmd": "vol-upload", "type": "none"}, - "download": {"cmd": "vol-download", "type": "none"}}} + "download": {"cmd": "vol-download", "type": "none"}, + "wipe": {"cmd": "vol-wipe", "type": "none"}}} From 4e82f5fa147a9960181ad12ec5f12256ee0fa22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:47:07 +0100 Subject: [PATCH 133/167] Add the function 'volume.conf' that returns the configuration of a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 8bc8610..c1bcab3 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -120,4 +120,5 @@ "delete": {"cmd": "vol-delete", "type": "none"}, "upload": {"cmd": "vol-upload", "type": "none"}, "download": {"cmd": "vol-download", "type": "none"}, - "wipe": {"cmd": "vol-wipe", "type": "none"}}} + "wipe": {"cmd": "vol-wipe", "type": "none"}, + "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}}} From 4e6d7c8c8b848061afb2b0ff4790f74bf7b00683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:52:08 +0100 Subject: [PATCH 134/167] Add the function 'volume.info' that returns the informations of a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index c1bcab3..f303113 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -121,4 +121,5 @@ "upload": {"cmd": "vol-upload", "type": "none"}, "download": {"cmd": "vol-download", "type": "none"}, "wipe": {"cmd": "vol-wipe", "type": "none"}, - "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}}} + "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}, + "info": {"cmd": "vol-info", "type": "dict"}}} From 03eb2c20f2a0b6486826c867f6dbc449445e6a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:52:57 +0100 Subject: [PATCH 135/167] Add the function 'volume.path' that returns the path of a volume based of its name. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index f303113..46c8ba6 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -122,4 +122,5 @@ "download": {"cmd": "vol-download", "type": "none"}, "wipe": {"cmd": "vol-wipe", "type": "none"}, "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}, - "info": {"cmd": "vol-info", "type": "dict"}}} + "info": {"cmd": "vol-info", "type": "dict"}, + "path": {"cmd": "vol-path", "type": "str"}}} From 2f31da4550007031851aed5ba987f205c895ab49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:53:40 +0100 Subject: [PATCH 136/167] Add the function 'volume.name' that returns the name of a volume based of its path or key. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 46c8ba6..12a5a66 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -123,4 +123,5 @@ "wipe": {"cmd": "vol-wipe", "type": "none"}, "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}, "info": {"cmd": "vol-info", "type": "dict"}, - "path": {"cmd": "vol-path", "type": "str"}}} + "path": {"cmd": "vol-path", "type": "str"}, + "name": {"cmd": "vol-name", "type": "str"}}} From cee4ce4b4e72536a8abbf0b37ae7ed7b20380fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:54:06 +0100 Subject: [PATCH 137/167] Add the function 'volume.key' that returns the key of a volume based of its name or path. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 12a5a66..7b6608c 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -124,4 +124,5 @@ "conf": {"cmd": "vol-dumpxml", "type": "xml", "key": "volume"}, "info": {"cmd": "vol-info", "type": "dict"}, "path": {"cmd": "vol-path", "type": "str"}, - "name": {"cmd": "vol-name", "type": "str"}}} + "name": {"cmd": "vol-name", "type": "str"}, + "key": {"cmd": "vol-key", "type": "str"}}} From 8d0916b294ce8aa8f8523bdc594c9d66bb1088d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:54:36 +0100 Subject: [PATCH 138/167] Add the function 'volume.resize' that resize a volume. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 7b6608c..a5e41c1 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -125,4 +125,5 @@ "info": {"cmd": "vol-info", "type": "dict"}, "path": {"cmd": "vol-path", "type": "str"}, "name": {"cmd": "vol-name", "type": "str"}, - "key": {"cmd": "vol-key", "type": "str"}}} + "key": {"cmd": "vol-key", "type": "str"}, + "resize": {"cmd": "vol-resize", "type": "none"}}} From fbae0c75b88774fc65fff08fb9bde7887b1cbb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 00:59:54 +0100 Subject: [PATCH 139/167] Begins adding usage examples. --- doc/source/examles.rst | 652 +++++++++++++++++++++++++++++++++++++++++ doc/source/index.rst | 1 + 2 files changed, 653 insertions(+) create mode 100644 doc/source/examles.rst diff --git a/doc/source/examles.rst b/doc/source/examles.rst new file mode 100644 index 0000000..150ae28 --- /dev/null +++ b/doc/source/examles.rst @@ -0,0 +1,652 @@ +******** +Examples +******** + +.. code:: + + >>> import unix, kvm + >>> host = unix.Remote() + >>> host.connect('remote_host') + >>> host = kvm.Hypervisor(host) + +Managing the hypervisor +======================= +Virsh version +~~~~~~~~~~~~~ +.. code:: + + >>> host.hypervisor.version() + {'compiled_against_library': 'libvirt 1.2.2', + 'running_hypervisor': 'QEMU 2.0.0', + 'using_api': 'QEMU 1.2.2', + 'using_library': 'libvirt 1.2.2'} + +URI +~~~ +.. code:: + + >>> host.hypervisor.uri() + 'qemu:///system' + +System information +~~~~~~~~~~~~~~~~~~ +.. code:: + + >>> host.hypervisor.sysinfo() + {'bios': {'date': '02/06/2014', 'vendor': 'HP', 'version': 'A28'}, + 'memory_device': [{'bank_locator': 'Not Specified', + 'form_factor': 'DIMM', + 'locator': 'Proc 1 DIMM 1A', + 'manufacturer': 'HP', + 'part_number': '647650-171', + 'serial_number': 'Not Specified', + 'size': '8192 MB', + 'speed': '1333 MHz', + 'type': 'DDR3', + 'type_detail': 'Synchronous Registered (Buffered)'}, + {'bank_locator': 'Not Specified', + 'form_factor': 'DIMM', + 'locator': 'Proc 1 DIMM 3E', + 'manufacturer': 'HP', + 'part_number': '647650-171', + 'serial_number': 'Not Specified', + 'size': '8192 MB', + 'speed': '1333 MHz', + 'type': 'DDR3', + 'type_detail': 'Synchronous Registered (Buffered)'}, + ... + {'bank_locator': 'Not Specified', + 'form_factor': 'DIMM', + 'locator': 'Proc 2 DIMM 12H', + 'manufacturer': 'HP', + 'part_number': '647650-171', + 'serial_number': 'Not Specified', + 'size': '8192 MB', + 'speed': '1333 MHz', + 'type': 'DDR3', + 'type_detail': 'Synchronous Registered (Buffered)'}], + 'processor': [{'external_clock': '200 MHz', + 'family': 'Opteron', + 'manufacturer': 'AMD', + 'max_speed': '3500 MHz', + 'part_number': 'Not Specified', + 'serial_number': 'Not Specified', + 'signature': 'Family 21, Model 2, Stepping 0', + 'socket_destination': 'Proc 1', + 'status': 'Populated, Enabled', + 'type': 'Central Processor', + 'version': 'AMD Opteron(tm) Processor 6376'}, + {'external_clock': '200 MHz', + 'family': 'Opteron', + 'manufacturer': 'AMD', + 'max_speed': '3500 MHz', + 'part_number': 'Not Specified', + 'serial_number': 'Not Specified', + 'signature': 'Family 21, Model 2, Stepping 0', + 'socket_destination': 'Proc 2', + 'status': 'Populated, Idle', + 'type': 'Central Processor', + 'version': 'AMD Opteron(tm) Processor 6376'}], + 'system': {'family': 'ProLiant', + 'manufacturer': 'HP', + 'product': 'ProLiant DL385p Gen8', + 'serial': 'CZJ4020390', + 'sku': '703932-421', + 'uuid': '39333037-3233-5A43-4A34-303230333930', + 'version': 'Not Specified'}, + 'type': 'smbios'} + +Basic information about the node +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: + + >>> host.hypervisor.nodeinfo() + {'cores_per_socket': 32, + 'cpu_frequency': '1400 MHz', + 'cpu_model': 'x86_64', + 'cpu_sockets': 1, + 'cpus': 32, + 'memory_size': '131919564 KiB', + 'numa_cells': 1, + 'threads_per_core': 1} + +CPU map +~~~~~~~ +.. code:: + + >>> host.hypervisor.nodecpumap() + {'cpu_map': 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy', + 'cpus_online': 32, + 'cpus_present': 32} + +CPU stats +~~~~~~~~~ +.. code:: + + >>> host.hypervisor.nodecpustats() + {'idle': 67050204750000000, + 'iowait': 47793370000000, + 'system': 1004314090000000, + 'user': 2927654340000000} + + >>> host.hypervisor.nodecpustats(percent=True) + {'idle': '90.3%', + 'iowait': '0.1%', + 'system': '1.8%', + 'usage': '9.6%', + 'user': '7.8%'} + + >>> host.hypervisor.nodecpustats(31, percent=True) + {'idle': '97.0%', + 'iowait': '0.0%', + 'system': '1.0%', + 'usage': '3.0%', + 'user': '2.0%'} + +Memory stats +~~~~~~~~~~~~ +.. code:: + + >>> host.hypervisor.nodememstats() + {'buffers': '246688 KiB', + 'cached': '97146740 KiB', + 'free': '2155148 KiB', + 'total': '131919564 KiB'} + + >>> host.hypervisor.nodememstats(0) + {'free': '1138132 KiB', 'total': '32848952 KiB'} + +Tune memory parameters +~~~~~~~~~~~~~~~~~~~~~~ +.. code:: + + >>> host.hypervisor.node_memory_tune() + {'shm_full_scans': 138, + 'shm_merge_across_nodes': 1, + 'shm_pages_shared': 424645, + 'shm_pages_sharing': 3721845, + 'shm_pages_to_scan': 100, + 'shm_pages_unshared': 3907333, + 'shm_pages_volatile': 2108845, + 'shm_sleep_millisecs': 200} + + >>> host.hypervisor.node_memory_tune(shm_pages_to_scan=150, shm_sleep_millisecs=100) + (True, '', '') + + >>> host.hypervisor.node_memory_tune() + {'shm_full_scans': 138, + 'shm_merge_across_nodes': 1, + 'shm_pages_shared': 424622, + 'shm_pages_sharing': 3721888, + 'shm_pages_to_scan': 150, + 'shm_pages_unshared': 3910168, + 'shm_pages_volatile': 2105990, + 'shm_sleep_millisecs': 100} + +Suspend host +~~~~~~~~~~~~ +.. code:: + + >>> host.hypervisor.nodesuspend('mem', 60) + (True, '', '') + +Capabilities +~~~~~~~~~~~~ +.. code:: + + >>> kvm.pprint(host.hypervisor.capabilities()) + {'guest': [{'arch': {'@name': 'i686', + 'domain': [{'@type': 'qemu'}, + {'@type': 'kvm', + 'emulator': '/usr/bin/kvm-spice', + 'machine': [{'#text': 'pc', + '@canonical': 'pc-i440fx-trusty', + '@maxCpus': '255'}, + {'#text': 'pc-1.3', '@maxCpus': '255'}, + ... + {'#text': 'pc-0.13', '@maxCpus': '255'}]}], + 'emulator': '/usr/bin/qemu-system-i386', + 'machine': [{'#text': 'pc', + '@canonical': 'pc-i440fx-trusty', + '@maxCpus': '255'}, + {'#text': 'pc-0.12', '@maxCpus': '255'}, + ... + {'#text': 'pc-0.13', '@maxCpus': '255'}], + 'wordsize': '32'}, + 'features': {'acpi': {'@default': 'on', '@toggle': 'yes'}, + 'apic': {'@default': 'on', '@toggle': 'no'}, + 'cpuselection': True, + 'deviceboot': True, + 'nonpae': True, + 'pae': True}, + 'os_type': 'hvm'}, + {'arch': {'@name': 'x86_64', + 'domain': [{'@type': 'qemu'}, + {'@type': 'kvm', + 'emulator': '/usr/bin/kvm-spice', + 'machine': [{'#text': 'pc', + '@canonical': 'pc-i440fx-trusty', + '@maxCpus': '255'}, + {'#text': 'pc-1.3', '@maxCpus': '255'}, + ... + {'#text': 'pc-0.13', '@maxCpus': '255'}]}], + 'emulator': '/usr/bin/qemu-system-x86_64', + 'machine': [{'#text': 'pc', + '@canonical': 'pc-i440fx-trusty', + '@maxCpus': '255'}, + {'#text': 'pc-1.3', '@maxCpus': '255'}, + ... + {'#text': 'pc-0.13', '@maxCpus': '255'}], + 'wordsize': '64'}, + 'features': {'acpi': {'@default': 'on', '@toggle': 'yes'}, + 'apic': {'@default': 'on', '@toggle': 'no'}, + 'cpuselection': True, + 'deviceboot': True}, + 'os_type': 'hvm'}], + 'host': {'cpu': {'arch': 'x86_64', + 'feature': [{'@name': 'bmi1'}, + {'@name': 'perfctr_nb'}, + {'@name': 'perfctr_core'}, + {'@name': 'topoext'}, + {'@name': 'nodeid_msr'}, + {'@name': 'tce'}, + {'@name': 'lwp'}, + {'@name': 'wdt'}, + {'@name': 'skinit'}, + {'@name': 'ibs'}, + {'@name': 'osvw'}, + {'@name': 'cr8legacy'}, + {'@name': 'extapic'}, + {'@name': 'cmp_legacy'}, + {'@name': 'fxsr_opt'}, + {'@name': 'mmxext'}, + {'@name': 'osxsave'}, + {'@name': 'monitor'}, + {'@name': 'ht'}, + {'@name': 'vme'}], + 'model': 'Opteron_G5', + 'topology': {'@cores': '32', '@sockets': '1', '@threads': '1'}, + 'vendor': 'AMD'}, + 'migration_features': {'live': True, + 'uri_transports': {'uri_transport': 'tcp'}}, + 'power_management': {'suspend_disk': True, 'suspend_hybrid': True}, + 'secmodel': [{'doi': '0', 'model': 'apparmor'}, + {'baselabel': [{'#text': '+110:+117', '@type': 'kvm'}, + {'#text': '+110:+117', '@type': 'qemu'}], + 'doi': '0', + 'model': 'dac'}], + 'topology': {'cells': {'@num': '4', + 'cell': [{'@id': '0', + 'cpus': {'@num': '8', + 'cpu': [{'@core_id': '0', + '@id': '0', + '@siblings': '0,2', + '@socket_id': '0'}, + {'@core_id': '1', '@id': '2', '@siblings': '0,2', '@socket_id': '0'}, + {'@core_id': '2', '@id': '4', '@siblings': '4,6', '@socket_id': '0'}, + {'@core_id': '3', '@id': '6', '@siblings': '4,6', '@socket_id': '0'}, + {'@core_id': '4', '@id': '8', '@siblings': '8,10', '@socket_id': '0'}, + {'@core_id': '5', '@id': '10', '@siblings': '8,10', '@socket_id': '0'}, + {'@core_id': '6', + '@id': '12', + '@siblings': '12,14', + '@socket_id': '0'}, + {'@core_id': '7', + '@id': '14', + '@siblings': '12,14', + '@socket_id': '0'}]}, + 'memory': {'#text': '32848952', '@unit': 'KiB'}}, + {'@id': '1', + 'cpus': {'@num': '8', + 'cpu': [{'@core_id': '0', + '@id': '16', + '@siblings': '16,18', + '@socket_id': '0'}, + .... + {'@core_id': '7', + '@id': '30', + '@siblings': '28,30', + '@socket_id': '0'}]}, + 'memory': {'#text': '33029144', '@unit': 'KiB'}}, + {'@id': '2', + 'cpus': {'@num': '8', + 'cpu': [{'@core_id': '0', + '@id': '1', + '@siblings': '1,3', + '@socket_id': '1'}, + ... + {'@core_id': '7', + '@id': '15', + '@siblings': '13,15', + '@socket_id': '1'}]}, + 'memory': {'#text': '33029148', '@unit': 'KiB'}}, + {'@id': '3', + 'cpus': {'@num': '8', + 'cpu': [{'@core_id': '0', + '@id': '17', + '@siblings': '17,19', + '@socket_id': '1'}, + ... + {'@core_id': '7', + '@id': '31', + '@siblings': '29,31', + '@socket_id': '1'}]}, + 'memory': {'#text': '33012320', '@unit': 'KiB'}}]}}, + 'uuid': '39333037-3233-5a43-4a34-303230333930'}} + +.. note:: By default the method that parse XML files return an **OrderedDict** for keeping order. ``kvm.pprint()`` function allow to pretty print **OrderedDict** dicts. + +Freecell +~~~~~~~~ +.. code:: + + >>> host.hypervisor.freecell(all=True) + {'0': '1034804 KiB', + '1': '501332 KiB', + '2': '268616 KiB', + '3': '322696 KiB', + 'total': '2127448 KiB'} + + >>> host.hypervisor.freecell(cellno=0) + {'0': '1020744 KiB'} + +Managing domains +================ + + +Managing interfaces +=================== + +Managing networks +================= + +Managing storage pools +====================== +List +~~~~ +.. code:: + + >>> host.list_pools(all=True) + {} + +Define +~~~~~~ +.. code:: + + >>> pool = {'@type': 'dir', + 'name': 'default', + 'source': True, + 'target': {'path': '/vm/disk', + 'permissions': {'group': '-1', + 'mode': '0711', + 'owner': '-1'}}} + >>> with host.open('/tmp/pool.xml', 'w') as fhandler: + ... fhandler.write(kvm.to_xml('pool', pool)) + >>> host.pool.define('/tmp/pool.xml') + (True, 'Pool default defined from /tmp/pool.xml', '') + + >>> host.list_pools(all=True, details=True) + {'default': {'allocation': '-', + 'autostart': False, + 'available': '', + 'capacity': '- -', + 'persistent': True, + 'state': 'inactive'}} + +Build +~~~~~ +.. code:: + + >>> host.listdir('/vm') + [] + + >>> host.pool.build('default') + (True, 'Pool default built', '') + + >>> host.listdir('/vm') + ['disk'] + +Start +~~~~~ +.. code:: + + >>> host.pool.start('default') + (True, 'Pool default started', '') + + >>> host.list_pools(all=True, details=True) + {'default': {'allocation': '2.48 GiB', + 'autostart': False, + 'available': '11.14 GiB', + 'capacity': '13.62 GiB', + 'persistent': True, + 'state': 'running'}} + +Autostart +~~~~~~~~~ +.. code:: + + >>> host.pool.autostart('default') + (True, 'Pool default marked as autostarted', '') + + >>> host.list_pools(all=True) + {'default': {'autostart': True, 'state': 'active'}} + + >>> host.pool.autostart('default', disable=True) + (True, 'Pool default unmarked as autostarted', '') + + >>> host.list_pools(all=True) + {'default': {'autostart': False, 'state': 'active'}} + +Info +~~~~ +.. code:: + + >>> host.pool.info('default') + {'allocation': '2.48 GiB', + 'autostart': False, + 'available': '11.14 GiB', + 'capacity': '13.62 GiB', + 'name': 'default', + 'persistent': True, + 'state': 'running', + 'uuid': '28d614d5-7e17-40fc-b866-cc4bd26eab47'} + +Conf +~~~~ +.. code:: + + >>> kvm.pprint(host.pool.conf('default')) + {'@type': 'dir', + 'allocation': {'#text': '2663366656', '@unit': 'bytes'}, + 'available': {'#text': '11965825024', '@unit': 'bytes'}, + 'capacity': {'#text': '14629191680', '@unit': 'bytes'}, + 'name': 'default', + 'source': True, + 'target': {'path': '/vm/disk', + 'permissions': {'group': '0', 'mode': '0711', 'owner': '0'}}, + 'uuid': '28d614d5-7e17-40fc-b866-cc4bd26eab47'} + +Uuid +~~~~ +.. code:: + + >>> host.pool.uuid('default') + '28d614d5-7e17-40fc-b866-cc4bd26eab47' + +Name +~~~~ +.. code:: + + >>> host.pool.name('28d614d5-7e17-40fc-b866-cc4bd26eab47') + 'default' + +Destroy +~~~~~~~ +.. code:: + + >>> host.pool.destroy('default') + (True, 'Pool default destroyed', '') + + >>> host.list_pools(all=True) + {'default': {'autostart': False, 'state': 'inactive'}} + +Undefine +~~~~~~~~ +.. code:: + + >>> host.pool.undefine('default') + (True, 'Pool default has been undefined', '') + + >>> host.list_pools(all=True) + {} + +Create +~~~~~~ +.. code:: + + >>> host.pool.create('/tmp/pool.xml') + (True, 'Pool default created from /tmp/pool.xml', '') + + >>> host.list_pools(all=True, details=True) + {'default': {'allocation': '2.48 GiB', + 'autostart': False, + 'available': '11.14 GiB', + 'capacity': '13.62 GiB', + 'persistent': False, + 'state': 'running'}} + +Delete +~~~~~~ + + +Managing volumes +================ +Create +~~~~~~ +.. code:: + + >>> host.volume.create_as('default', 'disk.qcow2', '20G', format='qcow2') + (True, 'Vol disk.qcow2 created', '') + + >>> host.list_volumes('default', details=True) + {'disk.qcow2': {'allocation': '196.00 KiB', + 'capacity': '20.00 GiB', + 'path': '/vm/disk/disk.qcow2', + 'type': 'file'}} + +.. code:: + + >>> vol = {'@type': 'file', + 'capacity': {'#text': '5368709120', '@unit': 'bytes'}, + 'key': '/vm/disk/disk3.qcow2', + 'name': 'disk3.qcow2', + 'source': True, + 'target': {'format': {'@type': 'qcow2'}, 'path': '/vm/disk/disk3.qcow2'}} + + >>> with host.open('/tmp/volume.xml', 'w') as fhandler: + fhandler.write(kvm.to_xml('volume', vol)) + + >>> host.volume.create('default', '/tmp/volume.xml') + (True, 'Vol disk3.qcow2 created from /tmp/volume.xml', '') + +Delete +~~~~~~ +.. code:: + + >>> host.volume.delete('disk.qcow2', pool='default') + (True, 'Vol disk.qcow2 deleted', '') + +Info +~~~~ +.. code:: + + >>> host.volume.info('disk.qcow2', pool='default') + {'allocation': '3.32 MiB', + 'capacity': '20.00 GiB', + 'name': 'disk.qcow2', + 'type': 'file'} + +Conf +~~~~ +.. code:: + + >>> kvm.pprint(host.volume.conf('disk.qcow2', pool='default')) + {'@type': 'file', + 'allocation': {'#text': '3485696', '@unit': 'bytes'}, + 'capacity': {'#text': '21474836480', '@unit': 'bytes'}, + 'key': '/vm/disk/disk.qcow2', + 'name': 'disk.qcow2', + 'source': True, + 'target': {'format': {'@type': 'qcow2'}, + 'path': '/vm/disk/disk.qcow2', + 'permissions': {'group': '0', 'mode': '0600', 'owner': '0'}, + 'timestamps': {'atime': '1453761773.202566393', + 'ctime': '1453761770.938733302', + 'mtime': '1453761770.918734776'}}} + +Wipe +~~~~ +.. code:: + + >>> host.volume.wipe('disk.qcow2', pool='default', algorithm='dod') + +.. note:: Other algorithms than *zero* need the ``scrub`` package to be installed. + +Clone +~~~~~ +.. code:: + + >>> host.volume.clone('disk.qcow2', 'disk2.qcow2', pool='default', prealloc_metadata=True) + (True, 'Vol disk2.qcow2 cloned from disk.qcow2', '') + + >>> host.list_volumes('default', details=True) + {'disk.qcow2': {'allocation': '524.00 KiB', + 'capacity': '2.00 GiB', + 'path': '/vm/disk/disk.qcow2', + 'type': 'file'}, + 'disk2.qcow2': {'allocation': '524.00 KiB', + 'capacity': '2.00 GiB', + 'path': '/vm/disk/disk2.qcow2', + 'type': 'file'}} + +Resize +~~~~~~ +.. code:: + + >>> host.volume.resize('disk.qcow2', '5GiB', pool='default') + (True, "Size of volume 'disk.qcow2' successfully changed to 5GiB", '') + + >>> host.list_volumes('default', details=True) + {'disk.qcow2': {'allocation': '528.00 KiB', + 'capacity': '5.00 GiB', + 'path': '/vm/disk/disk.qcow2', + 'type': 'file'}, + 'disk2.qcow2': {'allocation': '524.00 KiB', + 'capacity': '2.00 GiB', + 'path': '/vm/disk/disk2.qcow2', + 'type': 'file'}} + +Upload/Download +~~~~~~~~~~~~~~~ +.. code:: + + >>> host.listdir('/vm') + ['disk', 'modele-trusty.qcow2'] + + >>> host.volume.create_as('default', 'trusty.qcow2', '1GiB') + (True, 'Vol trusty.qcow2 created', '') + + >>> host.volume.upload(pool='default', file='/vm/modele-trusty.qcow2', vol='trusty.qcow2') + (True, '', '') + + >>> host.list_volumes('default', details=True) + {'trusty.qcow2': {'allocation': '1.83 GiB', + 'capacity': '30.00 GiB', + 'path': '/vm/disk/trusty.qcow2', + 'type': 'file'}} + + >>> host..volume.download(pool='default', file='/vm/new.qcow2', vol='trusty.qcow2')Out[205]: (True, '', '') + + >>> host.listdir('/vm') + ['disk', 'modele-trusty.qcow2', 'new.qcow2'] diff --git a/doc/source/index.rst b/doc/source/index.rst index 20a456a..eec99aa 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -11,6 +11,7 @@ Welcome to kvm's documentation! Overview API + Examples Indices and tables ================== From 94d0498552de7488ed6a575cbe6e8f6287e6ea8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 21:46:17 +0100 Subject: [PATCH 140/167] Correct a typo in the doc file with examples. --- doc/source/{examles.rst => examples.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/source/{examles.rst => examples.rst} (100%) diff --git a/doc/source/examles.rst b/doc/source/examples.rst similarity index 100% rename from doc/source/examles.rst rename to doc/source/examples.rst From 3fa8fd0f68fef0e29d80d41912c873d8c6eaea1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:00:19 +0100 Subject: [PATCH 141/167] Add the function 'secret.define' that defines a secret from an XML file. --- kvm/kvm.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index a5e41c1..a89b324 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -126,4 +126,6 @@ "path": {"cmd": "vol-path", "type": "str"}, "name": {"cmd": "vol-name", "type": "str"}, "key": {"cmd": "vol-key", "type": "str"}, - "resize": {"cmd": "vol-resize", "type": "none"}}} + "resize": {"cmd": "vol-resize", "type": "none"}}, + "secret": { + "define": {"cmd": "secret-define", "type": "none"}}} From c559a28792cf0c30520e2c26898cefd9aabd2c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:27:52 +0100 Subject: [PATCH 142/167] Add the function 'list_secrets' allowing to list secrets. --- kvm/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index 5855e49..ad83001 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -376,6 +376,15 @@ def list_volumes(self, pool, **kwargs): volumes.setdefault(name, volume) return volumes + def list_secrets(self, **kwargs): + with self.set_controls(parse=True): + stdout = self.virsh('secret-list', **kwargs) + secrets = {} + for line in stdout[2:]: + uuid, *usage = line.split() + secrets.setdefault(uuid, ' '.join(usage)) + return secrets + @property def image(self): return _Image(weakref.ref(self)()) From 3960fd9cd2e6a8d3742819735b9f41bdf456bb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:29:07 +0100 Subject: [PATCH 143/167] Add the function 'secret.conf' that returns the configuration of a secret. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index a89b324..eef75a4 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -128,4 +128,5 @@ "key": {"cmd": "vol-key", "type": "str"}, "resize": {"cmd": "vol-resize", "type": "none"}}, "secret": { - "define": {"cmd": "secret-define", "type": "none"}}} + "define": {"cmd": "secret-define", "type": "none"}, + "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}}} From 4edfe6290a76a2f07b18be7c477de511332ce925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:52:40 +0100 Subject: [PATCH 144/167] Add the function 'secret.set_value' that set the value of a secret. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index eef75a4..7fa213f 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -129,4 +129,5 @@ "resize": {"cmd": "vol-resize", "type": "none"}}, "secret": { "define": {"cmd": "secret-define", "type": "none"}, - "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}}} + "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}, + "set_value": {"cmd": "secret-set-value", "type": "none"}}} From 6e52b8524ceda0cf835d68091d855a5e6d6b9631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:52:56 +0100 Subject: [PATCH 145/167] Add the function 'secret.get_value' that get the value of a secret. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 7fa213f..a1d82b3 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -130,4 +130,5 @@ "secret": { "define": {"cmd": "secret-define", "type": "none"}, "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}, - "set_value": {"cmd": "secret-set-value", "type": "none"}}} + "set_value": {"cmd": "secret-set-value", "type": "none"}, + "get_value": {"cmd": "secret-get-value", "type": "str"}}} From dde5bf63a2fedbb75da0df6bf3623129ad9f163a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:53:32 +0100 Subject: [PATCH 146/167] Add the function 'secret.undefine' that undefine a secret. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index a1d82b3..b658c68 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -131,4 +131,5 @@ "define": {"cmd": "secret-define", "type": "none"}, "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}, "set_value": {"cmd": "secret-set-value", "type": "none"}, - "get_value": {"cmd": "secret-get-value", "type": "str"}}} + "get_value": {"cmd": "secret-get-value", "type": "str"}, + "undefine": {"cmd": "secret-undefine", "type": "none"}}} From 611442569a52db14f63ef8a6c36cfd8a44cd2682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 22:54:36 +0100 Subject: [PATCH 147/167] Add basic usage of secrets in the doc. --- doc/source/examples.rst | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 150ae28..650b48d 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -650,3 +650,63 @@ Upload/Download >>> host.listdir('/vm') ['disk', 'modele-trusty.qcow2', 'new.qcow2'] + +Secrets +======= +Define +~~~~~~ +.. code:: + + >>> secret = {'@ephemeral': 'no', + ...: '@private': 'no', + ...: 'uuid': kvm.gen + ...: 'uuid': kvm.gen_uuid(), + ...: 'usage': {'@type': 'volume', + ...: 'volume': '/vm/disk/encrypted.qcow2'}} + + >>> with host.open('/tmp/secret.xml', 'w') as fhandler: + ...: fhandler.write(kvm.to_xml('secret', secret)) + ...: + + >>> host.secret.define('/tmp/secret.xml') + (True, 'Secret 6d14f73a-1087-7180-792d-8d80fc6b55ec created', '') + +List +~~~~ +.. code:: + + >>> host.list_secrets() + {'6d14f73a-1087-7180-792d-8d80fc6b55ec': 'volume /vm/disk/encrypted.qcow2'} + +Conf +~~~~ +.. code:: + + >>> kvm.pprint(host.secret.conf('6d14f73a-1087-7180-792d-8d80fc6b55ec')) + {'@ephemeral': 'no', + '@private': 'no', + 'usage': {'@type': 'volume', 'volume': '/vm/disk/encrypted.qcow2'}, + 'uuid': '6d14f73a-1087-7180-792d-8d80fc6b55ec'} + +Set value +~~~~~~~~~ +.. code:: + + >>> import base64 + >>> passphrase = base64.b64encode(b'passphrase').decode() + >>> host.secret.set_value('6d14f73a-1087-7180-792d-8d80fc6b55ec', passphrase) + (True, 'Secret value set', '') + +Get value +~~~~~~~~~ +.. code:: + + >>> base64.b64decode(host.secret.get_value('6d14f73a-1087-7180-792d-8d80fc6b55ec')).decode() + 'passphrase' + +Undefine +~~~~~~~~~ +.. code:: + + >>> host.secret.undefine('6d14f73a-1087-7180-792d-8d80fc6b55ec') + (True, 'Secret 6d14f73a-1087-7180-792d-8d80fc6b55ec deleted', '') From 91414a30ee39883b533f3844ac38506e28e2757c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 23:50:18 +0100 Subject: [PATCH 148/167] Rename 'net' property to 'network'. --- kvm/kvm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index b658c68..7e87b90 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -71,7 +71,7 @@ "detach_disk": {"cmd": "detach-disk", "type": "none"}, "detach_interface": {"cmd": "detach-interface", "type": "none"}, "update_device": {"cmd": "update-device", "type": "none"}}, - "net": { + "network": { "autostart": {"cmd": "net-autostart", "type": "none"}, "create": {"cmd": "net-create", "type": "none"}, "define": {"cmd": "net-define", "type": "none"}, From 46d21b35b881bc350b98c2b98da6df99ba12e9c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Tue, 26 Jan 2016 23:50:34 +0100 Subject: [PATCH 149/167] Rename 'iface' property to 'interface'. --- kvm/kvm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 7e87b90..770d2f9 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -83,7 +83,7 @@ "undefine": {"cmd": "net-undefine", "type": "none"}, "uuid": {"cmd": "net-uuid", "type": "str"}, "update": {"cmd": "net-update", "type": "none"}}, - "iface": { + "interface": { "bridge": {"cmd": "iface-bridge", "type": "none"}, "define": {"cmd": "iface-define", "type": "none"}, "destroy": {"cmd": "iface-destroy", "type": "none"}, From 1575880a2148ea21edf642955f851fb4331e80b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:02:16 +0100 Subject: [PATCH 150/167] Add some domain's examples in doc. --- doc/source/examples.rst | 161 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 6 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 650b48d..b067c00 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -9,6 +9,15 @@ Examples >>> host.connect('remote_host') >>> host = kvm.Hypervisor(host) +This is for testing purpose. In general, it is probably better to use +the ``unix.connect`` context manager (which close the connection at when +quitting): + +.. code:: + + with unix.connect('remote_host') as host: + host = kvm.Hypervisor(host) + Managing the hypervisor ======================= Virsh version @@ -351,15 +360,95 @@ Freecell >>> host.hypervisor.freecell(cellno=0) {'0': '1020744 KiB'} -Managing domains -================ - - Managing interfaces =================== +List +~~~~ +.. code:: + + >>> host.list_interfaces() + {'br0': {'mac': '64:70:02:00:6a:95', 'state': 'active'}, + 'lo': {'mac': '00:00:00:00:00:00', 'state': 'active'}} + +Conf +~~~~ +.. code:: + + >>> kvm.pprint(host.iface.conf('br0')) + {'@name': 'br0', + '@type': 'bridge', + 'bridge': {'interface': {'@name': 'enp3s5', + '@type': 'ethernet', + 'link': {'@speed': '1000', '@state': 'up'}, + 'mac': {'@address': '64:70:02:00:6a:95'}}}, + 'protocol': [{'@family': 'ipv4', + 'ip': {'@address': '192.168.0.10', '@prefix': '24'}}, + {'@family': 'ipv6', + 'ip': {'@address': 'fe80::6670:2ff:fe00:6a95', '@prefix': '64'}}]} Managing networks ================= +List +~~~~ +.. code:: + + >>> host.list_networks(all=True) + {'default': {'autostart': True, 'persistent': True, 'state': 'active'}} + +Conf +~~~~ +.. code:: + + >>> kvm.pprint(host.net.conf('default')) + {'bridge': {'@delay': '0', '@name': 'virbr0', '@stp': 'on'}, + 'forward': {'@mode': 'nat', + 'nat': {'port': {'@end': '65535', '@start': '1024'}}}, + 'ip': {'@address': '192.168.122.1', + '@netmask': '255.255.255.0', + 'dhcp': {'range': {'@end': '192.168.122.254', '@start': '192.168.122.2'}}}, + 'mac': {'@address': '52:54:00:d1:7f:f7'}, + 'name': 'default', + 'uuid': '403015c8-8339-4a66-bc37-ec794bc39e9d'} + +Destroy (stop) +~~~~~~~~~~~~~~ +.. code:: + + >>> host.net.destroy('default') + (True, 'Network default destroyed', '') + + >>> host.list_networks(all=True) + {'default': {'autostart': True, 'persistent': True, 'state': 'inactive'}} + +Undefine +~~~~~~~~ +.. code:: + + >>> host.net.undefine('default') + (True, 'Network default has been undefined', '') + + >>> host.list_networks(all=True) + {} + +Create +~~~~~~ +.. code:: + + >>> net = {'name': 'br0', 'forward': {'@mode': 'bridge'}, 'bridge': {'@name': 'br0'}} + >>> with host.open('/vm/conf/networks/br0.xml', 'w') as fhandler: + ... fhandler.write(kvm.to_xml('network', net)) + + >>> host.network.define('/vm/conf/networks/br0.xml') + (True, 'Network br0 defined from /vm/conf/networks/br0.xml', '') + + >>> host.network.autostart('br0') + (True, 'Network br0 marked as autostarted', '') + + >>> host.network.start('br0') + (True, 'Network br0 started', '') + + >>> host.list_networks() + {'br0': {'autostart': True, 'persistent': True, 'state': 'active'}} Managing storage pools ====================== @@ -708,5 +797,65 @@ Undefine ~~~~~~~~~ .. code:: - >>> host.secret.undefine('6d14f73a-1087-7180-792d-8d80fc6b55ec') - (True, 'Secret 6d14f73a-1087-7180-792d-8d80fc6b55ec deleted', '') + >>> host.secret.undefine('6d14f73a-1087-7180-792d-8d80fc6b55ec') + (True, 'Secret 6d14f73a-1087-7180-792d-8d80fc6b55ec deleted', '') + +Managing domains +================ +.. code:: + + >>> domain = {'@type': 'kvm', + 'name': 'trusty', + 'uuid': kvm.gen_uuid(), + 'title': 'Ubuntu 14.04', + 'memory': {'@unit': 'GiB', '#text': 2}, + 'currentMemory': {'@unit': 'GiB', '#text': 2}, + 'vcpu': {'#text': 2}, + 'os': {'type': {'@arch': 'x86_64', '@machine': 'pc', '#text': 'hvm'}, + 'boot': {'@dev': 'hd'}, + 'bootmenu': {'@enable': 'no'}}, + 'features': {'acpi': None, 'apic': None, 'pae': None}, + 'clock': {'@offset': 'utc'}, + 'on_poweroff': {'#text': 'destroy'}, + 'on_reboot': {'#text': 'restart'}, + 'on_crash': {'#text': 'restart'}, + 'devices': { + 'emulator': {'#text': '/usr/bin/kvm'}, + 'disk': [ + {'@type': 'volume', + '@device': 'disk', + 'driver': {'@name': 'qemu', '@type': 'qcow2'}, + 'source': {'@pool': 'default', '@volume': 'trusty.qcow2'}, + 'target': {'@dev': 'vda', '@bus': 'virtio'}} + ], + 'interface': [ + {'@type': 'network', + 'mac': {'@address': kvm.gen_mac()}, + 'model': {'@type': 'virtio'}, + 'source': {'@network': 'br0'}} + ], + 'serial': {'@type': 'pty', 'target': {'@port': 0}}, + 'console': {'@type': 'pty', 'target': {'@type': 'serial', '@port': 0}}, + 'input': [{'@type': 'mouse', '@bus': 'ps2'}, + {'@type': 'keyboard', '@bus': 'ps2'}], + 'graphics': {'@type': 'vnc', + '@port': -1, + '@autoport': 'yes', + '@keymap': 'fr', + 'listen': {'@type': 'address', '@address': '127.0.0.1'}}, + 'video': {'model': {'@type': 'cirrus'}}, + 'memballon': {'@type': 'virtio'} + } + } + + >>> with host.open('/vm/conf/trusty.xml', 'w') as fhandler: + ... fhandler.write(kvm.to_xml('domain', domain)) + + >>> host.domain.define('/vm/conf/trusty.xml') + (True, 'Domain trusty defined from /vm/conf/trusty.xml', '') + + >>> host.domain.start('trusty') + (True, 'Domain trusty started', '') + + >>> host.list_domains() + {'trusty': {'id': 2, 'state': 'running'}} From b51f0398f92d5c64429885f17a160d6e7ae446ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:03:27 +0100 Subject: [PATCH 151/167] Add the function 'snapshot.create' that create a snapshot from an XML file. --- kvm/kvm.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 770d2f9..243de21 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -132,4 +132,6 @@ "conf": {"cmd": "secret-dumpxml", "type": "xml", "key": "secret"}, "set_value": {"cmd": "secret-set-value", "type": "none"}, "get_value": {"cmd": "secret-get-value", "type": "str"}, - "undefine": {"cmd": "secret-undefine", "type": "none"}}} + "undefine": {"cmd": "secret-undefine", "type": "none"}}, + "snapshot": { + "create": {"cmd": "snaphost-create", "type": "none"}}} From 10c3daac77aa5be07680a19ff64993de2fad2368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:03:55 +0100 Subject: [PATCH 152/167] Add the function 'snapshot.create_as' that create a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 243de21..711d526 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -134,4 +134,5 @@ "get_value": {"cmd": "secret-get-value", "type": "str"}, "undefine": {"cmd": "secret-undefine", "type": "none"}}, "snapshot": { - "create": {"cmd": "snaphost-create", "type": "none"}}} + "create": {"cmd": "snaphost-create", "type": "none"}, + "create_as": {"cmd": "snapshot-create-as", "type": "none"}}} From 81fd1c67f63b019fc7566a80304d471b6924088d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:06:13 +0100 Subject: [PATCH 153/167] Add the function 'list_snaphots' that list snapshots. --- kvm/__init__.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index ad83001..881431e 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -9,6 +9,7 @@ import unix import lxml.etree as etree from collections import OrderedDict +from datetime import datetime import sys _SELF = sys.modules[__name__] @@ -385,6 +386,28 @@ def list_secrets(self, **kwargs): secrets.setdefault(uuid, ' '.join(usage)) return secrets + def list_snapshots(self, domain, **kwargs): + kwargs.pop('tree', None) + kwargs.pop('name', None) + with self.set_controls(parse=True): + stdout = self.virsh('snapshot-list', domain, **kwargs) + snapshots = {} + for line in stdout[2:]: + line = line.split() + creation_date = datetime.strptime(' '.join(line[1:4]), + '%Y-%m-%d %H:%M:%S %z') + state = line[4] + if state == 'shut': + state += line[5] + parent = line[6] if 'parent' in kwargs else None + else: + parent = line[5] if 'parent' in kwargs else None + snapshot = {'creation_date': creation_date, 'state': state} + if parent and parent != 'null': + snapshot.update(parent=parent) + snapshots.setdefault(line[0], snapshot) + return snapshots + @property def image(self): return _Image(weakref.ref(self)()) @@ -436,7 +459,6 @@ def __hypervisor_node_memory_tune(self, **kwargs): def __domain_time(self, domain, **kwargs): kwargs.pop('pretty', None) if not kwargs: - from datetime import datetime with self._host.set_controls(parse=True): time = self._host.virsh('domtime', domain, **kwargs)[0] return datetime.fromtimestamp(int(time.split(':')[1])) From 4bc36cddc194968e840a8151fe7588c26efebc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:10:32 +0100 Subject: [PATCH 154/167] Add the function 'snapshot.info' that returns informations of a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 711d526..8ca2e73 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -135,4 +135,5 @@ "undefine": {"cmd": "secret-undefine", "type": "none"}}, "snapshot": { "create": {"cmd": "snaphost-create", "type": "none"}, - "create_as": {"cmd": "snapshot-create-as", "type": "none"}}} + "create_as": {"cmd": "snapshot-create-as", "type": "none"}, + "info": {"cmd": "snapshot-info", "type": "dict"}}} From 9c138e4eb8d763b49b0bfc9a229492df900293f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:12:24 +0100 Subject: [PATCH 155/167] Add the function 'snapshot.conf' that returns the configuration of a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 8ca2e73..0524b96 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -136,4 +136,5 @@ "snapshot": { "create": {"cmd": "snaphost-create", "type": "none"}, "create_as": {"cmd": "snapshot-create-as", "type": "none"}, - "info": {"cmd": "snapshot-info", "type": "dict"}}} + "info": {"cmd": "snapshot-info", "type": "dict"}, + "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"}}} From 3618c7c9386a4cab42ee3c087ed15f2fa0bbcbb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:12:51 +0100 Subject: [PATCH 156/167] Add the function 'snapshot.current' that returns the configuration (or only the name) of the current snapshot. --- kvm/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index 881431e..a1df7c5 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -509,6 +509,13 @@ def timeout_handler(signum, frame): signal.alarm(0) return [True, '', ''] +def __snapshot_current(self, domain, **kwargs): + with self._host.set_controls(parse=True): + result = self._host.virsh('snapshot-current', domain, **kwargs) + return (result[0] + if 'name' in kwargs + else from_xml(etree.fromstring('\n'.join(result)), [])['domainsnapshot']) + class _Image(object): def __init__(self, host): From 0e56855e77572a3ce3e6fd2023bd9b27e9917fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:13:27 +0100 Subject: [PATCH 157/167] Add the function 'snapshot.parent' that returns the parent of a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 0524b96..5f1f000 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -137,4 +137,5 @@ "create": {"cmd": "snaphost-create", "type": "none"}, "create_as": {"cmd": "snapshot-create-as", "type": "none"}, "info": {"cmd": "snapshot-info", "type": "dict"}, - "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"}}} + "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"}, + "parent": {"cmd": "snapshot-parent", "type": "str"}}} From ed9f181e43240cb39e8d7de7eb8596ca9abfde47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:14:05 +0100 Subject: [PATCH 158/167] Add the function 'snapshot.revert' that reverts a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 5f1f000..4d8334b 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -138,4 +138,5 @@ "create_as": {"cmd": "snapshot-create-as", "type": "none"}, "info": {"cmd": "snapshot-info", "type": "dict"}, "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"}, - "parent": {"cmd": "snapshot-parent", "type": "str"}}} + "parent": {"cmd": "snapshot-parent", "type": "str"}, + "revert": {"cmd": "snapshot-revert", "type": "none"}}} From db22b44b5fac669829eda61a0747520925dcb2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:14:23 +0100 Subject: [PATCH 159/167] Add the function 'snapshot.delete' that delete a snapshot. --- kvm/kvm.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 4d8334b..3ff0eb6 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -139,4 +139,5 @@ "info": {"cmd": "snapshot-info", "type": "dict"}, "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"}, "parent": {"cmd": "snapshot-parent", "type": "str"}, - "revert": {"cmd": "snapshot-revert", "type": "none"}}} + "revert": {"cmd": "snapshot-revert", "type": "none"}, + "delete": {"cmd": "snapshot-delete", "type": "none"}}} From 5f21f7931bafdaa0e47fd100435d4643143568cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:15:00 +0100 Subject: [PATCH 160/167] Add some uncomplete snapshots examples in the doc. --- doc/source/examples.rst | 113 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index b067c00..6003a4f 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -859,3 +859,116 @@ Managing domains >>> host.list_domains() {'trusty': {'id': 2, 'state': 'running'}} + + +Snapshots +========= +.. code:: + + >>> host.snapshot.create_as('trusty') + (True, 'Domain snapshot 1453929671 created', '') + + >>> host.snapshot.info('trusty', '1453929671') + {'children': 0, + 'current': True, + 'descendants': 0, + 'domain': 'trusty', + 'location': 'internal', + 'metadata': True, + 'name': 1453929671, + 'parent': '-', + 'state': 'running'} + + >>> host.snapshot.create_as('trusty') + (True, 'Domain snapshot 1453929756 created', '') + + >>> host.snapshot.info('trusty', '1453929671') + {'children': 1, + 'current': False, + 'descendants': 1, + 'domain': 'trusty', + 'location': 'internal', + 'metadata': True, + 'name': 1453929671, + 'parent': '-', + 'state': 'running'} + + >>> host.snapshot.info('trusty', '1453929756') + {'children': 0, + 'current': True, + 'descendants': 0, + 'domain': 'trusty', + 'location': 'internal', + 'metadata': True, + 'name': 1453929756, + 'parent': 1453929671, + 'state': 'running'} + + >>> kvm.pprint(host.snapshot.conf('trusty', '1453929756')) + {'creationTime': '1453929756', + 'disks': {'disk': {'@name': 'vda', '@snapshot': 'internal'}}, + 'domain': {'@type': 'kvm', + 'clock': {'@offset': 'utc'}, + 'currentMemory': {'#text': '2097152', '@unit': 'KiB'}, + 'devices': {'console': {'@type': 'pty', + 'target': {'@port': '0', '@type': 'serial'}}, + 'controller': [{'@index': '0', '@type': 'usb'}, + {'@index': '0', '@model': 'pci-root', '@type': 'pci'}], + 'disk': {'@device': 'disk', + '@type': 'volume', + 'address': {'@bus': '0x00', + '@domain': '0x0000', + '@function': '0x0', + '@slot': '0x04', + '@type': 'pci'}, + 'driver': {'@name': 'qemu', '@type': 'qcow2'}, + 'source': {'@pool': 'default', '@volume': 'trusty.qcow2'}, + 'target': {'@bus': 'virtio', '@dev': 'vda'}}, + 'emulator': '/usr/bin/kvm', + 'graphics': {'@autoport': 'yes', + '@keymap': 'fr', + '@listen': '127.0.0.1', + '@port': '-1', + '@type': 'vnc', + 'listen': {'@address': '127.0.0.1', '@type': 'address'}}, + 'input': [{'@bus': 'ps2', '@type': 'mouse'}, + {'@bus': 'ps2', '@type': 'keyboard'}], + 'interface': {'@type': 'network', + 'address': {'@bus': '0x00', + '@domain': '0x0000', + '@function': '0x0', + '@slot': '0x03', + '@type': 'pci'}, + 'mac': {'@address': '54:52:00:cc:ba:4a'}, + 'model': {'@type': 'virtio'}, + 'source': {'@network': 'br0'}}, + 'memballoon': {'@model': 'virtio', + 'address': {'@bus': '0x00', + '@domain': '0x0000', + '@function': '0x0', + '@slot': '0x05', + '@type': 'pci'}}, + 'serial': {'@type': 'pty', 'target': {'@port': '0'}}, + 'video': {'address': {'@bus': '0x00', + '@domain': '0x0000', + '@function': '0x0', + '@slot': '0x02', + '@type': 'pci'}, + 'model': {'@heads': '1', '@type': 'cirrus', '@vram': '16384'}}}, + 'features': {'acpi': True, 'apic': True, 'pae': True}, + 'memory': {'#text': '2097152', '@unit': 'KiB'}, + 'name': 'trusty', + 'on_crash': 'restart', + 'on_poweroff': 'destroy', + 'on_reboot': 'restart', + 'os': {'boot': {'@dev': 'hd'}, + 'bootmenu': {'@enable': 'no'}, + 'type': {'#text': 'hvm', '@arch': 'x86_64', '@machine': 'pc-i440fx-2.3'}}, + 'resource': {'partition': '/machine'}, + 'title': 'Ubuntu 14.04', + 'uuid': 'e486003f-9f11-f7df-5ed4-cceddbf087ab', + 'vcpu': {'#text': '2', '@placement': 'static'}}, + 'memory': {'@snapshot': 'internal'}, + 'name': '1453929756', + 'parent': {'name': '1453929671'}, + 'state': 'running'} From c80602020f68bef691bd67c874098cbe576ce867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Wed, 27 Jan 2016 23:18:53 +0100 Subject: [PATCH 161/167] Correct a bug that raised an error with python 2. --- kvm/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index a1df7c5..77f9728 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -382,7 +382,8 @@ def list_secrets(self, **kwargs): stdout = self.virsh('secret-list', **kwargs) secrets = {} for line in stdout[2:]: - uuid, *usage = line.split() + line = line.split() + uuid, usage = line[0], line[1:] secrets.setdefault(uuid, ' '.join(usage)) return secrets From 84ee07a197e95e67c93c0422ffa2f6e49b7e3314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 28 Jan 2016 22:29:18 +0100 Subject: [PATCH 162/167] Add an 'uri' parameter to the Hypervisor object allowing to specify the connection uri. --- kvm/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kvm/__init__.py b/kvm/__init__.py index 77f9728..eff7ce4 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -225,7 +225,7 @@ class TimeoutException(Exception): # ## Classes. # -def Hypervisor(host): +def Hypervisor(host, uri=None): unix.isvalid(host) try: @@ -254,8 +254,9 @@ def virsh(self, command, *args, **kwargs): for opt in self._ignore_opts: kwargs.update({opt: False}) + virsh_cmd = 'virsh --connect %s' % (uri or 'qemu:///session') with self.set_controls(options_place='after', decode='utf-8'): - status, stdout, stderr = self.execute('virsh', command, *args, **kwargs) + status, stdout, stderr = self.execute(virsh_cmd, command, *args, **kwargs) # Clean stdout and stderr. if stdout: stdout = stdout.rstrip('\n') From 05788925eb9f9c3d96aeb15453d54c49fc43affe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 28 Jan 2016 22:41:12 +0100 Subject: [PATCH 163/167] Add the functions 'hypervisor.cpu_baseline' and 'hypervisor.cpu_compare' (https://docs.fedoraproject.org/en-US/Fedora/18/html/Virtualization_Administration_Guide/ch15s13s03.html). --- kvm/kvm.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index 3ff0eb6..bf97f18 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -11,7 +11,9 @@ "domcapabilities": {"cmd": "domcapabilities", "type": "xml", "key": "domainCapabilities"}, "freecell": {"cmd": "freecell", "type": "dict"}, "freepages": {"cmd": "freepages", "type": "dict"}, - "allocpages": {"cmd": "allocpages", "type": "none"}}, + "allocpages": {"cmd": "allocpages", "type": "none"}, + "cpu_baseline": {"cmd": "cpu-baseline", "type": "none"}, + "cpu_compare": {"cmd": "cpu-compare", "type": "none"}}, "domain": { "autostart": {"cmd": "autostart", "type": "none"}, "inject_nmi": {"cmd": "inject_nmi", "type": "none"}, From 4ba42dfaf7985f368ad741348f7c8cbd41e0bd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Thu, 28 Jan 2016 22:42:21 +0100 Subject: [PATCH 164/167] Add the function 'hypervisor.cpu_modles' that print the list of CPU models known for the specified architecture. --- kvm/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kvm/__init__.py b/kvm/__init__.py index eff7ce4..453e6da 100644 --- a/kvm/__init__.py +++ b/kvm/__init__.py @@ -435,6 +435,10 @@ def image(self): def __init(self, host): self._host = host +def __hypervisor_cpu_models(self, arch): + with self._host.set_controls(parse=True): + return self._host.virsh('cpu-models', arch) + def __hypervisor_sysinfo(self): entry = lambda value: {elt['@name']: elt['#text'] for elt in value} From 01cb024a89a5f7677670557f172aeafa95ef4532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Sun, 7 Jan 2018 18:42:20 +0100 Subject: [PATCH 165/167] Change version. --- .gitignore | 3 +-- README.rst | 19 ------------------- setup.py | 13 +++++-------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 02d5bc4..18d65d2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,4 @@ MANIFEST doc/build/* # virtualenv -env/ -env3/ +env*/ diff --git a/README.rst b/README.rst index f1a5cfe..9542ce9 100644 --- a/README.rst +++ b/README.rst @@ -91,22 +91,3 @@ disks manipulations need *nbd* module to be loaded so it is better to use an # Wait for the domain to stop. >>> host.domain.state('guest1') 'shut off' - - -Releases notes --------------- -1.0.5 (2015-12-03) -~~~~~~~~~~~~~~~~~~ - * Correct a bug when parsing XML (https://github.com/fmenabe/python-kvm/commit/8fad97e4528ca47af198cc107b9c59d8735c712d) - -1.0.4 (2015-07-02) -~~~~~~~~~~~~~~~~~~ - * Wrapper to ''virsh'' command. - * Each type (domain, nodedev, net, ...) has one command for listing and a property regrouping commands to apply to one element. - * Properties: - * ``hypervisor``: generic commands (``nodeinfo``, ``capabilities``, ...) - * ``domains``: commands for managing a domain - * ``nodedev``: commands for managing a node device - * ``net``: commands for managing a vritual network - * ``iface``: commands for manage an interface - * Transfrom XML outputs to dictionnaries. diff --git a/setup.py b/setup.py index 7035c77..541d1f8 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- -from distutils.core import setup -from distutils.command.install import INSTALL_SCHEMES - +from setuptools import setup setup ( name='kvm', - version='1.0.5', + version='1.1.0', author='François Ménabé', author_email='francois.menabe@gmail.com', packages=['kvm'], @@ -14,10 +12,9 @@ license='MIT License', description='An API for managing KVM host.', long_description=open('README.rst').read(), - install_requires=[ - 'unix', - 'lxml' - ], + keywords = ['python', 'kvm', 'unix', 'virsh'], + install_requires=['unix', 'lxml'], +# entry_points={'unix': ['Hypervisor = kvm.__init__:Hypervisor']}, classifiers=[ 'License :: OSI Approved :: MIT License', 'Development Status :: 3 - Alpha', From d4f36db4b8b960239060d6ec4b3066a837e65c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M=C3=A9nab=C3=A9?= Date: Fri, 10 Aug 2018 14:49:18 +0200 Subject: [PATCH 166/167] Set version to 1.1.1 A function that was not even commited had been pushed on the PyPi package ... So change version for pushing new version with the correct code base. Also correct some typos in README. --- README.rst | 10 ++++++++-- setup.py | 5 ++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9542ce9..df30475 100644 --- a/README.rst +++ b/README.rst @@ -72,9 +72,9 @@ disks manipulations need *nbd* module to be loaded so it is better to use an >>> localhost.list_networks() {'default': {'autostart': True, 'persistent': True, 'state': 'active'}} - >>> host = unix.Remote() + >>> host = Remote() >>> host.connect('hypervisor1') - >>> host = kvm.Hypervisor(Linux(host) + >>> host = kvm.Hypervisor(Linux(host)) >>> host.hypervisor.nodeinfo() {'cores_per_socket': 12, 'cpu_frequency': '2200 MHz', @@ -91,3 +91,9 @@ disks manipulations need *nbd* module to be loaded so it is better to use an # Wait for the domain to stop. >>> host.domain.state('guest1') 'shut off' + + # Using the context manager for the connecion. + >>> from unix.linux as linux, kvm + >>> with linux.connect('hypervisor1') as host: + ... host = kvm.Hypervisor(host) + ... host.hypervisor.node_info() diff --git a/setup.py b/setup.py index 541d1f8..7755277 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup ( name='kvm', - version='1.1.0', + version='1.1.1', author='François Ménabé', author_email='francois.menabe@gmail.com', packages=['kvm'], @@ -21,8 +21,7 @@ 'Intended Audience :: System Administrators', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3', 'Operating System :: Unix', 'Topic :: System :: Systems Administration'] ) From a034e7a1402a4249d218b3d080678ddf82557ed8 Mon Sep 17 00:00:00 2001 From: ilanisme Date: Mon, 3 May 2021 21:07:18 +0300 Subject: [PATCH 167/167] Update kvm.json Typo fix on snapshot command --- kvm/kvm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm/kvm.json b/kvm/kvm.json index bf97f18..e5fb623 100644 --- a/kvm/kvm.json +++ b/kvm/kvm.json @@ -136,7 +136,7 @@ "get_value": {"cmd": "secret-get-value", "type": "str"}, "undefine": {"cmd": "secret-undefine", "type": "none"}}, "snapshot": { - "create": {"cmd": "snaphost-create", "type": "none"}, + "create": {"cmd": "snapshot-create", "type": "none"}, "create_as": {"cmd": "snapshot-create-as", "type": "none"}, "info": {"cmd": "snapshot-info", "type": "dict"}, "conf": {"cmd": "snapshot-dumpxml", "type": "xml", "key": "domainsnapshot"},