Compare commits
	
		
			32 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 0999d98225 | ||
|  | d8dd3e133f | ||
|  | 528470c37f | ||
|  | c03cd53853 | ||
|  | b57fc8a29c | ||
|  | a04ed5c3ca | ||
|  | 3ad1df14f6 | ||
|  | d8caf12fdc | ||
|  | 5ca9d30d5f | ||
|  | a7a71b4a46 | ||
|  | 638603ac6b | ||
|  | 1d70c15027 | ||
|  | 7a5f03d672 | ||
|  | 39e97c5589 | ||
|  | 1943d8367e | ||
|  | f91c5af9a1 | ||
|  | 2be71fc877 | ||
|  | f5f5b4a8db | ||
|  | ac9cfd09ea | ||
|  | 4cfc85dbfd | ||
|  | 1f3d2f47b1 | ||
|  | 653c482ff7 | ||
|  | 4b069cc2b0 | ||
|  | c89349a43a | ||
|  | 6e92d6c62c | ||
|  | 5d3d3e9076 | ||
|  | b440c772d6 | ||
|  | 2895560b30 | ||
|  | bedcecb2e1 | ||
|  | 656ac829a4 | ||
|  | 4d83debc0e | ||
|  | 4ff5d19979 | 
							
								
								
									
										13
									
								
								.github/workflows/ci-tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/ci-tests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -42,9 +42,7 @@ jobs: | ||||
|           cd api/tacticalrmm | ||||
|           source ../env/bin/activate | ||||
|           rm -f .coverage coverage.lcov | ||||
|           coverage run --concurrency=multiprocessing manage.py test -v 2 --parallel | ||||
|           coverage combine | ||||
|           coverage lcov | ||||
|           pytest | ||||
|           if [ $? -ne 0 ]; then | ||||
|               exit 1 | ||||
|           fi | ||||
| @@ -58,9 +56,8 @@ jobs: | ||||
|               exit 1 | ||||
|           fi | ||||
|  | ||||
|       - name: Coveralls | ||||
|         uses: coverallsapp/github-action@master | ||||
|       - uses: codecov/codecov-action@v2 | ||||
|         with: | ||||
|           github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           path-to-lcov: ./api/tacticalrmm/coverage.lcov | ||||
|           base-path: ./api/tacticalrmm | ||||
|           directory: ./api/tacticalrmm | ||||
|           files: ./api/tacticalrmm/coverage.xml | ||||
|           verbose: true | ||||
|   | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -53,3 +53,5 @@ nats-api.conf | ||||
| ignore/ | ||||
| coverage.lcov | ||||
| daphne.sock.lock | ||||
| .pytest_cache | ||||
| coverage.xml | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # Tactical RMM | ||||
|  | ||||
|  | ||||
| [](https://coveralls.io/github/amidaware/tacticalrmm?branch=develop) | ||||
| [](https://codecov.io/gh/amidaware/tacticalrmm) | ||||
| [](https://github.com/python/black) | ||||
|  | ||||
| Tactical RMM is a remote monitoring & management tool, built with Django and Vue.\ | ||||
|   | ||||
| @@ -69,17 +69,17 @@ class TestAccounts(TacticalTestCase): | ||||
|         self.assertEqual(r.status_code, 400) | ||||
|         self.assertIn("non_field_errors", r.data.keys()) | ||||
|  | ||||
|     @override_settings(DEBUG=True) | ||||
|     @patch("pyotp.TOTP.verify") | ||||
|     def test_debug_login_view(self, mock_verify): | ||||
|         url = "/login/" | ||||
|         mock_verify.return_value = True | ||||
|     # @override_settings(DEBUG=True) | ||||
|     # @patch("pyotp.TOTP.verify") | ||||
|     # def test_debug_login_view(self, mock_verify): | ||||
|     #     url = "/login/" | ||||
|     #     mock_verify.return_value = True | ||||
|  | ||||
|         data = {"username": "bob", "password": "hunter2", "twofactor": "sekret"} | ||||
|         r = self.client.post(url, data, format="json") | ||||
|         self.assertEqual(r.status_code, 200) | ||||
|         self.assertIn("expiry", r.data.keys()) | ||||
|         self.assertIn("token", r.data.keys()) | ||||
|     #     data = {"username": "bob", "password": "hunter2", "twofactor": "sekret"} | ||||
|     #     r = self.client.post(url, data, format="json") | ||||
|     #     self.assertEqual(r.status_code, 200) | ||||
|     #     self.assertIn("expiry", r.data.keys()) | ||||
|     #     self.assertIn("token", r.data.keys()) | ||||
|  | ||||
|  | ||||
| class TestGetAddUsers(TacticalTestCase): | ||||
| @@ -338,7 +338,7 @@ class TestAPIKeyViews(TacticalTestCase): | ||||
|         resp = self.client.put(url, data, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         apikey = APIKey.objects.get(pk=apikey.pk) | ||||
|         self.assertEquals(apikey.name, "New Name") | ||||
|         self.assertEqual(apikey.name, "New Name") | ||||
|  | ||||
|         self.check_not_authenticated("put", url) | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,7 @@ def agent_update(agent_id: str, force: bool = False) -> str: | ||||
|  | ||||
| @app.task | ||||
| def force_code_sign(agent_ids: list[str]) -> None: | ||||
|     chunks = (agent_ids[i : i + 50] for i in range(0, len(agent_ids), 50)) | ||||
|     chunks = (agent_ids[i : i + 70] for i in range(0, len(agent_ids), 70)) | ||||
|     for chunk in chunks: | ||||
|         for agent_id in chunk: | ||||
|             agent_update(agent_id=agent_id, force=True) | ||||
| @@ -77,7 +77,7 @@ def force_code_sign(agent_ids: list[str]) -> None: | ||||
|  | ||||
| @app.task | ||||
| def send_agent_update_task(agent_ids: list[str]) -> None: | ||||
|     chunks = (agent_ids[i : i + 50] for i in range(0, len(agent_ids), 50)) | ||||
|     chunks = (agent_ids[i : i + 70] for i in range(0, len(agent_ids), 70)) | ||||
|     for chunk in chunks: | ||||
|         for agent_id in chunk: | ||||
|             agent_update(agent_id) | ||||
| @@ -97,7 +97,7 @@ def auto_self_agent_update_task() -> None: | ||||
|         if pyver.parse(i.version) < pyver.parse(settings.LATEST_AGENT_VER) | ||||
|     ] | ||||
|  | ||||
|     chunks = (agent_ids[i : i + 30] for i in range(0, len(agent_ids), 30)) | ||||
|     chunks = (agent_ids[i : i + 70] for i in range(0, len(agent_ids), 70)) | ||||
|     for chunk in chunks: | ||||
|         for agent_id in chunk: | ||||
|             agent_update(agent_id) | ||||
|   | ||||
| @@ -245,7 +245,10 @@ class TestAgentViews(TacticalTestCase): | ||||
|  | ||||
|     def test_get_agent_versions(self): | ||||
|         url = "/agents/versions/" | ||||
|         r = self.client.get(url) | ||||
|  | ||||
|         with self.assertNumQueries(1): | ||||
|             r = self.client.get(url) | ||||
|  | ||||
|         self.assertEqual(r.status_code, 200) | ||||
|         assert any(i["hostname"] == self.agent.hostname for i in r.json()["agents"]) | ||||
|  | ||||
|   | ||||
| @@ -136,10 +136,10 @@ class GetAgents(APIView): | ||||
|         else: | ||||
|             agents = ( | ||||
|                 Agent.objects.filter_by_role(request.user)  # type: ignore | ||||
|                 .select_related("site") | ||||
|                 .defer(*AGENT_DEFER) | ||||
|                 .select_related("site__client") | ||||
|                 .filter(monitoring_type_filter) | ||||
|                 .filter(client_site_filter) | ||||
|                 .only("agent_id", "hostname", "site") | ||||
|             ) | ||||
|             serializer = AgentHostnameSerializer(agents, many=True) | ||||
|  | ||||
| @@ -297,9 +297,9 @@ class AgentMeshCentral(APIView): | ||||
| @permission_classes([IsAuthenticated, AgentPerms]) | ||||
| def get_agent_versions(request): | ||||
|     agents = ( | ||||
|         Agent.objects.filter_by_role(request.user)  # type: ignore | ||||
|         .prefetch_related("site") | ||||
|         .only("pk", "hostname") | ||||
|         Agent.objects.defer(*AGENT_DEFER) | ||||
|         .filter_by_role(request.user)  # type: ignore | ||||
|         .select_related("site__client") | ||||
|     ) | ||||
|     return Response( | ||||
|         { | ||||
|   | ||||
| @@ -70,8 +70,8 @@ class TestAlertsViews(TacticalTestCase): | ||||
|         data = {"top": 3} | ||||
|         resp = self.client.patch(url, data, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         self.assertEquals(resp.data["alerts"], AlertSerializer(alerts, many=True).data) | ||||
|         self.assertEquals(resp.data["alerts_count"], 10) | ||||
|         self.assertEqual(resp.data["alerts"], AlertSerializer(alerts, many=True).data) | ||||
|         self.assertEqual(resp.data["alerts_count"], 10) | ||||
|  | ||||
|         # test filter data | ||||
|         # test data and result counts | ||||
| @@ -409,15 +409,15 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         core.server_policy = policy | ||||
|         core.save() | ||||
|  | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|  | ||||
|         # assign second Alert Template to as default alert template | ||||
|         core.alert_template = alert_templates[1] | ||||
|         core.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[1].pk) | ||||
|  | ||||
|         # assign third Alert Template to client | ||||
|         workstation.client.alert_template = alert_templates[2] | ||||
| @@ -425,8 +425,8 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         workstation.client.save() | ||||
|         server.client.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[2].pk) | ||||
|  | ||||
|         # apply policy to client and should override | ||||
|         workstation.client.workstation_policy = policy | ||||
| @@ -434,8 +434,8 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         workstation.client.save() | ||||
|         server.client.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|  | ||||
|         # assign fouth Alert Template to site | ||||
|         workstation.site.alert_template = alert_templates[3] | ||||
| @@ -443,8 +443,8 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         workstation.site.save() | ||||
|         server.site.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|  | ||||
|         # apply policy to site | ||||
|         workstation.site.workstation_policy = policy | ||||
| @@ -452,8 +452,8 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         workstation.site.save() | ||||
|         server.site.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|  | ||||
|         # apply policy to agents | ||||
|         workstation.policy = policy | ||||
| @@ -461,35 +461,35 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         workstation.save() | ||||
|         server.save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[0].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[0].pk) | ||||
|  | ||||
|         # test disabling alert template | ||||
|         alert_templates[0].is_active = False | ||||
|         alert_templates[0].save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|  | ||||
|         # test policy exclusions | ||||
|         alert_templates[3].excluded_agents.set([workstation.pk]) | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|  | ||||
|         # test workstation exclusions | ||||
|         alert_templates[2].exclude_workstations = True | ||||
|         alert_templates[2].save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[3].pk) | ||||
|  | ||||
|         # test server exclusions | ||||
|         alert_templates[3].exclude_servers = True | ||||
|         alert_templates[3].save() | ||||
|  | ||||
|         self.assertEquals(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEquals(server.set_alert_template().pk, alert_templates[2].pk) | ||||
|         self.assertEqual(workstation.set_alert_template().pk, alert_templates[1].pk) | ||||
|         self.assertEqual(server.set_alert_template().pk, alert_templates[2].pk) | ||||
|  | ||||
|     @patch("agents.tasks.sleep") | ||||
|     @patch("core.models.CoreSettings.send_mail") | ||||
| @@ -523,7 +523,7 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         # call outages task and no alert should be created | ||||
|         agent_outages_task() | ||||
|  | ||||
|         self.assertEquals(Alert.objects.count(), 0) | ||||
|         self.assertEqual(Alert.objects.count(), 0) | ||||
|  | ||||
|         # set overdue_dashboard_alert and alert should be created | ||||
|         agent_dashboard_alert.overdue_dashboard_alert = True | ||||
| @@ -574,22 +574,22 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         agent_outages_task() | ||||
|  | ||||
|         # should have created 6 alerts | ||||
|         self.assertEquals(Alert.objects.count(), 6) | ||||
|         self.assertEqual(Alert.objects.count(), 6) | ||||
|  | ||||
|         # other specific agents should have created alerts | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_dashboard_alert).count(), 1) | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_text_alert).count(), 1) | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_email_alert).count(), 1) | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_template_email).count(), 1) | ||||
|         self.assertEquals( | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_dashboard_alert).count(), 1) | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_text_alert).count(), 1) | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_email_alert).count(), 1) | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_template_email).count(), 1) | ||||
|         self.assertEqual( | ||||
|             Alert.objects.filter(agent=agent_template_dashboard).count(), 1 | ||||
|         ) | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_template_text).count(), 1) | ||||
|         self.assertEquals(Alert.objects.filter(agent=agent_template_blank).count(), 0) | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_template_text).count(), 1) | ||||
|         self.assertEqual(Alert.objects.filter(agent=agent_template_blank).count(), 0) | ||||
|  | ||||
|         # check if email and text tasks were called | ||||
|         self.assertEquals(outage_email.call_count, 2) | ||||
|         self.assertEquals(outage_sms.call_count, 2) | ||||
|         self.assertEqual(outage_email.call_count, 2) | ||||
|         self.assertEqual(outage_sms.call_count, 2) | ||||
|  | ||||
|         outage_sms.assert_any_call( | ||||
|             pk=Alert.objects.get(agent=agent_text_alert).pk, alert_interval=None | ||||
| @@ -630,7 +630,7 @@ class TestAlertTasks(TacticalTestCase): | ||||
|  | ||||
|         # calling agent outage task again shouldn't create duplicate alerts and won't send alerts | ||||
|         agent_outages_task() | ||||
|         self.assertEquals(Alert.objects.count(), 6) | ||||
|         self.assertEqual(Alert.objects.count(), 6) | ||||
|  | ||||
|         # test periodic notification | ||||
|         # change email/text sent to sometime in the past | ||||
| @@ -942,7 +942,7 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         send_email.assert_not_called() | ||||
|         send_sms.assert_not_called() | ||||
|  | ||||
|         self.assertEquals( | ||||
|         self.assertEqual( | ||||
|             Alert.objects.filter(assigned_check=check_template_email).count(), 1 | ||||
|         ) | ||||
|  | ||||
| @@ -1244,7 +1244,7 @@ class TestAlertTasks(TacticalTestCase): | ||||
|         send_email.assert_not_called() | ||||
|         send_sms.assert_not_called() | ||||
|  | ||||
|         self.assertEquals( | ||||
|         self.assertEqual( | ||||
|             Alert.objects.filter(assigned_task=task_template_email).count(), 1 | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -62,7 +62,7 @@ class TestAPIv3(TacticalTestCase): | ||||
|         r = self.client.get(url) | ||||
|         self.assertEqual(r.status_code, 200) | ||||
|         self.assertEqual(r.data["check_interval"], 20) | ||||
|         self.assertEquals(len(r.data["checks"]), 2) | ||||
|         self.assertEqual(len(r.data["checks"]), 2) | ||||
|  | ||||
|         url = "/api/v3/Maj34ACb324j234asdj2n34kASDjh34-DESKTOPTEST123/checkrunner/" | ||||
|         r = self.client.get(url) | ||||
|   | ||||
| @@ -266,9 +266,9 @@ class TaskRunner(APIView): | ||||
|     permission_classes = [IsAuthenticated] | ||||
|  | ||||
|     def get(self, request, pk, agentid): | ||||
|         _ = get_object_or_404(Agent, agent_id=agentid) | ||||
|         agent = get_object_or_404(Agent, agent_id=agentid) | ||||
|         task = get_object_or_404(AutomatedTask, pk=pk) | ||||
|         return Response(TaskGOGetSerializer(task).data) | ||||
|         return Response(TaskGOGetSerializer(task, context={"agent": agent}).data) | ||||
|  | ||||
|     def patch(self, request, pk, agentid): | ||||
|         from alerts.models import Alert | ||||
|   | ||||
| @@ -410,11 +410,11 @@ class TestPolicyTasks(TacticalTestCase): | ||||
|         ) | ||||
|  | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         self.assertEquals(len(resp.data["server_clients"]), 1) | ||||
|         self.assertEquals(len(resp.data["server_sites"]), 0) | ||||
|         self.assertEquals(len(resp.data["workstation_clients"]), 1) | ||||
|         self.assertEquals(len(resp.data["workstation_sites"]), 0) | ||||
|         self.assertEquals(len(resp.data["agents"]), 0) | ||||
|         self.assertEqual(len(resp.data["server_clients"]), 1) | ||||
|         self.assertEqual(len(resp.data["server_sites"]), 0) | ||||
|         self.assertEqual(len(resp.data["workstation_clients"]), 1) | ||||
|         self.assertEqual(len(resp.data["workstation_sites"]), 0) | ||||
|         self.assertEqual(len(resp.data["agents"]), 0) | ||||
|  | ||||
|         # Add Site to Policy | ||||
|         policy.server_sites.add(server_agents[10].site) | ||||
| @@ -422,9 +422,9 @@ class TestPolicyTasks(TacticalTestCase): | ||||
|         resp = self.client.get( | ||||
|             f"/automation/policies/{policy.pk}/related/", format="json" | ||||
|         ) | ||||
|         self.assertEquals(len(resp.data["server_sites"]), 1) | ||||
|         self.assertEquals(len(resp.data["workstation_sites"]), 1) | ||||
|         self.assertEquals(len(resp.data["agents"]), 0) | ||||
|         self.assertEqual(len(resp.data["server_sites"]), 1) | ||||
|         self.assertEqual(len(resp.data["workstation_sites"]), 1) | ||||
|         self.assertEqual(len(resp.data["agents"]), 0) | ||||
|  | ||||
|         # Add Agent to Policy | ||||
|         policy.agents.add(server_agents[2]) | ||||
| @@ -432,7 +432,7 @@ class TestPolicyTasks(TacticalTestCase): | ||||
|         resp = self.client.get( | ||||
|             f"/automation/policies/{policy.pk}/related/", format="json" | ||||
|         ) | ||||
|         self.assertEquals(len(resp.data["agents"]), 2) | ||||
|         self.assertEqual(len(resp.data["agents"]), 2) | ||||
|  | ||||
|     def test_getting_agent_policy_checks(self): | ||||
|  | ||||
| @@ -442,7 +442,7 @@ class TestPolicyTasks(TacticalTestCase): | ||||
|         agent = baker.make_recipe("agents.agent", policy=policy) | ||||
|  | ||||
|         # test policy assigned to agent | ||||
|         self.assertEquals(len(agent.get_checks_from_policies()), 7) | ||||
|         self.assertEqual(len(agent.get_checks_from_policies()), 7) | ||||
|  | ||||
|     def test_getting_agent_policy_checks_with_enforced(self): | ||||
|         # setup data | ||||
|   | ||||
| @@ -28,7 +28,9 @@ class GetAddPolicies(APIView): | ||||
|     permission_classes = [IsAuthenticated, AutomationPolicyPerms] | ||||
|  | ||||
|     def get(self, request): | ||||
|         policies = Policy.objects.all() | ||||
|         policies = Policy.objects.select_related("alert_template").prefetch_related( | ||||
|             "excluded_agents", "excluded_sites", "excluded_clients" | ||||
|         ) | ||||
|  | ||||
|         return Response( | ||||
|             PolicyTableSerializer( | ||||
|   | ||||
| @@ -198,13 +198,14 @@ class TaskGOGetSerializer(serializers.ModelSerializer): | ||||
|     def get_task_actions(self, obj): | ||||
|         tmp = [] | ||||
|         actions_to_remove = [] | ||||
|         agent = self.context["agent"] if "agent" in self.context.keys() else obj.agent | ||||
|         for action in obj.actions: | ||||
|             if action["type"] == "cmd": | ||||
|                 tmp.append( | ||||
|                     { | ||||
|                         "type": "cmd", | ||||
|                         "command": Script.parse_script_args( | ||||
|                             agent=obj.agent, | ||||
|                             agent=agent, | ||||
|                             shell=action["shell"], | ||||
|                             args=[action["command"]], | ||||
|                         )[0], | ||||
| @@ -225,7 +226,7 @@ class TaskGOGetSerializer(serializers.ModelSerializer): | ||||
|                         "script_name": script.name, | ||||
|                         "code": script.code, | ||||
|                         "script_args": Script.parse_script_args( | ||||
|                             agent=obj.agent, | ||||
|                             agent=agent, | ||||
|                             shell=script.shell, | ||||
|                             args=action["script_args"], | ||||
|                         ), | ||||
|   | ||||
| @@ -842,8 +842,8 @@ class TestCheckTasks(TacticalTestCase): | ||||
|  | ||||
|         check_result = CheckResult.objects.get(assigned_check=check, agent=self.agent) | ||||
|  | ||||
|         self.assertEquals(check.alert_severity, "warning") | ||||
|         self.assertEquals(check_result.status, "failing") | ||||
|         self.assertEqual(check.alert_severity, "warning") | ||||
|         self.assertEqual(check_result.status, "failing") | ||||
|  | ||||
|         # test passing when contains | ||||
|         resp = self.client.patch(url, no_logs_data, format="json") | ||||
| @@ -851,7 +851,7 @@ class TestCheckTasks(TacticalTestCase): | ||||
|  | ||||
|         check_result = CheckResult.objects.get(assigned_check=check, agent=self.agent) | ||||
|  | ||||
|         self.assertEquals(check_result.status, "passing") | ||||
|         self.assertEqual(check_result.status, "passing") | ||||
|  | ||||
|         # test failing when not contains and message and source | ||||
|         check.fail_when = "not_contains" | ||||
| @@ -863,8 +863,8 @@ class TestCheckTasks(TacticalTestCase): | ||||
|  | ||||
|         check_result = CheckResult.objects.get(assigned_check=check, agent=self.agent) | ||||
|  | ||||
|         self.assertEquals(check_result.status, "failing") | ||||
|         self.assertEquals(check.alert_severity, "error") | ||||
|         self.assertEqual(check_result.status, "failing") | ||||
|         self.assertEqual(check.alert_severity, "error") | ||||
|  | ||||
|         # test passing when contains with source and message | ||||
|         resp = self.client.patch(url, data, format="json") | ||||
| @@ -872,7 +872,7 @@ class TestCheckTasks(TacticalTestCase): | ||||
|  | ||||
|         check_result = CheckResult.objects.get(assigned_check=check, agent=self.agent) | ||||
|  | ||||
|         self.assertEquals(check_result.status, "passing") | ||||
|         self.assertEqual(check_result.status, "passing") | ||||
|  | ||||
|  | ||||
| class TestCheckPermissions(TacticalTestCase): | ||||
|   | ||||
| @@ -32,9 +32,8 @@ class GetAddClients(APIView): | ||||
|  | ||||
|     def get(self, request): | ||||
|         clients = ( | ||||
|             Client.objects.select_related( | ||||
|                 "workstation_policy", "server_policy", "alert_template" | ||||
|             ) | ||||
|             Client.objects.order_by("name") | ||||
|             .select_related("workstation_policy", "server_policy", "alert_template") | ||||
|             .filter_by_role(request.user)  # type: ignore | ||||
|             .prefetch_related( | ||||
|                 Prefetch( | ||||
|   | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 4.0.4 on 2022-04-23 14:26 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('core', '0033_coresettings_mesh_disable_auto_login'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='customfield', | ||||
|             name='name', | ||||
|             field=models.CharField(max_length=100), | ||||
|         ), | ||||
|     ] | ||||
| @@ -175,12 +175,12 @@ class CoreSettings(BaseAuditModel): | ||||
|         body: str, | ||||
|         alert_template: "Optional[AlertTemplate]" = None, | ||||
|         test: bool = False, | ||||
|     ) -> Union[bool, str]: | ||||
|     ) -> tuple[str, bool]: | ||||
|         if test and not self.email_is_configured: | ||||
|             return "There needs to be at least one email recipient configured" | ||||
|             return ("There needs to be at least one email recipient configured", False) | ||||
|         # return since email must be configured to continue | ||||
|         elif not self.email_is_configured: | ||||
|             return "SMTP messaging not configured." | ||||
|             return ("SMTP messaging not configured.", False) | ||||
|  | ||||
|         # override email from if alert_template is passed and is set | ||||
|         if alert_template and alert_template.email_from: | ||||
| @@ -194,7 +194,7 @@ class CoreSettings(BaseAuditModel): | ||||
|         elif self.email_alert_recipients: | ||||
|             email_recipients = ", ".join(cast(List[str], self.email_alert_recipients)) | ||||
|         else: | ||||
|             return "There needs to be at least one email recipient configured" | ||||
|             return ("There needs to be at least one email recipient configured", False) | ||||
|  | ||||
|         try: | ||||
|             msg = EmailMessage() | ||||
| @@ -221,18 +221,21 @@ class CoreSettings(BaseAuditModel): | ||||
|         except Exception as e: | ||||
|             DebugLog.error(message=f"Sending email failed with error: {e}") | ||||
|             if test: | ||||
|                 return str(e) | ||||
|         finally: | ||||
|             return True | ||||
|                 return (str(e), False) | ||||
|  | ||||
|         if test: | ||||
|             return ("Email test ok!", True) | ||||
|  | ||||
|         return ("ok", True) | ||||
|  | ||||
|     def send_sms( | ||||
|         self, | ||||
|         body: str, | ||||
|         alert_template: "Optional[AlertTemplate]" = None, | ||||
|         test: bool = False, | ||||
|     ) -> Union[str, bool]: | ||||
|     ) -> tuple[str, bool]: | ||||
|         if not self.sms_is_configured: | ||||
|             return "Sms alerting is not setup correctly." | ||||
|             return ("Sms alerting is not setup correctly.", False) | ||||
|  | ||||
|         # override email recipients if alert_template is passed and is set | ||||
|         if alert_template and alert_template.text_recipients: | ||||
| @@ -240,7 +243,7 @@ class CoreSettings(BaseAuditModel): | ||||
|         elif self.sms_alert_recipients: | ||||
|             text_recipients = cast(List[str], self.sms_alert_recipients) | ||||
|         else: | ||||
|             return "No sms recipients found" | ||||
|             return ("No sms recipients found", False) | ||||
|  | ||||
|         tw_client = TwClient(self.twilio_account_sid, self.twilio_auth_token) | ||||
|         for num in text_recipients: | ||||
| @@ -249,9 +252,12 @@ class CoreSettings(BaseAuditModel): | ||||
|             except TwilioRestException as e: | ||||
|                 DebugLog.error(message=f"SMS failed to send: {e}") | ||||
|                 if test: | ||||
|                     return str(e) | ||||
|                     return (str(e), False) | ||||
|  | ||||
|         return True | ||||
|         if test: | ||||
|             return ("SMS Test sent successfully!", True) | ||||
|  | ||||
|         return ("ok", True) | ||||
|  | ||||
|     @staticmethod | ||||
|     def serialize(core): | ||||
| @@ -284,7 +290,7 @@ class CustomField(BaseAuditModel): | ||||
|         blank=True, | ||||
|         default=list, | ||||
|     ) | ||||
|     name = models.CharField(max_length=30) | ||||
|     name = models.CharField(max_length=100) | ||||
|     required = models.BooleanField(blank=True, default=False) | ||||
|     default_value_string = models.TextField(null=True, blank=True) | ||||
|     default_value_bool = models.BooleanField(default=False) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ from clients.models import Client, Site | ||||
| from checks.models import Check, CheckResult | ||||
| from core.utils import get_core_settings | ||||
| from django.conf import settings | ||||
| from django.db.models import Prefetch, Exists, OuterRef | ||||
| from django.db.models import Prefetch | ||||
| from logs.models import PendingAction | ||||
| from logs.tasks import prune_audit_log, prune_debug_log | ||||
| from packaging import version as pyver | ||||
| @@ -53,6 +53,23 @@ def core_maintenance_tasks() -> None: | ||||
|  | ||||
| @app.task | ||||
| def handle_resolved_stuff() -> None: | ||||
|  | ||||
|     # change agent update pending status to completed if agent has just updated | ||||
|     actions = ( | ||||
|         PendingAction.objects.select_related("agent") | ||||
|         .defer("agent__services", "agent__wmi_detail") | ||||
|         .filter(action_type="agentupdate", status="pending") | ||||
|     ) | ||||
|  | ||||
|     to_update = [ | ||||
|         action.id | ||||
|         for action in actions | ||||
|         if pyver.parse(action.agent.version) == pyver.parse(settings.LATEST_AGENT_VER) | ||||
|         and action.agent.status == "online" | ||||
|     ] | ||||
|  | ||||
|     PendingAction.objects.filter(pk__in=to_update).update(status="completed") | ||||
|  | ||||
|     agent_queryset = ( | ||||
|         Agent.objects.defer(*AGENT_DEFER) | ||||
|         .select_related( | ||||
| @@ -80,26 +97,11 @@ def handle_resolved_stuff() -> None: | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|     for agent in agent_queryset.annotate( | ||||
|         has_pending_actions=Exists( | ||||
|             PendingAction.objects.filter( | ||||
|                 pk=OuterRef("pk"), action_type="agent_update", status="pending" | ||||
|             ) | ||||
|         ) | ||||
|     ): | ||||
|     for agent in agent_queryset: | ||||
|         if ( | ||||
|             pyver.parse(agent.version) >= pyver.parse("1.6.0") | ||||
|             and agent.status == "online" | ||||
|         ): | ||||
|             # change agent update pending status to completed if agent has just updated | ||||
|             if ( | ||||
|                 pyver.parse(agent.version) == pyver.parse(settings.LATEST_AGENT_VER) | ||||
|                 and agent.has_pending_actions | ||||
|             ): | ||||
|                 agent.pendingactions.filter( | ||||
|                     action_type="agent_update", status="pending" | ||||
|                 ).update(status="completed") | ||||
|  | ||||
|             # sync scheduled tasks | ||||
|             for task in agent.get_tasks_with_policies(): | ||||
|                 if not task.task_result or task.task_result.sync_status == "initial": | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| from unittest.mock import patch | ||||
|  | ||||
| import requests | ||||
| from django.conf import settings | ||||
| from channels.db import database_sync_to_async | ||||
| from channels.testing import WebsocketCommunicator | ||||
| from model_bakery import baker | ||||
| @@ -11,7 +12,9 @@ from core.utils import get_core_settings | ||||
| from .consumers import DashInfo | ||||
| from .models import CustomField, GlobalKVStore, URLAction | ||||
| from .serializers import CustomFieldSerializer, KeyStoreSerializer, URLActionSerializer | ||||
| from .tasks import core_maintenance_tasks | ||||
| from .tasks import core_maintenance_tasks, handle_resolved_stuff | ||||
| from logs.models import PendingAction | ||||
| from agents.models import Agent | ||||
|  | ||||
|  | ||||
| class TestCodeSign(TacticalTestCase): | ||||
| @@ -393,6 +396,29 @@ class TestCoreTasks(TacticalTestCase): | ||||
|  | ||||
|         self.check_not_authenticated("get", url) | ||||
|  | ||||
|     def test_resolved_pending_agentupdate_task(self): | ||||
|         online = baker.make_recipe("agents.online_agent", version="2.0.0", _quantity=20) | ||||
|         offline = baker.make_recipe( | ||||
|             "agents.offline_agent", version="2.0.0", _quantity=20 | ||||
|         ) | ||||
|         agents = online + offline | ||||
|         for agent in agents: | ||||
|             baker.make_recipe("logs.pending_agentupdate_action", agent=agent) | ||||
|  | ||||
|         Agent.objects.update(version=settings.LATEST_AGENT_VER) | ||||
|  | ||||
|         handle_resolved_stuff() | ||||
|  | ||||
|         complete = PendingAction.objects.filter( | ||||
|             action_type="agentupdate", status="completed" | ||||
|         ).count() | ||||
|         old = PendingAction.objects.filter( | ||||
|             action_type="agentupdate", status="pending" | ||||
|         ).count() | ||||
|  | ||||
|         self.assertEqual(complete, 20) | ||||
|         self.assertEqual(old, 20) | ||||
|  | ||||
|  | ||||
| class TestCorePermissions(TacticalTestCase): | ||||
|     def setUp(self): | ||||
|   | ||||
| @@ -93,14 +93,14 @@ def dashboard_info(request): | ||||
| @permission_classes([IsAuthenticated, CoreSettingsPerms]) | ||||
| def email_test(request): | ||||
|     core = get_core_settings() | ||||
|     r = core.send_mail( | ||||
|  | ||||
|     msg, ok = core.send_mail( | ||||
|         subject="Test from Tactical RMM", body="This is a test message", test=True | ||||
|     ) | ||||
|     if not ok: | ||||
|         return notify_error(msg) | ||||
|  | ||||
|     if not isinstance(r, bool) and isinstance(r, str): | ||||
|         return notify_error(r) | ||||
|  | ||||
|     return Response("Email Test OK!") | ||||
|     return Response(msg) | ||||
|  | ||||
|  | ||||
| @api_view(["POST"]) | ||||
| @@ -388,9 +388,8 @@ class TwilioSMSTest(APIView): | ||||
|                 "All fields are required, including at least 1 recipient" | ||||
|             ) | ||||
|  | ||||
|         r = core.send_sms("TacticalRMM Test SMS", test=True) | ||||
|         msg, ok = core.send_sms("TacticalRMM Test SMS", test=True) | ||||
|         if not ok: | ||||
|             return notify_error(msg) | ||||
|  | ||||
|         if not isinstance(r, bool) and isinstance(r, str): | ||||
|             return notify_error(r) | ||||
|  | ||||
|         return Response("SMS Test sent successfully!") | ||||
|         return Response(msg) | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| default_app_config = "logs.apps.LogsConfig" | ||||
|   | ||||
| @@ -26,3 +26,7 @@ object_logs = Recipe( | ||||
| ) | ||||
|  | ||||
| login_logs = Recipe("logs.AuditLog", action=cycle(login_actions), object_type="user") | ||||
|  | ||||
| pending_agentupdate_action = Recipe( | ||||
|     "logs.PendingAction", action_type="agentupdate", status="pending" | ||||
| ) | ||||
|   | ||||
| @@ -96,9 +96,14 @@ class PendingActions(APIView): | ||||
|     def get(self, request, agent_id=None): | ||||
|         if agent_id: | ||||
|             agent = get_object_or_404( | ||||
|                 Agent.objects.defer(*AGENT_DEFER), agent_id=agent_id | ||||
|                 Agent.objects.defer(*AGENT_DEFER).prefetch_related("pendingactions"), | ||||
|                 agent_id=agent_id, | ||||
|             ) | ||||
|             actions = ( | ||||
|                 PendingAction.objects.filter(agent=agent) | ||||
|                 .select_related("agent__site", "agent__site__client") | ||||
|                 .defer("agent__services", "agent__wmi_detail") | ||||
|             ) | ||||
|             actions = PendingAction.objects.filter(agent=agent) | ||||
|         else: | ||||
|             actions = ( | ||||
|                 PendingAction.objects.filter_by_role(request.user)  # type: ignore | ||||
|   | ||||
							
								
								
									
										7
									
								
								api/tacticalrmm/pytest.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								api/tacticalrmm/pytest.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| [pytest] | ||||
| DJANGO_SETTINGS_MODULE = tacticalrmm.settings | ||||
| python_files = tests.py | ||||
| addopts = -vvv --cov --cov-config=.coveragerc --cov-report=xml -n auto | ||||
|  | ||||
| filterwarnings = | ||||
|     ignore::django.core.cache.CacheKeyWarning | ||||
| @@ -1,5 +1,8 @@ | ||||
| coverage | ||||
| coveralls | ||||
| model_bakery | ||||
| black | ||||
| tblib | ||||
| pytest | ||||
| pytest-django | ||||
| pytest-xdist | ||||
| pytest-cov | ||||
| codecov | ||||
| @@ -80,7 +80,7 @@ class TestScriptViews(TacticalTestCase): | ||||
|         resp = self.client.put(url, data, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         script = Script.objects.get(pk=script.pk) | ||||
|         self.assertEquals(script.description, "Description Change") | ||||
|         self.assertEqual(script.description, "Description Change") | ||||
|  | ||||
|         # correct_hash = hmac.new( | ||||
|         #     settings.SECRET_KEY.encode(), data["script_body"].encode(), hashlib.sha256 | ||||
| @@ -467,7 +467,7 @@ class TestScriptSnippetViews(TacticalTestCase): | ||||
|         resp = self.client.put(url, data, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         snippet = ScriptSnippet.objects.get(pk=snippet.pk) | ||||
|         self.assertEquals(snippet.name, "New Name") | ||||
|         self.assertEqual(snippet.name, "New Name") | ||||
|  | ||||
|         self.check_not_authenticated("put", url) | ||||
|  | ||||
|   | ||||
| @@ -63,7 +63,7 @@ class TestServiceViews(TacticalTestCase): | ||||
|         resp = self.client.get(url, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         nats_cmd.assert_called_with(data={"func": "winservices"}, timeout=10) | ||||
|         self.assertEquals(Agent.objects.get(pk=agent.pk).services, nats_return) | ||||
|         self.assertEqual(Agent.objects.get(pk=agent.pk).services, nats_return) | ||||
|  | ||||
|         self.check_not_authenticated("get", url) | ||||
|  | ||||
| @@ -141,7 +141,7 @@ class TestServiceViews(TacticalTestCase): | ||||
|         nats_cmd.assert_called_with( | ||||
|             {"func": "winsvcdetail", "payload": {"name": "alg"}}, timeout=10 | ||||
|         ) | ||||
|         self.assertEquals(resp.data, nats_return) | ||||
|         self.assertEqual(resp.data, nats_return) | ||||
|  | ||||
|         self.check_not_authenticated("get", url) | ||||
|  | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class TestSoftwareViews(TacticalTestCase): | ||||
|         # test without agent software | ||||
|         resp = self.client.get(url, format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         self.assertEquals(resp.data, []) | ||||
|         self.assertEqual(resp.data, []) | ||||
|  | ||||
|         # make some software | ||||
|         software = baker.make( | ||||
| @@ -60,7 +60,7 @@ class TestSoftwareViews(TacticalTestCase): | ||||
|         serializer = InstalledSoftwareSerializer([software], many=True) | ||||
|         resp = self.client.get(f"{base_url}/", format="json") | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|         self.assertEquals(resp.data, serializer.data) | ||||
|         self.assertEqual(resp.data, serializer.data) | ||||
|  | ||||
|         self.check_not_authenticated("get", url) | ||||
|  | ||||
|   | ||||
| @@ -17,11 +17,11 @@ LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh" | ||||
| AUTH_USER_MODEL = "accounts.User" | ||||
|  | ||||
| # latest release | ||||
| TRMM_VERSION = "0.13.0" | ||||
| TRMM_VERSION = "0.13.2" | ||||
|  | ||||
| # bump this version everytime vue code is changed | ||||
| # to alert user they need to manually refresh their browser | ||||
| APP_VER = "0.0.161" | ||||
| APP_VER = "0.0.163" | ||||
|  | ||||
| # https://github.com/amidaware/rmmagent | ||||
| LATEST_AGENT_VER = "2.0.3" | ||||
| @@ -49,7 +49,6 @@ ASGI_APPLICATION = "tacticalrmm.asgi.application" | ||||
| LANGUAGE_CODE = "en-us" | ||||
| TIME_ZONE = "UTC" | ||||
| USE_I18N = True | ||||
| USE_L10N = True | ||||
| USE_TZ = True | ||||
|  | ||||
| STATIC_URL = "/static/" | ||||
| @@ -96,23 +95,6 @@ if not DEBUG: | ||||
|         {"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",)} | ||||
|     ) | ||||
|  | ||||
| MIDDLEWARE = [ | ||||
|     "django.middleware.security.SecurityMiddleware", | ||||
|     "django.contrib.sessions.middleware.SessionMiddleware", | ||||
|     "corsheaders.middleware.CorsMiddleware",  ## | ||||
|     "tacticalrmm.middleware.LogIPMiddleware", | ||||
|     "django.middleware.common.CommonMiddleware", | ||||
|     "django.middleware.csrf.CsrfViewMiddleware", | ||||
|     "django.contrib.auth.middleware.AuthenticationMiddleware", | ||||
|     "tacticalrmm.middleware.AuditMiddleware", | ||||
|     "tacticalrmm.middleware.LinuxMiddleware", | ||||
|     "django.middleware.clickjacking.XFrameOptionsMiddleware", | ||||
| ] | ||||
|  | ||||
| if DEMO: | ||||
|     MIDDLEWARE += ("tacticalrmm.middleware.DemoMiddleware",) | ||||
|  | ||||
|  | ||||
| INSTALLED_APPS = [ | ||||
|     "django.contrib.auth", | ||||
|     "django.contrib.contenttypes", | ||||
| @@ -257,11 +239,9 @@ LOGGING = { | ||||
|     }, | ||||
| } | ||||
|  | ||||
| if "AZPIPELINE" in os.environ: | ||||
|     ADMIN_ENABLED = False | ||||
|  | ||||
| if "GHACTIONS" in os.environ: | ||||
|     print("-----------------------PIPELINE----------------------------") | ||||
|     print("-----------------------GHACTIONS----------------------------") | ||||
|     DATABASES = { | ||||
|         "default": { | ||||
|             "ENGINE": "django.db.backends.postgresql", | ||||
|   | ||||
| @@ -65,9 +65,11 @@ services: | ||||
|     depends_on: | ||||
|       - tactical-postgres | ||||
|       - tactical-meshcentral | ||||
|       - tactical-redis | ||||
|     networks: | ||||
|       - api-db | ||||
|       - proxy | ||||
|       - redis | ||||
|     volumes: | ||||
|       - tactical_data:/opt/tactical | ||||
|       - mesh_data:/meshcentral-data | ||||
|   | ||||
| @@ -328,7 +328,12 @@ else | ||||
|     echo -ne ${RED} SSL Certificate has expired or doesnt exist for $domain  | tee -a checklog.log | ||||
| 	printf >&2 "\n\n" | ||||
| fi | ||||
| 	echo -ne ${YELLOW} Getting summary output of logs | tee -a checklog.log   | ||||
|  | ||||
| tail /rmm/api/tacticalrmm/tacticalrmm/private/log/django_debug.log  | tee -a checklog.log | ||||
| 	printf >&2 "\n\n" | ||||
| tail /rmm/api/tacticalrmm/tacticalrmm/private/log/error.log  | tee -a checklog.log | ||||
| 	printf >&2 "\n\n" | ||||
|  | ||||
| printf >&2 "\n\n" | ||||
| echo -ne ${YELLOW}  | ||||
|   | ||||
| @@ -23,7 +23,7 @@ export default function () { | ||||
|         showCommunityScripts: false, | ||||
|         agentDblClickAction: "", | ||||
|         agentUrlAction: null, | ||||
|         defaultAgentTblTab: "server", | ||||
|         defaultAgentTblTab: null, | ||||
|         clientTreeSort: "alphafail", | ||||
|         clientTreeSplitter: 20, | ||||
|         noCodeSign: false, | ||||
| @@ -253,14 +253,15 @@ export default function () { | ||||
|           } | ||||
|  | ||||
|  | ||||
|           const sorted = output.sort((a, b) => a.label.localeCompare(b.label)); | ||||
|           if (state.clientTreeSort === "alphafail") { | ||||
|             // move failing clients to the top | ||||
|             const failing = output.filter(i => i.color === "negative" || i.color === "warning"); | ||||
|             const ok = output.filter(i => i.color !== "negative" && i.color !== "warning"); | ||||
|             const failing = sorted.filter(i => i.color === "negative" || i.color === "warning"); | ||||
|             const ok = sorted.filter(i => i.color !== "negative" && i.color !== "warning"); | ||||
|             const sortedByFailing = [...failing, ...ok]; | ||||
|             commit("loadTree", sortedByFailing); | ||||
|           } else { | ||||
|             commit("loadTree", output); | ||||
|             commit("loadTree", sorted); | ||||
|           } | ||||
|  | ||||
|         }) | ||||
|   | ||||
| @@ -453,7 +453,6 @@ export default { | ||||
|           field: "last_seen", | ||||
|           sortable: true, | ||||
|           align: "left", | ||||
|           sort: (a, b) => this.dateStringToUnix(a) - this.dateStringToUnix(b), | ||||
|         }, | ||||
|         { | ||||
|           name: "boot_time", | ||||
| @@ -491,10 +490,12 @@ export default { | ||||
|     selectedTree(newVal, oldVal) { | ||||
|       if (this.clearSearchWhenSwitching) this.clearFilter(); | ||||
|     }, | ||||
|     tab(newVal, oldVal) { | ||||
|       this.$store.dispatch("loadAgents"); | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     getTree() { | ||||
|       this.$store.dispatch("loadAgents"); | ||||
|       this.$store.dispatch("loadTree"); | ||||
|     }, | ||||
|     clearTreeSelected() { | ||||
| @@ -708,7 +709,6 @@ export default { | ||||
|       }, | ||||
|       set(newVal) { | ||||
|         this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", newVal); | ||||
|         this.$store.dispatch("loadAgents"); | ||||
|         this.$store.commit("destroySubTable"); | ||||
|       }, | ||||
|     }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user