forked from p34709852/monkey
commit
eb16969a56
|
@ -1,18 +1,22 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from typing import List, Sequence, Union
|
from typing import List, Mapping, Sequence, Union
|
||||||
|
|
||||||
from bson import json_util
|
from bson import json_util
|
||||||
|
|
||||||
from common.credentials import Credentials
|
from common.credentials import Credentials
|
||||||
|
from common.types import AgentID, MachineID
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
||||||
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
|
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
|
||||||
|
from monkey_island.cc.models import Agent, Machine
|
||||||
|
|
||||||
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
|
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
|
||||||
|
GET_AGENTS_ENDPOINT = "api/agents"
|
||||||
|
GET_LOG_ENDPOINT = "api/agent-logs"
|
||||||
|
GET_MACHINES_ENDPOINT = "api/machines"
|
||||||
MONKEY_TEST_ENDPOINT = "api/test/monkey"
|
MONKEY_TEST_ENDPOINT = "api/test/monkey"
|
||||||
TELEMETRY_TEST_ENDPOINT = "api/test/telemetry"
|
TELEMETRY_TEST_ENDPOINT = "api/test/telemetry"
|
||||||
LOG_TEST_ENDPOINT = "api/test/log"
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,11 +161,21 @@ class MonkeyIslandClient(object):
|
||||||
)
|
)
|
||||||
return MonkeyIslandClient.get_test_query_results(response)
|
return MonkeyIslandClient.get_test_query_results(response)
|
||||||
|
|
||||||
def find_log_in_db(self, query):
|
def get_agents(self) -> Sequence[Agent]:
|
||||||
response = self.requests.get(
|
response = self.requests.get(GET_AGENTS_ENDPOINT)
|
||||||
LOG_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(query)
|
|
||||||
)
|
return [Agent(**a) for a in response.json()]
|
||||||
return MonkeyIslandClient.get_test_query_results(response)
|
|
||||||
|
def get_machines(self) -> Mapping[MachineID, Machine]:
|
||||||
|
response = self.requests.get(GET_MACHINES_ENDPOINT)
|
||||||
|
machines = (Machine(**m) for m in response.json())
|
||||||
|
|
||||||
|
return {m.id: m for m in machines}
|
||||||
|
|
||||||
|
def get_agent_log(self, agent_id: AgentID) -> str:
|
||||||
|
response = self.requests.get(f"{GET_LOG_ENDPOINT}/{agent_id}")
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def form_find_query_for_request(query: Union[dict, None]) -> dict:
|
def form_find_query_for_request(query: Union[dict, None]) -> dict:
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from bson import ObjectId
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class MonkeyLog(object):
|
|
||||||
def __init__(self, monkey, log_dir_path):
|
|
||||||
self.monkey = monkey
|
|
||||||
self.log_dir_path = log_dir_path
|
|
||||||
|
|
||||||
def download_log(self, island_client):
|
|
||||||
log = island_client.find_log_in_db({"monkey_id": ObjectId(self.monkey["_id"])})
|
|
||||||
if not log:
|
|
||||||
LOGGER.error("Log for monkey {} not found".format(self.monkey["ip_addresses"][0]))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.write_log_to_file(log)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def write_log_to_file(self, log):
|
|
||||||
with open(self.get_log_path_for_monkey(self.monkey), "w") as log_file:
|
|
||||||
log_file.write(MonkeyLog.parse_log(log))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_log(log):
|
|
||||||
log = log.strip('"')
|
|
||||||
log = log.replace("\\n", "\n ")
|
|
||||||
return log
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_filename_for_monkey_log(monkey):
|
|
||||||
return "{}.txt".format(monkey["ip_addresses"][0])
|
|
||||||
|
|
||||||
def get_log_path_for_monkey(self, monkey):
|
|
||||||
return os.path.join(self.log_dir_path, MonkeyLog.get_filename_for_monkey_log(monkey))
|
|
|
@ -1,25 +1,65 @@
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Thread
|
||||||
|
from typing import List, Mapping
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_log import MonkeyLog
|
from common.types import MachineID
|
||||||
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
from monkey_island.cc.models import Agent, Machine
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MonkeyLogsDownloader(object):
|
class MonkeyLogsDownloader(object):
|
||||||
def __init__(self, island_client, log_dir_path):
|
def __init__(self, island_client: MonkeyIslandClient, log_dir_path: str):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
self.log_dir_path = log_dir_path
|
self.log_dir_path = Path(log_dir_path)
|
||||||
self.monkey_log_paths = []
|
self.monkey_log_paths: List[Path] = []
|
||||||
|
|
||||||
def download_monkey_logs(self):
|
def download_monkey_logs(self):
|
||||||
|
try:
|
||||||
LOGGER.info("Downloading each monkey log.")
|
LOGGER.info("Downloading each monkey log.")
|
||||||
all_monkeys = self.island_client.get_all_monkeys_from_db()
|
|
||||||
for monkey in all_monkeys:
|
|
||||||
downloaded_log_path = self._download_monkey_log(monkey)
|
|
||||||
if downloaded_log_path:
|
|
||||||
self.monkey_log_paths.append(downloaded_log_path)
|
|
||||||
|
|
||||||
def _download_monkey_log(self, monkey):
|
agents = self.island_client.get_agents()
|
||||||
log_handler = MonkeyLog(monkey, self.log_dir_path)
|
machines = self.island_client.get_machines()
|
||||||
download_successful = log_handler.download_log(self.island_client)
|
|
||||||
return log_handler.get_log_path_for_monkey(monkey) if download_successful else None
|
download_threads: List[Thread] = []
|
||||||
|
|
||||||
|
# TODO: Does downloading logs concurrently still improve performance after resolving
|
||||||
|
# https://github.com/guardicore/monkey/issues/2383?
|
||||||
|
for agent in agents:
|
||||||
|
t = Thread(target=self._download_log, args=(agent, machines), daemon=True)
|
||||||
|
t.start()
|
||||||
|
download_threads.append(t)
|
||||||
|
|
||||||
|
for thread in download_threads:
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
LOGGER.exception(err)
|
||||||
|
|
||||||
|
def _download_log(self, agent: Agent, machines: Mapping[MachineID, Machine]):
|
||||||
|
log_file_path = self._get_log_file_path(agent, machines)
|
||||||
|
log_contents = self.island_client.get_agent_log(agent.id)
|
||||||
|
|
||||||
|
MonkeyLogsDownloader._write_log_to_file(log_file_path, log_contents)
|
||||||
|
|
||||||
|
self.monkey_log_paths.append(log_file_path)
|
||||||
|
|
||||||
|
def _get_log_file_path(self, agent: Agent, machines: Mapping[MachineID, Machine]) -> Path:
|
||||||
|
try:
|
||||||
|
machine_ip = machines[agent.machine_id].network_interfaces[0].ip
|
||||||
|
except IndexError:
|
||||||
|
LOGGER.error(f"Machine with ID {agent.machine_id} has no network interfaces")
|
||||||
|
machine_ip = "UNKNOWN"
|
||||||
|
|
||||||
|
start_time = agent.start_time.strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
|
||||||
|
return self.log_dir_path / f"agent_{start_time}_{machine_ip}.log"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _write_log_to_file(log_file_path: Path, log_contents: str):
|
||||||
|
LOGGER.debug(f"Writing {len(log_contents)} bytes to {log_file_path}")
|
||||||
|
|
||||||
|
with open(log_file_path, "w") as f:
|
||||||
|
f.write(log_contents)
|
||||||
|
|
|
@ -22,8 +22,8 @@ class AgentLogs(AbstractResource):
|
||||||
try:
|
try:
|
||||||
log_contents = self._agent_log_repository.get_agent_log(agent_id)
|
log_contents = self._agent_log_repository.get_agent_log(agent_id)
|
||||||
except UnknownRecordError as err:
|
except UnknownRecordError as err:
|
||||||
logger.error(f"Error occurred while getting agent log: {err}")
|
logger.exception(f"Error occurred while getting agent log: {err}")
|
||||||
return {}, HTTPStatus.NOT_FOUND
|
return "", HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
return log_contents, HTTPStatus.OK
|
return log_contents, HTTPStatus.OK
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ def flask_client(build_flask_client):
|
||||||
def test_agent_logs_endpoint__get_empty(flask_client):
|
def test_agent_logs_endpoint__get_empty(flask_client):
|
||||||
resp = flask_client.get(AGENT_LOGS_URL_1, follow_redirects=True)
|
resp = flask_client.get(AGENT_LOGS_URL_1, follow_redirects=True)
|
||||||
assert resp.status_code == HTTPStatus.NOT_FOUND
|
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||||
assert resp.json == {}
|
assert resp.json == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
Loading…
Reference in New Issue