Skip to content

Commit e2b4bd5

Browse files
jipanyangqiluo-msft
authored andcommitted
Warm reboot: Add support for docker upgrade (sonic-net#292)
* Add support for docker upgrade * Remove the extra spaces * Make pending task check optional and add function support to get docker tag from docker meta label * Use proc.returncode instead proc result for orchagent_restart_check * Remove extra spaces
1 parent f1236aa commit e2b4bd5

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

sonic_installer/main.py

+101
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,20 @@ def remove_image(image):
173173
run_command('grub-set-default --boot-directory=' + HOST_PATH + ' 0')
174174
click.echo('Image removed')
175175

176+
# TODO: Embed tag name info into docker image meta data at build time,
177+
# and extract tag name from docker image file.
178+
def get_docker_tag_name(image):
179+
# Try to get tag name from label metadata
180+
cmd = "docker inspect --format '{{.ContainerConfig.Labels.Tag}}' " + image
181+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
182+
(out, err) = proc.communicate()
183+
if proc.returncode != 0:
184+
return "unknown"
185+
tag = out.rstrip()
186+
if tag == "<no value>":
187+
return "unknown"
188+
return tag
189+
176190
# Callback for confirmation prompt. Aborts if user enters "n"
177191
def abort_if_false(ctx, param, value):
178192
if not value:
@@ -343,5 +357,92 @@ def cleanup():
343357
if image_removed == 0:
344358
click.echo("No image(s) to remove")
345359

360+
# Upgrade docker image
361+
@cli.command()
362+
@click.option('-y', '--yes', is_flag=True, callback=abort_if_false,
363+
expose_value=False, prompt='New docker image will be installed, continue?')
364+
@click.option('--cleanup_image', is_flag=True, help="Clean up old docker image")
365+
@click.option('--enforce_check', is_flag=True, help="Enforce pending task check for docker upgrade")
366+
@click.option('--tag', type=str, help="Tag for the new docker image")
367+
@click.argument('container_name', metavar='<container_name>', required=True,
368+
type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd"]))
369+
@click.argument('url')
370+
def upgrade_docker(container_name, url, cleanup_image, enforce_check, tag):
371+
""" Upgrade docker image from local binary or URL"""
372+
373+
# example image: docker-lldp-sv2:latest
374+
cmd = "docker inspect --format '{{.Config.Image}}' " + container_name
375+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
376+
(out, err) = proc.communicate()
377+
if proc.returncode != 0:
378+
sys.exit(proc.returncode)
379+
image_latest = out.rstrip()
380+
381+
# example image_name: docker-lldp-sv2
382+
cmd = "echo " + image_latest + " | cut -d ':' -f 1"
383+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
384+
image_name = proc.stdout.read().rstrip()
385+
386+
DEFAULT_IMAGE_PATH = os.path.join("/tmp/", image_name)
387+
if url.startswith('http://') or url.startswith('https://'):
388+
click.echo('Downloading image...')
389+
try:
390+
urllib.urlretrieve(url, DEFAULT_IMAGE_PATH, reporthook)
391+
except Exception, e:
392+
click.echo("Download error", e)
393+
return
394+
image_path = DEFAULT_IMAGE_PATH
395+
else:
396+
image_path = os.path.join("./", url)
397+
398+
# make sure orchagent is in clean state if swss is to be upgraded
399+
if container_name == "swss":
400+
skipPendingTaskCheck = " -s"
401+
if enforce_check:
402+
skipPendingTaskCheck = ""
403+
404+
cmd = "docker exec -it " + container_name + " orchagent_restart_check" + skipPendingTaskCheck
405+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
406+
(out, err) = proc.communicate()
407+
if proc.returncode != 0:
408+
if enforce_check:
409+
click.echo("Orchagent is not in clean state, check syslog and try later")
410+
sys.exit(proc.returncode)
411+
else:
412+
click.echo("Orchagent is not in clean state, upgrading it anyway")
413+
else:
414+
click.echo("Orchagent is in clean state and frozen for warm upgrade")
415+
416+
run_command("systemctl stop %s" % container_name)
417+
run_command("docker rm %s " % container_name)
418+
run_command("docker rmi %s " % image_latest)
419+
run_command("docker load < %s" % image_path)
420+
if tag == None:
421+
# example image: docker-lldp-sv2:latest
422+
tag = get_docker_tag_name(image_latest)
423+
run_command("docker tag %s:latest %s:%s" % (image_name, image_name, tag))
424+
run_command("systemctl restart %s" % container_name)
425+
426+
# Clean up old docker images
427+
if cleanup_image:
428+
# All images id under the image name
429+
cmd = "docker images --format '{{.ID}}' " + image_name
430+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
431+
image_id_all = proc.stdout.read()
432+
image_id_all = image_id_all.splitlines()
433+
image_id_all = set(image_id_all)
434+
435+
# this is image_id for image with "latest" tag
436+
cmd = "docker images --format '{{.ID}}' " + image_latest
437+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
438+
image_id_latest = proc.stdout.read().rstrip()
439+
440+
for id in image_id_all:
441+
if id != image_id_latest:
442+
run_command("docker rmi -f %s" % id)
443+
444+
run_command("sleep 5") # wait 5 seconds for application to sync
445+
click.echo('Done')
446+
346447
if __name__ == '__main__':
347448
cli()

0 commit comments

Comments
 (0)