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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

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

14 changes: 7 additions & 7 deletions openstack_tui/.config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,11 @@ mode_aliases:
# View output
# <RESOURCE_KEY>:
# fields: <ARRAY OF COLUMNS TO SHOW>
# wide: true
views:
# Block Storage
block_storage.backup:
default_fields: [id, name, az, size, status, created_at]
block_storage.snapshots:
block_storage.snapshot:
default_fields: [id, name, status, created_at]
block_storage.volume:
default_fields: [id, name, az, size, status, updated_at]
Expand All @@ -371,18 +370,15 @@ views:
default_fields: [name, uuid, az, updated_at]
compute.flavor:
default_fields: [id, name, vcpus, ram, disk, swap]
wide_fields: [swap]
compute.hypervisor:
default_fields: [ip, hostname, status, state]
wide_fields: [vcpus, "memory mb"]
status_field: status
compute.server/instance_action/event:
default_fields: [event, result, start_time, finish_time, host]
compute.server/instance_action:
default_fields: [id, action, message, start_time, user_id]
compute.server:
default_fields: [id, name, flavor, status, created, updated]
wide_fields: ["task state", "power state", "availability zone"]
status_field: status
fields:
- name: flavor
Expand All @@ -404,19 +400,23 @@ views:
# image
image.image:
default_fields: [id, name, distro, version, visibility, min_disk, min_ram]
wide_fields: ["disk format", "container format"]
status_field: status
# load balancer
load-balancer.healthmonitor:
default_fields: [id, name, status, type]
status_field: operating_status
load-balancer.listener:
default_fields: [id, name, status, protocol, port]
status_field: operating_status
load-balancer.loadbalancer:
default_fields: [id, name, status, address]
status_field: operating_status
load-balancer.pool/member:
default_fields: [id, name, status, port]
status_field: operating_status
load-balancer.pool:
default_fields: [id, name, status, protocol]
status_field: operating_status
# network
network.network:
default_fields: [id, name, status, description, created_at, updated_at]
Expand All @@ -427,4 +427,4 @@ views:
network.security_group_rule:
default_fields: [id, ethertype, direction, protocol, port_range_min, port_range_max, description]
network.security_group:
default_fields: [id, name, description, created_at, updated_at, description]
default_fields: [id, name, description, created_at, updated_at]
1 change: 0 additions & 1 deletion openstack_tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ itertools = { workspace = true }
lazy_static = "^1.5"
open.workspace = true
openstack_sdk = { path = "../openstack_sdk", version = "^0.22", default-features = false, features = ["async", "block_storage", "compute", "dns", "identity", "image", "load_balancer", "network"] }
openstack_types = { path = "../openstack_types", version = "^0.22" }
pretty_assertions = "^1.4"
ratatui = { version = "^0.30", features = ["serde", "macros", "crossterm"] }
secrecy = "0.10.3"
Expand Down
24 changes: 0 additions & 24 deletions openstack_tui/src/cloud_worker/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;

use openstack_sdk::AsyncOpenStack;
use openstack_sdk::api::{AsyncClient, RestEndpoint, rest_endpoint::negotiate_microversion};
use openstack_sdk::types::ApiVersion;

use crate::action;

pub trait ConfirmableRequest {
Expand All @@ -25,26 +21,6 @@ pub trait ConfirmableRequest {
}
}

/// Learn the microversion that will actually be negotiated for `endpoint` against the cloud
/// behind `session`, without sending any request.
///
/// Reuses `openstack_sdk`'s own `negotiate_microversion` (the same bounds-check logic that picks
/// the version sent in the `OpenStack-API-Version` header) so callers that need to choose a
/// version-appropriate response schema can learn the answer up front, rather than guessing which
/// microversion-specific struct will match the JSON that comes back.
pub(crate) async fn negotiated_version<E: RestEndpoint>(
session: &AsyncOpenStack,
endpoint: &E,
) -> Result<Option<ApiVersion>, CloudWorkerError> {
let service_endpoint = session
.get_service_endpoint(&endpoint.service_type(), endpoint.api_version().as_ref())
.await?;
Ok(negotiate_microversion::<AsyncOpenStack, E>(
&service_endpoint,
endpoint,
)?)
}

#[derive(Error, Debug)]
pub enum CloudWorkerError {
#[error(transparent)]
Expand Down
3 changes: 1 addition & 2 deletions openstack_tui/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub mod compute;
pub mod confirm_popup;
pub mod describe;
pub mod dns;
pub mod dynamic_item;
pub mod error_popup;
pub mod generic_resource_view;
pub mod header;
Expand All @@ -42,10 +41,10 @@ pub mod network;
pub mod project_select_popup;
pub mod region_select_popup;
pub mod resource_behaviour;
mod resource_key_impls; // bring ResourceKey impls into scope
pub mod resource_select_popup;
pub mod resource_table;
pub mod table_view;
pub mod view_render;
// pub mod modal; // removed – replaced by generic Popup widget
pub use crate::widgets::fuzzy_select::{FuzzySelect, FuzzySelectState};
pub use crate::widgets::popup::Popup;
Expand Down
1 change: 1 addition & 0 deletions openstack_tui/src/components/block_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
// SPDX-License-Identifier: Apache-2.0

pub mod backups;
pub(crate) mod generated;
pub mod snapshots;
pub mod volumes;
2 changes: 0 additions & 2 deletions openstack_tui/src/components/block_storage/backups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ use crate::cloud_worker::types::ApiRequest;
use crate::components::generic_resource_view::GenericResourceView;
use crate::components::resource_behaviour::ResourceBehaviour;
use crate::mode::Mode;
use openstack_types::block_storage::v3::backup::response::list_detailed::BackupResponse;

/// Behaviour implementation for BlockStorageBackups.
pub struct BlockStorageBackupsBehaviour;

impl ResourceBehaviour for BlockStorageBackupsBehaviour {
type Item = BackupResponse;
type Filter = BlockStorageBackupList;

fn view_key() -> &'static str {
Expand Down
19 changes: 19 additions & 0 deletions openstack_tui/src/components/block_storage/generated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//
// WARNING: Individual `pub(crate) mod <resource>;` lines below are automatically generated
// from OpenAPI schema using `openstack-codegenerator`. This scaffold line itself is hand-added,
// once per service, as the anchor new resources' lines get inserted after.

// GENERATED-ANCHOR: resource mods
2 changes: 0 additions & 2 deletions openstack_tui/src/components/block_storage/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ use crate::cloud_worker::types::ApiRequest;
use crate::components::generic_resource_view::GenericResourceView;
use crate::components::resource_behaviour::ResourceBehaviour;
use crate::mode::Mode;
use openstack_types::block_storage::v3::snapshot::response::list_detailed::SnapshotResponse;

/// Behaviour implementation for BlockStorageSnapshots.
pub struct BlockStorageSnapshotsBehaviour;

impl ResourceBehaviour for BlockStorageSnapshotsBehaviour {
type Item = SnapshotResponse;
type Filter = BlockStorageSnapshotList;

fn view_key() -> &'static str {
Expand Down
33 changes: 14 additions & 19 deletions openstack_tui/src/components/block_storage/volumes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,18 @@ use crate::cloud_worker::types::ApiRequest;
use crate::components::generic_resource_view::GenericResourceView;
use crate::components::resource_behaviour::ResourceBehaviour;
use crate::mode::Mode;
use openstack_types::block_storage::v3::volume::response::list_detailed::VolumeResponse;

const VIEW_CONFIG_KEY: &str = "block_storage.volume";

impl crate::utils::ResourceKey for VolumeResponse {
fn get_key() -> &'static str {
VIEW_CONFIG_KEY
}
}

impl TryFrom<&VolumeResponse> for BlockStorageVolumeDelete {
impl TryFrom<&serde_json::Value> for BlockStorageVolumeDelete {
type Error = crate::cloud_worker::block_storage::v3::BlockStorageVolumeDeleteBuilderError;
fn try_from(value: &VolumeResponse) -> Result<Self, Self::Error> {
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
let mut builder = BlockStorageVolumeDeleteBuilder::default();
builder.id(value.id.clone());
if let Some(val) = &value.name {
builder.name(val.clone());
if let Some(val) = crate::components::view_render::get_str(value, "/id") {
builder.id(val.to_string());
}
if let Some(val) = crate::components::view_render::get_str(value, "/name") {
builder.name(val.to_string());
}
builder.build()
}
Expand All @@ -46,7 +41,6 @@ impl TryFrom<&VolumeResponse> for BlockStorageVolumeDelete {
pub struct BlockStorageVolumesBehaviour;

impl ResourceBehaviour for BlockStorageVolumesBehaviour {
type Item = VolumeResponse;
type Filter = BlockStorageVolumeList;

fn view_key() -> &'static str {
Expand All @@ -70,7 +64,10 @@ impl ResourceBehaviour for BlockStorageVolumesBehaviour {
if matches!(**boxreq, BlockStorageVolumeApiRequest::ListDetailed(_))
)
}
fn confirm_request(action: &Action, selected: Option<&Self::Item>) -> Option<ApiRequest> {
fn confirm_request(
action: &Action,
selected: Option<&serde_json::Value>,
) -> Option<ApiRequest> {
if let Action::ResourceOp {
key,
op: crate::action::ResourceOp::Delete,
Expand All @@ -93,10 +90,9 @@ pub type BlockStorageVolumes = GenericResourceView<'static, BlockStorageVolumesB
mod tests {
use super::*;
use crate::components::resource_behaviour::ResourceBehaviour;
use openstack_types::block_storage::v3::volume::response::list_detailed::VolumeResponse;

fn make_volume(id: &str, name: &str) -> VolumeResponse {
let json = serde_json::json!({
fn make_volume(id: &str, name: &str) -> serde_json::Value {
serde_json::json!({
"id": id,
"name": name,
"status": "available",
Expand All @@ -119,8 +115,7 @@ mod tests {
"os-vol-mig-status.migration_status": null,
"os-vol-host-attr:host": null,
"os-vol-tenant-attr:tenant_id": "tenant-1"
});
serde_json::from_value(json).unwrap()
})
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions openstack_tui/src/components/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

pub mod aggregates;
pub mod flavors;
pub(crate) mod generated;
pub mod hypervisors;
pub mod server_instance_action_events;
pub mod server_instance_actions;
Expand Down
2 changes: 0 additions & 2 deletions openstack_tui/src/components/compute/aggregates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ use crate::cloud_worker::types::ApiRequest;
use crate::components::generic_resource_view::GenericResourceView;
use crate::components::resource_behaviour::ResourceBehaviour;
use crate::mode::Mode;
use openstack_types::compute::v2::aggregate::response::list_241::AggregateResponse;

/// Behaviour implementation for ComputeAggregates.
pub struct ComputeAggregatesBehaviour;

impl ResourceBehaviour for ComputeAggregatesBehaviour {
type Item = AggregateResponse;
type Filter = ComputeAggregateList;

fn view_key() -> &'static str {
Expand Down
59 changes: 6 additions & 53 deletions openstack_tui/src/components/compute/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use crate::{
ComputeApiRequest, ComputeFlavorApiRequest, ComputeFlavorList, ComputeServerListBuilder,
},
cloud_worker::types::ApiRequest,
components::dynamic_item::{ColumnSpec, impl_dynamic_item},
components::generic_resource_view::GenericResourceView,
components::resource_behaviour::{Mutation, ResourceBehaviour},
mode::Mode,
Expand All @@ -27,54 +26,9 @@ use crate::{
const TITLE: &str = "Compute Flavors";
const VIEW_CONFIG_KEY: &str = "compute.flavor";

// Flavor's `swap` field changed type (i64 -> i32) at microversion 2.102 -- no single
// `openstack_types` struct correctly represents every microversion, so `Item` reads columns out
// of the raw response by JSON pointer instead of deserializing into a versioned struct.
static FLAVOR_COLUMNS: &[ColumnSpec] = &[
ColumnSpec {
title: "ID",
pointer: "/id",
wide: false,
status: false,
},
ColumnSpec {
title: "Name",
pointer: "/name",
wide: false,
status: false,
},
ColumnSpec {
title: "RAM",
pointer: "/ram",
wide: false,
status: false,
},
ColumnSpec {
title: "VCPUs",
pointer: "/vcpus",
wide: false,
status: false,
},
ColumnSpec {
title: "Disk",
pointer: "/disk",
wide: false,
status: false,
},
ColumnSpec {
title: "Swap",
pointer: "/swap",
wide: true,
status: false,
},
];

impl_dynamic_item!(FlavorItem, VIEW_CONFIG_KEY, FLAVOR_COLUMNS);

pub struct ComputeFlavorsBehaviour;

impl ResourceBehaviour for ComputeFlavorsBehaviour {
type Item = FlavorItem;
type Filter = ComputeFlavorList;

fn view_key() -> &'static str {
Expand Down Expand Up @@ -114,15 +68,15 @@ impl ResourceBehaviour for ComputeFlavorsBehaviour {

fn filter_carry_action(
action: &Action,
selected: Option<&Self::Item>,
selected: Option<&serde_json::Value>,
_filter: &Self::Filter,
) -> Vec<Action> {
if let Action::ShowResource(key) = action
&& *key == crate::mode::COMPUTE_SERVER
&& let Some(sel) = selected
&& let Some(flavor_id) = sel.get_str("/id")
&& let Some(flavor_id) = crate::components::view_render::get_str(sel, "/id")
&& let Ok(server_list) = ComputeServerListBuilder::default()
.flavor(flavor_id)
.flavor(flavor_id.to_string())
.build()
{
return vec![
Expand Down Expand Up @@ -153,8 +107,8 @@ mod tests {
use crate::cloud_worker::compute::v2::ComputeServerApiRequest;
use crate::components::resource_behaviour::ResourceBehaviour;

fn make_flavor(id: &str) -> FlavorItem {
let json = serde_json::json!({
fn make_flavor(id: &str) -> serde_json::Value {
serde_json::json!({
"id": id,
"name": "test",
"vcpus": 1,
Expand All @@ -166,8 +120,7 @@ mod tests {
"OS-FLV-EXT-DATA:ephemeral": 0,
"metadata": {},
"os-flavor-access:is_public": true,
});
FlavorItem(json)
})
}

#[test]
Expand Down
Loading
Loading