Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 80 additions & 0 deletions docs/how-to-run.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,83 @@ unique local addresses in the subnet of the first Sled Agent: `fd00:1122:3344:1:

Note that Sled Agent runs in the global zone and is the one responsible for bringing up all the other
other services and allocating them with vNICs and IPv6 addresses.

=== How to provision an instance using the CLI

Here are the current steps to provision an instance using the https://github.com/oxidecomputer/cli[oxide]
command line interface.

1. Create an organization and project that the resources will live under:

oxide org create myorg
oxide project create -o myorg myproj

2. Define a global image that will be used as initial disk contents. This can be the alpine.iso image that ships with propolis, or an ISO / raw disk image / etc hosted at a URL:

oxide api /images --method POST --input - <<EOF
{
"name": "alpine",
"description": "boot from propolis zone blob!",
"block_size": 512,
"distribution": "alpine",
"version": "propolis-blob",
"source": {
"type": "you_can_boot_anything_as_long_as_its_alpine"
}
}
EOF

oxide api /images --method POST --input - <<EOF
{
"name": "crucible-tester-sparse",
"description": "boot from a url!",
"block_size": 512,
"distribution": "debian",
"version": "9",
"source": {
"type": "url",
"url": "http://[fd00:1122:3344:101::15]/crucible-tester-sparse.img"
}
}
EOF

3. Create a disk from that global image (note that disk size must be greater than or equal to image size!). The example below creates a disk using the image made from the alpine ISO that ships with propolis, and sets the size to be the exact same as the image size:

oxide api /organizations/myorg/projects/myproj/disks/ --method POST --input - <<EOF
{
"name": "alpine",
"description": "alpine.iso blob",
"block_size": 512,
"size": $(oxide api /images/alpine | jq -r .size),
"disk_source": {
"type": "global_image",
"image_id": "$(oxide api /images/alpine | jq -r .id)"
}
}
EOF

4. Create an instance, attaching the disk created above:

oxide api /organizations/myorg/projects/myproj/instances --method POST --input - <<EOF
{
"name": "myinst",
"description": "my inst",
"hostname": "myinst",
"memory": 1073741824,
"ncpus": 2,
"disks": [
{
"type": "attach",
"name": "alpine"
}
]
}
EOF

5. Optionally, attach to the propolis server serial console:

a. find the zone launched for the instance: `zoneadm list -c | grep oxz_propolis-server`
b. get the instance uuid from the zone name. if the zone's name is `oxz_propolis-server_3b03ad43-4e9b-4f3a-866c-238d9ec4ac45`, then the uuid is `3b03ad43-4e9b-4f3a-866c-238d9ec4ac45`
c. find the propolis server listen address: `pfexec zlogin oxz_propolis-server_3b03ad43-4e9b-4f3a-866c-238d9ec4ac45 svccfg -s svc:/system/illumos/propolis-server:vm-3b03ad43-4e9b-4f3a-866c-238d9ec4ac45 listprop config/server_addr`
d. build and launch propolis-cli, filling in values for IP and PORT based on the listen address: `./target/release/propolis-cli -s <IP> -p <PORT> serial`

57 changes: 57 additions & 0 deletions nexus/src/app/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,63 @@ impl super::Nexus {
&"creating images from snapshots not supported",
));
}

params::ImageSource::YouCanBootAnythingAsLongAsItsAlpine => {
// Each Propolis zone ships with an alpine.iso (it's part of the
// package-manifest.toml blobs), and for development purposes
// allow users to boot that. This should go away when that blob
// does.
let db_block_size = db::model::BlockSize::Traditional;
let block_size: u64 = db_block_size.to_bytes() as u64;

let volume_construction_request = sled_agent_client::types::VolumeConstructionRequest::Volume {
block_size,
sub_volumes: vec![
sled_agent_client::types::VolumeConstructionRequest::File {
block_size,
path: "/opt/oxide/propolis-server/blob/alpine.iso".into(),
}
],
read_only_parent: None,
};

let volume_data =
serde_json::to_string(&volume_construction_request)?;

// Nexus runs in its own zone so we can't ask the propolis zone
// image tar file for size of alpine.iso. Conservatively set the
// size to 100M (at the time of this comment, it's 41M). Any
// disk created from this image has to be larger than it.
let size: u64 = 100 * 1024 * 1024;
let size: external::ByteCount =
size.try_into().map_err(|e| Error::InvalidValue {
label: String::from("size"),
message: format!("size is invalid: {}", e),
})?;

let new_image_volume =
db::model::Volume::new(Uuid::new_v4(), volume_data);
let volume =
self.db_datastore.volume_create(new_image_volume).await?;

db::model::GlobalImage {
identity: db::model::GlobalImageIdentity::new(
Uuid::new_v4(),
params.identity.clone(),
),
volume_id: volume.id(),
url: None,
distribution: "alpine".parse().map_err(|_| {
Error::internal_error(
&"alpine is not a valid distribution?",
)
})?,
version: "propolis-blob".into(),
digest: None,
block_size: db_block_size,
size: size.into(),
}
}
};

self.db_datastore.global_image_create_image(opctx, new_image).await
Expand Down
12 changes: 10 additions & 2 deletions nexus/src/external_api/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,16 @@ pub struct NetworkInterfaceIdentifier {
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ImageSource {
Url { url: String },
Snapshot { id: Uuid },
Url {
url: String,
},
Snapshot {
id: Uuid,
},

/// Boot the Alpine ISO that ships with the Propolis zone. Intended for
/// development purposes only.
YouCanBootAnythingAsLongAsItsAlpine,
}

/// OS image distribution
Expand Down
15 changes: 15 additions & 0 deletions openapi/nexus.json
Original file line number Diff line number Diff line change
Expand Up @@ -6100,6 +6100,21 @@
"id",
"type"
]
},
{
"description": "Boot the Alpine ISO that ships with the Propolis zone. Intended for development purposes only.",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"you_can_boot_anything_as_long_as_its_alpine"
]
}
},
"required": [
"type"
]
}
]
},
Expand Down
4 changes: 2 additions & 2 deletions package-manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ zone = true
[external_package.propolis-server.source]
type = "prebuilt"
repo = "propolis"
commit = "a11ccd5bf001e0c690c67d117f8beb06b0bc6914"
commit = "1538f78c1656bd3ac8ef816f6177ae9b1bef348a"
# The SHA256 digest is automatically posted to:
# https://buildomat.eng.oxide.computer/public/file/oxidecomputer/propolis/image/<commit>/propolis-server.sha256.txt
sha256 = "52dd465bf70f3130e16cfb32eefb87f5d01514f1f60a7dd9a1eca970a0669159"
sha256 = "1e21d95a1254f6796a2a7fee3a49f14988beb5646505fc7c9d669ef49f6a50f3"
2 changes: 1 addition & 1 deletion sled-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ omicron-common = { path = "../common" }
p256 = "0.9.0"
percent-encoding = "2.1.0"
progenitor = { git = "https://github.com/oxidecomputer/progenitor" }
propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "a11ccd5bf001e0c690c67d117f8beb06b0bc6914" }
propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "1538f78c1656bd3ac8ef816f6177ae9b1bef348a" }
rand = { version = "0.8.5", features = ["getrandom"] }
reqwest = { version = "0.11.8", default-features = false, features = ["rustls-tls", "stream"] }
schemars = { version = "0.8.10", features = [ "chrono", "uuid1" ] }
Expand Down