Skip to content

Commit 380c3fe

Browse files
fultonjclaude
andcommitted
[adoption_osp_deploy] Add dcn_storage scenario for DCN adoption with Ceph HCI
Add VM layout for DCN deployment with 3 Ceph clusters. Changes from dcn_nostorage: - Increase extra_disks_size from 15G to 30G for Ceph OSDs - All 9 compute nodes (central, dcn1, dcn2) configured for HCI - Stack name is passed to adoption ceph playbook - Fix os-net-config to use stack-specific ctlplane network (ctlplanedcn1/ctlplanedcn2) instead of hardcoded 'ctlplane' - Fix tripleo-ansible-inventory template computes group condition to match DCN group names like 'osp-dcn1-compute-az1s' which contain 'compute' but not 'computes' - Add adoption_dcn_export.yml playbook to export central stack for DCN site deployments - Add adoption_dcn_update_central.yml playbook to update central glance to use ceph at the DCN sites. This scenario pairs with data-plane-adoption's dcn_storage.yaml to deploy OSP 17.1 with Ceph at central and edge sites. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: John Fulton <fulton@redhat.com>
1 parent bf2d410 commit 380c3fe

6 files changed

Lines changed: 473 additions & 5 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
# Copyright Red Hat, Inc.
3+
# All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
# This playbook exports a TripleO stack for use by DCN stacks.
18+
# It creates the <stack_name>-export.yaml file that contains
19+
# the parameters DCN stacks need to connect to the central site.
20+
21+
- name: Export TripleO stack for DCN
22+
hosts: "{{ cifmw_target_host | default('localhost') }}"
23+
gather_facts: false
24+
vars:
25+
_stack_name: "{{ stack_name | default('central') }}"
26+
_ansible_user_dir: "{{ ansible_user_dir | default('/home/zuul') }}"
27+
tasks:
28+
- name: Gather ansible_user_dir from undercloud
29+
delegate_to: "osp-undercloud-0"
30+
ansible.builtin.setup:
31+
gather_subset:
32+
- user_dir
33+
34+
- name: Export stack for DCN usage
35+
delegate_to: "osp-undercloud-0"
36+
vars:
37+
_export_cmd: >-
38+
source {{ _ansible_user_dir }}/stackrc;
39+
openstack overcloud export
40+
--stack {{ _stack_name }}
41+
--force-overwrite
42+
--output-file {{ _ansible_user_dir }}/overcloud-deploy/{{ _stack_name }}/{{ _stack_name }}-export.yaml
43+
cifmw.general.ci_script:
44+
chdir: "{{ _ansible_user_dir }}"
45+
output_dir: "{{ _ansible_user_dir }}/ci-framework-data/artifacts"
46+
script: "{{ _export_cmd }}"
47+
48+
- name: Export Ceph configuration for DCN usage
49+
delegate_to: "osp-undercloud-0"
50+
vars:
51+
_export_ceph_cmd: >-
52+
source {{ ansible_user_dir }}/stackrc;
53+
openstack overcloud export ceph
54+
--stack {{ _stack_name }}
55+
--force-overwrite
56+
--output-file {{ ansible_user_dir }}/{{ _stack_name }}_ceph_external.yaml
57+
cifmw.general.ci_script:
58+
chdir: "{{ ansible_user_dir }}"
59+
output_dir: "{{ ansible_user_dir }}/ci-framework-data/artifacts"
60+
script: "{{ _export_ceph_cmd }}"
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
---
2+
# Copyright Red Hat, Inc.
3+
# All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
# This playbook updates the central site after all DCN sites are deployed.
18+
# 1. Copy Ceph keys and configuration from DCN sites to central controllers
19+
# 2. Configure Glance with DCN Ceph stores
20+
# 3. Restart Glance to apply the new configuration
21+
22+
- name: Update central site with DCN Ceph stores
23+
hosts: "{{ cifmw_target_host | default('localhost') }}"
24+
gather_facts: false
25+
vars:
26+
_dcn_stack_names: "{{ dcn_stack_names | default('dcn1,dcn2') }}"
27+
_glance_config_file: "/var/lib/config-data/puppet-generated/glance_api/etc/glance/glance-api.conf"
28+
tasks:
29+
- name: Build list of DCN sites from scenario
30+
vars:
31+
_dcn_name: "{{ item }}"
32+
_dcn_stack: >-
33+
{{ cifmw_adoption_osp_deploy_scenario.stacks |
34+
selectattr('stackname', 'equalto', _dcn_name) |
35+
first }}
36+
_dcn_group: "{{ _dcn_stack.stack_nodes | first }}"
37+
_dcn_node: "{{ _vm_groups[_dcn_group] | first }}"
38+
ansible.builtin.set_fact:
39+
_dcn_sites: >-
40+
{{ _dcn_sites | default([]) + [{'name': _dcn_name, 'ceph_node': _dcn_node}] }}
41+
loop: "{{ _dcn_stack_names.split(',') }}"
42+
43+
- name: Get list of controller nodes
44+
vars:
45+
_central_stack: >-
46+
{{ cifmw_adoption_osp_deploy_scenario.stacks |
47+
selectattr('stackname', 'equalto', central_stack_name | default('central')) |
48+
first }}
49+
_controller_group: >-
50+
{{ _central_stack.stack_nodes |
51+
select('search', 'controller') |
52+
first | default('osp-controllers') }}
53+
ansible.builtin.set_fact:
54+
_controller_nodes: "{{ _vm_groups[_controller_group] | default([]) }}"
55+
56+
- name: Display discovered nodes
57+
ansible.builtin.debug:
58+
msg:
59+
- "DCN sites: {{ _dcn_sites }}"
60+
- "Controller nodes: {{ _controller_nodes }}"
61+
62+
- name: Build Glance DCN store configuration
63+
ansible.builtin.set_fact:
64+
_glance_dcn_config: |
65+
{% for site in _dcn_sites %}
66+
[{{ site.name }}]
67+
rbd_store_ceph_conf=/etc/ceph/{{ site.name }}.conf
68+
rbd_store_user=openstack
69+
rbd_store_pool=images
70+
rbd_thin_provisioning=False
71+
store_description={{ site.name }} rbd glance store
72+
{% endfor %}
73+
74+
- name: Copy Ceph files from DCN sites to controllers
75+
block:
76+
- name: Ensure ceph config directory exists on controllers
77+
delegate_to: "{{ item }}"
78+
become: true
79+
ansible.builtin.file:
80+
path: "/var/lib/tripleo-config/ceph"
81+
state: directory
82+
owner: root
83+
group: root
84+
mode: "0755"
85+
loop: "{{ _controller_nodes }}"
86+
87+
- name: Fetch Ceph config from DCN site
88+
delegate_to: "{{ item.ceph_node }}"
89+
become: true
90+
ansible.builtin.fetch:
91+
src: "/var/lib/tripleo-config/ceph/{{ item.name }}.conf"
92+
dest: "/tmp/ceph_files/{{ item.name }}.conf"
93+
flat: true
94+
loop: "{{ _dcn_sites }}"
95+
loop_control:
96+
label: "{{ item.name }}"
97+
98+
- name: Fetch Ceph keyring from DCN site
99+
delegate_to: "{{ item.ceph_node }}"
100+
become: true
101+
ansible.builtin.fetch:
102+
src: "/var/lib/tripleo-config/ceph/{{ item.name }}.client.openstack.keyring"
103+
dest: "/tmp/ceph_files/{{ item.name }}.client.openstack.keyring"
104+
flat: true
105+
loop: "{{ _dcn_sites }}"
106+
loop_control:
107+
label: "{{ item.name }}"
108+
109+
- name: Copy Ceph config to controllers
110+
delegate_to: "{{ item.1 }}"
111+
become: true
112+
ansible.builtin.copy:
113+
src: "/tmp/ceph_files/{{ item.0.name }}.conf"
114+
dest: "/var/lib/tripleo-config/ceph/{{ item.0.name }}.conf"
115+
owner: root
116+
group: root
117+
mode: "0644"
118+
loop: "{{ _dcn_sites | product(_controller_nodes) | list }}"
119+
loop_control:
120+
label: "{{ item.0.name }} -> {{ item.1 }}"
121+
122+
- name: Copy Ceph keyring to controllers
123+
delegate_to: "{{ item.1 }}"
124+
become: true
125+
ansible.builtin.copy:
126+
src: "/tmp/ceph_files/{{ item.0.name }}.client.openstack.keyring"
127+
dest: "/var/lib/tripleo-config/ceph/{{ item.0.name }}.client.openstack.keyring"
128+
owner: root
129+
group: root
130+
mode: "0644"
131+
loop: "{{ _dcn_sites | product(_controller_nodes) | list }}"
132+
loop_control:
133+
label: "{{ item.0.name }} -> {{ item.1 }}"
134+
135+
- name: Configure Glance with DCN stores
136+
block:
137+
- name: Check glance config status on each controller
138+
delegate_to: "{{ controller }}"
139+
become: true
140+
ansible.builtin.stat:
141+
path: "{{ _glance_config_file }}"
142+
register: _glance_status
143+
loop: "{{ _controller_nodes }}"
144+
loop_control:
145+
loop_var: controller
146+
147+
- name: Build list of controllers needing Glance update
148+
ansible.builtin.set_fact:
149+
_controllers_to_update: >-
150+
{{ _glance_status.results |
151+
selectattr('stat.exists') |
152+
map(attribute='controller') |
153+
list }}
154+
155+
- name: Display controllers to update
156+
ansible.builtin.debug:
157+
msg: "Controllers needing Glance update: {{ _controllers_to_update }}"
158+
159+
- name: Get current enabled_backends value
160+
delegate_to: "{{ controller }}"
161+
become: true
162+
ansible.builtin.command:
163+
cmd: awk -F'\s*=\s*' '/^enabled_backends\s*=/{print $2; exit}' "{{ _glance_config_file }}"
164+
register: _current_backends
165+
changed_when: false
166+
loop: "{{ _controllers_to_update }}"
167+
loop_control:
168+
loop_var: controller
169+
170+
- name: Build DCN backends string
171+
ansible.builtin.set_fact:
172+
_dcn_backends_suffix: >-
173+
{{ _dcn_sites | map(attribute='name') | map('regex_replace', '^(.*)$', ',\1:rbd') | join('') }}
174+
175+
- name: Update enabled_backends to include DCN stores
176+
delegate_to: "{{ item.controller }}"
177+
become: true
178+
ansible.builtin.lineinfile:
179+
path: "{{ _glance_config_file }}"
180+
regexp: '^enabled_backends\s*='
181+
line: "enabled_backends={{ item.stdout }}{{ _dcn_backends_suffix }}"
182+
backrefs: false
183+
loop: "{{ _current_backends.results }}"
184+
loop_control:
185+
label: "{{ item.controller }}"
186+
when: >-
187+
_dcn_sites | map(attribute='name') |
188+
map('regex_replace', '^(.*)$', '\1:rbd') |
189+
reject('in', item.stdout) | list | length > 0
190+
notify: Restart Glance API service on updated controllers
191+
192+
- name: Enable copy-image import method for multistore
193+
delegate_to: "{{ controller }}"
194+
become: true
195+
ansible.builtin.lineinfile:
196+
path: "{{ _glance_config_file }}"
197+
regexp: '^enabled_import_methods\s*='
198+
line: 'enabled_import_methods=["web-download","copy-image"]'
199+
insertafter: '^\[DEFAULT\]'
200+
loop: "{{ _controllers_to_update }}"
201+
loop_control:
202+
loop_var: controller
203+
notify: Restart Glance API service on updated controllers
204+
205+
- name: Add DCN store configuration to glance-api.conf
206+
delegate_to: "{{ controller }}"
207+
become: true
208+
ansible.builtin.blockinfile:
209+
path: "{{ _glance_config_file }}"
210+
block: "{{ _glance_dcn_config }}"
211+
insertafter: EOF
212+
loop: "{{ _controllers_to_update }}"
213+
loop_control:
214+
loop_var: controller
215+
notify: Restart Glance API service on updated controllers
216+
217+
- name: Clean up temporary Ceph files
218+
ansible.builtin.file:
219+
path: /tmp/ceph_files
220+
state: absent
221+
222+
handlers:
223+
- name: Restart Glance API service on updated controllers
224+
delegate_to: "{{ controller }}"
225+
become: true
226+
ansible.builtin.systemd:
227+
name: tripleo_glance_api.service
228+
state: restarted
229+
loop: "{{ _controllers_to_update }}"
230+
loop_control:
231+
loop_var: controller

hooks/playbooks/adoption_deploy_ceph.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
}}
7373
_cloud_domain: "{{ cifmw_adoption_osp_deploy_scenario.cloud_domain }}"
7474
_source_cmd: "source {{ ansible_user_dir }}/stackrc"
75+
# Use stack-specific filename for non-default stack names to avoid overwrites
76+
# Default 'overcloud' stack uses 'deployed_ceph.yaml' for backward compatibility
77+
_deployed_ceph_suffix: "{{ '' if _overcloud_name == 'overcloud' else '_' ~ _overcloud_name }}"
78+
_deployed_ceph_file: "{{ ansible_user_dir }}/deployed_ceph{{ _deployed_ceph_suffix }}.yaml"
7579
block:
7680
- name: Copy ceph osd file
7781
delegate_to: "osp-undercloud-0"
@@ -99,6 +103,7 @@
99103
_ceph_spec_cmd: >-
100104
openstack overcloud ceph spec
101105
{{ ansible_user_dir }}/config_download_{{ _overcloud_name }}.yaml
106+
--stack {{ _overcloud_name }}
102107
--tld {{ _cloud_domain }}
103108
--osd-spec {{ _ceph_osd_spec_file_dest }}
104109
--roles-data {{ _roles_file_dest }}
@@ -111,7 +116,7 @@
111116
- name: Ensure deployed_ceph file does not exist
112117
delegate_to: "osp-undercloud-0"
113118
ansible.builtin.file:
114-
path: "{{ ansible_user_dir }}/deployed_ceph.yaml"
119+
path: "{{ _deployed_ceph_file }}"
115120
state: absent
116121

117122
- name: Gather nodes for stack {{ _overcloud_name }}
@@ -159,14 +164,18 @@
159164
- name: Deploy ceph
160165
delegate_to: "osp-undercloud-0"
161166
vars:
167+
# Only pass --cluster for non-default stacks to set unique cluster names
168+
# Default 'overcloud' stack uses 'ceph' cluster name for backward compatibility
162169
_ceph_deploy_cmd: >-
163170
openstack overcloud ceph deploy
171+
--stack {{ _overcloud_name }}
172+
{%- if _overcloud_name != 'overcloud' %} --cluster {{ _overcloud_name }}{% endif %}
164173
--tld {{ _cloud_domain }}
165174
--ntp-server {{ cifmw_adoption_osp_deploy_ntp_server }}
166175
--ceph-spec ceph_spec.yaml
167176
--network-data {{ _network_data_file_dest }}
168177
--container-image-prepare {{ ansible_user_dir }}/containers-prepare-parameters.yaml
169-
--output {{ ansible_user_dir }}/deployed_ceph.yaml
178+
--output {{ _deployed_ceph_file }}
170179
cifmw.general.ci_script:
171180
chdir: "{{ ansible_user_dir }}"
172181
output_dir: "{{ _cifmw_basedir_undercloud }}/artifacts"

roles/adoption_osp_deploy/tasks/prepare_overcloud.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@
254254
delegate_to: "{{ overcloud_vm }}"
255255
vars:
256256
_node_net: "{{ cifmw_networking_env_definition.instances[overcloud_vm] }}"
257-
_ctlplane_node_net: "{{ _node_net.networks.ctlplane | default({}) }}"
258-
_ctlplane_net: "{{ cifmw_networking_env_definition.networks.ctlplane | default({}) }}"
257+
_ctlplane_node_net: "{{ _node_net.networks[_ctlplane] | default({}) }}"
258+
_ctlplane_net: "{{ cifmw_networking_env_definition.networks[_ctlplane] | default({}) }}"
259259
_dns_server: "{{ _ctlplane_net[dns_version|default('dns_v4')] | default(None) }}"
260260
_os_net_config_template: "{{ 'os_net_config_overcloud_bgp.yml.j2' if (cifmw_adoption_osp_deploy_bgp | bool) else 'os_net_config_overcloud.yml.j2' }}"
261261
# Non-BGP specific vars (may not exist in BGP mode, so use default)

roles/adoption_osp_deploy/templates/tripleo-ansible-inventory.yaml.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ allovercloud:
3636
computes:
3737
children:
3838
{% for group in _stack.stack_nodes %}
39-
{% if 'computes' in group %}
39+
{% if 'compute' in group and 'controller' not in group %}
4040
{{ cifmw_adoption_osp_deploy_scenario.roles_groups_map[group] }}: {}
4141
{% endif %}
4242
{% endfor %}

0 commit comments

Comments
 (0)