Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
8 changes: 4 additions & 4 deletions docs/runbooks/srv3-nbd-proxy-ws-upgrade-dry-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ TOKEN=$(
curl -sk -X POST "https://${BMC_HOST}/login" \
-H 'Content-Type: application/json' \
-d '{"username":"root","password":"0penBmc"}' \
| jq -r '.data // empty'
| jq -r '.token // empty'
)

test -n "$TOKEN" || { echo "FAIL: no session token from /login"; exit 1; }
Expand All @@ -66,7 +66,7 @@ acceptable for this gate; step 4 ensures no media is mounted.
# Primary endpoint (cited in nbd_proxy.dag: /nbd/{slot}, slot=0).
# websocat: exit after 5s idle or first inbound frame; do NOT send NBD server greeting.
timeout 8 websocat -v \
-H "X-Auth-Token: ${TOKEN}" \
-H "Authorization: Token ${TOKEN}" \
"wss://${BMC_HOST}/nbd/0" \
2>&1 | tee /tmp/srv3-nbd0-ws-dry-run.log
```
Expand All @@ -75,7 +75,7 @@ Alternate with `wscat`:

```bash
timeout 8 wscat -c "wss://${BMC_HOST}/nbd/0" \
-H "X-Auth-Token: ${TOKEN}" \
-H "Authorization: Token ${TOKEN}" \
2>&1 | tee /tmp/srv3-nbd0-ws-dry-run.log
```

Expand Down Expand Up @@ -105,7 +105,7 @@ Only if step 2 is inconclusive. Same constraints — upgrade only, no export ser

```bash
timeout 8 websocat -v \
-H "X-Auth-Token: ${TOKEN}" \
-H "Authorization: Token ${TOKEN}" \
"wss://${BMC_HOST}/vm/0/0" \
2>&1 | tee /tmp/srv3-vm00-ws-dry-run.log
```
Expand Down
61 changes: 61 additions & 0 deletions dsl/extdeps/bmc/os_install_actuator_toolchain.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module extdeps.bmc.os_install_actuator_toolchain

import std.types { NonEmptyStr, List }
import extdeps.tools { CliTool }
import extdeps.tools.curl { curl_cli_tool }
import extdeps.tools.websocat { websocat_cli_tool }
import extdeps.tools.nbdkit { nbdkit_cli_tool }
import extdeps.tools.socat { socat_cli_tool }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "github.com/openbmc/docs/blob/master/designs/virtual-media.md"
}
}

type ActuatorHostToolchainGrant {
host_label: NonEmptyStr
required_tools: List<CliTool>
}

data srv3_os_install_actuator_host_label: NonEmptyStr = "install-actuator-host"

data srv3_os_install_actuator_toolchain_grant: ActuatorHostToolchainGrant = ActuatorHostToolchainGrant {
host_label: srv3_os_install_actuator_host_label,
required_tools: [
curl_cli_tool,
websocat_cli_tool,
nbdkit_cli_tool,
socat_cli_tool,
],
}

fn actuator_toolchain_grant_includes_tool(grant: ActuatorHostToolchainGrant, tool_name: NonEmptyStr) -> Bool {
fold(
grant.required_tools,
init: false,
f: (acc, tool) => acc || tool.name == tool_name
)
}

fn srv3_os_install_actuator_toolchain_is_complete() -> Bool {
actuator_toolchain_grant_includes_tool(
grant: srv3_os_install_actuator_toolchain_grant,
tool_name: curl_cli_tool.name
)
&& actuator_toolchain_grant_includes_tool(
grant: srv3_os_install_actuator_toolchain_grant,
tool_name: websocat_cli_tool.name
)
&& actuator_toolchain_grant_includes_tool(
grant: srv3_os_install_actuator_toolchain_grant,
tool_name: nbdkit_cli_tool.name
)
&& actuator_toolchain_grant_includes_tool(
grant: srv3_os_install_actuator_toolchain_grant,
tool_name: socat_cli_tool.name
)
}
33 changes: 31 additions & 2 deletions dsl/extdeps/bmc/webui/nbd_proxy.dag
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
module extdeps.bmc.webui.nbd_proxy

import std.types { Int, List, Bool }
import std.types { Int, List, Bool, NonEmptyStr }
import std.measure { ByteSize }
import std.http_path { PathTemplate, LiteralToken, ParamToken }
import std.http_path {
PathTemplate,
LiteralToken,
ParamToken,
PathParamBinding,
render_path_template,
}
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

Expand Down Expand Up @@ -184,6 +190,29 @@ fn bmcweb_nbd_proxy_endpoint_path_template(variant: BmcwebNbdProxyEndpointVarian
}
}

fn bmcweb_nbd_proxy_path_params(
variant: BmcwebNbdProxyEndpointVariant,
slot: Int,
index: Int,
) -> List<PathParamBinding> {
match variant {
NbdSlotEndpoint => [
PathParamBinding { name: "slot", value: to_string(slot) },
]
VmSlotIndexEndpoint => [
PathParamBinding { name: "slot", value: to_string(slot) },
PathParamBinding { name: "index", value: to_string(index) },
]
}
}

fn bmcweb_nbd_proxy_ws_path(variant: BmcwebNbdProxyEndpointVariant, slot: Int, index: Int) -> NonEmptyStr {
render_path_template(
template: bmcweb_nbd_proxy_endpoint_path_template(variant: variant),
params: bmcweb_nbd_proxy_path_params(variant: variant, slot: slot, index: index),
) as NonEmptyStr
}

type BmcwebNbdProxyWsUpgrade {
endpoint: BmcwebNbdProxyEndpointVariant
requires_upgrade_header: Bool
Expand Down
172 changes: 172 additions & 0 deletions dsl/extdeps/bmc/webui/nbd_proxy_serve.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
module extdeps.bmc.webui.nbd_proxy_serve

import std.types { NonEmptyStr, Int, String, List }
import std.logic { Bool }
import extdeps.external_authority { ExternalAuthority }
import extdeps.tools.curl { curl_cli_tool }
import extdeps.tools.websocat { websocat_cli_tool }
import extdeps.tools.nbdkit { nbdkit_cli_tool }
import extdeps.tools.socat { socat_cli_tool }
import extdeps.languages.bash.program {
ShellWord,
ShellStmt,
ShellProgram,
Lit,
Command,
RawLine,
lit,
command,
assign,
serialize_bash,
}
import extdeps.bmc.webui.nbd_proxy {
extdeps_external_authority_anchor,
nbd_proxy_role_assignment,
NbdServer,
}

data nbd_proxy_serve_transport_authority: ExternalAuthority = extdeps_external_authority_anchor

data bmcweb_session_token_env_ref: NonEmptyStr = concat("$", "BMCWEB_SESSION_TOKEN") as NonEmptyStr

data srv3_nbd_proxy_local_port: Int = 10809

data nbd_proxy_serve_bash_emit_dissolution_trigger: String = "🟡 dissolve-on: nbd_proxy_serve — held-open nbdkit→websocat→socat pipeline serialized via extdeps.languages.bash.program ShellProgram (SCAFFOLD ahead of grammar-row bash_fold emit + host_effect NbdProxyServe transport handler); shell structure is grammar-rows but Background (&), Trap (quoted kill), Pipeline, and quoted-arg forms still ride as opaque Lit text — DISSOLVES WHEN bash_fold grammar adds those nodes and ingest ∘ emit = id with host_effect_apply binding NbdProxyServe without serialize_bash; retire medium_structure_exception_roster entry for dsl/extdeps/bmc/webui/nbd_proxy_serve.dag"

type NbdProxyServeTransportIntent {
bmc_host: NonEmptyStr
session_token: NonEmptyStr
ws_path: NonEmptyStr
iso_path: NonEmptyStr
local_nbd_port: Int
}

data nbd_proxy_serve_cited_tool_names: List<NonEmptyStr> = [
nbdkit_cli_tool.name,
websocat_cli_tool.name,
socat_cli_tool.name,
]

fn nbd_proxy_serve_transport_intent(
bmc_host: NonEmptyStr,
session_token: NonEmptyStr,
ws_path: NonEmptyStr,
iso_path: NonEmptyStr,
local_nbd_port: Int,
) -> NbdProxyServeTransportIntent {
NbdProxyServeTransportIntent {
bmc_host: bmc_host,
session_token: session_token,
ws_path: ws_path,
iso_path: iso_path,
local_nbd_port: local_nbd_port,
}
}

fn bmcweb_session_login_program(bmc_host: NonEmptyStr, request_body_file: NonEmptyStr) -> ShellProgram {
ShellProgram {
set_e: true,
statements: [
command(words: [
lit(text: curl_cli_tool.name),
lit(text: "-sk"),
lit(text: "-X"),
lit(text: "POST"),
lit(text: concat("https://", concat(bmc_host, "/login"))),
lit(text: "-H"),
lit(text: "Content-Type: application/json"),
lit(text: "--data"),
lit(text: concat("@", request_body_file)),
]),
],
}
}

fn nbd_proxy_serve_program(intent: NbdProxyServeTransportIntent) -> ShellProgram {
let port = to_string(intent.local_nbd_port)
let wss_url = concat("wss://", concat(intent.bmc_host, intent.ws_path))
let auth_header = concat("Authorization: Token ", intent.session_token)
let socat_bridge = concat(
"exec:'",
concat(socat_cli_tool.name, concat(" STDIO TCP:127.0.0.1:", concat(port, "'"))),
)
ShellProgram {
set_e: false,
statements: [
command(words: [
lit(text: "set"),
lit(text: "-euo"),
lit(text: "pipefail"),
]),
command(words: [
lit(text: nbdkit_cli_tool.name),
lit(text: "file"),
lit(text: concat("file=", intent.iso_path)),
lit(text: concat("port=", port)),
lit(text: "rdonly=yes"),
lit(text: "&"),
]),
assign(name: "NBDKIT_PID", value: lit(text: "$!")),
command(words: [
lit(text: "trap"),
lit(text: "'kill \"$NBDKIT_PID\"'"),
lit(text: "EXIT"),
lit(text: "INT"),
lit(text: "TERM"),
]),
command(words: [
lit(text: websocat_cli_tool.name),
lit(text: "-b"),
lit(text: "--insecure"),
lit(text: "-H"),
lit(text: auth_header),
lit(text: wss_url),
lit(text: socat_bridge),
]),
],
}
}

fn nbd_proxy_serve_script(intent: NbdProxyServeTransportIntent) -> String {
serialize_bash(p: nbd_proxy_serve_program(intent: intent))
}

fn nbd_proxy_serve_program_statement_count(intent: NbdProxyServeTransportIntent) -> Int {
nbd_proxy_serve_program(intent: intent).statements.length()
}

fn shell_command_leading_lit_text(stmt: ShellStmt) -> String {
match stmt {
Command { words: ws } => fold(ws, init: "", f: (acc, w) => {
if acc != "" {
acc
} else {
match w {
Lit { text: t } => t
_ => ""
}
}
})
_ => ""
}
}

fn shell_program_final_statement(p: ShellProgram) -> ShellStmt {
fold(p.statements, init: RawLine { text: "" }, f: (_, stmt) => stmt)
}

fn nbd_proxy_serve_program_foreground_command_is_websocat(intent: NbdProxyServeTransportIntent) -> Bool {
let program = nbd_proxy_serve_program(intent: intent)
nbd_proxy_serve_program_statement_count(intent: intent) == 5
&& shell_command_leading_lit_text(
stmt: shell_program_final_statement(p: program)
) == websocat_cli_tool.name
}

fn nbd_proxy_serve_requires_host_as_nbd_server() -> Bool {
nbd_proxy_role_assignment.host_program_role == NbdServer
}

fn nbd_proxy_serve_cited_tool_row_count() -> Int {
nbd_proxy_serve_cited_tool_names.length()
}
Loading
Loading