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):
|
def double_check_capsul_address(id, ipv4, get_ssh_host_keys):
|
||||||
try:
|
try:
|
||||||
result = current_app.config["HUB_MODEL"].get(id, get_ssh_host_keys)
|
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
|
ipv4 = result.ipv4
|
||||||
get_model().update_vm_ip(email=session["account"], id=id, 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)
|
get_model().update_vm_ssh_host_keys(email=session["account"], id=id, ssh_host_keys=result.ssh_host_keys)
|
||||||
except:
|
except:
|
||||||
current_app.logger.error(f"""
|
current_app.logger.error(f"""
|
||||||
|
@ -45,6 +45,9 @@ class MockHub(VirtualizationInterface):
|
|||||||
def destroy(self, email: str, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
current_app.logger.info(f"mock destroy: {id} for {email}")
|
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):
|
class CapsulFlaskHub(VirtualizationInterface):
|
||||||
|
|
||||||
@ -236,3 +239,25 @@ class CapsulFlaskHub(VirtualizationInterface):
|
|||||||
|
|
||||||
if not result_status == "success":
|
if not result_status == "success":
|
||||||
raise ValueError(f"""failed to destroy vm "{id}" on host "{host.id}" for {email}: {result_json_string}""")
|
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):
|
def destroy(self, email: str, id: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def vm_state_command(self, email: str, id: str, command: str):
|
||||||
|
pass
|
||||||
|
|
||||||
def validate_capsul_id(id):
|
def validate_capsul_id(id):
|
||||||
if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", 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}}$\"")
|
raise ValueError(f"vm id \"{id}\" must match \"^capsul-[a-z0-9]{{10}}$\"")
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
vmname="$1"
|
vmname="$1"
|
||||||
@ -7,10 +8,13 @@ if echo "$vmname" | grep -vqE '^(cvm|capsul)-[a-z0-9]{10}$'; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! virsh list --name --all | grep -qE "^$vmname$" ; then
|
# this will let us know if the vm exists or not
|
||||||
echo "Error: $vmname not found"
|
exists="false"
|
||||||
exit 1
|
if virsh domuuid "$vmname" | grep -vqE '^[\t\s\n]*$'; then
|
||||||
|
exists="true"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# this gets the ipv4
|
# 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,
|
"list_ids": handle_list_ids,
|
||||||
"create": handle_create,
|
"create": handle_create,
|
||||||
"destroy": handle_destroy,
|
"destroy": handle_destroy,
|
||||||
|
"vm_state_command": handle_vm_state_command,
|
||||||
}
|
}
|
||||||
|
|
||||||
error_message = ""
|
error_message = ""
|
||||||
@ -90,11 +91,8 @@ def handle_get(operation_id, request_body):
|
|||||||
return abort(400, f"bad request; id is required for get")
|
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'])
|
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
|
if vm is None:
|
||||||
# when this happens it logs an "error reading assignment_status"
|
return jsonify(dict(assignment_status="assigned"))
|
||||||
# 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
|
|
||||||
|
|
||||||
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))
|
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))
|
||||||
|
|
||||||
@ -167,4 +165,26 @@ def handle_destroy(operation_id, request_body):
|
|||||||
current_app.logger.error(f"current_app.config['SPOKE_MODEL'].destroy(id='{request_body['id']}', email='{request_body['email']}') failed: {error_message}")
|
current_app.logger.error(f"current_app.config['SPOKE_MODEL'].destroy(id='{request_body['id']}', email='{request_body['email']}') failed: {error_message}")
|
||||||
return jsonify(dict(assignment_status="assigned", status="error", error_message=error_message))
|
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"))
|
return jsonify(dict(assignment_status="assigned", status="success"))
|
@ -41,6 +41,9 @@ class MockSpoke(VirtualizationInterface):
|
|||||||
def destroy(self, email: str, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
current_app.logger.info(f"mock destroy: {id} for {email}")
|
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):
|
class ShellScriptSpoke(VirtualizationInterface):
|
||||||
|
|
||||||
def validate_completed_process(self, completedProcess, email=None):
|
def validate_completed_process(self, completedProcess, email=None):
|
||||||
@ -85,11 +88,20 @@ class ShellScriptSpoke(VirtualizationInterface):
|
|||||||
lines = completedProcess.stdout.splitlines()
|
lines = completedProcess.stdout.splitlines()
|
||||||
if len(lines) == 0:
|
if len(lines) == 0:
|
||||||
return None
|
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):
|
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:
|
if get_ssh_host_keys:
|
||||||
try:
|
try:
|
||||||
@ -172,4 +184,13 @@ class ShellScriptSpoke(VirtualizationInterface):
|
|||||||
{completedProcess.stderr}
|
{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