Files
3cloud-backend/streamlit_server/views/containers.py
T
coryHawkvelt ff989be202 PortForwarding work
Added workload id to portforward object
Updated streamlit to show workload name
2025-07-25 15:38:52 +09:30

292 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import streamlit as st
from streamlit_server.utils.helpers import format_timestamp
def render_list():
"""
Renders a list of container pods, each with its containers.
"""
st.header("Container Workload Management")
# Initialize session state flags for pod deletion
for flag in ['pod_delete_all_confirmation_shown', 'pod_delete_all_confirmed',
'pod_delete_confirmation_shown', 'pod_delete_confirmed','container_delete_confirmation_shown','container_delete_confirmed']:
if flag not in st.session_state:
st.session_state[flag] = False
# Fetch VDCs
vdcs = st.session_state.client.get_virtual_data_centers()
vdc_options = {vdc['name']: vdc['id'] for vdc in vdcs} if vdcs else {}
selected_vdc_name = st.selectbox("Filter by Virtual Data Center", list(vdc_options.keys()))
# Create Container Workload Form
with st.expander("Create New Container Workload"):
with st.form("create_container"):
selected_vdc_name_create = st.selectbox("VDC", list(vdc_options.keys()))
container_name = st.text_input("Container Name", value="my-container")
# Docker image selection
docker_image_options = ["nginx", "traefik/whoami:latest", "whoami81", "noisy_container", "joke_container"]
selected_predefined = st.selectbox("Choose a common image", [""] + docker_image_options)
custom_input = st.text_input("Or enter custom Docker image", value="")
docker_image = custom_input.strip() if custom_input.strip() else selected_predefined.strip()
# Port mappings
st.markdown("**Port Mappings**")
if 'port_mappings' not in st.session_state:
st.session_state.port_mappings = [{
'internal': None, 'external': None,
'use_dns': True, 'protocol': 'tcp'
}]
for i, mapping in enumerate(st.session_state.port_mappings):
with st.container():
cols = st.columns([2, 2, 2, 1])
with cols[0]:
internal_port = st.number_input("Internal Port", 1, 65535, value=mapping['internal'] or 1, key=f"internal_{i}")
with cols[1]:
external_port = st.number_input("External Port", 1, 65535, value=mapping['external'] or 1, key=f"external_{i}")
with cols[2]:
protocol = st.selectbox("Protocol", ["tcp", "udp"], index=0 if mapping['protocol'] == 'tcp' else 1, key=f"protocol_{i}")
with cols[3]:
use_dns = st.checkbox("Use DNS", value=False, key=f"use_dns_{i}")
submit = st.form_submit_button("Create Container Workload")
if submit:
if not docker_image:
st.warning("Please specify a Docker image.")
else:
vdc_id = vdc_options[selected_vdc_name_create]
port_mappings = []
for i in range(len(st.session_state.port_mappings)):
internal = st.session_state.get(f"internal_{i}")
external = st.session_state.get(f"external_{i}")
protocol = st.session_state.get(f"protocol_{i}")
use_dns = st.session_state.get(f"use_dns_{i}")
if internal and external:
port_mappings.append({
"internal": int(internal),
"external": int(external),
"protocol": protocol,
"use_dns": use_dns
})
container_payload = {
"docker_image": docker_image,
"container_name": container_name,
"networks": "bridge"
}
if port_mappings:
container_payload["ports"] = port_mappings
payload = {
"virtual_data_center": vdc_id,
"containers": [container_payload]
}
result = st.session_state.client.create_container(payload)
if result:
st.success("Container workload created successfully!")
st.session_state.port_mappings = [{
'internal': None, 'external': None,
'use_dns': True, 'protocol': 'tcp'
}]
st.rerun()
if st.button("➕ Add Another Port Mapping"):
st.session_state.port_mappings.append({
'internal': None, 'external': None,
'use_dns': True, 'protocol': 'tcp'
})
st.rerun()
# List Container Pods
st.subheader("Existing Container Pods")
pods = st.session_state.client.get_container_pods()
if pods:
selected_vdc_id = vdc_options[selected_vdc_name]
filtered_pods = [pod for pod in pods if pod['vdc_id'] == selected_vdc_id]
if not st.session_state.pod_delete_all_confirmation_shown:
if st.button("Delete All Pods", key="delete_all_pods", type="primary"):
st.session_state.pod_delete_all_confirmation_shown = True
st.rerun()
if st.session_state.pod_delete_all_confirmation_shown and not st.session_state.pod_delete_all_confirmed:
st.warning("Are you sure you want to delete all pods?")
col1, col2 = st.columns([1, 1])
with col1:
if st.button("Yes, Delete All Pods", key="confirm_delete_all_pods"):
st.session_state.pod_delete_all_confirmed = True
st.rerun()
with col2:
if st.button("Cancel", key="cancel_delete_all_pods"):
st.session_state.pod_delete_all_confirmation_shown = False
st.rerun()
if st.session_state.pod_delete_all_confirmed:
deletion_count = 0
for pod in filtered_pods:
result = st.session_state.client.delete_container_pod(pod['pod_id'])
if result:
deletion_count += 1
st.success(f"Successfully deleted {deletion_count} pods.")
st.session_state.pod_delete_all_confirmation_shown = False
st.session_state.pod_delete_all_confirmed = False
st.rerun()
for pod in filtered_pods:
with st.expander(f"🛰️ Pod: {pod['pod_name']} (ID: {pod['pod_id']})"):
st.markdown(f"**Virtual Data Center ID:** `{pod['vdc_id']}`")
st.markdown(f"**Region ID:** `{pod.get('region_id', 'N/A')}`")
st.markdown(f"**NSController ID:** `{pod['nscontroller_workload_id']}`")
if pod.get('port_forwardings'):
st.markdown("🔁 **Port Forwarding:**")
for pf in pod['port_forwardings']:
dns_status = "🌐 DNS" if pf.get('dns_record_id') else "🚫 No DNS"
cols = st.columns([1, 2, 2,1])
with cols[0]: st.markdown(f"**{pf['protocol'].upper()}**")
with cols[1]: st.text(f"{pf['external_port']} → {pf['internal_port']}")
with cols[2]: st.text(dns_status)
with cols[3]: st.text(pf['container_name'])
link = pf.get('dns_record_hostname') or f"{pf['ip_address']}:{pf['external_port']}"
st.page_link(f"http://{link}", label=f"http://{link}")
st.markdown("---")
else:
st.text("No port forwardings")
for container in pod['containers']:
with st.container():
cols = st.columns([3, 2, 1])
with cols[0]:
st.markdown(f"**Container Name:** {container['container_name']}")
st.text(f"Status: {container['status']}")
with cols[1]:
st.text(f"Container ID: {container['container_id']}")
with cols[2]:
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.rerun()
st.page_link(
f"http://127.0.0.5:5000/ui/container_logs/{container['container_id']}",
label="Console & Logs",
icon="🖥️"
)
st.markdown("---")
if st.button(f"🗑️ Delete Pod {pod['pod_name']}", key=f"delete_pod_{pod['pod_id']}"):
result = st.session_state.client.delete_container_pod(pod['pod_id'])
if result:
st.success(f"Deleted Pod {pod['pod_name']}")
st.rerun()
else:
st.info("No pods found in the selected VDC.")
def render_detail():
"""
Renders the details of a specific container workload.
"""
container = st.session_state.client.get_container(st.session_state.selected_resource['container_id'])
if not container:
st.error("Container workload not found.")
st.session_state.selected_resource = None
st.session_state.view_type = 'list'
st.session_state.selected_resource_type = None
return
st.title(f"Container Workload: {container['container_id']}")
if st.button("Back to list view"):
st.session_state.view_type = 'list'
st.rerun()
st.header("Container Workload Details")
col1, col2 = st.columns(2)
with col1:
st.markdown(f"**ID:** `{container['id']}`")
st.markdown(f"**Created:** {format_timestamp(container.get('created_at', 'Unknown'))}")
st.markdown(f"**VDC ID:** {container['vdc_id']}")
st.markdown(f"**Workload Host ID:** {container.get('container_host_id', 'N/A')}")
with col2:
if container.get('updated_at'):
st.markdown(f"**Last Updated:** {format_timestamp(container['updated_at'])}")
st.markdown(f"**Status:** {container['status']}")
st.markdown(f"**Launch Parameters:** {container.get('launch_params', 'N/A')}")
# Define container states once
CONTAINER_STATES = [
"running", "stopped", "pending",
"failed-deleted", "pending-deleted",
"pending-allocation", "failed-allocation",
"pending-allocated", "dead"
]
# Edit Container Workload Form
with st.expander("Edit Container Workload"):
with st.form("edit_container"):
new_status = st.selectbox(
"Status",
CONTAINER_STATES,
index=CONTAINER_STATES.index(container['status'])
)
new_launch_params = st.text_input("Launch Parameters", value=container.get('launch_params', ''))
submit = st.form_submit_button("Update Container Workload")
if submit:
result = st.session_state.client.edit_container(container['id'], {
"status": new_status,
"launch_params": new_launch_params
})
if result:
st.success("Container workload updated successfully!")
st.session_state.selected_resource = container
st.session_state.view_type = 'detail'
st.session_state.selected_resource_type = 'container'
st.rerun()
# Delete functionality
if not st.session_state.container_delete_confirmation_shown:
if st.button("Delete container", key="delete_container", type="primary", help="This will delete visible container"):
st.session_state.container_delete_confirmation_shown = True
st.rerun()
# Show confirmation if delete was clicked
if st.session_state.container_delete_confirmation_shown and not st.session_state.container_delete_confirmed:
st.warning("Are you sure you want to delete container? This action cannot be undone.")
confirm_col1, confirm_col2 = st.columns([1, 1])
with confirm_col1:
if st.button("Yes, Delete", key="confirm_delete_container"):
st.session_state.container_delete_confirmed = True
st.rerun()
with confirm_col2:
if st.button("Cancel", key="cancel_delete_container"):
st.session_state.container_delete_confirmation_shown = False
st.rerun()
# Perform deletion if confirmed
if st.session_state.container_delete_confirmed:
result = st.session_state.client.delete_container(container['id'])
st.session_state.container_delete_confirmation_shown = False
st.session_state.container_delete_confirmed = False
if result:
st.success("Container workload deleted successfully!")
st.session_state.selected_resource = None
st.session_state.view_type = 'list'
st.session_state.selected_resource_type = 'container'
st.rerun()
st.success(f"Successfully deleted container.")
# Reset the confirmation flags
st.session_state.container_delete_confirmation_shown = False
st.session_state.container_delete_confirmed = False
if st.session_state.view_type == 'detail' and st.session_state.selected_resource_type=='container':
render_detail()
else:
render_list()