Skip to content

Commit 01786e6

Browse files
wangxinbotwangxinCopilot
authored andcommitted
ansible: support pulling ceos_image from docker registry and enhance image discovery (sonic-net#23300)
Cherry-pick of sonic-net#22794 and sonic-net#23299 to 202511 branch. Original PRs: - sonic-net#22794 - sonic-net#23299 Includes enhanced ceos image discovery: when ceos_registry is not defined, a consolidated 'docker images | grep' discovers pre-pulled registry images locally, avoiding unnecessary local image builds that trigger S360 alerts. Signed-off-by: Xin Wang <[email protected]> Co-authored-by: Xin Wang <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent f17b396 commit 01786e6

4 files changed

Lines changed: 225 additions & 90 deletions

File tree

ansible/group_vars/vm_host/ceos.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,16 @@ ceos_image_url:
99
- "http://example1.com/cEOS64-lab-4.32.5M.tar"
1010
- "http://example2.com/cEOS64-lab-4.32.5M.tar"
1111
skip_ceos_image_downloading: false
12+
13+
# Registry settings for pulling ceos docker image. If ceos_registry is defined, the code will first
14+
# check whether ceos_image is already cached locally from the registry, and if not, try to pull it
15+
# from the registry before falling back to the download-and-build approach.
16+
#
17+
# Uncomment ceos_registry and set it to your registry URL to enable registry pulling.
18+
# If the registry requires authentication, also uncomment and set ceos_registry_username and
19+
# ceos_registry_password. If the registry is publicly accessible without authentication, leave the
20+
# username and password commented out.
21+
#
22+
# ceos_registry: "your-registry.example.com"
23+
# ceos_registry_username: "your-username"
24+
# ceos_registry_password: "your-password"

ansible/roles/eos/tasks/ceos.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,56 @@
2323
- include_vars: group_vars/vm_host/ceos.yml
2424
- include_tasks: ceos_config.yml
2525

26+
- name: Check if ceos_image from registry is available locally
27+
docker_image_info:
28+
name:
29+
- "{{ ceos_registry }}/{{ ceos_image }}"
30+
become: yes
31+
register: ceos_registry_image_info
32+
delegate_to: "{{ VM_host[0] }}"
33+
when: ceos_registry is defined
34+
35+
- name: Discover ceos_image locally (plain or from any registry)
36+
# Searches all local docker images for one matching ceos_image. This single grep covers:
37+
# - Plain local image: ceosimage:4.32.5M-1
38+
# - Image pre-pulled from any registry: soniccr1.azurecr.io/ceosimage:4.32.5M-1
39+
# When ceos_registry is defined, this task is skipped — the explicit registry check above is used.
40+
become: yes
41+
shell: >-
42+
docker images --format
43+
'{% raw %}{{.Repository}}:{{.Tag}}{% endraw %}'
44+
| grep -m1 '{{ ceos_image }}$'
45+
register: ceos_image_discovery_result
46+
delegate_to: "{{ VM_host[0] }}"
47+
ignore_errors: yes
48+
changed_when: no
49+
when: ceos_registry is not defined
50+
51+
- name: Set ceos_effective_image
52+
set_fact:
53+
# Priority order for determining the effective ceos image:
54+
# 1. When ceos_registry is defined and the registry image is available locally, use the
55+
# registry-prefixed image name. This ensures containers always reference the registry
56+
# image, satisfying security requirements (e.g. Microsoft ACR/MCR policy).
57+
# 2. When ceos_registry is not defined, use whatever local image matches ceos_image
58+
# (plain or from any registry). Preferring a pre-pulled registry image avoids building
59+
# a local image and triggering S360 alerts.
60+
# 3. Fall back to the plain ceos_image name when no image is discovered.
61+
ceos_effective_image: >-
62+
{{ ceos_registry + '/' + ceos_image
63+
if (ceos_registry is defined and ceos_registry_image_info.images | length > 0)
64+
else (ceos_image_discovery_result.stdout | trim)
65+
if (ceos_registry is not defined and
66+
ceos_image_discovery_result is not skipped and
67+
ceos_image_discovery_result.rc == 0 and
68+
ceos_image_discovery_result.stdout | trim != '')
69+
else ceos_image }}
70+
2671
- name: Create cEOS container
2772
become: yes
2873
docker_container:
2974
name: ceos_{{ vm_set_name }}_{{ inventory_hostname }}
30-
image: "{{ ceos_image }}"
75+
image: "{{ ceos_effective_image }}"
3176
command: /sbin/init systemd.setenv=INTFTYPE=eth systemd.setenv=ETBA=1 systemd.setenv=SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 systemd.setenv=CEOS=1 systemd.setenv=EOS_PLATFORM=ceoslab systemd.setenv=container=docker systemd.setenv=MGMT_INTF=eth0
3277
pull: no
3378
state: started

ansible/roles/eos/tasks/ceos_ensure_reachable.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
- name: Set ceos_effective_image fallback if not already defined
2+
# ceos_effective_image is normally set by the caller (ceos.yml in the eos role) before this
3+
# file is included. This task provides a safe fallback in case this file is invoked
4+
# independently, so it does not fail with an undefined variable error.
5+
set_fact:
6+
ceos_effective_image: "{{ ceos_image }}"
7+
when: ceos_effective_image is not defined
8+
19
- block:
210
- name: set time out threshold
311
set_fact:
@@ -18,7 +26,7 @@
1826
become: yes
1927
docker_container:
2028
name: ceos_{{ vm_set_name }}_{{ inventory_hostname }}
21-
image: "{{ ceos_image }}"
29+
image: "{{ ceos_effective_image }}"
2230
command: /sbin/init systemd.setenv=INTFTYPE=eth systemd.setenv=ETBA=1 systemd.setenv=SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 systemd.setenv=CEOS=1 systemd.setenv=EOS_PLATFORM=ceoslab systemd.setenv=container=docker systemd.setenv=MGMT_INTF=eth0
2331
pull: no
2432
state: started

ansible/roles/vm_set/tasks/add_ceos_list.yml

Lines changed: 157 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,172 @@
1-
- name: Check if cEOS docker image exists or not
1+
- name: Check if cEOS docker image from registry is already cached locally
22
docker_image_info:
33
name:
4-
- "{{ ceos_image }}"
4+
- "{{ ceos_registry }}/{{ ceos_image }}"
55
become: yes
6-
register: ceos_docker_image_stat
6+
register: ceos_registry_cached_image_stat
7+
when: ceos_registry is defined
8+
9+
- name: Discover cEOS docker image locally (plain or from any registry)
10+
# Searches all local docker images for one matching ceos_image. This single grep covers:
11+
# - Plain local image: ceosimage:4.32.5M-1
12+
# - Image pre-pulled from any registry: soniccr1.azurecr.io/ceosimage:4.32.5M-1
13+
# When ceos_registry is defined, this task is skipped — the explicit registry check above is used.
14+
become: yes
15+
shell: >-
16+
docker images --format
17+
'{% raw %}{{.Repository}}:{{.Tag}}{% endraw %}'
18+
| grep -m1 '{{ ceos_image }}$'
19+
register: ceos_image_discovery_result
20+
ignore_errors: yes
21+
changed_when: no
22+
when: ceos_registry is not defined
23+
24+
- name: Set fact for whether ceos_image is already available
25+
set_fact:
26+
# When registry is defined, only the registry-prefixed image is considered "found" - using
27+
# a local image in that case would bypass the registry requirement (e.g. Microsoft ACR/MCR).
28+
# When registry is not defined, the image is "found" if any local image (plain or from any
29+
# registry) matches ceos_image. This covers images pre-pulled during infrastructure
30+
# provisioning (e.g. VMSS setup) without exposing the registry URL in public code.
31+
# The | bool filter ensures this is stored as a Python boolean, not the string "True"/"False",
32+
# so that "when: not ceos_image_found" evaluates correctly.
33+
ceos_image_found: "{{ ((ceos_registry is not defined and
34+
ceos_image_discovery_result is not skipped and
35+
ceos_image_discovery_result.rc == 0 and
36+
ceos_image_discovery_result.stdout | trim != '') or
37+
(ceos_registry is defined and
38+
ceos_registry_cached_image_stat.images | length > 0)) | bool }}"
739

840
- name: Prepare ceos_image if it does not exist
941
block:
1042

11-
- name: Check if ceos_image_orig exists or not
12-
docker_image_info:
13-
name:
14-
- "{{ ceos_image_orig }}"
15-
become: yes
16-
register: ceos_image_orig_stat
43+
- name: Try to get ceos_image from registry
44+
block:
45+
46+
- name: Login to ceos registry if credentials are defined
47+
become: yes
48+
docker_login:
49+
registry_url: "{{ ceos_registry }}"
50+
username: "{{ ceos_registry_username }}"
51+
password: "{{ ceos_registry_password }}"
52+
when: >
53+
ceos_registry_username is defined and
54+
ceos_registry_password is defined
55+
56+
- name: Pull ceos_image from registry
57+
become: yes
58+
docker_image:
59+
name: "{{ ceos_registry }}/{{ ceos_image }}"
60+
source: pull
61+
register: ceos_registry_pull_result
62+
ignore_errors: yes
1763

18-
- name: Prepare ceos_image_orig if it does not exist
64+
- name: Warn that registry pull failed, will fall back to download and build
65+
debug:
66+
msg: >-
67+
WARNING: Failed to pull {{ ceos_registry }}/{{ ceos_image }} from registry.
68+
Will fall back to the download-and-build path. Check ceos_registry and credentials.
69+
when: ceos_registry_pull_result is failed
70+
71+
when: ceos_registry is defined
72+
73+
- name: Prepare ceos_image via download and build if registry pull failed or registry not defined
1974
block:
20-
- name: Check if local ceos image file exists or not
21-
stat:
22-
path: "{{ root_path }}/images/{{ ceos_image_filename }}"
23-
register: ceos_image_file_stat
2475

25-
- name: Download cEOS image file if no local ceos image file exists
76+
- name: Check if ceos_image_orig exists or not
77+
docker_image_info:
78+
name:
79+
- "{{ ceos_image_orig }}"
80+
become: yes
81+
register: ceos_image_orig_stat
82+
83+
- name: Prepare ceos_image_orig if it does not exist
2684
block:
27-
- name: Fail if skip_ceos_image_downloading is true
28-
fail:
29-
msg: [
30-
"Failed, no ceos docker image, no ceos image file and skip_ceos_image_downloading is true",
31-
"Please manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}"
32-
]
33-
when: skip_ceos_image_downloading == true
34-
35-
- name: Init ceos_image_urls when ceos_image_url value type is string
36-
set_fact:
37-
ceos_image_urls:
38-
- "{{ ceos_image_url }}"
39-
when: ceos_image_url | type_debug == 'string'
40-
41-
- name: Init ceos_image_urls when ceos_image_url value type is list
42-
set_fact:
43-
ceos_image_urls: "{{ ceos_image_url }}"
44-
when: ceos_image_url | type_debug == 'list'
45-
46-
- name: Init working_image_urls list
47-
set_fact:
48-
working_image_urls: []
49-
50-
- name: Loop ceos_image_urls to find out working URLs
51-
include_tasks: probe_image_url.yml
52-
loop: "{{ ceos_image_urls }}"
53-
54-
- name: Fail if no working ceos image download url is found
55-
fail:
56-
msg: [
57-
"Failed, no working ceos image download URL is found. There are 2 options to fix it:",
58-
" 1. Fix ceos_image_url defined in ansible/group_vars/vm_host/ceos.yml",
59-
" 2. Manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}",
60-
]
61-
when: working_image_urls | length == 0
62-
63-
- name: Ensure {{ root_path }}/images exists
64-
file: path={{ root_path }}/images state=directory
65-
66-
- name: Download cEOS image file from working ceos_image_urls using the first working URL
67-
get_url:
68-
url: "{{ working_image_urls[0] }}"
69-
dest: "{{ root_path }}/images/{{ ceos_image_filename }}"
70-
environment: "{{ proxy_env | default({}) }}"
71-
register: ceos_image_download_result
72-
73-
when: ceos_image_file_stat.stat.exists == false
74-
75-
- name: Import ceos_image_orig docker image
85+
- name: Check if local ceos image file exists or not
86+
stat:
87+
path: "{{ root_path }}/images/{{ ceos_image_filename }}"
88+
register: ceos_image_file_stat
89+
90+
- name: Download cEOS image file if no local ceos image file exists
91+
block:
92+
- name: Fail if skip_ceos_image_downloading is true
93+
fail:
94+
msg: [
95+
"Failed, no ceos docker image, no ceos image file and skip_ceos_image_downloading is true",
96+
"Please manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}"
97+
]
98+
when: skip_ceos_image_downloading == true
99+
100+
- name: Init ceos_image_urls when ceos_image_url value type is string
101+
set_fact:
102+
ceos_image_urls:
103+
- "{{ ceos_image_url }}"
104+
when: ceos_image_url | type_debug == 'string'
105+
106+
- name: Init ceos_image_urls when ceos_image_url value type is list
107+
set_fact:
108+
ceos_image_urls: "{{ ceos_image_url }}"
109+
when: ceos_image_url | type_debug == 'list'
110+
111+
- name: Init working_image_urls list
112+
set_fact:
113+
working_image_urls: []
114+
115+
- name: Loop ceos_image_urls to find out working URLs
116+
include_tasks: probe_image_url.yml
117+
loop: "{{ ceos_image_urls }}"
118+
119+
- name: Fail if no working ceos image download url is found
120+
fail:
121+
msg: [
122+
"Failed, no working ceos image download URL is found. There are 2 options to fix it:",
123+
" 1. Fix ceos_image_url defined in ansible/group_vars/vm_host/ceos.yml",
124+
" 2. Manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}",
125+
]
126+
when: working_image_urls | length == 0
127+
128+
- name: Ensure {{ root_path }}/images exists
129+
file: path={{ root_path }}/images state=directory
130+
131+
- name: Download cEOS image file from working ceos_image_urls using the first working URL
132+
get_url:
133+
url: "{{ working_image_urls[0] }}"
134+
dest: "{{ root_path }}/images/{{ ceos_image_filename }}"
135+
environment: "{{ proxy_env | default({}) }}"
136+
register: ceos_image_download_result
137+
138+
when: ceos_image_file_stat.stat.exists == false
139+
140+
- name: Import ceos_image_orig docker image
141+
become: yes
142+
shell: "docker import {{ root_path }}/images/{{ ceos_image_filename }} {{ ceos_image_orig }}"
143+
144+
when: ceos_image_orig_stat.images | length == 0
145+
146+
- name: Create directory for building ceos docker image
147+
become: yes
148+
file:
149+
path: "/tmp/ceosimage"
150+
state: directory
151+
152+
- name: Copy the ceos image template
76153
become: yes
77-
shell: "docker import {{ root_path }}/images/{{ ceos_image_filename }} {{ ceos_image_orig }}"
78-
79-
when: ceos_image_orig_stat.images | length == 0
80-
81-
- name: Create directory for building ceos docker image
82-
become: yes
83-
file:
84-
path: "/tmp/ceosimage"
85-
state: directory
86-
87-
- name: Copy the ceos image template
88-
become: yes
89-
template: src=ceos_dockerfile.j2 dest=/tmp/ceosimage/Dockerfile mode=0644
90-
91-
- name: Build the ceos image with increasing inotify limit
92-
become: yes
93-
docker_image:
94-
name: "{{ ceos_image }}"
95-
build:
96-
path: "/tmp/ceosimage"
97-
pull: no
98-
source: build
99-
100-
when: ceos_docker_image_stat.images | length == 0
154+
template: src=ceos_dockerfile.j2 dest=/tmp/ceosimage/Dockerfile mode=0644
155+
156+
- name: Build the ceos image with increasing inotify limit
157+
become: yes
158+
docker_image:
159+
name: "{{ ceos_image }}"
160+
build:
161+
path: "/tmp/ceosimage"
162+
pull: no
163+
source: build
164+
165+
when: >
166+
ceos_registry is not defined or
167+
(ceos_registry_pull_result is defined and ceos_registry_pull_result is failed)
168+
169+
when: not ceos_image_found
101170

102171
- name: Create VMs network
103172
become: yes

0 commit comments

Comments
 (0)