add live process manager

This commit is contained in:
wh1te909 2020-01-12 10:50:26 +00:00
parent 822ea426b2
commit f83baaa121
6 changed files with 293 additions and 3 deletions

View File

@ -0,0 +1,33 @@
from __future__ import absolute_import
import psutil
from time import sleep
import random
import string
def get_procs():
ret = []
# setup
for proc in psutil.process_iter():
with proc.oneshot():
proc.cpu_percent(interval=None)
# need time for psutil to record cpu percent
sleep(1)
for proc in psutil.process_iter():
x = {}
with proc.oneshot():
x['name'] = proc.name()
x['cpu_percent'] = proc.cpu_percent(interval=None) / psutil.cpu_count()
x['memory_percent'] = proc.memory_percent()
x['pid'] = proc.pid
x['ppid'] = proc.ppid()
x['status'] = proc.status()
x['username'] = proc.username()
x['id'] = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(10)])
if proc.pid != 0:
ret.append(x)
return ret

View File

@ -16,4 +16,6 @@ urlpatterns = [
path("<pk>/geteventlog/<logtype>/<days>/", views.get_event_log),
path("getagentversions/", views.get_agent_versions),
path("updateagents/", views.update_agents),
path("<pk>/getprocs/", views.get_processes),
path("<pk>/<pid>/killproc/", views.kill_proc),
]

View File

@ -194,6 +194,42 @@ def agent_detail(request, pk):
return Response(AgentSerializer(agent).data)
@api_view()
def get_processes(request, pk):
agent = get_object_or_404(Agent, pk=pk)
try:
resp = agent.salt_api_cmd(
hostname=agent.salt_id,
timeout=70,
func="process_manager.get_procs"
)
data = resp.json()
except Exception:
return Response(
{"error": "unable to contact the agent"}, status=status.HTTP_400_BAD_REQUEST
)
return Response(data["return"][0][agent.salt_id])
@api_view()
def kill_proc(request, pk, pid):
agent = get_object_or_404(Agent, pk=pk)
resp = agent.salt_api_cmd(
hostname=agent.salt_id,
timeout=60,
func="ps.kill_pid",
arg=int(pid)
)
data = resp.json()
if not data["return"][0][agent.salt_id]:
return Response(
{"error": "Unable to kill the process"}, status=status.HTTP_400_BAD_REQUEST
)
else:
return Response("ok")
@api_view()
def get_event_log(request, pk, logtype, days):
agent = get_object_or_404(Agent, pk=pk)

View File

@ -1,6 +1,6 @@
{
"name": "web",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View File

@ -0,0 +1,213 @@
<template>
<div class="q-pa-md">
<q-table
dense
class="procs-sticky-header-table"
:data="procs"
:columns="columns"
:pagination.sync="pagination"
:filter="filter"
row-key="id"
binary-state-sort
hide-bottom
>
<template v-slot:top>
<q-btn v-if="poll" dense flat push @click="stopPoll" icon="stop" label="Stop Live Refresh" />
<q-btn
v-else
dense
flat
push
@click="startPoll"
icon="play_arrow"
label="Resume Live Refresh"
/>
<q-space />
<q-input v-model="filter" outlined label="Search" dense clearable>
<template v-slot:prepend>
<q-icon name="search" />
</template>
</q-input>
</template>
<template slot="body" slot-scope="props" :props="props">
<q-tr :props="props">
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="killProc(props.row.pid, props.row.name)">
<q-item-section thumbnail>
<q-icon name="fas fa-trash-alt" size="xs" />
</q-item-section>
<q-item-section>End task</q-item-section>
</q-item>
</q-list>
</q-menu>
<q-td>{{ props.row.name }}</q-td>
<q-td>{{ Math.ceil(props.row.cpu_percent) }}%</q-td>
<q-td>{{ convert(props.row.memory_percent) }} MB</q-td>
<q-td>{{ props.row.username }}</q-td>
<q-td>{{ props.row.pid }}</q-td>
<q-td>{{ props.row.status }}</q-td>
</q-tr>
</template>
</q-table>
</div>
</template>
<script>
import axios from "axios";
import mixins from "@/mixins/mixins";
export default {
name: "ProcessManager",
props: ["pk"],
mixins: [mixins],
data() {
return {
polling: null,
mem: null,
poll: true,
procs: [],
filter: "",
pagination: {
rowsPerPage: 99999,
sortBy: "cpu_percent",
descending: true
},
columns: [
{
name: "name",
label: "Name",
field: "name",
align: "left",
sortable: true
},
{
name: "cpu_percent",
label: "CPU",
field: "cpu_percent",
align: "left",
sortable: true
},
{
name: "memory_percent",
label: "Memory",
field: "memory_percent",
align: "left",
sortable: true
},
{
name: "username",
label: "User",
field: "username",
align: "left",
sortable: true
},
{
name: "pid",
label: "PID",
field: "pid",
align: "left",
sortable: true
},
{
name: "status",
label: "Status",
field: "status",
align: "left",
sortable: true
}
]
};
},
methods: {
getProcesses() {
this.procs = [];
this.$q.loading.show({ message: "Loading Processes..." });
axios
.get(`/agents/${this.pk}/getprocs/`)
.then(r => {
this.procs = r.data;
this.$q.loading.hide();
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data.error);
});
},
refreshProcs() {
this.polling = setInterval(() => {
axios
.get(`/agents/${this.pk}/getprocs/`)
.then(r => (this.procs = r.data))
.catch(() => this.notifyError("Unable to contact the agent"));
}, 10000);
},
killProc(pid, name) {
this.$q.loading.show({ message: `Attempting to kill process ${name}` });
axios
.get(`/agents/${this.pk}/${pid}/killproc/`)
.then(r => {
this.$q.loading.hide();
this.notifySuccess(`${name} was terminated!`);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data.error);
});
},
stopPoll() {
this.poll = false;
clearInterval(this.polling);
},
startPoll() {
this.poll = true;
axios
.get(`/agents/${this.pk}/getprocs/`)
.then(r => (this.procs = r.data));
this.refreshProcs();
},
getAgent() {
axios
.get(`/agents/${this.pk}/agentdetail/`)
.then(r => (this.mem = r.data.total_ram));
},
convert(percent) {
const mb = this.mem * 1024;
return Math.ceil((percent * mb) / 100).toLocaleString();
}
},
beforeDestroy() {
clearInterval(this.polling);
},
created() {
this.getAgent();
// disable loading bar
this.$q.loadingBar.setDefaults({ size: "0px" });
this.getProcesses();
},
mounted() {
this.refreshProcs();
}
};
</script>
<style lang="stylus">
.procs-sticky-header-table {
/* max height is important */
.q-table__middle {
max-height: 650px;
}
.q-table__top, .q-table__bottom, thead tr:first-child th {
background-color: #f5f4f2;
}
thead tr:first-child th {
position: sticky;
top: 0;
opacity: 1;
z-index: 1;
}
}
</style>

View File

@ -13,7 +13,8 @@
<q-tab name="terminal" icon="fas fa-terminal" label="Terminal" />
<q-tab name="filebrowser" icon="far fa-folder-open" label="File Browser" />
<q-tab name="services" icon="fas fa-cogs" label="Services" />
<q-tab name="eventlog" icon="fas fa-cogs" label="Event Log" />
<q-tab name="processes" icon="fas fa-chart-area" label="Processes" />
<q-tab name="eventlog" icon="fas fa-clipboard-list" label="Event Log" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab">
@ -24,6 +25,9 @@
>
</iframe>
</q-tab-panel>
<q-tab-panel name="processes">
<ProcessManager :pk="pk" />
</q-tab-panel>
<q-tab-panel name="services">
<Services :pk="pk" />
</q-tab-panel>
@ -43,6 +47,7 @@
<script>
import axios from "axios";
import ProcessManager from "@/components/ProcessManager";
import Services from "@/components/Services";
import EventLog from "@/components/EventLog";
@ -50,7 +55,8 @@ export default {
name: "RemoteBackground",
components: {
Services,
EventLog
EventLog,
ProcessManager
},
data() {
return {