This commit is contained in:
2025-03-20 21:24:47 +10:30
parent 505370bb06
commit 67efecad34
7 changed files with 288 additions and 44 deletions
+2 -1
View File
@@ -3,4 +3,5 @@ instance/*
logs/*
.venv/
mariadb_data/
.env
.env
.aider*
+47 -1
View File
@@ -217,4 +217,50 @@ class IaaSClient:
return self._make_request('PUT', f'workloads/{workload_id}', data)
def delete_workload(self, workload_id):
return self._make_request('DELETE', f'workloads/{workload_id}')
return self._make_request('DELETE', f'workloads/{workload_id}')
# Workload Host Resources
def get_pooled_resources_for_host(self, host_id):
"""
Fetch pooled resources for a specific workload host.
"""
return self._make_request('GET', f'workload-hosts/{host_id}/pooled-resources')
def get_fixed_resources_for_host(self, host_id):
"""
Fetch fixed resources for a specific workload host.
"""
return self._make_request('GET', f'workload-hosts/{host_id}/fixed-resources')
def get_ovs_bridges_for_host(self, host_id):
"""
Fetch OVS bridges for a specific workload host.
"""
return self._make_request('GET', f'workload-hosts/{host_id}/ovs-bridges')
# Workloads for Host
def get_workloads_for_host(self, host_id):
"""
Fetch all workloads (containers and virtual machines) for a specific host.
"""
# Fetch all containers and virtual machines
containers = self.get_containers() or []
virtual_machines = self.get_virtual_machines() or []
# Filter workloads by host_id
workloads = []
for container in containers:
if container.get('workload_host_id') == host_id:
container['workload_type'] = 'container' # Add workload type
workloads.append(container)
for vm in virtual_machines:
if vm.get('workload_host_id') == host_id:
vm['workload_type'] = 'virtual_machine' # Add workload type
workloads.append(vm)
return workloads
+101 -3
View File
@@ -67,8 +67,18 @@ VM Launch payload
"memory": 2048,
"vcpu": 2,
"volumes": [
{"name": "disk1", "size_gb": 20, "boot": true, "type": "local", "source":"image id or 'blank'"},
{"name": "disk2", "size_gb": 10, "type": "Ceph"}
{
"name": "disk1",
"size_gb": 20,
"boot": true,
"type": "local",
"image":"image id or 'blank'"
},
{
"name": "disk2",
"size_gb": 10,
"type": "Ceph"
}
],
"networks": [
{
@@ -90,4 +100,92 @@ VM Launch payload
}
]
}
-----------
-----------
Payload sent to worker from API server
-----------
{
"worker_id":"UUID(""d266b29d-a888-430d-8873-151c41fd34e3"")",
"task_type":"virtual-machine-create",
"job_details":{
"virtual_machine_name":"my-vm",
"virtual_machine_id":"UUID(""16cb0f4a-d445-48c2-ab53-b8c9821e8c68"")",
"desired_state":"running",
"virtual_machine_config":{
"memory":2048,
"vcpu":2,
"volumes":[
{
"id":"UUID(""e6553965-6039-44e0-96cb-e0094f3b60be"")",
"name":"v1",
"size_gb":11.0,
"type":"None",
"source":"vol.source"
},
{
"id":"UUID(""7309cd8e-9845-4b16-bae2-092dcb78fbea"")",
"name":"v2",
"size_gb":12.0,
"type":"None",
"source":"vol.source"
},
{
"id":"UUID(""79cafd2e-f29e-4a70-b6f1-2d8ae6d07d05"")",
"name":"v3",
"size_gb":13.0,
"type":"None",
"source":"vol.source"
}
],
"networks":[
]
}
}
}
Image \ Volume flow
Create a blank volume locally
Input:
type: local
source: None
Output:
type: local
source: None
path: None
volume_id: {volumeID}
Create a blank volume on ceph-highspeed
Input:
type: ceph-highspeed
source: None
Output
type: ceph
path: highspeed
source: None
volume_id: {volumeID}
Create a volume locally from a URL image
Input
type: local
source: {imageID}
Output:
type: local
path: None
source: http://some-url/image.qcow2?secret=dd
volume_id: {volumeID}
Create a volume on ceph-nvme from a URL image
Input
type: ceph-nvme
source: {imageID}
Output:
type: ceph
path: nvme
source: http://some-url/image.qcow2?secret=dd
volume_id: {volumeID}
Create a volume locally from a ceph-slow image
Create a volume on ceph from a ceph-nvme image
-38
View File
@@ -1,38 +0,0 @@
import streamlit as st
import pandas as pd
def render():
resource = st.session_state.selected_resource
resource_type = st.session_state.selected_resource_type
if not resource:
st.warning("No resource selected.")
return
print(f"Resource type: {resource_type}")
if resource_type == 'universe':
render_detail_view_universe(resource)
elif resource_type == 'project':
render_detail_view_project(resource)
elif resource_type == 'vdc':
render_detail_view_vdc(resource)
elif resource_type == 'region':
render_detail_view_region(resource)
elif resource_type == 'network':
render_detail_view_network(resource)
elif resource_type == 'container':
render_detail_view_container(resource)
elif resource_type == 'VirtualMachine':
render_detail_view_virtual_machine(resource)
import streamlit as st
+136
View File
@@ -0,0 +1,136 @@
import streamlit as st
from streamlit_server.utils.helpers import format_timestamp
import uuid
def render_list():
st.header("Workload Host Management")
# Create Workload Host Form
with st.expander("Create New Workload Host"):
with st.form("create_workload_host"):
name = st.text_input("Workload Host Name")
region_id = st.text_input("Region ID")
secret_key = st.text_input("Secret Key", value=str(uuid.uuid4()))
system_manufacturer = st.text_input("System Manufacturer")
system_model = st.text_input("System Model")
physical_identifier = st.text_input("Physical Identifier")
dcim_identifier = st.text_input("DCIM Identifier")
installed_date = st.date_input("Installed Date")
installed_status = st.text_input("Installed Status")
hostname = st.text_input("Hostname")
available_for_scheduling = st.checkbox("Available for Scheduling", value=True)
placement_priority = st.number_input("Placement Priority", value=100)
submit = st.form_submit_button("Create Workload Host")
if submit and name and region_id:
result = st.session_state.client.create_workload_host(
name=name,
region_id=region_id,
secret_key=secret_key,
system_manufacturer=system_manufacturer,
system_model=system_model,
physical_identifier=physical_identifier,
dcim_identifier=dcim_identifier,
installed_date=installed_date,
installed_status=installed_status,
hostname=hostname,
available_for_scheduling=available_for_scheduling,
placement_priority=placement_priority
)
if result:
st.success(f"Workload Host '{name}' created successfully!")
# List Workload Hosts
st.subheader("Existing Workload Hosts")
workload_hosts = st.session_state.client.get_workload_hosts()
if workload_hosts:
for host in workload_hosts:
with st.container():
col1, col2, col3 = st.columns([3, 1, 1])
with col1:
st.markdown(f"### {host['name']}")
st.text(f"Hostname: {host['hostname']}")
st.text(f"Region ID: {host['region_id']}")
with col2:
st.text(f"ID: {host['id'][:8]}...")
with col3:
if st.button("View Details", key=f"view_{host['id']}"):
st.session_state.selected_resource = host
st.session_state.view_type = 'detail'
st.session_state.selected_resource_type = 'workload_host'
st.rerun() # Force Streamlit to rerun the script
def render_detail():
host_id = st.session_state.selected_resource['id']
workload_host = st.session_state.client.get_workload_host(host_id)
st.title(f"Workload Host: {workload_host['name']}")
if st.button("Back to list view"):
st.session_state.view_type = 'list'
st.rerun()
st.header("Workload Host 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']}`")
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']}`")
# 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:
for workload in workloads:
st.markdown(f"- **{workload['name']}** (ID: `{workload['id']}`)")
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.")
if st.session_state.view_type == 'detail' and st.session_state.selected_resource_type == 'workload_host':
render_detail()
else:
render_list()
+1
View File
@@ -16,6 +16,7 @@ pages = {
],
"Workloads": [
st.Page("streamlit_server/views/workload_hosts.py", title="Workload Hosts"),
st.Page("streamlit_server/views/containers.py", title="Containers"),
st.Page("streamlit_server/views/virtual_machines.py", title="Virtual Machines"),
+1 -1
View File
@@ -529,7 +529,7 @@ def handle_libvirt_event(data):
logger.debug(f"Sending task update payload to API server for VM ID {system_vm_id}")
payload = {
"new_status": "started",
"new_status": "running",
"system_vm_id": system_vm_id,
"worker_id": worker_id,
"timestamp": timestamp