diff --git a/salt_observer/management/commands/fetchlisteningservices.py b/salt_observer/management/commands/fetchlisteningservices.py new file mode 100644 index 0000000000000000000000000000000000000000..c44e50fa9f73441adb072bb49b2399963deac100 --- /dev/null +++ b/salt_observer/management/commands/fetchlisteningservices.py @@ -0,0 +1,30 @@ +from django.core.management.base import BaseCommand + +from salt_observer.models import Minion +from . import ApiCommand + + +class Command(ApiCommand, BaseCommand): + help = 'Fetch and save listening network services' + + def save_services(self, api): + listening_services = api.get('network.netstat') + + for minion_fqdn, services in listening_services.items(): + minion = Minion.objects.filter(fqdn=minion_fqdn).first() + + if not minion: + continue + + minion_services = [] + for service in services: + if service.get('state', '') == 'LISTEN': + minion_services.append(service) + + minion.update_data({'listening_services': minion_services}) + minion.save() + + def handle(self, *args, **kwargs): + api = super().handle(*args, **kwargs) + self.save_services(api) + api.logout() diff --git a/salt_observer/templates/minion/detail.html b/salt_observer/templates/minion/detail.html index 9577fabe1651173ce38d1f3b1dd705c601a6b4e3..9a518061d32248fc79a2fbd5faa6dd34fa362018 100644 --- a/salt_observer/templates/minion/detail.html +++ b/salt_observer/templates/minion/detail.html @@ -45,6 +45,7 @@ <li role="presentation"><a {% if minion.outdated_package_count %}class="text-danger"{% endif %} href="#packages" aria-controls="packages" role="tab" data-toggle="tab">Packages</a></li> <li role="presentation"><a href="#users" aria-controls="users" role="tab" data-toggle="tab">Users</a></li> <li role="presentation"><a href="#partitions" aria-controls="partitions" role="tab" data-toggle="tab">Partitions</a></li> + <li role="presentation"><a href="#netstat" aria-controls="netstat" role="tab" data-toggle="tab">Netstat</a></li> </ul> <!-- tab panes --> @@ -240,9 +241,9 @@ {% for network in minion.networkinterface_set.all %} <tr> <td>{{ network.name }}</td> - <td>{{ network.mac_address }}</td> - <td>{{ network.network.ipv4 }}</td> - <td><a href="{% url 'network-detail' network.network.ipv4 %}">{{ network.network.mask }}</a></td> + <td><samp>{{ network.mac_address }}</samp></td> + <td><samp>{{ network.network.ipv4 }}</samp></td> + <td><samp><a href="{% url 'network-detail' network.network.ipv4 %}">{{ network.network.mask }}</a></samp></td> </tr> {% endfor %} </tbody> @@ -319,8 +320,8 @@ <tr data-uid="{{ user.id }}"> <td>{{ user.id }}</td> <td>{{ user.name }}</td> - <td><code>{{ user.home }}</code></td> - <td><code>{{ user.shell }}</code></td> + <td><samp>{{ user.home }}</samp></td> + <td><samp>{{ user.shell }}</samp></td> </tr> {% endfor %} </tbody> @@ -353,6 +354,32 @@ </table> </div> + <!-- netstat --> + <div role="tabpanel" class="tab-pane fade" id="netstat"> + <table class="table sortable"> + <thead> + <tr> + <th>User id</th> + <th>Remote address</th> + <th>Local address</th> + <th>Program</th> + <th>Protocol</th> + </tr> + </thead> + <tbody> + {% for service in minion.data.listening_services %} + <tr> + <td>{{ service.user }}</td> + <td><samp>{{ service|get:"remote-address" }}</samp></td> + <td><samp>{{ service|get:"local-address" }}</samp></td> + <td><samp>{{ service.program }}</samp></td> + <td>{{ service.proto }}</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> <!-- tab panes end --> </div>