updated vm_launch params

This commit is contained in:
2025-03-17 23:04:27 +10:30
parent a80c3fc59f
commit a59f20b74c
3 changed files with 236 additions and 66 deletions
+195 -58
View File
@@ -15,6 +15,77 @@ from app.models.network import Network
websocket_server_url = "http://127.0.0.1:6000/api/create_task"
def validate_port_entry(port_entry):
"""
Validate a single port entry in the open_ports list.
Valid formats include:
- protocol/port (e.g., "tcp/80")
- protocol/port-range (e.g., "tcp/1024-2048")
- protocol/port:source_ip (e.g., "tcp/22:103.104.105.106")
- protocol name only (e.g., "icmp", "tcp", "udp")
Args:
port_entry (str): The port entry to validate
Returns:
bool: True if valid, False otherwise
"""
import re
# Protocol names only (e.g., "icmp", "tcp", "udp")
if port_entry.lower() in ["icmp", "tcp", "udp"]:
return True
# For other formats, split by protocol and the rest
if "/" not in port_entry:
return False
protocol, rest = port_entry.split("/", 1)
# Validate protocol
if protocol.lower() not in ["tcp", "udp", "icmp"]:
return False
# Check for source IP format (port:ip)
if ":" in rest:
port_part, ip_part = rest.split(":", 1)
# Validate IP address format
ip_pattern = r"^(\d{1,3}\.){3}\d{1,3}$"
if not re.match(ip_pattern, ip_part):
return False
# Also validate that IP octets are in valid range (0-255)
octets = ip_part.split(".")
if not all(0 <= int(octet) <= 255 for octet in octets):
return False
else:
port_part = rest
# Validate port or port range
if "-" in port_part:
# Port range format
try:
start_port, end_port = port_part.split("-", 1)
start_port = int(start_port)
end_port = int(end_port)
# Validate port range
if not (0 <= start_port <= 65535 and 0 <= end_port <= 65535 and start_port < end_port):
return False
except ValueError:
return False
else:
# Single port format
try:
port = int(port_part)
if not (0 <= port <= 65535):
return False
except ValueError:
return False
return True
def validate_payload(payload):
"""
Validate the input payload according to the specified rules and return a sanitized version.
@@ -28,6 +99,8 @@ def validate_payload(payload):
Raises:
ValueError: If the payload fails any validation rule.
"""
import uuid # Added import for UUID validation
# Ensure the input is a valid JSON object (dict)
if not isinstance(payload, dict):
raise ValueError("Input must be a valid JSON object (dict).")
@@ -40,11 +113,11 @@ def validate_payload(payload):
except ValueError:
raise ValueError("'vdc' must be a valid UUID.")
# Check for the 'virtual-machines' key and ensure it is a list with at least one VirtualMachine
# Check for the 'virtual-machines' key and ensure it is a list with at least one VM
if 'virtual-machines' not in payload:
raise ValueError("'virtual-machines' key is missing.")
if not isinstance(payload['virtual-machines'], list) or len(payload['virtual-machines']) == 0:
raise ValueError("'virtual-machines' must be a list with at least one VirtualMachine.")
raise ValueError("'virtual-machines' must be a list with at least one VM.")
# Initialize the sanitized payload
sanitized_payload = {
@@ -52,63 +125,127 @@ def validate_payload(payload):
'virtual-machines': []
}
# Validate each VirtualMachine in the 'virtual-machines' list and build the sanitized version
for VirtualMachine in payload['virtual-machines']:
if not isinstance(VirtualMachine , dict):
raise ValueError("Each VirtualMachine must be a dictionary.")
# Validate each VM in the 'virtual-machines' list and build the sanitized version
for vm in payload['virtual-machines']:
if not isinstance(vm, dict):
raise ValueError("Each virtual machine must be a dictionary.")
# Check for required keys: 'virtual_machine_name' and 'virtual_machine_config'
if 'virtual_machine_name' not in VirtualMachine :
raise ValueError("VirtualMachine is missing 'virtual_machine_name'.")
if 'virtual_machine_config' not in VirtualMachine :
raise ValueError("VirtualMachine is missing 'virtual_machine_config'.")
# Check for required keys in the new format
if 'name' not in vm:
raise ValueError("Virtual machine is missing 'name'.")
if 'memory' not in vm:
raise ValueError("Virtual machine is missing 'memory'.")
if 'vcpu' not in vm:
raise ValueError("Virtual machine is missing 'vcpu'.")
# Validate 'virtual_machine_config'
virtual_machine_config = VirtualMachine['virtual_machine_config']
if not isinstance(virtual_machine_config, dict):
raise ValueError("'virtual_machine_config' must be a dictionary.")
# Validate 'memory' and 'vcpu'
if 'memory' not in virtual_machine_config or not isinstance(virtual_machine_config['memory'], int) or virtual_machine_config['memory'] <= 0:
# Validate numeric fields
if not isinstance(vm['memory'], int) or vm['memory'] <= 0:
raise ValueError("'memory' must be a positive integer.")
if 'vcpu' not in virtual_machine_config or not isinstance(virtual_machine_config['vcpu'], int) or virtual_machine_config['vcpu'] <= 0:
if not isinstance(vm['vcpu'], int) or vm['vcpu'] <= 0:
raise ValueError("'vcpu' must be a positive integer.")
# Validate 'volumes' if present
if 'volumes' in virtual_machine_config:
if not isinstance(virtual_machine_config['volumes'], list):
raise ValueError("'volumes' must be a list.")
for volume in virtual_machine_config['volumes']:
if not isinstance(volume, dict):
raise ValueError("Each volume must be a dictionary.")
if 'name' not in volume or 'size_gb' not in volume:
raise ValueError("Volume is missing 'name' or 'size_gb'.")
if not isinstance(volume['size_gb'], (int, float)) or volume['size_gb'] <= 0:
raise ValueError("'size_gb' must be a positive number.")
# Validate 'networks' if present
if 'networks' in virtual_machine_config:
if not isinstance(virtual_machine_config['networks'], list):
raise ValueError("'networks' must be a list.")
for network in virtual_machine_config['networks']:
if not isinstance(network, dict):
raise ValueError("Each network must be a dictionary.")
if 'id' not in network:
raise ValueError("Network is missing 'id'.")
# Initialize the sanitized VirtualMachine
sanitized_VirtualMachine = {
'virtual_machine_name': VirtualMachine['virtual_machine_name'],
'virtual_machine_config': {
'memory': virtual_machine_config['memory'],
'vcpu': virtual_machine_config['vcpu'],
'volumes': virtual_machine_config.get('volumes', []),
'networks': virtual_machine_config.get('networks', [])
}
# Initialize the sanitized VM
sanitized_vm = {
'name': vm['name'],
'memory': vm['memory'],
'vcpu': vm['vcpu']
}
# Add the sanitized VirtualMachine to the sanitized payload
sanitized_payload['virtual-machines'].append(sanitized_VirtualMachine)
# Validate 'volumes' if present
if 'volumes' in vm:
if not isinstance(vm['volumes'], list):
raise ValueError("'volumes' must be a list.")
sanitized_volumes = []
for volume in vm['volumes']:
if not isinstance(volume, dict):
raise ValueError("Each volume must be a dictionary.")
if 'name' not in volume or 'size_gb' not in volume:
raise ValueError("Volume is missing 'name' or 'size_gb'.")
if not isinstance(volume['size_gb'], (int, float)) or volume['size_gb'] <= 0:
raise ValueError("'size_gb' must be a positive number.")
sanitized_volume = {
'name': volume['name'],
'size_gb': volume['size_gb']
}
# Add optional volume fields if present
if 'boot' in volume:
if not isinstance(volume['boot'], bool):
raise ValueError("'boot' must be a boolean.")
sanitized_volume['boot'] = volume['boot']
if 'type' in volume:
sanitized_volume['type'] = volume['type']
if 'source' in volume:
sanitized_volume['source'] = volume['source']
sanitized_volumes.append(sanitized_volume)
sanitized_vm['volumes'] = sanitized_volumes
# Validate 'networks' if present
if 'networks' in vm:
if not isinstance(vm['networks'], list):
raise ValueError("'networks' must be a list.")
sanitized_networks = []
for network in vm['networks']:
if not isinstance(network, dict):
raise ValueError("Each network must be a dictionary.")
if 'id' not in network:
raise ValueError("Network is missing 'id'.")
sanitized_network = {
'id': network['id']
}
# Add optional network fields if present
if 'mac' in network:
# Could add MAC address format validation here
sanitized_network['mac'] = network['mac']
if 'ip_address' in network:
# Could add IP address format validation here
sanitized_network['ip_address'] = network['ip_address']
if 'open_ports' in network:
if not isinstance(network['open_ports'], list):
raise ValueError("'open_ports' must be a list.")
# Validate each port entry
for port_entry in network['open_ports']:
if not isinstance(port_entry, str):
raise ValueError("Each port entry must be a string.")
if not validate_port_entry(port_entry):
raise ValueError(f"Invalid port entry format: '{port_entry}'")
sanitized_network['open_ports'] = network['open_ports']
sanitized_networks.append(sanitized_network)
sanitized_vm['networks'] = sanitized_networks
# Validate 'tags' if present
if 'tags' in vm:
if not isinstance(vm['tags'], dict):
raise ValueError("'tags' must be a dictionary.")
sanitized_vm['tags'] = vm['tags']
# Validate 'requirements' if present
if 'requirements' in vm:
if not isinstance(vm['requirements'], dict):
raise ValueError("'requirements' must be a dictionary.")
sanitized_vm['requirements'] = vm['requirements']
# Add the sanitized VM to the sanitized payload
sanitized_payload['virtual-machines'].append(sanitized_vm)
return sanitized_payload
@@ -142,7 +279,7 @@ def add_VirtualMachine_workload1():
# Create the Workload instance in the database
new_VirtualMachine = Workload(
name=VirtualMachine['virtual_machine_name'],
name=VirtualMachine['name'],
workload_type="VirtualMachine", # Set workload_type to VirtualMachine
vdc_id=request_vdc.id,
status="pending-allocation",
@@ -155,7 +292,7 @@ def add_VirtualMachine_workload1():
# Create volumes in the database
volumes = []
for volume in VirtualMachine['virtual_machine_config'].get('volumes', []):
for volume in VirtualMachine.get('volumes', []):
new_volume = Volume(
name=volume['name'],
size_gb=volume['size_gb'],
@@ -168,7 +305,7 @@ def add_VirtualMachine_workload1():
# Create network ports if networks are specified
networks = []
for network in VirtualMachine['virtual_machine_config'].get('networks', []):
for network in VirtualMachine.get('networks', []):
network_obj = Network.query.filter_by(id=uuid.UUID(network['id'])).first()
if network_obj:
port = network_obj.create_port(db.session, workload_id=new_VirtualMachine.id)
@@ -179,12 +316,12 @@ def add_VirtualMachine_workload1():
"worker_id": random_host.id,
"task_type": "virtual-machine-create",
"job_details": {
"virtual_machine_name": VirtualMachine['virtual_machine_name'],
"virtual_machine_name": VirtualMachine['name'],
"virtual_machine_id": new_VirtualMachine.id,
"desired_state": "running",
"virtual_machine_config": {
"memory": VirtualMachine['virtual_machine_config']['memory'],
"vcpu": VirtualMachine['virtual_machine_config']['vcpu'],
"memory": VirtualMachine['memory'],
"vcpu": VirtualMachine['vcpu'],
"volumes": [{"id": vol.id, "size_gb": vol.size_gb, "format": "qcow2"} for vol in volumes],
"networks": [{"id": net.id, "mac_address": net.mac_address} for net in networks]
}
+35
View File
@@ -56,3 +56,38 @@ VM launch flow
User requests vm launch
We create and add networkPort and virtual machine objects to DB
Craft the 'create-vm' task to send to the worker accordingly
---
VM Launch payload
{
"vdc": "f9640ab7-3e8f-430a-83a7-abd0a2102fa3",
"virtual-machines": [
{
"name": "test_vm_name",
"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"}
],
"networks": [
{
"id": "{networkID}",
"mac":"00:11:22:33:44:55",
"ip_address":"192.168.50.124",
"open_ports":["tcp/80","udp/81","icmp","tcp/1024-2048","tcp/22:103.104.105.106"]
}
],
"tags": {
"tag1": "value",
"tag3": "value"
}
,
"requirements": {
"tag": "value"
}
}
]
}
-----------
+6 -8
View File
@@ -71,17 +71,15 @@ def render():
if submit and selected_vdc_name_create and vm_name:
# Use the selected VDC's ID
vdc_id = vdc_options[selected_vdc_name_create]
vm_config = {
"memory": memory,
"vcpu": vcpu,
"volumes": st.session_state.volumes,
"networks": [{"id": net_id} for net_id in selected_network_ids]
}
result = st.session_state.client.create_virtual_machine({
"vdc": vdc_id,
"virtual-machines": [{
"virtual_machine_name": vm_name,
"virtual_machine_config": vm_config
"name": vm_name,
"memory": memory,
"vcpu": vcpu,
"volumes": st.session_state.volumes,
"networks": [{"id": net_id} for net_id in selected_network_ids]
}]
})
if result: