Machines: Systems under Control or Simulation

class carthage.machine.AbstractMachineModel(*args, **kwargs)

Represents properties of a machine that do not involve interacting with an implementation of that machine. All the AbstractMachineModels in a layout can be instantiated to reason about things like network connections, configuration, and what machines will be built without instantiating any of the machines. Typically if a Machine has a model, the model will be made available either by setting the model property on the machine, or by providing a dependency for AbstractModel in the injector in which the machine is instantiated.

The most common concrete implementation of a machine model is carthage.modeling.MachineModel.

classmethod default_class_injection_key()

Called by Injector.add_provider() in the single argument form to get the injection key to use when this class is added to provide a dependency.

override_dependencies: bool | Injector | Injectable | InjectionKey = False

If True, Machine.start_dependencies() will stop collecting dependencies at the injector of this model. In the normal situation where the Machine is instantiated within the model’s dependency context, what this means is that only system dependencies declared on the model will be started. This may also be an InjectionKey, an Injector, or an Injectable. Se the documentation of Machine.start_dependencies().

classmethod supplementary_injection_keys(k)

Returns an iteration of InjectionKeys that should be added to an injector when this class is added. The current injection key is taken as an argument so that constraints applied can modify what keys are added.

class carthage.machine.BareMetalMachine(**kwargs)

Represents physical hardware that Carthage cannot start or stop

async async_ready()

This may need to be overridden, but is provided as a default

async find()

See if the machine exists. Override if it is desirable to do a dns check or similar.

async is_machine_running()
Returns:

Whether the machine is running

Probe whether the machine is running and set self.running appropriately. It is most important that running be set accurately when start_machine() and stop_machine() can start or stop the machine. For BareMetalMachine it is reasonable to assume that the machine is running. This interface point should not call ssh_online() or confirm the machine is on the network.

stamp_subdir()

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

async start_machine()

Must be overridden. Start the machine.

async stop_machine()

Must be overridden; stop the machine.

class carthage.machine.BaseCustomization(apply_to: Machine, stamp=None, **kwargs)
async apply()

Run setup tasks against host

async async_ready()

This may need to be overridden, but is provided as a default

check_stamp(stamp)
Returns:

a tuple containing the unix time of the stamp and the text contents of the stamp. The first element of the tuple is False if the stamp does not exist

customization_context()

Can be overridden; context in which customization tasks are run.

classmethod default_class_injection_key()

Called by Injector.add_provider() in the single argument form to get the injection key to use when this class is added to provide a dependency.

description = ''

A description of the customization for inclusion in task logging

inspect_setup_tasks()

Iterates over the setup tasks of a ninstance and provides an inspector that can determine if a task would run and what its description is.

async last_run()
Returns:

the most recent time any setup task on this Customization has run against the given host. Returns false if the tasks definitely need to run.

runas_user = None

The user to run as

property stamp_subdir

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

class carthage.machine.ContainerCustomization(apply_to, **kwargs)

A customization class for running tasks on Container instances or ImageVolume instances without actually booting the container. May also run on objects providing _apply_to_container_customization() like carthage.podman.PodmanContainer. This is valuable for tasks used in image production that want to manipulate the filesystem.

class carthage.machine.FilesystemCustomization(apply_to, **kwargs)

A Customization class for interacting with the filesystem either of a carthage.container.Container, ImageVolume or Machine. If possible (for containers and image volumes), do not actually boot the machine.

class carthage.machine.Machine(name=None, **kwargs)

Represents a machine that can be interacted with.

This is an abstract class representing the interface to machines independent of the technology being used to control them. This class can be used for:

The main capabilities of this interface are to be able to start and stop machines, know their IP address, and connect via ssh.

async apply_customization(cust_class, method='apply', **kwargs)

Apply a BaseCustomization to this machine..

Parameters:

stamp – A distinguisher for stamps created by the customization. The stamp will include this value as well as the stamp from the setup_task().

async dynamic_dependencies()

See carthage.deployment.Deployable.dynamic_dependencies() for documentation. Returns technology specific networks for links where that is possible.

filesystem_access(user=None)

An asynchronous context manager that makes the filesystem of the Machine available on a local path.

Returns:

Path at which the filesystem can be accessed while in the context.

async is_machine_running()
Returns:

Whether the machine is running

Probe whether the machine is running and set self.running appropriately. It is most important that running be set accurately when start_machine() and stop_machine() can start or stop the machine. For BareMetalMachine it is reasonable to assume that the machine is running. This interface point should not call ssh_online() or confirm the machine is on the network.

machine_running(**kwargs)

Returns a asynchronous context manager; within the context manager, the machine is expected to be running unless stop_machine() is explicitly called.

machine_running_ssh_online: bool = True

Should machine_running default to calling ssh_online

network_implementation_class: type[carthage.network.TechnologySpecificNetwork] = None

A class of TechnologySpecificNetwork that will be instantiated for the links on this NetworkedModel.

rsync_uses_filesystem_access: bool = False

If true, use self.filesystem_access for rsync, otherwise use ssh.

async run_command(*args, _bg=True, _bg_exc=False, _user=None, **kwargs)

This method is the machine-specific part of run_command(). Override in subclasses if there is a better way to run a command than sshing into a machine. This method is async, although that is not reflected in the signature because this implementation returns an awaitable.

param user:

The user to run as. defaults to runas_user.

This implementation calls ssh(). Ssh has really bad quoting; it effectively removes one level of quoting from the input.

This handles quoting and makes sure each argument is a separate argument on the eventual shell; it works like carthage.container.Container.container_command() and is used to give a consistent interface by run_command().

By the time run_command is awaited, the command will have completed.

async start_dependencies()

Interface point that should be called by start_machine() to start any dependent machines such as routers needed by this machine.

Default behavior is provided for machines with AbstractMachineModels attached to thennn model property. In this case, any providers of InjectionKey(SystemDependency) with a name constraint are collected. These objects are called with an AsyncInjector. For example carthage.system_dependency.MachineDependency will start some other machine and optionally wait for it to become online.

In typical usage, the Machine is contained in the injector context of its model. Dependencies for the current machine may be added directly to its model’s injector. Dependencies shared among a group of machines may be added to injector contexts that contain the model. For example:

network.injector.add_provider(MachineDependency("router.network"))
# But the fileserver also needs the domain controller
network.fileserver_model.injector.add_provider(MachineDependency("domain-controller.network"))

But in such a situation, the router itself should not depend on itself. Two approaches are possible. The first is to mask out the dependency in the router’s model:

ignore_system_dependency(network.router_model.injector, MachineDependency("router.network"))

Another mechanism is available: the override_dependencies property of AbstractMachineModel. This property controls how far up the injector chain to look for dependencies:

true

Only consider dependencies directly defined on the model or the Machine. Does not work correctly if the self.injector is not in the injector context of self.model.injector.

an Injector

Filter out dependencies declared between the parent of the model injector and the provided injector, inclusive. So, consider a machine in a network in an enclave. If the enclave’s injector is provided, then injectors declared on the enclave and network will be ignored, but dependencies declared at a level larger than the enclave will still be started.

an Injectable

Filter out dependencies between the machine model and the injector contained in the Injectable inclusive.

an InjectionKey

Instantiate the key, and assume it is an Injectable. Treat as that injectable

For finer grain control, implementations can override this method.

async start_machine()

Must be overridden. Start the machine.

async stop_machine()

Must be overridden; stop the machine.

class carthage.machine.MachineCustomization(apply_to: Machine, stamp=None, **kwargs)

A customization class for running customizations on running machines.

property customization_context

Can be overridden; context in which customization tasks are run.

class carthage.machine.NetworkedMixin

Represents something like a AbstractMachineModel or a carthage.podman.PodmanPod that generates a set of network_links from a NetworkConfig.

When resolve_networking() is called, if self.injector provides network_namespace_key, then the network_links are reused from the object providing that dependency. Typical usage is for a OciPod or similar network namespace in which a AbstractMachineModel will be run to provide network_namespace_key.

async resolve_networking(force: bool = False)

Adds all carthage.network.NetworkLink objects specified in the carthage.network.NetworkConfig to the network_links property.

Parameters:

force – if True, resolve the network config even if it has already been resolved once.

class carthage.machine.NetworkedModel(*args, **kwargs)

A NetworkedMixin that should be available at modeling time. Generally not a Deployable unless no_generate_at_ready is True.

class carthage.machine.ResolvableModel(*args, **kwargs)

In a carthage.modeling.CarthageLayout all the models are instantiated as part of bringing the layout to ready. This permits models to contribute to a shared understanding of networking and other global aspects of the layout.

This class represents a model that can be instantiated and resolved. A layout will instantiate all ResolvableModel objects in the scope of its injector that have a name constraint on their InjectionKey.

This class is defined here rather than in the modeling layer so that AbstractMachineModel does not need to depend on the modeling layer.

Subclasses of this model will typically need to override default_class_injection_key and probably supplimentary_injection_keys.

class carthage.machine.SshMixin

An item that accepts ssh connections.

ip_address represents the endpoint to connect to in order to manage the resource via ssh. In early Carthage development, this was always an IP address. More recently, this can be an IP address or hostname.

The ssh_origin injection key is used to look up a Container from which the ssh should be run. If ssh_origin is provided by the injector hierarchy, then Carthage enters the network namespace of the Container before running ssh. Note that the mount namespace is not typically used, so the host’s ssh binary and keys are used. There is support for using a Linux VRF within the namespace; see carthage.network.access_ssh_origin() for details.

If the injector hierarchy provides ssh_jump_host, then that is used as a ProxyJump host.

If neither ssh_origin nor ssh_jump_host are provided then Carthage connects directly to ip_address.

If ssh_origin is provided but not ssh_jump_host, then Carthage enters the network namespace of ssh_origin and connects to ip_address within the context of that namespace (possibly within a VRF within that namespace). DNS resolution for the connection to ip_address may be performed by the host in the host’s network namespace (systemd-resolved’sNSS plugin or similar) or it may be performed in the ssh_origin network namespace using the host’s nameserver configuration. It is best if ip_address actually is an IP address to avoid this ambiguity.

If ssh_jump_host is provided but not ssh_origin, then Carthage first connects to ssh_jump_host and tunnels a connection to ip_address within the jump host connection. The DNS resolution for the connection to ip_address is performed by the jump host; the DNS resolution for the connection to the jump host is performed by the host.

If both ssh_origin and ssh_jump_host are provided: Carthage enters the namespace of ssh_origin. Within that namespace it establishes an ssh connection to ssh_jump_host. Over that connection it tunnels to ip_address. DNS resolution for the connection to ssh_jump_host is ambiguous; it is best if ip_address is an IP address in this case. DNS resolution to ip_address is performed by ssh_jump_host.

ip_address()

The IP address or name at which this machine should be managed.

rsync(*args)

Call rsync with given arguments. An argument may be a RsyncPath generated by rsync_path(). Such a path encapsulates a host name and a path. When rsync is called, Carthage finds the appropriate ssh_origin to select the right namespace for rsync.

Typical usage:

await machine.rsync("file",
    rsync_path("/etc/script")
#Copy file to /etc/script on machine
rsync_path(p)

A marker in a call to rsync() indicating that p should be copied to or from self. Interacts with the Carthage rsync machinery to select the right network namespace.

runas_user()

The user to run commands as. Mechanisms like carthage.become_privileged.BecomePrivilegedMixin provide a mechanism to use a privilege gateway like sudo so that runas_user can differ from ssh_login_user. Can be set on the machine or model. Defaults to root.

ssh_jump_host()

An SshMixin or string to use as a jump host. Also supports a AbstractMachineModel with a machine attribute.

ssh_login_user()

The ssh user to log in as. Defaults to root, can be set either on the machine or the model.

ssh_online_command = 'echo online'

The command run remotely by ssh_online()

ssh_rekeyed()

Indicate that this host has been rekeyed

carthage.machine.ssh_jump_host = InjectionKey('ssh_jump_host')

A Machine to use as a jump host.

Containers

class carthage.container.Container(name, *, network_config, skip_ssh_keygen=False, **kwargs)

VMs

carthage.vm.VM

alias of Vm

Hardware Configuration

VMs and cloud instances will look for the following properties in a AbstractMachineModel to configure hardware:

cpus

The number of CPUs on the virtual machine

memory_mb

The amount of memory in megabytes

disk_sizes

A sequence of disk sizes for primary and secondary disks. Provided in GiB.

disk_config

A sequence of dicts configuring primary and secondary disks. The only key defined at this level is size, the size of the disk in GiB. If disk_config is provided, disk_sizes is ignored. The intent of disk_config is to permit MachineImplementation specific configuration of disks. Consult the specific machine implementations for details.

nested_virt

Boolean indicating whether to allow nested virtualization

hardware_tpm

Whether to provide a TPM in the virtual machine.

console_needed

Whether a graphical console is needed.