Skip to content

Commit fec6080

Browse files
authored
DBUS API for GNOI System.SetPackage (#171)
Create image_service class for supporting GNOI implementation for SetPackage. TESTED: unit test.
1 parent 13a5419 commit fec6080

File tree

2 files changed

+507
-0
lines changed

2 files changed

+507
-0
lines changed

host_modules/image_service.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
This module provides services related to SONiC images, including:
3+
1) Downloading images
4+
2) Installing images
5+
3) Calculating checksums for images
6+
"""
7+
8+
import errno
9+
import hashlib
10+
import logging
11+
import os
12+
import requests
13+
import stat
14+
import subprocess
15+
16+
from host_modules import host_service
17+
import tempfile
18+
19+
MOD_NAME = "image_service"
20+
21+
DEFAULT_IMAGE_SAVE_AS = "/tmp/downloaded-sonic.bin"
22+
23+
logger = logging.getLogger(__name__)
24+
25+
26+
class ImageService(host_service.HostModule):
27+
"""DBus endpoint that handles downloading and installing SONiC images"""
28+
29+
@host_service.method(
30+
host_service.bus_name(MOD_NAME), in_signature="ss", out_signature="is"
31+
)
32+
def download(self, image_url, save_as):
33+
"""
34+
Download a SONiC image.
35+
36+
Args:
37+
image_url: url for remote image.
38+
save_as: local path for the downloaded image. The directory must exist and be *all* writable.
39+
"""
40+
logger.info("Download new sonic image from {} as {}".format(image_url, save_as))
41+
# Check if the directory exists, is absolute and has write permission.
42+
if not os.path.isabs(save_as):
43+
logger.error("The path {} is not an absolute path".format(save_as))
44+
return errno.EINVAL, "Path is not absolute"
45+
dir = os.path.dirname(save_as)
46+
if not os.path.isdir(dir):
47+
logger.error("Directory {} does not exist".format(dir))
48+
return errno.ENOENT, "Directory does not exist"
49+
st_mode = os.stat(dir).st_mode
50+
if (
51+
not (st_mode & stat.S_IWUSR)
52+
or not (st_mode & stat.S_IWGRP)
53+
or not (st_mode & stat.S_IWOTH)
54+
):
55+
logger.error("Directory {} is not all writable {}".format(dir, st_mode))
56+
return errno.EACCES, "Directory is not all writable"
57+
try:
58+
response = requests.get(image_url, stream=True)
59+
if response.status_code != 200:
60+
logger.error(
61+
"Failed to download image: HTTP status code {}".format(
62+
response.status_code
63+
)
64+
)
65+
return errno.EIO, "HTTP error: {}".format(response.status_code)
66+
67+
with tempfile.NamedTemporaryFile(dir="/tmp", delete=False) as tmp_file:
68+
for chunk in response.iter_content(chunk_size=8192):
69+
tmp_file.write(chunk)
70+
temp_file_path = tmp_file.name
71+
os.replace(temp_file_path, save_as)
72+
return 0, "Download successful"
73+
except Exception as e:
74+
logger.error("Failed to write downloaded image to disk: {}".format(e))
75+
return errno.EIO, str(e)
76+
77+
@host_service.method(
78+
host_service.bus_name(MOD_NAME), in_signature="s", out_signature="is"
79+
)
80+
def install(self, where):
81+
"""
82+
Install a a sonic image:
83+
84+
Args:
85+
where: either a local path or a remote url pointing to the image.
86+
"""
87+
logger.info("Using sonic-installer to install the image at {}.".format(where))
88+
cmd = ["/usr/local/bin/sonic-installer", "install", "-y", where]
89+
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
90+
msg = ""
91+
if result.returncode:
92+
lines = result.stderr.decode().split("\n")
93+
for line in lines:
94+
if "Error" in line:
95+
msg = line
96+
break
97+
return result.returncode, msg
98+
99+
@host_service.method(
100+
host_service.bus_name(MOD_NAME), in_signature="ss", out_signature="is"
101+
)
102+
def checksum(self, file_path, algorithm):
103+
"""
104+
Calculate the checksum of a file.
105+
106+
Args:
107+
file_path: path to the file.
108+
algorithm: checksum algorithm to use (sha256, sha512, md5).
109+
"""
110+
111+
logger.info("Calculating {} checksum for file {}".format(algorithm, file_path))
112+
113+
if not os.path.isfile(file_path):
114+
logger.error("File {} does not exist".format(file_path))
115+
return errno.ENOENT, "File does not exist"
116+
117+
hash_func = None
118+
if algorithm == "sha256":
119+
hash_func = hashlib.sha256()
120+
elif algorithm == "sha512":
121+
hash_func = hashlib.sha512()
122+
elif algorithm == "md5":
123+
hash_func = hashlib.md5()
124+
else:
125+
logger.error("Unsupported algorithm: {}".format(algorithm))
126+
return errno.EINVAL, "Unsupported algorithm"
127+
128+
try:
129+
with open(file_path, "rb") as f:
130+
for chunk in iter(lambda: f.read(4096), b""):
131+
hash_func.update(chunk)
132+
return 0, hash_func.hexdigest()
133+
except Exception as e:
134+
logger.error("Failed to calculate checksum: {}".format(e))
135+
return errno.EIO, str(e)

0 commit comments

Comments
 (0)