fixing get inconsistency and adding vm_state_command
This commit is contained in:
parent
2e265703bd
commit
e8348052a8
@ -30,11 +30,11 @@ def makeCapsulId():
|
||||
def double_check_capsul_address(id, ipv4, get_ssh_host_keys):
|
||||
try:
|
||||
result = current_app.config["HUB_MODEL"].get(id, get_ssh_host_keys)
|
||||
if result != None and result.ipv4 != ipv4:
|
||||
if result != None and result.ipv4 != None and result.ipv4 != ipv4:
|
||||
ipv4 = result.ipv4
|
||||
get_model().update_vm_ip(email=session["account"], id=id, ipv4=result.ipv4)
|
||||
|
||||
if result != None and get_ssh_host_keys:
|
||||
if result != None and result.ipv4 != None and get_ssh_host_keys:
|
||||
get_model().update_vm_ssh_host_keys(email=session["account"], id=id, ssh_host_keys=result.ssh_host_keys)
|
||||
except:
|
||||
current_app.logger.error(f"""
|
||||
|
@ -45,6 +45,9 @@ class MockHub(VirtualizationInterface):
|
||||
def destroy(self, email: str, id: str):
|
||||
current_app.logger.info(f"mock destroy: {id} for {email}")
|
||||
|
||||
def vm_state_command(self, email: str, id: str, command: str):
|
||||
current_app.logger.info(f"mock {command}: {id} for {email}")
|
||||
|
||||
|
||||
class CapsulFlaskHub(VirtualizationInterface):
|
||||
|
||||
@ -236,3 +239,25 @@ class CapsulFlaskHub(VirtualizationInterface):
|
||||
|
||||
if not result_status == "success":
|
||||
raise ValueError(f"""failed to destroy vm "{id}" on host "{host.id}" for {email}: {result_json_string}""")
|
||||
|
||||
|
||||
def vm_state_command(self, email: str, id: str, command: str):
|
||||
validate_capsul_id(id)
|
||||
result_status = None
|
||||
host = get_model().host_of_capsul(id)
|
||||
if host is not None:
|
||||
payload = json.dumps(dict(type="vm_state_command", email=email, id=id, command=command))
|
||||
results = self.synchronous_operation([host], payload)
|
||||
result_json_string = "<no response from host>"
|
||||
for result in results:
|
||||
try:
|
||||
result_json_string = result.body
|
||||
result_body = json.loads(result_json_string)
|
||||
if isinstance(result_body, dict) and 'status' in result_body:
|
||||
result_status = result_body['status']
|
||||
except:
|
||||
pass
|
||||
|
||||
if not result_status == "success":
|
||||
raise ValueError(f"""failed to {command} vm "{id}" on host "{host.id}" for {email}: {result_json_string}""")
|
||||
|
||||
|
@ -39,6 +39,9 @@ class VirtualizationInterface:
|
||||
def destroy(self, email: str, id: str):
|
||||
pass
|
||||
|
||||
def vm_state_command(self, email: str, id: str, command: str):
|
||||
pass
|
||||
|
||||
def validate_capsul_id(id):
|
||||
if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", id):
|
||||
raise ValueError(f"vm id \"{id}\" must match \"^capsul-[a-z0-9]{{10}}$\"")
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
vmname="$1"
|
||||
@ -7,10 +8,13 @@ if echo "$vmname" | grep -vqE '^(cvm|capsul)-[a-z0-9]{10}$'; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! virsh list --name --all | grep -qE "^$vmname$" ; then
|
||||
echo "Error: $vmname not found"
|
||||
exit 1
|
||||
# this will let us know if the vm exists or not
|
||||
exists="false"
|
||||
if virsh domuuid "$vmname" | grep -vqE '^[\t\s\n]*$'; then
|
||||
exists="true"
|
||||
fi
|
||||
|
||||
# this gets the ipv4
|
||||
virsh domifaddr "$vmname" | awk '/vnet/ {print $4}' | cut -d'/' -f1
|
||||
ipv4="$(virsh domifaddr "$vmname" | awk '/vnet/ {print $4}' | cut -d'/' -f1)"
|
||||
|
||||
echo "$exists $ipv4"
|
@ -52,6 +52,7 @@ def operation_impl(operation_id: int):
|
||||
"list_ids": handle_list_ids,
|
||||
"create": handle_create,
|
||||
"destroy": handle_destroy,
|
||||
"vm_state_command": handle_vm_state_command,
|
||||
}
|
||||
|
||||
error_message = ""
|
||||
@ -90,11 +91,8 @@ def handle_get(operation_id, request_body):
|
||||
return abort(400, f"bad request; id is required for get")
|
||||
|
||||
vm = current_app.config['SPOKE_MODEL'].get(request_body['id'], request_body['get_ssh_host_keys'])
|
||||
# TODO vm can be None when the capsul exists on this host but has no IP address yet
|
||||
# when this happens it logs an "error reading assignment_status"
|
||||
# To fix this we need to
|
||||
# 1. modify shell script to return does it exist on this host + if it does whats its ip
|
||||
# 2. if exists but no ip, return assigned with no ip
|
||||
if vm is None:
|
||||
return jsonify(dict(assignment_status="assigned"))
|
||||
|
||||
return jsonify(dict(assignment_status="assigned", id=vm.id, host=vm.host, ipv4=vm.ipv4, ipv6=vm.ipv6, ssh_host_keys=vm.ssh_host_keys))
|
||||
|
||||
@ -168,3 +166,25 @@ def handle_destroy(operation_id, request_body):
|
||||
return jsonify(dict(assignment_status="assigned", status="error", error_message=error_message))
|
||||
|
||||
return jsonify(dict(assignment_status="assigned", status="success"))
|
||||
|
||||
|
||||
def handle_vm_state_command(operation_id, request_body):
|
||||
|
||||
required_properties = ['id', 'email', 'command']
|
||||
for required_property in required_properties:
|
||||
if required_property not in request_body:
|
||||
current_app.logger.error(f"/hosts/operation returned 400: {required_property} is required for vm_state_command")
|
||||
return abort(400, f"bad request; {required_property} is required for vm_state_command")
|
||||
|
||||
if request_body['command'] not in ["stop", "force-stop", "start", "restart"]:
|
||||
current_app.logger.error(f"/hosts/operation returned 400: command ({request_body['command']}) must be one of stop, force-stop, start, or restart")
|
||||
return abort(400, f"bad request; command ({request_body['command']}) must be one of stop, force-stop, start, or restart")
|
||||
|
||||
try:
|
||||
current_app.config['SPOKE_MODEL'].vm_state_command(id=request_body['id'], email=request_body['email'], command=request_body['command'])
|
||||
except:
|
||||
error_message = my_exec_info_message(sys.exc_info())
|
||||
current_app.logger.error(f"current_app.config['SPOKE_MODEL'].vm_state_command(id='{request_body['id']}', email='{request_body['email']}, command='{request_body['command']}') failed: {error_message}")
|
||||
return jsonify(dict(assignment_status="assigned", status="error", error_message=error_message))
|
||||
|
||||
return jsonify(dict(assignment_status="assigned", status="success"))
|
@ -41,6 +41,9 @@ class MockSpoke(VirtualizationInterface):
|
||||
def destroy(self, email: str, id: str):
|
||||
current_app.logger.info(f"mock destroy: {id} for {email}")
|
||||
|
||||
def vm_state_command(self, email: str, id: str, command: str):
|
||||
current_app.logger.info(f"mock {command}: {id} for {email}")
|
||||
|
||||
class ShellScriptSpoke(VirtualizationInterface):
|
||||
|
||||
def validate_completed_process(self, completedProcess, email=None):
|
||||
@ -86,10 +89,19 @@ class ShellScriptSpoke(VirtualizationInterface):
|
||||
if len(lines) == 0:
|
||||
return None
|
||||
|
||||
ipaddr = lines[0].decode("utf-8")
|
||||
result_string = lines[0].decode("utf-8")
|
||||
|
||||
fields = result_string.split(" ")
|
||||
if fields[0] != "true":
|
||||
return None
|
||||
|
||||
if len(fields) < 2:
|
||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"])
|
||||
|
||||
ipaddr = fields[1]
|
||||
|
||||
if not re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", ipaddr):
|
||||
return None
|
||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"])
|
||||
|
||||
if get_ssh_host_keys:
|
||||
try:
|
||||
@ -172,4 +184,13 @@ class ShellScriptSpoke(VirtualizationInterface):
|
||||
{completedProcess.stderr}
|
||||
""")
|
||||
|
||||
def vm_state_command(self, email: str, id: str, command: str):
|
||||
validate_capsul_id(id)
|
||||
if command not in ["stop", "force-stop", "start", "restart"]:
|
||||
raise ValueError(f"command ({command}) must be one of stop, force-stop, start, or restart")
|
||||
|
||||
completedProcess = run([join(current_app.root_path, f"shell_scripts/{command}.sh"), id], capture_output=True)
|
||||
self.validate_completed_process(completedProcess, email)
|
||||
returned_string = completedProcess.stdout.decode("utf-8")
|
||||
current_app.logger.info(f"{command} vm {id} for {email} returned: {returned_string}")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user