1eb8dc403SDave Cobbley# Development tool - deploy/undeploy command plugin
2eb8dc403SDave Cobbley#
3eb8dc403SDave Cobbley# Copyright (C) 2014-2016 Intel Corporation
4eb8dc403SDave Cobbley#
5c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only
6eb8dc403SDave Cobbley#
7eb8dc403SDave Cobbley"""Devtool plugin containing the deploy subcommands"""
8eb8dc403SDave Cobbley
9eb8dc403SDave Cobbleyimport logging
10eb8dc403SDave Cobbleyimport os
11eb8dc403SDave Cobbleyimport shutil
12eb8dc403SDave Cobbleyimport subprocess
13eb8dc403SDave Cobbleyimport tempfile
14eb8dc403SDave Cobbley
15eb8dc403SDave Cobbleyimport bb.utils
16eb8dc403SDave Cobbleyimport argparse_oe
17eb8dc403SDave Cobbleyimport oe.types
18eb8dc403SDave Cobbley
19*73bd93f1SPatrick Williamsfrom devtool import exec_fakeroot_no_d, setup_tinfoil, check_workspace_recipe, DevtoolError
20eb8dc403SDave Cobbley
21eb8dc403SDave Cobbleylogger = logging.getLogger('devtool')
22eb8dc403SDave Cobbley
23eb8dc403SDave Cobbleydeploylist_path = '/.devtool'
24eb8dc403SDave Cobbley
25eb8dc403SDave Cobbleydef _prepare_remote_script(deploy, verbose=False, dryrun=False, undeployall=False, nopreserve=False, nocheckspace=False):
26eb8dc403SDave Cobbley    """
27eb8dc403SDave Cobbley    Prepare a shell script for running on the target to
28eb8dc403SDave Cobbley    deploy/undeploy files. We have to be careful what we put in this
29eb8dc403SDave Cobbley    script - only commands that are likely to be available on the
30eb8dc403SDave Cobbley    target are suitable (the target might be constrained, e.g. using
31eb8dc403SDave Cobbley    busybox rather than bash with coreutils).
32eb8dc403SDave Cobbley    """
33eb8dc403SDave Cobbley    lines = []
34eb8dc403SDave Cobbley    lines.append('#!/bin/sh')
35eb8dc403SDave Cobbley    lines.append('set -e')
36eb8dc403SDave Cobbley    if undeployall:
37eb8dc403SDave Cobbley        # Yes, I know this is crude - but it does work
38eb8dc403SDave Cobbley        lines.append('for entry in %s/*.list; do' % deploylist_path)
39eb8dc403SDave Cobbley        lines.append('[ ! -f $entry ] && exit')
40eb8dc403SDave Cobbley        lines.append('set `basename $entry | sed "s/.list//"`')
41eb8dc403SDave Cobbley    if dryrun:
42eb8dc403SDave Cobbley        if not deploy:
43eb8dc403SDave Cobbley            lines.append('echo "Previously deployed files for $1:"')
44eb8dc403SDave Cobbley    lines.append('manifest="%s/$1.list"' % deploylist_path)
45eb8dc403SDave Cobbley    lines.append('preservedir="%s/$1.preserve"' % deploylist_path)
46eb8dc403SDave Cobbley    lines.append('if [ -f $manifest ] ; then')
47eb8dc403SDave Cobbley    # Read manifest in reverse and delete files / remove empty dirs
48eb8dc403SDave Cobbley    lines.append('    sed \'1!G;h;$!d\' $manifest | while read file')
49eb8dc403SDave Cobbley    lines.append('    do')
50eb8dc403SDave Cobbley    if dryrun:
51eb8dc403SDave Cobbley        lines.append('        if [ ! -d $file ] ; then')
52eb8dc403SDave Cobbley        lines.append('            echo $file')
53eb8dc403SDave Cobbley        lines.append('        fi')
54eb8dc403SDave Cobbley    else:
55eb8dc403SDave Cobbley        lines.append('        if [ -d $file ] ; then')
56eb8dc403SDave Cobbley        # Avoid deleting a preserved directory in case it has special perms
57eb8dc403SDave Cobbley        lines.append('            if [ ! -d $preservedir/$file ] ; then')
58eb8dc403SDave Cobbley        lines.append('                rmdir $file > /dev/null 2>&1 || true')
59eb8dc403SDave Cobbley        lines.append('            fi')
60eb8dc403SDave Cobbley        lines.append('        else')
61eb8dc403SDave Cobbley        lines.append('            rm -f $file')
62eb8dc403SDave Cobbley        lines.append('        fi')
63eb8dc403SDave Cobbley    lines.append('    done')
64eb8dc403SDave Cobbley    if not dryrun:
65eb8dc403SDave Cobbley        lines.append('    rm $manifest')
66eb8dc403SDave Cobbley    if not deploy and not dryrun:
67eb8dc403SDave Cobbley        # May as well remove all traces
68eb8dc403SDave Cobbley        lines.append('    rmdir `dirname $manifest` > /dev/null 2>&1 || true')
69eb8dc403SDave Cobbley    lines.append('fi')
70eb8dc403SDave Cobbley
71eb8dc403SDave Cobbley    if deploy:
72eb8dc403SDave Cobbley        if not nocheckspace:
73eb8dc403SDave Cobbley            # Check for available space
74eb8dc403SDave Cobbley            # FIXME This doesn't take into account files spread across multiple
75eb8dc403SDave Cobbley            # partitions, but doing that is non-trivial
76eb8dc403SDave Cobbley            # Find the part of the destination path that exists
77eb8dc403SDave Cobbley            lines.append('checkpath="$2"')
78eb8dc403SDave Cobbley            lines.append('while [ "$checkpath" != "/" ] && [ ! -e $checkpath ]')
79eb8dc403SDave Cobbley            lines.append('do')
80eb8dc403SDave Cobbley            lines.append('    checkpath=`dirname "$checkpath"`')
81eb8dc403SDave Cobbley            lines.append('done')
82eb8dc403SDave Cobbley            lines.append(r'freespace=$(df -P $checkpath | sed -nre "s/^(\S+\s+){3}([0-9]+).*/\2/p")')
83eb8dc403SDave Cobbley            # First line of the file is the total space
84eb8dc403SDave Cobbley            lines.append('total=`head -n1 $3`')
85eb8dc403SDave Cobbley            lines.append('if [ $total -gt $freespace ] ; then')
86eb8dc403SDave Cobbley            lines.append('    echo "ERROR: insufficient space on target (available ${freespace}, needed ${total})"')
87eb8dc403SDave Cobbley            lines.append('    exit 1')
88eb8dc403SDave Cobbley            lines.append('fi')
89eb8dc403SDave Cobbley        if not nopreserve:
90eb8dc403SDave Cobbley            # Preserve any files that exist. Note that this will add to the
91eb8dc403SDave Cobbley            # preserved list with successive deployments if the list of files
92eb8dc403SDave Cobbley            # deployed changes, but because we've deleted any previously
93eb8dc403SDave Cobbley            # deployed files at this point it will never preserve anything
94eb8dc403SDave Cobbley            # that was deployed, only files that existed prior to any deploying
95eb8dc403SDave Cobbley            # (which makes the most sense)
96eb8dc403SDave Cobbley            lines.append('cat $3 | sed "1d" | while read file fsize')
97eb8dc403SDave Cobbley            lines.append('do')
98eb8dc403SDave Cobbley            lines.append('    if [ -e $file ] ; then')
99eb8dc403SDave Cobbley            lines.append('    dest="$preservedir/$file"')
100eb8dc403SDave Cobbley            lines.append('    mkdir -p `dirname $dest`')
101eb8dc403SDave Cobbley            lines.append('    mv $file $dest')
102eb8dc403SDave Cobbley            lines.append('    fi')
103eb8dc403SDave Cobbley            lines.append('done')
104eb8dc403SDave Cobbley            lines.append('rm $3')
105eb8dc403SDave Cobbley        lines.append('mkdir -p `dirname $manifest`')
106eb8dc403SDave Cobbley        lines.append('mkdir -p $2')
107eb8dc403SDave Cobbley        if verbose:
108eb8dc403SDave Cobbley            lines.append('    tar xv -C $2 -f - | tee $manifest')
109eb8dc403SDave Cobbley        else:
110eb8dc403SDave Cobbley            lines.append('    tar xv -C $2 -f - > $manifest')
111eb8dc403SDave Cobbley        lines.append('sed -i "s!^./!$2!" $manifest')
112eb8dc403SDave Cobbley    elif not dryrun:
113eb8dc403SDave Cobbley        # Put any preserved files back
114eb8dc403SDave Cobbley        lines.append('if [ -d $preservedir ] ; then')
115eb8dc403SDave Cobbley        lines.append('    cd $preservedir')
116eb8dc403SDave Cobbley        # find from busybox might not have -exec, so we don't use that
117eb8dc403SDave Cobbley        lines.append('    find . -type f | while read file')
118eb8dc403SDave Cobbley        lines.append('    do')
119eb8dc403SDave Cobbley        lines.append('        mv $file /$file')
120eb8dc403SDave Cobbley        lines.append('    done')
121eb8dc403SDave Cobbley        lines.append('    cd /')
122eb8dc403SDave Cobbley        lines.append('    rm -rf $preservedir')
123eb8dc403SDave Cobbley        lines.append('fi')
124eb8dc403SDave Cobbley
125eb8dc403SDave Cobbley    if undeployall:
126eb8dc403SDave Cobbley        if not dryrun:
127eb8dc403SDave Cobbley            lines.append('echo "NOTE: Successfully undeployed $1"')
128eb8dc403SDave Cobbley        lines.append('done')
129eb8dc403SDave Cobbley
130eb8dc403SDave Cobbley    # Delete the script itself
131eb8dc403SDave Cobbley    lines.append('rm $0')
132eb8dc403SDave Cobbley    lines.append('')
133eb8dc403SDave Cobbley
134eb8dc403SDave Cobbley    return '\n'.join(lines)
135eb8dc403SDave Cobbley
136eb8dc403SDave Cobbleydef deploy(args, config, basepath, workspace):
137eb8dc403SDave Cobbley    """Entry point for the devtool 'deploy' subcommand"""
138169d7bccSPatrick Williams    import oe.utils
139eb8dc403SDave Cobbley
140eb8dc403SDave Cobbley    check_workspace_recipe(workspace, args.recipename, checksrc=False)
141eb8dc403SDave Cobbley
142*73bd93f1SPatrick Williams    tinfoil = setup_tinfoil(basepath=basepath)
143*73bd93f1SPatrick Williams    try:
144*73bd93f1SPatrick Williams        try:
145*73bd93f1SPatrick Williams            rd = tinfoil.parse_recipe(args.recipename)
146*73bd93f1SPatrick Williams        except Exception as e:
147*73bd93f1SPatrick Williams            raise DevtoolError('Exception parsing recipe %s: %s' %
148*73bd93f1SPatrick Williams                            (args.recipename, e))
149*73bd93f1SPatrick Williams
150*73bd93f1SPatrick Williams        srcdir = rd.getVar('D')
151*73bd93f1SPatrick Williams        workdir = rd.getVar('WORKDIR')
152*73bd93f1SPatrick Williams        path = rd.getVar('PATH')
153*73bd93f1SPatrick Williams        strip_cmd = rd.getVar('STRIP')
154*73bd93f1SPatrick Williams        libdir = rd.getVar('libdir')
155*73bd93f1SPatrick Williams        base_libdir = rd.getVar('base_libdir')
156*73bd93f1SPatrick Williams        max_process = oe.utils.get_bb_number_threads(rd)
157*73bd93f1SPatrick Williams        fakerootcmd = rd.getVar('FAKEROOTCMD')
158*73bd93f1SPatrick Williams        fakerootenv = rd.getVar('FAKEROOTENV')
159*73bd93f1SPatrick Williams    finally:
160*73bd93f1SPatrick Williams        tinfoil.shutdown()
161*73bd93f1SPatrick Williams
162*73bd93f1SPatrick Williams    return deploy_no_d(srcdir, workdir, path, strip_cmd, libdir, base_libdir, max_process, fakerootcmd, fakerootenv, args)
163*73bd93f1SPatrick Williams
164*73bd93f1SPatrick Williamsdef deploy_no_d(srcdir, workdir, path, strip_cmd, libdir, base_libdir, max_process, fakerootcmd, fakerootenv, args):
165*73bd93f1SPatrick Williams    import math
166*73bd93f1SPatrick Williams    import oe.package
167*73bd93f1SPatrick Williams
168eb8dc403SDave Cobbley    try:
169eb8dc403SDave Cobbley        host, destdir = args.target.split(':')
170eb8dc403SDave Cobbley    except ValueError:
171eb8dc403SDave Cobbley        destdir = '/'
172eb8dc403SDave Cobbley    else:
173eb8dc403SDave Cobbley        args.target = host
174eb8dc403SDave Cobbley    if not destdir.endswith('/'):
175eb8dc403SDave Cobbley        destdir += '/'
176eb8dc403SDave Cobbley
177*73bd93f1SPatrick Williams    recipe_outdir = srcdir
178eb8dc403SDave Cobbley    if not os.path.exists(recipe_outdir) or not os.listdir(recipe_outdir):
179eb8dc403SDave Cobbley        raise DevtoolError('No files to deploy - have you built the %s '
180eb8dc403SDave Cobbley                        'recipe? If so, the install step has not installed '
181eb8dc403SDave Cobbley                        'any files.' % args.recipename)
182eb8dc403SDave Cobbley
183eb8dc403SDave Cobbley    if args.strip and not args.dry_run:
184eb8dc403SDave Cobbley        # Fakeroot copy to new destination
185eb8dc403SDave Cobbley        srcdir = recipe_outdir
186*73bd93f1SPatrick Williams        recipe_outdir = os.path.join(workdir, 'devtool-deploy-target-stripped')
187eb8dc403SDave Cobbley        if os.path.isdir(recipe_outdir):
188*73bd93f1SPatrick Williams            exec_fakeroot_no_d(fakerootcmd, fakerootenv, "rm -rf %s" % recipe_outdir, shell=True)
189*73bd93f1SPatrick Williams        exec_fakeroot_no_d(fakerootcmd, fakerootenv, "cp -af %s %s" % (os.path.join(srcdir, '.'), recipe_outdir), shell=True)
190*73bd93f1SPatrick Williams        os.environ['PATH'] = ':'.join([os.environ['PATH'], path or ''])
191*73bd93f1SPatrick Williams        oe.package.strip_execs(args.recipename, recipe_outdir, strip_cmd, libdir, base_libdir, max_process)
192eb8dc403SDave Cobbley
193eb8dc403SDave Cobbley    filelist = []
194c9f7865aSAndrew Geissler    inodes = set({})
195eb8dc403SDave Cobbley    ftotalsize = 0
196eb8dc403SDave Cobbley    for root, _, files in os.walk(recipe_outdir):
197eb8dc403SDave Cobbley        for fn in files:
198c9f7865aSAndrew Geissler            fstat = os.lstat(os.path.join(root, fn))
199eb8dc403SDave Cobbley            # Get the size in kiB (since we'll be comparing it to the output of du -k)
200eb8dc403SDave Cobbley            # MUST use lstat() here not stat() or getfilesize() since we don't want to
201eb8dc403SDave Cobbley            # dereference symlinks
202c9f7865aSAndrew Geissler            if fstat.st_ino in inodes:
203c9f7865aSAndrew Geissler                fsize = 0
204c9f7865aSAndrew Geissler            else:
205c9f7865aSAndrew Geissler                fsize = int(math.ceil(float(fstat.st_size)/1024))
206c9f7865aSAndrew Geissler            inodes.add(fstat.st_ino)
207eb8dc403SDave Cobbley            ftotalsize += fsize
208eb8dc403SDave Cobbley            # The path as it would appear on the target
209eb8dc403SDave Cobbley            fpath = os.path.join(destdir, os.path.relpath(root, recipe_outdir), fn)
210eb8dc403SDave Cobbley            filelist.append((fpath, fsize))
211eb8dc403SDave Cobbley
212eb8dc403SDave Cobbley    if args.dry_run:
213eb8dc403SDave Cobbley        print('Files to be deployed for %s on target %s:' % (args.recipename, args.target))
214eb8dc403SDave Cobbley        for item, _ in filelist:
215eb8dc403SDave Cobbley            print('  %s' % item)
216eb8dc403SDave Cobbley        return 0
217eb8dc403SDave Cobbley
218eb8dc403SDave Cobbley    extraoptions = ''
219eb8dc403SDave Cobbley    if args.no_host_check:
220eb8dc403SDave Cobbley        extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
221eb8dc403SDave Cobbley    if not args.show_status:
222eb8dc403SDave Cobbley        extraoptions += ' -q'
223eb8dc403SDave Cobbley
22419323693SBrad Bishop    scp_sshexec = ''
22519323693SBrad Bishop    ssh_sshexec = 'ssh'
22619323693SBrad Bishop    if args.ssh_exec:
22719323693SBrad Bishop        scp_sshexec = "-S %s" % args.ssh_exec
22819323693SBrad Bishop        ssh_sshexec = args.ssh_exec
229eb8dc403SDave Cobbley    scp_port = ''
230eb8dc403SDave Cobbley    ssh_port = ''
231eb8dc403SDave Cobbley    if args.port:
232eb8dc403SDave Cobbley        scp_port = "-P %s" % args.port
233eb8dc403SDave Cobbley        ssh_port = "-p %s" % args.port
234eb8dc403SDave Cobbley
23564c979e8SBrad Bishop    if args.key:
23664c979e8SBrad Bishop        extraoptions += ' -i %s' % args.key
23764c979e8SBrad Bishop
238eb8dc403SDave Cobbley    # In order to delete previously deployed files and have the manifest file on
239eb8dc403SDave Cobbley    # the target, we write out a shell script and then copy it to the target
240eb8dc403SDave Cobbley    # so we can then run it (piping tar output to it).
241eb8dc403SDave Cobbley    # (We cannot use scp here, because it doesn't preserve symlinks.)
242eb8dc403SDave Cobbley    tmpdir = tempfile.mkdtemp(prefix='devtool')
243eb8dc403SDave Cobbley    try:
244eb8dc403SDave Cobbley        tmpscript = '/tmp/devtool_deploy.sh'
245eb8dc403SDave Cobbley        tmpfilelist = os.path.join(os.path.dirname(tmpscript), 'devtool_deploy.list')
246eb8dc403SDave Cobbley        shellscript = _prepare_remote_script(deploy=True,
247eb8dc403SDave Cobbley                                            verbose=args.show_status,
248eb8dc403SDave Cobbley                                            nopreserve=args.no_preserve,
249eb8dc403SDave Cobbley                                            nocheckspace=args.no_check_space)
250eb8dc403SDave Cobbley        # Write out the script to a file
251eb8dc403SDave Cobbley        with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f:
252eb8dc403SDave Cobbley            f.write(shellscript)
253eb8dc403SDave Cobbley        # Write out the file list
254eb8dc403SDave Cobbley        with open(os.path.join(tmpdir, os.path.basename(tmpfilelist)), 'w') as f:
255eb8dc403SDave Cobbley            f.write('%d\n' % ftotalsize)
256eb8dc403SDave Cobbley            for fpath, fsize in filelist:
257eb8dc403SDave Cobbley                f.write('%s %d\n' % (fpath, fsize))
258eb8dc403SDave Cobbley        # Copy them to the target
25919323693SBrad Bishop        ret = subprocess.call("scp %s %s %s %s/* %s:%s" % (scp_sshexec, scp_port, extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True)
260eb8dc403SDave Cobbley        if ret != 0:
261eb8dc403SDave Cobbley            raise DevtoolError('Failed to copy script to %s - rerun with -s to '
262eb8dc403SDave Cobbley                            'get a complete error message' % args.target)
263eb8dc403SDave Cobbley    finally:
264eb8dc403SDave Cobbley        shutil.rmtree(tmpdir)
265eb8dc403SDave Cobbley
266eb8dc403SDave Cobbley    # Now run the script
267*73bd93f1SPatrick Williams    ret = exec_fakeroot_no_d(fakerootcmd, fakerootenv, 'tar cf - . | %s  %s %s %s \'sh %s %s %s %s\'' % (ssh_sshexec, ssh_port, extraoptions, args.target, tmpscript, args.recipename, destdir, tmpfilelist), cwd=recipe_outdir, shell=True)
268eb8dc403SDave Cobbley    if ret != 0:
269eb8dc403SDave Cobbley        raise DevtoolError('Deploy failed - rerun with -s to get a complete '
270eb8dc403SDave Cobbley                        'error message')
271eb8dc403SDave Cobbley
272eb8dc403SDave Cobbley    logger.info('Successfully deployed %s' % recipe_outdir)
273eb8dc403SDave Cobbley
274eb8dc403SDave Cobbley    files_list = []
275eb8dc403SDave Cobbley    for root, _, files in os.walk(recipe_outdir):
276eb8dc403SDave Cobbley        for filename in files:
277eb8dc403SDave Cobbley            filename = os.path.relpath(os.path.join(root, filename), recipe_outdir)
278eb8dc403SDave Cobbley            files_list.append(os.path.join(destdir, filename))
279eb8dc403SDave Cobbley
280eb8dc403SDave Cobbley    return 0
281eb8dc403SDave Cobbley
282eb8dc403SDave Cobbleydef undeploy(args, config, basepath, workspace):
283eb8dc403SDave Cobbley    """Entry point for the devtool 'undeploy' subcommand"""
284eb8dc403SDave Cobbley    if args.all and args.recipename:
285eb8dc403SDave Cobbley        raise argparse_oe.ArgumentUsageError('Cannot specify -a/--all with a recipe name', 'undeploy-target')
286eb8dc403SDave Cobbley    elif not args.recipename and not args.all:
287eb8dc403SDave Cobbley        raise argparse_oe.ArgumentUsageError('If you don\'t specify a recipe, you must specify -a/--all', 'undeploy-target')
288eb8dc403SDave Cobbley
289eb8dc403SDave Cobbley    extraoptions = ''
290eb8dc403SDave Cobbley    if args.no_host_check:
291eb8dc403SDave Cobbley        extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
292eb8dc403SDave Cobbley    if not args.show_status:
293eb8dc403SDave Cobbley        extraoptions += ' -q'
294eb8dc403SDave Cobbley
29519323693SBrad Bishop    scp_sshexec = ''
29619323693SBrad Bishop    ssh_sshexec = 'ssh'
29719323693SBrad Bishop    if args.ssh_exec:
29819323693SBrad Bishop        scp_sshexec = "-S %s" % args.ssh_exec
29919323693SBrad Bishop        ssh_sshexec = args.ssh_exec
300eb8dc403SDave Cobbley    scp_port = ''
301eb8dc403SDave Cobbley    ssh_port = ''
302eb8dc403SDave Cobbley    if args.port:
303eb8dc403SDave Cobbley        scp_port = "-P %s" % args.port
304eb8dc403SDave Cobbley        ssh_port = "-p %s" % args.port
305eb8dc403SDave Cobbley
306eb8dc403SDave Cobbley    args.target = args.target.split(':')[0]
307eb8dc403SDave Cobbley
308eb8dc403SDave Cobbley    tmpdir = tempfile.mkdtemp(prefix='devtool')
309eb8dc403SDave Cobbley    try:
310eb8dc403SDave Cobbley        tmpscript = '/tmp/devtool_undeploy.sh'
311eb8dc403SDave Cobbley        shellscript = _prepare_remote_script(deploy=False, dryrun=args.dry_run, undeployall=args.all)
312eb8dc403SDave Cobbley        # Write out the script to a file
313eb8dc403SDave Cobbley        with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f:
314eb8dc403SDave Cobbley            f.write(shellscript)
315eb8dc403SDave Cobbley        # Copy it to the target
31619323693SBrad Bishop        ret = subprocess.call("scp %s %s %s %s/* %s:%s" % (scp_sshexec, scp_port, extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True)
317eb8dc403SDave Cobbley        if ret != 0:
318eb8dc403SDave Cobbley            raise DevtoolError('Failed to copy script to %s - rerun with -s to '
319eb8dc403SDave Cobbley                                'get a complete error message' % args.target)
320eb8dc403SDave Cobbley    finally:
321eb8dc403SDave Cobbley        shutil.rmtree(tmpdir)
322eb8dc403SDave Cobbley
323eb8dc403SDave Cobbley    # Now run the script
32419323693SBrad Bishop    ret = subprocess.call('%s %s %s %s \'sh %s %s\'' % (ssh_sshexec, ssh_port, extraoptions, args.target, tmpscript, args.recipename), shell=True)
325eb8dc403SDave Cobbley    if ret != 0:
326eb8dc403SDave Cobbley        raise DevtoolError('Undeploy failed - rerun with -s to get a complete '
327eb8dc403SDave Cobbley                           'error message')
328eb8dc403SDave Cobbley
329eb8dc403SDave Cobbley    if not args.all and not args.dry_run:
330eb8dc403SDave Cobbley        logger.info('Successfully undeployed %s' % args.recipename)
331eb8dc403SDave Cobbley    return 0
332eb8dc403SDave Cobbley
333eb8dc403SDave Cobbley
334eb8dc403SDave Cobbleydef register_commands(subparsers, context):
335eb8dc403SDave Cobbley    """Register devtool subcommands from the deploy plugin"""
336eb8dc403SDave Cobbley
337eb8dc403SDave Cobbley    parser_deploy = subparsers.add_parser('deploy-target',
338eb8dc403SDave Cobbley                                          help='Deploy recipe output files to live target machine',
339eb8dc403SDave Cobbley                                          description='Deploys a recipe\'s build output (i.e. the output of the do_install task) to a live target machine over ssh. By default, any existing files will be preserved instead of being overwritten and will be restored if you run devtool undeploy-target. Note: this only deploys the recipe itself and not any runtime dependencies, so it is assumed that those have been installed on the target beforehand.',
340eb8dc403SDave Cobbley                                          group='testbuild')
341eb8dc403SDave Cobbley    parser_deploy.add_argument('recipename', help='Recipe to deploy')
342eb8dc403SDave Cobbley    parser_deploy.add_argument('target', help='Live target machine running an ssh server: user@hostname[:destdir]')
343eb8dc403SDave Cobbley    parser_deploy.add_argument('-c', '--no-host-check', help='Disable ssh host key checking', action='store_true')
344eb8dc403SDave Cobbley    parser_deploy.add_argument('-s', '--show-status', help='Show progress/status output', action='store_true')
345eb8dc403SDave Cobbley    parser_deploy.add_argument('-n', '--dry-run', help='List files to be deployed only', action='store_true')
346eb8dc403SDave Cobbley    parser_deploy.add_argument('-p', '--no-preserve', help='Do not preserve existing files', action='store_true')
347eb8dc403SDave Cobbley    parser_deploy.add_argument('--no-check-space', help='Do not check for available space before deploying', action='store_true')
34819323693SBrad Bishop    parser_deploy.add_argument('-e', '--ssh-exec', help='Executable to use in place of ssh')
349eb8dc403SDave Cobbley    parser_deploy.add_argument('-P', '--port', help='Specify port to use for connection to the target')
35064c979e8SBrad Bishop    parser_deploy.add_argument('-I', '--key',
351d25ed324SAndrew Geissler                               help='Specify ssh private key for connection to the target')
352eb8dc403SDave Cobbley
353eb8dc403SDave Cobbley    strip_opts = parser_deploy.add_mutually_exclusive_group(required=False)
354eb8dc403SDave Cobbley    strip_opts.add_argument('-S', '--strip',
355eb8dc403SDave Cobbley                               help='Strip executables prior to deploying (default: %(default)s). '
356eb8dc403SDave Cobbley                                    'The default value of this option can be controlled by setting the strip option in the [Deploy] section to True or False.',
357eb8dc403SDave Cobbley                               default=oe.types.boolean(context.config.get('Deploy', 'strip', default='0')),
358eb8dc403SDave Cobbley                               action='store_true')
359eb8dc403SDave Cobbley    strip_opts.add_argument('--no-strip', help='Do not strip executables prior to deploy', dest='strip', action='store_false')
360eb8dc403SDave Cobbley
361eb8dc403SDave Cobbley    parser_deploy.set_defaults(func=deploy)
362eb8dc403SDave Cobbley
363eb8dc403SDave Cobbley    parser_undeploy = subparsers.add_parser('undeploy-target',
364eb8dc403SDave Cobbley                                            help='Undeploy recipe output files in live target machine',
365eb8dc403SDave Cobbley                                            description='Un-deploys recipe output files previously deployed to a live target machine by devtool deploy-target.',
366eb8dc403SDave Cobbley                                            group='testbuild')
367eb8dc403SDave Cobbley    parser_undeploy.add_argument('recipename', help='Recipe to undeploy (if not using -a/--all)', nargs='?')
368eb8dc403SDave Cobbley    parser_undeploy.add_argument('target', help='Live target machine running an ssh server: user@hostname')
369eb8dc403SDave Cobbley    parser_undeploy.add_argument('-c', '--no-host-check', help='Disable ssh host key checking', action='store_true')
370eb8dc403SDave Cobbley    parser_undeploy.add_argument('-s', '--show-status', help='Show progress/status output', action='store_true')
371eb8dc403SDave Cobbley    parser_undeploy.add_argument('-a', '--all', help='Undeploy all recipes deployed on the target', action='store_true')
372eb8dc403SDave Cobbley    parser_undeploy.add_argument('-n', '--dry-run', help='List files to be undeployed only', action='store_true')
37319323693SBrad Bishop    parser_undeploy.add_argument('-e', '--ssh-exec', help='Executable to use in place of ssh')
374eb8dc403SDave Cobbley    parser_undeploy.add_argument('-P', '--port', help='Specify port to use for connection to the target')
37564c979e8SBrad Bishop    parser_undeploy.add_argument('-I', '--key',
376d25ed324SAndrew Geissler                               help='Specify ssh private key for connection to the target')
37764c979e8SBrad Bishop
378eb8dc403SDave Cobbley    parser_undeploy.set_defaults(func=undeploy)
379