def command(shell, args):
     # get installed commands
     installed = set(shell.commands.keys())
     # get list of commands to uninstall / not-uninstallable
     requested_to_uninstall = set(args)
     to_uninstall = requested_to_uninstall.intersection(installed)
     not_uninstallable = requested_to_uninstall.difference(installed)
     need_reload = False
     # not uninstallable
     for cmd in not_uninstallable:
         dtslogger.warn('The command `%s` cannot be found.' % cmd)
     # uninstall
     for cmd in to_uninstall:
         dtslogger.info('Removing command `%s`...' % cmd)
         shell.disable_command(cmd)
         need_reload = True
         dtslogger.info('Successfully completed calibration!')
         print('Done!')
     # update list of commands
     if need_reload:
         print('Updating index...')
         shell.reload_commands()
         print('Done!')
     else:
         print('Nothing to do.')
     return True
Esempio n. 2
0
def run_image_on_duckiebot(image_name, duckiebot_name, env=None, volumes=None):
    duckiebot_ip = get_duckiebot_ip(duckiebot_name)
    duckiebot_client = get_remote_client(duckiebot_ip)
    env_vars = default_env(duckiebot_name, duckiebot_ip)

    if env is not None:
        env_vars.update(env)

    dtslogger.info("Running %s with environment: %s" % (image_name, env_vars))

    params = {
        'image': image_name,
        'remove': True,
        'network_mode': 'host',
        'privileged': True,
        'detach': True,
        'environment': env_vars
    }

    if volumes is not None:
        params['volumes'] = volumes

    # Make sure we are not already running the same image
    if all(elem.image != image_name
           for elem in duckiebot_client.containers.list()):
        return duckiebot_client.containers.run(**params)
    else:
        dtslogger.warn(
            'Container with image %s is already running on %s, skipping...' %
            (image_name, duckiebot_name))
Esempio n. 3
0
def remove_if_running(client, container_name):
    try:
        container = client.containers.get(container_name)
        dtslogger.info("%s already running - stopping it first.." %
                       container_name)
        stop_container(container)
        dtslogger.info("removing %s" % container_name)
        remove_container(container)
    except Exception as e:
        dtslogger.warn("couldn't remove existing container: %s" % e)
 def command(shell, args):
     if shell.commands_path_leave_alone:
         dtslogger.warn(
             'Will not update the commands because the path was set explicitly.'
         )
     else:
         if shell.update_commands():
             dtslogger.info('Duckietown Shell commands updated.')
         else:
             dtslogger.error('Update was not successful.')
Esempio n. 5
0
def run_gui_controller(hostname, image):
    client = check_docker_environment()
    container_name = "joystick_gui_%s" % hostname
    remove_if_running(client, container_name)
    duckiebot_ip = get_duckiebot_ip(hostname)
    env = {
        'HOSTNAME': hostname,
        'ROS_MASTER': hostname,
        'DUCKIEBOT_NAME': hostname,
        'ROS_MASTER_URI': 'http://%s:11311' % duckiebot_ip
    }

    env['QT_X11_NO_MITSHM'] = 1

    volumes = {}

    subprocess.call(["xhost", "+"])

    p = platform.system().lower()
    if 'darwin' in p:
        dtslogger.warn(
            "We can try but running the joystick gui on MacOSx is not expected to work..."
        )
        env['DISPLAY'] = 'host.docker.internal:0'
        volumes = {'/tmp/.X11-unix': {'bind': '/tmp/.X11-unix', 'mode': 'rw'}}
    else:
        env['DISPLAY'] = os.environ['DISPLAY']

    dtslogger.info("Running %s on localhost with environment vars: %s" %
                   (container_name, env))

    if 'master19' in image:
        image = "duckietown/rpi-duckiebot-base:master19-no-arm"
        cmd = "python misc/virtualJoy/virtualJoy.py %s" % hostname
    elif 'daffy' in image:
        image = "duckietown/dt-core:daffy-amd64"
        cmd = "roslaunch virtual_joystick virtual_joystick_gui.launch veh:=%s" % hostname

    params = {
        'image': image,
        'name': container_name,
        'network_mode': 'host',
        'environment': env,
        'privileged': True,
        'stdin_open': True,
        'tty': True,
        'command': cmd,
        'detach': True,
        'volumes': volumes
    }

    container = client.containers.run(**params)
    cmd = 'docker attach %s' % container_name
    start_command_in_subprocess(cmd)
Esempio n. 6
0
def run_image_on_localhost(image_name,
                           duckiebot_name,
                           container_name,
                           env=None,
                           volumes=None):
    duckiebot_ip = get_duckiebot_ip(duckiebot_name)
    local_client = check_docker_environment()

    env_vars = default_env(duckiebot_name, duckiebot_ip)

    if env is not None:
        env_vars.update(env)

    try:
        container = local_client.containers.get(container_name)
        dtslogger.info("an image already on localhost - stopping it first..")
        stop_container(container)
        remove_container(container)
    except Exception as e:
        dtslogger.warn("coulgn't remove existing container: %s" % e)

    dtslogger.info("Running %s on localhost with environment vars: %s" %
                   (image_name, env_vars))

    params = {
        'image': image_name,
        'remove': True,
        'network_mode': 'host',
        'privileged': True,
        'detach': True,
        'tty': True,
        'name': container_name,
        'environment': env_vars
    }

    if volumes is not None:
        params['volumes'] = volumes

    new_local_container = local_client.containers.run(**params)
    return new_local_container
Esempio n. 7
0
    def command(shell, args):

        prog = 'dts duckiebot calibrate_extrinsics DUCKIEBOT_NAME'
        usage = """
Calibrate: 

    %(prog)s
"""

        parser = argparse.ArgumentParser(prog=prog, usage=usage)
        parser.add_argument('hostname',
                            default=None,
                            help='Name of the Duckiebot to calibrate')
        parser.add_argument('--base_image',
                            dest='image',
                            default="duckietown/rpi-duckiebot-base:master19")
        parser.add_argument(
            '--no_verification',
            action='store_true',
            default=False,
            help="If you don't have a lane you can skip the verificaiton step")

        parsed_args = parser.parse_args(args)
        hostname = parsed_args.hostname
        duckiebot_ip = get_duckiebot_ip(hostname)
        duckiebot_client = get_remote_client(duckiebot_ip)

        calibration_container_name = "extrinsic_calibration"
        validation_container_name = "extrinsic_calibration_validation"
        remove_if_running(duckiebot_client, calibration_container_name)
        remove_if_running(duckiebot_client, validation_container_name)

        # need to temporarily pause the image streaming from the robot
        try:
            duckiebot_containers = duckiebot_client.containers.list()
            interface_container_found = False
            for c in duckiebot_containers:
                if 'duckiebot-interface' in c.name:
                    interface_container_found = True
                    interface_container = c
                    dtslogger.info("Temporarily stopping image streaming...")
                    interface_container.stop()
        except Exception as e:
            dtslogger.warn(
                "Not sure if the duckiebot-interface is running because we got and exception when trying: %s"
                % e)

        image = parsed_args.image

        timestamp = datetime.date.today().strftime('%Y%m%d%H%M%S')

        raw_input(
            "{}\nPlace the Duckiebot on the calibration patterns and press ENTER."
            .format('*' * 20))
        log_file = 'out-calibrate-extrinsics-%s-%s' % (hostname, timestamp)
        rosrun_params = '-o /data/{0} > /data/{0}.log'.format(log_file)
        ros_pkg = 'complete_image_pipeline calibrate_extrinsics'
        start_command = 'rosrun {0} {1}'.format(ros_pkg, rosrun_params)
        dtslogger.info('Running command: {}'.format(start_command))

        env = default_env(hostname, duckiebot_ip)

        duckiebot_client.containers.run(image=image,
                                        name=calibration_container_name,
                                        privileged=True,
                                        network_mode='host',
                                        volumes=bind_duckiebot_data_dir(),
                                        command="/bin/bash -c '%s'" %
                                        start_command,
                                        environment=env)

        if not parsed_args.no_verification:
            raw_input(
                "{}\nPlace the Duckiebot in a lane and press ENTER.".format(
                    '*' * 20))
            log_file = 'out-pipeline-%s-%s' % (hostname, timestamp)
            rosrun_params = '-o /data/{0} > /data/{0}.log'.format(log_file)
            ros_pkg = 'complete_image_pipeline single_image_pipeline'
            start_command = 'rosrun {0} {1}'.format(ros_pkg, rosrun_params)
            dtslogger.info('Running command: {}'.format(start_command))

            duckiebot_client.containers.run(image=image,
                                            name=validation_container_name,
                                            privileged=True,
                                            network_mode='host',
                                            volumes=bind_duckiebot_data_dir(),
                                            command="/bin/bash -c '%s'" %
                                            start_command,
                                            environment=env)

        # restart the camera streaming
        try:
            all_duckiebot_containers = duckiebot_client.containers.list(
                all=True)
            found = False
            for c in all_duckiebot_containers:
                if 'duckiebot-interface' in c.name:
                    found = True
                    dtslogger.info("Restarting image streaming...")
                    c.start()
        except Exception as e:
            dtslogger.warn(
                "Not sure if the duckiebot-interface is running because we got and exception when trying: %s"
                % e)
Esempio n. 8
0
    def command(shell, args):

        prog = 'dts duckiebot calibrate_intrinsics DUCKIEBOT_NAME'
        usage = """
Calibrate: 

    %(prog)s
"""

        parser = argparse.ArgumentParser(prog=prog, usage=usage)
        parser.add_argument('hostname', default=None, help='Name of the Duckiebot to calibrate')
        parser.add_argument('--base_image', dest='image',
                            default="duckietown/rpi-duckiebot-base:master19-no-arm")
        parser.add_argument('--debug', action='store_true', default=False,
                            help="will enter you into the running container")


        parsed_args = parser.parse_args(args)
        hostname = parsed_args.hostname
        duckiebot_ip = get_duckiebot_ip(hostname)
        duckiebot_client = get_remote_client(duckiebot_ip)

        # is the interface running?
        try:
            duckiebot_containers = duckiebot_client.containers.list()
            interface_container_found = False
            for c in duckiebot_containers:
                if 'duckiebot-interface' in c.name:
                    interface_container_found = True
            if not interface_container_found:
                dtslogger.error("The  duckiebot-interface is not running on the duckiebot")
                exit()
        except Exception as e:
            dtslogger.warn(
                "Not sure if the duckiebot-interface is running because we got and exception when trying: %s" % e)

        # is the raw imagery being published?
        try:
            duckiebot_containers = duckiebot_client.containers.list()
            raw_imagery_found = False
            for c in duckiebot_containers:
                if 'demo_camera' in c.name:
                    raw_imagery_found = True
            if not raw_imagery_found:
                dtslogger.error("The  demo_camera is not running on the duckiebot - please run `dts duckiebot demo --demo_name camera --duckiebot_name %s" % hostname)
                exit()

        except Exception as e:
            dtslogger.warn("%s" % e)


        image = parsed_args.image


        client = check_docker_environment()
        container_name = "intrinsic_calibration_%s" % hostname
        remove_if_running(client,container_name)
        env = {'HOSTNAME': hostname,
               'ROS_MASTER': hostname,
               'DUCKIEBOT_NAME': hostname,
               'ROS_MASTER_URI': 'http://%s:11311' % duckiebot_ip,
               'QT_X11_NO_MITSHM': 1}

        volumes = {}
        subprocess.call(["xhost", "+"])

        p = platform.system().lower()
        if 'darwin' in p:
            env['DISPLAY'] = 'host.docker.internal:0' 
            volumes = {
                '/tmp/.X11-unix': {'bind': '/tmp/.X11-unix', 'mode': 'rw'}
            }
        else:
            env['DISPLAY'] = os.environ['DISPLAY']

        dtslogger.info("Running %s on localhost with environment vars: %s" %
                       (container_name, env))

        dtslogger.info("When the window opens you will need to move the checkerboard around in front of the Duckiebot camera")
        cmd = "roslaunch pi_camera intrinsic_calibration.launch veh:=%s" % hostname

        params = {'image': image,
                  'name': container_name,
                  'network_mode': 'host',
                  'environment': env,
                  'privileged': True,
                  'stdin_open': True,
                  'tty': True,
                  'detach': True,
                  'command': cmd,
                  'volumes': volumes
                  }

        container = client.containers.run(**params)

        if parsed_args.debug:
            attach_cmd = 'docker attach %s' % (container_name)
            start_command_in_subprocess(attach_cmd)
    def command(shell, args):
        prog = 'dts duckiebot evaluate'
        parser = argparse.ArgumentParser(prog=prog, usage=usage)
        group = parser.add_argument_group('Basic')
        group.add_argument('--duckiebot_name', default=None,
                            help="Name of the Duckiebot on which to perform evaluation")
        group.add_argument('--duckiebot_username', default="duckie",
                           help="The duckiebot username")
        group.add_argument('--image', dest='image_name',
                           help="Image to evaluate, if none specified then we will build your current context",
                           default=None)
        group.add_argument('--glue_node_image', default="duckietown/challenge-aido_lf-duckiebot:aido2",
                           help="The node that glues your submission with ROS on the duckiebot. Probably don't change")
        group.add_argument('--duration', help="Number of seconds to run evaluation", default=15)
        group.add_argument('--remotely', action='store_true', default=True,
                           help="If true run the image over network without pushing to Duckiebot")
        group.add_argument('--no_cache', help="disable cache on docker build",
                           action="store_true", default=False)
        group.add_argument('--record_bag', action='store_true', default=False,
                           help="If true record a rosbag")
        group.add_argument("--debug", action='store_true', default=False,
                           help="If true you will get a shell instead of executing")
        group.add_argument('--max_vel', help="the max velocity for the duckiebot", default=0.7)
        group.add_argument('--challenge', help="Specific challenge to evaluate")
        parsed = parser.parse_args(args)

        tmpdir = '/tmp'
        USERNAME = getpass.getuser()
        dir_home_guest = os.path.expanduser('~')
        dir_fake_home = os.path.join(tmpdir, 'fake-%s-home' % USERNAME)
        if not os.path.exists(dir_fake_home):
            os.makedirs(dir_fake_home)
        get_calibration_files(dir_fake_home, parsed.duckiebot_username, parsed.duckiebot_name)

        client = check_docker_environment()
        agent_container_name = "agent"
        glue_container_name = "aido_glue"

        # remove the containers if they are already running
        remove_if_running(client, agent_container_name)
        remove_if_running(client, glue_container_name)

        # setup the fifos2 volume (requires pruning it if it's still hanging around from last time)
        try:
            client.volumes.prune()
            fifo2_volume=client.volumes.create(name='fifos2')
        except Exception as e:
            dtslogger.warn("error creating volume: %s" % e)


        duckiebot_ip = get_duckiebot_ip(parsed.duckiebot_name)

        duckiebot_client = get_remote_client(duckiebot_ip)
        try:
            duckiebot_containers = duckiebot_client.containers.list()
            interface_container_found=False
            for c in duckiebot_containers:
                if 'duckiebot-interface' in c.name:
                    interface_container_found=True
            if not interface_container_found:
                dtslogger.error("The  duckiebot-interface is not running on the duckiebot")
        except Exception as e:
            dtslogger.warn("Not sure if the duckiebot-interface is running because we got and exception when trying: %s" % e)
            

        # let's start building stuff for the "glue" node
        glue_volumes =  {fifo2_volume.name: {'bind': '/fifos', 'mode': 'rw'}}
        glue_env = {'HOSTNAME':parsed.duckiebot_name,
                    'DUCKIEBOT_NAME':parsed.duckiebot_name,
                    'ROS_MASTER_URI':'http://%s:11311' % duckiebot_ip}

        dtslogger.info("Running %s on localhost with environment vars: %s" %
                       (parsed.glue_node_image, glue_env))
        params = {'image': parsed.glue_node_image,
                  'name': glue_container_name,
                  'network_mode': 'host',
                  'privileged': True,
                  'environment': glue_env,
                  'detach': True,
                  'tty': True,
                  'volumes': glue_volumes}

        # run the glue container
        glue_container = client.containers.run(**params)

        if not parsed.debug:
            monitor_thread = threading.Thread(target=continuously_monitor, args=(client, glue_container.name))
            monitor_thread.start()

        if parsed.image_name is None:
            # if we didn't get an `image_name` we try need to build the local container
            path = '.'
            dockerfile = os.path.join(path, 'Dockerfile')
            if not os.path.exists(dockerfile):
                msg = 'No Dockerfile'
                raise Exception(msg)
            tag='myimage'
            if parsed.no_cache:
                cmd = ['docker', 'build', '--no-cache', '-t', tag, '-f', dockerfile]
            else:
                cmd = ['docker', 'build', '-t', tag, '-f', dockerfile]
            dtslogger.info("Running command: %s" % cmd)
            cmd.append(path)
            subprocess.check_call(cmd)
            image_name = tag
        else:
            image_name = parsed.image_name


        # start to build the agent stuff
        agent_env = {'AIDONODE_DATA_IN':'/fifos/agent-in',
                    'AIDONODE_DATA_OUT':'fifo:/fifos/agent-out'}

        agent_volumes = {fifo2_volume.name: {'bind': '/fifos', 'mode': 'rw'},
                         dir_fake_home: {'bind': '/data/config', 'mode': 'rw'}
                         }


        params = {'image': image_name,
                  'remove': True,
                  'name': agent_container_name,
                  'environment': agent_env,
                  'detach': True,
                  'tty': True,
                  'volumes': agent_volumes}

        if parsed.debug:
            params['command'] = '/bin/bash'
            params['stdin_open'] = True


        
        dtslogger.info("Running %s on localhost with environment vars: %s" % (image_name, agent_env))
        agent_container = client.containers.run(**params)

        if parsed.debug:
            attach_cmd = 'docker attach %s' % agent_container_name
            start_command_in_subprocess(attach_cmd)

        else:
            monitor_thread = threading.Thread(target=continuously_monitor,args=(client, agent_container_name))
            monitor_thread.start()

        duration = int(parsed.duration)
        # should we record a bag?
        if parsed.record_bag:
            bag_container = record_bag(parsed.hostname, duration)

        dtslogger.info("Running for %d s" % duration)
        time.sleep(duration)
        stop_container(glue_container)
        stop_container(agent_container)

        if parsed.record_bag:
            stop_container(bag_container)
 def command(shell, args):
     # configure arguments
     parser = argparse.ArgumentParser()
     parser.add_argument('-C',
                         '--workdir',
                         default=None,
                         help="Directory containing the project to build")
     parser.add_argument('-a',
                         '--arch',
                         default=DEFAULT_ARCH,
                         choices=set(CANONICAL_ARCH.values()),
                         help="Target architecture for the image to build")
     parser.add_argument(
         '-H',
         '--machine',
         default=DEFAULT_MACHINE,
         help="Docker socket or hostname where to build the image")
     parser.add_argument(
         '--pull',
         default=False,
         action='store_true',
         help="Whether to pull the latest base image used by the Dockerfile"
     )
     parser.add_argument('--no-cache',
                         default=False,
                         action='store_true',
                         help="Whether to use the Docker cache")
     parser.add_argument(
         '--no-multiarch',
         default=False,
         action='store_true',
         help="Whether to disable multiarch support (based on bin_fmt)")
     parser.add_argument(
         '-f',
         '--force',
         default=False,
         action='store_true',
         help="Whether to force the build when the git index is not clean")
     parser.add_argument('--push',
                         default=False,
                         action='store_true',
                         help="Whether to push the resulting image")
     parser.add_argument(
         '--rm',
         default=False,
         action='store_true',
         help=
         "Whether to remove the images once the build succeded (after pushing)"
     )
     parser.add_argument(
         '--loop',
         default=False,
         action='store_true',
         help=
         "(Experimental) Whether to reuse the same base image to speed up the build process"
     )
     parser.add_argument(
         '--ignore-watchtower',
         default=False,
         action='store_true',
         help="Whether to ignore a running Docker watchtower")
     parsed, _ = parser.parse_known_args(args=args)
     # ---
     code_dir = parsed.workdir if parsed.workdir else os.getcwd()
     dtslogger.info('Project workspace: {}'.format(code_dir))
     # show info about project
     shell.include.devel.info.command(shell, args)
     # get info about current repo
     repo_info = shell.include.devel.info.get_repo_info(code_dir)
     repo = repo_info['REPOSITORY']
     branch = repo_info['BRANCH']
     nmodified = repo_info['INDEX_NUM_MODIFIED']
     nadded = repo_info['INDEX_NUM_ADDED']
     # check if the index is clean
     if nmodified + nadded > 0:
         dtslogger.warning(
             'Your index is not clean (some files are not committed).')
         dtslogger.warning(
             'If you know what you are doing, use --force (-f) to force the execution of the command.'
         )
         if not parsed.force:
             exit(1)
         dtslogger.warning('Forced!')
     # create defaults
     default_tag = "duckietown/%s:%s" % (repo, branch)
     tag = "%s-%s" % (default_tag, parsed.arch)
     # get info about docker endpoint
     dtslogger.info('Retrieving info about Docker endpoint...')
     epoint = _run_cmd([
         'docker',
         '-H=%s' % parsed.machine, 'info', '--format', '{{json .}}'
     ],
                       get_output=True,
                       print_output=False)
     epoint = json.loads(epoint[0])
     if 'ServerErrors' in epoint:
         dtslogger.error('\n'.join(epoint['ServerErrors']))
         return
     epoint['MemTotal'] = _sizeof_fmt(epoint['MemTotal'])
     print(DOCKER_INFO.format(**epoint))
     # check if there is a watchtower instance running on the endpoint
     if shell.include.devel.watchtower.is_running(parsed.machine):
         dtslogger.warning(
             'An instance of a Docker watchtower was found running on the Docker endpoint.'
         )
         dtslogger.warning(
             'Building new images next to an active watchtower might (sure it will) create race conditions.'
         )
         dtslogger.warning('Solutions:')
         dtslogger.warning(
             '  - Recommended: Use the command `dts devel watchtower stop [options]` to stop the watchtower.'
         )
         dtslogger.warning(
             '  - NOT Recommended: Use the flag `--ignore-watchtower` to ignore this warning and continue.'
         )
         if not parsed.ignore_watchtower:
             exit(2)
         dtslogger.warning('Ignored!')
     # print info about multiarch
     msg = 'Building an image for {} on {}.'.format(parsed.arch,
                                                    epoint['Architecture'])
     dtslogger.info(msg)
     # register bin_fmt in the target machine (if needed)
     if not parsed.no_multiarch:
         if epoint['Architecture'] not in ARCH_MAP[CANONICAL_ARCH[
                 parsed.arch]]:
             dtslogger.info('Configuring machine for multiarch builds...')
             try:
                 _run_cmd([
                     'docker',
                     '-H=%s' % parsed.machine, 'run', '--rm',
                     '--privileged', 'multiarch/qemu-user-static:register',
                     '--reset'
                 ], True)
                 dtslogger.info('Multiarch Enabled!')
             except:
                 msg = 'Multiarch cannot be enabled on the target machine. This might create issues.'
                 dtslogger.warning(msg)
         else:
             msg = 'Building an image for {} on {}. Multiarch not needed!'.format(
                 parsed.arch, epoint['Architecture'])
             dtslogger.info(msg)
     # define labels
     buildlabels = []
     # define build args
     buildargs = ['--build-arg', 'ARCH={}'.format(parsed.arch)]
     # loop mode (Experimental)
     if parsed.loop:
         buildargs += ['--build-arg', 'BASE_IMAGE={}'.format(repo)]
         buildargs += [
             '--build-arg', 'BASE_TAG={}-{}'.format(branch, parsed.arch)
         ]
         buildlabels += ['--label', 'LOOP=1']
         # ---
         msg = "WARNING: Experimental mode 'loop' is enabled!. Use with caution"
         dtslogger.warn(msg)
     # build
     buildlog = _run_cmd([
         'docker',
             '-H=%s' % parsed.machine,
             'build',
                 '--pull=%d' % int(parsed.pull),
                 '--no-cache=%d' % int(parsed.no_cache),
                 '-t', tag] + \
                 buildlabels + \
                 buildargs + [
                 code_dir
     ], True, True)
     # get image history
     historylog = _run_cmd([
         'docker',
         '-H=%s' % parsed.machine, 'history', '-H=false', '--format',
         '{{.ID}}:{{.Size}}', tag
     ], True)
     historylog = [l.split(':') for l in historylog if len(l.strip()) > 0]
     # run docker image analysis
     ImageAnalyzer.process(buildlog, historylog, codens=100)
     # image tagging
     if parsed.arch == DEFAULT_ARCH:
         dtslogger.info("Tagging image {} as {}.".format(tag, default_tag))
         _run_cmd(
             ['docker',
              '-H=%s' % parsed.machine, 'tag', tag, default_tag])
     # perform push (if needed)
     if parsed.push:
         if not parsed.loop:
             shell.include.devel.push.command(shell, args)
         else:
             msg = "Forbidden: You cannot push an image when using the experimental mode `--loop`."
             dtslogger.warn(msg)
     # perform remove (if needed)
     if parsed.rm:
         shell.include.devel.clean.command(shell, args)
    def command(shell, args):

        prog = 'dts challenges evaluate'
        parser = argparse.ArgumentParser(prog=prog, usage=usage)

        group = parser.add_argument_group('Basic')

        group.add_argument('--no-cache',
                           action='store_true',
                           default=False,
                           help="")
        group.add_argument('--no-build',
                           action='store_true',
                           default=False,
                           help="")
        group.add_argument('--no-pull',
                           action='store_true',
                           default=False,
                           help="")
        group.add_argument('--challenge',
                           help="Specific challenge to evaluate")

        group.add_argument('--image',
                           help="Evaluator image to run",
                           default='duckietown/dt-challenges-evaluator:v4')
        group.add_argument('--shell',
                           action='store_true',
                           default=False,
                           help="Runs a shell in the container")
        group.add_argument('--output', help="", default='output')
        group.add_argument('--visualize',
                           help="Visualize the evaluation",
                           action='store_true',
                           default=False)
        parser.add_argument('--impersonate', type=str, default=None)
        group.add_argument('-C', dest='change', default=None)

        parsed = parser.parse_args(args)

        if parsed.change:
            os.chdir(parsed.change)

        client = check_docker_environment()

        command = ['dt-challenges-evaluate-local']
        if parsed.no_cache:
            command.append('--no-cache')
        if parsed.no_build:
            command.append('--no-build')
        if parsed.challenge:
            command.extend(['--challenge', parsed.challenge])
        if parsed.impersonate:
            command.extend(['--impersonate', parsed.impersonate])
        output_rp = os.path.realpath(parsed.output)
        command.extend(['--output', parsed.output])
        #
        # if parsed.features:
        #     dtslogger.debug('Passing features %r' % parsed.features)
        #     command += ['--features', parsed.features]
        # fake_dir = '/submission'
        tmpdir = '/tmp'

        UID = os.getuid()
        USERNAME = getpass.getuser()
        dir_home_guest = '/fake-home/%s' % USERNAME  # os.path.expanduser('~')
        dir_fake_home_host = os.path.join(tmpdir, 'fake-%s-home' % USERNAME)
        if not os.path.exists(dir_fake_home_host):
            os.makedirs(dir_fake_home_host)

        dir_fake_home_guest = dir_home_guest
        dir_dtshell_host = os.path.join(os.path.expanduser('~'), '.dt-shell')
        dir_dtshell_guest = os.path.join(dir_fake_home_guest, '.dt-shell')
        dir_tmpdir_host = '/tmp'
        dir_tmpdir_guest = '/tmp'

        volumes = {
            '/var/run/docker.sock': {
                'bind': '/var/run/docker.sock',
                'mode': 'rw'
            }
        }
        d = os.path.join(os.getcwd(), parsed.output)
        if not os.path.exists(d):
            os.makedirs(d)
        volumes[output_rp] = {'bind': d, 'mode': 'rw'}
        volumes[os.getcwd()] = {'bind': os.getcwd(), 'mode': 'ro'}
        volumes[dir_tmpdir_host] = {'bind': dir_tmpdir_guest, 'mode': 'rw'}
        volumes[dir_dtshell_host] = {'bind': dir_dtshell_guest, 'mode': 'ro'}
        volumes[dir_fake_home_host] = {
            'bind': dir_fake_home_guest,
            'mode': 'rw'
        }
        volumes['/etc/group'] = {'bind': '/etc/group', 'mode': 'ro'}

        binds = [_['bind'] for _ in volumes.values()]
        for b1 in binds:
            for b2 in binds:
                if b1 == b2:
                    continue
                if b1.startswith(b2):
                    msg = 'Warning, it might be a problem to have binds with overlap'
                    msg += '\n  b1: %s' % b1
                    msg += '\n  b2: %s' % b2
                    dtslogger.warn(msg)
        # command.extend(['-C', fake_dir])
        env = {}

        extra_environment = dict(username=USERNAME,
                                 uid=UID,
                                 USER=USERNAME,
                                 HOME=dir_fake_home_guest)

        env.update(extra_environment)

        dtslogger.debug('Volumes:\n\n%s' %
                        yaml.safe_dump(volumes, default_flow_style=False))

        dtslogger.debug('Environment:\n\n%s' %
                        yaml.safe_dump(env, default_flow_style=False))

        from duckietown_challenges.rest import get_duckietown_server_url
        url = get_duckietown_server_url()
        dtslogger.info('The server URL is: %s' % url)
        if 'localhost' in url:
            h = socket.gethostname()
            replacement = h + '.local'

            dtslogger.warning(
                'There is "localhost" inside, so I will try to change it to %r'
                % replacement)
            dtslogger.warning(
                'This is because Docker cannot see the host as "localhost".')

            url = url.replace("localhost", replacement)
            dtslogger.warning('The new url is: %s' % url)
            dtslogger.warning(
                'This will be passed to the evaluator in the Docker container.'
            )

        env['DTSERVER'] = url

        container_name = 'local-evaluator'
        image = parsed.image
        name, tag = image.split(':')
        if not parsed.no_pull:
            dtslogger.info('Updating container %s' % image)

            dtslogger.info('This might take some time.')
            client.images.pull(name, tag)
        #
        try:
            container = client.containers.get(container_name)
        except:
            pass
        else:
            dtslogger.error('stopping previous %s' % container_name)
            container.stop()
            dtslogger.error('removing')
            container.remove()

        dtslogger.info('Starting container %s with %s' %
                       (container_name, image))

        detach = True

        env[DTShellConstants.DT1_TOKEN_CONFIG_KEY] = shell.get_dt1_token()
        dtslogger.info('Container command: %s' % " ".join(command))

        # add all the groups
        on_mac = 'Darwin' in platform.system()
        if on_mac:
            group_add = []
        else:
            group_add = [
                g.gr_gid for g in grp.getgrall() if USERNAME in g.gr_mem
            ]

        interactive = False
        if parsed.shell:
            interactive = True
            detach = False
            command = ['/bin/bash', '-l']

        params = dict(working_dir=os.getcwd(),
                      user=UID,
                      group_add=group_add,
                      command=command,
                      tty=interactive,
                      volumes=volumes,
                      environment=env,
                      remove=True,
                      network_mode='host',
                      detach=detach,
                      name=container_name)
        dtslogger.info('Parameters:\n%s' % json.dumps(params, indent=4))
        client.containers.run(image, **params)

        if parsed.visualize:
            start_rqt_image_view()

        continuously_monitor(client, container_name)
Esempio n. 12
0
def remove_container(container):
    try:
        container.remove()
    except Exception as e:
        dtslogger.warn("Container %s not found to remove! %s" % (container, e))
Esempio n. 13
0
def stop_container(container):
    try:
        container.stop()
    except Exception as e:
        dtslogger.warn("Container %s not found to stop! %s" % (container, e))