@@ -53,12 +53,63 @@ def reporthook(count, block_size, total_size):
53
53
(percent , progress_size / (1024 * 1024 ), speed , time_left ))
54
54
sys .stdout .flush ()
55
55
56
- def get_image_type ():
56
+ def get_running_image_type ():
57
+ """ Attempt to determine whether we are running an ONIE or Aboot image """
57
58
cmdline = open ('/proc/cmdline' , 'r' )
58
59
if "Aboot=" in cmdline .read ():
59
60
return IMAGE_TYPE_ABOOT
60
61
return IMAGE_TYPE_ONIE
61
62
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
+
62
113
def aboot_read_boot_config (path ):
63
114
config = collections .OrderedDict ()
64
115
with open (path ) as f :
@@ -100,7 +151,7 @@ def run_command(command):
100
151
# Returns list of installed images
101
152
def get_installed_images ():
102
153
images = []
103
- if get_image_type () == IMAGE_TYPE_ABOOT :
154
+ if get_running_image_type () == IMAGE_TYPE_ABOOT :
104
155
for filename in os .listdir (HOST_PATH ):
105
156
if filename .startswith (IMAGE_DIR_PREFIX ):
106
157
images .append (filename .replace (IMAGE_DIR_PREFIX , IMAGE_PREFIX ))
@@ -123,7 +174,7 @@ def get_current_image():
123
174
124
175
# Returns name of next boot image
125
176
def get_next_image ():
126
- if get_image_type () == IMAGE_TYPE_ABOOT :
177
+ if get_running_image_type () == IMAGE_TYPE_ABOOT :
127
178
config = open (HOST_PATH + ABOOT_BOOT_CONFIG , 'r' )
128
179
next_image = re .search ("SWI=flash:(\S+)/" , config .read ()).group (1 ).replace (IMAGE_DIR_PREFIX , IMAGE_PREFIX )
129
180
config .close ()
@@ -143,7 +194,7 @@ def get_next_image():
143
194
return next_image
144
195
145
196
def remove_image (image ):
146
- if get_image_type () == IMAGE_TYPE_ABOOT :
197
+ if get_running_image_type () == IMAGE_TYPE_ABOOT :
147
198
nextimage = get_next_image ()
148
199
current = get_current_image ()
149
200
if image == nextimage :
@@ -227,11 +278,13 @@ def cli():
227
278
@cli .command ()
228
279
@click .option ('-y' , '--yes' , is_flag = True , callback = abort_if_false ,
229
280
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" )
230
283
@click .argument ('url' )
231
- def install (url ):
284
+ def install (url , force ):
232
285
""" Install image from local binary or URL"""
233
286
cleanup_image = False
234
- if get_image_type () == IMAGE_TYPE_ABOOT :
287
+ if get_running_image_type () == IMAGE_TYPE_ABOOT :
235
288
DEFAULT_IMAGE_PATH = ABOOT_DEFAULT_IMAGE_PATH
236
289
else :
237
290
DEFAULT_IMAGE_PATH = ONIE_DEFAULT_IMAGE_PATH
@@ -248,24 +301,40 @@ def install(url):
248
301
else :
249
302
image_path = os .path .join ("./" , url )
250
303
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" )
255
309
raise click .Abort ()
256
310
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 ()
260
317
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 ( )
267
324
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
269
338
run_command ("sync;sync;sync" )
270
339
run_command ("sleep 3" ) # wait 3 seconds after sync
271
340
click .echo ('Done' )
@@ -289,16 +358,9 @@ def list():
289
358
@click .argument ('image' )
290
359
def set_default (image ):
291
360
""" 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 ()
302
364
303
365
304
366
# Set image for next boot
@@ -310,7 +372,7 @@ def set_next_boot(image):
310
372
if image not in images :
311
373
click .echo ('Image does not exist' )
312
374
sys .exit (1 )
313
- if get_image_type () == IMAGE_TYPE_ABOOT :
375
+ if get_running_image_type () == IMAGE_TYPE_ABOOT :
314
376
image_path = aboot_image_path (image )
315
377
aboot_boot_config_set (SWI = image_path )
316
378
else :
@@ -341,36 +403,12 @@ def remove(image):
341
403
@click .argument ('binary_image_path' )
342
404
def binary_version (binary_image_path ):
343
405
""" 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" )
346
409
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 )
360
410
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 )
374
412
375
413
# Remove installed images which are not current and next
376
414
@cli .command ()
@@ -395,7 +433,7 @@ def cleanup():
395
433
@cli .command ()
396
434
@click .option ('-y' , '--yes' , is_flag = True , callback = abort_if_false ,
397
435
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) " )
399
437
@click .option ('--enforce_check' , is_flag = True , help = "Enforce pending task check for docker upgrade" )
400
438
@click .option ('--tag' , type = str , help = "Tag for the new docker image" )
401
439
@click .argument ('container_name' , metavar = '<container_name>' , required = True ,
0 commit comments