:_mod-docs-content-type: PROCEDURE
[id="converting-object-storage-nodes"]

= Converting {object_storage} storage nodes

[role="_abstract"]
If you used external {object_storage} storage nodes, you can convert these
nodes after the initial {object_storage_first_ref} adoption is complete. This
step reconfigures the nodes in-place while keeping the {object_storage}
available throughout the process.

Before you begin, review the following key concepts:

Pre-provisioned nodes:: Setting `preProvisioned: true` in the
  `OpenStackDataPlaneNodeSet` custom resource (CR) skips provisioning the node from scratch and does not reinstall the operating system.
{object_storage} disk and mount preservation:: `edpm_swift_disks: []` must be
  set in both the `nodeTemplate` and in the individual node definition under
  `nodes` to ensure that no disks are formatted or mounted differently.
  Existing disk mounts are untouched. The `SwiftRawDisks` parameter from
  {OpenStackPreviousInstaller} is not migrated or converted. Disks on
  converted nodes must be managed manually.
Rolling conversion:: The `swift-conversion` service uses `serial: 1` by
  default, which converts one node at a time to keep the {object_storage}
  available.

.Prerequisites

* The initial {object_storage} adoption is complete. For more information, see
  xref:adopting-the-object-storage-service_hsm-integration[Adopting the
  {object_storage}].
* Nodes cannot be members of more than one `OpenStackDataPlaneNodeSet` CR at
  the same time. If the nodes you want to convert were part of the node set
  that you created during the control plane adoption, delete that node set
  before proceeding:
+
----
$ oc delete openstackdataplanenodeset <adoption-nodeset-name>
----
+
where:

`<adoption-nodeset-name>`::
Specifies the node set that you want to delete.

.Procedure

. Create a patch file to configure the {object_storage} data plane storage
  ports:
+
[NOTE]
The {OpenStackPreviousInstaller} deployment used different default
ports: 6002 for the account server, 6001 for the container server, 6000 for
the object server. The converted nodes must continue to use these same
ports to ensure connectivity between all {object_storage} storage nodes by using
the unmodified rings.
+
----
cat > swift-config-patch.yaml << EOF
spec:
  swift:
    template:
      swiftStorage:
        defaultConfigOverwrite:
          01-account-server.conf: |
            [DEFAULT]
            bind_port = 6002
          01-container-server.conf: |
            [DEFAULT]
            bind_port = 6001
          01-object-server.conf: |
            [DEFAULT]
            bind_port = 6000
EOF
----
+
[NOTE]
If you customized any {object_storage} service settings  in the
{OpenStackPreviousInstaller} deployment, include those settings as well. For
example, if you changed  the workers count or added additional configuration options
for the account, container, or object services, add them to the
corresponding section in the patch file.

. Apply the patch to the `OpenStackControlPlane` CR:
+
----
$ oc patch openstackcontrolplane openstack --type=merge --patch-file=swift-config-patch.yaml
----

. Create a `OpenStackDataPlaneService` CR to remove remaining
  {OpenStackPreviousInstaller} artifacts and deploy the {object_storage} services
  on the node:
+
----
$ oc apply -f - <<EOF
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneService
metadata:
  name: swift-conversion
  namespace: openstack
spec:
  addCertMounts: false
  caCerts: combined-ca-bundle
  edpmServiceType: swift
  dataSources:
  - secretRef:
      name: swift-conf
  - configMapRef:
      name: swift-storage-config-data
  - configMapRef:
      name: swift-ring-files
  playbookContents: |
    - name: Cleanup and deploy Swift
      hosts: "{{ edpm_override_hosts | default('all', true) }}"
      serial: 1
      strategy: linear
      gather_facts: "{{ gather_facts | default(false) }}"
      any_errors_fatal: "{{ edpm_any_errors_fatal | default(true) }}"
      max_fail_percentage: "{{ edpm_max_fail_percentage | default(0) }}"
      environment: "{{ edpm_playbook_environment | default({}) }}"
      tasks:
        - ansible.builtin.import_role:
            name: osp.edpm.edpm_tripleo_cleanup
        - ansible.builtin.import_role:
            name: osp.edpm.edpm_swift
EOF
----

ifeval::["{build}" == "upstream"]
. Create a `OpenStackDataPlaneService` CR to configure RPM repositories on the node:
+
----
$ oc apply -f - <<EOF
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneService
metadata:
  name: repo-setup
  namespace: openstack
spec:
  addCertMounts: false
  caCerts: combined-ca-bundle
  edpmServiceType: repo-setup
  playbookContents: |
    - hosts: all
      strategy: linear
      tasks:
        - name: Enable podified-repos
          become: true
          ansible.builtin.shell: |
            # This needs rhos-release.rpm to be downloaded before or an
            # alternative way to use upstream repos
            dnf install -y /tmp/rhos-release.rpm
            rhos-release -x
            rhos-release 18.0
EOF
----
endif::[]

. Create the `OpenStackDataPlaneNodeSet` CR that defines which nodes to convert and how to configure them:
+
[subs="+quotes"]
----
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneNodeSet
metadata:
  name: openstack-edpm-ipam
  namespace: openstack
spec:
  networkAttachments:
  - ctlplane
  - storage
  nodeTemplate:
    ansible:
      ansibleUser: cloud-admin
ifeval::["{build}" == "downstream"]
        ansibleVarsFrom:
        - secretRef:
            name: subscription-manager
        - secretRef:
            name: redhat-registry
endif::[]
      ansibleVars:
        edpm_nodes_validation_validate_controllers_icmp: false
        edpm_nodes_validation_validate_gateway_icmp: false
        edpm_service_removal_skip_list:
        - tripleo-container-shutdown.service
        edpm_sshd_allowed_ranges:
        - 192.168.122.0/24
        edpm_swift_disks: []
        enable_debug: false
        gather_facts: false
        timesync_ntp_servers:
        - hostname: pool.ntp.org
ifeval::["{build}" != "downstream"]
        edpm_bootstrap_command: |
          # This is a hack to deploy RDO Delorean repos to RHEL as if it were Centos 9 Stream
          set -euxo pipefail
          curl -sL https://github.com/openstack-k8s-operators/repo-setup/archive/refs/heads/main.tar.gz | tar -xz
          python3 -m venv ./venv
          PBR_VERSION=0.0.0 ./venv/bin/pip install ./repo-setup-main
          # This is required for FIPS enabled until trunk.rdoproject.org
          # is not being served from a centos7 host, tracked by
          # https://issues.redhat.com/browse/RHOSZUUL-1517
          dnf -y install crypto-policies
          update-crypto-policies --set FIPS:NO-ENFORCE-EMS
          ./venv/bin/repo-setup current-podified -b antelope -d centos9 --stream
          dnf -y upgrade openstack-selinux
          rm -f /run/virtlogd.pid
          rm -rf repo-setup-main
endif::[]
ifeval::["{build}" == "downstream"]
        rhc_release: 9.2
        rhc_repositories:
            - {name: "*", state: disabled}
            - {name: "rhel-9-for-x86_64-baseos-eus-rpms", state: enabled}
            - {name: "rhel-9-for-x86_64-appstream-eus-rpms", state: enabled}
            - {name: "rhel-9-for-x86_64-highavailability-eus-rpms", state: enabled}
            - {name: "rhoso-18.0-for-rhel-9-x86_64-rpms", state: enabled}
endif::[]
    ansibleSSHPrivateKeySecret: dataplane-adoption-secret
    managementNetwork: ctlplane
  nodes:
    edpm-swift-0:
      ansible:
        ansibleHost: 192.168.122.100
        ansibleVars:
          edpm_swift_disks: []
      hostName: edpm-swift-0
      networks:
      - defaultRoute: true
        fixedIP: 192.168.122.100
        name: ctlplane
        subnetName: subnet1
      - name: internalapi
        subnetName: subnet2
      - name: storage
        subnetName: subnet3
      - name: tenant
        subnetName: subnet4
  preProvisioned: true
  services:
  - bootstrap
  - download-cache
  - install-os
  - configure-os
  - ssh-known-hosts
  - run-os
  - reboot-os
  - install-certs
  - swift-conversion
  tlsEnabled: true
----

. Apply the `OpenStackDataPlaneNodeSet` CR:
+
----
$ oc apply -f nodeset.yaml
----

. Create an `OpenStackDataPlaneDeployment` CR to trigger the Ansible pipeline on the node:
+
----
$ oc apply -f - <<EOF
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneDeployment
metadata:
  name: openstack-edpm-ipam
  namespace: openstack
spec:
  nodeSets:
  - openstack-edpm-ipam
EOF
----

. Wait for the deployment to complete:
+
----
$ oc wait --for condition=Ready openstackdataplanedeployment/openstack-edpm-ipam --timeout=30m
----

.. Monitor the progress:
+
----
$ watch oc get pod -l app=openstackansibleee
----
+
----
$ oc logs -l app=openstackansibleee -f --max-log-requests 40
----

.Verification

* Verify that the Object Storage services are running on the converted node:
+
----
$ ssh cloud-admin@192.168.122.100 "sudo systemctl status edpm_swift_*"
----

* Verify that the Object Storage services are functional after conversion, for
  example by uploading an object:
+
----
$ oc rsh openstackclient /bin/sh -c "touch obj && openstack container create cont && openstack object create cont obj"
----
