updated vm_launch params
This commit is contained in:
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
-----------
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user