From dd635d8998adc0ac55cce28c086c350d3675910a Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Tue, 17 Oct 2023 13:40:48 +0100 Subject: [PATCH 1/4] Add simple server actions None of these actions have complicated request arguments or have any responses returned. --- src/compute/servers.rs | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/compute/servers.rs b/src/compute/servers.rs index 466651c35f..45c64b5c59 100644 --- a/src/compute/servers.rs +++ b/src/compute/servers.rs @@ -343,6 +343,24 @@ impl Server { #[non_exhaustive] #[allow(missing_copy_implementations)] pub enum ServerAction { + /// Adds a security group to a server. + #[serde(rename = "addSecurityGroup")] + AddSecurityGroup { + /// The security group name. + name: String, + }, + /// Changes the administrative password for a server. + #[serde(rename = "changePassword")] + ChangePassword { + /// The administrative password for the server. + admin_pass: String, + }, + /// Confirms a pending resize action for a server. + #[serde(rename = "confirmResize", serialize_with = "unit_to_null")] + ConfirmResize, + /// Pauses a server. Changes its status to PAUSED. + #[serde(rename = "pause", serialize_with = "unit_to_null")] + Pause, /// Reboots a server. #[serde(rename = "reboot")] Reboot { @@ -350,12 +368,51 @@ pub enum ServerAction { #[serde(rename = "type")] reboot_type: protocol::RebootType, }, + /// Removes a security group from a server. + #[serde(rename = "removeSecurityGroup")] + RemoveSecurityGroup { + /// The security group name. + name: String, + }, + /// Resumes a suspended server and changes its status to ACTIVE. + #[serde(rename = "resume", serialize_with = "unit_to_null")] + Resume, + /// Cancels and reverts a pending resize action for a server. + #[serde(rename = "revertResize", serialize_with = "unit_to_null")] + RevertResize, /// Starts a stopped server. #[serde(rename = "os-start", serialize_with = "unit_to_null")] Start, /// Stops a running server. #[serde(rename = "os-stop", serialize_with = "unit_to_null")] Stop, + /// Suspends a server and changes its status to SUSPENDED. + #[serde(rename = "suspend", serialize_with = "unit_to_null")] + Suspend, + /// Unlocks a locked server. + #[serde(rename = "unlock", serialize_with = "unit_to_null")] + Unlock, + /// Unpauses a paused server and changes its status to ACTIVE. + #[serde(rename = "unpause", serialize_with = "unit_to_null")] + Unpause, + /// Unrescues a server. Changes status to ACTIVE. + #[serde(rename = "unrescue", serialize_with = "unit_to_null")] + Unrescue, + /// Force-deletes a server before deferred cleanup. + #[serde(rename = "forceDelete", serialize_with = "unit_to_null")] + ForceDelete, + /// Restores a previously soft-deleted server instance. + #[serde(rename = "restore", serialize_with = "unit_to_null")] + Restore, + /// Shelves a server. + #[serde(rename = "shelve", serialize_with = "unit_to_null")] + Shelve, + /// Shelf-offloads, or removes, a shelved server. + #[serde(rename = "shelveOffload", serialize_with = "unit_to_null")] + ShelveOffload, + /// Trigger a crash dump in a server. + #[serde(rename = "trigger_crash_dump", serialize_with = "unit_to_null")] + TriggerCrashDump, } #[async_trait] From 0d5ebec8ffd37de1d5e62189e94fc61eef1c83d7 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Sat, 28 Oct 2023 16:59:15 +0100 Subject: [PATCH 2/4] Retrieve response from server actions --- src/compute/api.rs | 15 +++++++++++---- src/compute/servers.rs | 8 ++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/compute/api.rs b/src/compute/api.rs index de9d3479f2..1ffb20d30b 100644 --- a/src/compute/api.rs +++ b/src/compute/api.rs @@ -19,7 +19,7 @@ use std::fmt::Debug; use osauth::common::{IdAndName, Ref}; use osauth::services::COMPUTE; -use osauth::ErrorKind; +use osauth::{Error, ErrorKind}; use serde::Serialize; use super::super::common::ApiVersion; @@ -315,19 +315,26 @@ pub async fn list_servers_detail( } /// Run an action on a server. -pub async fn server_action_with_args(session: &Session, id: S1, action: Q) -> Result<()> +pub async fn server_action_with_args( + session: &Session, + id: S1, + action: Q, +) -> Result where S1: AsRef, Q: Serialize + Send + Debug, { trace!("Running {:?} on server {}", action, id.as_ref(),); - let _ = session + let response = session .post(COMPUTE, &["servers", id.as_ref(), "action"]) .json(&action) .send() .await?; debug!("Successfully ran {:?} on server {}", action, id.as_ref()); - Ok(()) + response + .json::() + .await + .map_err(|e| Error::new(ErrorKind::InvalidResponse, e.to_string())) } /// Whether key pair pagination is supported. diff --git a/src/compute/servers.rs b/src/compute/servers.rs index 45c64b5c59..3f29ece2f4 100644 --- a/src/compute/servers.rs +++ b/src/compute/servers.rs @@ -307,7 +307,7 @@ impl Server { &mut self, reboot_type: protocol::RebootType, ) -> Result> { - self.action(ServerAction::Reboot { reboot_type }).await?; + let _ = self.action(ServerAction::Reboot { reboot_type }).await?; Ok(ServerStatusWaiter { server: self, target: protocol::ServerStatus::Active, @@ -316,7 +316,7 @@ impl Server { /// Start the server, optionally wait for it to be active. pub async fn start(&mut self) -> Result> { - self.action(ServerAction::Start).await?; + let _ = self.action(ServerAction::Start).await?; Ok(ServerStatusWaiter { server: self, target: protocol::ServerStatus::Active, @@ -325,7 +325,7 @@ impl Server { /// Stop the server, optionally wait for it to be powered off. pub async fn stop(&mut self) -> Result> { - self.action(ServerAction::Stop).await?; + let _ = self.action(ServerAction::Stop).await?; Ok(ServerStatusWaiter { server: self, target: protocol::ServerStatus::ShutOff, @@ -333,7 +333,7 @@ impl Server { } /// Run an action on the server. - pub async fn action(&mut self, action: ServerAction) -> Result<()> { + pub async fn action(&mut self, action: ServerAction) -> Result { api::server_action_with_args(&self.session, &self.inner.id, action).await } } From 887532791bb1ed231dce7e33c8b356246b252319 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Sat, 28 Oct 2023 17:07:12 +0100 Subject: [PATCH 3/4] Add some more Server Actions These are actions which, while having return values, are relatively simple in what they take as arguments. --- src/compute/servers.rs | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/compute/servers.rs b/src/compute/servers.rs index 3f29ece2f4..2e8ead07e6 100644 --- a/src/compute/servers.rs +++ b/src/compute/servers.rs @@ -358,6 +358,28 @@ pub enum ServerAction { /// Confirms a pending resize action for a server. #[serde(rename = "confirmResize", serialize_with = "unit_to_null")] ConfirmResize, + /// Creates a back up of a server. + #[serde(rename = "createBackup")] + CreateBackup { + /// The name of the image to be backed up. + name: String, + /// The type of the backup, for example, daily. + backup_type: String, + /// The rotation of the back up image, the oldest image will be removed when image count exceed the rotation count. + rotation: u16, + /// Metadata key and value pairs for the image. + #[serde(skip_serializing_if = "Option::is_none")] + metadata: Option>, + }, + /// Creates an image from a server. + #[serde(rename = "createImage")] + CreateImage { + /// The display name of an Image. + name: String, + /// Metadata key and value pairs for the image. + #[serde(skip_serializing_if = "Option::is_none")] + metadata: Option>, + }, /// Pauses a server. Changes its status to PAUSED. #[serde(rename = "pause", serialize_with = "unit_to_null")] Pause, @@ -374,6 +396,26 @@ pub enum ServerAction { /// The security group name. name: String, }, + /// Puts a server in rescue mode and changes its status to RESCUE. + #[serde(rename = "rescue")] + Rescue { + /// The password for the rescued instance. + #[serde(rename = "adminPass", skip_serializing_if = "Option::is_none")] + admin_pass: Option, + /// The image reference to use to rescue your server instance. + #[serde(skip_serializing_if = "Option::is_none")] + rescue_image_ref: Option, + }, + /// Resizes a server. + #[serde(rename = "resize")] + Resize { + /// The flavor ID for resizing the server. + #[serde(rename = "flavorRef")] + flavor_ref: String, + /// Controls how the API partitions the disk when you create, rebuild, or resize servers. + #[serde(rename = "OS-DCF:diskConfig")] + disk_config: String, + }, /// Resumes a suspended server and changes its status to ACTIVE. #[serde(rename = "resume", serialize_with = "unit_to_null")] Resume, @@ -404,6 +446,13 @@ pub enum ServerAction { /// Restores a previously soft-deleted server instance. #[serde(rename = "restore", serialize_with = "unit_to_null")] Restore, + /// Shows console output for a server. + #[serde(rename = "os-getConsoleOutput")] + OsGetConsoleOutput { + /// The number of lines to fetch from the end of console log. All lines will be returned if this is not specified. + #[serde(skip_serializing_if = "Option::is_none")] + length: Option, + }, /// Shelves a server. #[serde(rename = "shelve", serialize_with = "unit_to_null")] Shelve, @@ -1049,5 +1098,21 @@ mod test { .unwrap(), "{\"reboot\":{\"type\":\"HARD\"}}" ); + assert_eq!( + serde_json::to_string(&ServerAction::CreateImage { + name: "new-image".to_string(), + metadata: None, + }) + .unwrap(), + r#"{"createImage":{"name":"new-image"}}"# + ); + assert_eq!( + serde_json::to_string(&ServerAction::CreateImage { + name: "new-image".to_string(), + metadata: Some(HashMap::from([("tag".into(), "foo".into())])), + }) + .unwrap(), + r#"{"createImage":{"name":"new-image","metadata":{"tag":"foo"}}}"# + ); } } From e644b15b8107cbd3f0ad120ab809564a70deea18 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Mon, 30 Oct 2023 22:06:53 +0000 Subject: [PATCH 4/4] Return None if server action returns nothing Some server actions return an empty body, which is not valid JSON. --- src/compute/api.rs | 15 ++++++++++----- src/compute/servers.rs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/compute/api.rs b/src/compute/api.rs index 1ffb20d30b..a5abbe18ac 100644 --- a/src/compute/api.rs +++ b/src/compute/api.rs @@ -319,7 +319,7 @@ pub async fn server_action_with_args( session: &Session, id: S1, action: Q, -) -> Result +) -> Result> where S1: AsRef, Q: Serialize + Send + Debug, @@ -331,10 +331,15 @@ where .send() .await?; debug!("Successfully ran {:?} on server {}", action, id.as_ref()); - response - .json::() - .await - .map_err(|e| Error::new(ErrorKind::InvalidResponse, e.to_string())) + Ok(match response.content_length() { + Some(0) => None, + _ => Some( + response + .json::() + .await + .map_err(|e| Error::new(ErrorKind::InvalidResponse, e.to_string()))?, + ), + }) } /// Whether key pair pagination is supported. diff --git a/src/compute/servers.rs b/src/compute/servers.rs index 2e8ead07e6..321d02937a 100644 --- a/src/compute/servers.rs +++ b/src/compute/servers.rs @@ -333,7 +333,7 @@ impl Server { } /// Run an action on the server. - pub async fn action(&mut self, action: ServerAction) -> Result { + pub async fn action(&mut self, action: ServerAction) -> Result> { api::server_action_with_args(&self.session, &self.inner.id, action).await } }