blob: 48b48185e178eb746b413de17e19bbc865b8b216 [file]
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
virtualOS Orchestrator Extension
================================
Orchestrator extension that lets Apache CloudStack manage macOS virtual
machines in virtualOS (https://github.com/yep/virtualOS) on Apple silicon
Macs. Each Mac running virtualOS is added to CloudStack as a host in a
cluster mapped to this extension.
virtualOS has no command line interface or scripting dictionary. The script
therefore works on the virtualOS VM bundles directly and starts a VM by
launching a separate virtualOS process with the autostartVMBundlePath user
default passed as launch argument:
open -n -g -a virtualOS --args -autostartVMBundlePath <dir>/<name>.bundle
Each running instance is one virtualOS process; stopping an instance
terminates that process. The extension is a Rust program that runs on the
management server and reaches the Mac through SSH. When the management server itself runs on the
Mac (e.g. a development setup), the host url can be "localhost" and no SSH
is used.
Requirements
------------
Management server:
- The virtualos binary built for the management server's platform (see
Building); it has no runtime dependencies besides the C library
- ssh client; sshpass only when password authentication is used
Building:
- A current stable Rust toolchain with cargo (https://rustup.rs)
Mac:
- Apple silicon, virtualOS 3.0 or later (the autostart setting is used)
- The SSH user logged in to the GUI session, virtualOS is a GUI app
- Remote Login (SSH) enabled, with "Allow full disk access for remote
users" when vm_directory is inside the virtualOS sandbox container
(the default), as macOS protects other apps' containers
- One or more installed macOS VMs in virtualOS to clone from, stopped
- An APFS volume for vm_directory, so clones are copy-on-write
Supported operations
--------------------
create Clone the template VM bundle, name it after the CloudStack
instance's internal name, give it a new machine
identifier, set CPU count, memory (rounded up to whole
GiB), MAC address and network mode, then start it. The
clone is removed again when any step fails.
start, stop, reboot, delete, status, statuses
start and reboot first write the instance's current CPU
count, memory and MAC address to the VM, so a changed
service offering takes effect;
start succeeds once virtualOS logs "vm started" for the
VM and fails (stopping the process again) when it logs
anything else or exits; stop terminates the virtualOS
process, which powers the guest off without a shutdown,
and waits until no process holds the bundle's files, so
reboot can start it again right away
getconsole Not supported, virtualOS has no VNC endpoint to hand out.
Custom actions (register them with addCustomAction):
GetIpAddresses List the IP addresses the Mac's DHCP server leased to the
instance's MAC address (NAT network mode only)
Configuration details
---------------------
Extension or host details (host details win):
url Mac hostname/IP, or "localhost" to run locally
username SSH user owning the virtualOS VMs; leave empty for local
password Optional SSH password (needs sshpass); keys preferred
ssh_key Optional private key path on the management server
ssh_port Optional, default 22
verify_host_key Optional, "true" (default) or "false"
app_path Optional, default /Applications/virtualOS.app, falling
back to a Spotlight lookup of the app bundle
vm_directory Optional, directory holding the <name>.bundle VMs. It
must be the "VM files" directory configured in virtualOS
as the sandboxed app can not open bundles elsewhere.
Default: ~/Library/Containers/com.github.yep.ios.virtualOS
/Data/Documents
template_name Optional default VM bundle to clone, without .bundle
network_mode Optional: nat or bridged. When set it is applied to the
instance, otherwise the template's mode is kept
bridge_interface macOS interface for bridged mode, e.g. en0, or its
virtualOS name, e.g. "Wi-Fi (en0)"
wait_timeout Optional, seconds to wait for state changes, default 120
start_in_background
Optional, "true" to launch virtualOS without bringing
its window to the front, default "false"
Template, service offering or instance details:
template_name VM bundle to clone, overrides the host/extension value
Setup
-----
1. In virtualOS, install a macOS VM to use as template, e.g. "macOS-15".
Set it up as wanted (user account, Remote Login, ...) and shut it down.
2. Build the binary on (or for) the management server's platform, e.g. on
a Linux management server:
cargo build --release
and copy it to every management server:
mkdir -p /usr/share/cloudstack-management/extensions/virtualOS
cp target/release/virtualos /usr/share/cloudstack-management/extensions/virtualOS/virtualos
chmod 755 /usr/share/cloudstack-management/extensions/virtualOS/virtualos
chown -R cloud:cloud /usr/share/cloudstack-management/extensions/virtualOS
The unit tests run with "cargo test".
For SSH key authentication, create a key for the cloud user and add the
public key to ~/.ssh/authorized_keys of the user on the Mac.
3. Register the extension and its custom action (CloudMonkey):
cmk create extension name=virtualOS type=Orchestrator path=virtualos
cmk add customaction extensionid=<id> name=GetIpAddresses resourcetype=VirtualMachine
4. Create a cluster with hypervisor External, register the extension to it,
and add each Mac as a host with url, username and (optionally) ssh_key,
network_mode and bridge_interface as details.
5. Register a template with hypervisor External and extension virtualOS,
using a dummy url, and set the external detail template_name to the name
of the virtualOS VM to clone.
6. Deploy instances from that template, using a service offering with at
least the CPU count and memory macOS needs (e.g. 2 CPUs, 4 GiB).
Limitations
-----------
- One NIC per instance and no VLAN isolation: virtualOS configures a single
NAT or bridged network device. Instances get the CloudStack MAC address
only, so the guest network must be reachable through the Mac's NAT or
bridged networking.
- Apple's macOS license and the Virtualization framework allow at most
two macOS VMs running at the same time per Mac.
- No graceful shutdown: stop and reboot power the guest off.
- Status is derived from the virtualOS process together with the files
of the VM bundle being in use, so a shutdown from inside the guest is
reported as stopped even though its virtualOS window stays open; a
later start closes that window first. VMs started from a virtualOS
window instead of from CloudStack are not seen as running.
- No console access, no root disk resizing and no snapshot support;
virtualOS 3.0 only offers snapshots through its window.
- The bundle name equals the CloudStack internal instance name; renaming
the VM in virtualOS breaks the mapping.
- Tested on macOS 26.6 with virtualOS 3.0 and a macOS 26.6.2 guest
through a CloudStack 24.0.0 management server: deploy (clone with new
machine identifier, CPU, memory and MAC), GetIpAddresses, stop, start,
reboot and destroy with expunge. Templates must be registered with
format EXTERNAL.