Provisioning
Some resources aren't created or deleted through a direct REST endpoint. Their lifecycle runs
through Waldur's marketplace order flow: you submit an order against an offering, Waldur
processes it asynchronously, and a resource appears when it completes. The CLI exposes this as
provision and terminate verbs.
There are two ways in:
- Typed OpenStack commands —
openstack tenant,openstack instance,openstack volumegetprovision/terminatewith a typed--generate-skeleton(flavor, image, ports, …), covered below. - Generic
marketplace resource— provisions any offering type (SLURM, VMware, Azure, custom, …), with a free-form attributes body. See Provisioning other offerings.
The flow is the same either way: find an offering → submit an order → wait for it → get the resource.
1. Find an offering
Provisioning is always against an offering (a specific provider's OpenStack service). Browse
them under marketplace offering, filtering by type:
1 2 | |
You'll need the offering's url, one of its plans' url, and the target project's url.
2. Build the order
provision takes the whole order body with the same --request / --request-file /
--generate-skeleton options as create. The skeleton is the order envelope —
offering, project, plan, limits, accepting_terms_of_service — with the resource's
typed attributes filled in under attributes, derived from that offering type's own
schema (so an instance skeleton has flavor, image, ports, … and a volume skeleton has
size, type, …):
1 | |
Fill in offering, project, and the attributes you want. As with create, any field left
null — including nested attributes fields — is omitted, so you only fill what you need.
accepting_terms_of_service defaults to true in the skeleton.
Ambient project scope
If you've set a current project, you
can leave project out of the order body entirely — it's filled in from the scope. An
explicit project in the body still overrides it.
A tenant (VPC) order, for example:
1 2 3 4 5 6 7 8 9 10 11 12 | |
3. Provision
1 | |
By default provision submits the order and polls it to completion, then prints the
resulting resource. The order moves through pending/executing states to done; a failed
order (erred, rejected, or canceled) surfaces its error_message as an error and a
non-zero exit.
--no-wait— submit and return the order immediately, without polling. Useful in scripts that track orders themselves.--timeout N— how long to wait for a terminal state before giving up, in seconds (default 600). Timing out doesn't cancel the order; it just stops waiting.--interval N— how often to poll, in seconds (default 3). Raise it for an order you expect to take a while, to cut down on request volume; lower it if you want faster feedback on a quick one.
1 2 3 | |
The provisioned resource object includes both resource_uuid (the OpenStack tenant/instance/
volume itself) and uuid — its marketplace resource UUID, which is what you'll need to
terminate it.
Preview first
Add --dry-run to print the exact order that would be submitted (with the project and
any defaults already filled in) without actually creating anything — see
--dry-run.
Terminating
terminate tears a resource down through the same order flow. It takes the resource's
marketplace_resource_uuid — the marketplace resource, not the plugin resource's own
UUID. You'll find it as the uuid/marketplace_resource_uuid field on the provision result,
or via get/list:
1 | |
Some resources accept termination options as JSON:
1 2 | |
Like provision, terminate waits for the termination order to finish by default;
--no-wait, --timeout N, and --interval N apply the same way.
Provisioning other offerings
marketplace resource provisions any offering type — SLURM allocations, VMware or Azure
VMs, or a provider's custom offering — using the same order flow, just with a free-form
attributes body instead of a typed one (the accepted attributes are offering-specific and not
all modeled in the schema):
1 2 3 4 5 6 7 8 9 10 11 | |
marketplace resource provision --generate-skeleton gives the order envelope with a generic
{name, description} attributes stub — fill in the offering-specific fields yourself (from the
offering in the Waldur portal). Everything the typed commands support applies here too:
--no-wait/--timeout/--interval, --dry-run, and the ambient
--project (leave project out of the body and
it's filled in).
marketplace resource list/get browse provisioned resources (handy for finding the
marketplace_resource_uuid to terminate), and marketplace resource terminate <uuid> tears
any of them down:
1 2 | |
The typed openstack ... provision commands are just a convenience for the three OpenStack
types — anything they can do, marketplace resource can do generically.
Waiting on anything with real async state, not just orders
Resources with genuine async, server-side state to reach — an OpenStack tenant/instance/
volume, a network, subnet, security group, router, port or floating IP, a generic marketplace
resource, and the order itself — get a wait command: polling
on an interval until a --jmespath condition against the fetched object is met, or timing
out. Waldur's API has no server-side push/watch mechanism, so this is a client-side poll, not
an instant notification -- but it saves you from writing your own poll loop. Plain CRUD
resources (a customer, a role, a catalog entry like a flavor) reach their final state
synchronously on create/update, so there's nothing to wait for and no wait command --
check --help on a given resource to see whether it has one.
The condition can be a boolean comparison:
1 | |
or a plain presence check -- anything the expression returns other than false/null counts
as met:
1 | |
--timeout N (default 600s) and --interval N (default 3s) control how long to wait and how
often to poll.
This is what makes --no-wait genuinely useful for fire-and-forget provisioning: submit
without blocking, keep the printed order uuid, and wait on it later — from the same script,
a different process, or a different machine entirely:
1 2 3 4 | |
marketplace order get <uuid> is the one-shot version — no polling, just the current state
(the same object provision's own polling already checks internally, exposed standalone for
when you submitted with --no-wait and only have the order UUID, before any resource exists
to check instead).
Networking inside a tenant
Tenants, instances and volumes are ordered through the marketplace. Everything an instance
plugs into inside a tenant is not: networks, subnets, security groups, routers, ports and
floating IPs are created directly, and each is created asynchronously (Creating → OK).
Most of them have no REST create of their own — you create them through an action on their
parent:
| To create | Command | Parent |
|---|---|---|
| a network | openstack tenant create-network <tenant-uuid> |
tenant |
| a subnet | openstack network create-subnet <network-uuid> |
network |
| a security group (with rules) | openstack tenant create-security-group <tenant-uuid> |
tenant |
| a floating IP | openstack tenant create-floating-ip <tenant-uuid> |
tenant |
| a server group | openstack tenant create-server-group <tenant-uuid> |
tenant |
| a router | openstack router create (the body names the tenant) |
— |
| a port | openstack port create (the body names the network) |
— |
Every one of these takes a request body and has a --generate-skeleton template, which you can
print without a UUID:
1 2 | |
The body is validated against the API's schema before anything is sent, and --dry-run shows
the exact request.
Wait between dependent steps
A network isn't usable until it's OK, and create-subnet refuses a network that isn't. The
create commands return the new object straight away (still Creating), so capture its uuid
and wait on it:
1 2 3 4 5 6 | |
Guard the captured UUID as above: with --format json, an API error is printed as a JSON
{"error": ...} object, so jq -r .uuid alone yields null (or, with // empty, nothing)
and the next command would run against the wrong path.
A new subnet is attached to a router of the tenant unless the body sets
"skip_router_connection": true or names a specific router. subnet connect/disconnect
attach or detach it later.
Security groups and their rules
tenant create-security-group creates a group together with its rules. A rule is
ethertype (IPv4/IPv6), direction (ingress/egress), protocol (tcp, udp,
icmp, an IANA protocol number such as 58 for ICMPv6, or empty for any), a port range, and
either a cidr or a remote_group:
1 2 3 4 5 6 7 8 9 | |
For ICMP (and ICMPv6), the port fields hold the ICMP type and code: use -1 for any. Leaving
them out is rejected (to_port: Empty value is not allowed).
To change the rules of an existing group, security-group set-rules <uuid> takes a JSON
array of rules and replaces the whole rule set, so send every rule you want to keep,
not just the new one:
1 2 | |
An array body is validated as one: a single rule object instead of an array is rejected
before anything is sent. The default group is managed by OpenStack itself and can't be
renamed or deleted.
To attach groups to an instance at creation, list them under security_groups in the
instance order (below). For an existing instance's port, port update-security-groups.
Routers and ports
Most tenants need neither: a tenant's default router connects its subnets, and an instance order creates its own ports. Reach for them when you need to control the topology:
router create/delete,add-router-interface/remove-router-interface(a subnet or port),set-external-gateway/remove-external-gateway, andset-routesfor static routes.port create/update/delete,enable-port/disable-port,enable-port-security/disable-port-security,set-allowed-address-pairs,update-port-ip, andupdate-security-groups.
Port security and allowed address pairs matter for a VM that sends or receives traffic for addresses other than its own — a router or VPN gateway, or a VM running its own nested cloud. Neutron's anti-spoofing drops that traffic until you widen the pairs or disable port security on the port.
Flavors, images and SSH keys
An instance order references a flavor, an image and (optionally) an SSH key by URL. Look the first two up in the tenant's catalog, and register your key once:
1 2 3 4 5 6 7 | |
A flavor's own disk is often small. Set system_volume_size (in MB) in the order to boot
from a larger volume instead, and system_volume_type to pick its type.
Example: an IPv6-only VM
This puts a VM on a tenant network that has only an IPv6 subnet (here 2001:db8:1::/64,
routed from the internet to the tenant's router), reachable over SSH from any IPv6 host. It
uses the commands above end to end; $TENANT is the tenant's own UUID, and $A the API base
URL (https://waldur.example.com/api).
1. Find the subnet, and check how it assigns addresses.
1 2 | |
Waldur can't create IPv6 subnets yet, so this one was created outside it — and Waldur doesn't
show the subnet's IPv6 address mode (SLAAC, DHCPv6, or none) either. If it's SLAAC, the
instance configures its address from the router's advertisements. If it's unset, Neutron
hands out no address at all, and the instance learns it only from its config drive. Set
"config_drive": true in the order either way: it works in both cases, and you can't tell
from Waldur which one you have.
2. Allow SSH and ICMPv6 over IPv6. The groups a tenant starts with usually allow only
IPv4. Create ssh-v6 as in Security groups and their rules.
3. Set DNS in cloud-init if the subnet has none. An IPv6-only VM also can't reach IPv4-only hosts (github.com has no IPv6 address). A DNS64 resolver, such as the public nat64.net service, answers for those hosts with addresses its NAT64 gateway translates:
1 2 3 4 5 6 7 8 9 | |
4. Order the instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 | |
The instance offering is the one scoped to your tenant:
marketplace offering list --filter type=OpenStack.Instance --filter project_uuid=<uuid>.
5. Connect.
1 | |
On a routed IPv6 subnet the instance's own address is public, so there's no floating IP;
internal_ips holds it. (get has no --jmespath, so pipe its --format json output
through jq.)
The bigger picture
An instance needs a tenant (and usually a network/subnet) to exist first, and its flavor and image are looked up within that tenant — so a from-scratch VM is a chain: provision a tenant, create a network and subnet in it, look up the tenant's flavor/image, then provision the instance referencing all of the above. Recipes & tips walks through a complete example.