Generate the XML string to define a libvirt VM

def _gen_xml(name,
             cpu,
             mem,
             diskp,
             nicp,
             hypervisor,
             os_type,
             arch,
             graphics=None,
             loader=None,
             **kwargs):
    '''
    Generate the XML string to define a libvirt VM
    '''
    mem = int(mem) * 1024  # MB
    context = {
        'hypervisor': hypervisor,
        'name': name,
        'cpu': six.text_type(cpu),
        'mem': six.text_type(mem),
    }
    if hypervisor in ['qemu', 'kvm']:
        context['controller_model'] = False
    elif hypervisor == 'vmware':
        # TODO: make bus and model parameterized, this works for 64-bit Linux
        context['controller_model'] = 'lsilogic'

    # By default, set the graphics to listen to all addresses
    if graphics:
        if 'listen' not in graphics:
            graphics['listen'] = {'type': 'address', 'address': '0.0.0.0'}
        elif 'address' not in graphics['listen'] and graphics['listen']['type'] == 'address':
            graphics['listen']['address'] = '0.0.0.0'

        # Graphics of type 'none' means no graphics device at all
        if graphics.get('type', 'none') == 'none':
            graphics = None
    context['graphics'] = graphics

    if loader and 'path' not in loader:
        log.info('`path` is a required property of `loader`, and cannot be found. Skipping loader configuration')
        loader = None
    elif loader:
        loader_attributes = []
        for key, val in loader.items():
            if key == 'path':
                continue
            loader_attributes.append("{key}='{val}'".format(key=key, val=val))
        loader['_attributes'] = ' '.join(loader_attributes)

    if 'boot_dev' in kwargs:
        context['boot_dev'] = []
        for dev in kwargs['boot_dev'].split():
            context['boot_dev'].append(dev)
    else:
        context['boot_dev'] = ['hd']

    context['loader'] = loader

    if os_type == 'xen':
        # Compute the Xen PV boot method
        if __grains__['os_family'] == 'Suse':
            context['kernel'] = '/usr/lib/grub2/x86_64-xen/grub.xen'
            context['boot_dev'] = []

    if 'serial_type' in kwargs:
        context['serial_type'] = kwargs['serial_type']
    if 'serial_type' in context and context['serial_type'] == 'tcp':
        if 'telnet_port' in kwargs:
            context['telnet_port'] = kwargs['telnet_port']
        else:
            context['telnet_port'] = 23023  # FIXME: use random unused port
    if 'serial_type' in context:
        if 'console' in kwargs:
            context['console'] = kwargs['console']
        else:
            context['console'] = True

    context['disks'] = []
    disk_bus_map = {'virtio': 'vd', 'xen': 'xvd', 'fdc': 'fd', 'ide': 'hd'}
    for i, disk in enumerate(diskp):
        prefix = disk_bus_map.get(disk['model'], 'sd')
        disk_context = {
            'device': disk.get('device', 'disk'),
            'target_dev': '{0}{1}'.format(prefix, string.ascii_lowercase[i]),
            'disk_bus': disk['model'],
            'type': disk['format'],
            'index': six.text_type(i),
        }
        if 'source_file' and disk['source_file']:
            disk_context['source_file'] = disk['source_file']

        if hypervisor in ['qemu', 'kvm', 'bhyve', 'xen']:
            disk_context['address'] = False
            disk_context['driver'] = True
        elif hypervisor in ['esxi', 'vmware']:
            disk_context['address'] = True
            disk_context['driver'] = False
        context['disks'].append(disk_context)
    context['nics'] = nicp

    context['os_type'] = os_type
    context['arch'] = arch

    fn_ = 'libvirt_domain.jinja'
    try:
        template = JINJA.get_template(fn_)
    except jinja2.exceptions.TemplateNotFound:
        log.error('Could not load template %s', fn_)
        return ''

    return template.render(**context)