# MetaOS虚拟设备管理

### 配置虚拟机PCIe控制器

#### 概述

虚拟机内部的网卡、磁盘控制器、PCIe直通设备都需要挂接到PCIe Root Port下面，每个Root Port对应一个PCIe插槽。Root Port的下挂设备支持热插拔，但是Root Port本身不支持热插拔，因此需要用户考虑设备热插的需求，规划虚拟机需要预留的最大PCIe Root Port数量，在虚拟机启动之前完成Root Port的静态配置。

#### 配置PCIe Root、PCIe Root Port和PCIe-PCI-Bridge

虚拟机PCIe控制器通过XML文件进行配置，PCIe Root、PCIe Root Port和PCIe-PCI-Bridge对应XML中的model分别为pcie-root、pcie-root-port、pcie-to-pci-bridge。

- 简化配置方法

  在虚拟机的XML文件中写入以下内容，controller的其他属性由libvirt自动填充：

        <controller type='pci' index='0' model='pcie-root'/>
        <controller type='pci' index='1' model='pcie-root-port'/>
        <controller type='pci' index='2' model='pcie-to-pci-bridge'/>
        <controller type='pci' index='3' model='pcie-root-port'/>
        <controller type='pci' index='4' model='pcie-root-port'/>
        <controller type='pci' index='5' model='pcie-root-port'/>

  其中：由于pcie-root和pcie-to-pci-bridge分别占用1个index，因此最终的index等于需要的Root Port数量+1。

- 完整配制方法

  在虚拟机的XML文件中写入以下内容：

        <controller type='pci' index='0' model='pcie-root'/>
        <controller type='pci' index='1' model='pcie-root-port'>
          <model name='pcie-root-port'/>
          <target chassis='1' port='0x8'/>
          <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
        </controller>
        <controller type='pci' index='2' model='pcie-to-pci-bridge'>
          <model name='pcie-pci-bridge'/>
          <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
        </controller>
        <controller type='pci' index='3' model='pcie-root-port'>
          <model name='pcie-root-port'/>
          <target chassis='3' port='0x9'/>
          <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
        </controller>
        <controller type='pci' index='3' model='pcie-root-port'>

  其中：

  - Root Port的chassis和port属性必须依次递增，由于中间插入一个PCIe-PCI-Bridge，chassis编号跳过2，但是port编号仍然连续。
  - Root Port的address function的取值范围为0x0~0x7。
  - 每个slot最多下挂8个function，挂满之后需要递增slot编号。

  由于完整配置方法相对复杂，建议采用简化配置方法。

### 管理虚拟磁盘

#### 概述

虚拟磁盘类型主要包含virtio-blk、virtio-scsi、vhost-scsi等。virtio-blk模拟的是一种block设备，virtio-scsi和vhost-scsi模拟的是一种scsi设备。

- virtio-blk：普通系统盘和数据盘可用，该种配置下虚拟磁盘在虚拟机内部呈现为vd[a-z]或vd[a-z][a-z]。
- virtio-scsi：普通系统盘和数据盘建议选用，该种配置下虚拟磁盘在虚拟机内部呈现为sd[a-z]或sd[a-z][a-z]。
- vhost-scsi：对性能要求高的虚拟磁盘建议选用，该种配置下虚拟磁盘在虚拟机内部呈现为sd[a-z]或sd[a-z][a-z]。

#### 操作步骤

虚拟磁盘的配置步骤，请参见“虚拟机配置 > 存储设备”。本节以virtio-scsi磁盘为例，介绍挂载和卸载虚拟磁盘的简单方法。

- 挂载virtio-scsi磁盘：

  使用virsh attach-device命令挂载virtio-scsi虚拟磁盘：

      # virsh attach-device <VMInstance> <attach-device.xml>

  上述命令可以为虚拟机在线挂载磁盘，其中磁盘信息由attach-device.xml文件指定。下面是一个attach-device.xml文件的例子：

      ### attach-device.xml ###
          <disk type='file' device='disk'>
            <driver name='qemu' type='qcow2' cache='none' io='native'/>
            <source file='/path/to/another/qcow2-file'/>
            <backingStore/>
            <target dev='sdb' bus='scsi'/>
            <address type='drive' controller='0' bus='0' target='1' unit='0'/>
          </disk>

  通过上述命令挂载的磁盘，在虚拟机关机重启后失效。如果需要为虚拟机持久化挂载虚拟磁盘，需要使用带--config参数的virsh attach-device命令。

- 卸载virtio-scsi磁盘：

  通过在线挂载的磁盘，如果不需要再使用，可以通过virsh detach-device命令动态卸载：

      # virsh detach-device <VMInstance> <detach-device.xml>

  其中，detach-device.xml指定了需要卸载的磁盘的XML信息，与动态挂载时的XML信息保持一致。

### 管理虚拟网卡

#### 概述

虚拟网卡类型主要包含virtio-net、vhost-net、vhost-user等。用户在创建虚拟机后，可能会有挂载或者卸载虚拟网卡的需求。metaOS提供了网卡热插拔的功能，通过网卡热插拔，能够改变网络的吞吐量，提高系统的灵活性和扩展性。

#### 操作步骤

虚拟网卡的配置步骤，请参见“虚拟机配置 > 网络设备”。本节以vhost-net网卡为例，介绍挂载和卸载虚拟网卡的简单方法。

- 挂载vhost-net网卡：

  使用virsh attach-device命令挂载vhost-net虚拟网卡：

      # virsh attach-device <VMInstance> <attach-device.xml>

  上述命令可以为虚拟机在线挂载vhost-net网卡，其中网卡信息由attach-device.xml文件指定。下面是一个attach-device.xml文件的例子：

      ### attach-device.xml ###
          <interface type='bridge'>
            <mac address='52:54:00:76:f2:bb'/>
            <source bridge='br0'/>
            <virtualport type='openvswitch'/>
            <model type='virtio'/>
            <driver name='vhost' queues='2'/>
          </interface>

  通过上述命令挂载的vhost-net网卡，在虚拟机关机重启后失效。如果需要为虚拟机持久化挂载虚拟网卡，需要使用带--config参数的virsh attach-device命令。

- 卸载vhost-net网卡：

  通过在线挂载的网卡，如果不需要再使用，可以通过virsh detach命令动态卸载：

      # virsh detach-device <VMInstance> <detach-device.xml>

  其中，detach-device.xml指定了需要卸载虚拟网卡的XML信息，与动态挂载时的XML信息保持一致。

### 配置虚拟串口

#### 概述

在虚拟化环境下，由于管理和业务的需求，虚拟机与宿主机需要互相通信。但在云管理系统复杂的网络架构下，运行在管理平面的服务与运行在业务平面的虚拟机之间，不能简单的进行三层网络互相通信，导致服务部署和信息收集不够快速。因此需要提供虚拟串口，来达到虚拟机与宿主机之间互相通信的目的。 通过在虚拟机的XML配置文件中增加相应串口的配置项，可以实现虚拟机与宿主机之间的互相通信。

#### 操作步骤

Linux虚拟机串口控制台，即虚拟机串口连接到宿主机的一个伪终端设备，通过宿主机的设备间接实现对虚拟机的交互式操作。在该场景下串口需配置为pty类型，本节介绍pty型串口的配置方法。

- 在虚拟机的XML配置文件中"devices"节点下添加如下所示的虚拟串口配置项：

      <serial type='pty'>
      </serial>
      <console type='pty'>
          <target type='serial'/>
      </console>

- 使用virsh console命令连接到正在运行的虚拟机的pty串口。

      # virsh console <VMInstance>

- 如果要确保没有遗漏任何串口消息，请在启动虚拟机时使用--console选项连接到串口。

      # virsh start --console <VMInstance>

### 管理设备直通

设备直通技术是指将host上的物理设备直接呈现给一台虚拟机，虚拟机可以直接访问该设备资源的一种使用方式。使用设备直通的方式可以让虚拟机获得良好的I/O性能。

当前设备直通使用的是VFIO方式，按照直通的设备类型可以分为PCI直通和SR-IOV直通两种类型。

#### PCI/PCIE穿透

PCI直通是指将host上的物理PCI设备直接呈现给一台虚拟机，供虚拟机直接访问的一种使用方式。PCI直通使用了vfio设备直通方式，为虚拟机配置PCI直通的xml配置如下：

    <hostdev mode='subsystem' type='pci' managed='yes'>
        <driver name='vfio'/>
        <source>
            <address domain='0x0000' bus='0x04' slot='0x10' function='0x01'/>
        </source>
        <rom bar='off'/>
        <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </hostdev>

> 说明： VFIO直通方式的最小直通单位是iommu_group，host根据硬件上的ACS位，来划分iommu_group。同一个iommu_group中的设备只允许直通给同一台虚拟机（一个PCI设备上的若干个function，如果属于同一个iommu_group，只允许直通给一个虚拟机使用）。

### 管理虚拟机USB

为了方便在虚拟机内部使用USBkey设备、USB海量存储设备等USB设备，metaOS提供了USB设备直通的功能。用户可以通过USB直通和热插拔相关接口给虚拟机配置直通USB设备、或者在虚拟机处于运行的状态下热插/热拔USB设备。

#### 配置USB控制器

##### 概述

USB控制器是为虚拟机上的USB设备提供具体USB功能的虚拟控制器设备，在虚拟机内部使用USB设备必须给虚拟机配置USB控制器。当前metaOS支持如下三种USB控制器：

- UHCI（Universal Host Controller Interface）：通用主机控制器接口，也称为USB 1.1主机控制器规范。
- EHCI（Enhanced Host Controller Interface）：增强主机控制器接口，也称为USB 2.0主机控制器规范。
- xHCI（eXtensible Host Controller Interface）：可扩展主机控制器接口，也称为USB 3.0主机控制器规范。

##### 注意事项

- 主机服务器上需存在支持USB 1.1、USB 2.0和USB 3.0标准的USB控制器硬件和模块。
- 为虚拟机配置USB控制器时，请按照USB 1.1、USB 2.0到USB 3.0的顺序来配置。
- 一个xHCI控制器有8个端口，最多可以挂载4个USB 3.0设备和4个USB 2.0设备。一个EHCI控制器有6个端口，最多可以挂载6个USB2.0设备。一个UHCI控制器有2个端口，最多可以挂载2个USB 1.1设备。
- 每台虚拟机最多支持配置一个相同类型的USB控制器。
- 不支持热插拔USB控制器。
- 若虚拟机没有安装USB 3.0的驱动，可能无法识别xHCI控制器，USB 3.0驱动下载和安装方法请参见对应OS发行商官方说明。
- 为了不影响操作系统的兼容性，为虚拟机配置USB接口的tablet设备时，请指定USB控制器bus号为0（默认挂载到USB 1.1控制器上）。

##### 配置方法

这里介绍为虚拟机配置USB控制器的配置内容说明。建议同时配置USB 1.1、USB 2.0和USB 3.0，做到同时兼容三种设备。

USB 1.1控制器（UHCI）的XML配置项为：

    <controller type='usb' index='0' model='piix3-uhci'>
    </controller>

USB 2.0控制器（EHCI）的XML配置为：

    <controller type='usb' index='1' model='ehci'>
    </controller>

USB 3.0控制器（xHCI）的XML配置为：

    <controller type='usb' index='2' model='nec-xhci'>
    </controller>

#### 配置USB直通设备

##### 概述

当虚拟机配置好USB控制器后，就可以通过设备直通的方式将主机上的物理USB设备挂载到虚拟机内部供虚拟机使用。在虚拟化场景下，除了支持静态配置以外还同时支持USB设备的热插/拔操作，即在虚拟机处于运行状态的时候挂载/卸载USB设备。

##### 注意事项

- 一个USB设备只能直通给一台虚拟机使用
- 配置了直通USB设备的虚拟机不支持热迁移
- 虚拟机配置文件中直通的USB设备不存在时，虚拟机会创建失败
- 对一个正在读写的USB存储设备进行强制热拔操作有可能会损坏USB设备内的文件

##### 配置说明

这里介绍为虚拟机配置USB设备的配置内容说明。

USB设备的XML描述：

    <hostdev mode='subsystem' type='usb' managed='yes'>
        <source>
            <address bus='m' device='n'/>
        </source>
        <address type='usb' bus='x' port='y'/>
    </hostdev>

- <address bus='m' device='n'/>，其中，m表示该USB设备在主机上的bus地址，n表示device ID编号。
- <address type='usb' bus='x' port='y'/> 表示该USB设备要挂载到虚拟机指定的USB控制器。其中x表示控制器ID，与虚拟机所配置的USB控制器index编号相对应，y表示port地址。用户配置直通USB设备的时候需要配置这个字段，确保设备挂载的控制器与预期相符。

##### 配置方法

配置USB直通的步骤如下：

1. 为虚拟机配置USB控制器，配置方法请参见“虚拟机配置 > 配置USB控制器”。

2. 查询主机上的USB设备信息。

   通过lsusb命令（需要安装usbutils软件包）查询主机上的USB设备信息，包含bus地址、device地址、设备厂商ID、设备ID和产品描述信息等。例如：

       # lsusb

3. 准备USB设备的XML描述文件，注意在设备热拔之前，请确保USB设备当前不在使用当中，否则可能造成数据丢失。

4. 执行热插、热拔命令。

   假设虚拟机名称为metaOSVM，对应的配置文件为usb.xml。

   - 热插USB设备，只对当前运行的虚拟机有效，虚拟机冷重启后需要重新配置。

         # virsh attach-device metaOSVM usb.xml --live

   - 热插USB设备，持久化该配置，即该虚拟机重启后该设备会自动直通给该虚拟机使用。

         # virsh attach-device metaOSVM usb.xml --config

   - 热拔USB设备，只对当前运行的虚拟机有效，持久化配置的USB设备在虚拟机重启后USB设备会自动直通给该虚拟机。

         # virsh detach-device metaOSVM usb.xml --live

   - 热拔USB设备，持久化该配置。

         # virsh detach-device metaOSVM usb.xml --config

### 管理快照

#### 概述

虚拟机在使用过程中可能由于病毒对系统的破坏、系统文件被误删除或误格式化等原因造成虚拟机系统损坏导致系统无法启动。为了使损坏的系统快速恢复，metaOS提供了存储快照功能。metaOS可以在用户不感知的情况下制作虚拟机在某一时刻的快照（制作通常指需要几秒钟），该快照能帮助用户将磁盘快速恢复到某一时刻的状态，例如系统损坏后能快速恢复系统，从而提升系统可靠性。

> 说明： 当前存储快照只支持raw、qcow2格式镜像，不支持block块设备。

#### 操作步骤

制作虚拟机存储快照的操作步骤如下：

1. 登录主机，通过virsh domblklist命令查询虚拟机使用的磁盘。

       $ virsh domblklist metaOSVM

2. 创建虚拟机磁盘快照_metaOS-snapshot1.qcow2_，命令及回显如下：

       $ virsh snapshot-create-as --domain metaOSVM --disk-only --diskspec vda,snapshot=external,file=/mnt/metaOS-snapshot1.qcow2 --atomic
       Domain snapshot 1582605802 created

3. 磁盘快照查询操作。

       $ virsh snapshot-list metaOSVM

### 配置磁盘IO悬挂

#### 总体介绍

#### 概述

存储故障（比如存储断链）场景下，物理磁盘的IO错误，通过虚拟化层传给虚拟机前端，虚拟机内部收到IO错误，可能导致虚拟机内部的用户文件系统变成read-only状态，需要重启虚拟机或者用户手动恢复，这给用户带来额外的工作量。

这种情况下，虚拟化平台提供了一种磁盘IO悬挂的能力，即当存储故障时，虚拟机IO下发到主机侧时将IO悬挂住，在悬挂时间内不对虚拟机内部返回IO错误，这样虚拟机内部的文件系统就不会因为IO错误而变为只读状态，而是呈现为Hang住；同时虚拟机后端按指定的悬挂间隔对IO进行重试。如果存储故障在悬挂时间内恢复正常，悬挂住的IO即可恢复落盘，虚拟机内部文件系统自动恢复运行，不需要重启虚拟机；如果存储故障在悬挂时间内未能恢复正常，则上报错误给虚拟机内部，通知给用户。

#### 应用场景

使用可能会发生存储面链路断链的云盘作为虚拟磁盘后端的场景。

#### 注意事项和约束限制

- 磁盘IO悬挂仅支持virtio-blk或virtio-scsi类型的虚拟磁盘。

- 磁盘IO悬挂的虚拟磁盘后端一般为可能会发生存储面链路断链的云盘。

- 磁盘IO悬挂可对读写IO错误分别使能，同一磁盘的读写IO错误重试间隔和超时时间使用相同配置。

- 磁盘IO悬挂重试间隔不包含主机侧实际读写IO的开销，即两次IO重试操作实际间隔会大于配置的IO错误重试间隔。

- 磁盘IO悬挂无法区分IO错误的具体类型（如存储断链、扇区坏道、预留冲突等），只要硬件返回IO错误，都会进行悬挂处理。

- 磁盘IO悬挂时，虚拟机内部IO不会返回，fdisk等访问磁盘的系统命令会卡住，虚拟机内部依赖该命令返回的业务也会一直卡住。

- 磁盘IO悬挂时，IO无法正常落盘，可能会导致虚拟机无法优雅关机，需要强制关机。

- 磁盘IO悬挂时，无法读取磁盘数据，会造成虚拟机无法正常重启，需要先将虚拟机强制关机，等待存储故障恢复后在重新启动虚拟机。

- 存储故障发生后，虽然存在磁盘IO悬挂，依然解决不了以下问题：

  1. 存储相关高级特性执行失败

     高级特性包括：虚拟磁盘热插、虚拟磁盘热拔、创建虚拟磁盘、虚拟机启动、虚拟机关机、虚拟机强制关机、虚拟机休眠、虚拟机唤醒、虚拟机存储热迁移、虚拟机存储热迁移取消、虚拟机创建存储快照、虚拟机存储快照合并、查询虚拟机磁盘容量、磁盘在线扩容、插入虚拟光驱、弹出虚拟机光驱。

  2. 虚拟机生命周期执行失败

- 配置了磁盘IO悬挂的虚拟机发起热迁移时，应该在目的端磁盘的XML配置中带上与源端相同的磁盘IO悬挂配置。

### 磁盘IO悬挂配置

#### Qemu命令行配置

磁盘IO悬挂功能通过在虚拟磁盘设备上指定`werror=retry` `rerror=retry`进行使能，使用`retry_interval`和`retry_timeout`进行重试策略的配置。`retry_interval`为IO错误重试的间隔，配置范围为0-MAX_LONG，单位为毫秒，未配置时使用缺省值1000ms；`retry_timeout`为IO错误重试超时时间，配置范围为0-MAX_LONG，0值表示不会发生超时，单位为毫秒，未配置时使用缺省值0。

virtio-blk磁盘的磁盘IO悬挂配置如下：

    -drive file=/path/to/your/storage,format=raw,if=none,id=drive-virtio-disk0,cache=none,aio=native \
    -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,\
    drive=drive-virtio-disk0,id=virtio-disk0,write-cache=on,\
    werror=retry,rerror=retry,retry_interval=2000,retry_timeout=10000

virtio-scsi磁盘的磁盘IO悬挂配置如下：

    -drive file=/path/to/your/storage,format=raw,if=none,id=drive-scsi0-0-0-0,cache=none,aio=native \
    -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
    device_id=drive-scsi0-0-0-0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,write-cache=on,\
    werror=retry,rerror=retry,retry_interval=2000,retry_timeout=10000

#### xml配置方式

磁盘IO悬挂功能通过在磁盘xml配置中指定`error_policy='retry'` `rerror_policy='retry'`进行使能。主要是配置上`retry_interval`和`retry_timeout`的值。`retry_interval`为IO错误重试的间隔，配置范围为0-MAX_LONG，单位为毫秒，未配置时使用缺省值1000ms；`retry_timeout`为IO错误重试超时时间，配置范围为0-MAX_LONG，0值表示不会发生超时，单位为毫秒，未配置时使用缺省值0。

virtio-blk磁盘的磁盘IO悬挂xml配置如下：

    <disk type='block' device='disk'>
      <driver name='qemu' type='raw' cache='none' io='native' error_policy='retry' rerror_policy='retry' retry_interval='2000' retry_timeout='10000'/>
      <source dev='/path/to/your/storage'/>
      <target dev='vdb' bus='virtio'/>
      <backingStore/>
    </disk>

virtio-scsi磁盘的磁盘IO悬挂xml配置如下：

    <disk type='block' device='disk'>
      <driver name='qemu' type='raw' cache='none' io='native' error_policy='retry' rerror_policy='retry' retry_interval='2000' retry_timeout='10000'/>
      <source dev='/path/to/your/storage'/>
      <target dev='sdb' bus='scsi'/>
      <backingStore/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>