Skip to content

Commit 8c5a30b

Browse files
saiarcot895gshemesh2
authored andcommitted
Add support for Ubuntu 24.04 as the host and sonic-mgmt container (sonic-net#16039)
Approach What is the motivation for this PR? When running with Ubuntu 24.04 host, many tasks from a sonic-mgmt container may fail. For example, running add-topo for a KVM testbed will fail. This is because of 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. How did you do it? Update the code in sonic-mgmt to add support for Ubuntu 24.04 as a host and for the sonic-mgmt container to be running Ubuntu 24.04. How did you verify/test it? Tested on a Ubuntu 24.04 host and sonic-mgmt container based on Ubuntu 24.04. Signed-off-by: Guy Shemesh <gshemesh@nvidia.com>
1 parent dad784d commit 8c5a30b

13 files changed

Lines changed: 150 additions & 70 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/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/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: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@
5555
become: yes
5656
when: host_distribution_version.stdout == "22.04" and docker_repo.matched == 0
5757

58+
- name: Add docker repository for 24.04
59+
apt_repository:
60+
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu noble stable
61+
state: present
62+
become: yes
63+
when: host_distribution_version.stdout == "24.04" and docker_repo.matched == 0
64+
5865
# 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
5966
- name: Run the "apt-get update" as a separate and retryable step
6067
apt:
@@ -95,4 +102,17 @@
95102
pip: name=docker version=7.1.0 state=forcereinstall executable={{ pip_executable }}
96103
become: yes
97104
environment: "{{ proxy_env | default({}) }}"
98-
when: pip_executable=="pip3"
105+
when: pip_executable=="pip3" and host_distribution_version.stdout < "24.04"
106+
107+
- name: Update python3 packages
108+
block:
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"

ansible/roles/vm_set/tasks/main.yml

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -69,35 +69,27 @@
6969

7070
- block:
7171
- name: Install necessary packages
72-
apt: pkg={{ item }} update_cache=yes cache_valid_time=86400
73-
become: yes
72+
apt:
73+
update_cache: yes
74+
cache_valid_time: 86400
75+
pkg:
76+
- ifupdown
77+
- openvswitch-switch
78+
- net-tools
79+
- bridge-utils
80+
- util-linux
81+
- iproute2
82+
- vlan
83+
- apt-transport-https
84+
- ca-certificates
85+
- curl
86+
- software-properties-common
87+
- libvirt-clients
7488
register: apt_res
7589
retries: 2
7690
delay: 30
7791
until: apt_res is success
78-
with_items:
79-
- ifupdown
80-
- qemu
81-
- openvswitch-switch
82-
- net-tools
83-
- bridge-utils
84-
- util-linux
85-
86-
- name: Install necessary packages
87-
apt: pkg={{ item }} update_cache=yes cache_valid_time=86400
8892
become: yes
89-
register: apt_res
90-
retries: 2
91-
delay: 30
92-
until: apt_res is success
93-
with_items:
94-
- iproute2
95-
- vlan
96-
- apt-transport-https
97-
- ca-certificates
98-
- curl
99-
- software-properties-common
100-
- libvirt-clients
10193

10294
- name: Install necessary packages
10395
register: apt_res
@@ -125,23 +117,18 @@
125117
- libvirt-daemon-system
126118
- qemu-system-x86
127119
become: yes
128-
when: host_distribution_version.stdout == "20.04" or host_distribution_version.stdout == "22.04"
120+
when: host_distribution_version.stdout >= "20.04"
129121
when: package_installation|bool
130122

131123
- name: Get default pip_executable
132124
set_fact:
133125
pip_executable: pip
134-
when: pip_executable is not defined and host_distribution_version.stdout != "20.04" and host_distribution_version.stdout != "22.04"
126+
when: pip_executable is not defined and host_distribution_version.stdout < "20.04"
135127

136128
- name: Get default pip_executable
137129
set_fact:
138130
pip_executable: pip3
139-
when: pip_executable is not defined and (host_distribution_version.stdout == "20.04" or host_distribution_version.stdout == "22.04")
140-
141-
- name: remove old python packages
142-
pip: name=docker-py state=absent executable={{ pip_executable }}
143-
environment: "{{ proxy_env | default({}) }}"
144-
ignore_errors: yes
131+
when: pip_executable is not defined and (host_distribution_version.stdout >= "20.04")
145132

146133
- include_tasks: docker.yml
147134
when: package_installation|bool
@@ -150,7 +137,13 @@
150137
pip: name=requests version=2.32.3 state=present executable={{ pip_executable }}
151138
become: yes
152139
environment: "{{ proxy_env | default({}) }}"
153-
when: pip_executable=="pip3"
140+
when: pip_executable=="pip3" and host_distribution_version.stdout < "24.04"
141+
142+
- name: Install requests package
143+
pip: name=requests version=2.32.3 state=present virtualenv=/tmp/sonic-mgmt-virtualenv virtualenv_site_packages=true virtualenv_command="python3 -m venv"
144+
become: yes
145+
environment: "{{ proxy_env | default({}) }}"
146+
when: host_distribution_version.stdout >= "24.04"
154147

155148
- name: Ensure {{ ansible_user }} in docker,sudo group
156149
user:
@@ -165,7 +158,7 @@
165158
append: yes
166159
groups: libvirt
167160
become: yes
168-
when: host_distribution_version.stdout == "20.04" or host_distribution_version.stdout == "22.04"
161+
when: host_distribution_version.stdout >= "20.04"
169162

170163
- name: Install br_netfilter kernel module
171164
become: yes
@@ -223,7 +216,7 @@
223216
# root_path is supposed to be absolute path.
224217
- set_fact:
225218
root_path: "{{ home_path + '/' + root_path }}"
226-
when: "not '{{ root_path }}'.startswith('/')"
219+
when: "not root_path.startswith('/')"
227220

228221
- debug: msg="home_path = {{ home_path }} root_path = {{ root_path }}"
229222

@@ -382,3 +375,8 @@
382375
loop_control:
383376
loop_var: dut_name
384377
when: duts_name is defined
378+
379+
- name: Use virtualenv for all Python scripts on Ubuntu 24.04 and newer
380+
set_fact:
381+
ansible_python_interpreter: "/tmp/sonic-mgmt-virtualenv/bin/python"
382+
when: host_distribution_version.stdout >= "24.04"

tests/arp/test_unknown_mac.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def initClassVars(func):
3030
"""
3131
Automatically assign instance variables. currently handles only arg list
3232
"""
33-
names, varargs, keywords, defaults = inspect.getargspec(func)
33+
signature = inspect.signature(func)
34+
names = list(signature.parameters.keys())
3435

3536
@functools.wraps(func)
3637
def wrapper(self, *args):

tests/common/cache/facts_cache.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,8 @@ def _get_default_zone(function, func_args, func_kargs):
209209
raise ValueError("Failed to get attribute 'hostname' of type string from instance of type %s."
210210
% type(func_args[0]))
211211
zone = hostname
212-
if sys.version_info.major > 2:
213-
arg_names = inspect.getfullargspec(function)[0]
214-
else:
215-
arg_names = inspect.getargspec(function)[0]
212+
signature = inspect.signature(function)
213+
arg_names = list(signature.parameters.keys())
216214
if 'namespace' in arg_names:
217215
try:
218216
index = arg_names.index('namespace')

tests/common/connections/base_console_conn.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import logging
66

77
from netmiko.cisco_base_connection import CiscoBaseConnection
8-
from netmiko.ssh_exception import NetMikoAuthenticationException
8+
try:
9+
from netmiko.ssh_exception import NetMikoAuthenticationException
10+
except ImportError:
11+
from netmiko.exceptions import NetMikoAuthenticationException
912

1013
# For interactive shell
1114
import sys

tests/common/connections/ssh_console_conn.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import time
22
import re
33
from .base_console_conn import CONSOLE_SSH_DIGI_CONFIG, BaseConsoleConn, CONSOLE_SSH
4-
from netmiko.ssh_exception import NetMikoAuthenticationException
4+
try:
5+
from netmiko.ssh_exception import NetMikoAuthenticationException
6+
except ImportError:
7+
from netmiko.exceptions import NetMikoAuthenticationException
58
from paramiko.ssh_exception import SSHException
69

710

tests/common/connections/telnet_console_conn.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import time
22
import re
33
from .base_console_conn import BaseConsoleConn
4-
from netmiko.ssh_exception import NetMikoAuthenticationException
4+
try:
5+
from netmiko.ssh_exception import NetMikoAuthenticationException
6+
except ImportError:
7+
from netmiko.exceptions import NetMikoAuthenticationException
58

69

710
class TelnetConsoleConn(BaseConsoleConn):

0 commit comments

Comments
 (0)