Skip to content

Commit 48bf447

Browse files
authored
[202411] Support Ubuntu 24 server in KVM (sonic-net#18929)
What is the motivation for this PR? When running with Ubuntu 24.04 host, many tasks from a sonic-mgmt container may fail. One reason is that a newer version of Python being used in Ubuntu 24.04 that doesn't support some of the packages that ansible modules attempt to import. Such as imp module is removed in Python 3.11, need to use importlib, and PR sonic-net#16039 and sonic-net#17883 did the fix and need to cherry-pick to 202411 How did you do it? Merge the change of sonic-net#16039 and sonic-net#17883 to 202411
1 parent 194b907 commit 48bf447

17 files changed

Lines changed: 216 additions & 83 deletions

File tree

ansible/ansible.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ fact_caching_timeout = 86400
170170

171171
[privilege_escalation]
172172
#become=True
173-
become_method='sudo'
173+
become_method=sudo
174174
#become_user='root'
175175
#become_ask_pass=False
176176

ansible/devutil/conn_graph_helper.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
import os
22
import inspect
33
import sys
4-
import imp
4+
try:
5+
import importlib.util
6+
import importlib.machinery
7+
use_importlib = True
8+
except ImportError:
9+
import imp
10+
use_importlib = False
511

612
CONN_GRAPH_LOG = "/tmp/conn_graph_debug.txt"
713

814

15+
def load_source(modname, filename):
16+
if use_importlib:
17+
loader = importlib.machinery.SourceFileLoader(modname, filename)
18+
spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
19+
module = importlib.util.module_from_spec(spec)
20+
# The module is always executed and not cached in sys.modules.
21+
# Uncomment the following line to cache the module.
22+
# sys.modules[module.__name__] = module
23+
loader.exec_module(module)
24+
else:
25+
# For Python 2.x compatibility
26+
module = imp.load_source(modname, filename)
27+
return module
28+
29+
930
def get_conn_graph_facts(hostnames):
1031
"""
1132
@summary: Load conn_graph_facts from conn_graph_facts.xml
@@ -18,7 +39,7 @@ def get_conn_graph_facts(hostnames):
1839
if ansible_path not in sys.path:
1940
sys.path.append(ansible_path)
2041

21-
utils = imp.load_source('conn_graph_utils', os.path.join(
42+
utils = load_source('conn_graph_utils', os.path.join(
2243
ansible_path, 'library/conn_graph_facts.py'))
2344
utils.LAB_GRAPHFILE_PATH = os.path.join(
2445
ansible_path, utils.LAB_GRAPHFILE_PATH)

ansible/health_checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/env python3
22

3-
import imp
43
import os
54
import logging
65
import sys
@@ -13,6 +12,7 @@
1312
except ImportError:
1413
# ToDo: Support running without Ansible
1514
has_ansible = False
15+
from devutil.conn_graph_helper import load_source
1616

1717
ANSIBLE_DIR = os.path.abspath(os.path.dirname(__file__))
1818
SONIC_MGMT_DIR = os.path.dirname(ANSIBLE_DIR)
@@ -35,7 +35,7 @@
3535

3636
def get_testbeds_dict():
3737
"""Return a dictionary containing mapping from dut hostname to testbed name."""
38-
testbed = imp.load_source('testbed', os.path.join(
38+
testbed = load_source('testbed', os.path.join(
3939
SONIC_MGMT_DIR, 'tests/common/testbed.py'))
4040
testbeds_dict = testbed.TestbedInfo(TESTBED_FILE).testbed_topo
4141
return testbeds_dict

ansible/plugins/connection/multi_passwd_ssh.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import hashlib
2-
import imp
2+
try:
3+
import importlib.util
4+
import importlib.machinery
5+
use_importlib = True
6+
except ImportError:
7+
import imp
8+
use_importlib = False
39
import logging
410
import os
511

@@ -10,9 +16,24 @@
1016

1117
logger = logging.getLogger(__name__)
1218

19+
20+
def load_source(modname, filename):
21+
loader = importlib.machinery.SourceFileLoader(modname, filename)
22+
spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
23+
module = importlib.util.module_from_spec(spec)
24+
# The module is always executed and not cached in sys.modules.
25+
# Uncomment the following line to cache the module.
26+
# sys.modules[module.__name__] = module
27+
loader.exec_module(module)
28+
return module
29+
30+
1331
# HACK: workaround to import the SSH connection plugin
1432
_ssh_mod = os.path.join(os.path.dirname(connection.__file__), "ssh.py")
15-
_ssh = imp.load_source("_ssh", _ssh_mod)
33+
if use_importlib:
34+
_ssh = load_source("_ssh", _ssh_mod)
35+
else:
36+
_ssh = imp.load_source("_ssh", _ssh_mod)
1637

1738
# Use same options as the builtin Ansible SSH plugin
1839
DOCUMENTATION = _ssh.DOCUMENTATION

ansible/recover_server.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
import argparse
1111
import collections
1212
import datetime
13-
import imp
1413
import logging
1514
import os
1615
import subprocess
1716
import sys
1817
import tempfile
1918
import threading
2019
import time
21-
20+
from devutil.conn_graph_helper import load_source
2221
from tabulate import tabulate
2322
# Add tests path to syspath
2423
sys.path.append('../')
@@ -40,7 +39,7 @@
4039

4140
def parse_testbed(testbedfile, testbed_servers):
4241
"""Return a dictionary containing mapping from server name to testbeds."""
43-
testbed = imp.load_source('testbed', os.path.join(
42+
testbed = load_source('testbed', os.path.join(
4443
SONIC_MGMT_DIR, 'tests/common/testbed.py'))
4544
testbeds = {server_name: list() for server_name in testbed_servers}
4645
for tbname, tb in testbed.TestbedInfo(testbedfile).testbed_topo.items():
@@ -258,7 +257,7 @@ def _join_all(threads):
258257
break
259258
time.sleep(5)
260259

261-
utilities = imp.load_source('utilities', os.path.join(
260+
utilities = load_source('utilities', os.path.join(
262261
SONIC_MGMT_DIR, 'tests/common/utilities.py'))
263262

264263
curr_date = datetime.datetime.today().strftime('%Y-%m-%d_%H-%M-%S')

ansible/restart_nightly_ptf.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
import argparse
44
import logging
5-
import imp
65
import os
76
import recover_server
87
import sys
98
import collections
109
import datetime
1110
import time
1211
import tempfile
13-
12+
from devutil.conn_graph_helper import load_source
1413
# Add tests path to syspath
1514
sys.path.append('../')
1615

@@ -69,7 +68,7 @@ def __call__(self):
6968

7069
def parse_testbed(testbedfile, servers):
7170
"""Return a dictionary containing mapping from server name to nightly testbeds that need restart-ptf."""
72-
testbed = imp.load_source('testbed', os.path.join(
71+
testbed = load_source('testbed', os.path.join(
7372
SONIC_MGMT_DIR, 'tests/common/testbed.py'))
7473
all_testbeds = testbed.TestbedInfo(testbedfile).testbed_topo
7574
nightly_dir = os.path.join(SONIC_MGMT_DIR, ".azure-pipelines", "nightly")
@@ -117,7 +116,7 @@ def _join_all(threads):
117116
break
118117
time.sleep(5)
119118

120-
utilities = imp.load_source('utilities', os.path.join(
119+
utilities = load_source('utilities', os.path.join(
121120
SONIC_MGMT_DIR, 'tests/common/utilities.py'))
122121

123122
curr_date = datetime.datetime.today().strftime('%Y-%m-%d_%H-%M-%S')

ansible/roles/vm_set/tasks/control_mux_simulator.yml

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,43 @@
1717
werkzeug_version: "1.0.1"
1818
python_command: "python"
1919

20-
- name: Use newer Flask version for pip3
20+
- name: Use newer Flask and Werkzeug version for pip3
2121
set_fact:
2222
flask_version: "2.3.3"
23-
python_command: "python3"
24-
when: pip_executable == "pip3"
25-
26-
- name: Use newer Werkzeug version for pip3
27-
set_fact:
2823
werkzeug_version: "2.3.7"
2924
python_command: "python3"
3025
when: pip_executable == "pip3"
3126

32-
- name: Install flask
33-
pip: name=flask version={{ flask_version }} state=forcereinstall executable={{ pip_executable }}
34-
become: yes
35-
environment: "{{ proxy_env | default({}) }}"
36-
37-
- name: Install werkzeug
38-
pip: name=werkzeug version={{ werkzeug_version }} state=forcereinstall executable={{ pip_executable }}
39-
become: yes
40-
environment: "{{ proxy_env | default({}) }}"
27+
- name: Run python3 in a virtualenv
28+
set_fact:
29+
python_command: "/tmp/sonic-mgmt-virtualenv/bin/python3"
30+
when: host_distribution_version.stdout >= "24.04"
31+
32+
- name: Install Flask and Werkzeug
33+
block:
34+
- name: Install flask
35+
pip: name=flask version={{ flask_version }} state=forcereinstall executable={{ pip_executable }}
36+
become: yes
37+
environment: "{{ proxy_env | default({}) }}"
38+
39+
- name: Install werkzeug
40+
pip: name=werkzeug version={{ werkzeug_version }} state=forcereinstall executable={{ pip_executable }}
41+
become: yes
42+
environment: "{{ proxy_env | default({}) }}"
43+
when: host_distribution_version.stdout < "24.04"
44+
45+
- name: Install Flask and Werkzeug
46+
block:
47+
- name: Install flask
48+
pip: name=flask version={{ flask_version }} state=forcereinstall virtualenv=/tmp/sonic-mgmt-virtualenv virtualenv_site_packages=true
49+
become: yes
50+
environment: "{{ proxy_env | default({}) }}"
51+
52+
- name: Install werkzeug
53+
pip: name=werkzeug version={{ werkzeug_version }} state=forcereinstall virtualenv=/tmp/sonic-mgmt-virtualenv virtualenv_site_packages=true
54+
become: yes
55+
environment: "{{ proxy_env | default({}) }}"
56+
when: host_distribution_version.stdout >= "24.04"
4157

4258
- name: Copy the mux simulator to test server
4359
copy:

ansible/roles/vm_set/tasks/docker.yml

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
- name: Add docker official GPG key
2-
apt_key: url=https://download.docker.com/linux/ubuntu/gpg state=present
3-
become: yes
4-
environment: "{{ proxy_env | default({}) }}"
5-
61
- name: Check docker repository
72
find:
83
paths: /etc/apt/sources.list.d/
@@ -54,6 +49,13 @@
5449
become: yes
5550
when: host_distribution_version.stdout == "22.04" and docker_repo.matched == 0
5651

52+
- name: Add docker repository for 24.04
53+
apt_repository:
54+
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu noble stable
55+
state: present
56+
become: yes
57+
when: host_distribution_version.stdout == "24.04" and docker_repo.matched == 0
58+
5759
# In ansible 2.8, there isn't update_cache_retries option in apt module, we can manually run update as a seperate and retryable step
5860
- name: Run the "apt-get update" as a separate and retryable step
5961
apt:
@@ -94,4 +96,23 @@
9496
pip: name=docker version=7.1.0 state=forcereinstall executable={{ pip_executable }}
9597
become: yes
9698
environment: "{{ proxy_env | default({}) }}"
97-
when: pip_executable=="pip3"
99+
when: pip_executable=="pip3" and host_distribution_version.stdout < "24.04"
100+
101+
- name: Update python3 packages
102+
block:
103+
- name: Ensure python3-venv is installed
104+
apt:
105+
name: python3-venv
106+
state: present
107+
update_cache: yes
108+
become: yes
109+
- name: Install python packages
110+
pip: name=docker version=7.1.0 state=forcereinstall virtualenv=/tmp/sonic-mgmt-virtualenv virtualenv_site_packages=true virtualenv_command="python3 -m venv"
111+
become: yes
112+
environment: "{{ proxy_env | default({}) }}"
113+
when: host_distribution_version.stdout >= "24.04"
114+
115+
- name: Use virtualenv for all Python scripts on Ubuntu 24.04 and newer
116+
set_fact:
117+
ansible_python_interpreter: "/tmp/sonic-mgmt-virtualenv/bin/python"
118+
when: host_distribution_version.stdout >= "24.04"

0 commit comments

Comments
 (0)