:_mod-docs-content-type: PROCEDURE
[id="migrating-ovn-data_{context}"]

= Migrating OVN data

[role="_abstract"]
Migrate the data in the OVN databases from the original {rhos_prev_long} deployment to `ovsdb-server` instances that are running in the {rhocp_long} cluster.

.Prerequisites

* The `OpenStackControlPlane` resource is created.
* `NetworkAttachmentDefinition` custom resources (CRs) for the original cluster are defined. Specifically, the `internalapi` network is defined.
* The original {networking_first_ref} and OVN `northd` are not running.
* The cloud is migrated to the Modular Layer 2 plug-in with Open Virtual Networking (ML2/OVN) mechanism driver.
* SSH access is available to the original {rhos_prev_long} Controller nodes to allow direct access to database files.
* Define the following shell variables. Replace the example values with values that are correct for your environment:
+
----
ifeval::["{build}" != "downstream"]
STORAGE_CLASS_NAME=local-storage
OVSDB_IMAGE=quay.io/podified-antelope-centos9/openstack-ovn-base:current-podified
endif::[]
ifeval::["{build}" == "downstream"]
STORAGE_CLASS=local-storage
OVSDB_IMAGE=registry.redhat.io/rhoso/openstack-ovn-base-rhel9:18.0
endif::[]
----
+
ifeval::["{build_variant}" == "ospdo"]
[NOTE]
If you use a disconnected environment director Operator deployment, use
`OVSDB_IMAGE=registry.redhat.io/rhoso/openstack-ovn-base-rhel9@sha256:967046c6bdb8f55c236085b5c5f9667f0dbb9f3ac52a6560dd36a6bfac051e1f`.
For more information, see link:https://docs.redhat.com/en/documentation/red_hat_openstack_platform/17.1/html-single/deploying_an_overcloud_in_a_red_hat_openshift_container_platform_cluster_with_director_operator/index#proc_configuring-an-airgapped-environment_air-gapped-environment[Configuring an airgapped environment] in _Deploying an overcloud in a Red Hat OpenShift Container Platform cluster with director Operator_.
endif::[]
* Ensure that you have SSH access to the original {rhos_prev_long} Controller nodes to extract database files directly from the Controller file system.

.Procedure
ifeval::["{build_variant}" == "ospdo"]
. Get the {OpenShiftShort} master node that contains the {OpenStackShort} Controller node:
+
----
$ oc get vmi -n $<ospdo_namespace> -o jsonpath='{.items[0].metadata.labels.kubevirt\.io/nodeName}'
----
+
* Replace `<ospdo_namespace>` with your OSPdO namespace.
endif::[]
. Convert the OVN cluster databases to standalone format on the Controller node and copy them to the local system:
+
----
# Set up variables for the controller SSH connection
CONTROLLER1_SSH="ssh -i ~/.ssh/id_rsa heat-admin@$CONTROLLER_IP"

# Create working directory on controller and convert databases
$CONTROLLER1_SSH sudo mkdir -p /tmp/ovn_standalone_conversion
$CONTROLLER1_SSH sudo ovsdb-tool cluster-to-standalone /tmp/ovn_standalone_conversion/ovnnb_db.db /var/lib/openvswitch/ovn/ovnnb_db.db
$CONTROLLER1_SSH sudo ovsdb-tool cluster-to-standalone /tmp/ovn_standalone_conversion/ovnsb_db.db /var/lib/openvswitch/ovn/ovnsb_db.db
$CONTROLLER1_SSH sudo chmod 644 /tmp/ovn_standalone_conversion/ovn*.db

# Create local directory and copy converted databases
mkdir -p /tmp/ovn_adoption_dbs
CONTROLLER1_SCP=$(echo "$CONTROLLER1_SSH" | sed 's/^ssh/scp/')
${CONTROLLER1_SCP}:/tmp/ovn_standalone_conversion/ovnnb_db.db /tmp/ovn_adoption_dbs/
${CONTROLLER1_SCP}:/tmp/ovn_standalone_conversion/ovnsb_db.db /tmp/ovn_adoption_dbs/

# Cleanup on controller
$CONTROLLER1_SSH sudo rm -rf /tmp/ovn_standalone_conversion
----
+

. Create the `PersistentVolumeClaim` resources that will be used by the OVN database pods:
+
----
ifeval::["{build_variant}" != "ospdo"]
$ oc apply -f - <<EOF
endif::[]
ifeval::["{build_variant}" == "ospdo"]
$ oc apply -n $<ospdo_namespace> -f - <<EOF
endif::[]
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ovndbcluster-nb-etc-ovn-ovsdbserver-nb-0
  labels:
    service: ovsdbserver-nb
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: $STORAGE_CLASS
  resources:
    requests:
      storage: 10G
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ovndbcluster-sb-etc-ovn-ovsdbserver-sb-0
  labels:
    service: ovsdbserver-sb
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: $STORAGE_CLASS
  resources:
    requests:
      storage: 10G
EOF
----

. Create a helper pod to populate the PVCs with the converted databases:
+
----
ifeval::["{build_variant}" != "ospdo"]
$ oc apply -f - <<EOF
endif::[]
ifeval::["{build_variant}" == "ospdo"]
$ oc apply -n $<ospdo_namespace> -f - <<EOF
endif::[]
---
apiVersion: v1
kind: Pod
metadata:
  name: ovn-copy-data
  annotations:
    openshift.io/scc: anyuid
  labels:
    app: adoption
spec:
  containers:
  - image: $OVSDB_IMAGE
    command: [ "sh", "-c", "sleep infinity"]
    name: adoption
    volumeMounts:
    - mountPath: /nb-data
      name: nb-data
    - mountPath: /sb-data
      name: sb-data
  securityContext:
    allowPrivilegeEscalation: false
    capabilities:
      drop: ALL
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  volumes:
  - name: nb-data
    persistentVolumeClaim:
      claimName: ovndbcluster-nb-etc-ovn-ovsdbserver-nb-0
  - name: sb-data
    persistentVolumeClaim:
      claimName: ovndbcluster-sb-etc-ovn-ovsdbserver-sb-0
EOF
----

. Wait for the helper pod to be ready:
+
----
ifeval::["{build_variant}" != "ospdo"]
$ oc wait --for=condition=Ready pod/ovn-copy-data --timeout=30s
endif::[]
ifeval::["{build_variant}" == "ospdo"]
$ oc wait --for=condition=Ready pod/ovn-copy-data -n $<ospdo_namespace> --timeout=30s
endif::[]
----

. Copy the converted standalone databases to the PVCs:
+
----
ifeval::["{build_variant}" != "ospdo"]
$ oc cp /tmp/ovn_adoption_dbs/ovnnb_db.db ovn-copy-data:/nb-data/ovnnb_db.db
$ oc cp /tmp/ovn_adoption_dbs/ovnsb_db.db ovn-copy-data:/sb-data/ovnsb_db.db
endif::[]
ifeval::["{build_variant}" == "ospdo"]
$ oc cp /tmp/ovn_adoption_dbs/ovnnb_db.db ovn-copy-data:/nb-data/ovnnb_db.db -n $<ospdo_namespace>
$ oc cp /tmp/ovn_adoption_dbs/ovnsb_db.db ovn-copy-data:/sb-data/ovnsb_db.db -n $<ospdo_namespace>
endif::[]
----

. Remove the helper pod now that the data is in the PVCs:
+
----
ifeval::["{build_variant}" != "ospdo"]
$ oc delete pod ovn-copy-data
endif::[]
ifeval::["{build_variant}" == "ospdo"]
$ oc delete pod ovn-copy-data -n $<ospdo_namespace>
endif::[]
----

. Clean up the local temporary files:
+
----
$ rm -f /tmp/ovn_adoption_dbs/ovn*.db
$ rmdir /tmp/ovn_adoption_dbs
----

. Start the control plane OVN database services. The databases will automatically load the converted data that was populated in the PVCs:
+
----
$ oc patch openstackcontrolplane openstack --type=merge --patch '
spec:
  ovn:
    enabled: true
    template:
      ovnDBCluster:
        ovndbcluster-nb:
          replicas: 3
          dbType: NB
          storageRequest: 10G
          networkAttachment: internalapi
        ovndbcluster-sb:
          replicas: 3
          dbType: SB
          storageRequest: 10G
          networkAttachment: internalapi
'
----
+
[NOTE]
The OVN database pods will automatically handle schema conversion during startup and form a proper 3-replica cluster with the preserved data.

. Wait for the OVN database services to start up:
+
----
$ oc wait pod --for condition=Ready --selector=service=ovsdbserver-nb --timeout=300s
$ oc wait pod --for condition=Ready --selector=service=ovsdbserver-sb --timeout=300s
----

. Verify that the data was successfully migrated:
+
----
$ oc exec -it ovsdbserver-nb-0 -- ovn-nbctl show
$ oc exec -it ovsdbserver-sb-0 -- ovn-sbctl show
$ oc exec -it ovsdbserver-sb-0 -- ovn-sbctl list Chassis
----

. Start the control plane `ovn-northd` service to keep both OVN databases in sync:
+
----
$ oc patch openstackcontrolplane openstack --type=merge --patch '
spec:
  ovn:
    enabled: true
    template:
      ovnNorthd:
        replicas: 1
'
----

. If you are running OVN gateway services on {OpenShiftShort} nodes, enable the control plane `ovn-controller` service:
+
----
$ oc patch openstackcontrolplane openstack --type=merge --patch '
spec:
  ovn:
    enabled: true
    template:
      ovnController:
        nicMappings:
          physNet: NIC
'
----
+
* `physNet` defines the name of your physical network. `NIC` is the name of the physical interface that is connected to your physical network.
+
[NOTE]
Running OVN gateways on {OpenShiftShort} nodes might be prone to data plane downtime during Open vSwitch upgrades. Consider running OVN gateways on dedicated `Networker` data plane nodes for production deployments instead.

. The migration is now complete.

+
[NOTE]
The OVN databases now contain the migrated data with preserved UUIDs. Consider taking a snapshot of the database PVCs for backup purposes. For more information, see link:{defaultOCPURL}/storage/index#lvms-about-volume-snapsot_logical-volume-manager-storage[About volume snapshots] in _OpenShift Container Platform storage overview_.

. Stop the adopted OVN database servers:
+
----
ServicesToStop=("tripleo_ovn_cluster_north_db_server.service"
                "tripleo_ovn_cluster_south_db_server.service")

echo "Stopping systemd OpenStack services"
for service in ${ServicesToStop[*]}; do
ifeval::["{build_variant}" != "ospdo"]
    for i in {1..3}; do
        SSH_CMD=CONTROLLER${i}_SSH
endif::[]
ifeval::["{build_variant}" == "ospdo"]
        SSH_CMD=CONTROLLER_SSH
endif::[]
        if [ ! -z "${!SSH_CMD}" ]; then
            echo "Stopping the $service in controller $i"
            if ${!SSH_CMD} sudo systemctl is-active $service; then
                ${!SSH_CMD} sudo systemctl stop $service
            fi
        fi
ifeval::["{build_variant}" != "ospdo"]
    done
endif::[]
done

echo "Checking systemd OpenStack services"
for service in ${ServicesToStop[*]}; do
ifeval::["{build_variant}" != "ospdo"]
    for i in {1..3}; do
        SSH_CMD=CONTROLLER${i}_SSH
endif::[]
ifeval::["{build_variant}" == "ospdo"]
        SSH_CMD=CONTROLLER_SSH
endif::[]
        if [ ! -z "${!SSH_CMD}" ]; then
            if ! ${!SSH_CMD} systemctl show $service | grep ActiveState=inactive >/dev/null; then
                echo "ERROR: Service $service still running on controller $i"
            else
                echo "OK: Service $service is not running on controller $i"
            fi
        fi
ifeval::["{build_variant}" != "ospdo"]
    done
endif::[]
done
----
