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." } }
6.2.2. 检查默认验证规则
在创建自定义规则前,您必须检查 Validation
服务的默认规则,以确保您不会创建重新定义现有默认值的规则。
示例:如果默认规则包含行 default valid_input = false
,并且您创建一个包含行 default valid_input = true
,则 Validation
服务将不会启动。
流程
连接到
Validation
pod 的终端:$ oc rsh <validation_pod>
进入您的供应商的 OPA 策略目录:
$ cd /usr/share/opa/policies/io/konveyor/forklift/<provider> 1
- 1
- 指定
vmware
或ovirt
。
搜索默认策略:
$ grep -R "default" *
6.2.3. 检索库存服务 JSON
您可以通过向虚拟机发送一个 Inventory
服务查询来获取 Inventory
服务 JSON。输出中包含一个 "input"
键,其中包含由 Validation
服务规则查询的清单属性。
您可以根据 "input"
键中的任何属性(如 input.snapshot.kind
)创建验证规则。
流程
为项目检索路由:
oc get route -n openshift-mtv
检索
Inventory
服务路由:$ oc get route <inventory_service> -n openshift-mtv
检索访问令牌:
$ TOKEN=$(oc whoami -t)
触发 HTTP GET 请求(例如,使用 Curl):
$ curl -H "Authorization: Bearer $TOKEN" https://<inventory_service_route>/providers -k
检索供应商的
UUID
:$ curl -H "Authorization: Bearer $TOKEN" https://<inventory_service_route>/providers/<provider> -k 1
- 1
- 供应商允许的值有
vsphere
和ovirt
。
检索供应商的虚拟机:
$ curl -H "Authorization: Bearer $TOKEN" https://<inventory_service_route>/providers/<provider>/<UUID>/vms -k
检索虚拟机的详情:
$ 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`
流程
根据以下示例创建配置映射 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
通过将
forklift-controller
部署扩展到0
来停止Validation
pod:$ oc scale -n openshift-mtv --replicas=0 deployment/forklift-controller
通过将
forklift-controller
部署扩展到1
来启动Validation
pod:$ oc scale -n openshift-mtv --replicas=1 deployment/forklift-controller
检查
Validation
pod 日志,以验证 pod 是否已启动:$ oc logs -f <validation_pod>
如果自定义规则与默认规则冲突,则
Validation
pod 将不会启动。删除源供应商:
$ oc delete provider <provider> -n openshift-mtv
添加源供应商以应用新规则:
$ 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
您必须在创建自定义规则后更新规则版本,以便 Inventory
服务检测到更改并验证虚拟机。
6.2.5. 更新清单规则版本
每次更新规则时,您必须更新 inventory 规则版本,以便 Provider Inventory
服务检测到更改并触发 Validation
服务。
规则版本记录在每个供应商的 rules_version.rego
文件中。
流程
检索当前的规则版本:
$ GET https://forklift-validation/v1/data/io/konveyor/forklift/<provider>/rules_version 1
输出示例
{ "result": { "rules_version": 5 } }
连接到
Validation
pod 的终端:$ oc rsh <validation_pod>
-
更新
/usr/share/opa/policies/io/konveyor/forklift/<provider>/rules_version.rego
文件中的规则版本。 -
从
Validation
pod 终端注销。 验证更新的规则版本:
$ GET https://forklift-validation/v1/data/io/konveyor/forklift/<provider>/rules_version 1
输出示例
{ "result": { "rules_version": 6 } }