diff --git a/Cargo.Bazel.Fuzzing.json.lock b/Cargo.Bazel.Fuzzing.json.lock
index d2171526decb..2e2478dc7ace 100644
--- a/Cargo.Bazel.Fuzzing.json.lock
+++ b/Cargo.Bazel.Fuzzing.json.lock
@@ -1,5 +1,5 @@
{
- "checksum": "ada7ff8894ed435c6c2a37398bb9ae44089fd3c8aa2976081d77833ac5d58c83",
+ "checksum": "92fd3df747c08a83977d8eaa3c5ad4d5b1d8a0c6aaa99a790bc6a8f51f42f85f",
"crates": {
"abnf 0.12.0": {
"name": "abnf",
@@ -9954,6 +9954,70 @@
],
"license_file": "LICENSE-APACHE"
},
+ "bstr 0.2.17": {
+ "name": "bstr",
+ "version": "0.2.17",
+ "package_url": "https://github.com/BurntSushi/bstr",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/bstr/0.2.17/download",
+ "sha256": "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "bstr",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "bstr",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "lazy_static",
+ "regex-automata",
+ "unicode"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "lazy_static 1.5.0",
+ "target": "lazy_static"
+ },
+ {
+ "id": "memchr 2.6.4",
+ "target": "memchr"
+ },
+ {
+ "id": "regex-automata 0.1.10",
+ "target": "regex_automata"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "0.2.17"
+ },
+ "license": "MIT OR Apache-2.0",
+ "license_ids": [
+ "Apache-2.0",
+ "MIT"
+ ],
+ "license_file": "LICENSE-APACHE"
+ },
"bstr 1.8.0": {
"name": "bstr",
"version": "1.8.0",
@@ -20301,6 +20365,10 @@
"id": "getrandom 0.2.10",
"target": "getrandom"
},
+ {
+ "id": "goldenfile 1.8.0",
+ "target": "goldenfile"
+ },
{
"id": "group 0.13.0",
"target": "group"
@@ -28156,6 +28224,65 @@
],
"license_file": "LICENSE-APACHE"
},
+ "goldenfile 1.8.0": {
+ "name": "goldenfile",
+ "version": "1.8.0",
+ "package_url": "https://github.com/calder/rust-goldenfile",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/goldenfile/1.8.0/download",
+ "sha256": "cf39e208efa110ca273f7255aea02485103ffcb7e5dfa5e4196b05a02411618e"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "goldenfile",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "goldenfile",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "scopeguard 1.2.0",
+ "target": "scopeguard"
+ },
+ {
+ "id": "similar-asserts 1.7.0",
+ "target": "similar_asserts"
+ },
+ {
+ "id": "tempfile 3.12.0",
+ "target": "tempfile"
+ },
+ {
+ "id": "yansi 1.0.1",
+ "target": "yansi"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "1.8.0"
+ },
+ "license": "MIT",
+ "license_ids": [
+ "MIT"
+ ],
+ "license_file": "LICENSE"
+ },
"governor 0.8.1": {
"name": "governor",
"version": "0.8.1",
@@ -70161,9 +70288,25 @@
],
"crate_features": {
"common": [
+ "bstr",
"default",
"inline",
- "text"
+ "text",
+ "unicode",
+ "unicode-segmentation"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "bstr 0.2.17",
+ "target": "bstr"
+ },
+ {
+ "id": "unicode-segmentation 1.12.0",
+ "target": "unicode_segmentation"
+ }
],
"selects": {}
},
@@ -70176,6 +70319,64 @@
],
"license_file": "LICENSE"
},
+ "similar-asserts 1.7.0": {
+ "name": "similar-asserts",
+ "version": "1.7.0",
+ "package_url": "https://github.com/mitsuhiko/similar-asserts",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/similar-asserts/1.7.0/download",
+ "sha256": "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "similar_asserts",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "similar_asserts",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default",
+ "unicode"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "console 0.15.7",
+ "target": "console"
+ },
+ {
+ "id": "similar 2.3.0",
+ "target": "similar"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "1.7.0"
+ },
+ "license": "Apache-2.0",
+ "license_ids": [
+ "Apache-2.0"
+ ],
+ "license_file": "LICENSE"
+ },
"simple_asn1 0.6.2": {
"name": "simple_asn1",
"version": "0.6.2",
@@ -89831,6 +90032,53 @@
],
"license_file": "LICENSE-APACHE"
},
+ "yansi 1.0.1": {
+ "name": "yansi",
+ "version": "1.0.1",
+ "package_url": "https://github.com/SergioBenitez/yansi",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/yansi/1.0.1/download",
+ "sha256": "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "yansi",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "yansi",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "alloc",
+ "default",
+ "std"
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "1.0.1"
+ },
+ "license": "MIT OR Apache-2.0",
+ "license_ids": [
+ "Apache-2.0",
+ "MIT"
+ ],
+ "license_file": "LICENSE-APACHE"
+ },
"yasna 0.5.2": {
"name": "yasna",
"version": "0.5.2",
@@ -91264,6 +91512,7 @@
"futures-util 0.3.31",
"get_if_addrs 0.5.3",
"getrandom 0.2.10",
+ "goldenfile 1.8.0",
"group 0.13.0",
"hashlink 0.8.4",
"hex 0.4.3",
diff --git a/Cargo.Bazel.Fuzzing.toml.lock b/Cargo.Bazel.Fuzzing.toml.lock
index 875a9139200a..50096d2b726a 100644
--- a/Cargo.Bazel.Fuzzing.toml.lock
+++ b/Cargo.Bazel.Fuzzing.toml.lock
@@ -658,7 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
dependencies = [
"anstyle",
- "bstr",
+ "bstr 1.8.0",
"doc-comment",
"libc",
"predicates",
@@ -1694,6 +1694,17 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "lazy_static",
+ "memchr",
+ "regex-automata 0.1.10",
+]
+
[[package]]
name = "bstr"
version = "1.8.0"
@@ -3462,6 +3473,7 @@ dependencies = [
"futures-util",
"get_if_addrs",
"getrandom 0.2.10",
+ "goldenfile",
"group 0.13.0",
"hashlink",
"hex",
@@ -3714,7 +3726,7 @@ dependencies = [
"wycheproof",
"x509-cert",
"x509-parser 0.16.0",
- "yansi",
+ "yansi 0.5.1",
"zeroize",
"zstd",
]
@@ -4817,6 +4829,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "goldenfile"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf39e208efa110ca273f7255aea02485103ffcb7e5dfa5e4196b05a02411618e"
+dependencies = [
+ "scopeguard",
+ "similar-asserts",
+ "tempfile",
+ "yansi 1.0.1",
+]
+
[[package]]
name = "governor"
version = "0.8.1"
@@ -9472,7 +9496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
"diff",
- "yansi",
+ "yansi 0.5.1",
]
[[package]]
@@ -11788,6 +11812,20 @@ name = "similar"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597"
+dependencies = [
+ "bstr 0.2.17",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "similar-asserts"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+dependencies = [
+ "console 0.15.7",
+ "similar",
+]
[[package]]
name = "simple_asn1"
@@ -14878,6 +14916,12 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
[[package]]
name = "yasna"
version = "0.5.2"
diff --git a/Cargo.Bazel.json.lock b/Cargo.Bazel.json.lock
index 6bc9cec3f7c3..251f87bf4604 100644
--- a/Cargo.Bazel.json.lock
+++ b/Cargo.Bazel.json.lock
@@ -1,5 +1,5 @@
{
- "checksum": "58b0da31d7c9cd4b0c36ed81181d6fc658810ea335a0bd8b4d648dc83860a604",
+ "checksum": "72bed5a3f2cbd0acb2539c4bcdd25f79d0b44efae028df0a9d15fb4571ae91f7",
"crates": {
"abnf 0.12.0": {
"name": "abnf",
@@ -9863,6 +9863,70 @@
],
"license_file": "LICENSE-APACHE"
},
+ "bstr 0.2.17": {
+ "name": "bstr",
+ "version": "0.2.17",
+ "package_url": "https://github.com/BurntSushi/bstr",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/bstr/0.2.17/download",
+ "sha256": "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "bstr",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "bstr",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "lazy_static",
+ "regex-automata",
+ "unicode"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "lazy_static 1.5.0",
+ "target": "lazy_static"
+ },
+ {
+ "id": "memchr 2.7.4",
+ "target": "memchr"
+ },
+ {
+ "id": "regex-automata 0.1.10",
+ "target": "regex_automata"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "0.2.17"
+ },
+ "license": "MIT OR Apache-2.0",
+ "license_ids": [
+ "Apache-2.0",
+ "MIT"
+ ],
+ "license_file": "LICENSE-APACHE"
+ },
"bstr 1.6.0": {
"name": "bstr",
"version": "1.6.0",
@@ -20119,6 +20183,10 @@
"id": "getrandom 0.2.10",
"target": "getrandom"
},
+ {
+ "id": "goldenfile 1.8.0",
+ "target": "goldenfile"
+ },
{
"id": "group 0.13.0",
"target": "group"
@@ -28000,6 +28068,65 @@
],
"license_file": "LICENSE-APACHE"
},
+ "goldenfile 1.8.0": {
+ "name": "goldenfile",
+ "version": "1.8.0",
+ "package_url": "https://github.com/calder/rust-goldenfile",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/goldenfile/1.8.0/download",
+ "sha256": "cf39e208efa110ca273f7255aea02485103ffcb7e5dfa5e4196b05a02411618e"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "goldenfile",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "goldenfile",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "scopeguard 1.2.0",
+ "target": "scopeguard"
+ },
+ {
+ "id": "similar-asserts 1.7.0",
+ "target": "similar_asserts"
+ },
+ {
+ "id": "tempfile 3.12.0",
+ "target": "tempfile"
+ },
+ {
+ "id": "yansi 1.0.1",
+ "target": "yansi"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "1.8.0"
+ },
+ "license": "MIT",
+ "license_ids": [
+ "MIT"
+ ],
+ "license_file": "LICENSE"
+ },
"governor 0.8.1": {
"name": "governor",
"version": "0.8.1",
@@ -70031,9 +70158,25 @@
],
"crate_features": {
"common": [
+ "bstr",
"default",
"inline",
- "text"
+ "text",
+ "unicode",
+ "unicode-segmentation"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "bstr 0.2.17",
+ "target": "bstr"
+ },
+ {
+ "id": "unicode-segmentation 1.12.0",
+ "target": "unicode_segmentation"
+ }
],
"selects": {}
},
@@ -70046,6 +70189,64 @@
],
"license_file": "LICENSE"
},
+ "similar-asserts 1.7.0": {
+ "name": "similar-asserts",
+ "version": "1.7.0",
+ "package_url": "https://github.com/mitsuhiko/similar-asserts",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/similar-asserts/1.7.0/download",
+ "sha256": "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "similar_asserts",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "similar_asserts",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default",
+ "unicode"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "console 0.15.7",
+ "target": "console"
+ },
+ {
+ "id": "similar 2.2.1",
+ "target": "similar"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "1.7.0"
+ },
+ "license": "Apache-2.0",
+ "license_ids": [
+ "Apache-2.0"
+ ],
+ "license_file": "LICENSE"
+ },
"simple_asn1 0.6.2": {
"name": "simple_asn1",
"version": "0.6.2",
@@ -89672,6 +89873,53 @@
],
"license_file": "LICENSE-APACHE"
},
+ "yansi 1.0.1": {
+ "name": "yansi",
+ "version": "1.0.1",
+ "package_url": "https://github.com/SergioBenitez/yansi",
+ "repository": {
+ "Http": {
+ "url": "https://static.crates.io/crates/yansi/1.0.1/download",
+ "sha256": "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "yansi",
+ "crate_root": "src/lib.rs",
+ "srcs": {
+ "allow_empty": true,
+ "include": [
+ "**/*.rs"
+ ]
+ }
+ }
+ }
+ ],
+ "library_target_name": "yansi",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "alloc",
+ "default",
+ "std"
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "1.0.1"
+ },
+ "license": "MIT OR Apache-2.0",
+ "license_ids": [
+ "Apache-2.0",
+ "MIT"
+ ],
+ "license_file": "LICENSE-APACHE"
+ },
"yasna 0.5.2": {
"name": "yasna",
"version": "0.5.2",
@@ -91229,6 +91477,7 @@
"futures-util 0.3.31",
"get_if_addrs 0.5.3",
"getrandom 0.2.10",
+ "goldenfile 1.8.0",
"group 0.13.0",
"hashlink 0.8.3",
"hex 0.4.3",
diff --git a/Cargo.Bazel.toml.lock b/Cargo.Bazel.toml.lock
index 1ac729de6583..37c2b8be7f28 100644
--- a/Cargo.Bazel.toml.lock
+++ b/Cargo.Bazel.toml.lock
@@ -659,7 +659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
dependencies = [
"anstyle",
- "bstr",
+ "bstr 1.6.0",
"doc-comment",
"libc",
"predicates",
@@ -1695,6 +1695,17 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "lazy_static",
+ "memchr",
+ "regex-automata 0.1.10",
+]
+
[[package]]
name = "bstr"
version = "1.6.0"
@@ -3451,6 +3462,7 @@ dependencies = [
"futures-util",
"get_if_addrs",
"getrandom 0.2.10",
+ "goldenfile",
"group 0.13.0",
"hashlink",
"hex",
@@ -3703,7 +3715,7 @@ dependencies = [
"wycheproof",
"x509-cert",
"x509-parser 0.16.0",
- "yansi",
+ "yansi 0.5.1",
"zeroize",
"zstd 0.13.2",
]
@@ -4806,6 +4818,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "goldenfile"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf39e208efa110ca273f7255aea02485103ffcb7e5dfa5e4196b05a02411618e"
+dependencies = [
+ "scopeguard",
+ "similar-asserts",
+ "tempfile",
+ "yansi 1.0.1",
+]
+
[[package]]
name = "governor"
version = "0.8.1"
@@ -9462,7 +9486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
"diff",
- "yansi",
+ "yansi 0.5.1",
]
[[package]]
@@ -11784,6 +11808,20 @@ name = "similar"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
+dependencies = [
+ "bstr 0.2.17",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "similar-asserts"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+dependencies = [
+ "console 0.15.7",
+ "similar",
+]
[[package]]
name = "simple_asn1"
@@ -14873,6 +14911,12 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
[[package]]
name = "yasna"
version = "0.5.2"
diff --git a/Cargo.lock b/Cargo.lock
index 6393fc5d623b..69e7bf3ea4c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2552,9 +2552,12 @@ name = "config"
version = "1.0.0"
dependencies = [
"anyhow",
+ "askama",
"clap 4.5.27",
"config_types",
"deterministic_ips",
+ "goldenfile",
+ "ic-metrics-tool",
"ic-types",
"macaddr",
"network",
@@ -5028,6 +5031,18 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+[[package]]
+name = "goldenfile"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf39e208efa110ca273f7255aea02485103ffcb7e5dfa5e4196b05a02411618e"
+dependencies = [
+ "scopeguard",
+ "similar-asserts",
+ "tempfile",
+ "yansi 1.0.1",
+]
+
[[package]]
name = "governor"
version = "0.8.1"
@@ -21585,6 +21600,20 @@ name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
+dependencies = [
+ "bstr",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "similar-asserts"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+dependencies = [
+ "console",
+ "similar",
+]
[[package]]
name = "simple_asn1"
diff --git a/Cargo.toml b/Cargo.toml
index bcd936495b10..6ed47318ae1e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -563,6 +563,7 @@ flate2 = "1.0.31"
futures = "0.3.31"
futures-util = "0.3.31"
getrandom = { version = "0.2", features = ["custom"] }
+goldenfile = "1.8.0"
hex = { version = "0.4.3", features = ["serde"] }
http = "1.3.1"
http-body = "1.0.1"
diff --git a/bazel/external_crates.bzl b/bazel/external_crates.bzl
index 3efdb4e58a65..46b994468eae 100644
--- a/bazel/external_crates.bzl
+++ b/bazel/external_crates.bzl
@@ -526,6 +526,9 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable
"custom",
],
),
+ "goldenfile": crate.spec(
+ version = "^1.8",
+ ),
"group": crate.spec(
version = "^0.13",
),
diff --git a/ic-os/components/BUILD.bazel b/ic-os/components/BUILD.bazel
index 580aaee6d7f4..e10837be5902 100644
--- a/ic-os/components/BUILD.bazel
+++ b/ic-os/components/BUILD.bazel
@@ -47,7 +47,6 @@ REPO_COMPONENTS = glob(
# files used for testing and development that aren't "used" by any ic-os variant
ignored_repo_components = [
- "hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh",
"networking/dev-certs/canister_http_test_ca.key",
"networking/dev-certs/root_cert_gen.sh",
]
diff --git a/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh b/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh
deleted file mode 100755
index 7b4ec8b0fa30..000000000000
--- a/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh
+++ /dev/null
@@ -1,163 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Generate the GuestOS configuration.
-
-source /opt/ic/bin/logging.sh
-source /opt/ic/bin/metrics.sh
-source /opt/ic/bin/config.sh
-
-# Get keyword arguments
-for argument in "${@}"; do
- case ${argument} in
- -h | --help)
- echo 'Usage:
-Generate GuestOS Configuration
-
-Arguments:
- -h, --help show this help message and exit
- -i=, --input= specify the input template file (Default: /opt/ic/share/guestos.xml.template)
- -m=, --media= specify the config media image file (Default: /run/ic-node/config.img)
- -o=, --output= specify the output configuration file (Default: /var/lib/libvirt/guestos.xml)
-'
- exit 1
- ;;
- -i=* | --input=*)
- INPUT="${argument#*=}"
- shift
- ;;
- -m=* | --media=*)
- MEDIA="${argument#*=}"
- shift
- ;;
- -o=* | --output=*)
- OUTPUT="${argument#*=}"
- shift
- ;;
- *)
- echo "Error: Argument is not supported."
- exit 1
- ;;
- esac
-done
-
-function validate_arguments() {
- if [ "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then
- $0 --help
- fi
-}
-
-# Set arguments if undefined
-INPUT="${INPUT:=/opt/ic/share/guestos.xml.template}"
-MEDIA="${MEDIA:=/run/ic-node/config.img}"
-OUTPUT="${OUTPUT:=/var/lib/libvirt/guestos.xml}"
-
-function read_config_variables() {
- ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway')
- ipv4_address=$(get_config_value '.network_settings.ipv4_config.address')
- ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length')
- ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway')
- domain_name=$(get_config_value '.network_settings.domain_name')
- node_reward_type=$(get_config_value '.icos_settings.node_reward_type')
- nns_urls=$(get_config_value '.icos_settings.nns_urls | join(",")')
- mgmt_mac=$(get_config_value '.icos_settings.mgmt_mac')
-
- use_nns_public_key=$(get_config_value '.icos_settings.use_nns_public_key')
- use_node_operator_private_key=$(get_config_value '.icos_settings.use_node_operator_private_key')
- use_ssh_authorized_keys=$(get_config_value '.icos_settings.use_ssh_authorized_keys')
-
- vm_memory=$(get_config_value '.hostos_settings.vm_memory')
- vm_cpu=$(get_config_value '.hostos_settings.vm_cpu')
- vm_nr_of_vcpus=$(get_config_value '.hostos_settings.vm_nr_of_vcpus')
-}
-
-function assemble_config_media() {
- /opt/ic/bin/config generate-guestos-config
-
- cmd=(/opt/ic/bin/build-bootstrap-config-image.sh ${MEDIA})
- cmd+=(--guestos_config "/boot/config/config-guestos.json")
- if [[ "${use_nns_public_key,,}" == "true" ]]; then
- cmd+=(--nns_public_key "/boot/config/nns_public_key.pem")
- fi
- if [[ "${use_node_operator_private_key,,}" == "true" ]]; then
- cmd+=(--node_operator_private_key "/boot/config/node_operator_private_key.pem")
- fi
- if [[ "${use_ssh_authorized_keys,,}" == "true" ]]; then
- cmd+=(--accounts_ssh_authorized_keys "/boot/config/ssh_authorized_keys")
- fi
- cmd+=(--ipv6_address "$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type GuestOS)")
- cmd+=(--ipv6_gateway "${ipv6_gateway}")
- if [[ -n "$ipv4_address" && -n "$ipv4_prefix_length" && -n "$ipv4_gateway" ]]; then
- cmd+=(--ipv4_address "${ipv4_address}/${ipv4_prefix_length}")
- cmd+=(--ipv4_gateway "${ipv4_gateway}")
- fi
- if [[ -n "$domain_name" ]]; then
- cmd+=(--domain "${domain_name}")
- fi
- if [[ -n "$node_reward_type" ]]; then
- cmd+=(--node_reward_type "${node_reward_type}")
- fi
- cmd+=(--hostname "guest-${mgmt_mac//:/}")
- cmd+=(--nns_urls "${nns_urls}")
-
- # Run the above command
- "${cmd[@]}"
- write_log "Assembling config media for GuestOS: ${MEDIA}"
-}
-
-function generate_guestos_config() {
- MAC_ADDRESS=$(/opt/ic/bin/hostos_tool generate-mac-address --node-type GuestOS)
-
- # Generate inline CPU spec based on mode
- CPU_SPEC=$(mktemp)
- if [ "${vm_cpu}" == "qemu" ]; then
- CPU_DOMAIN="qemu"
- cat >"${CPU_SPEC}" <
-EOF
- else
- CPU_DOMAIN="kvm"
- CORE_COUNT=$((vm_nr_of_vcpus / 4))
- cat >"${CPU_SPEC}" <
-
-
-
-
-EOF
- fi
-
- if [ ! -f "${OUTPUT}" ]; then
- mkdir -p "$(dirname "$OUTPUT")"
- sed -e "s@{{ resources_memory }}@${vm_memory}@" \
- -e "s@{{ nr_of_vcpus }}@${vm_nr_of_vcpus}@" \
- -e "s@{{ mac_address }}@${MAC_ADDRESS}@" \
- -e "s@{{ cpu_domain }}@${CPU_DOMAIN}@" \
- -e "/{{ cpu_spec }}/{r ${CPU_SPEC}" -e "d" -e "}" \
- "${INPUT}" >"${OUTPUT}"
- restorecon -R "$(dirname "$OUTPUT")"
- write_log "Generating GuestOS configuration file: ${OUTPUT}"
- write_metric "hostos_generate_guestos_config" \
- "1" \
- "HostOS generate GuestOS config" \
- "gauge"
- else
- write_log "GuestOS configuration file already exists: ${OUTPUT}"
- write_metric "hostos_generate_guestos_config" \
- "0" \
- "HostOS generate GuestOS config" \
- "gauge"
- fi
-
- rm -f "${CPU_SPEC}"
-}
-
-function main() {
- validate_arguments
- read_config_variables
- assemble_config_media
- generate_guestos_config
-}
-
-main
diff --git a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.service b/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.service
index 0f84a44bee8e..d8ca1824d858 100644
--- a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.service
+++ b/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.service
@@ -9,7 +9,9 @@ RequiresMountsFor=/var
[Service]
Type=oneshot
RemainAfterExit=true
-ExecStart=/opt/ic/bin/generate-guestos-config.sh
+ExecStart=/opt/ic/bin/config generate-guest-vm-config
+StandardOutput=journal+console
+StandardError=journal+console
[Install]
WantedBy=multi-user.target
diff --git a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh b/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh
deleted file mode 100755
index a241db5b1a39..000000000000
--- a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Generate the GuestOS configuration.
-
-source /opt/ic/bin/logging.sh
-source /opt/ic/bin/metrics.sh
-source /opt/ic/bin/config.sh
-
-# Get keyword arguments
-for argument in "${@}"; do
- case ${argument} in
- -h | --help)
- echo 'Usage:
-Generate GuestOS Configuration
-
-Arguments:
- -h, --help show this help message and exit
- -i=, --input= specify the input template file (Default: /opt/ic/share/guestos.xml.template)
- -m=, --media= specify the config media image file (Default: /run/ic-node/config.img)
- -o=, --output= specify the output configuration file (Default: /var/lib/libvirt/guestos.xml)
-'
- exit 1
- ;;
- -i=* | --input=*)
- INPUT="${argument#*=}"
- shift
- ;;
- -m=* | --media=*)
- MEDIA="${argument#*=}"
- shift
- ;;
- -o=* | --output=*)
- OUTPUT="${argument#*=}"
- shift
- ;;
- *)
- echo "Error: Argument is not supported."
- exit 1
- ;;
- esac
-done
-
-function validate_arguments() {
- if [ "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then
- $0 --help
- fi
-}
-
-# Set arguments if undefined
-INPUT="${INPUT:=/opt/ic/share/guestos.xml.template}"
-MEDIA="${MEDIA:=/run/ic-node/config.img}"
-OUTPUT="${OUTPUT:=/var/lib/libvirt/guestos.xml}"
-
-function read_config_variables() {
- ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway')
- ipv4_address=$(get_config_value '.network_settings.ipv4_config.address')
- ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length')
- ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway')
- domain_name=$(get_config_value '.network_settings.domain_name')
- node_reward_type=$(get_config_value '.icos_settings.node_reward_type')
- nns_urls=$(get_config_value '.icos_settings.nns_urls | join(",")')
- mgmt_mac=$(get_config_value '.icos_settings.mgmt_mac')
-
- use_nns_public_key=$(get_config_value '.icos_settings.use_nns_public_key')
- use_node_operator_private_key=$(get_config_value '.icos_settings.use_node_operator_private_key')
-
- vm_memory=$(get_config_value '.hostos_settings.vm_memory')
- vm_cpu=$(get_config_value '.hostos_settings.vm_cpu')
- vm_nr_of_vcpus=$(get_config_value '.hostos_settings.vm_nr_of_vcpus')
-}
-
-function assemble_config_media() {
- /opt/ic/bin/config generate-guestos-config
-
- cmd=(/opt/ic/bin/build-bootstrap-config-image.sh ${MEDIA})
- cmd+=(--guestos_config "/boot/config/config-guestos.json")
- if [[ "${use_nns_public_key,,}" == "true" ]]; then
- cmd+=(--nns_public_key "/boot/config/nns_public_key.pem")
- fi
- if [[ "${use_node_operator_private_key,,}" == "true" ]]; then
- cmd+=(--node_operator_private_key "/boot/config/node_operator_private_key.pem")
- fi
- cmd+=(--ipv6_address "$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type GuestOS)")
- cmd+=(--ipv6_gateway "${ipv6_gateway}")
- if [[ -n "$ipv4_address" && -n "$ipv4_prefix_length" && -n "$ipv4_gateway" ]]; then
- cmd+=(--ipv4_address "${ipv4_address}/${ipv4_prefix_length}")
- cmd+=(--ipv4_gateway "${ipv4_gateway}")
- fi
- if [[ -n "$domain_name" ]]; then
- cmd+=(--domain "${domain_name}")
- fi
- if [[ -n "$node_reward_type" ]]; then
- cmd+=(--node_reward_type "${node_reward_type}")
- fi
- cmd+=(--hostname "guest-${mgmt_mac//:/}")
- cmd+=(--nns_urls "${nns_urls}")
-
- # Run the above command
- "${cmd[@]}"
- write_log "Assembling config media for GuestOS: ${MEDIA}"
-}
-
-function generate_guestos_config() {
- MAC_ADDRESS=$(/opt/ic/bin/hostos_tool generate-mac-address --node-type GuestOS)
-
- # Generate inline CPU spec based on mode
- CPU_SPEC=$(mktemp)
- if [ "${vm_cpu}" == "qemu" ]; then
- CPU_DOMAIN="qemu"
- cat >"${CPU_SPEC}" <
-EOF
- else
- CPU_DOMAIN="kvm"
- CORE_COUNT=$((vm_nr_of_vcpus / 4))
- cat >"${CPU_SPEC}" <
-
-
-
-
-EOF
- fi
-
- if [ ! -f "${OUTPUT}" ]; then
- mkdir -p "$(dirname "$OUTPUT")"
- sed -e "s@{{ resources_memory }}@${vm_memory}@" \
- -e "s@{{ nr_of_vcpus }}@${vm_nr_of_vcpus}@" \
- -e "s@{{ mac_address }}@${MAC_ADDRESS}@" \
- -e "s@{{ cpu_domain }}@${CPU_DOMAIN}@" \
- -e "/{{ cpu_spec }}/{r ${CPU_SPEC}" -e "d" -e "}" \
- "${INPUT}" >"${OUTPUT}"
- restorecon -R "$(dirname "$OUTPUT")"
- write_log "Generating GuestOS configuration file: ${OUTPUT}"
- write_metric "hostos_generate_guestos_config" \
- "1" \
- "HostOS generate GuestOS config" \
- "gauge"
- else
- write_log "GuestOS configuration file already exists: ${OUTPUT}"
- write_metric "hostos_generate_guestos_config" \
- "0" \
- "HostOS generate GuestOS config" \
- "gauge"
- fi
-
- rm -f "${CPU_SPEC}"
-}
-
-function main() {
- validate_arguments
- read_config_variables
- assemble_config_media
- generate_guestos_config
-}
-
-main
diff --git a/ic-os/components/hostos.bzl b/ic-os/components/hostos.bzl
index 1bbff4ba26a7..cc94a82f3988 100644
--- a/ic-os/components/hostos.bzl
+++ b/ic-os/components/hostos.bzl
@@ -4,11 +4,9 @@ Enumerate every component file dependency for HostOS
component_files = {
# hostos-scripts
- Label("hostos-scripts/generate-guestos-config/generate-guestos-config.sh"): "/opt/ic/bin/generate-guestos-config.sh",
Label("hostos-scripts/generate-guestos-config/generate-guestos-config.service"): "/etc/systemd/system/generate-guestos-config.service",
Label("hostos-scripts/guestos/guestos.service"): "/etc/systemd/system/guestos.service",
Label("hostos-scripts/guestos/guestos.sh"): "/opt/ic/bin/guestos.sh",
- Label("hostos-scripts/guestos/guestos.xml.template"): "/opt/ic/share/guestos.xml.template",
Label("hostos-scripts/libvirt/setup-libvirt.sh"): "/opt/ic/bin/setup-libvirt.sh",
Label("hostos-scripts/libvirt/setup-libvirt.service"): "/etc/systemd/system/setup-libvirt.service",
Label("hostos-scripts/misc/setup-var.sh"): "/opt/ic/bin/setup-var.sh",
diff --git a/ic-os/docs/Configuration.adoc b/ic-os/docs/Configuration.adoc
index 6d2a5ceac8b6..bbfbcb602eff 100644
--- a/ic-os/docs/Configuration.adoc
+++ b/ic-os/docs/Configuration.adoc
@@ -22,7 +22,7 @@ HostOS creates a bootstrap config image containing a tar file with the GuestOS c
Refer to link:../components/hostos-scripts/generate-guestos-config.sh[generate-guestos-config.sh] and link:../components/hostos-scripts/build-bootstrap-config-image.sh[build-bootstrap-config-image.sh] for more details.
-When the HostOS launches the GuestOS, the bootstrap config image is attached to the GuestOS as a virtual USB. Refer to link:../components/hostos-scripts/guestos/guestos.xml.template[guestos.xml.template]
+When the HostOS launches the GuestOS, the bootstrap config image is attached to the GuestOS as a virtual USB. Refer to link:../../rs/ic_os/config/templates/guestos_vm_template.xml[guestos_vm_template.xml]
When the GuestOS boots, it checks for available removable media devices (i.e. the bootstrap config image). If such a device is found, the media must contain a VFAT filesystem and a single file called `ic-bootstrap.tar`.
@@ -88,8 +88,8 @@ Consider that values may be controlled by an attacker on boot. Bootstrapping a n
=== Testing
-For testing, to add new configuration bits, you can modify the config tool located at
-link:../../rs/ic_os/config/README.md[rs/ic_os/config]. Or, you may find it easier to update *build-bootstrap-config-image.sh* and *bootstrap-ic-node.sh* directly,
+For testing, to add new configuration bits, you can modify the config tool located at
+link:../../rs/ic_os/config/README.md[rs/ic_os/config]. Or, you may find it easier to update *build-bootstrap-config-image.sh* and *bootstrap-ic-node.sh* directly,
particularly if you wish to add a new configuration file (as opposed to just a new configuration _field_).
* *ic_os config tool* can be run stand-alone to verify that it produces the intended configuration object.
@@ -101,7 +101,7 @@ After all is done, it is advised to prepare a configuration for a single node an
=== Injecting external state
-*Typical bootstrap process:* On first boot, the system will perform technical initialization (filesystems, etc.) and afterwards, initialize itself to act as a node in the IC. The node is initialized using key generation on the node itself (such that the private key never leaves the node) and through joining the IC (the node gets the rest of its state via joining the IC). "Registration" to the target IC is initiated by the node itself by sending a Node Operator-signed "join" request to its NNS.
+*Typical bootstrap process:* On first boot, the system will perform technical initialization (filesystems, etc.) and afterwards, initialize itself to act as a node in the IC. The node is initialized using key generation on the node itself (such that the private key never leaves the node) and through joining the IC (the node gets the rest of its state via joining the IC). "Registration" to the target IC is initiated by the node itself by sending a Node Operator-signed "join" request to its NNS.
However, the typical bootstrap process can be modified such that the node is initialized using externally generated private keys and an externally generated initial state. All "registration" to the target IC is assumed to have been performed by other means.
diff --git a/ic-os/docs/Network-Configuration.adoc b/ic-os/docs/Network-Configuration.adoc
index 32ff2d957602..2b28cf51550e 100644
--- a/ic-os/docs/Network-Configuration.adoc
+++ b/ic-os/docs/Network-Configuration.adoc
@@ -7,7 +7,7 @@ Network configuration details for each IC-OS:
* SetupOS
** Basic network connectivity is checked via pinging nns.ic0.app and the default gateway. Virtually no network traffic goes through SetupOS.
* HostOS
-** The br6 bridge network interface is set up and passed to the GuestOS VM through qemu (refer to guestos.xml.template).
+** The br6 bridge network interface is set up and passed to the GuestOS VM through qemu (refer to guestos_template.xml).
* GuestOS
** An internet connection is received via the br6 bridge interface from qemu.
@@ -50,7 +50,7 @@ Examples:
replica--3cecef6b3799-4wd4u
boundary-3cecef6b3799-4wd4u
-== IPv6 Address
+== IPv6 Address
The IP address can be derived from the MAC address and vice versa: As every virtual machine ends in the same MAC address, the IPv6 address of each node on the same physical machine can be derived, including the hypervisor itself.
In other words, the prefix of the EUI-64 formatted IPv6 SLAAC address is swapped to get to the IPv6 address of the next node.
diff --git a/ic-os/guestos/docs/Interface.adoc b/ic-os/guestos/docs/Interface.adoc
index 0aa51d723362..2a027149071d 100644
--- a/ic-os/guestos/docs/Interface.adoc
+++ b/ic-os/guestos/docs/Interface.adoc
@@ -8,5 +8,5 @@ This document covers the GuestOS interface requirements, providing all the infor
** Details on populating the GuestOS config partition
* link:../../../rs/ic_os/vsock/README.md[Vsock]
** Communication between GuestOS and HostOS
-* link:../../components/hostos-scripts/guestos/guestos.xml.template[guestos.xml.template]
+* link:../../../rs/ic_os/config/templates/guestos_vm_template.xml[guestos_vm_template.xml]
** Details on resource allocations used in production
diff --git a/ic-os/hostos/defs.bzl b/ic-os/hostos/defs.bzl
index 888a2b4c4fe4..f4c4d22c14c5 100644
--- a/ic-os/hostos/defs.bzl
+++ b/ic-os/hostos/defs.bzl
@@ -79,7 +79,7 @@ def image_deps(mode, _malicious = False):
deps.update(image_variants[mode])
if "dev" in mode:
- deps["rootfs"].update({"//ic-os/components:hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh": "/opt/ic/bin/generate-guestos-config.sh:0755"})
+ deps["rootfs"].update({"//rs/ic_os/release:config_dev": "/opt/ic/bin/config:0755"})
return deps
diff --git a/rs/ic_os/config/BUILD.bazel b/rs/ic_os/config/BUILD.bazel
index f62bf6b725b0..2e8e69c0c3be 100644
--- a/rs/ic_os/config/BUILD.bazel
+++ b/rs/ic_os/config/BUILD.bazel
@@ -1,15 +1,25 @@
+load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
package(default_visibility = ["//rs:ic-os-pkg"])
+cargo_build_script(
+ name = "build_script",
+ srcs = ["build.rs"],
+ data = ["templates/guestos_vm_template.xml"],
+)
+
DEPENDENCIES = [
# Keep sorted.
+ ":build_script",
"//rs/ic_os/config_types",
"//rs/ic_os/deterministic_ips",
+ "//rs/ic_os/metrics_tool",
"//rs/ic_os/network",
"//rs/ic_os/utils",
"//rs/types/types",
"@crate_index//:anyhow",
+ "@crate_index//:askama",
"@crate_index//:clap",
"@crate_index//:macaddr",
"@crate_index//:regex",
@@ -22,20 +32,29 @@ DEPENDENCIES = [
DEV_DEPENDENCIES = [
# Keep sorted.
+ "@crate_index//:goldenfile",
"@crate_index//:once_cell",
"@crate_index//:tempfile",
+ "@crate_index//:url",
]
MACRO_DEPENDENCIES = []
ALIASES = {}
+BIN_SOURCES = [
+ "src/main.rs",
+ "src/guest_vm_config.rs",
+]
+
+LIB_SOURCES = glob(
+ ["src/**/*.rs"],
+ exclude = BIN_SOURCES,
+)
+
rust_library(
name = "config_lib",
- srcs = glob(
- ["src/**/*.rs"],
- exclude = ["src/main.rs"],
- ),
+ srcs = LIB_SOURCES,
crate_name = "config",
visibility = [
"//rs:ic-os-pkg",
@@ -46,10 +65,7 @@ rust_library(
rust_library(
name = "config_lib_dev",
- srcs = glob(
- ["src/**/*.rs"],
- exclude = ["src/main.rs"],
- ),
+ srcs = LIB_SOURCES,
crate_features = ["dev"],
crate_name = "config",
visibility = [
@@ -69,7 +85,7 @@ rust_test(
rust_binary(
name = "config",
- srcs = ["src/main.rs"],
+ srcs = BIN_SOURCES,
aliases = ALIASES,
crate_name = "config",
proc_macro_deps = MACRO_DEPENDENCIES,
@@ -77,3 +93,26 @@ rust_binary(
":config_lib",
] + DEPENDENCIES,
)
+
+rust_binary(
+ name = "config_dev",
+ srcs = BIN_SOURCES,
+ aliases = ALIASES,
+ crate_name = "config",
+ proc_macro_deps = MACRO_DEPENDENCIES,
+ deps = [
+ ":config_lib_dev",
+ ] + DEPENDENCIES,
+)
+
+rust_test(
+ name = "config_test",
+ crate = ":config_dev",
+ crate_features = ["dev"],
+ data = glob(["golden/*"]),
+ env = {"CARGO_MANIFEST_DIR": "rs/ic_os/generate_guestos_vm_config"},
+ # Run without sandbox so that goldenfiles can be updated by passing "--test_env UPDATE_GOLDENFILES=1"
+ tags = ["local"],
+ # You may add other deps that are specific to the test configuration
+ deps = DEV_DEPENDENCIES,
+)
diff --git a/rs/ic_os/config/Cargo.toml b/rs/ic_os/config/Cargo.toml
index 1cc713b92968..65e888130354 100644
--- a/rs/ic_os/config/Cargo.toml
+++ b/rs/ic_os/config/Cargo.toml
@@ -5,7 +5,9 @@ edition = "2021"
[dependencies]
anyhow = { workspace = true }
+askama = { workspace = true }
ic-types = { path = "../../types/types" }
+ic-metrics-tool = { path = "../metrics_tool" }
clap = { workspace = true }
macaddr = { workspace = true }
utils = { path = "../utils" }
@@ -22,6 +24,8 @@ tempfile = { workspace = true }
[dev-dependencies]
once_cell = "1.8"
tempfile = { workspace = true }
+url = { workspace = true }
+goldenfile = { workspace = true }
[lib]
name = "config"
diff --git a/rs/ic_os/config/build.rs b/rs/ic_os/config/build.rs
new file mode 100644
index 000000000000..72f98044d382
--- /dev/null
+++ b/rs/ic_os/config/build.rs
@@ -0,0 +1,40 @@
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+// TODO: Remove this workaround when the issue is fixed in askama.
+// https://github.com/askama-rs/askama/issues/461
+//
+// Build reproducibility. askama adds a include_bytes! call when it's generating
+// a template impl so that rustc will recompile the module when the file changes
+// on disk. See https://github.com/djc/askama/blob/180696053833147a61b3348646a953e7d92ae582/askama_shared/src/generator.rs#L141
+// The stringified output of every proc-macro is added to the metadata hash for
+// a crate. That output includes the full filepath to include_bytes!. It may be
+// different on two machines, if they use different tempdir paths for the build.
+// However, if we include the html source directly in the output, no
+// inconsistency is introduced.
+fn main() {
+ println!("cargo:rerun-if-changed=templates/guestos_vm_template.xml");
+ let mut f = File::create(
+ PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("guestos_vm_template.rs"),
+ )
+ .unwrap();
+ f.write_all(
+ format!(
+ r#"
+#[derive(Template)]
+#[template(escape = "xml", source = {:?}, ext = "xml")]
+pub struct GuestOSTemplateProps<'a> {{
+ pub cpu_domain: &'a str,
+ pub vm_memory: u32,
+ pub nr_of_vcpus: u32,
+ pub mac_address: macaddr::MacAddr6,
+ pub config_media: &'a str,
+}}
+ "#,
+ std::fs::read_to_string("templates/guestos_vm_template.xml").unwrap()
+ )
+ .as_bytes(),
+ )
+ .unwrap();
+}
diff --git a/rs/ic_os/config/src/guest_vm_config.rs b/rs/ic_os/config/src/guest_vm_config.rs
new file mode 100644
index 000000000000..13c0a189ce81
--- /dev/null
+++ b/rs/ic_os/config/src/guest_vm_config.rs
@@ -0,0 +1,439 @@
+use anyhow::{bail, Context, Result};
+use askama::Template;
+use clap::Parser;
+use config::guestos_bootstrap_image::BootstrapOptions;
+use config::guestos_config::generate_guestos_config;
+use config::{serialize_and_write_config, DEFAULT_HOSTOS_CONFIG_OBJECT_PATH};
+use config_types::{GuestOSConfig, HostOSConfig, Ipv6Config};
+use deterministic_ips::node_type::NodeType;
+use deterministic_ips::{calculate_deterministic_mac, IpVariant};
+use ic_metrics_tool::{Metric, MetricsWriter};
+use std::fs::{self, File};
+use std::io::Write;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+// See build.rs
+include!(concat!(env!("OUT_DIR"), "/guestos_vm_template.rs"));
+
+/// Generate the GuestOS VM configuration
+#[derive(Parser, Debug)]
+#[command(author, version, about, long_about = None)]
+pub struct GenerateGuestVmConfigArgs {
+ /// Specify the config media image file
+ #[arg(short, long, default_value = "/run/ic-node/config.img")]
+ media: PathBuf,
+
+ /// Specify the output configuration file
+ #[arg(short, long, default_value = "/var/lib/libvirt/guestos.xml")]
+ output: PathBuf,
+
+ /// Path to the input HostOS config file
+ #[arg(short, long, default_value = DEFAULT_HOSTOS_CONFIG_OBJECT_PATH)]
+ config: PathBuf,
+}
+
+/// Generate the GuestOS VM configuration by assembling the bootstrap config media image
+/// and creating the libvirt XML configuration file.
+pub fn generate_guest_vm_config(args: GenerateGuestVmConfigArgs) -> Result<()> {
+ let metrics_writer = MetricsWriter::new(PathBuf::from(
+ "/run/node_exporter/collector_textfile/hostos_generate_guestos_config.prom",
+ ));
+
+ run(args, &metrics_writer, restorecon)
+}
+
+fn run(
+ args: GenerateGuestVmConfigArgs,
+ metrics_writer: &MetricsWriter,
+ // We pass a functor to allow mocking in tests.
+ restorecon: impl Fn(&Path) -> Result<()>,
+) -> Result<()> {
+ let hostos_config: HostOSConfig = serde_json::from_reader(
+ File::open(&args.config).context("Failed to open HostOS config file")?,
+ )
+ .context("Failed to parse HostOS config file")?;
+
+ assemble_config_media(&hostos_config, &args.media)
+ .context("Failed to assemble config media")?;
+
+ if args.output.exists() {
+ metrics_writer.write_metrics(&[Metric::with_annotation(
+ "hostos_generate_guestos_config",
+ 0.0,
+ "HostOS generate GuestOS config",
+ )])?;
+
+ bail!(
+ "GuestOS configuration file already exists: {}",
+ args.output.display()
+ );
+ }
+
+ let vm_config_path = &args.output;
+
+ // Create parent directory if it doesn't exist
+ if let Some(parent) = vm_config_path.parent() {
+ fs::create_dir_all(parent).context("Failed to create output directory")?;
+ }
+
+ File::create(vm_config_path)
+ .context("Failed to create output file")?
+ .write_all(generate_vm_config(&hostos_config, &args.media)?.as_bytes())
+ .context("Failed to write output file")?;
+
+ // Restore SELinux security context
+ if let Some(parent) = vm_config_path.parent() {
+ restorecon(parent)?
+ }
+
+ println!(
+ "Generating GuestOS configuration file: {}",
+ vm_config_path.display(),
+ );
+
+ metrics_writer.write_metrics(&[Metric::with_annotation(
+ "hostos_generate_guestos_config",
+ 1.0,
+ "HostOS generate GuestOS config",
+ )])?;
+
+ Ok(())
+}
+
+fn assemble_config_media(hostos_config: &HostOSConfig, media_path: &Path) -> Result<()> {
+ let guestos_config_file = tempfile::NamedTempFile::new()?;
+ let guestos_config =
+ generate_guestos_config(hostos_config).context("Failed to generate GuestOS config")?;
+ serialize_and_write_config(guestos_config_file.path(), &guestos_config).with_context(|| {
+ format!(
+ "Failed to write GuestOS config to {}",
+ guestos_config_file.path().display()
+ )
+ })?;
+
+ let bootstrap_options =
+ make_bootstrap_options(hostos_config, guestos_config, guestos_config_file.path())?;
+
+ bootstrap_options.build_bootstrap_config_image(media_path)?;
+
+ println!(
+ "Assembling config media for GuestOS: {}",
+ media_path.display()
+ );
+
+ Ok(())
+}
+
+fn make_bootstrap_options(
+ hostos_config: &HostOSConfig,
+ guestos_config: GuestOSConfig,
+ guestos_config_path: &Path,
+) -> Result {
+ let mut bootstrap_options = BootstrapOptions {
+ guestos_config: Some(guestos_config_path.to_path_buf()),
+ ..Default::default()
+ };
+
+ #[cfg(feature = "dev")]
+ if hostos_config.icos_settings.use_ssh_authorized_keys {
+ bootstrap_options.accounts_ssh_authorized_keys =
+ Some(PathBuf::from("/boot/config/ssh_authorized_keys"));
+ }
+
+ if hostos_config.icos_settings.use_nns_public_key {
+ bootstrap_options.nns_public_key = Some(PathBuf::from("/boot/config/nns_public_key.pem"));
+ }
+
+ if hostos_config.icos_settings.use_node_operator_private_key {
+ bootstrap_options.node_operator_private_key =
+ Some(PathBuf::from("/boot/config/node_operator_private_key.pem"));
+ }
+
+ let guestos_ipv6_config = match guestos_config.network_settings.ipv6_config {
+ Ipv6Config::Fixed(ip_config) => ip_config,
+ _ => bail!(
+ "Expected GuestOS IPv6 address to be fixed but was {:?}",
+ guestos_config.network_settings.ipv6_config
+ ),
+ };
+ bootstrap_options.ipv6_address = Some(guestos_ipv6_config.address);
+ bootstrap_options.ipv6_gateway = Some(guestos_ipv6_config.gateway.to_string());
+
+ if let Some(ipv4_config) = &hostos_config.network_settings.ipv4_config {
+ bootstrap_options.ipv4_address = Some(format!(
+ "{}/{}",
+ ipv4_config.address, ipv4_config.prefix_length
+ ));
+ bootstrap_options.ipv4_gateway = Some(ipv4_config.gateway.to_string());
+ }
+
+ if let Some(domain) = &hostos_config.network_settings.domain_name {
+ bootstrap_options.domain = Some(domain.clone());
+ }
+
+ if let Some(node_reward_type) = &hostos_config.icos_settings.node_reward_type {
+ bootstrap_options.node_reward_type = Some(node_reward_type.clone());
+ }
+
+ let hostname = format!(
+ "guest-{}",
+ hostos_config
+ .icos_settings
+ .mgmt_mac
+ .to_string()
+ .replace(":", "")
+ );
+ bootstrap_options.hostname = Some(hostname);
+
+ bootstrap_options.nns_urls = hostos_config
+ .icos_settings
+ .nns_urls
+ .iter()
+ .map(|url| url.to_string())
+ .collect();
+
+ Ok(bootstrap_options)
+}
+
+/// Generate the GuestOS VM libvirt XML configuration and return it as String.
+fn generate_vm_config(config: &HostOSConfig, media_path: &Path) -> Result {
+ let mac_address = calculate_deterministic_mac(
+ &config.icos_settings.mgmt_mac,
+ config.icos_settings.deployment_environment,
+ IpVariant::V6,
+ NodeType::GuestOS,
+ );
+
+ let cpu_domain = if config.hostos_settings.vm_cpu == "qemu" {
+ "qemu"
+ } else {
+ "kvm"
+ };
+
+ GuestOSTemplateProps {
+ cpu_domain,
+ vm_memory: config.hostos_settings.vm_memory,
+ nr_of_vcpus: config.hostos_settings.vm_nr_of_vcpus,
+ mac_address,
+ config_media: &media_path.display().to_string(),
+ }
+ .render()
+ .context("Failed to render GuestOS VM XML template")
+}
+
+fn restorecon(path: &Path) -> Result<()> {
+ Command::new("restorecon")
+ .arg("-R")
+ .arg(path)
+ .status()?
+ .success()
+ .then_some(())
+ .context("Failed to run restorecon")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use config_types::{
+ DeploymentEnvironment, DeterministicIpv6Config, HostOSConfig, HostOSSettings, ICOSSettings,
+ Ipv4Config, Ipv6Config, Logging, NetworkSettings,
+ };
+ use std::os::unix::prelude::MetadataExt;
+ use std::path::Path;
+ use tempfile::tempdir;
+
+ fn create_test_hostos_config() -> HostOSConfig {
+ HostOSConfig {
+ config_version: "1.0".to_string(),
+ network_settings: NetworkSettings {
+ ipv6_config: Ipv6Config::Deterministic(DeterministicIpv6Config {
+ prefix: "2001:db8::".to_string(),
+ prefix_length: 64,
+ gateway: "2001:db8::ffff".parse().unwrap(),
+ }),
+ ipv4_config: Some(Ipv4Config {
+ address: "192.168.1.2".parse().unwrap(),
+ gateway: "192.168.1.1".parse().unwrap(),
+ prefix_length: 24,
+ }),
+ domain_name: Some("test.domain".to_string()),
+ },
+ icos_settings: ICOSSettings {
+ node_reward_type: Some("type3.1".to_string()),
+ mgmt_mac: "00:11:22:33:44:55".parse().unwrap(),
+ deployment_environment: DeploymentEnvironment::Testnet,
+ logging: Logging {
+ elasticsearch_hosts: None,
+ elasticsearch_tags: None,
+ },
+ use_nns_public_key: false,
+ nns_urls: vec![url::Url::parse("https://example.com").unwrap()],
+ use_node_operator_private_key: false,
+ use_ssh_authorized_keys: false,
+ icos_dev_settings: Default::default(),
+ },
+ hostos_settings: HostOSSettings {
+ vm_memory: 490,
+ vm_cpu: "qemu".to_string(),
+ vm_nr_of_vcpus: 56,
+ verbose: false,
+ },
+ guestos_settings: Default::default(),
+ }
+ }
+
+ fn mock_restorecon(_path: &Path) -> Result<()> {
+ Ok(())
+ }
+
+ #[test]
+ fn test_make_bootstrap_options() {
+ let mut config = create_test_hostos_config();
+ config.icos_settings.use_nns_public_key = true;
+ config.icos_settings.use_ssh_authorized_keys = true;
+ config.icos_settings.use_node_operator_private_key = true;
+
+ let guestos_config = generate_guestos_config(&config).unwrap();
+
+ let options =
+ make_bootstrap_options(&config, guestos_config, Path::new("/tmp/test")).unwrap();
+
+ assert_eq!(
+ options,
+ BootstrapOptions {
+ ipv6_address: Some("2001:db8::6801:aeff:fe1a:9bb/64".to_string()),
+ ipv6_gateway: Some("2001:db8::ffff".to_string()),
+ ipv4_address: Some("192.168.1.2/24".to_string()),
+ ipv4_gateway: Some("192.168.1.1".to_string()),
+ domain: Some("test.domain".to_string()),
+ node_reward_type: Some("type3.1".to_string()),
+ hostname: Some("guest-001122334455".to_string()),
+ nns_urls: vec!["https://example.com/".to_string()],
+ guestos_config: Some(PathBuf::from("/tmp/test")),
+ nns_public_key: Some(PathBuf::from("/boot/config/nns_public_key.pem")),
+ node_operator_private_key: Some(PathBuf::from(
+ "/boot/config/node_operator_private_key.pem"
+ )),
+ #[cfg(feature = "dev")]
+ accounts_ssh_authorized_keys: Some(PathBuf::from(
+ "/boot/config/ssh_authorized_keys"
+ )),
+ ..Default::default()
+ }
+ );
+ }
+
+ // Temporarily comment these out until the next PR where we check in the golden files
+ // (we do that to avoid messing up git history).
+
+ // fn goldenfiles_path() -> PathBuf {
+ // let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+ // path.push("golden");
+ // path
+ // }
+ //
+ // fn test_vm_config(cpu_type: &str, filename: &str) {
+ // let mut mint = Mint::new(goldenfiles_path());
+ // let mut config = create_test_hostos_config();
+ //
+ // config.hostos_settings = HostOSSettings {
+ // vm_memory: 490,
+ // vm_cpu: cpu_type.to_string(),
+ // vm_nr_of_vcpus: 56,
+ // verbose: false,
+ // };
+ //
+ // let vm_config = generate_vm_config(&config, Path::new("/tmp/config.img")).unwrap();
+ // fs::write(mint.new_goldenpath(filename).unwrap(), vm_config).unwrap();
+ // }
+ //
+ // #[test]
+ // fn test_generate_vm_config_qemu() {
+ // test_vm_config("qemu", "guestos_vm_qemu.xml");
+ // }
+ //
+ // #[test]
+ // fn test_generate_vm_config_kvm() {
+ // test_vm_config("kvm", "guestos_vm_kvm.xml");
+ // }
+
+ #[test]
+ fn test_run_success() {
+ let temp_dir = tempdir().unwrap();
+ let hostos_config_path = temp_dir.path().join("hostos.json");
+ let media_path = temp_dir.path().join("config.img");
+ let vm_config_path = temp_dir.path().join("guestos.xml");
+ let metrics_path = temp_dir.path().join("metrics.prom");
+
+ serialize_and_write_config(&hostos_config_path, &create_test_hostos_config()).unwrap();
+
+ let args = GenerateGuestVmConfigArgs {
+ media: media_path.clone(),
+ output: vm_config_path.clone(),
+ config: hostos_config_path.clone(),
+ };
+
+ let result = run(
+ args,
+ &MetricsWriter::new(metrics_path.clone()),
+ mock_restorecon,
+ );
+ assert!(result.is_ok(), "{result:?}");
+
+ assert_eq!(
+ fs::read_to_string(metrics_path).unwrap(),
+ "# HELP hostos_generate_guestos_config HostOS generate GuestOS config\n\
+ # TYPE hostos_generate_guestos_config counter\n\
+ hostos_generate_guestos_config 1\n"
+ );
+
+ assert!(media_path.metadata().unwrap().size() > 0);
+ assert!(vm_config_path.metadata().unwrap().size() > 0);
+ }
+
+ #[test]
+ fn test_run_existing_output_file() {
+ let temp_dir = tempdir().unwrap();
+ let hostos_config_path = temp_dir.path().join("hostos.json");
+ let media_path = temp_dir.path().join("config.img");
+ let vm_config_path = temp_dir.path().join("guestos.xml");
+ let metrics_path = temp_dir.path().join("metrics.prom");
+
+ serialize_and_write_config(&hostos_config_path, &create_test_hostos_config()).unwrap();
+
+ // Create the output file so it already exists
+ fs::write(&vm_config_path, "test").unwrap();
+
+ let args = GenerateGuestVmConfigArgs {
+ media: media_path,
+ output: vm_config_path,
+ config: hostos_config_path,
+ };
+
+ let result_err = run(
+ args,
+ &MetricsWriter::new(metrics_path.clone()),
+ mock_restorecon,
+ )
+ .unwrap_err();
+
+ assert!(
+ result_err.to_string().contains("already exists"),
+ "{result_err:?}"
+ );
+
+ assert_eq!(
+ fs::read_to_string(metrics_path).unwrap(),
+ "# HELP hostos_generate_guestos_config HostOS generate GuestOS config\n\
+ # TYPE hostos_generate_guestos_config counter\n\
+ hostos_generate_guestos_config 0\n"
+ )
+ }
+
+ #[test]
+ fn ensure_tested_with_dev() {
+ // Ensure that the test is run with the dev feature enabled.
+ assert!(cfg!(feature = "dev"));
+ }
+}
diff --git a/rs/ic_os/config/src/guestos_bootstrap_image.rs b/rs/ic_os/config/src/guestos_bootstrap_image.rs
index 878878544428..f992cb27afbb 100644
--- a/rs/ic_os/config/src/guestos_bootstrap_image.rs
+++ b/rs/ic_os/config/src/guestos_bootstrap_image.rs
@@ -11,7 +11,7 @@ use std::process::Command;
use ic_types::malicious_behaviour::MaliciousBehaviour;
/// Configuration options for GuestOS bootstrap image/tar creation.
-#[derive(Default, Debug, Clone)]
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct BootstrapOptions {
/// The serialized GuestOS config object.
pub guestos_config: Option,
@@ -538,6 +538,7 @@ mod tests {
guestos_config: Some(config_path),
nns_public_key: Some(nns_key_path),
node_operator_private_key: Some(node_key_path),
+ #[cfg(feature = "dev")]
accounts_ssh_authorized_keys: Some(ssh_keys_dir),
ic_crypto: Some(crypto_dir),
ic_state: Some(state_dir),
@@ -553,10 +554,15 @@ mod tests {
nns_urls: vec!["url1".to_string(), "url2".to_string()],
backup_retention_time_sec: Some(3600),
backup_purging_interval_sec: Some(300),
+ #[cfg(feature = "dev")]
malicious_behavior: Some(MaliciousBehaviour::new(true)),
+ #[cfg(feature = "dev")]
query_stats_epoch_length: Some(60),
+ #[cfg(feature = "dev")]
bitcoind_addr: Some("127.0.0.1:8332".to_string()),
+ #[cfg(feature = "dev")]
jaeger_addr: Some("127.0.0.1:14250".to_string()),
+ #[cfg(feature = "dev")]
socks_proxy: Some("socks5://127.0.0.1:1080".to_string()),
};
@@ -671,4 +677,10 @@ mod tests {
Ok(())
}
+
+ #[test]
+ fn ensure_tested_with_dev() {
+ // Ensure that the test is run with the dev feature enabled.
+ assert!(cfg!(feature = "dev"));
+ }
}
diff --git a/rs/ic_os/config/src/main.rs b/rs/ic_os/config/src/main.rs
index 68a662ecad74..23df4bef3cc4 100644
--- a/rs/ic_os/config/src/main.rs
+++ b/rs/ic_os/config/src/main.rs
@@ -1,20 +1,22 @@
+use crate::guest_vm_config::{generate_guest_vm_config, GenerateGuestVmConfigArgs};
use anyhow::Result;
use clap::{Args, Parser, Subcommand};
use config::config_ini::{get_config_ini_settings, ConfigIniSettings};
use config::deployment_json::get_deployment_settings;
+use config::generate_testnet_config::{
+ generate_testnet_config, GenerateTestnetConfigArgs, Ipv6ConfigType,
+};
+use config::guestos_config::generate_guestos_config;
use config::serialize_and_write_config;
use config::update_config::{update_guestos_config, update_hostos_config};
+use config_types::*;
use macaddr::MacAddr6;
use network::resolve_mgmt_mac;
use regex::Regex;
use std::fs::File;
use std::path::{Path, PathBuf};
-use config::generate_testnet_config::{
- generate_testnet_config, GenerateTestnetConfigArgs, Ipv6ConfigType,
-};
-use config::guestos_config::generate_guestos_config;
-use config_types::*;
+mod guest_vm_config;
#[derive(Subcommand)]
#[allow(clippy::large_enum_variant)]
@@ -58,6 +60,9 @@ pub enum Commands {
#[arg(long, default_value = config::DEFAULT_HOSTOS_CONFIG_OBJECT_PATH, value_name = "config.json")]
hostos_config_json_path: PathBuf,
},
+ /// Generates the GuestOS VM configuration by assembling the bootstrap config media image
+ /// and creating the libvirt XML configuration file.
+ GenerateGuestVmConfig(GenerateGuestVmConfigArgs),
}
#[derive(Parser)]
@@ -362,5 +367,6 @@ pub fn main() -> Result<()> {
println!("No command provided. Use --help for usage information.");
Ok(())
}
+ Some(Commands::GenerateGuestVmConfig(args)) => generate_guest_vm_config(args),
}
}
diff --git a/ic-os/components/hostos-scripts/guestos/guestos.xml.template b/rs/ic_os/config/templates/guestos_vm_template.xml
old mode 100755
new mode 100644
similarity index 92%
rename from ic-os/components/hostos-scripts/guestos/guestos.xml.template
rename to rs/ic_os/config/templates/guestos_vm_template.xml
index e03dac16178f..e214573f5af2
--- a/ic-os/components/hostos-scripts/guestos/guestos.xml.template
+++ b/rs/ic_os/config/templates/guestos_vm_template.xml
@@ -1,4 +1,4 @@
-
+
guestos
fd897da5-8017-41c8-8575-a706dba30766
@@ -6,10 +6,20 @@
- {{ resources_memory }}
- {{ resources_memory }}
- {{ nr_of_vcpus }}
- {{ cpu_spec }}
+ {{vm_memory}}
+ {{vm_memory}}
+ {{nr_of_vcpus}}
+
+ {%- if cpu_domain == "qemu" %}
+
+ {% else %}
+
+
+
+
+
+ {% endif -%}
+
/machine
@@ -46,7 +56,7 @@
-
+
@@ -122,7 +132,7 @@
-
+
diff --git a/rs/ic_os/metrics_tool/BUILD.bazel b/rs/ic_os/metrics_tool/BUILD.bazel
index b182a7a8a935..f9ceeaf31117 100644
--- a/rs/ic_os/metrics_tool/BUILD.bazel
+++ b/rs/ic_os/metrics_tool/BUILD.bazel
@@ -26,7 +26,10 @@ rust_library(
aliases = ALIASES,
crate_name = "ic_metrics_tool",
proc_macro_deps = MACRO_DEPENDENCIES,
- visibility = ["//rs:system-tests-pkg"],
+ visibility = [
+ "//rs:ic-os-pkg",
+ "//rs:system-tests-pkg",
+ ],
deps = DEPENDENCIES,
)
diff --git a/rs/ic_os/release/BUILD.bazel b/rs/ic_os/release/BUILD.bazel
index 902e58a30828..62f75459bf8e 100644
--- a/rs/ic_os/release/BUILD.bazel
+++ b/rs/ic_os/release/BUILD.bazel
@@ -9,6 +9,7 @@ OBJECTS = {
"nft-exporter": "//rs/ic_os/nft_exporter:nft-exporter",
"setupos_tool": "//rs/ic_os/os_tools/setupos_tool:setupos_tool",
"config": "//rs/ic_os/config:config",
+ "config_dev": "//rs/ic_os/config:config_dev",
"vsock_guest": "//rs/ic_os/vsock/guest:vsock_guest",
"vsock_host": "//rs/ic_os/vsock/host:vsock_host",
"metrics-proxy": "@crate_index//:metrics-proxy__metrics-proxy",