diff options
author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2016-02-19 22:38:57 +1300 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-02-21 09:32:42 +0000 |
commit | b95c72c4dcac97b00ad46cd6b0ff0a854a0e867f (patch) | |
tree | dd753c778d8ba4b3764ebd8bb03dd76b199d2e15 | |
parent | 62989efe6d65d438f578dbeb8a8831d900dac891 (diff) | |
download | poky-b95c72c4dcac97b00ad46cd6b0ff0a854a0e867f.tar.gz |
devtool: deploy-target: write deployed files list to target
When running devtool deploy-target, we save a list of deployed files,
and this list is used by devtool undeploy-target (or the next time
deploy-target is run if the list is present, in case any files have been
renamed or deleted since the first time). We were writing this file to
the host, but it makes more sense to write the list to the target
instead, so that if we for example swap in a different board, or switch
hosts, things will work as expected.
In order to do this properly we have to construct a shell script and
ship it over to the target so we can run it. The manifest is written out
to a hidden directory in the root (/.devtool).
Fixes [YOCTO #7908].
(From OE-Core rev: a16a0c9334b785e2df896266c8911a2c7a1806b8)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | scripts/lib/devtool/deploy.py | 144 |
1 files changed, 103 insertions, 41 deletions
diff --git a/scripts/lib/devtool/deploy.py b/scripts/lib/devtool/deploy.py index d742ed3f8e..d2a314b236 100644 --- a/scripts/lib/devtool/deploy.py +++ b/scripts/lib/devtool/deploy.py | |||
@@ -1,6 +1,6 @@ | |||
1 | # Development tool - deploy/undeploy command plugin | 1 | # Development tool - deploy/undeploy command plugin |
2 | # | 2 | # |
3 | # Copyright (C) 2014-2015 Intel Corporation | 3 | # Copyright (C) 2014-2016 Intel Corporation |
4 | # | 4 | # |
5 | # This program is free software; you can redistribute it and/or modify | 5 | # This program is free software; you can redistribute it and/or modify |
6 | # it under the terms of the GNU General Public License version 2 as | 6 | # it under the terms of the GNU General Public License version 2 as |
@@ -19,10 +19,57 @@ | |||
19 | import os | 19 | import os |
20 | import subprocess | 20 | import subprocess |
21 | import logging | 21 | import logging |
22 | import tempfile | ||
23 | import shutil | ||
22 | from devtool import exec_fakeroot, setup_tinfoil, check_workspace_recipe, DevtoolError | 24 | from devtool import exec_fakeroot, setup_tinfoil, check_workspace_recipe, DevtoolError |
23 | 25 | ||
24 | logger = logging.getLogger('devtool') | 26 | logger = logging.getLogger('devtool') |
25 | 27 | ||
28 | deploylist_path = '/.devtool' | ||
29 | |||
30 | def _prepare_remote_script(deploy, verbose=False): | ||
31 | """ | ||
32 | Prepare a shell script for running on the target to | ||
33 | deploy/undeploy files. We have to be careful what we put in this | ||
34 | script - only commands that are likely to be available on the | ||
35 | target are suitable (the target might be constrained, e.g. using | ||
36 | busybox rather than bash with coreutils). | ||
37 | """ | ||
38 | lines = [] | ||
39 | lines.append('#!/bin/sh') | ||
40 | lines.append('set -e') | ||
41 | lines.append('manifest="%s/$1.list"' % deploylist_path) | ||
42 | lines.append('if [ -f $manifest ] ; then') | ||
43 | # Read manifest in reverse and delete files / remove empty dirs | ||
44 | lines.append(' sed \'1!G;h;$!d\' $manifest | while read file') | ||
45 | lines.append(' do') | ||
46 | lines.append(' if [ -d $file ] ; then') | ||
47 | lines.append(' rmdir $file > /dev/null 2>&1 || true') | ||
48 | lines.append(' else') | ||
49 | lines.append(' rm $file') | ||
50 | lines.append(' fi') | ||
51 | lines.append(' done') | ||
52 | lines.append(' rm $manifest') | ||
53 | if not deploy: | ||
54 | # May as well remove all traces | ||
55 | lines.append(' rmdir `dirname $manifest` > /dev/null 2>&1 || true') | ||
56 | lines.append('fi') | ||
57 | |||
58 | if deploy: | ||
59 | lines.append('mkdir -p `dirname $manifest`') | ||
60 | lines.append('mkdir -p $2') | ||
61 | if verbose: | ||
62 | lines.append(' tar xv -C $2 -f - | tee $manifest') | ||
63 | else: | ||
64 | lines.append(' tar xv -C $2 -f - > $manifest') | ||
65 | lines.append('sed -i "s!^./!$2!" $manifest') | ||
66 | # Delete the script itself | ||
67 | lines.append('rm $0') | ||
68 | lines.append('') | ||
69 | |||
70 | return '\n'.join(lines) | ||
71 | |||
72 | |||
26 | def deploy(args, config, basepath, workspace): | 73 | def deploy(args, config, basepath, workspace): |
27 | """Entry point for the devtool 'deploy' subcommand""" | 74 | """Entry point for the devtool 'deploy' subcommand""" |
28 | import re | 75 | import re |
@@ -36,9 +83,8 @@ def deploy(args, config, basepath, workspace): | |||
36 | destdir = '/' | 83 | destdir = '/' |
37 | else: | 84 | else: |
38 | args.target = host | 85 | args.target = host |
39 | 86 | if not destdir.endswith('/'): | |
40 | deploy_dir = os.path.join(basepath, 'target_deploy', args.target) | 87 | destdir += '/' |
41 | deploy_file = os.path.join(deploy_dir, args.recipename + '.list') | ||
42 | 88 | ||
43 | tinfoil = setup_tinfoil(basepath=basepath) | 89 | tinfoil = setup_tinfoil(basepath=basepath) |
44 | try: | 90 | try: |
@@ -59,74 +105,90 @@ def deploy(args, config, basepath, workspace): | |||
59 | print(' %s' % os.path.join(destdir, os.path.relpath(root, recipe_outdir), fn)) | 105 | print(' %s' % os.path.join(destdir, os.path.relpath(root, recipe_outdir), fn)) |
60 | return 0 | 106 | return 0 |
61 | 107 | ||
62 | if os.path.exists(deploy_file): | ||
63 | if undeploy(args, config, basepath, workspace): | ||
64 | # Error already shown | ||
65 | return 1 | ||
66 | 108 | ||
67 | extraoptions = '' | 109 | extraoptions = '' |
68 | if args.no_host_check: | 110 | if args.no_host_check: |
69 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' | 111 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' |
70 | if args.show_status: | 112 | if not args.show_status: |
71 | tarextractopts = 'xv' | ||
72 | else: | ||
73 | tarextractopts = 'x' | ||
74 | extraoptions += ' -q' | 113 | extraoptions += ' -q' |
75 | # We cannot use scp here, because it doesn't preserve symlinks | 114 | |
76 | ret = exec_fakeroot(rd, 'tar cf - . | ssh %s %s \'tar %s -C %s -f -\'' % (extraoptions, args.target, tarextractopts, destdir), cwd=recipe_outdir, shell=True) | 115 | # In order to delete previously deployed files and have the manifest file on |
116 | # the target, we write out a shell script and then copy it to the target | ||
117 | # so we can then run it (piping tar output to it). | ||
118 | # (We cannot use scp here, because it doesn't preserve symlinks.) | ||
119 | tmpdir = tempfile.mkdtemp(prefix='devtool') | ||
120 | try: | ||
121 | tmpscript = '/tmp/devtool_deploy.sh' | ||
122 | shellscript = _prepare_remote_script(deploy=True, verbose=args.show_status) | ||
123 | # Write out the script to a file | ||
124 | with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f: | ||
125 | f.write(shellscript) | ||
126 | # Copy it to the target | ||
127 | ret = subprocess.call("scp %s %s/* %s:%s" % (extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True) | ||
128 | if ret != 0: | ||
129 | raise DevtoolError('Failed to copy script to %s - rerun with -s to ' | ||
130 | 'get a complete error message' % args.target) | ||
131 | finally: | ||
132 | shutil.rmtree(tmpdir) | ||
133 | |||
134 | # Now run the script | ||
135 | ret = exec_fakeroot(rd, 'tar cf - . | ssh %s %s \'sh %s %s %s\'' % (extraoptions, args.target, tmpscript, args.recipename, destdir), cwd=recipe_outdir, shell=True) | ||
77 | if ret != 0: | 136 | if ret != 0: |
78 | raise DevtoolError('Deploy failed - rerun with -s to get a complete ' | 137 | raise DevtoolError('Deploy failed - rerun with -s to get a complete ' |
79 | 'error message') | 138 | 'error message') |
80 | 139 | ||
81 | logger.info('Successfully deployed %s' % recipe_outdir) | 140 | logger.info('Successfully deployed %s' % recipe_outdir) |
82 | 141 | ||
83 | if not os.path.exists(deploy_dir): | ||
84 | os.makedirs(deploy_dir) | ||
85 | |||
86 | files_list = [] | 142 | files_list = [] |
87 | for root, _, files in os.walk(recipe_outdir): | 143 | for root, _, files in os.walk(recipe_outdir): |
88 | for filename in files: | 144 | for filename in files: |
89 | filename = os.path.relpath(os.path.join(root, filename), recipe_outdir) | 145 | filename = os.path.relpath(os.path.join(root, filename), recipe_outdir) |
90 | files_list.append(os.path.join(destdir, filename)) | 146 | files_list.append(os.path.join(destdir, filename)) |
91 | 147 | ||
92 | with open(deploy_file, 'w') as fobj: | ||
93 | fobj.write('\n'.join(files_list)) | ||
94 | |||
95 | return 0 | 148 | return 0 |
96 | 149 | ||
97 | def undeploy(args, config, basepath, workspace): | 150 | def undeploy(args, config, basepath, workspace): |
98 | """Entry point for the devtool 'undeploy' subcommand""" | 151 | """Entry point for the devtool 'undeploy' subcommand""" |
99 | deploy_file = os.path.join(basepath, 'target_deploy', args.target, args.recipename + '.list') | ||
100 | if not os.path.exists(deploy_file): | ||
101 | raise DevtoolError('%s has not been deployed' % args.recipename) | ||
102 | |||
103 | if args.dry_run: | ||
104 | print('Previously deployed files to be un-deployed for %s on target %s:' % (args.recipename, args.target)) | ||
105 | with open(deploy_file, 'r') as f: | ||
106 | for line in f: | ||
107 | print(' %s' % line.rstrip()) | ||
108 | return 0 | ||
109 | |||
110 | extraoptions = '' | 152 | extraoptions = '' |
111 | if args.no_host_check: | 153 | if args.no_host_check: |
112 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' | 154 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' |
113 | if not args.show_status: | 155 | if not args.show_status: |
114 | extraoptions += ' -q' | 156 | extraoptions += ' -q' |
115 | 157 | ||
116 | ret = subprocess.call("scp %s %s %s:/tmp" % (extraoptions, deploy_file, args.target), shell=True) | 158 | args.target = args.target.split(':')[0] |
117 | if ret != 0: | ||
118 | raise DevtoolError('Failed to copy file list to %s - rerun with -s to ' | ||
119 | 'get a complete error message' % args.target) | ||
120 | 159 | ||
121 | ret = subprocess.call("ssh %s %s 'xargs -n1 rm -f </tmp/%s'" % (extraoptions, args.target, os.path.basename(deploy_file)), shell=True) | 160 | if args.dry_run: |
122 | if ret == 0: | 161 | listfile = os.path.join(deploylist_path, '%s.list' % args.recipename) |
123 | logger.info('Successfully undeployed %s' % args.recipename) | 162 | print('Previously deployed files to be un-deployed for %s on target %s:' % (args.recipename, args.target)) |
124 | os.remove(deploy_file) | 163 | ret = subprocess.call('ssh %s %s \'[ -f %s ] && cat %s || true\'' % (extraoptions, args.target, listfile, listfile), shell=True) |
125 | else: | 164 | if ret != 0: |
165 | raise DevtoolError('Undeploy failed - rerun with -s to get a complete ' | ||
166 | 'error message') | ||
167 | return 0 | ||
168 | |||
169 | tmpdir = tempfile.mkdtemp(prefix='devtool') | ||
170 | try: | ||
171 | tmpscript = '/tmp/devtool_undeploy.sh' | ||
172 | shellscript = _prepare_remote_script(deploy=False) | ||
173 | # Write out the script to a file | ||
174 | with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f: | ||
175 | f.write(shellscript) | ||
176 | # Copy it to the target | ||
177 | ret = subprocess.call("scp %s %s/* %s:%s" % (extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True) | ||
178 | if ret != 0: | ||
179 | raise DevtoolError('Failed to copy script to %s - rerun with -s to ' | ||
180 | 'get a complete error message' % args.target) | ||
181 | finally: | ||
182 | shutil.rmtree(tmpdir) | ||
183 | |||
184 | # Now run the script | ||
185 | ret = subprocess.call('ssh %s %s \'sh %s %s\'' % (extraoptions, args.target, tmpscript, args.recipename), shell=True) | ||
186 | if ret != 0: | ||
126 | raise DevtoolError('Undeploy failed - rerun with -s to get a complete ' | 187 | raise DevtoolError('Undeploy failed - rerun with -s to get a complete ' |
127 | 'error message') | 188 | 'error message') |
128 | 189 | ||
129 | return ret | 190 | logger.info('Successfully undeployed %s' % args.recipename) |
191 | return 0 | ||
130 | 192 | ||
131 | 193 | ||
132 | def register_commands(subparsers, context): | 194 | def register_commands(subparsers, context): |