Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
```

- CDI and a default StorageClass, for migrations that import disks. Neither is needed to install Forklift.
The StorageClass backing the migrated disks must support the file locking `qemu-img` takes on an image it opens.
A volume that cannot honor those locks fails the migration before conversion starts.
NFS-backed storage needs mount options that permit locking, such as `nfsvers=3,nolock` or NFSv4, set on the StorageClass rather than only on the export.
Comment on lines +46 to +48

## How to

Expand Down Expand Up @@ -96,20 +99,31 @@

Put the profiles on every node that can run a conversion. Use the Security Profiles Operator if you already run it; otherwise you may apply the ConfigMap and DaemonSet patch below, after inspection.

**Note** This is a privileged DaemonSet. It writes both profiles under the kubelet's seccomp directory, and loads the AppArmor one on nodes that have `apparmor_parser`. Its log says which it did on each node:

**Note** This is a privileged DaemonSet. It writes both profiles under the kubelet's seccomp directory, and loads the AppArmor one on nodes that have `apparmor_parser`.

Check failure on line 102 in calico-enterprise/networking/kubevirt/install-forklift-kubernetes.mdx

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'seccomp'? Raw Output: {"message":"[Vale.Spelling] Did you really mean 'seccomp'?","location":{"path":"calico-enterprise/networking/kubevirt/install-forklift-kubernetes.mdx","range":{"start":{"line":102,"column":86},"end":{"line":102,"column":93}}},"severity":"ERROR","code":{"value":"Vale.Spelling"}}

```bash
kubectl apply -f $[filesUrl]/forklift/forklift-virt-v2v-profiles.yaml

kubectl -n konveyor-forklift rollout status ds/forklift-virt-v2v-profile-installer
```

Each pod reports what it did on its own node, and a single `kubectl logs` across the DaemonSet loses that attribution, so read the logs one pod at a time:

```bash
kubectl -n konveyor-forklift logs -l service=forklift-virt-v2v-profile-installer -c install
for pod in $(kubectl -n konveyor-forklift get pods \
-l service=forklift-virt-v2v-profile-installer -o name); do
echo "== $(kubectl -n konveyor-forklift get $pod -o jsonpath='{.spec.nodeName}')"
kubectl -n konveyor-forklift logs $pod -c install
done
```

Then name the profiles on the `ForkliftController`:
A node that enforces AppArmor reports `loaded AppArmor profile`.
One that does not reports `no apparmor_parser on this node, skipping AppArmor`.
What you see decides which fields to set next.

Then name the profiles on the `ForkliftController`.
The patch below sets all four fields, which is what a cluster of AppArmor-enforcing nodes needs.
Drop the two `apparmor` lines if no node enforces it:

```bash
kubectl -n konveyor-forklift patch forkliftcontroller forklift-controller \
Expand All @@ -120,7 +134,22 @@
"virt_v2v_apparmor_profile_path": "forklift-virt-v2v-unshare"}}'
```

All four fields are unset by default, which leaves every cluster on its runtime default: naming a profile that a node does not carry makes the kubelet refuse the pod outright. A path given without its type is rejected as the CR is saved. On nodes that do not enforce AppArmor, set only the seccomp pair.
All four fields are unset by default, which leaves every cluster on its runtime default.
A path given without its type is rejected as the CR is saved.

The fields belong to the one `ForkliftController`, so they apply to every conversion pod in the cluster.
There is no per-node and no per-`Plan` override, which makes the AppArmor pair a property of the cluster rather than of a node:

| Nodes that can run a conversion | Set |
| --- | --- |
| All enforce AppArmor | All four fields |
| None enforce AppArmor | The seccomp pair only |

Check failure on line 146 in calico-enterprise/networking/kubevirt/install-forklift-kubernetes.mdx

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'seccomp'? Raw Output: {"message":"[Vale.Spelling] Did you really mean 'seccomp'?","location":{"path":"calico-enterprise/networking/kubevirt/install-forklift-kubernetes.mdx","range":{"start":{"line":146,"column":31},"end":{"line":146,"column":38}}},"severity":"ERROR","code":{"value":"Vale.Spelling"}}

A cluster whose conversion-capable nodes are mixed has no valid setting.
Name the AppArmor profile and the kubelet refuses the conversion pod outright wherever AppArmor is absent, reporting `Cannot enforce AppArmor: AppArmor is not enabled on the host`.
Omit it and the pod is admitted on an AppArmor node, where the remount then fails.

If your nodes are mixed, confine conversions to a uniform set of them with `spec.convertorNodeSelector` on the `Plan`, and set these fields to match the set you chose.

### 5. Map a VM's networks to $[prodname]

Expand All @@ -137,10 +166,24 @@
To additionally force the primary NIC to get attached to a pre-configured Calico `Network`, the struct has
fields `network` and `vlan` which target the Calico `Network` to use.

`calico` is a property of a map item's `destination`, alongside `type`, and is valid only on an item of `type: pod`.
At most one item in a `NetworkMap` may carry it.

```yaml
map:
- source:
id: <pod-network-portgroup-id>
destination:
type: pod
calico:
network: mock-l2-net # cluster-scoped projectcalico.org/v3 Network CR
vlan: 100 # must match a vlan.id entry in that Network
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```

| Field | Effect |
Expand All @@ -152,28 +195,30 @@

Note: The MAC is always carried over; the addresses follow when the `Plan` sets `preserveStaticIPs`. Definitions that use another CNI are left untouched. The values are set before the VM starts, so each interface is created already holding them.

```yaml
map:
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```


### Verify address preservation worked

Inspect the `virt-launcher` pod, or the VM template created post-migration.
It should contain annotations for Calico address preservation, for each NIC
in the NetworkMap:
Each NIC in the `NetworkMap` carries a pair of annotations, and which keys to look for depends on the NIC.

The primary NIC, mapped by an item of `type: pod` with a `calico` block, uses keys with no interface name:

```yaml
cni.projectcalico.org/<interface>.hwAddr: ...
cni.projectcalico.org/<interface>.ipAddrs: ...
cni.projectcalico.org/hwAddr: '52:54:00:3a:1c:04'
cni.projectcalico.org/ipAddrs: '["10.10.0.42"]'
```

A secondary NIC, mapped by an item of `type: multus`, scopes each key to the interface it applies to:

```yaml
cni.projectcalico.org/<interface>.hwAddr: '52:54:00:3a:1c:05'
cni.projectcalico.org/<interface>.ipAddrs: '["10.10.10.42"]'
```

For the name to use in place of `<interface>`, see [Which interface name to use](../l2-bridge/vm-identity.mdx#which-interface-name-to-use).

The MAC is always stamped; `ipAddrs` appears only when the `Plan` sets `preserveStaticIPs`.
$[prodname] adds `cni.projectcalico.org/podIP` and `podIPs` once the interface is up, which is what confirms the requested address was assigned.


See [Connect workloads to an existing VLAN](../l2-bridge/connect-vlan.mdx) for the `Network`, IP pool and attachment definition, and [Set a VM's IP and MAC address](../l2-bridge/vm-identity.mdx) for the annotations themselves.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Install the Tigera fork of Forklift on an OpenShift cluster and connect it to a
- The BPF data plane enabled: `bpfEnabled: true` in the default `FelixConfiguration`. See [Enable eBPF on an existing cluster](../../operations/ebpf/enabling-ebpf.mdx).
- OpenShift Virtualization (KubeVirt and CDI) installed, with `HyperConverged` Available.
- A StorageClass for the migrated VM disks.
It must support the file locking `qemu-img` takes on an image it opens: a volume that cannot honor those locks fails the migration before conversion starts.
NFS-backed storage needs mount options that permit locking, such as `nfsvers=3,nolock` or NFSv4, set on the StorageClass rather than only on the export.
Comment on lines 29 to +31
- IP pools covering the VM addresses. The pod network uses an ordinary pool; each L2 VLAN needs its own pool with `allowedUses: [L2Workload]` and `disableBGPExport: true`. A VM keeps its address only if that address falls inside a pool. See [Connect workloads to an existing VLAN](../l2-bridge/connect-vlan.mdx).

## How to
Expand Down Expand Up @@ -122,10 +124,24 @@ can be included to flag that the primary network should also receive address-pre
To additionally force the primary NIC to get attached to a pre-configured Calico `Network`, the struct has
fields `network` and `vlan` which target the Calico `Network` to use.

`calico` is a property of a map item's `destination`, alongside `type`, and is valid only on an item of `type: pod`.
At most one item in a `NetworkMap` may carry it.

```yaml
map:
- source:
id: <pod-network-portgroup-id>
destination:
type: pod
calico:
network: mock-l2-net # cluster-scoped projectcalico.org/v3 Network CR
vlan: 100 # must match a vlan.id entry in that Network
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```

| Field | Effect |
Expand All @@ -137,28 +153,30 @@ fields `network` and `vlan` which target the Calico `Network` to use.

Note: The MAC is always carried over; the addresses follow when the `Plan` sets `preserveStaticIPs`. Definitions that use another CNI are left untouched. The values are set before the VM starts, so each interface is created already holding them.

```yaml
map:
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```


### Verify address preservation worked

Inspect the `virt-launcher` pod, or the VM template created post-migration.
It should contain annotations for Calico address preservation, for each NIC
in the NetworkMap:
Each NIC in the `NetworkMap` carries a pair of annotations, and which keys to look for depends on the NIC.

The primary NIC, mapped by an item of `type: pod` with a `calico` block, uses keys with no interface name:

```yaml
cni.projectcalico.org/<interface>.hwAddr: ...
cni.projectcalico.org/<interface>.ipAddrs: ...
cni.projectcalico.org/hwAddr: '52:54:00:3a:1c:04'
cni.projectcalico.org/ipAddrs: '["10.10.0.42"]'
```

A secondary NIC, mapped by an item of `type: multus`, scopes each key to the interface it applies to:

```yaml
cni.projectcalico.org/<interface>.hwAddr: '52:54:00:3a:1c:05'
cni.projectcalico.org/<interface>.ipAddrs: '["10.10.10.42"]'
```

For the name to use in place of `<interface>`, see [Which interface name to use](../l2-bridge/vm-identity.mdx#which-interface-name-to-use).

The MAC is always stamped; `ipAddrs` appears only when the `Plan` sets `preserveStaticIPs`.
$[prodname] adds `cni.projectcalico.org/podIP` and `podIPs` once the interface is up, which is what confirms the requested address was assigned.


See [Connect workloads to an existing VLAN](../l2-bridge/connect-vlan.mdx) for the `Network`, IP pool and attachment definition, and [Set a VM's IP and MAC address](../l2-bridge/vm-identity.mdx) for the annotations themselves.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
```

- CDI and a default StorageClass, for migrations that import disks. Neither is needed to install Forklift.
The StorageClass backing the migrated disks must support the file locking `qemu-img` takes on an image it opens.
A volume that cannot honor those locks fails the migration before conversion starts.
NFS-backed storage needs mount options that permit locking, such as `nfsvers=3,nolock` or NFSv4, set on the StorageClass rather than only on the export.
Comment on lines +46 to +48

## How to

Expand Down Expand Up @@ -96,20 +99,31 @@

Put the profiles on every node that can run a conversion. Use the Security Profiles Operator if you already run it; otherwise you may apply the ConfigMap and DaemonSet patch below, after inspection.

**Note** This is a privileged DaemonSet. It writes both profiles under the kubelet's seccomp directory, and loads the AppArmor one on nodes that have `apparmor_parser`. Its log says which it did on each node:

**Note** This is a privileged DaemonSet. It writes both profiles under the kubelet's seccomp directory, and loads the AppArmor one on nodes that have `apparmor_parser`.

Check failure on line 102 in calico-enterprise_versioned_docs/version-3.24-2/networking/kubevirt/install-forklift-kubernetes.mdx

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'seccomp'? Raw Output: {"message":"[Vale.Spelling] Did you really mean 'seccomp'?","location":{"path":"calico-enterprise_versioned_docs/version-3.24-2/networking/kubevirt/install-forklift-kubernetes.mdx","range":{"start":{"line":102,"column":86},"end":{"line":102,"column":93}}},"severity":"ERROR","code":{"value":"Vale.Spelling"}}

```bash
kubectl apply -f $[filesUrl]/forklift/forklift-virt-v2v-profiles.yaml

kubectl -n konveyor-forklift rollout status ds/forklift-virt-v2v-profile-installer
```

Each pod reports what it did on its own node, and a single `kubectl logs` across the DaemonSet loses that attribution, so read the logs one pod at a time:

```bash
kubectl -n konveyor-forklift logs -l service=forklift-virt-v2v-profile-installer -c install
for pod in $(kubectl -n konveyor-forklift get pods \
-l service=forklift-virt-v2v-profile-installer -o name); do
echo "== $(kubectl -n konveyor-forklift get $pod -o jsonpath='{.spec.nodeName}')"
kubectl -n konveyor-forklift logs $pod -c install
done
```

Then name the profiles on the `ForkliftController`:
A node that enforces AppArmor reports `loaded AppArmor profile`.
One that does not reports `no apparmor_parser on this node, skipping AppArmor`.
What you see decides which fields to set next.

Then name the profiles on the `ForkliftController`.
The patch below sets all four fields, which is what a cluster of AppArmor-enforcing nodes needs.
Drop the two `apparmor` lines if no node enforces it:

```bash
kubectl -n konveyor-forklift patch forkliftcontroller forklift-controller \
Expand All @@ -120,7 +134,22 @@
"virt_v2v_apparmor_profile_path": "forklift-virt-v2v-unshare"}}'
```

All four fields are unset by default, which leaves every cluster on its runtime default: naming a profile that a node does not carry makes the kubelet refuse the pod outright. A path given without its type is rejected as the CR is saved. On nodes that do not enforce AppArmor, set only the seccomp pair.
All four fields are unset by default, which leaves every cluster on its runtime default.
A path given without its type is rejected as the CR is saved.

The fields belong to the one `ForkliftController`, so they apply to every conversion pod in the cluster.
There is no per-node and no per-`Plan` override, which makes the AppArmor pair a property of the cluster rather than of a node:

| Nodes that can run a conversion | Set |
| --- | --- |
| All enforce AppArmor | All four fields |
| None enforce AppArmor | The seccomp pair only |

Check failure on line 146 in calico-enterprise_versioned_docs/version-3.24-2/networking/kubevirt/install-forklift-kubernetes.mdx

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'seccomp'? Raw Output: {"message":"[Vale.Spelling] Did you really mean 'seccomp'?","location":{"path":"calico-enterprise_versioned_docs/version-3.24-2/networking/kubevirt/install-forklift-kubernetes.mdx","range":{"start":{"line":146,"column":31},"end":{"line":146,"column":38}}},"severity":"ERROR","code":{"value":"Vale.Spelling"}}

A cluster whose conversion-capable nodes are mixed has no valid setting.
Name the AppArmor profile and the kubelet refuses the conversion pod outright wherever AppArmor is absent, reporting `Cannot enforce AppArmor: AppArmor is not enabled on the host`.
Omit it and the pod is admitted on an AppArmor node, where the remount then fails.

If your nodes are mixed, confine conversions to a uniform set of them with `spec.convertorNodeSelector` on the `Plan`, and set these fields to match the set you chose.

### 5. Map a VM's networks to $[prodname]

Expand All @@ -137,10 +166,24 @@
To additionally force the primary NIC to get attached to a pre-configured Calico `Network`, the struct has
fields `network` and `vlan` which target the Calico `Network` to use.

`calico` is a property of a map item's `destination`, alongside `type`, and is valid only on an item of `type: pod`.
At most one item in a `NetworkMap` may carry it.

```yaml
map:
- source:
id: <pod-network-portgroup-id>
destination:
type: pod
calico:
network: mock-l2-net # cluster-scoped projectcalico.org/v3 Network CR
vlan: 100 # must match a vlan.id entry in that Network
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```

| Field | Effect |
Expand All @@ -152,28 +195,30 @@

Note: The MAC is always carried over; the addresses follow when the `Plan` sets `preserveStaticIPs`. Definitions that use another CNI are left untouched. The values are set before the VM starts, so each interface is created already holding them.

```yaml
map:
- source:
id: <secondary-portgroup-id>
destination:
type: multus
name: my-network-vlan10
namespace: vms
```


### Verify address preservation worked

Inspect the `virt-launcher` pod, or the VM template created post-migration.
It should contain annotations for Calico address preservation, for each NIC
in the NetworkMap:
Each NIC in the `NetworkMap` carries a pair of annotations, and which keys to look for depends on the NIC.

The primary NIC, mapped by an item of `type: pod` with a `calico` block, uses keys with no interface name:

```yaml
cni.projectcalico.org/<interface>.hwAddr: ...
cni.projectcalico.org/<interface>.ipAddrs: ...
cni.projectcalico.org/hwAddr: '52:54:00:3a:1c:04'
cni.projectcalico.org/ipAddrs: '["10.10.0.42"]'
```

A secondary NIC, mapped by an item of `type: multus`, scopes each key to the interface it applies to:

```yaml
cni.projectcalico.org/<interface>.hwAddr: '52:54:00:3a:1c:05'
cni.projectcalico.org/<interface>.ipAddrs: '["10.10.10.42"]'
```

For the name to use in place of `<interface>`, see [Which interface name to use](../l2-bridge/vm-identity.mdx#which-interface-name-to-use).

The MAC is always stamped; `ipAddrs` appears only when the `Plan` sets `preserveStaticIPs`.
$[prodname] adds `cni.projectcalico.org/podIP` and `podIPs` once the interface is up, which is what confirms the requested address was assigned.


See [Connect workloads to an existing VLAN](../l2-bridge/connect-vlan.mdx) for the `Network`, IP pool and attachment definition, and [Set a VM's IP and MAC address](../l2-bridge/vm-identity.mdx) for the annotations themselves.

Expand Down
Loading