admin api, cliv2: implement garage layout show using api functions
All checks were successful
ci/woodpecker/push/debug Pipeline was successful
All checks were successful
ci/woodpecker/push/debug Pipeline was successful
This commit is contained in:
parent
913e6da41b
commit
004866caac
5 changed files with 198 additions and 194 deletions
|
@ -1899,6 +1899,7 @@
|
||||||
"required": [
|
"required": [
|
||||||
"version",
|
"version",
|
||||||
"roles",
|
"roles",
|
||||||
|
"partitionSize",
|
||||||
"parameters",
|
"parameters",
|
||||||
"stagedRoleChanges"
|
"stagedRoleChanges"
|
||||||
],
|
],
|
||||||
|
@ -1906,10 +1907,15 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"$ref": "#/components/schemas/LayoutParameters"
|
"$ref": "#/components/schemas/LayoutParameters"
|
||||||
},
|
},
|
||||||
|
"partitionSize": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/NodeRoleResp"
|
"$ref": "#/components/schemas/LayoutNodeRole"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stagedParameters": {
|
"stagedParameters": {
|
||||||
|
@ -2059,6 +2065,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"LayoutNodeRole": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"zone",
|
||||||
|
"tags"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"capacity": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"usableCapacity": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"zone": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"LayoutParameters": {
|
"LayoutParameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -2853,6 +2897,36 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"NodeAssignedRole": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"zone",
|
||||||
|
"tags"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"capacity": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zone": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"NodeResp": {
|
"NodeResp": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -2916,7 +2990,7 @@
|
||||||
"type": "null"
|
"type": "null"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/NodeRoleResp"
|
"$ref": "#/components/schemas/NodeAssignedRole"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2986,36 +3060,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"NodeRoleResp": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"id",
|
|
||||||
"zone",
|
|
||||||
"tags"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"capacity": {
|
|
||||||
"type": [
|
|
||||||
"integer",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"format": "int64",
|
|
||||||
"minimum": 0
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zone": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PreviewClusterLayoutChangesResponse": {
|
"PreviewClusterLayoutChangesResponse": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -174,7 +174,7 @@ pub struct GetClusterStatusResponse {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NodeResp {
|
pub struct NodeResp {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub role: Option<NodeRoleResp>,
|
pub role: Option<NodeAssignedRole>,
|
||||||
#[schema(value_type = Option<String> )]
|
#[schema(value_type = Option<String> )]
|
||||||
pub addr: Option<SocketAddr>,
|
pub addr: Option<SocketAddr>,
|
||||||
pub hostname: Option<String>,
|
pub hostname: Option<String>,
|
||||||
|
@ -189,7 +189,7 @@ pub struct NodeResp {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NodeRoleResp {
|
pub struct NodeAssignedRole {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub zone: String,
|
pub zone: String,
|
||||||
pub capacity: Option<u64>,
|
pub capacity: Option<u64>,
|
||||||
|
@ -272,12 +272,23 @@ pub struct GetClusterLayoutRequest;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetClusterLayoutResponse {
|
pub struct GetClusterLayoutResponse {
|
||||||
pub version: u64,
|
pub version: u64,
|
||||||
pub roles: Vec<NodeRoleResp>,
|
pub roles: Vec<LayoutNodeRole>,
|
||||||
|
pub partition_size: u64,
|
||||||
pub parameters: LayoutParameters,
|
pub parameters: LayoutParameters,
|
||||||
pub staged_role_changes: Vec<NodeRoleChange>,
|
pub staged_role_changes: Vec<NodeRoleChange>,
|
||||||
pub staged_parameters: Option<LayoutParameters>,
|
pub staged_parameters: Option<LayoutParameters>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct LayoutNodeRole {
|
||||||
|
pub id: String,
|
||||||
|
pub zone: String,
|
||||||
|
pub capacity: Option<u64>,
|
||||||
|
pub usable_capacity: Option<u64>,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NodeRoleChange {
|
pub struct NodeRoleChange {
|
||||||
|
@ -306,13 +317,13 @@ pub enum NodeRoleChangeEnum {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
#[derive(Copy, Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LayoutParameters {
|
pub struct LayoutParameters {
|
||||||
pub zone_redundancy: ZoneRedundancy,
|
pub zone_redundancy: ZoneRedundancy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
#[derive(Copy, Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ZoneRedundancy {
|
pub enum ZoneRedundancy {
|
||||||
AtLeast(usize),
|
AtLeast(usize),
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl RequestHandler for GetClusterStatusRequest {
|
||||||
|
|
||||||
for (id, _, role) in layout.current().roles.items().iter() {
|
for (id, _, role) in layout.current().roles.items().iter() {
|
||||||
if let layout::NodeRoleV(Some(r)) = role {
|
if let layout::NodeRoleV(Some(r)) = role {
|
||||||
let role = NodeRoleResp {
|
let role = NodeAssignedRole {
|
||||||
id: hex::encode(id),
|
id: hex::encode(id),
|
||||||
zone: r.zone.to_string(),
|
zone: r.zone.to_string(),
|
||||||
capacity: r.capacity,
|
capacity: r.capacity,
|
||||||
|
@ -182,16 +182,21 @@ impl RequestHandler for GetClusterLayoutRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_cluster_layout(layout: &layout::LayoutHistory) -> GetClusterLayoutResponse {
|
fn format_cluster_layout(layout: &layout::LayoutHistory) -> GetClusterLayoutResponse {
|
||||||
let roles = layout
|
let current = layout.current();
|
||||||
.current()
|
|
||||||
|
let roles = current
|
||||||
.roles
|
.roles
|
||||||
.items()
|
.items()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(k, _, v)| v.0.clone().map(|x| (k, x)))
|
.filter_map(|(k, _, v)| v.0.clone().map(|x| (k, x)))
|
||||||
.map(|(k, v)| NodeRoleResp {
|
.map(|(k, v)| LayoutNodeRole {
|
||||||
id: hex::encode(k),
|
id: hex::encode(k),
|
||||||
zone: v.zone.clone(),
|
zone: v.zone.clone(),
|
||||||
capacity: v.capacity,
|
capacity: v.capacity,
|
||||||
|
usable_capacity: current
|
||||||
|
.get_node_usage(k)
|
||||||
|
.ok()
|
||||||
|
.map(|x| x as u64 * current.partition_size),
|
||||||
tags: v.tags.clone(),
|
tags: v.tags.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -202,7 +207,7 @@ fn format_cluster_layout(layout: &layout::LayoutHistory) -> GetClusterLayoutResp
|
||||||
.roles
|
.roles
|
||||||
.items()
|
.items()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(k, _, v)| layout.current().roles.get(k) != Some(v))
|
.filter(|(k, _, v)| current.roles.get(k) != Some(v))
|
||||||
.map(|(k, _, v)| match &v.0 {
|
.map(|(k, _, v)| match &v.0 {
|
||||||
None => NodeRoleChange {
|
None => NodeRoleChange {
|
||||||
id: hex::encode(k),
|
id: hex::encode(k),
|
||||||
|
@ -219,17 +224,17 @@ fn format_cluster_layout(layout: &layout::LayoutHistory) -> GetClusterLayoutResp
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let staged_parameters = if *layout.staging.get().parameters.get() != layout.current().parameters
|
let staged_parameters = if *layout.staging.get().parameters.get() != current.parameters {
|
||||||
{
|
|
||||||
Some((*layout.staging.get().parameters.get()).into())
|
Some((*layout.staging.get().parameters.get()).into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
GetClusterLayoutResponse {
|
GetClusterLayoutResponse {
|
||||||
version: layout.current().version,
|
version: current.version,
|
||||||
roles,
|
roles,
|
||||||
parameters: layout.current().parameters.into(),
|
partition_size: current.partition_size,
|
||||||
|
parameters: current.parameters.into(),
|
||||||
staged_role_changes,
|
staged_role_changes,
|
||||||
staged_parameters,
|
staged_parameters,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use bytesize::ByteSize;
|
|
||||||
|
|
||||||
use format_table::format_table;
|
use format_table::format_table;
|
||||||
use garage_util::error::*;
|
use garage_util::error::*;
|
||||||
|
|
||||||
|
@ -9,54 +7,6 @@ use garage_rpc::*;
|
||||||
|
|
||||||
use crate::cli::structs::*;
|
use crate::cli::structs::*;
|
||||||
|
|
||||||
pub async fn cmd_show_layout(
|
|
||||||
rpc_cli: &Endpoint<SystemRpc, ()>,
|
|
||||||
rpc_host: NodeID,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let layout = fetch_layout(rpc_cli, rpc_host).await?;
|
|
||||||
|
|
||||||
println!("==== CURRENT CLUSTER LAYOUT ====");
|
|
||||||
print_cluster_layout(layout.current(), "No nodes currently have a role in the cluster.\nSee `garage status` to view available nodes.");
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"Current cluster layout version: {}",
|
|
||||||
layout.current().version
|
|
||||||
);
|
|
||||||
|
|
||||||
let has_role_changes = print_staging_role_changes(&layout);
|
|
||||||
if has_role_changes {
|
|
||||||
let v = layout.current().version;
|
|
||||||
let res_apply = layout.apply_staged_changes(Some(v + 1));
|
|
||||||
|
|
||||||
// this will print the stats of what partitions
|
|
||||||
// will move around when we apply
|
|
||||||
match res_apply {
|
|
||||||
Ok((layout, msg)) => {
|
|
||||||
println!();
|
|
||||||
println!("==== NEW CLUSTER LAYOUT AFTER APPLYING CHANGES ====");
|
|
||||||
print_cluster_layout(layout.current(), "No nodes have a role in the new layout.");
|
|
||||||
println!();
|
|
||||||
|
|
||||||
for line in msg.iter() {
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
println!("To enact the staged role changes, type:");
|
|
||||||
println!();
|
|
||||||
println!(" garage layout apply --version {}", v + 1);
|
|
||||||
println!();
|
|
||||||
println!("You can also revert all proposed changes with: garage layout revert");
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error while trying to compute the assignment: {}", e);
|
|
||||||
println!("This new layout cannot yet be applied.");
|
|
||||||
println!("You can also revert all proposed changes with: garage layout revert");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn cmd_layout_history(
|
pub async fn cmd_layout_history(
|
||||||
rpc_cli: &Endpoint<SystemRpc, ()>,
|
rpc_cli: &Endpoint<SystemRpc, ()>,
|
||||||
rpc_host: NodeID,
|
rpc_host: NodeID,
|
||||||
|
@ -252,88 +202,3 @@ pub async fn send_layout(
|
||||||
.await??;
|
.await??;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_cluster_layout(layout: &LayoutVersion, empty_msg: &str) {
|
|
||||||
let mut table = vec!["ID\tTags\tZone\tCapacity\tUsable capacity".to_string()];
|
|
||||||
for (id, _, role) in layout.roles.items().iter() {
|
|
||||||
let role = match &role.0 {
|
|
||||||
Some(r) => r,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
let tags = role.tags.join(",");
|
|
||||||
let usage = layout.get_node_usage(id).unwrap_or(0);
|
|
||||||
let capacity = layout.get_node_capacity(id).unwrap_or(0);
|
|
||||||
if capacity > 0 {
|
|
||||||
table.push(format!(
|
|
||||||
"{:?}\t{}\t{}\t{}\t{} ({:.1}%)",
|
|
||||||
id,
|
|
||||||
tags,
|
|
||||||
role.zone,
|
|
||||||
role.capacity_string(),
|
|
||||||
ByteSize::b(usage as u64 * layout.partition_size).to_string_as(false),
|
|
||||||
(100.0 * usage as f32 * layout.partition_size as f32) / (capacity as f32)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
table.push(format!(
|
|
||||||
"{:?}\t{}\t{}\t{}",
|
|
||||||
id,
|
|
||||||
tags,
|
|
||||||
role.zone,
|
|
||||||
role.capacity_string()
|
|
||||||
));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if table.len() > 1 {
|
|
||||||
format_table(table);
|
|
||||||
println!();
|
|
||||||
println!("Zone redundancy: {}", layout.parameters.zone_redundancy);
|
|
||||||
} else {
|
|
||||||
println!("{}", empty_msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_staging_role_changes(layout: &LayoutHistory) -> bool {
|
|
||||||
let staging = layout.staging.get();
|
|
||||||
let has_role_changes = staging
|
|
||||||
.roles
|
|
||||||
.items()
|
|
||||||
.iter()
|
|
||||||
.any(|(k, _, v)| layout.current().roles.get(k) != Some(v));
|
|
||||||
let has_layout_changes = *staging.parameters.get() != layout.current().parameters;
|
|
||||||
|
|
||||||
if has_role_changes || has_layout_changes {
|
|
||||||
println!();
|
|
||||||
println!("==== STAGED ROLE CHANGES ====");
|
|
||||||
if has_role_changes {
|
|
||||||
let mut table = vec!["ID\tTags\tZone\tCapacity".to_string()];
|
|
||||||
for (id, _, role) in staging.roles.items().iter() {
|
|
||||||
if layout.current().roles.get(id) == Some(role) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(role) = &role.0 {
|
|
||||||
let tags = role.tags.join(",");
|
|
||||||
table.push(format!(
|
|
||||||
"{:?}\t{}\t{}\t{}",
|
|
||||||
id,
|
|
||||||
tags,
|
|
||||||
role.zone,
|
|
||||||
role.capacity_string()
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
table.push(format!("{:?}\tREMOVED", id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format_table(table);
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
if has_layout_changes {
|
|
||||||
println!(
|
|
||||||
"Zone redundancy: {}",
|
|
||||||
staging.parameters.get().zone_redundancy
|
|
||||||
);
|
|
||||||
}
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::cli_v2::*;
|
||||||
impl Cli {
|
impl Cli {
|
||||||
pub async fn layout_command_dispatch(&self, cmd: LayoutOperation) -> Result<(), Error> {
|
pub async fn layout_command_dispatch(&self, cmd: LayoutOperation) -> Result<(), Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
|
LayoutOperation::Show => self.cmd_show_layout().await,
|
||||||
LayoutOperation::Assign(assign_opt) => self.cmd_assign_role(assign_opt).await,
|
LayoutOperation::Assign(assign_opt) => self.cmd_assign_role(assign_opt).await,
|
||||||
LayoutOperation::Remove(remove_opt) => self.cmd_remove_role(remove_opt).await,
|
LayoutOperation::Remove(remove_opt) => self.cmd_remove_role(remove_opt).await,
|
||||||
LayoutOperation::Config(config_opt) => self.cmd_config_layout(config_opt).await,
|
LayoutOperation::Config(config_opt) => self.cmd_config_layout(config_opt).await,
|
||||||
|
@ -20,9 +21,6 @@ impl Cli {
|
||||||
LayoutOperation::Revert(revert_opt) => self.cmd_revert_layout(revert_opt).await,
|
LayoutOperation::Revert(revert_opt) => self.cmd_revert_layout(revert_opt).await,
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
LayoutOperation::Show => {
|
|
||||||
cli_v1::cmd_show_layout(&self.system_rpc_endpoint, self.rpc_host).await
|
|
||||||
}
|
|
||||||
LayoutOperation::History => {
|
LayoutOperation::History => {
|
||||||
cli_v1::cmd_layout_history(&self.system_rpc_endpoint, self.rpc_host).await
|
cli_v1::cmd_layout_history(&self.system_rpc_endpoint, self.rpc_host).await
|
||||||
}
|
}
|
||||||
|
@ -37,6 +35,50 @@ impl Cli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn cmd_show_layout(&self) -> Result<(), Error> {
|
||||||
|
let layout = self.api_request(GetClusterLayoutRequest).await?;
|
||||||
|
|
||||||
|
println!("==== CURRENT CLUSTER LAYOUT ====");
|
||||||
|
print_cluster_layout(&layout, "No nodes currently have a role in the cluster.\nSee `garage status` to view available nodes.");
|
||||||
|
println!();
|
||||||
|
println!("Current cluster layout version: {}", layout.version);
|
||||||
|
|
||||||
|
let has_role_changes = print_staging_role_changes(&layout);
|
||||||
|
if has_role_changes {
|
||||||
|
let res_apply = self.api_request(PreviewClusterLayoutChangesRequest).await?;
|
||||||
|
|
||||||
|
// this will print the stats of what partitions
|
||||||
|
// will move around when we apply
|
||||||
|
match res_apply {
|
||||||
|
PreviewClusterLayoutChangesResponse::Success {
|
||||||
|
message,
|
||||||
|
new_layout,
|
||||||
|
} => {
|
||||||
|
println!();
|
||||||
|
println!("==== NEW CLUSTER LAYOUT AFTER APPLYING CHANGES ====");
|
||||||
|
print_cluster_layout(&new_layout, "No nodes have a role in the new layout.");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
for line in message.iter() {
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
|
println!("To enact the staged role changes, type:");
|
||||||
|
println!();
|
||||||
|
println!(" garage layout apply --version {}", new_layout.version);
|
||||||
|
println!();
|
||||||
|
println!("You can also revert all proposed changes with: garage layout revert");
|
||||||
|
}
|
||||||
|
PreviewClusterLayoutChangesResponse::Error { error } => {
|
||||||
|
println!("Error while trying to compute the assignment: {}", error);
|
||||||
|
println!("This new layout cannot yet be applied.");
|
||||||
|
println!("You can also revert all proposed changes with: garage layout revert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn cmd_assign_role(&self, opt: AssignRoleOpt) -> Result<(), Error> {
|
pub async fn cmd_assign_role(&self, opt: AssignRoleOpt) -> Result<(), Error> {
|
||||||
let status = self.api_request(GetClusterStatusRequest).await?;
|
let status = self.api_request(GetClusterStatusRequest).await?;
|
||||||
let layout = self.api_request(GetClusterLayoutRequest).await?;
|
let layout = self.api_request(GetClusterLayoutRequest).await?;
|
||||||
|
@ -218,7 +260,7 @@ pub fn capacity_string(v: Option<u64>) -> String {
|
||||||
pub fn get_staged_or_current_role(
|
pub fn get_staged_or_current_role(
|
||||||
id: &str,
|
id: &str,
|
||||||
layout: &GetClusterLayoutResponse,
|
layout: &GetClusterLayoutResponse,
|
||||||
) -> Option<NodeRoleResp> {
|
) -> Option<NodeAssignedRole> {
|
||||||
for node in layout.staged_role_changes.iter() {
|
for node in layout.staged_role_changes.iter() {
|
||||||
if node.id == id {
|
if node.id == id {
|
||||||
return match &node.action {
|
return match &node.action {
|
||||||
|
@ -227,7 +269,7 @@ pub fn get_staged_or_current_role(
|
||||||
zone,
|
zone,
|
||||||
capacity,
|
capacity,
|
||||||
tags,
|
tags,
|
||||||
} => Some(NodeRoleResp {
|
} => Some(NodeAssignedRole {
|
||||||
id: id.to_string(),
|
id: id.to_string(),
|
||||||
zone: zone.to_string(),
|
zone: zone.to_string(),
|
||||||
capacity: *capacity,
|
capacity: *capacity,
|
||||||
|
@ -239,7 +281,12 @@ pub fn get_staged_or_current_role(
|
||||||
|
|
||||||
for node in layout.roles.iter() {
|
for node in layout.roles.iter() {
|
||||||
if node.id == id {
|
if node.id == id {
|
||||||
return Some(node.clone());
|
return Some(NodeAssignedRole {
|
||||||
|
id: node.id.clone(),
|
||||||
|
zone: node.zone.clone(),
|
||||||
|
capacity: node.capacity,
|
||||||
|
tags: node.tags.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,11 +314,46 @@ pub fn find_matching_node<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_cluster_layout(layout: &GetClusterLayoutResponse, empty_msg: &str) {
|
||||||
|
let mut table = vec!["ID\tTags\tZone\tCapacity\tUsable capacity".to_string()];
|
||||||
|
for role in layout.roles.iter() {
|
||||||
|
let tags = role.tags.join(",");
|
||||||
|
if let (Some(capacity), Some(usable_capacity)) = (role.capacity, role.usable_capacity) {
|
||||||
|
table.push(format!(
|
||||||
|
"{:.16}\t{}\t{}\t{}\t{} ({:.1}%)",
|
||||||
|
role.id,
|
||||||
|
tags,
|
||||||
|
role.zone,
|
||||||
|
capacity_string(role.capacity),
|
||||||
|
ByteSize::b(usable_capacity).to_string_as(false),
|
||||||
|
(100.0 * usable_capacity as f32) / (capacity as f32)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
table.push(format!(
|
||||||
|
"{:.16}\t{}\t{}\t{}",
|
||||||
|
role.id,
|
||||||
|
tags,
|
||||||
|
role.zone,
|
||||||
|
capacity_string(role.capacity),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if table.len() > 1 {
|
||||||
|
format_table(table);
|
||||||
|
println!();
|
||||||
|
println!(
|
||||||
|
"Zone redundancy: {}",
|
||||||
|
Into::<layout::ZoneRedundancy>::into(layout.parameters.zone_redundancy)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("{}", empty_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_staging_role_changes(layout: &GetClusterLayoutResponse) -> bool {
|
pub fn print_staging_role_changes(layout: &GetClusterLayoutResponse) -> bool {
|
||||||
let has_role_changes = !layout.staged_role_changes.is_empty();
|
let has_role_changes = !layout.staged_role_changes.is_empty();
|
||||||
|
|
||||||
// TODO!! Layout parameters
|
let has_layout_changes = layout.staged_parameters.is_some();
|
||||||
let has_layout_changes = false;
|
|
||||||
|
|
||||||
if has_role_changes || has_layout_changes {
|
if has_role_changes || has_layout_changes {
|
||||||
println!();
|
println!();
|
||||||
|
@ -302,15 +384,12 @@ pub fn print_staging_role_changes(layout: &GetClusterLayoutResponse) -> bool {
|
||||||
format_table(table);
|
format_table(table);
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
//TODO
|
if let Some(p) = layout.staged_parameters.as_ref() {
|
||||||
/*
|
|
||||||
if has_layout_changes {
|
|
||||||
println!(
|
println!(
|
||||||
"Zone redundancy: {}",
|
"Zone redundancy: {}",
|
||||||
staging.parameters.get().zone_redundancy
|
Into::<layout::ZoneRedundancy>::into(p.zone_redundancy)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
Loading…
Add table
Reference in a new issue