Roll out software updates to machines

If you have already deployed software, you can inspect the fragment you have created. The JSON object for the deployed module or package has a version field. Unless the version field is set to a specific version, some or all updates for that module or package can happen automatically.

Test and update software

You can either use fragment tags for testing or manually overwrite the version of the module or package for a subset of machines. We strongly recommend that you test updates on a subset of machines before deploying it to all machines.

1. Navigate to your fragment’s page on On your fragment’s page, from the FRAGMENTS tab.

2. Create a stable fragment tag. On your fragment’s page, click on Versions in the menu bar and add a tag called stable.

3. Pin all machine configurations to the stable fragment tag. For each machine that uses the fragment, update its configuration.

4. Edit the fragment and change the version of your module or package in the development fragment. This will create a new version of the fragment.

For example:

{
  "components": [
    {
      "api": "rdk:component:camera",
      "attributes": {},
      "model": "rdk:builtin:fake",
      "name": "camera-1"
    },
    {
      "api": "rdk:component:generic",
      "attributes": {},
      "model": "naomi:my-control-logic:control-logic",
      "name": "generic-1"
    }
  ],
  "debug": true,
  "modules": [
    {
      "module_id": "naomi:my-control-logic",
      "name": "naomi_my-control-logic",
      "type": "registry",
      "version": "0.0.7"
    }
  ]
}

5. Create a development tag. On your fragment’s page, click on Versions in the menu bar and add a tag called development. Select the most recent version that you just created for the tag.

6. Add the development fragment to a subset of machines by pinning the fragment configuration to the development fragment tag. For each machine that you want to test the changes on, update the configuration.

7. Test the new version of your module or package.

8. Update the stable fragment tag. When you are satisfied that your module or package works as expected, set the Version for the stable fragment tag. to the new version. This will update all machines that use the stable fragment tag.

1. Change the version of the module.

You can overwrite parts of a fragment to use a new version of a module or package without modifying the upstream fragment.

For each machine that you would like to test the new version of the module or package on, go to its CONFIGURE tab, find the module or package, and edit its version number.

Configuration builder UI

Click Save in the upper right corner of the screen.

2. Test the new version of your module or package.

3. Update the fragment.

When you are satisfied that your module or package works as expected, update your fragment.

For example:

{
  "components": [
    {
      "api": "rdk:component:camera",
      "attributes": {},
      "model": "rdk:builtin:fake",
      "name": "camera-1"
    },
    {
      "api": "rdk:component:generic",
      "attributes": {},
      "model": "naomi:my-control-logic:control-logic",
      "name": "generic-1"
    }
  ],
  "debug": true,
  "modules": [
    {
      "module_id": "naomi:my-control-logic",
      "name": "naomi_my-control-logic",
      "type": "registry",
      "version": "0.0.7"
    }
  ]
}

All machines configured with your fragment will update when they next check for configuration updates.

Check machine status

To check when your machines have last updated their configuration, iterate over your machines using the Fleet Management API, connect to each machine, and use the GetMachineStatus method.

The following example script iterates over all machines in a given location and if it can connect to the machines, it prints their status information. If it cannot connect to a machine, it prints the most recent log entries.

import asyncio

from viam.rpc.dial import DialOptions
from viam.app.viam_client import ViamClient
from viam.robot.client import RobotClient


# Replace "<API-KEY>" (including brackets) with your API key and "<API-KEY-ID>"
# with your API key ID
API_KEY = "<API-KEY>"
API_KEY_ID = "<API-KEY-ID>"
LOCATION_ID = "<LOCATION-ID>"


async def connect() -> ViamClient:
    dial_options = DialOptions.with_api_key(API_KEY, API_KEY_ID)
    return await ViamClient.create_from_dial_options(dial_options)


async def machine_connect(address):
    opts = RobotClient.Options.with_api_key(
        api_key=API_KEY,
        api_key_id=API_KEY_ID
    )
    return await RobotClient.at_address(address, opts)


async def main():
    viam_client = await connect()
    cloud = viam_client.app_client

    machines = await cloud.list_robots(location_id=LOCATION_ID)
    print("Found {} machines.".format(len(machines)))

    for m in machines:
        machine_parts = await cloud.get_robot_parts(m.id)
        main_part = None
        for p in machine_parts:
            if p.main_part:
                main_part = p

        print("Attempting to connect to {}...".format(main_part.fqdn))

        try:
            machine = await machine_connect(main_part.fqdn)
            status = await machine.get_machine_status()
            print(status.config)

        except ConnectionError:
            print("Unable to establish a connection to the machine.")
            logs = await cloud.get_robot_part_logs(
                robot_part_id=main_part.id,
                num_log_entries=5
            )
            if not logs:
                print("No logs available.")
            else:
                print("Most recent 5 log entries:")
            for log in logs:
                print("{}-{} {}: {}".format(
                    log.logger_name, log.level, log.time, log.message))
                if log.stack:
                    print(log.stack)

    viam_client.close()

if __name__ == '__main__':
    asyncio.run(main())