mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge pull request #47892 from cbosdo/virt-capabilities
Virt capabilities
This commit is contained in:
commit
b7ba930f45
@ -87,6 +87,7 @@ import datetime
|
||||
from xml.etree import ElementTree
|
||||
|
||||
# Import third party libs
|
||||
import xml.dom
|
||||
from xml.dom import minidom
|
||||
import jinja2
|
||||
import jinja2.exceptions
|
||||
@ -2500,9 +2501,7 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs):
|
||||
|
||||
def _capabilities(conn):
|
||||
'''
|
||||
Return connection capabilities
|
||||
It's a huge klutz to parse right,
|
||||
so hide func for now and pass on the XML instead
|
||||
Return connection capabilities as XML.
|
||||
'''
|
||||
caps = conn.getCapabilities()
|
||||
caps = minidom.parseString(caps)
|
||||
@ -2510,6 +2509,438 @@ def _capabilities(conn):
|
||||
return caps
|
||||
|
||||
|
||||
def _get_xml_first_element_by_tag_name(node, name):
|
||||
'''
|
||||
Convenience function getting the first result of getElementsByTagName() or None.
|
||||
'''
|
||||
nodes = node.getElementsByTagName(name)
|
||||
return nodes[0] if nodes else None
|
||||
|
||||
|
||||
def _get_xml_element_text(node):
|
||||
'''
|
||||
Get the text value of an XML element.
|
||||
'''
|
||||
return "".join([child.data for child in node.childNodes if child.nodeType == xml.dom.Node.TEXT_NODE])
|
||||
|
||||
|
||||
def _get_xml_child_text(node, name, default):
|
||||
'''
|
||||
Get the text value of the child named name of XML element node
|
||||
'''
|
||||
result = "".join([_get_xml_element_text(child) for child in node.childNodes
|
||||
if child.nodeType == xml.dom.Node.ELEMENT_NODE and child.tagName == name])
|
||||
return result if result else default
|
||||
|
||||
|
||||
def _caps_add_machine(machines, node):
|
||||
'''
|
||||
Parse the <machine> element of the host capabilities and add it
|
||||
to the machines list.
|
||||
'''
|
||||
maxcpus = node.getAttribute('maxCpus')
|
||||
canonical = node.getAttribute('canonical')
|
||||
name = _get_xml_element_text(node)
|
||||
|
||||
alternate_name = ""
|
||||
if canonical:
|
||||
alternate_name = name
|
||||
name = canonical
|
||||
|
||||
machine = machines.get(name)
|
||||
if not machine:
|
||||
machine = {'alternate_names': []}
|
||||
if maxcpus:
|
||||
machine['maxcpus'] = int(maxcpus)
|
||||
machines[name] = machine
|
||||
if alternate_name:
|
||||
machine['alternate_names'].append(alternate_name)
|
||||
|
||||
|
||||
def _parse_caps_guest(guest):
|
||||
'''
|
||||
Parse the <guest> element of the connection capabilities XML
|
||||
'''
|
||||
arch_node = _get_xml_first_element_by_tag_name(guest, 'arch')
|
||||
result = {
|
||||
'os_type': _get_xml_element_text(guest.getElementsByTagName('os_type')[0]),
|
||||
'arch': {
|
||||
'name': arch_node.getAttribute('name'),
|
||||
'machines': {},
|
||||
'domains': {}
|
||||
},
|
||||
}
|
||||
|
||||
for child in arch_node.childNodes:
|
||||
if child.nodeType != xml.dom.Node.ELEMENT_NODE:
|
||||
continue
|
||||
if child.tagName == 'wordsize':
|
||||
result['arch']['wordsize'] = int(_get_xml_element_text(child))
|
||||
elif child.tagName == 'emulator':
|
||||
result['arch']['emulator'] = _get_xml_element_text(child)
|
||||
elif child.tagName == 'machine':
|
||||
_caps_add_machine(result['arch']['machines'], child)
|
||||
elif child.tagName == 'domain':
|
||||
domain_type = child.getAttribute('type')
|
||||
domain = {
|
||||
'emulator': None,
|
||||
'machines': {}
|
||||
}
|
||||
emulator_node = _get_xml_first_element_by_tag_name(child, 'emulator')
|
||||
if emulator_node:
|
||||
domain['emulator'] = _get_xml_element_text(emulator_node)
|
||||
for machine in child.getElementsByTagName('machine'):
|
||||
_caps_add_machine(domain['machines'], machine)
|
||||
result['arch']['domains'][domain_type] = domain
|
||||
|
||||
# Note that some features have no default and toggle attributes.
|
||||
# This may not be a perfect match, but represent them as enabled by default
|
||||
# without possibility to toggle them.
|
||||
features_node = _get_xml_first_element_by_tag_name(guest, 'features')
|
||||
result['features'] = {child.tagName: {'toggle': True if child.getAttribute('toggle') == 'yes' else False,
|
||||
'default': True if child.getAttribute('default') == 'no' else True}
|
||||
for child in features_node.childNodes if child.nodeType == xml.dom.Node.ELEMENT_NODE}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _parse_caps_cell(cell):
|
||||
'''
|
||||
Parse the <cell> nodes of the connection capabilities XML output.
|
||||
'''
|
||||
result = {
|
||||
'id': int(cell.getAttribute('id'))
|
||||
}
|
||||
|
||||
mem_node = _get_xml_first_element_by_tag_name(cell, 'memory')
|
||||
if mem_node:
|
||||
unit = mem_node.getAttribute('unit') if mem_node.hasAttribute('unit') else 'KiB'
|
||||
memory = _get_xml_element_text(mem_node)
|
||||
result['memory'] = "{} {}".format(memory, unit)
|
||||
|
||||
pages = [{'size': "{} {}".format(page.getAttribute('size'),
|
||||
page.getAttribute('unit') if page.getAttribute('unit') else 'KiB'),
|
||||
'available': int(_get_xml_element_text(page))}
|
||||
for page in cell.getElementsByTagName('pages')]
|
||||
if pages:
|
||||
result['pages'] = pages
|
||||
|
||||
distances = {int(distance.getAttribute('id')): int(distance.getAttribute('value'))
|
||||
for distance in cell.getElementsByTagName('sibling')}
|
||||
if distances:
|
||||
result['distances'] = distances
|
||||
|
||||
cpus = []
|
||||
for cpu_node in cell.getElementsByTagName('cpu'):
|
||||
cpu = {
|
||||
'id': int(cpu_node.getAttribute('id'))
|
||||
}
|
||||
socket_id = cpu_node.getAttribute('socket_id')
|
||||
if socket_id:
|
||||
cpu['socket_id'] = int(socket_id)
|
||||
|
||||
core_id = cpu_node.getAttribute('core_id')
|
||||
if core_id:
|
||||
cpu['core_id'] = int(core_id)
|
||||
siblings = cpu_node.getAttribute('siblings')
|
||||
if siblings:
|
||||
cpu['siblings'] = siblings
|
||||
cpus.append(cpu)
|
||||
if cpus:
|
||||
result['cpus'] = cpus
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _parse_caps_bank(bank):
|
||||
'''
|
||||
Parse the <bank> element of the connection capabilities XML.
|
||||
'''
|
||||
result = {
|
||||
'id': int(bank.getAttribute('id')),
|
||||
'level': int(bank.getAttribute('level')),
|
||||
'type': bank.getAttribute('type'),
|
||||
'size': "{} {}".format(bank.getAttribute('size'), bank.getAttribute('unit')),
|
||||
'cpus': bank.getAttribute('cpus')
|
||||
}
|
||||
|
||||
controls = []
|
||||
for control in bank.getElementsByTagName('control'):
|
||||
unit = control.getAttribute('unit')
|
||||
result_control = {
|
||||
'granularity': "{} {}".format(control.getAttribute('granularity'), unit),
|
||||
'type': control.getAttribute('type'),
|
||||
'maxAllocs': int(control.getAttribute('maxAllocs'))
|
||||
}
|
||||
|
||||
minimum = control.getAttribute('min')
|
||||
if minimum:
|
||||
result_control['min'] = "{} {}".format(minimum, unit)
|
||||
controls.append(result_control)
|
||||
if controls:
|
||||
result['controls'] = controls
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _parse_caps_host(host):
|
||||
'''
|
||||
Parse the <host> element of the connection capabilities XML.
|
||||
'''
|
||||
result = {}
|
||||
for child in host.childNodes:
|
||||
if child.nodeType != xml.dom.Node.ELEMENT_NODE:
|
||||
continue
|
||||
|
||||
if child.tagName == 'uuid':
|
||||
result['uuid'] = _get_xml_element_text(child)
|
||||
|
||||
elif child.tagName == 'cpu':
|
||||
cpu = {
|
||||
'arch': _get_xml_child_text(child, 'arch', None),
|
||||
'model': _get_xml_child_text(child, 'model', None),
|
||||
'vendor': _get_xml_child_text(child, 'vendor', None),
|
||||
'features': [feature.getAttribute('name') for feature in child.getElementsByTagName('feature')],
|
||||
'pages': [{'size': '{} {}'.format(page.getAttribute('size'),
|
||||
page.getAttribute('unit') if page.hasAttribute('unit') else 'KiB')}
|
||||
for page in child.getElementsByTagName('pages')]
|
||||
}
|
||||
# Parse the cpu tag
|
||||
microcode = _get_xml_first_element_by_tag_name(child, 'microcode')
|
||||
if microcode:
|
||||
cpu['microcode'] = microcode.getAttribute('version')
|
||||
|
||||
topology = _get_xml_first_element_by_tag_name(child, 'topology')
|
||||
if topology:
|
||||
cpu['sockets'] = int(topology.getAttribute('sockets'))
|
||||
cpu['cores'] = int(topology.getAttribute('cores'))
|
||||
cpu['threads'] = int(topology.getAttribute('threads'))
|
||||
result['cpu'] = cpu
|
||||
|
||||
elif child.tagName == "power_management":
|
||||
result['power_management'] = [node.tagName for node in child.childNodes
|
||||
if node.nodeType == xml.dom.Node.ELEMENT_NODE]
|
||||
|
||||
elif child.tagName == "migration_features":
|
||||
result['migration'] = {
|
||||
'live': bool(child.getElementsByTagName('live')),
|
||||
'transports': [_get_xml_element_text(node) for node in child.getElementsByTagName('uri_transport')]
|
||||
}
|
||||
|
||||
elif child.tagName == "topology":
|
||||
result['topology'] = {
|
||||
'cells': [_parse_caps_cell(cell) for cell in child.getElementsByTagName('cell')]
|
||||
}
|
||||
|
||||
elif child.tagName == 'cache':
|
||||
result['cache'] = {
|
||||
'banks': [_parse_caps_bank(bank) for bank in child.getElementsByTagName('bank')]
|
||||
}
|
||||
|
||||
result['security'] = [{
|
||||
'model': _get_xml_child_text(secmodel, 'model', None),
|
||||
'doi': _get_xml_child_text(secmodel, 'doi', None),
|
||||
'baselabels': [{'type': label.getAttribute('type'), 'label': _get_xml_element_text(label)}
|
||||
for label in secmodel.getElementsByTagName('baselabel')]
|
||||
}
|
||||
for secmodel in host.getElementsByTagName('secmodel')]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def capabilities(**kwargs):
|
||||
'''
|
||||
Return the hypervisor connection capabilities.
|
||||
|
||||
:param connection: libvirt connection URI, overriding defaults
|
||||
:param username: username to connect with, overriding defaults
|
||||
:param password: password to connect with, overriding defaults
|
||||
|
||||
..versionadded:: Fluorine
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' virt.capabilities
|
||||
'''
|
||||
conn = __get_conn(**kwargs)
|
||||
caps = _capabilities(conn)
|
||||
conn.close()
|
||||
|
||||
return {
|
||||
'host': _parse_caps_host(caps.getElementsByTagName('host')[0]),
|
||||
'guests': [_parse_caps_guest(guest) for guest in caps.getElementsByTagName('guest')]
|
||||
}
|
||||
|
||||
|
||||
def _parse_caps_enum(node):
|
||||
'''
|
||||
Return a tuple containing the name of the enum and the possible values
|
||||
'''
|
||||
return (node.getAttribute('name') or None,
|
||||
[_get_xml_element_text(value) for value in node.getElementsByTagName('value')])
|
||||
|
||||
|
||||
def _parse_caps_cpu(node):
|
||||
'''
|
||||
Parse the <cpu> element of the domain capabilities
|
||||
'''
|
||||
result = {}
|
||||
for mode in node.getElementsByTagName('mode'):
|
||||
if not mode.getAttribute('supported') == 'yes':
|
||||
continue
|
||||
|
||||
name = mode.getAttribute('name')
|
||||
if name == 'host-passthrough':
|
||||
result[name] = True
|
||||
|
||||
elif name == 'host-model':
|
||||
host_model = {}
|
||||
model_node = _get_xml_first_element_by_tag_name(mode, 'model')
|
||||
if model_node:
|
||||
model = {
|
||||
'name': _get_xml_element_text(model_node)
|
||||
}
|
||||
|
||||
vendor_id = model_node.getAttribute('vendor_id')
|
||||
if vendor_id:
|
||||
model['vendor_id'] = vendor_id
|
||||
|
||||
fallback = model_node.getAttribute('fallback')
|
||||
if fallback:
|
||||
model['fallback'] = fallback
|
||||
host_model['model'] = model
|
||||
|
||||
vendor = _get_xml_child_text(mode, 'vendor', None)
|
||||
if vendor:
|
||||
host_model['vendor'] = vendor
|
||||
|
||||
features = {feature.getAttribute('name'): feature.getAttribute('policy')
|
||||
for feature in mode.getElementsByTagName('feature')}
|
||||
if features:
|
||||
host_model['features'] = features
|
||||
|
||||
result[name] = host_model
|
||||
|
||||
elif name == 'custom':
|
||||
custom_model = {}
|
||||
models = {_get_xml_element_text(model): model.getAttribute('usable')
|
||||
for model in mode.getElementsByTagName('model')}
|
||||
if models:
|
||||
custom_model['models'] = models
|
||||
result[name] = custom_model
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _parse_caps_devices_features(node):
|
||||
'''
|
||||
Parse the devices or features list of the domain capatilities
|
||||
'''
|
||||
result = {}
|
||||
for child in node.childNodes:
|
||||
if child.nodeType != xml.dom.Node.ELEMENT_NODE or not child.getAttribute('supported') == 'yes':
|
||||
continue
|
||||
enums = [_parse_caps_enum(node) for node in child.getElementsByTagName('enum')]
|
||||
result[child.tagName] = {item[0]: item[1] for item in enums if item[0]}
|
||||
return result
|
||||
|
||||
|
||||
def _parse_caps_loader(node):
|
||||
'''
|
||||
Parse the <loader> element of the domain capabilities.
|
||||
'''
|
||||
enums = [_parse_caps_enum(enum) for enum in node.getElementsByTagName('enum')]
|
||||
result = {item[0]: item[1] for item in enums if item[0]}
|
||||
|
||||
values = [_get_xml_element_text(child) for child in node.childNodes
|
||||
if child.nodeType == xml.dom.Node.ELEMENT_NODE and child.tagName == 'value']
|
||||
|
||||
if values:
|
||||
result['values'] = values
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def domain_capabilities(emulator=None, arch=None, machine=None, domain=None, **kwargs):
|
||||
'''
|
||||
Return the domain capabilities given an emulator, architecture, machine or virtualization type.
|
||||
|
||||
..versionadded:: Fluorine
|
||||
|
||||
:param emulator: return the capabilities for the given emulator binary
|
||||
:param arch: return the capabilities for the given CPU architecture
|
||||
:param machine: return the capabilities for the given emulated machine type
|
||||
:param domain: return the capabilities for the given virtualization type.
|
||||
:param connection: libvirt connection URI, overriding defaults
|
||||
:param username: username to connect with, overriding defaults
|
||||
:param password: password to connect with, overriding defaults
|
||||
|
||||
The list of the possible emulator, arch, machine and domain can be found in
|
||||
the host capabilities output.
|
||||
|
||||
If none of the parameters is provided the libvirt default domain capabilities
|
||||
will be returned.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' virt.domain_capabilities arch='x86_64' domain='kvm'
|
||||
|
||||
'''
|
||||
conn = __get_conn(**kwargs)
|
||||
caps = conn.getDomainCapabilities(emulator, arch, machine, domain, 0)
|
||||
caps = minidom.parseString(caps)
|
||||
conn.close()
|
||||
|
||||
root_node = caps.getElementsByTagName('domainCapabilities')[0]
|
||||
|
||||
result = {
|
||||
'emulator': _get_xml_child_text(root_node, 'path', None),
|
||||
'domain': _get_xml_child_text(root_node, 'domain', None),
|
||||
'machine': _get_xml_child_text(root_node, 'machine', None),
|
||||
'arch': _get_xml_child_text(root_node, 'arch', None)
|
||||
}
|
||||
|
||||
for child in root_node.childNodes:
|
||||
if child.nodeType != xml.dom.Node.ELEMENT_NODE:
|
||||
continue
|
||||
|
||||
if child.tagName == 'vcpu' and child.getAttribute('max'):
|
||||
result['max_vcpus'] = int(child.getAttribute('max'))
|
||||
|
||||
elif child.tagName == 'iothreads':
|
||||
iothreads = child.getAttribute('supported') == 'yes'
|
||||
if iothreads is not None:
|
||||
result['iothreads'] = iothreads
|
||||
|
||||
elif child.tagName == 'os':
|
||||
result['os'] = {}
|
||||
loader_node = _get_xml_first_element_by_tag_name(child, 'loader')
|
||||
if loader_node and loader_node.getAttribute('supported') == 'yes':
|
||||
loader = _parse_caps_loader(loader_node)
|
||||
result['os']['loader'] = loader
|
||||
|
||||
elif child.tagName == 'cpu':
|
||||
cpu = _parse_caps_cpu(child)
|
||||
if cpu:
|
||||
result['cpu'] = cpu
|
||||
|
||||
elif child.tagName == 'devices':
|
||||
devices = _parse_caps_devices_features(child)
|
||||
if devices:
|
||||
result['devices'] = devices
|
||||
|
||||
elif child.tagName == 'features':
|
||||
features = _parse_caps_devices_features(child)
|
||||
if features:
|
||||
result['features'] = features
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def cpu_baseline(full=False, migratable=False, out='libvirt', **kwargs):
|
||||
'''
|
||||
Return the optimal 'custom' CPU baseline config for VM's on this minion
|
||||
|
@ -629,6 +629,258 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
||||
mock_remove.assert_called_once()
|
||||
mock_remove.assert_any_call('/disks/test.qcow2')
|
||||
|
||||
def test_capabilities(self):
|
||||
xml = '''
|
||||
<capabilities>
|
||||
<host>
|
||||
<uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
|
||||
<cpu>
|
||||
<arch>x86_64</arch>
|
||||
<model>Nehalem</model>
|
||||
<vendor>Intel</vendor>
|
||||
<microcode version='25'/>
|
||||
<topology sockets='1' cores='4' threads='2'/>
|
||||
<feature name='vme'/>
|
||||
<feature name='ds'/>
|
||||
<feature name='acpi'/>
|
||||
<pages unit='KiB' size='4'/>
|
||||
<pages unit='KiB' size='2048'/>
|
||||
</cpu>
|
||||
<power_management>
|
||||
<suspend_mem/>
|
||||
<suspend_disk/>
|
||||
<suspend_hybrid/>
|
||||
</power_management>
|
||||
<migration_features>
|
||||
<live/>
|
||||
<uri_transports>
|
||||
<uri_transport>tcp</uri_transport>
|
||||
<uri_transport>rdma</uri_transport>
|
||||
</uri_transports>
|
||||
</migration_features>
|
||||
<topology>
|
||||
<cells num='1'>
|
||||
<cell id='0'>
|
||||
<memory unit='KiB'>12367120</memory>
|
||||
<pages unit='KiB' size='4'>3091780</pages>
|
||||
<pages unit='KiB' size='2048'>0</pages>
|
||||
<distances>
|
||||
<sibling id='0' value='10'/>
|
||||
</distances>
|
||||
<cpus num='8'>
|
||||
<cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
|
||||
<cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
|
||||
<cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
|
||||
<cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
|
||||
<cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
|
||||
<cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
|
||||
<cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
|
||||
<cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
|
||||
</cpus>
|
||||
</cell>
|
||||
</cells>
|
||||
</topology>
|
||||
<cache>
|
||||
<bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
|
||||
</cache>
|
||||
<secmodel>
|
||||
<model>apparmor</model>
|
||||
<doi>0</doi>
|
||||
</secmodel>
|
||||
<secmodel>
|
||||
<model>dac</model>
|
||||
<doi>0</doi>
|
||||
<baselabel type='kvm'>+487:+486</baselabel>
|
||||
<baselabel type='qemu'>+487:+486</baselabel>
|
||||
</secmodel>
|
||||
</host>
|
||||
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<arch name='i686'>
|
||||
<wordsize>32</wordsize>
|
||||
<emulator>/usr/bin/qemu-system-i386</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<domain type='qemu'/>
|
||||
<domain type='kvm'>
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
<features>
|
||||
<cpuselection/>
|
||||
<deviceboot/>
|
||||
<disksnapshot default='on' toggle='no'/>
|
||||
<acpi default='on' toggle='yes'/>
|
||||
<apic default='on' toggle='no'/>
|
||||
<pae/>
|
||||
<nonpae/>
|
||||
</features>
|
||||
</guest>
|
||||
|
||||
<guest>
|
||||
<os_type>hvm</os_type>
|
||||
<arch name='x86_64'>
|
||||
<wordsize>64</wordsize>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
<domain type='qemu'/>
|
||||
<domain type='kvm'>
|
||||
<emulator>/usr/bin/qemu-kvm</emulator>
|
||||
<machine maxCpus='255'>pc-i440fx-2.6</machine>
|
||||
<machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
|
||||
<machine maxCpus='255'>pc-0.12</machine>
|
||||
</domain>
|
||||
</arch>
|
||||
<features>
|
||||
<cpuselection/>
|
||||
<deviceboot/>
|
||||
<disksnapshot default='on' toggle='no'/>
|
||||
<acpi default='on' toggle='yes'/>
|
||||
<apic default='on' toggle='no'/>
|
||||
</features>
|
||||
</guest>
|
||||
|
||||
</capabilities>
|
||||
'''
|
||||
self.mock_conn.getCapabilities.return_value = xml
|
||||
caps = virt.capabilities()
|
||||
|
||||
expected = {
|
||||
'host': {
|
||||
'uuid': '44454c4c-3400-105a-8033-b3c04f4b344a',
|
||||
'cpu': {
|
||||
'arch': 'x86_64',
|
||||
'model': 'Nehalem',
|
||||
'vendor': 'Intel',
|
||||
'microcode': '25',
|
||||
'sockets': 1,
|
||||
'cores': 4,
|
||||
'threads': 2,
|
||||
'features': ['vme', 'ds', 'acpi'],
|
||||
'pages': [{'size': '4 KiB'}, {'size': '2048 KiB'}]
|
||||
},
|
||||
'power_management': ['suspend_mem', 'suspend_disk', 'suspend_hybrid'],
|
||||
'migration': {
|
||||
'live': True,
|
||||
'transports': ['tcp', 'rdma']
|
||||
},
|
||||
'topology': {
|
||||
'cells': [
|
||||
{
|
||||
'id': 0,
|
||||
'memory': '12367120 KiB',
|
||||
'pages': [
|
||||
{'size': '4 KiB', 'available': 3091780},
|
||||
{'size': '2048 KiB', 'available': 0}
|
||||
],
|
||||
'distances': {
|
||||
0: 10,
|
||||
},
|
||||
'cpus': [
|
||||
{'id': 0, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'},
|
||||
{'id': 1, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'},
|
||||
{'id': 2, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'},
|
||||
{'id': 3, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'},
|
||||
{'id': 4, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'},
|
||||
{'id': 5, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'},
|
||||
{'id': 6, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'},
|
||||
{'id': 7, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
'cache': {
|
||||
'banks': [
|
||||
{'id': 0, 'level': 3, 'type': 'both', 'size': '8 MiB', 'cpus': '0-7'}
|
||||
]
|
||||
},
|
||||
'security': [
|
||||
{'model': 'apparmor', 'doi': '0', 'baselabels': []},
|
||||
{'model': 'dac', 'doi': '0', 'baselabels': [
|
||||
{'type': 'kvm', 'label': '+487:+486'},
|
||||
{'type': 'qemu', 'label': '+487:+486'}
|
||||
]}
|
||||
]
|
||||
},
|
||||
'guests': [
|
||||
{
|
||||
'os_type': 'hvm',
|
||||
'arch': {
|
||||
'name': 'i686',
|
||||
'wordsize': 32,
|
||||
'emulator': '/usr/bin/qemu-system-i386',
|
||||
'machines': {
|
||||
'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
|
||||
'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
|
||||
},
|
||||
'domains': {
|
||||
'qemu': {
|
||||
'emulator': None,
|
||||
'machines': {}
|
||||
},
|
||||
'kvm': {
|
||||
'emulator': '/usr/bin/qemu-kvm',
|
||||
'machines': {
|
||||
'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
|
||||
'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'features': {
|
||||
'cpuselection': {'default': True, 'toggle': False},
|
||||
'deviceboot': {'default': True, 'toggle': False},
|
||||
'disksnapshot': {'default': True, 'toggle': False},
|
||||
'acpi': {'default': True, 'toggle': True},
|
||||
'apic': {'default': True, 'toggle': False},
|
||||
'pae': {'default': True, 'toggle': False},
|
||||
'nonpae': {'default': True, 'toggle': False}
|
||||
}
|
||||
},
|
||||
{
|
||||
'os_type': 'hvm',
|
||||
'arch': {
|
||||
'name': 'x86_64',
|
||||
'wordsize': 64,
|
||||
'emulator': '/usr/bin/qemu-system-x86_64',
|
||||
'machines': {
|
||||
'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
|
||||
'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
|
||||
},
|
||||
'domains': {
|
||||
'qemu': {
|
||||
'emulator': None,
|
||||
'machines': {}
|
||||
},
|
||||
'kvm': {
|
||||
'emulator': '/usr/bin/qemu-kvm',
|
||||
'machines': {
|
||||
'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
|
||||
'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'features': {
|
||||
'cpuselection': {'default': True, 'toggle': False},
|
||||
'deviceboot': {'default': True, 'toggle': False},
|
||||
'disksnapshot': {'default': True, 'toggle': False},
|
||||
'acpi': {'default': True, 'toggle': True},
|
||||
'apic': {'default': True, 'toggle': False}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.assertEqual(expected, caps)
|
||||
|
||||
def test_network(self):
|
||||
xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch')
|
||||
root = ET.fromstring(xml_data)
|
||||
@ -637,6 +889,177 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
|
||||
self.assertEqual(root.find('forward').attrib['mode'], 'bridge')
|
||||
self.assertEqual(root.find('virtualport').attrib['type'], 'openvswitch')
|
||||
|
||||
def test_domain_capabilities(self):
|
||||
xml = '''
|
||||
<domainCapabilities>
|
||||
<path>/usr/bin/qemu-system-aarch64</path>
|
||||
<domain>kvm</domain>
|
||||
<machine>virt-2.12</machine>
|
||||
<arch>aarch64</arch>
|
||||
<vcpu max='255'/>
|
||||
<iothreads supported='yes'/>
|
||||
<os supported='yes'>
|
||||
<loader supported='yes'>
|
||||
<value>/usr/share/AAVMF/AAVMF_CODE.fd</value>
|
||||
<value>/usr/share/AAVMF/AAVMF32_CODE.fd</value>
|
||||
<value>/usr/share/OVMF/OVMF_CODE.fd</value>
|
||||
<enum name='type'>
|
||||
<value>rom</value>
|
||||
<value>pflash</value>
|
||||
</enum>
|
||||
<enum name='readonly'>
|
||||
<value>yes</value>
|
||||
<value>no</value>
|
||||
</enum>
|
||||
</loader>
|
||||
</os>
|
||||
<cpu>
|
||||
<mode name='host-passthrough' supported='yes'/>
|
||||
<mode name='host-model' supported='yes'>
|
||||
<model fallback='forbid'>sample-cpu</model>
|
||||
<vendor>ACME</vendor>
|
||||
<feature policy='require' name='vme'/>
|
||||
<feature policy='require' name='ss'/>
|
||||
</mode>
|
||||
<mode name='custom' supported='yes'>
|
||||
<model usable='unknown'>pxa262</model>
|
||||
<model usable='yes'>pxa270-a0</model>
|
||||
<model usable='no'>arm1136</model>
|
||||
</mode>
|
||||
</cpu>
|
||||
<devices>
|
||||
<disk supported='yes'>
|
||||
<enum name='diskDevice'>
|
||||
<value>disk</value>
|
||||
<value>cdrom</value>
|
||||
<value>floppy</value>
|
||||
<value>lun</value>
|
||||
</enum>
|
||||
<enum name='bus'>
|
||||
<value>fdc</value>
|
||||
<value>scsi</value>
|
||||
<value>virtio</value>
|
||||
<value>usb</value>
|
||||
<value>sata</value>
|
||||
</enum>
|
||||
</disk>
|
||||
<graphics supported='yes'>
|
||||
<enum name='type'>
|
||||
<value>sdl</value>
|
||||
<value>vnc</value>
|
||||
</enum>
|
||||
</graphics>
|
||||
<video supported='yes'>
|
||||
<enum name='modelType'>
|
||||
<value>vga</value>
|
||||
<value>virtio</value>
|
||||
</enum>
|
||||
</video>
|
||||
<hostdev supported='yes'>
|
||||
<enum name='mode'>
|
||||
<value>subsystem</value>
|
||||
</enum>
|
||||
<enum name='startupPolicy'>
|
||||
<value>default</value>
|
||||
<value>mandatory</value>
|
||||
<value>requisite</value>
|
||||
<value>optional</value>
|
||||
</enum>
|
||||
<enum name='subsysType'>
|
||||
<value>usb</value>
|
||||
<value>pci</value>
|
||||
<value>scsi</value>
|
||||
</enum>
|
||||
<enum name='capsType'/>
|
||||
<enum name='pciBackend'>
|
||||
<value>default</value>
|
||||
<value>kvm</value>
|
||||
<value>vfio</value>
|
||||
</enum>
|
||||
</hostdev>
|
||||
</devices>
|
||||
<features>
|
||||
<gic supported='yes'>
|
||||
<enum name='version'>
|
||||
<value>3</value>
|
||||
</enum>
|
||||
</gic>
|
||||
<vmcoreinfo supported='yes'/>
|
||||
</features>
|
||||
</domainCapabilities>
|
||||
'''
|
||||
|
||||
self.mock_conn.getDomainCapabilities.return_value = xml
|
||||
caps = virt.domain_capabilities()
|
||||
|
||||
expected = {
|
||||
'emulator': '/usr/bin/qemu-system-aarch64',
|
||||
'domain': 'kvm',
|
||||
'machine': 'virt-2.12',
|
||||
'arch': 'aarch64',
|
||||
'max_vcpus': 255,
|
||||
'iothreads': True,
|
||||
'os': {
|
||||
'loader': {
|
||||
'type': ['rom', 'pflash'],
|
||||
'readonly': ['yes', 'no'],
|
||||
'values': [
|
||||
'/usr/share/AAVMF/AAVMF_CODE.fd',
|
||||
'/usr/share/AAVMF/AAVMF32_CODE.fd',
|
||||
'/usr/share/OVMF/OVMF_CODE.fd'
|
||||
]
|
||||
}
|
||||
},
|
||||
'cpu': {
|
||||
'host-passthrough': True,
|
||||
'host-model': {
|
||||
'model': {
|
||||
'name': 'sample-cpu',
|
||||
'fallback': 'forbid'
|
||||
},
|
||||
'vendor': 'ACME',
|
||||
'features': {
|
||||
'vme': 'require',
|
||||
'ss': 'require'
|
||||
}
|
||||
},
|
||||
'custom': {
|
||||
'models': {
|
||||
'pxa262': 'unknown',
|
||||
'pxa270-a0': 'yes',
|
||||
'arm1136': 'no'
|
||||
}
|
||||
}
|
||||
},
|
||||
'devices': {
|
||||
'disk': {
|
||||
'diskDevice': ['disk', 'cdrom', 'floppy', 'lun'],
|
||||
'bus': ['fdc', 'scsi', 'virtio', 'usb', 'sata'],
|
||||
},
|
||||
'graphics': {
|
||||
'type': ['sdl', 'vnc']
|
||||
},
|
||||
'video': {
|
||||
'modelType': ['vga', 'virtio']
|
||||
},
|
||||
'hostdev': {
|
||||
'mode': ['subsystem'],
|
||||
'startupPolicy': ['default', 'mandatory', 'requisite', 'optional'],
|
||||
'subsysType': ['usb', 'pci', 'scsi'],
|
||||
'capsType': [],
|
||||
'pciBackend': ['default', 'kvm', 'vfio']
|
||||
}
|
||||
},
|
||||
'features': {
|
||||
'gic': {
|
||||
'version': ['3']
|
||||
},
|
||||
'vmcoreinfo': {}
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(expected, caps)
|
||||
|
||||
def test_network_tag(self):
|
||||
xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch', 1001)
|
||||
root = ET.fromstring(xml_data)
|
||||
|
Loading…
Reference in New Issue
Block a user