From 5a688983210780215cb36471485c7df3739eefe9 Mon Sep 17 00:00:00 2001 From: Cory Hawkvelt Date: Mon, 24 Mar 2025 22:26:08 +1030 Subject: [PATCH] refactored the way we handed volumes and images to the client --- app/controller/api/workload_vm_routes.py | 10 +- app/models/models.py | 2 +- streamlit_server/views/virtual_machines.py | 181 +++++---------------- streamlit_server/views/workload_hosts.py | 95 ++++++----- 4 files changed, 100 insertions(+), 188 deletions(-) diff --git a/app/controller/api/workload_vm_routes.py b/app/controller/api/workload_vm_routes.py index b5a768a..68fa348 100644 --- a/app/controller/api/workload_vm_routes.py +++ b/app/controller/api/workload_vm_routes.py @@ -375,13 +375,15 @@ def add_VirtualMachine_workload(): "size_gb": vol.size_gb, "type": vol.type, "path": vol.path, - "source": vol.source, - **({"image": { # Include image object only if image_id exists - "id": vol.image.id, + **({"source": { # Include image object only if image_id exists + "type": "image", + "image_id": vol.image.id, "path": vol.image.location, "format": vol.image.format, "checksum": vol.image.checksum - }} if vol.image_id else {}) # Conditionally add image object + }} if vol.image_id else { + "source": "None" + }) # Conditionally add image object } for vol in volumes ], diff --git a/app/models/models.py b/app/models/models.py index 44c3cbe..4020c85 100644 --- a/app/models/models.py +++ b/app/models/models.py @@ -131,7 +131,7 @@ class Workload(BaseModel): status = Column(String, nullable=True) container_id = Column(String, nullable=True) launch_params = Column(String, nullable=True) - time_to_provision = Column(Float, nullable=False) + time_to_provision = Column(Float, nullable=True) # Relationship to VolumeWorkloadMapping volume_mappings = relationship("VolumeWorkloadMapping", back_populates="workload") diff --git a/streamlit_server/views/virtual_machines.py b/streamlit_server/views/virtual_machines.py index 0f25848..be9d18a 100644 --- a/streamlit_server/views/virtual_machines.py +++ b/streamlit_server/views/virtual_machines.py @@ -122,7 +122,7 @@ def render_list(): # List Container Workloads st.subheader("Existing Container Workloads") VirtualMachines = st.session_state.client.get_virtual_machines() - + print(VirtualMachines) if VirtualMachines: # Filter VirtualMachines based on the selected VDC if selected_vdc_name: @@ -222,156 +222,61 @@ def render_list(): - - - - - - - def render_detail(): - host_id = st.session_state.selected_resource['id'] - workload_host = st.session_state.client.get_workload_host(host_id) + """ + Renders the details of a specific VM workload. + """ + VirtualMachine = st.session_state.client.get_virtual_machine(st.session_state.selected_resource['id']) + if not VirtualMachine: + st.error("VM workload not found.") + return - st.title(f"Workload Host: {workload_host['name']}") + st.title(f"VM Workload: {VirtualMachine['name']}") if st.button("Back to list view"): st.session_state.view_type = 'list' st.rerun() - - st.header("Workload Host Details") + + st.header("VM Workload Details") col1, col2 = st.columns(2) with col1: - st.markdown(f"**ID:** `{workload_host['id']}`") - st.markdown(f"**Hostname:** `{workload_host['hostname']}`") - # Make Region ID clickable - if st.button(f"**Region ID:** `{workload_host['region_id']}`"): - st.session_state.selected_resource = {"id": workload_host['region_id']} - st.session_state.view_type = 'detail' - st.session_state.selected_resource_type = 'region' - st.switch_page("streamlit_server/views/regions.py") - - st.markdown(f"**System Manufacturer:** `{workload_host['system_manufacturer']}`") - st.markdown(f"**System Model:** `{workload_host['system_model']}`") + st.markdown(f"**ID:** `{VirtualMachine['id']}`") + st.markdown(f"**Created:** {format_timestamp(VirtualMachine.get('created_at', 'Unknown'))}") + st.markdown(f"**VDC ID:** {VirtualMachine['vdc_id']}") + st.markdown(f"**Workload Host ID:** {VirtualMachine.get('workload_host_id', 'N/A')}") with col2: - st.markdown(f"**Physical Identifier:** `{workload_host['physical_identifier']}`") - st.markdown(f"**DCIM Identifier:** `{workload_host['dcim_identifier']}`") - st.markdown(f"**Installed Status:** `{workload_host['installed_status']}`") - st.markdown(f"**Available for Scheduling:** `{workload_host['available_for_scheduling']}`") - st.markdown(f"**Placement Priority:** `{workload_host['placement_priority']}`") + if VirtualMachine.get('updated_at'): + st.markdown(f"**Last Updated:** {format_timestamp(VirtualMachine['updated_at'])}") + st.markdown(f"**Status:** {VirtualMachine['status']}") + st.markdown(f"**Launch Parameters:** {VirtualMachine.get('launch_params', 'N/A')}") - # Display OVS Bridges - st.subheader("Attached OVS Bridges") - ovs_bridges = st.session_state.client.get_ovs_bridges_for_host(host_id) - if ovs_bridges: - for bridge in ovs_bridges: - st.markdown(f"- **{bridge['ovs_bridge_name']}**") - else: - st.info("No OVS bridges attached to this host.") - - # Display Workloads - st.subheader("Assigned Workloads") - workloads = st.session_state.client.get_workloads_for_host(host_id) - - if workloads: - # Split workloads into containers and VMs - containers = [w for w in workloads if w['type'] == 'container'] - vms = [w for w in workloads if w['type'] == 'vm'] - - # Display Containers in a table - if containers: - st.markdown("### Containers") - container_data = [] - for container in containers: - container_data.append({ - "Name": container['name'], - "ID": container['id'], - "Status": container['status'], - "Created At": format_timestamp(container['created_at']), - "Actions": f""" -
- View Details - Delete - Migrate -
- """ + # Edit VM Workload Form + with st.expander("Edit VM Workload"): + with st.form("edit_vm"): + new_status = st.selectbox("Status", ["running", "stopped", "pending", "failed-deleted", "pending-deleted", "pending-allocation", "failed-allocation", "pending-allocated"], index=["running", "stopped", "pending", "failed-deleted", "pending-deleted", "pending-allocation", "failed-allocation", "pending-allocated"].index(VirtualMachine['status'])) + new_launch_params = st.text_input("Launch Parameters", value=VirtualMachine.get('launch_params', '')) + submit = st.form_submit_button("Update VM Workload") + + if submit: + result = st.session_state.client.edit_virtual_machine(VirtualMachine['id'], { + "status": new_status, + "launch_params": new_launch_params }) - st.table(container_data) - - # Handle actions for containers - for container in containers: - if st.session_state.get(f"view_container_{container['id']}"): - st.session_state.selected_resource = container - st.session_state.view_type = 'detail' - st.session_state.selected_resource_type = 'container' - st.switch_page("streamlit_server/views/containers.py") - if st.session_state.get(f"delete_container_{container['id']}"): - st.session_state.client.delete_workload(container['id']) - st.success(f"Container '{container['name']}' deleted successfully!") - st.rerun() - if st.session_state.get(f"migrate_container_{container['id']}"): - st.session_state.client.migrate_workload(container['id'], new_host_id="NEW_HOST_ID") - st.success(f"Container '{container['name']}' migration initiated!") - st.rerun() - else: - st.info("No containers assigned to this host.") - - # Display Virtual Machines in a table - if vms: - st.markdown("### Virtual Machines") - vm_data = [] - for vm in vms: - vm_data.append({ - "Name": vm['name'], - "ID": vm['id'], - "Status": vm['status'], - "Created At": format_timestamp(vm['created_at']), - "Actions": f""" -
- View Details - Delete - Migrate -
- """ - }) - st.table(vm_data) - - # Handle actions for VMs - for vm in vms: - if st.session_state.get(f"view_vm_{vm['id']}"): + if result: + st.success("VM workload updated successfully!") st.session_state.selected_resource = vm st.session_state.view_type = 'detail' - st.session_state.selected_resource_type = 'vm' - st.switch_page("streamlit_server/views/virtual_machines.py") - if st.session_state.get(f"delete_vm_{vm['id']}"): - st.session_state.client.delete_workload(vm['id']) - st.success(f"VM '{vm['name']}' deleted successfully!") + st.session_state.selected_resource_type = 'VirtualMachine' st.rerun() - if st.session_state.get(f"migrate_vm_{vm['id']}"): - st.session_state.client.migrate_workload(vm['id'], new_host_id="NEW_HOST_ID") - st.success(f"VM '{vm['name']}' migration initiated!") - st.rerun() - else: - st.info("No virtual machines assigned to this host.") - else: - st.info("No workloads assigned to this host.") - - # Display Fixed Resources - st.subheader("Fixed Resources") - fixed_resources = st.session_state.client.get_fixed_resources_for_host(host_id) - if fixed_resources: - for resource in fixed_resources: - st.markdown(f"- **{resource['type']}** (Model: `{resource['model']}`, Manufacturer: `{resource['manufacturer']}`)") - else: - st.info("No fixed resources attached to this host.") - - # Display Pooled Resources - st.subheader("Pooled Resources") - pooled_resources = st.session_state.client.get_pooled_resources_for_host(host_id) - if pooled_resources: - for resource in pooled_resources: - st.markdown(f"- **{resource['resource_type']}** (Total: `{resource['total_quantity']}`, In Use: `{resource['quantity_in_use']}`, Available: `{resource['quantity_available']}`)") - else: - st.info("No pooled resources attached to this host.") + + # Delete VM Workload Button + if st.button("Delete VM Workload"): + result = st.session_state.client.delete_virtual_machine(VirtualMachine['id']) + if result: + st.success("VM workload deleted successfully!") + st.session_state.selected_resource = None + st.session_state.view_type = 'list' + st.session_state.selected_resource_type = 'VirtualMachine' + st.rerun() diff --git a/streamlit_server/views/workload_hosts.py b/streamlit_server/views/workload_hosts.py index 3fa5260..5b27861 100644 --- a/streamlit_server/views/workload_hosts.py +++ b/streamlit_server/views/workload_hosts.py @@ -100,72 +100,78 @@ def render_detail(): else: st.info("No OVS bridges attached to this host.") - # Display Workloads + # Display Workloads st.subheader("Assigned Workloads") workloads = st.session_state.client.get_workloads_for_host(host_id) if workloads: - # Split workloads into containers and VMs containers = [w for w in workloads if w['workload_type'] == 'container'] vms = [w for w in workloads if w['workload_type'] == 'virtual_machine'] - # Display Containers if containers: st.markdown("### Containers") + # Table header + header_cols = st.columns([3, 2, 2, 2, 2]) + header_cols[0].markdown("**Name**") + header_cols[1].markdown("**ID**") + header_cols[2].markdown("**Status**") + header_cols[3].markdown("**Created At**") + header_cols[4].markdown("**Actions**") + + # Container rows for container in containers: - with st.container(): - col1, col2, col3 = st.columns([3, 1, 1]) - with col1: - st.markdown(f"**Name:** {container['name']}") - st.markdown(f"**ID:** `{container['id']}`") - st.markdown(f"**Status:** `{container['status']}`") - st.markdown(f"**Created At:** `{format_timestamp(container['created_at'])}`") - with col2: - if st.button(f"View Details", key=f"view_container_{container['id']}"): + cols = st.columns([3, 2, 2, 2, 2]) + cols[0].markdown(container['name']) + cols[1].markdown(f"`{container['id'][:8]}...`") + cols[2].markdown(f"`{container['status']}`") + cols[3].markdown(format_timestamp(container['created_at'])) + + with cols[4]: + with st.popover("⚙️"): + if st.button("View Details", key=f"view_container_{container['id']}"): st.session_state.selected_resource = container st.session_state.view_type = 'detail' st.session_state.selected_resource_type = 'container' st.switch_page("streamlit_server/views/containers.py") - with col3: - with st.popover("Actions"): - if st.button("Delete", key=f"delete_container_{container['id']}"): - st.session_state.client.delete_workload(container['id']) - st.success(f"Container '{container['name']}' deleted successfully!") - st.rerun() - if st.button("Migrate", key=f"migrate_container_{container['id']}"): - st.session_state.client.migrate_workload(container['id'], new_host_id="NEW_HOST_ID") - st.success(f"Container '{container['name']}' migration initiated!") - st.rerun() - else: - st.info("No containers assigned to this host.") + if st.button("Delete", key=f"delete_container_{container['id']}"): + st.session_state.client.delete_container(container['id']) + st.rerun() + if st.button("Migrate", key=f"migrate_container_{container['id']}"): + st.session_state.client.migrate_workload(container['id'], "NEW_HOST_ID") + st.rerun() - # Display Virtual Machines if vms: st.markdown("### Virtual Machines") + # Table header + header_cols = st.columns([3, 2, 2, 2, 2]) + header_cols[0].markdown("**Name**") + header_cols[1].markdown("**ID**") + header_cols[2].markdown("**Status**") + header_cols[3].markdown("**Created At**") + header_cols[4].markdown("**Actions**") + + # VM rows for vm in vms: - with st.container(): - col1, col2, col3 = st.columns([3, 1, 1]) - with col1: - st.markdown(f"**Name:** {vm['name']}") - st.markdown(f"**ID:** `{vm['id']}`") - st.markdown(f"**Status:** `{vm['status']}`") - st.markdown(f"**Created At:** `{format_timestamp(vm['created_at'])}`") - with col2: - if st.button(f"View Details", key=f"view_vm_{vm['id']}"): + cols = st.columns([3, 2, 2, 2, 2]) + cols[0].markdown(vm['name']) + cols[1].markdown(f"`{vm['id'][:8]}...`") + cols[2].markdown(f"`{vm['status']}`") + cols[3].markdown(format_timestamp(vm['created_at'])) + + with cols[4]: + with st.popover("⚙️"): + if st.button("View Details", key=f"view_vm_{vm['id']}"): st.session_state.selected_resource = vm st.session_state.view_type = 'detail' st.session_state.selected_resource_type = 'vm' st.switch_page("streamlit_server/views/virtual_machines.py") - with col3: - with st.popover("Actions"): - if st.button("Delete", key=f"delete_vm_{vm['id']}"): - st.session_state.client.delete_workload(vm['id']) - st.success(f"VM '{vm['name']}' deleted successfully!") - st.rerun() - if st.button("Migrate", key=f"migrate_vm_{vm['id']}"): - st.session_state.client.migrate_workload(vm['id'], new_host_id="NEW_HOST_ID") - st.success(f"VM '{vm['name']}' migration initiated!") - st.rerun() + if st.button("Delete", key=f"delete_vm_{vm['id']}"): + st.session_state.client.delete_virtual_machine(vm['id']) + st.rerun() + if st.button("Migrate", key=f"migrate_vm_{vm['id']}"): + st.session_state.client.migrate_workload(vm['id'], "NEW_HOST_ID") + st.rerun() + else: st.info("No virtual machines assigned to this host.") else: @@ -189,7 +195,6 @@ def render_detail(): else: st.info("No pooled resources attached to this host.") - if st.session_state.view_type == 'detail' and st.session_state.selected_resource_type == 'workload_host': render_detail() else: