From d25c6857603188d934e236641ae5e9e6fe8b8ecd Mon Sep 17 00:00:00 2001 From: matthewalanpenning Date: Sun, 29 Oct 2023 22:52:51 -0400 Subject: [PATCH] v0.4.0 --- CHANGELOG.md | 4 +- lxconsole/api/access_controls.py | 24 ++-- lxconsole/api/instance.py | 180 ++++++++++++------------------ lxconsole/api/instances.py | 3 +- lxconsole/templates/instance.html | 91 ++++++++++++--- lxconsole/templates/main.html | 6 +- lxconsole/templates/server.html | 2 +- 7 files changed, 169 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4dad0..5cde6f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ - Converted containers and virtual-machines endpoints to instances endpoint - Combined containers and virtual-machines pages to instances page - Datatable errors now display on console.log rather than the default alert - - Handled 404 Error on new virtual machine without logs + - Handled 404 error for logs on a new virtual machine + - Fixed several undefined value errors that may occur + - Updated virtual-machine cpu and memory functions to no longer use sleep # 0.3.0 - Improved Dockerfile for quicker builds diff --git a/lxconsole/api/access_controls.py b/lxconsole/api/access_controls.py index df00dc0..af8e740 100644 --- a/lxconsole/api/access_controls.py +++ b/lxconsole/api/access_controls.py @@ -40,6 +40,8 @@ def privilege_check(privilege, server_id = 0): #'change_cluster_member_state', #'change_instance_state', #'copy_instance', + 'copy_instance_proc_meminfo', + 'copy_instance_proc_stat', #'create_instance_backup', #'create_instance_snapshot_instance', #'create_instance_snapshot', @@ -82,13 +84,13 @@ def privilege_check(privilege, server_id = 0): 'get_server_resources', 'get_server_warnings', 'get_server', - 'get_instance_cpu_percentage', 'get_instance_cpu_usage', 'get_instance_disk_devices', 'get_instance_gpu_devices', 'get_instance_interfaces', - 'get_instance_memory_percentage', 'get_instance_network_devices', + 'get_instance_proc_meminfo', + 'get_instance_proc_stat', 'get_instance_proxy_devices', 'get_instance_state', 'get_instance_unix_devices', @@ -206,6 +208,8 @@ def privilege_check(privilege, server_id = 0): 'change_cluster_member_state', 'change_instance_state', 'copy_instance', + 'copy_instance_proc_meminfo', + 'copy_instance_proc_stat', 'create_instance_backup', 'create_instance_snapshot_instance', 'create_instance_snapshot', @@ -248,13 +252,13 @@ def privilege_check(privilege, server_id = 0): 'get_server_resources', 'get_server_warnings', 'get_server', - 'get_instance_cpu_percentage', 'get_instance_cpu_usage', 'get_instance_disk_devices', 'get_instance_gpu_devices', 'get_instance_interfaces', - 'get_instance_memory_percentage', 'get_instance_network_devices', + 'get_instance_proc_meminfo', + 'get_instance_proc_stat', 'get_instance_proxy_devices', 'get_instance_state', 'get_instance_unix_devices', @@ -372,6 +376,8 @@ def privilege_check(privilege, server_id = 0): 'change_cluster_member_state', 'change_instance_state', 'copy_instance', + 'copy_instance_proc_meminfo', + 'copy_instance_proc_stat', 'create_instance_backup', 'create_instance_snapshot_instance', 'create_instance_snapshot', @@ -414,13 +420,13 @@ def privilege_check(privilege, server_id = 0): 'get_server_resources', 'get_server_warnings', 'get_server', - 'get_instance_cpu_percentage', 'get_instance_cpu_usage', 'get_instance_disk_devices', 'get_instance_gpu_devices', 'get_instance_interfaces', - 'get_instance_memory_percentage', 'get_instance_network_devices', + 'get_instance_proc_meminfo', + 'get_instance_proc_stat', 'get_instance_proxy_devices', 'get_instance_state', 'get_instance_unix_devices', @@ -538,6 +544,8 @@ def privilege_check(privilege, server_id = 0): 'change_cluster_member_state', 'change_instance_state', 'copy_instance', + 'copy_instance_proc_meminfo', + 'copy_instance_proc_stat', 'create_instance_backup', 'create_instance_snapshot_instance', 'create_instance_snapshot', @@ -580,13 +588,13 @@ def privilege_check(privilege, server_id = 0): 'get_server_resources', 'get_server_warnings', 'get_server', - 'get_instance_cpu_percentage', 'get_instance_cpu_usage', 'get_instance_disk_devices', 'get_instance_gpu_devices', 'get_instance_interfaces', - 'get_instance_memory_percentage', 'get_instance_network_devices', + 'get_instance_proc_meminfo', + 'get_instance_proc_stat', 'get_instance_proxy_devices', 'get_instance_state', 'get_instance_unix_devices', diff --git a/lxconsole/api/instance.py b/lxconsole/api/instance.py index e43ee54..de416ea 100644 --- a/lxconsole/api/instance.py +++ b/lxconsole/api/instance.py @@ -614,7 +614,7 @@ def api_instance_endpoint(endpoint): return jsonify(results.json()) # Virtual Machine only - if endpoint == 'get_instance_cpu_percentage': + if endpoint == 'copy_instance_proc_stat': id = request.args.get('id') project = request.args.get('project') server = Server.query.filter_by(id=id).first() @@ -622,15 +622,62 @@ def api_instance_endpoint(endpoint): client_cert = get_client_crt() client_key = get_client_key() - first_cpu_time = 0 - first_idle_time = 0 - second_cpu_time = 0 - second_idle_time = 0 + # Unable to read /proc/stat directly on virtual machines use lxd files api...getting an EOF error + # So we will copy /proc/stat to /tmp/stat and then read that file as a work around + data = {} + #data.update({'command': ['/bin/sh', '-c', 'cat /proc/stat \u003e /tmp/stat' ]}) + data.update({'command': ['cp', '/proc/stat', '/tmp/stat']}) + data.update({'wait-for-websocket': False}) + data.update({'interactive': False}) + data.update({'width': 80 }) + data.update({'height': 24 }) + data.update({'user': 0 }) + data.update({'group': 0 }) + data.update({'cwd': "/" }) + data.update({'record-output': False }) + data.update({'environment': {} }) + + url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/exec?project=' + project + results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data) + return jsonify(results.json()) + + # Virtual Machine only + if endpoint == 'get_instance_proc_stat': + id = request.args.get('id') + project = request.args.get('project') + server = Server.query.filter_by(id=id).first() + name = request.args.get('name') + client_cert = get_client_crt() + client_key = get_client_key() + + #Get /proc/stat information but from /tmp/stat + url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/files?project=' + project + '&path=/tmp/stat' + results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key)) + if 'cpu' in results.text: + results = results.text.split('\n') + for result in results: + stats = result.split() + if 'cpu' in stats: + user_time = int(stats[1]) + system_time= int(stats[3]) + idle_time = int(stats[4]) + return jsonify({'user_time':user_time, 'system_time':system_time, 'idle_time':idle_time}) + return jsonify({'user_time':0, 'system_time':0, 'idle_time':1}) + + # Virtual Machine only + if endpoint == 'copy_instance_proc_meminfo': + id = request.args.get('id') + project = request.args.get('project') + server = Server.query.filter_by(id=id).first() + name = request.args.get('name') + client_cert = get_client_crt() + client_key = get_client_key() # Unable to read /proc/stat directly on virtual machines use lxd files api...getting an EOF error # So we will copy /proc/stat to /tmp/stat and then read that file as a work around data = {} - data.update({'command': ['cp', '/proc/stat', '/tmp/stat1']}) + #data.update({'command': ['/bin/sh', '-c', 'cat /proc/meminfo \u003e /tmp/meminfo' ]}) + data.update({'command': ['cp', '/proc/meminfo', '/tmp/meminfo']}) data.update({'wait-for-websocket': False}) data.update({'interactive': False}) data.update({'width': 80 }) @@ -643,71 +690,30 @@ def api_instance_endpoint(endpoint): url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/exec?project=' + project results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data) - - # Sleep to allow time for copy to complete - time.sleep(.5) + return jsonify(results.json()) - #Get first /proc/stat information but from /tmp/stat - url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/files?project=' + project + '&path=/tmp/stat1' + # Virtual Machine only + if endpoint == 'get_instance_proc_meminfo': + id = request.args.get('id') + project = request.args.get('project') + server = Server.query.filter_by(id=id).first() + name = request.args.get('name') + client_cert = get_client_crt() + client_key = get_client_key() + + #Get /proc/meminfo information but from /tmp/meminfo + url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/files?project=' + project + '&path=/tmp/meminfo' results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key)) - if 'cpu' in results.text: + if 'MemTotal' in results.text: results = results.text.split('\n') - for result in results: - stats = result.split() - if 'cpu' in stats: - user_time = int(stats[1]) - system_time= int(stats[3]) - idle_time = int(stats[4]) - first_cpu_time = user_time + system_time + idle_time - first_idle_time = idle_time - - # Sleep a second between reads - time.sleep(1) - - # Copy /proc/stat to /tmp/stat and then read that file as a work around - data = {} - data.update({'command': ['cp', '/proc/stat', '/tmp/stat2']}) - data.update({'wait-for-websocket': False}) - data.update({'interactive': False}) - data.update({'width': 80 }) - data.update({'height': 24 }) - data.update({'user': 0 }) - data.update({'group': 0 }) - data.update({'cwd': "/" }) - data.update({'record-output': False }) - data.update({'environment': {} }) - - url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/exec?project=' + project - results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data) - - # Sleep to allow time for copy to complete - time.sleep(.5) - - #Get second /proc/stat information - url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/files?project=' + project + '&path=/tmp/stat2' - results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key)) - if 'cpu' in results.text: - results = results.text.split('\n') - for result in results: - stats = result.split() - if 'cpu' in stats: - user_time = int(stats[1]) - system_time= int(stats[3]) - idle_time = int(stats[4]) - second_cpu_time = user_time + system_time + idle_time - second_idle_time = idle_time - - #Get CPU percentage - idle_time = second_idle_time - first_idle_time - total_time = second_cpu_time - first_cpu_time - if total_time == 0: - return jsonify({'percentage': 0, 'cpu1': first_cpu_time, 'cpu2': second_cpu_time, 'idle1': first_idle_time, 'idle2': second_idle_time}) - percentage = 100 * (1 - idle_time / total_time) - return jsonify({'percentage': percentage, 'cpu1': first_cpu_time, 'cpu2': second_cpu_time, 'idle1': first_idle_time, 'idle2': second_idle_time}) + mem_total = results[0].split() + mem_available = results[2].split() + percentage = 100 * ( 1 - ( int(mem_available[1]) / int(mem_total[1]) ) ) + return jsonify({'mem_total':int(mem_total[1]), 'mem_available':int(mem_available[1]), 'percentage':percentage}) + return jsonify({'mem_total':0, 'mem_available':0, 'percentage':0}) if endpoint == 'get_instance_cpu_usage': - id = request.args.get('id') project = request.args.get('project') server = Server.query.filter_by(id=id).first() @@ -875,50 +881,6 @@ def api_instance_endpoint(endpoint): interfaces.append(interface) return jsonify({ 'data': interfaces }) - # Virtual Machine only - if endpoint == 'get_instance_memory_percentage': - id = request.args.get('id') - project = request.args.get('project') - server = Server.query.filter_by(id=id).first() - name = request.args.get('name') - client_cert = get_client_crt() - client_key = get_client_key() - - percentage = 0 - mem_total = 0 - mem_available = 0 - - # Unable to read /proc/meminfo directly on virtual machines use lxd files api...getting an EOF error - # So we will copy /proc/meminfo to /tmp/meminfo and then read that file as a work around - data = {} - data.update({'command': ['cp', '/proc/meminfo', '/tmp/meminfo']}) - data.update({'wait-for-websocket': False}) - data.update({'interactive': False}) - data.update({'width': 80 }) - data.update({'height': 24 }) - data.update({'user': 0 }) - data.update({'group': 0 }) - data.update({'cwd': "/" }) - data.update({'record-output': False }) - data.update({'environment': {} }) - - url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/exec?project=' + project - results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data) - - # Sleep a second to give time to copy file - time.sleep(1) - - #Get /proc/meminfo information but from /tmp/meminfo - url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/instances/' + name + '/files?project=' + project + '&path=/tmp/meminfo' - results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key)) - if 'MemTotal' in results.text: - results = results.text.split('\n') - mem_total = results[0].split() - mem_available = results[2].split() - percentage = 100 * ( 1 - ( int(mem_available[1]) / int(mem_total[1]) ) ) - - return jsonify({'percentage': percentage, 'mem_total': mem_total, 'mem_available': mem_available}) - if endpoint == 'get_instance_network_devices': id = request.args.get('id') diff --git a/lxconsole/api/instances.py b/lxconsole/api/instances.py index 2c195a7..1428c84 100644 --- a/lxconsole/api/instances.py +++ b/lxconsole/api/instances.py @@ -193,7 +193,8 @@ def api_instances_endpoint(endpoint): # Set disk information if exists if 'disk' in instance['state'].keys(): - if 'root' in instance['state']['disk'].keys(): + # instance['state']['disk'] may exists but have have any .keys() + if instance['state']['disk'] and 'root' in instance['state']['disk'].keys(): if 'usage' in instance['state']['disk']['root'].keys(): disk = instance['state']['disk']['root']['usage'] if disk: diff --git a/lxconsole/templates/instance.html b/lxconsole/templates/instance.html index 8047e9b..47e3364 100644 --- a/lxconsole/templates/instance.html +++ b/lxconsole/templates/instance.html @@ -713,6 +713,9 @@ //Load Instance State Data and Configuration loadInstanceStateData() + sessionStorage.removeItem("instance_user_time"); + sessionStorage.removeItem("instance_system_time"); + sessionStorage.removeItem("instance_idle_time"); loadCpuPercentage() //Load Home Tab Tables @@ -762,6 +765,70 @@ } + function getInstanceProcMeminfo() { + $.get("../api/instance/get_instance_proc_meminfo?id="+serverId+"&project="+project+"&name="+instance, function (data) { + if (data.mem_total > 0){ + percentage = parseFloat(100 * ( 1 - parseInt(data.mem_available) / parseInt(data.mem_total))).toFixed(2) + } + else { + percentage = parseFloat(0).toFixed(2) + } + + //Update Memory progress bar + $('#memPercentage').text(percentage) + $('#memPercentageBar').css('width', percentage + '%'); + $('#memPercentageBar').attr('aria-valuenow', percentage + '%'); + if (percentage > 90.00) + $('#memPercentageBar').attr('class', 'progress-bar bg-danger'); + else + $('#memPercentageBar').attr('class', 'progress-bar bg-primary'); + }); + } + + function getInstanceProcStat() { + $.get("../api/instance/get_instance_proc_stat?id="+serverId+"&project="+project+"&name="+instance, function (data) { + + session_user_time = sessionStorage.getItem('instance_user_time') + session_system_time = sessionStorage.getItem('instance_system_time') + session_idle_time = sessionStorage.getItem('instance_idle_time') + + // If we have existing session values, find the difference else just use data values + if (session_user_time + session_system_time + session_idle_time > 0){ + // If difference between data and session values is greater than zero then calculate + if (data.user_time - session_user_time + data.system_time - session_system_time + data.idle_time - session_idle_time > 0) { + percentage = parseFloat(100 * (1 - (data.idle_time - session_idle_time) / (data.user_time - session_user_time + data.system_time - session_system_time + data.idle_time - session_idle_time))).toFixed(2) + } + else { + percentage = parseFloat(0).toFixed(2) + } + } + else { + //If data values are greating than zero then calculate + if (data.user_time + data.system_time + data.idle_time > 0){ + percentage = parseFloat(100 * ( 1 - data.idle_time / (data.user_time + data.system_time + data.idle_time))).toFixed(2) + } + else { + percentage = parseFloat(0).toFixed(2) + } + } + + //Store stats for page reload + sessionStorage.setItem('instance_user_time', data.user_time) + sessionStorage.setItem('instance_system_time', data.system_time) + sessionStorage.setItem('instance_idle_time', data.idle_time) + + //Update CPU progress bar + $('#cpuPercentage').text(percentage) + $('#cpuPercentageBar').css('width', percentage + '%'); + $('#cpuPercentageBar').attr('aria-valuenow', percentage + '%'); + if (percentage > 90.00) + $('#cpuPercentageBar').attr('class', 'progress-bar bg-danger'); + else + $('#cpuPercentageBar').attr('class', 'progress-bar bg-primary'); + }); + + } + // Loads the CPU bar for container or virtual-machine function loadCpuPercentage() { if (instanceType == "container"){ @@ -829,15 +896,9 @@ }) } if (instanceType == "virtual-machine"){ - $.get("../api/instance/get_instance_cpu_percentage?id="+serverId+"&project="+project+"&name="+instance, function (data) { - percentage = parseFloat(data.percentage).toFixed(2); - $('#cpuPercentage').text(percentage) - $('#cpuPercentageBar').css('width', percentage + '%'); - $('#cpuPercentageBar').attr('aria-valuenow', percentage + '%'); - if (percentage > 90.00) - $('#cpuPercentageBar').attr('class', 'progress-bar bg-danger'); - else - $('#cpuPercentageBar').attr('class', 'progress-bar bg-primary'); + $.get("../api/instance/copy_instance_proc_stat?id="+serverId+"&project="+project+"&name="+instance, function (data) { + // Get instance /proc/stat data + setTimeout(() => { getInstanceProcStat(); }, reloadTime / 2); }); } @@ -985,15 +1046,9 @@ } } if (instanceType == 'virtual-machine') { - $.get("../api/instance/get_instance_memory_percentage?id="+serverId+"&project="+project+"&name="+instance, function (data) { - percentage = parseFloat(data.percentage).toFixed(2); - $('#memPercentage').text(percentage) - $('#memPercentageBar').css('width', percentage + '%'); - $('#memPercentageBar').attr('aria-valuenow', percentage + '%'); - if (percentage > 90.00) - $('#memPercentageBar').attr('class', 'progress-bar bg-danger'); - else - $('#memPercentageBar').attr('class', 'progress-bar bg-primary'); + $.get("../api/instance/copy_instance_proc_meminfo?id="+serverId+"&project="+project+"&name="+instance, function (data) { + //Get instance /proc/meminfo + setTimeout(() => { getInstanceProcMeminfo(); }, reloadTime / 2); }); } }); diff --git a/lxconsole/templates/main.html b/lxconsole/templates/main.html index 8d756fb..3243c74 100644 --- a/lxconsole/templates/main.html +++ b/lxconsole/templates/main.html @@ -163,13 +163,13 @@ // Many of the operations will indicate which resource the operation is being perfomed on if (data.metadata.running[i].hasOwnProperty('resources') && data.metadata.running[i].resources != null) { if (data.metadata.running[i].resources.hasOwnProperty('instances') && data.metadata.running[i].resources.instances != null) { - notification += " " + data.metadata.running[i].resources.instances[i].replace("/1.0/instances/","") + notification += " " + data.metadata.running[i].resources.instances[0].replace("/1.0/instances/","") } else if (data.metadata.running[i].resources.hasOwnProperty('containers') && data.metadata.running[i].resources.containers != null) { - notification += " " + data.metadata.running[i].resources.containers[i].replace("/1.0/containers/","") + notification += " " + data.metadata.running[i].resources.containers[0].replace("/1.0/containers/","") } else if (data.metadata.running[i].resources.hasOwnProperty('virtual-machines') && data.metadata.running[i].resources.virtual-machines != null) { - notification += " " + data.metadata.running[i].resources.virtual-machines[i].replace("/1.0/virtual-machines/","") + notification += " " + data.metadata.running[i].resources.virtual-machines[0].replace("/1.0/virtual-machines/","") } } diff --git a/lxconsole/templates/server.html b/lxconsole/templates/server.html index ffd26df..e2c3e23 100644 --- a/lxconsole/templates/server.html +++ b/lxconsole/templates/server.html @@ -367,7 +367,7 @@ $("#totalMemory").text(memory + ' ' + unit + ' MEMORY'); $('#memoryPercentageProgressBar').css('width', memoryPercentage + '%'); $('#memoryPercentageProgressBar').attr('aria-valuenow', memoryPercentage + '%'); - if (memory > 90.00) + if (memoryPercentage > 90) $('#memoryPercentageProgressBar').attr('class', 'progress-bar bg-danger'); else $('#memoryPercentageProgressBar').attr('class', 'progress-bar bg-primary');