From 6f83043c31cde701499cc0af78a881d458a94f88 Mon Sep 17 00:00:00 2001 From: Alexis Koralewski Date: Fri, 13 Jan 2023 14:01:17 +0100 Subject: [PATCH] Add websocket for agent_cmd & agent_survey models & views --- CHANGELOG | 3 +++ VERSION | 2 +- docker/docker-compose.yml | 13 +++++++++++++ install/requirements_dev.txt | 11 +++++++++++ src/core/pyros_django/api/consumers.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/core/pyros_django/api/functions.py | 24 ++++++++++++++++++++++++ src/core/pyros_django/api/routing.py | 10 ++++++++++ src/core/pyros_django/api/views.py | 2 +- src/core/pyros_django/common/models.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------- src/core/pyros_django/dashboard/templates/dashboard/agents_state.html | 22 ++++++++++++++++++---- src/core/pyros_django/dashboard/views.py | 33 +++++++++++++++++++-------------- src/core/pyros_django/pyros/asgi.py | 25 +++++++++++++++++++++++++ src/core/pyros_django/pyros/settings.py | 35 ++++++++++++++++++++++++++++++++++- 14 files changed, 457 insertions(+), 92 deletions(-) create mode 100644 src/core/pyros_django/api/consumers.py create mode 100644 src/core/pyros_django/api/functions.py create mode 100644 src/core/pyros_django/api/routing.py create mode 100644 src/core/pyros_django/pyros/asgi.py diff --git a/CHANGELOG b/CHANGELOG index 38c3803..504ab71 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +13-01-2023 (AKo): v0.6.16.0 + - Add websocket for agent_cmd & agent_survey models & views + 03-01-2023 (AKo): v0.6.15.4 - new usage of delimiter in cmd parsing in agent_detail view diff --git a/VERSION b/VERSION index 3ac49ad..dd7e6f3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.15.4 \ No newline at end of file +0.6.16.0 \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index fc84ab0..9fdc961 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -25,6 +25,16 @@ services: interval: 5s retries: 20 + redis: + container_name: redis + image: redis:latest + restart: always + ports: + - "6379:6379" + networks: + pyros-network: + ipv4_address: 172.19.0.5 + # service image of python, that let users to interact with python scripts such as pyros. pyros: # path to the Dockerfile of this image @@ -61,6 +71,7 @@ services: # starting db service before install service depends_on: - db + - redis # create network to allow images to communicate with other images within the same network networks: pyros-network: @@ -85,6 +96,8 @@ services: ports: - "${PHPMYADMIN_PORT:-8081}:80" + + # declaring volumes volumes: db: diff --git a/install/requirements_dev.txt b/install/requirements_dev.txt index 0449fce..1c4deb1 100644 --- a/install/requirements_dev.txt +++ b/install/requirements_dev.txt @@ -22,3 +22,14 @@ GitPython==3.1.24 # For working with date python-dateutil==2.8.2 +# For MCO CNES + +astropy +photutils +scipy + +# for websocket +channels +daphne +attrs==22.1.0 +channels_redis==3.4.1 \ No newline at end of file diff --git a/src/core/pyros_django/api/consumers.py b/src/core/pyros_django/api/consumers.py new file mode 100644 index 0000000..bb434a6 --- /dev/null +++ b/src/core/pyros_django/api/consumers.py @@ -0,0 +1,133 @@ +# consumers.py +from django.contrib.auth.models import User +from .serializers import AgentCmdSerializer, AgentSurveySerializer +from common.models import AgentCmd, AgentSurvey +from asgiref.sync import async_to_sync +import json +from channels.db import database_sync_to_async +from .functions import get_agent_survey_instance, get_list_agent_cmd + +from channels.generic.websocket import AsyncWebsocketConsumer + + +class AgentCmdConsumer(AsyncWebsocketConsumer): + async def connect(self): + self.agent_name = self.scope["url_route"]["kwargs"]["agent_name"] + # Join room group + await self.channel_layer.group_add(f'agentcmd_{self.agent_name}_observers', self.channel_name) + await self.accept() + # try: + # agent_cmds = await database_sync_to_async(get_list_agent_cmd)(self.agent_name) + # except Exception as e: + # print(e) + await self.channel_layer.group_send( + f'agentcmd_{self.agent_name}_observers', {"type": "send_agentcmd_instance", "data": None,"action":"list"} + ) + + def get_agentcmd(self,id): + return AgentCmdSerializer(AgentCmd.objects.get(id=id)).data + + # async def disconnect(self, close_code): + # # Leave room group + # await self.channel_layer.group_discard(f'agentcmd_{self.agent_name}_observers', self.channel_name) + # await self.close(close_code) + + # Receive message from WebSocket + async def receive(self, text_data): + text_data_json = json.loads(text_data) + message = text_data_json["message"] + + # Send message to room group + await self.channel_layer.group_send( + f'agentcmd_{self.agent_name}_observers', {"type": "new_agentcmd_instance", "agent_name": self.agent_name,"action":"create"} + ) + + # ON Receive, send data to observers + async def send_agentcmd_instance(self, event): + data = event.get("data",None) + action = event["action"] + if action == "list": + data = await database_sync_to_async(get_list_agent_cmd)(self.agent_name) + elif action == "create" or action == "update": + id = data + data = await database_sync_to_async(self.get_agentcmd)(id) + # Send message to WebSocket + await self.send(text_data=json.dumps({"data": data,"action":action})) + + + +class AgentSurveyDetailConsumer(AsyncWebsocketConsumer): + async def connect(self): + self.agent_name = self.scope["url_route"]["kwargs"].get("agent_name") + print(self.agent_name) + # Join room group + await self.channel_layer.group_add(f'agentsurvey_{self.agent_name}_observers', self.channel_name) + await self.accept() + # try: + # agent_survey = await database_sync_to_async(self.get_agent_survey_instance)() + # except Exception as e: + # print("exception websocket : ",e) + await self.channel_layer.group_send( + f'agentsurvey_{self.agent_name}_observers', {"type": "send_agentsurvey_instance", "data": None} + ) + + def get_agent_survey_instance(self): + agent_survey = AgentSurvey.objects.get(name=self.agent_name) + return AgentSurveySerializer(agent_survey).data + + # async def disconnect(self, close_code): + # # Leave room group + # await self.channel_layer.group_discard(f'agentsurvey_{self.agent_name}_observers', self.channel_name) + # await self.close(close_code) + + # Receive message from WebSocket + async def receive(self, text_data): + text_data_json = json.loads(text_data) + message = text_data_json["message"] + + # Send message to room group + await self.channel_layer.group_send( + f'agentsurvey_{self.agent_name}_observers', {"type": "new_agentsurvey_instance", "agent_name": self.agent_name,"action":"create"} + ) + + # Receive message from room group + async def send_agentsurvey_instance(self, event): + data = await database_sync_to_async(self.get_agent_survey_instance)() + # Send message to WebSocket + await self.send(text_data=json.dumps({"data": data})) + + +class AgentSurveyConsumer(AsyncWebsocketConsumer): + async def connect(self): + # Join room group + await self.channel_layer.group_add(f'agentsurvey_observers', self.channel_name) + await self.accept() + # try: + # agent_survey = await database_sync_to_async(get_agent_survey_instance)() + # except Exception as e: + # print(e) + await self.channel_layer.group_send( + f'agentsurvey_observers', {"type": "send_agentsurvey_instance", "data": None} + ) + + + # async def disconnect(self, close_code): + # # Leave room group + # await self.channel_layer.group_discard(f'agentsurvey_observers', self.channel_name) + # await self.close(close_code) + + # Receive message from WebSocket + async def receive(self, text_data): + text_data_json = json.loads(text_data) + message = text_data_json["message"] + + # Send message to room group + await self.channel_layer.group_send( + f'agentsurvey_observers', {"type": "new_agentsurvey_instance", "agent_name": self.agent_name,"action":"create"} + ) + + # Receive message from room group + async def send_agentsurvey_instance(self, event): + data = await database_sync_to_async(get_agent_survey_instance)() + # Send message to WebSocket + await self.send(text_data=json.dumps({"data": data})) diff --git a/src/core/pyros_django/api/functions.py b/src/core/pyros_django/api/functions.py new file mode 100644 index 0000000..ee87a61 --- /dev/null +++ b/src/core/pyros_django/api/functions.py @@ -0,0 +1,24 @@ +from .serializers import AgentCmdSerializer, AgentSurveySerializer +from common.models import AgentCmd, AgentSurvey +from datetime import datetime, timezone, timedelta + + +def get_list_agent_cmd(agent_name): + commands_sent_by_agent = AgentCmd.get_commands_sent_by_agent(agent_name) + commands_recivied_by_agent = AgentCmd.get_commands_sent_to_agent(agent_name) + number = 20 + agent_cmds = commands_sent_by_agent | commands_recivied_by_agent + agent_cmds = agent_cmds.exclude(full_name="get_specific_cmds") + agent_cmds = agent_cmds.exclude(full_name="get_all_cmds") + agent_cmds = agent_cmds.order_by("-s_deposit_time") + agent_cmds = agent_cmds[:number] + return AgentCmdSerializer(agent_cmds,many=True).data + + +def get_agent_survey_instance(): + agents = AgentSurvey.objects.all() + datetime_now = datetime.utcnow() + date_minus_two_days = datetime_now - timedelta(days=2) + date_minus_two_days = date_minus_two_days.replace(tzinfo=timezone.utc) + agents = agents.exclude(updated__lt=date_minus_two_days) + return AgentSurveySerializer(agents,many=True).data \ No newline at end of file diff --git a/src/core/pyros_django/api/routing.py b/src/core/pyros_django/api/routing.py new file mode 100644 index 0000000..27b1dcd --- /dev/null +++ b/src/core/pyros_django/api/routing.py @@ -0,0 +1,10 @@ +from django.urls import path, re_path +from .views import AgentCmdViewSet +from .consumers import AgentCmdConsumer, AgentSurveyConsumer, AgentSurveyDetailConsumer + + +websocket_urlpatterns = [ + re_path(r"^ws/agent_cmd/(?P\w+)/$", AgentCmdConsumer.as_asgi()), + re_path(r"^ws/agent_survey/(?P\w+)/$", AgentSurveyDetailConsumer.as_asgi()), + re_path(r"^ws/agent_survey/", AgentSurveyConsumer.as_asgi()), +] \ No newline at end of file diff --git a/src/core/pyros_django/api/views.py b/src/core/pyros_django/api/views.py index 875d701..bbc3608 100644 --- a/src/core/pyros_django/api/views.py +++ b/src/core/pyros_django/api/views.py @@ -294,7 +294,7 @@ class AgentCmdViewSet(viewsets.ModelViewSet): http_method_names = ["get"] def get_queryset(self): agent_name = self.request.query_params.get('agent_name') - number = self.request.query_params.get('number') + number = self.request.query_params.get('number',20) if agent_name is None: if "agent_name" in self.kwargs: agent_name = self.kwargs["agent_name"] diff --git a/src/core/pyros_django/common/models.py b/src/core/pyros_django/common/models.py index bf85230..f5c36e3 100644 --- a/src/core/pyros_django/common/models.py +++ b/src/core/pyros_django/common/models.py @@ -25,6 +25,10 @@ from django.db.models.expressions import F from django.db.models.query import QuerySet from model_utils import Choices from django.utils import timezone +from asgiref.sync import async_to_sync +from channels.layers import get_channel_layer +from django.db.models.signals import post_save +from django.dispatch import receiver # Project imports from user_manager.models import PyrosUser # DeviceCommand is used by class Command @@ -1143,6 +1147,74 @@ class AgentCmd(models.Model): # Optimization, but does not work, why ?... # self.save(update_fields=["state"]) + # override save to use websocket + # def save(self, *args, **kwargs): + # super().save(*args, **kwargs) + # try: + # if self.pk == None: + # action = "create" + # else: + # action = "update" + # agent_survey = AgentSurvey.objects.all().values_list("name",flat=True) + # # initialize value, there is always a sender + # agent_name = self.sender + # if self.sender in agent_survey: + # agent_name = self.sender + # # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + # if self.recipient in agent_survey: + # agent_name = self.recipient + # # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + # async_to_sync(get_channel_layer().group_send)( + # f'agentcmd_{agent_name}_observers', {"type": f"send_agentcmd_instance","data":self.id,"action":action} + # ) + # except Exception as e: + # print(e) + +# signals decorator to trigger function after Model AgentCmd call save() +@receiver(post_save, sender=AgentCmd) +def send_agentcmd_to_websocket(sender, instance, created, **kwargs): + agent_survey = AgentSurvey.objects.all().values_list("name",flat=True) + if instance.sender in agent_survey: + agent_name = instance.sender + # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + async_to_sync(get_channel_layer().group_send)( + f'agentcmd_{agent_name}_observers', {"type": f"send_agentcmd_instance","action":"list"} + ) + elif instance.recipient in agent_survey: + agent_name = instance.recipient + # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + async_to_sync(get_channel_layer().group_send)( + f'agentcmd_{agent_name}_observers', {"type": f"send_agentcmd_instance","action":"list"} + ) + # if created: + # action = "create" + # else: + # action = "update" + # agent_survey = AgentSurvey.objects.all().values_list("name",flat=True) + # # initialize value, there is always a sender + # agent_name = instance.sender + # if instance.sender in agent_survey: + # agent_name = selinstancef.sender + # # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + # if instance.recipient in agent_survey: + # agent_name = instance.recipient + # # send to agentcmd_observers group a message to execute function new_agentcmd_agent_name_instance + # async_to_sync(get_channel_layer().group_send)( + # f'agentcmd_{agent_name}_observers', {"type": f"send_agentcmd_instance","data":instance.id,"action":action} + # ) + + +@receiver(post_save, sender=AgentSurvey) +def send_agentsurvey_to_websocket(sender, instance, created, **kwargs): + agent_name = instance.name + # send to agentsurvey_observers group a message to execute function new_agentcmd_agent_name_instance + async_to_sync(get_channel_layer().group_send)( + f'agentsurvey_{agent_name}_observers', {"type": f"send_agentsurvey_instance"} + ) + async_to_sync(get_channel_layer().group_send)( + f'agentsurvey_observers', {"type": f"send_agentsurvey_instance"} + ) + # TODO: OLD Config : à virer (mais utilisé dans dashboard/templatetags/tags.py) class Config(models.Model): diff --git a/src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html b/src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html index 25f4042..ad1c0ab 100644 --- a/src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html +++ b/src/core/pyros_django/dashboard/templates/dashboard/agent_detail.html @@ -113,7 +113,7 @@ {% endfor %} {% endcomment %}
{% comment %} {% endcomment %} - + {% comment %} {% else %} {% endcomment %} @@ -138,8 +138,11 @@ Agent + {% if agentsst_computers %} + Computer + {% endif %} Mode - status + status {% if CAN_SEND_COMMAND %} Actions {% endif %} @@ -150,6 +153,9 @@ {{ agent }} {% comment %} {{ status_of_agents|get_item:agent }} {% endcomment %} + {% if agentsst_computers %} + {{agentsst_computers|get_item:agent}} + {% endif %} {% if CAN_SEND_COMMAND %} @@ -463,51 +469,47 @@ get_parameters_of_selected_cmd(); }); - function get_agents_status(){ - get_agent_status(); + function update_agents_status(){ $('.agent_status').each(function(index){ let agent_name = document.getElementsByClassName("agent_status")[index].parentElement.children[0].textContent; agent_name = agent_name.trim(); let agent_status = $(this); let agent_mode = $(this).prev(); - fetch("/api/agent_survey/"+agent_name+"?format=json").then(response => response.json()).then(function(data){ - agent_status.text(data.status); - agent_mode.text(data.mode); - if (main_agent_status == "EXITING"){ - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = true; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#stopbtn").disabled = true; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restartbtn").disabled = true; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restarthardbtn").disabled = true; - } - else{ - if (data.status == "EXITING"){ - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = false; + + if (agent_status != null){ + + var ws = new WebSocket(`ws://${window.location.host}/ws/agent_survey/${agent_name}/`) + ws.onmessage = function (e) { + allData = JSON.parse(e.data); + data = allData.data; + agent_status.text(data.status); + agent_mode.text(data.mode); + if (main_agent_status == "EXITING"){ + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = true; document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#stopbtn").disabled = true; document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restartbtn").disabled = true; document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restarthardbtn").disabled = true; } else{ - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = true; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#stopbtn").disabled = false; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restartbtn").disabled = false; - document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restarthardbtn").disabled = false; + if (data.status == "EXITING"){ + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = false; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#stopbtn").disabled = true; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restartbtn").disabled = true; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restarthardbtn").disabled = true; + } + else{ + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#startbtn").disabled = true; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#stopbtn").disabled = false; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restartbtn").disabled = false; + document.getElementsByClassName("agent_status")[index].parentElement.children[3].querySelector("#restarthardbtn").disabled = false; + } + } - } - }); - + } }); } - get_agents_status(); - setInterval(get_agents_status,4000); - var main_agent_status; - var previous_status = "none"; - async function get_agent_status(){ - await fetch("/api/agent_survey/{{agent_name}}?format=json").then(response => response.json()).then(function(data){ - main_agent_status = data.status; - }); - - } + function refresh_agent_info(){ fetch("/api/agent_survey/{{agent_name}}?format=json").then(response => response.json()).then(function(data){ @@ -533,46 +535,64 @@ } function websocket_agentcmd(){ var ws = new WebSocket(`ws://${window.location.host}/ws/agent_cmd/{{agent_name}}/`) - //var ws = new WebSocket(`ws://${window.location.host}/ws/test/{{agent_name}}/`) - /*ws.onopen = function(){ - ws.send(JSON.stringify({ - action: "list", - request_id: new Date().getTime(), - })) - }*/ - /* - ws.onopen = function(){ - ws.send(JSON.stringify({ - action: "subscribe_to_agent_cmd_activity", - request_id: new Date().getTime(), - })) - } - ws.onmessage = function(e){ - console.log(e) - app.commands = e.data - }*/ - ws.onopen = function (e) { - ws.send( - JSON.stringify({ - action: "list", - request_id: new Date().getTime(), - }) - ); - console.log(ws) - }; - + last_id = null; ws.onmessage = function (e) { allData = JSON.parse(e.data); if (allData.action === "list") { app.commands = allData.data; - app.$forceUpdate(); - } else if (allData.action === "create") { - app.commands.push(allData.data); + } + /* + // OLD (tried to optimize) + else if (allData.action == "create") { + console.log("last id"); + console.log(last_id) + console.log("current id"); + console.log(allData.data.id) + app.commands.unshift(allData.data); + last_id = allData.data.id } + else if (allData.action == "update"){ + if (last_id == allData.data.id){ + console.log("last id"); + console.log(last_id) + console.log("current id"); + console.log(allData.data.id) + app.commands.shift(); + app.commands.unshift(allData.data); + last_id = allData.data.id + } + } + */ + app.$forceUpdate(); }; - ws.onclose = function(e) { + ws.onerror = function(error) { let now = new Date(); - console.error(now.toLocaleString() + ':' + now.getMilliseconds() + ' – Analysis socket closed with event code = ' + e.code + ' and reason=' + e.reason); + let msg = now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket error: ' + error; + console.error(msg); + } + } + var previous_status = "none"; + function websocket_agentsurvey(){ + var ws = new WebSocket(`ws://${window.location.host}/ws/agent_survey/{{agent_name}}/`) + last_id = null; + ws.onmessage = function (e) { + allData = JSON.parse(e.data); + app2.agent = allData.data; + data = allData.data + main_agent_status = data.status + if (data.status == "EXITING"){ + $("#agent_title").text(data.name + " (OFF)").css("color","red") + } + else{ + $("#agent_title").text(data.name + " (ON)").css("color","green") + } + + if (previous_status != data.status){ + display_form_cmd(data.status); + previous_status = data.status; + } + + app2.$forceUpdate(); }; ws.onerror = function(error) { let now = new Date(); @@ -597,12 +617,14 @@ }); $(document).ready(function() { - refresh_agent_info(); + //refresh_agent_info(); //setInterval(refresh_command_column,5000); - setInterval(refresh_agent_info,4000); - refresh_cmds_table(); - setInterval(refresh_cmds_table,10000); - //websocket_agentcmd(); + //setInterval(refresh_agent_info,4000); + //refresh_cmds_table(); + //setInterval(refresh_cmds_table,10000); + websocket_agentcmd(); + websocket_agentsurvey(); + update_agents_status(); $("cmdform").hide(); $("cmdform_exiting").hide(); }); diff --git a/src/core/pyros_django/dashboard/templates/dashboard/agents_state.html b/src/core/pyros_django/dashboard/templates/dashboard/agents_state.html index 8d6566a..f27cb29 100644 --- a/src/core/pyros_django/dashboard/templates/dashboard/agents_state.html +++ b/src/core/pyros_django/dashboard/templates/dashboard/agents_state.html @@ -33,7 +33,7 @@ Agent Mode - Status + Status Udpated Current command Nb restart attempts @@ -95,11 +95,25 @@ app.agents = data; }); } + function websocket_agentsurvey(){ + var ws = new WebSocket(`ws://${window.location.host}/ws/agent_survey/`) + ws.onmessage = function (e) { + allData = JSON.parse(e.data); + app.agents = allData.data; + + app.$forceUpdate(); + }; + ws.onerror = function(error) { + let now = new Date(); + let msg = now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket error: ' + error; + console.error(msg); + } + } $(document).ready(function() { - - refresh_status_table(); + websocket_agentsurvey(); + //refresh_status_table(); //setInterval(refresh_command_column,5000); - setInterval(refresh_status_table,4000); + //setInterval(refresh_status_table,4000); }); var app = new Vue({ delimiters:["[[","]]"], diff --git a/src/core/pyros_django/dashboard/views.py b/src/core/pyros_django/dashboard/views.py index 9065edb..237be62 100644 --- a/src/core/pyros_django/dashboard/views.py +++ b/src/core/pyros_django/dashboard/views.py @@ -218,8 +218,13 @@ def get_last_all_cmds(agent_name): else: # AgentSST doesn't have do_stop cmd... (for the moment) last_agent_all_cmds = AgentCmd.send_cmd_from_to("System",agent_name,"get_all_cmds") + max_wait_time = 3 + current_wait_time = 0 while not AgentCmd.objects.get(id=last_agent_all_cmds.id).is_executed() and not AgentCmd.objects.get(id=last_agent_all_cmds.id).is_exec_error(): time.sleep(0.5) + current_wait_time+=0.5 + if max_wait_time <= current_wait_time: + break return AgentCmd.objects.get(id=last_agent_all_cmds.id) @login_required @@ -309,14 +314,14 @@ def agent_detail(request, agent_name): return JsonResponse({"agent_general_commands":agent_general_commands,"specific_cmd_with_args":specific_cmd_with_args,"unimplemented_command":unimplemented_command,"cmd_with_choices":cmd_with_choices,"cmds_description":cmds_description},safe=False) if request.GET.get("cmd"): return JsonResponse(None,safe=False) - commands_sent_by_agent = AgentCmd.get_commands_sent_by_agent(agent_name) - commands_recivied_by_agent = AgentCmd.get_commands_sent_to_agent(agent_name) - agent_cmds = commands_sent_by_agent | commands_recivied_by_agent - agent_cmds = agent_cmds.exclude(full_name="get_specific_cmds") - agent_cmds = agent_cmds.exclude(full_name="get_all_cmds") - agent_cmds = agent_cmds.order_by("-s_deposit_time") - paginator = Paginator(agent_cmds, pyros_settings.NB_ELEMENT_PER_PAGE) - page_number = request.GET.get("page",1) + # commands_sent_by_agent = AgentCmd.get_commands_sent_by_agent(agent_name) + # commands_recivied_by_agent = AgentCmd.get_commands_sent_to_agent(agent_name) + # agent_cmds = commands_sent_by_agent | commands_recivied_by_agent + # agent_cmds = agent_cmds.exclude(full_name="get_specific_cmds") + # agent_cmds = agent_cmds.exclude(full_name="get_all_cmds") + # agent_cmds = agent_cmds.order_by("-s_deposit_time") + # paginator = Paginator(agent_cmds, pyros_settings.NB_ELEMENT_PER_PAGE) + # page_number = request.GET.get("page",1) config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) managed_agents = None agents_status = None @@ -349,12 +354,12 @@ def agent_detail(request, agent_name): break obj, created = Majordome.objects.get_or_create(id=1) CAN_SEND_COMMAND = obj.soft_mode == Majordome.MANUAL_MODE - try: - commands = paginator.page(page_number) - except PageNotAnInteger: - commands = paginator.page(1) - except EmptyPage: - commands = paginator.page(paginator.num_pages) + # try: + # commands = paginator.page(page_number) + # except PageNotAnInteger: + # commands = paginator.page(1) + # except EmptyPage: + # commands = paginator.page(paginator.num_pages) return render(request, "dashboard/agent_detail.html", locals()) @login_required diff --git a/src/core/pyros_django/pyros/asgi.py b/src/core/pyros_django/pyros/asgi.py new file mode 100644 index 0000000..492db9b --- /dev/null +++ b/src/core/pyros_django/pyros/asgi.py @@ -0,0 +1,25 @@ +import os + +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.security.websocket import AllowedHostsOriginValidator +from django.core.asgi import get_asgi_application +# from django.urls import path + + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pyros.settings") +# Initialize Django ASGI application early to ensure the AppRegistry +# is populated before importing code that may import ORM models. +from api.routing import websocket_urlpatterns +django_asgi_app = get_asgi_application() + +application = ProtocolTypeRouter({ + # Django's ASGI application to handle traditional HTTP requests + "http": django_asgi_app, + + "websocket": AllowedHostsOriginValidator( + AuthMiddlewareStack( + URLRouter(websocket_urlpatterns) + ) + ), +}) diff --git a/src/core/pyros_django/pyros/settings.py b/src/core/pyros_django/pyros/settings.py index 36b6f94..dea4b18 100644 --- a/src/core/pyros_django/pyros/settings.py +++ b/src/core/pyros_django/pyros/settings.py @@ -191,6 +191,10 @@ if not os.path.isfile(PATH_TO_OBSCONF_FILE): # Application definition INSTALLED_APPS = [ + # websocket server (asgi server) + 'daphne', + # websocket for django + 'channels', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -230,6 +234,17 @@ INSTALLED_APPS = [ #'kombu.transport.django' ] +CHANNEL_LAYERS = { + "default": { + + "BACKEND": "channels_redis.core.RedisChannelLayer", + "CONFIG": { + #"hosts": [("127.0.0.1", 6379)], + "hosts": [("172.19.0.5", 6379)], + }, + }, +} + REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', @@ -270,6 +285,7 @@ TEMPLATES = [ ] WSGI_APPLICATION = 'pyros.wsgi.application' +ASGI_APPLICATION = "pyros.asgi.application" FIXTURE_DIRS = ( 'misc/fixtures/', @@ -294,7 +310,7 @@ so Django highly recommends activating a strict mode for MySQL to prevent data l mysql_options = { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'" } from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig import socket -obsconfig = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"]) +obsconfig = OBSConfig(os.environ.get("PATH_TO_OBSCONF_FILE",os.path.join(BASE_DIR,"../../../privatedev/config/default/observatory_default.yml"))) database_computer = obsconfig.get_database_for_unit(obsconfig.unit_name)["computer"] database_file = obsconfig.get_database_environment(obsconfig.unit_name) current_computer = socket.gethostname() @@ -424,6 +440,23 @@ STATICFILES_DIRS = ( # Used for deployment (DEBUG = False). Need to run "python manage.py collectstatic" to fill it. STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'public', 'static') +# LOGGING = { +# 'version': 1, +# 'disable_existing_loggers': False, +# 'handlers': { +# 'file': { +# 'level': 'DEBUG', +# 'class': 'logging.FileHandler', +# 'filename': '../../../djangodebug.log', +# }, +# }, +# 'loggers': { +# 'daphne': { +# 'handlers': ['file'], +# 'level': 'DEBUG', +# }, +# }, +# } # EP added if not DEBUG: -- libgit2 0.21.2