6.2. 为 Validation 服务创建自定义规则

Validation 服务使用 Open Policy Agent(OPA)策略规则来检查要迁移的每个虚拟机(VM)的适用性。Validation 服务为每个虚拟机生成 问题 列表,它们作为虚拟机属性存储在 Provider Inventory 服务中。Web 控制台显示供应商清单中的每个虚拟机的顾虑。

您可以创建自定义规则来扩展 Validation 服务的默认规则集。例如,您可以创建一个规则来检查虚拟机是否有多个磁盘。

6.2.1. 关于 Rego 文件

验证规则使用 Rego 编写,即 Open Policy Agent (OPA) 原生查询语言。规则作为 .rego 文件存储在 Validation pod 的 /usr/share/opa/policies/io/konveyor/forklift/<provider> 目录中。

每个验证规则都在单独的 .rego 文件中定义,以及对特定条件的测试。如果条件评估为 true,则该规则会将 {"category", "label", "assessment"} hash 添加到 concerns 中。concerns 内容将添加到虚拟机清单记录中的 concerns 键中。Web 控制台显示供应商清单中每个虚拟机的 concerns 键的内容。

以下 .rego 文件示例检查在 VMware 虚拟机的集群中启用了分布式资源调度:

drs_enabled.rego 示例

package io.konveyor.forklift.vmware 1

has_drs_enabled {
    input.host.cluster.drsEnabled 2
}

concerns[flag] {
    has_drs_enabled
    flag := {
        "category": "Information",
        "label": "VM running in a DRS-enabled cluster",
        "assessment": "Distributed resource scheduling is not currently supported by OpenShift Virtualization. The VM can be migrated but it will not have this feature in the target environment."
    }
}

1
每个验证规则都在软件包中定义。软件包命名空间是 io.konveyor.forklift.vmware(VMware)和 io.konveyor.forklift.ovirt(Red Hat Virtualization)。
2
查询参数基于 Validation 服务 JSON 的 input 键。

6.2.2. 检查默认验证规则

在创建自定义规则前,您必须检查 Validation 服务的默认规则,以确保您不会创建重新定义现有默认值的规则。

示例:如果默认规则包含行 default valid_input = false,并且您创建一个包含行 default valid_input = true,则 Validation 服务将不会启动。

流程

  1. 连接到 Validation pod 的终端:

    $ oc rsh <validation_pod>
  2. 进入您的供应商的 OPA 策略目录:

    $ cd /usr/share/opa/policies/io/konveyor/forklift/<provider> 1
    1
    指定 vmwareovirt
  3. 搜索默认策略:

    $ grep -R "default" *

6.2.3. 检索库存服务 JSON

您可以通过向虚拟机发送一个 Inventory 服务查询来获取 Inventory 服务 JSON。输出中包含一个 "input" 键,其中包含由 Validation 服务规则查询的清单属性。

您可以根据 "input" 键中的任何属性(如 input.snapshot.kind )创建验证规则。

流程

  1. 为项目检索路由:

    oc get route -n openshift-mtv
  2. 检索 Inventory 服务路由:

    $ oc get route <inventory_service> -n openshift-mtv
  3. 检索访问令牌:

    $ TOKEN=$(oc whoami -t)
  4. 触发 HTTP GET 请求(例如,使用 Curl):

    $ curl -H "Authorization: Bearer $TOKEN" https://<inventory_service_route>/providers -k
  5. 检索供应商的 UUID

    $ curl -H "Authorization: Bearer $TOKEN"  https://<inventory_service_route>/providers/<provider> -k 1
    1
    供应商允许的值有 vsphereovirt
  6. 检索供应商的虚拟机:

    $ curl -H "Authorization: Bearer $TOKEN"  https://<inventory_service_route>/providers/<provider>/<UUID>/vms -k
  7. 检索虚拟机的详情:

    $ curl -H "Authorization: Bearer $TOKEN"  https://<inventory_service_route>/providers/<provider>/<UUID>/workloads/<vm> -k

    输出示例

    {
        "input": {
            "selfLink": "providers/vsphere/c872d364-d62b-46f0-bd42-16799f40324e/workloads/vm-431",
            "id": "vm-431",
            "parent": {
                "kind": "Folder",
                "id": "group-v22"
            },
            "revision": 1,
            "name": "iscsi-target",
            "revisionValidated": 1,
            "isTemplate": false,
            "networks": [
                {
                    "kind": "Network",
                    "id": "network-31"
                },
                {
                    "kind": "Network",
                    "id": "network-33"
                }
            ],
            "disks": [
                {
                    "key": 2000,
                    "file": "[iSCSI_Datastore] iscsi-target/iscsi-target-000001.vmdk",
                    "datastore": {
                        "kind": "Datastore",
                        "id": "datastore-63"
                    },
                    "capacity": 17179869184,
                    "shared": false,
                    "rdm": false
                },
                {
                    "key": 2001,
                    "file": "[iSCSI_Datastore] iscsi-target/iscsi-target_1-000001.vmdk",
                    "datastore": {
                        "kind": "Datastore",
                        "id": "datastore-63"
                    },
                    "capacity": 10737418240,
                    "shared": false,
                    "rdm": false
                }
            ],
            "concerns": [],
            "policyVersion": 5,
            "uuid": "42256329-8c3a-2a82-54fd-01d845a8bf49",
            "firmware": "bios",
            "powerState": "poweredOn",
            "connectionState": "connected",
            "snapshot": {
                "kind": "VirtualMachineSnapshot",
                "id": "snapshot-3034"
            },
            "changeTrackingEnabled": false,
            "cpuAffinity": [
                0,
                2
            ],
            "cpuHotAddEnabled": true,
            "cpuHotRemoveEnabled": false,
            "memoryHotAddEnabled": false,
            "faultToleranceEnabled": false,
            "cpuCount": 2,
            "coresPerSocket": 1,
            "memoryMB": 2048,
            "guestName": "Red Hat Enterprise Linux 7 (64-bit)",
            "balloonedMemory": 0,
            "ipAddress": "10.19.2.96",
            "storageUsed": 30436770129,
            "numaNodeAffinity": [
                "0",
                "1"
            ],
            "devices": [
                {
                    "kind": "RealUSBController"
                }
            ],
            "host": {
                "id": "host-29",
                "parent": {
                    "kind": "Cluster",
                    "id": "domain-c26"
                },
                "revision": 1,
                "name": "IP address or host name of the vCenter host or RHV Engine host",
                "selfLink": "providers/vsphere/c872d364-d62b-46f0-bd42-16799f40324e/hosts/host-29",
                "status": "green",
                "inMaintenance": false,
                "managementServerIp": "10.19.2.96",
                "thumbprint": <thumbprint>,
                "timezone": "UTC",
                "cpuSockets": 2,
                "cpuCores": 16,
                "productName": "VMware ESXi",
                "productVersion": "6.5.0",
                "networking": {
                    "pNICs": [
                        {
                            "key": "key-vim.host.PhysicalNic-vmnic0",
                            "linkSpeed": 10000
                        },
                        {
                            "key": "key-vim.host.PhysicalNic-vmnic1",
                            "linkSpeed": 10000
                        },
                        {
                            "key": "key-vim.host.PhysicalNic-vmnic2",
                            "linkSpeed": 10000
                        },
                        {
                            "key": "key-vim.host.PhysicalNic-vmnic3",
                            "linkSpeed": 10000
                        }
                    ],
                    "vNICs": [
                        {
                            "key": "key-vim.host.VirtualNic-vmk2",
                            "portGroup": "VM_Migration",
                            "dPortGroup": "",
                            "ipAddress": "192.168.79.13",
                            "subnetMask": "255.255.255.0",
                            "mtu": 9000
                        },
                        {
                            "key": "key-vim.host.VirtualNic-vmk0",
                            "portGroup": "Management Network",
                            "dPortGroup": "",
                            "ipAddress": "10.19.2.13",
                            "subnetMask": "255.255.255.128",
                            "mtu": 1500
                        },
                        {
                            "key": "key-vim.host.VirtualNic-vmk1",
                            "portGroup": "Storage Network",
                            "dPortGroup": "",
                            "ipAddress": "172.31.2.13",
                            "subnetMask": "255.255.0.0",
                            "mtu": 1500
                        },
                        {
                            "key": "key-vim.host.VirtualNic-vmk3",
                            "portGroup": "",
                            "dPortGroup": "dvportgroup-48",
                            "ipAddress": "192.168.61.13",
                            "subnetMask": "255.255.255.0",
                            "mtu": 1500
                        },
                        {
                            "key": "key-vim.host.VirtualNic-vmk4",
                            "portGroup": "VM_DHCP_Network",
                            "dPortGroup": "",
                            "ipAddress": "10.19.2.231",
                            "subnetMask": "255.255.255.128",
                            "mtu": 1500
                        }
                    ],
                    "portGroups": [
                        {
                            "key": "key-vim.host.PortGroup-VM Network",
                            "name": "VM Network",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch0"
                        },
                        {
                            "key": "key-vim.host.PortGroup-Management Network",
                            "name": "Management Network",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch0"
                        },
                        {
                            "key": "key-vim.host.PortGroup-VM_10G_Network",
                            "name": "VM_10G_Network",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch1"
                        },
                        {
                            "key": "key-vim.host.PortGroup-VM_Storage",
                            "name": "VM_Storage",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch1"
                        },
                        {
                            "key": "key-vim.host.PortGroup-VM_DHCP_Network",
                            "name": "VM_DHCP_Network",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch1"
                        },
                        {
                            "key": "key-vim.host.PortGroup-Storage Network",
                            "name": "Storage Network",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch1"
                        },
                        {
                            "key": "key-vim.host.PortGroup-VM_Isolated_67",
                            "name": "VM_Isolated_67",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch2"
                        },
                        {
                            "key": "key-vim.host.PortGroup-VM_Migration",
                            "name": "VM_Migration",
                            "vSwitch": "key-vim.host.VirtualSwitch-vSwitch2"
                        }
                    ],
                    "switches": [
                        {
                            "key": "key-vim.host.VirtualSwitch-vSwitch0",
                            "name": "vSwitch0",
                            "portGroups": [
                                "key-vim.host.PortGroup-VM Network",
                                "key-vim.host.PortGroup-Management Network"
                            ],
                            "pNICs": [
                                "key-vim.host.PhysicalNic-vmnic4"
                            ]
                        },
                        {
                            "key": "key-vim.host.VirtualSwitch-vSwitch1",
                            "name": "vSwitch1",
                            "portGroups": [
                                "key-vim.host.PortGroup-VM_10G_Network",
                                "key-vim.host.PortGroup-VM_Storage",
                                "key-vim.host.PortGroup-VM_DHCP_Network",
                                "key-vim.host.PortGroup-Storage Network"
                            ],
                            "pNICs": [
                                "key-vim.host.PhysicalNic-vmnic2",
                                "key-vim.host.PhysicalNic-vmnic0"
                            ]
                        },
                        {
                            "key": "key-vim.host.VirtualSwitch-vSwitch2",
                            "name": "vSwitch2",
                            "portGroups": [
                                "key-vim.host.PortGroup-VM_Isolated_67",
                                "key-vim.host.PortGroup-VM_Migration"
                            ],
                            "pNICs": [
                                "key-vim.host.PhysicalNic-vmnic3",
                                "key-vim.host.PhysicalNic-vmnic1"
                            ]
                        }
                    ]
                },
                "networks": [
                    {
                        "kind": "Network",
                        "id": "network-31"
                    },
                    {
                        "kind": "Network",
                        "id": "network-34"
                    },
                    {
                        "kind": "Network",
                        "id": "network-57"
                    },
                    {
                        "kind": "Network",
                        "id": "network-33"
                    },
                    {
                        "kind": "Network",
                        "id": "dvportgroup-47"
                    }
                ],
                "datastores": [
                    {
                        "kind": "Datastore",
                        "id": "datastore-35"
                    },
                    {
                        "kind": "Datastore",
                        "id": "datastore-63"
                    }
                ],
                "vms": null,
                "networkAdapters": [],
                "cluster": {
                    "id": "domain-c26",
                    "parent": {
                        "kind": "Folder",
                        "id": "group-h23"
                    },
                    "revision": 1,
                    "name": "mycluster",
                    "selfLink": "providers/vsphere/c872d364-d62b-46f0-bd42-16799f40324e/clusters/domain-c26",
                    "folder": "group-h23",
                    "networks": [
                        {
                            "kind": "Network",
                            "id": "network-31"
                        },
                        {
                            "kind": "Network",
                            "id": "network-34"
                        },
                        {
                            "kind": "Network",
                            "id": "network-57"
                        },
                        {
                            "kind": "Network",
                            "id": "network-33"
                        },
                        {
                            "kind": "Network",
                            "id": "dvportgroup-47"
                        }
                    ],
                    "datastores": [
                        {
                            "kind": "Datastore",
                            "id": "datastore-35"
                        },
                        {
                            "kind": "Datastore",
                            "id": "datastore-63"
                        }
                    ],
                    "hosts": [
                        {
                            "kind": "Host",
                            "id": "host-44"
                        },
                        {
                            "kind": "Host",
                            "id": "host-29"
                        }
                    ],
                    "dasEnabled": false,
                    "dasVms": [],
                    "drsEnabled": true,
                    "drsBehavior": "fullyAutomated",
                    "drsVms": [],
                    "datacenter": null
                }
            }
        }
    }

6.2.4. 创建验证规则

您可以通过将包含规则的配置映射自定义资源(CR)应用到 Validation 服务来创建验证规则。

重要
  • 如果您创建与现有规则相同的规则,Validation 服务将使用规则执行 OR 操作。
  • 如果您创建使用默认规则迭代的规则,则 Validation 服务将不会启动。

验证规则示例

验证规则基于由 Provider Inventory 服务收集的虚拟机(VM)属性。

例如,VMware API 使用此路径来检查 VMware 虚拟机是否配置了 NUMA 节点关联性:MOR:VirtualMachine.config.extraConfig["numa.nodeAffinity"]

Provider Inventory 服务使用列表值简化了此配置并返回一个可测试属性:

"numaNodeAffinity": [
    "0",
    "1"
],

您可以根据此属性创建一个 Rego 查询,并将其添加到 forklift-validation-config 配置映射中:

`count(input.numaNodeAffinity) != 0`

流程

  1. 根据以下示例创建配置映射 CR:

    $ cat << EOF | oc apply -f -
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: <forklift-validation-config>
      namespace: openshift-mtv
    data:
      vmware_multiple_disks.rego: |-
        package <provider_package> 1
    
        has_multiple_disks { 2
          count(input.disks) > 1
        }
    
        concerns[flag] {
          has_multiple_disks 3
            flag := {
              "category": "<Information>", 4
              "label": "Multiple disks detected",
              "assessment": "Multiple disks detected on this VM."
            }
        }
    EOF
    1
    指定供应商软件包名称。允许的值是 VMware 的 io.konveyor.forklift.vmwareio.konveyor.forklift.ovirt
    2
    指定 concerns 名称和 Rego 查询。
    3
    指定 concerns 名称和 flag 参数值。
    4
    允许的值是 Critical, Warning, 和 Information
  2. 通过将 forklift-controller 部署扩展到 0 来停止 Validation pod:

    $ oc scale -n openshift-mtv --replicas=0 deployment/forklift-controller
  3. 通过将 forklift-controller 部署扩展到 1 来启动 Validation pod:

    $ oc scale -n openshift-mtv --replicas=1 deployment/forklift-controller
  4. 检查 Validation pod 日志,以验证 pod 是否已启动:

    $ oc logs -f <validation_pod>

    如果自定义规则与默认规则冲突,则 Validation pod 将不会启动。

  5. 删除源供应商:

    $ oc delete provider <provider> -n openshift-mtv
  6. 添加源供应商以应用新规则:

    $ cat << EOF | oc apply -f -
    apiVersion: forklift.konveyor.io/v1beta1
    kind: Provider
    metadata:
      name: <provider>
      namespace: openshift-mtv
    spec:
      type: <provider_type> 1
      url: <api_end_point> 2
      secret:
        name: <secret> 3
        namespace: openshift-mtv
    EOF
    1
    允许的值是 ovirtvsphere
    2
    指定 API 端点 URL。例如 https://<vCenter_host>/sdk 用于 vSphere,或 https://<engine_host>/ovirt-engine/api/ 用于 RHV。
    3
    指定供应商 Secret CR 的名称。

您必须在创建自定义规则后更新规则版本,以便 Inventory 服务检测到更改并验证虚拟机。

6.2.5. 更新清单规则版本

每次更新规则时,您必须更新 inventory 规则版本,以便 Provider Inventory 服务检测到更改并触发 Validation 服务。

规则版本记录在每个供应商的 rules_version.rego 文件中。

流程

  1. 检索当前的规则版本:

    $ GET https://forklift-validation/v1/data/io/konveyor/forklift/<provider>/rules_version 1

    输出示例

    {
       "result": {
           "rules_version": 5
       }
    }

  2. 连接到 Validation pod 的终端:

    $ oc rsh <validation_pod>
  3. 更新 /usr/share/opa/policies/io/konveyor/forklift/<provider>/rules_version.rego 文件中的规则版本。
  4. Validation pod 终端注销。
  5. 验证更新的规则版本:

    $ GET https://forklift-validation/v1/data/io/konveyor/forklift/<provider>/rules_version 1

    输出示例

    {
       "result": {
           "rules_version": 6
       }
    }