Skip to content

Commit 50b7256

Browse files
authored
[sonic_installer] If asked to install an image which is already installed, simply set as default (#534)
* [sonic_installer] If asked to install an image which is already installed, just set as default * Update commands for retrieving SONiC version from Aboot image files
1 parent d823062 commit 50b7256

File tree

1 file changed

+98
-60
lines changed

1 file changed

+98
-60
lines changed

sonic_installer/main.py

+98-60
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,63 @@ def reporthook(count, block_size, total_size):
5353
(percent, progress_size / (1024 * 1024), speed, time_left))
5454
sys.stdout.flush()
5555

56-
def get_image_type():
56+
def get_running_image_type():
57+
""" Attempt to determine whether we are running an ONIE or Aboot image """
5758
cmdline = open('/proc/cmdline', 'r')
5859
if "Aboot=" in cmdline.read():
5960
return IMAGE_TYPE_ABOOT
6061
return IMAGE_TYPE_ONIE
6162

63+
# Returns None if image doesn't exist or isn't a regular file
64+
def get_binary_image_type(binary_image_path):
65+
""" Attempt to determine whether this is an ONIE or Aboot image file """
66+
if not os.path.isfile(binary_image_path):
67+
return None
68+
69+
with open(binary_image_path) as f:
70+
# Aboot file is a zip archive; check the start of the file for the zip magic number
71+
if f.read(4) == "\x50\x4b\x03\x04":
72+
return IMAGE_TYPE_ABOOT
73+
return IMAGE_TYPE_ONIE
74+
75+
# Returns None if image doesn't exist or doesn't appear to be a valid SONiC image file
76+
def get_binary_image_version(binary_image_path):
77+
binary_type = get_binary_image_type(binary_image_path)
78+
if not binary_type:
79+
return None
80+
elif binary_type == IMAGE_TYPE_ABOOT:
81+
p1 = subprocess.Popen(["unzip", "-p", binary_image_path, "boot0"], stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
82+
p2 = subprocess.Popen(["grep", "-m 1", "^image_name"], stdin=p1.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
83+
p3 = subprocess.Popen(["sed", "-n", r"s/^image_name=\"\image-\(.*\)\"$/\1/p"], stdin=p2.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
84+
else:
85+
p1 = subprocess.Popen(["cat", "-v", binary_image_path], stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
86+
p2 = subprocess.Popen(["grep", "-m 1", "^image_version"], stdin=p1.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
87+
p3 = subprocess.Popen(["sed", "-n", r"s/^image_version=\"\(.*\)\"$/\1/p"], stdin=p2.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
88+
89+
stdout = p3.communicate()[0]
90+
p3.wait()
91+
version_num = stdout.rstrip('\n')
92+
93+
# If we didn't read a version number, this doesn't appear to be a valid SONiC image file
94+
if len(version_num) == 0:
95+
return None
96+
97+
return IMAGE_PREFIX + version_num
98+
99+
# Sets specified image as default image to boot from
100+
def set_default_image(image):
101+
images = get_installed_images()
102+
if image not in images:
103+
return False
104+
105+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
106+
image_path = aboot_image_path(image)
107+
aboot_boot_config_set(SWI=image_path, SWI_DEFAULT=image_path)
108+
else:
109+
command = 'grub-set-default --boot-directory=' + HOST_PATH + ' ' + str(images.index(image))
110+
run_command(command)
111+
return True
112+
62113
def aboot_read_boot_config(path):
63114
config = collections.OrderedDict()
64115
with open(path) as f:
@@ -100,7 +151,7 @@ def run_command(command):
100151
# Returns list of installed images
101152
def get_installed_images():
102153
images = []
103-
if get_image_type() == IMAGE_TYPE_ABOOT:
154+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
104155
for filename in os.listdir(HOST_PATH):
105156
if filename.startswith(IMAGE_DIR_PREFIX):
106157
images.append(filename.replace(IMAGE_DIR_PREFIX, IMAGE_PREFIX))
@@ -123,7 +174,7 @@ def get_current_image():
123174

124175
# Returns name of next boot image
125176
def get_next_image():
126-
if get_image_type() == IMAGE_TYPE_ABOOT:
177+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
127178
config = open(HOST_PATH + ABOOT_BOOT_CONFIG, 'r')
128179
next_image = re.search("SWI=flash:(\S+)/", config.read()).group(1).replace(IMAGE_DIR_PREFIX, IMAGE_PREFIX)
129180
config.close()
@@ -143,7 +194,7 @@ def get_next_image():
143194
return next_image
144195

145196
def remove_image(image):
146-
if get_image_type() == IMAGE_TYPE_ABOOT:
197+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
147198
nextimage = get_next_image()
148199
current = get_current_image()
149200
if image == nextimage:
@@ -227,11 +278,13 @@ def cli():
227278
@cli.command()
228279
@click.option('-y', '--yes', is_flag=True, callback=abort_if_false,
229280
expose_value=False, prompt='New image will be installed, continue?')
281+
@click.option('-f', '--force', is_flag=True,
282+
help="Force installation of an image of a type which differs from that of the current running image")
230283
@click.argument('url')
231-
def install(url):
284+
def install(url, force):
232285
""" Install image from local binary or URL"""
233286
cleanup_image = False
234-
if get_image_type() == IMAGE_TYPE_ABOOT:
287+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
235288
DEFAULT_IMAGE_PATH = ABOOT_DEFAULT_IMAGE_PATH
236289
else:
237290
DEFAULT_IMAGE_PATH = ONIE_DEFAULT_IMAGE_PATH
@@ -248,24 +301,40 @@ def install(url):
248301
else:
249302
image_path = os.path.join("./", url)
250303

251-
# Verify that the local file exists and is a regular file
252-
# TODO: Verify the file is a *proper SONiC image file*
253-
if not os.path.isfile(image_path):
254-
click.echo("Image file '{}' does not exist or is not a regular file. Aborting...".format(image_path))
304+
running_image_type = get_running_image_type()
305+
binary_image_type = get_binary_image_type(image_path)
306+
binary_image_version = get_binary_image_version(image_path)
307+
if not binary_image_type or not binary_image_version:
308+
click.echo("Image file does not exist or is not a valid SONiC image file")
255309
raise click.Abort()
256310

257-
if get_image_type() == IMAGE_TYPE_ABOOT:
258-
run_command("/usr/bin/unzip -od /tmp %s boot0" % image_path)
259-
run_command("swipath=%s target_path=/host sonic_upgrade=1 . /tmp/boot0" % image_path)
311+
# Is this version already installed?
312+
if binary_image_version in get_installed_images():
313+
click.echo("Image {} is already installed. Setting it as default...".format(binary_image_version))
314+
if not set_default_image(binary_image_version):
315+
click.echo('Error: Failed to set image as default')
316+
raise click.Abort()
260317
else:
261-
os.chmod(image_path, stat.S_IXUSR)
262-
run_command(image_path)
263-
run_command('grub-set-default --boot-directory=' + HOST_PATH + ' 0')
264-
run_command("rm -rf /host/old_config")
265-
# copy directories and preserve original file structure, attributes and associated metadata
266-
run_command("cp -ar /etc/sonic /host/old_config")
318+
# Verify that the binary image is of the same type as the running image
319+
if (binary_image_type != running_image_type) and not force:
320+
click.echo("Image file '{}' is of a different type than running image.\n" +
321+
"If you are sure you want to install this image, use -f|--force.\n" +
322+
"Aborting...".format(image_path))
323+
raise click.Abort()
267324

268-
# sync filesystem, keep at last step.
325+
click.echo("Installing image {} and setting it as default...".format(binary_image_version))
326+
if running_image_type == IMAGE_TYPE_ABOOT:
327+
run_command("/usr/bin/unzip -od /tmp %s boot0" % image_path)
328+
run_command("swipath=%s target_path=/host sonic_upgrade=1 . /tmp/boot0" % image_path)
329+
else:
330+
os.chmod(image_path, stat.S_IXUSR)
331+
run_command(image_path)
332+
run_command('grub-set-default --boot-directory=' + HOST_PATH + ' 0')
333+
run_command("rm -rf /host/old_config")
334+
# copy directories and preserve original file structure, attributes and associated metadata
335+
run_command("cp -ar /etc/sonic /host/old_config")
336+
337+
# Finally, sync filesystem
269338
run_command("sync;sync;sync")
270339
run_command("sleep 3") # wait 3 seconds after sync
271340
click.echo('Done')
@@ -289,16 +358,9 @@ def list():
289358
@click.argument('image')
290359
def set_default(image):
291360
""" Choose image to boot from by default """
292-
images = get_installed_images()
293-
if image not in images:
294-
click.echo('Image does not exist')
295-
sys.exit(1)
296-
if get_image_type() == IMAGE_TYPE_ABOOT:
297-
image_path = aboot_image_path(image)
298-
aboot_boot_config_set(SWI=image_path, SWI_DEFAULT=image_path)
299-
else:
300-
command = 'grub-set-default --boot-directory=' + HOST_PATH + ' ' + str(images.index(image))
301-
run_command(command)
361+
if not set_default_image(image):
362+
click.echo('Error: Image does not exist')
363+
raise click.Abort()
302364

303365

304366
# Set image for next boot
@@ -310,7 +372,7 @@ def set_next_boot(image):
310372
if image not in images:
311373
click.echo('Image does not exist')
312374
sys.exit(1)
313-
if get_image_type() == IMAGE_TYPE_ABOOT:
375+
if get_running_image_type() == IMAGE_TYPE_ABOOT:
314376
image_path = aboot_image_path(image)
315377
aboot_boot_config_set(SWI=image_path)
316378
else:
@@ -341,36 +403,12 @@ def remove(image):
341403
@click.argument('binary_image_path')
342404
def binary_version(binary_image_path):
343405
""" Get version from local binary image file """
344-
if not os.path.isfile(binary_image_path):
345-
click.echo('Image file does not exist')
406+
binary_version = get_binary_image_version(binary_image_path)
407+
if not binary_version:
408+
click.echo("Image file does not exist or is not a valid SONiC image file")
346409
sys.exit(1)
347-
348-
# Attempt to determine whether this is an ONIE or Aboot image
349-
is_aboot = False
350-
351-
with open(binary_image_path) as f:
352-
# Aboot file is a zip archive; check the start of the file for the zip magic number
353-
if f.read(4) == "\x50\x4b\x03\x04":
354-
is_aboot = True
355-
356-
if is_aboot:
357-
p1 = subprocess.Popen(["unzip", "-p", binary_image_path, "boot0"], stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
358-
p2 = subprocess.Popen(["grep", "-m 1", "^image_path"], stdin=p1.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
359-
p3 = subprocess.Popen(["sed", "-n", r"s/^image_path=\"\$target_path\/image-\(.*\)\"$/\1/p"], stdin=p2.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
360410
else:
361-
p1 = subprocess.Popen(["cat", "-v", binary_image_path], stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
362-
p2 = subprocess.Popen(["grep", "-m 1", "^image_version"], stdin=p1.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
363-
p3 = subprocess.Popen(["sed", "-n", r"s/^image_version=\"\(.*\)\"$/\1/p"], stdin=p2.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe)
364-
365-
stdout = p3.communicate()[0]
366-
p3.wait()
367-
version_num = stdout.rstrip('\n')
368-
369-
if len(version_num) == 0:
370-
click.echo("File does not appear to be a vaild SONiC image file")
371-
sys.exit(1)
372-
373-
click.echo(IMAGE_PREFIX + version_num)
411+
click.echo(binary_version)
374412

375413
# Remove installed images which are not current and next
376414
@cli.command()
@@ -395,7 +433,7 @@ def cleanup():
395433
@cli.command()
396434
@click.option('-y', '--yes', is_flag=True, callback=abort_if_false,
397435
expose_value=False, prompt='New docker image will be installed, continue?')
398-
@click.option('--cleanup_image', is_flag=True, help="Clean up old docker image")
436+
@click.option('--cleanup_image', is_flag=True, help="Clean up old docker image(s)")
399437
@click.option('--enforce_check', is_flag=True, help="Enforce pending task check for docker upgrade")
400438
@click.option('--tag', type=str, help="Tag for the new docker image")
401439
@click.argument('container_name', metavar='<container_name>', required=True,

0 commit comments

Comments
 (0)