@@ -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+
62113def 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
101152def 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
125176def 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
145196def 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' )
290359def 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' )
342404def 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