Getting Started with Dockerized Servod
Author: Keith Haddow
Date: Aug 24, 2022
This is a quick introduction for those wishing to use servod in a dockerized container, both directly as part of implementing the docker servod into their lab processes.
It should be noted that as servod requires low level access to the USB hardware the container runs in protected mode, which takes down many of the protections to the host that a typical container would provide. To be clear, it is not worse or better than running servod natively on the host.
Currently servod will only run on linux distributions, running on Windows is not supported.
Also currently the image is available only on x86 architectures but should also be available on arm64 later in 2022. At that time the image will be multi architecture, so the instructions will be identical on arm64 devices.
Before you start you need to install the docker application, python 3 and the python docker api package.
You need to install docker onto the machine that is going to be the host for the docker containers.
https://docs.docker.com/desktop/install/linux-install/
Install Python
The example below works for debian/ubuntu - your command may be different depending on your linux distribution.
sudo apt install python3 |
Copy paste the code below and make the file start_servod_release.py.
#!/usr/bin/env python3 # Copyright 2022 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import docker import logging import argparse IMAGE = "us-docker.pkg.dev/chromeos-hw-tools/servod/servod:release" def setup(): client = docker.from_env() try: client.images.pull(IMAGE) except docker.errors.APIError: logging.exception("Failed to pull image") return client def start_servod(client, dut_hostname, board, model, serial_no): environment = [ "BOARD=%s" % board, "MODEL=%s" % model, "SERIAL=%s" % serial_no, "PORT=%s" % "9999", ] name = "%s-docker_servod" % dut_hostname logs_volume = "%s_log" % dut_hostname command = ["bash", "/start_servod.sh"] cont = client.containers.run( IMAGE, remove=True, privileged=True, name=name, hostname=name, cap_add=["NET_ADMIN"], detach=True, volumes=["/dev:/dev", "%s:/var/log/servod_9999/" % logs_volume], environment=environment, command=command, ) log_lines = cont.logs(stream=True, follow=True) started = False while log_lines and not started: cont.reload() for line in log_lines: print(line.decode("utf-8").strip()) if b"servod - INFO - Listening on 0.0.0.0 port" in line: started = True logging.info("Detected servod has started.") break if cont.status == "removing": break if __name__ == "__main__": parser = argparse.ArgumentParser(add_help=False) parser.add_argument( "-h", "--hostname", required=True, type=str, help="The IP or hostname of the DUT connected to servo.", ) parser.add_argument( "-b", "--board", required=True, type=str, help="The board of the DUT." ) parser.add_argument( "-m", "--model", required=True, type=str, help="The model of the DUT." ) parser.add_argument( "-s", "--serial", required=True, type=str, help="The serial number of the servo.", ) args = parser.parse_args() client = setup() start_servod( client, args.hostname, args.board, args.model, args.serial ) |
Note the first time you run this it will download the image to your local machine so it may take some time.
chmod u+x start_servod_release.py ./start_servod_release.py -b <DUT board> -m <DUT Model> -s <servo serial number> -h <DUT IP/Hostname> |
The DUT IP/Hostname just needs to be a unique string for the naming of the docker containers, any unique string will do.
You should see logs of logs ending with
Servod - INFO - Initialized servo_v4p1_uart_timestamp to on Servod - INFO - Initialized servo_dts_mode to on Servod - INFO - Initialized warm_reset to off activeV4Device - INFO - Active device is already 'ccd_cr50' DeviceWatchdog - INFO - Reinit capable device found. Polling rate set to 0.10s. DeviceWatchdog - INFO - Watchdog setup for devices: [18d1:520d SERVOV4P1-S-2107250755, 18d1:5014 0700A01F-9420C774] servod - INFO - Listening on 0.0.0.0 port 9999 |
At this point servod is running inside of your docker container.
To execute commands on the servo you can either:
Run bash on the container interactively - giving an experience similar to ssh into a host machine running the servod natively
docker exec -it <hostname>-docker_servod bash |
Then you can just run dut-control commands as normal.
Yout can also just run the dut control command:
docker exec -it <hostname>-docker_servod dut-control --get-all |
In this example I did not show opening the XML api to the host, this can be done. There can be some extra configuration to get this working if you want to scale it by creating a docker network that the containers run in and then you just address each servo API as hostname:9999 vs before addressing as localhost:<port number>. If more details on this scenario are needed please let me know.
Logs are preserved in a docker volume for each hostname. So there will be a volume called <hostname>_log
If you are in a servod container then the logs are just mounted at /var/log/servod_9999
If the container is not starting and you want to look at the logs, just start another vanilla container and mount the logs volume. So:
docker run -it -v <hostname>_log:/tmp/servo_logs debian bash |
And you will be able to see the logs at /tmp/servo_logs
Firmware updates can not run at the same time as servod. So the container needs to change to running that command first.
You can change the command line in the script to run
command = ["servo_updater", "-b", "<servo type>", "-s", serial_no] |
Better solution coming soon…..