def devices_xml(self): boot_no = Nid(1) scsi_device_no = Nid(1) virtual_device_no = Nid(1) devices = [] for device in filter(lambda d: d.is_available(), self.devices): if isinstance(device, (DISK, CDROM, RAW)): if device.data['attributes'].get('type') == 'VIRTIO': disk_no = virtual_device_no() else: disk_no = scsi_device_no() device_xml = device.xml(disk_number=disk_no, boot_number=boot_no()) else: device_xml = device.xml() devices.extend(device_xml if isinstance(device_xml, ( tuple, list)) else [device_xml]) if self.vm_data['ensure_display_device'] and not any( isinstance(device, DISPLAY) for device in self.devices): # We should add a video device if there is no display device configured because most by # default if not all headless servers like ubuntu etc require it to boot devices.append(create_element('video')) devices.append(create_element('serial', type='pty')) return create_element('devices', attribute_dict={'children': devices})
def devices_xml(self): boot_no = Nid(1) scsi_device_no = Nid(1) virtual_device_no = Nid(1) devices = [] for device in filter(lambda d: d.is_available(), self.devices): if isinstance(device, (DISK, CDROM, RAW)): if device.data['attributes'].get('type') == 'VIRTIO': disk_no = virtual_device_no() else: disk_no = scsi_device_no() device_xml = device.xml(disk_number=disk_no, boot_number=boot_no()) else: device_xml = device.xml() devices.extend(device_xml if isinstance(device_xml, ( tuple, list)) else [device_xml]) spice_server_available = display_device_available = False for device in filter(lambda d: isinstance(d, DISPLAY), self.devices): display_device_available = True if device.is_spice_type: spice_server_available = True break if self.vm_data[ 'ensure_display_device'] and not display_device_available: # We should add a video device if there is no display device configured because most by # default if not all headless servers like ubuntu etc require it to boot devices.append(create_element('video')) if spice_server_available: # We always add spicevmc channel device when a spice display device is available to allow users # to install guest agents for improved vm experience devices.append( create_element('channel', type='spicevmc', attribute_dict={ 'children': [ create_element( 'target', type='virtio', name='com.redhat.spice.0') ] })) devices.append(create_element('serial', type='pty')) return create_element('devices', attribute_dict={'children': devices})
def devices_xml(self): pptdev_choices = None boot_no = Nid(1) scsi_device_no = Nid(1) virtual_device_no = Nid(1) devices = [] for device in self.devices: if isinstance(device, (DISK, CDROM, RAW)): if device.data['attributes'].get('type') == 'VIRTIO': disk_no = virtual_device_no() else: disk_no = scsi_device_no() device_xml = device.xml(disk_number=disk_no, boot_number=boot_no()) elif isinstance(device, PCI): if pptdev_choices is None: pptdev_choices = self.middleware.call_sync( 'vm.device.passthrough_device_choices') if device.passthru_device() not in pptdev_choices: self.middleware.call_sync( 'alert.oneshot_create', 'PCIDeviceUnavailable', { 'pci': device.passthru_device(), 'vm_name': self.vm_data['name'] }) continue else: self.middleware.call_sync('alert.oneshot_delete', 'PCIDeviceUnavailable', device.passthru_device()) device_xml = device.xml(passthrough_choices=pptdev_choices) else: device_xml = device.xml() devices.extend(device_xml if isinstance(device_xml, ( tuple, list)) else [device_xml]) if not any(isinstance(device, DISPLAY) for device in self.devices): # We should add a video device if there is no display device configured because most by # default if not all headless servers like ubuntu etc require it to boot devices.append(create_element('video')) devices.append(create_element('serial', type='pty')) return create_element('devices', attribute_dict={'children': devices})
def devices_xml(self): pptdev_choices = None boot_no = Nid(1) scsi_device_no = Nid(1) virtual_device_no = Nid(1) devices = [] for device in self.devices: if isinstance(device, (DISK, CDROM, RAW)): if device.data['attributes'].get('type') == 'VIRTIO': disk_no = virtual_device_no() else: disk_no = scsi_device_no() device_xml = device.xml(disk_number=disk_no, boot_number=boot_no()) elif isinstance(device, PCI): if pptdev_choices is None: pptdev_choices = self.middleware.call_sync( 'vm.device.passthrough_device_choices') if device.passthru_device() not in pptdev_choices: self.middleware.call_sync( 'alert.oneshot_create', 'PCIDeviceUnavailable', { 'pci': device.passthru_device(), 'vm_name': self.vm_data['name'] }) continue else: self.middleware.call_sync('alert.oneshot_delete', 'PCIDeviceUnavailable', device.passthru_device()) device_xml = device.xml(passthrough_choices=pptdev_choices) else: device_xml = device.xml() devices.extend(device_xml if isinstance(device_xml, ( tuple, list)) else [device_xml]) devices.extend( [create_element('serial', type='pty'), create_element('video')]) return create_element('devices', attribute_dict={'children': devices})
def devices_xml(self): devices = [] pci_slot = Nid(3) controller_index = Nid(1) controller_base = { 'index': None, 'slot': None, 'function': 0, 'devices': 0 } ahci_current_controller = controller_base.copy() virtio_current_controller = controller_base.copy() ppt_maps = [] for device in self.devices: if isinstance(device, (DISK, CDROM, RAW)): # We classify all devices in 2 types: # 1) AHCI # 2) VIRTIO # Before deciding how we attach the disk/cdrom devices wrt slots/functions, following are few basic # rules: # We have a maximum of 32 slots ( 0-31 ) available which can be attached to the VM. Each slot supports # functions which for each slot can be up to 8 ( 0-7 ). For legacy reasons, we start with the 3rd # slot for numbering disks. # AHCI based devices can be up to 32 in number per function. # VIRTIO based disk devices consume a complete function meaning a maximum of 1 VIRTIO device can be # present in a function. # Libvirt / freebsd specific implementation # We do not have great support of bhyve driver in libvirt, so this is a best effort to emulate our # old implementation command. Following are a few points i have outlined to make the following logic # clearer: # 1) For AHCI based devices, libvirt assigns all of them to one slot ( a bug there ), we don't want # that of course as bhyve imposes a restriction of a maximum 32 devices per function for AHCI. To come # around this issue, controllers have been used for AHCI based devices which help us manage them # nicely allotting them on specific supplied slots/functions. # 2) For VIRTIO based disk devices, we use "pci" for their address type which helps us set # the slot/function number and it actually being respected. Reason this can't be used with AHCI is # that pci and sata bus are incompatible in AHCI and libvirt raises an error in this case. if device.data['attributes'].get('type') != 'VIRTIO': virtio = False current_controller = ahci_current_controller max_devices = 32 else: virtio = True current_controller = virtio_current_controller max_devices = 1 if not current_controller['slot'] or current_controller[ 'devices'] == max_devices: # Two scenarios will happen, either we bump function no or slot no if not current_controller['slot'] or current_controller[ 'function'] == 8: # We need to add a new controller with a new slot current_controller.update({ 'slot': pci_slot(), 'function': 0, 'devices': 0, }) else: # We just need to bump the function here current_controller.update({ 'function': current_controller['function'] + 1, 'devices': 0, }) # We should add this to xml now if not virtio: current_controller['index'] = controller_index() devices.append( create_element( 'controller', type='sata', index=str(current_controller['index']), attribute_dict={ 'children': [ create_element( 'address', type='pci', slot=str( current_controller['slot']), function=str( current_controller['function'] ), multifunction='on') ] })) current_controller['devices'] += 1 if virtio: address_dict = { 'type': 'pci', 'slot': str(current_controller['slot']), 'function': str(current_controller['function']) } else: address_dict = { 'type': 'drive', 'controller': str(current_controller['index']), 'target': str(current_controller['devices']) } device_xml = device.xml( child_element=create_element('address', **address_dict)) elif isinstance(device, NIC): device_xml = device.xml(slot=pci_slot()) elif isinstance(device, PCI): # PCI passthru section begins here # Check if ppt device is available for passthru. Map, only if available. host_bsf = device.ppt_map['host_bsf'] if host_bsf is not None: guest_bsf = self.guest_pptdev(ppt_maps, pci_slot, host_bsf) else: guest_bsf = None device.ppt_map['guest_bsf'] = guest_bsf if guest_bsf is not None: ppt_maps.append(device.ppt_map) device_xml = device.xml() # PCI passthru section ends here else: device_xml = device.xml() if device_xml is not None: devices.extend(device_xml if isinstance( device_xml, (tuple, list)) else [device_xml]) devices.append( create_element( 'serial', type='nmdm', attribute_dict={ 'children': [ create_element( 'source', master=f'/dev/nmdm{self.vm_data["id"]}A', slave=f'/dev/nmdm{self.vm_data["id"]}B') ] })) return create_element('devices', attribute_dict={'children': devices})
def run(self): args = [ 'bhyve', '-A', '-P', '-H', '-c', str(self.vm['vcpus']), '-m', str(self.vm['memory']), '-s', '0:0,hostbridge', '-s', '31,lpc', '-l', 'com1,/dev/nmdm{}A'.format(self.vm['id']), ] if self.vm['bootloader'] in ('UEFI', 'UEFI_CSM'): args += [ '-l', 'bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI{}.fd'.format('_CSM' if self.vm['bootloader'] == 'UEFI_CSM' else ''), ] nid = Nid(3) for device in self.vm['devices']: if device['dtype'] == 'DISK': if device['attributes'].get('mode') == 'AHCI': args += ['-s', '{},ahci-hd,{}'.format(nid(), device['attributes']['path'])] else: args += ['-s', '{},virtio-blk,{}'.format(nid(), device['attributes']['path'])] elif device['dtype'] == 'CDROM': args += ['-s', '{},ahci-cd,{}'.format(nid(), device['attributes']['path'])] elif device['dtype'] == 'NIC': tapname = netif.create_interface('tap') tap = netif.get_interface(tapname) tap.up() self.taps.append(tapname) # If Bridge if True: bridge = None for name, iface in netif.list_interfaces().items(): if name.startswith('bridge'): bridge = iface break if not bridge: bridge = netif.get_interface(netif.create_interface('bridge')) bridge.add_member(tapname) defiface = Popen("route -nv show default|grep -w interface|awk '{ print $2 }'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip() if defiface and defiface not in bridge.members: bridge.add_member(defiface) bridge.up() if device['attributes'].get('type') == 'VIRTIO': nictype = 'virtio-net' else: nictype = 'e1000' args += ['-s', '{},{},{}'.format(nid(), nictype, tapname)] elif device['dtype'] == 'VNC': if device['attributes'].get('wait'): wait = 'wait' else: wait = '' args += [ '-s', '29,fbuf,tcp=0.0.0.0:{},w=1024,h=768,{}'.format(5900 + self.vm['id'], wait), '-s', '30,xhci,tablet', ] args.append(self.vm['name']) self.logger.debug('Starting bhyve: {}'.format(' '.join(args))) self.proc = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in self.proc.stdout: self.logger.debug('{}: {}'.format(self.vm['name'], line)) self.proc.wait() self.logger.info('Destroying {}'.format(self.vm['name'])) Popen(['bhyvectl', '--destroy', '--vm={}'.format(self.vm['name'])], stdout=subprocess.PIPE, stderr=subprocess.PIPE).wait() while self.taps: netif.destroy_interface(self.taps.pop()) self.manager._vm.pop(self.vm['id'], None)
async def run(self): args = [ 'bhyve', '-H', '-w', '-c', str(self.vm['vcpus']), '-m', str(self.vm['memory']), '-s', '0:0,hostbridge', '-s', '31,lpc', '-l', 'com1,/dev/nmdm{}A'.format(self.vm['id']), ] if self.vm['bootloader'] in ('UEFI', 'UEFI_CSM'): args += [ '-l', 'bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI{}.fd'. format('_CSM' if self.vm['bootloader'] == 'UEFI_CSM' else ''), ] nid = Nid(3) for device in self.vm['devices']: if device['dtype'] == 'DISK' or device['dtype'] == 'RAW': disk_sector_size = device['attributes'].get('sectorsize', 0) if disk_sector_size > 0: sectorsize_args = ",sectorsize=" + str(disk_sector_size) else: sectorsize_args = "" if device['attributes'].get('type') == 'AHCI': args += [ '-s', '{},ahci-hd,{}{}'.format(nid(), device['attributes']['path'], sectorsize_args) ] else: args += [ '-s', '{},virtio-blk,{}{}'.format( nid(), device['attributes']['path'], sectorsize_args) ] elif device['dtype'] == 'CDROM': args += [ '-s', '{},ahci-cd,{}'.format(nid(), device['attributes']['path']) ] elif device['dtype'] == 'NIC': attach_iface = device['attributes'].get('nic_attach') self.logger.debug('====> NIC_ATTACH: {0}'.format(attach_iface)) tapname = netif.create_interface('tap') tap = netif.get_interface(tapname) tap.up() self.taps.append(tapname) await self.bridge_setup(tapname, tap, attach_iface) if device['attributes'].get('type') == 'VIRTIO': nictype = 'virtio-net' else: nictype = 'e1000' mac_address = device['attributes'].get('mac', None) # By default we add one NIC and the MAC address is an empty string. # Issue: 24222 if mac_address == "": mac_address = None if mac_address == '00:a0:98:FF:FF:FF' or mac_address is None: args += [ '-s', '{},{},{},mac={}'.format(nid(), nictype, tapname, self.random_mac()) ] else: args += [ '-s', '{},{},{},mac={}'.format(nid(), nictype, tapname, mac_address) ] elif device['dtype'] == 'VNC': if device['attributes'].get('wait'): wait = 'wait' else: wait = '' vnc_resolution = device['attributes'].get( 'vnc_resolution', None) vnc_port = int(device['attributes'].get( 'vnc_port', 5900 + self.vm['id'])) vnc_bind = device['attributes'].get('vnc_bind', '0.0.0.0') vnc_password = device['attributes'].get('vnc_password', None) vnc_web = device['attributes'].get('vnc_web', None) vnc_password_args = "" if vnc_password: vnc_password_args = ",password="******"==> Start WEBVNC at port {} with pid number {}".format( vnc_web_port, self.web_proc.pid)) while True: line = await self.proc.stdout.readline() if line == b'': break self.logger.debug('{}: {}'.format(self.vm['name'], line.decode())) # bhyve returns the following status code: # 0 - VM has been reset # 1 - VM has been powered off # 2 - VM has been halted # 3 - VM generated a triple fault # all other non-zero status codes are errors self.bhyve_error = await self.proc.wait() if self.bhyve_error == 0: self.logger.info( "===> Rebooting VM: {0} ID: {1} BHYVE_CODE: {2}".format( self.vm['name'], self.vm['id'], self.bhyve_error)) await self.manager.restart(self.vm['id']) await self.manager.start(self.vm['id']) elif self.bhyve_error == 1: # XXX: Need a better way to handle the vmm destroy. self.logger.info( "===> Powered off VM: {0} ID: {1} BHYVE_CODE: {2}".format( self.vm['name'], self.vm['id'], self.bhyve_error)) await self.destroy_vm() elif self.bhyve_error in (2, 3): self.logger.info( "===> Stopping VM: {0} ID: {1} BHYVE_CODE: {2}".format( self.vm['name'], self.vm['id'], self.bhyve_error)) await self.manager.stop(self.vm['id']) elif self.bhyve_error not in (0, 1, 2, 3, None): self.logger.info( "===> Error VM: {0} ID: {1} BHYVE_CODE: {2}".format( self.vm['name'], self.vm['id'], self.bhyve_error)) await self.destroy_vm()