From f54f278fca6e71d48c5bf375a03933b8db200891 Mon Sep 17 00:00:00 2001 From: Marek Wolan Date: Thu, 21 Sep 2023 10:13:01 +0100 Subject: [PATCH] Initialise observations in agent interface --- sandbox.ipynb | 134 ++++++++++++++++++ src/primaite/game/__init__.py | 0 src/primaite/game/actor/__init__.py | 0 src/primaite/game/actor/actions.py | 21 +++ src/primaite/game/actor/interface.py | 32 +++++ src/primaite/game/actor/observations.py | 107 ++++++++++++++ src/primaite/game/actor/rewards.py | 20 +++ src/primaite/game/session.py | 6 + .../simulator/file_system/file_system.py | 16 ++- src/primaite/simulator/network/container.py | 4 +- 10 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 sandbox.ipynb create mode 100644 src/primaite/game/__init__.py create mode 100644 src/primaite/game/actor/__init__.py create mode 100644 src/primaite/game/actor/actions.py create mode 100644 src/primaite/game/actor/interface.py create mode 100644 src/primaite/game/actor/observations.py create mode 100644 src/primaite/game/actor/rewards.py create mode 100644 src/primaite/game/session.py diff --git a/sandbox.ipynb b/sandbox.ipynb new file mode 100644 index 00000000..e7db5f4c --- /dev/null +++ b/sandbox.ipynb @@ -0,0 +1,134 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.simulator.network.networks import arcd_uc2_network\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "net = arcd_uc2_network()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_node = list(net.nodes.keys())[0]\n", + "f = net.nodes[random_node].file_system.create_file(file_name=\"testfile\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f.describe_state()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test_file_observation():\n", + " from primaite.simulator.sim_container import Simulation\n", + " from primaite.simulator.network.hardware.nodes.computer import Computer\n", + " from primaite.game.actor.observations import FileObservation\n", + "\n", + " sim = Simulation()\n", + " pc = Computer(hostname=\"beep\", ip_address=\"123.123.123.123\", subnet_mask=\"255.255.255.0\")\n", + " sim.network.add_node(pc)\n", + " f = pc.file_system.create_file(file_name=\"dog.png\")\n", + "\n", + " dog_file_obs = FileObservation(where=['network','nodes',pc.uuid,'file_system'])\n", + " print(sim.describe_state())\n", + "test_file_observation()" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NIC' object has no attribute 'gateway'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/repos/PrimAITE/venv/lib/python3.10/site-packages/pydantic/main.py:718\u001b[0m, in \u001b[0;36mBaseModel.__getattr__\u001b[0;34m(self, item)\u001b[0m\n\u001b[1;32m 717\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 718\u001b[0m \u001b[39mreturn\u001b[39;00m pydantic_extra[item]\n\u001b[1;32m 719\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mKeyError\u001b[39;00m \u001b[39mas\u001b[39;00m exc:\n", + "\u001b[0;31mKeyError\u001b[0m: 'gateway'", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/cade/repos/PrimAITE/test.ipynb Cell 6\u001b[0m line \u001b[0;36m1\n\u001b[1;32m 7\u001b[0m sim\u001b[39m.\u001b[39mnetwork\u001b[39m.\u001b[39madd_node(pc)\n\u001b[1;32m 8\u001b[0m f \u001b[39m=\u001b[39m pc\u001b[39m.\u001b[39mfile_system\u001b[39m.\u001b[39mcreate_file(file_name\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mdog.png\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m---> 10\u001b[0m sim\u001b[39m.\u001b[39;49mdescribe_state()\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/sim_container.py:54\u001b[0m, in \u001b[0;36mSimulation.describe_state\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 44\u001b[0m \u001b[39mProduce a dictionary describing the current state of this object.\u001b[39;00m\n\u001b[1;32m 45\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[39m:rtype: Dict\u001b[39;00m\n\u001b[1;32m 50\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 51\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 52\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 53\u001b[0m {\n\u001b[0;32m---> 54\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mnetwork\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mnetwork\u001b[39m.\u001b[39;49mdescribe_state(),\n\u001b[1;32m 55\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mdomain\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdomain\u001b[39m.\u001b[39mdescribe_state(),\n\u001b[1;32m 56\u001b[0m }\n\u001b[1;32m 57\u001b[0m )\n\u001b[1;32m 58\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/network/container.py:166\u001b[0m, in \u001b[0;36mNetwork.describe_state\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 158\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 159\u001b[0m \u001b[39mProduce a dictionary describing the current state of the Network.\u001b[39;00m\n\u001b[1;32m 160\u001b[0m \n\u001b[1;32m 161\u001b[0m \u001b[39m:return: A dictionary capturing the current state of the Network and its child objects.\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 163\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 164\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 165\u001b[0m {\n\u001b[0;32m--> 166\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mnodes\u001b[39m\u001b[39m\"\u001b[39m: {uuid:node\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, node \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mnodes\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 167\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mlinks\u001b[39m\u001b[39m\"\u001b[39m: {uuid:link\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, link \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mlinks\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 168\u001b[0m }\n\u001b[1;32m 169\u001b[0m )\n\u001b[1;32m 170\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/network/container.py:166\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 158\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 159\u001b[0m \u001b[39mProduce a dictionary describing the current state of the Network.\u001b[39;00m\n\u001b[1;32m 160\u001b[0m \n\u001b[1;32m 161\u001b[0m \u001b[39m:return: A dictionary capturing the current state of the Network and its child objects.\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 163\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 164\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 165\u001b[0m {\n\u001b[0;32m--> 166\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mnodes\u001b[39m\u001b[39m\"\u001b[39m: {uuid:node\u001b[39m.\u001b[39;49mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, node \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mnodes\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 167\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mlinks\u001b[39m\u001b[39m\"\u001b[39m: {uuid:link\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, link \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mlinks\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 168\u001b[0m }\n\u001b[1;32m 169\u001b[0m )\n\u001b[1;32m 170\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/network/hardware/base.py:954\u001b[0m, in \u001b[0;36mNode.describe_state\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 941\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 942\u001b[0m \u001b[39mProduce a dictionary describing the current state of this object.\u001b[39;00m\n\u001b[1;32m 943\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 947\u001b[0m \u001b[39m:rtype: Dict\u001b[39;00m\n\u001b[1;32m 948\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 949\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 950\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 951\u001b[0m {\n\u001b[1;32m 952\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mhostname\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mhostname,\n\u001b[1;32m 953\u001b[0m \u001b[39m\"\u001b[39m\u001b[39moperating_state\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39moperating_state\u001b[39m.\u001b[39mvalue,\n\u001b[0;32m--> 954\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mNICs\u001b[39m\u001b[39m\"\u001b[39m: {uuid: nic\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, nic \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mnics\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 955\u001b[0m \u001b[39m# \"switch_ports\": {uuid, sp for uuid, sp in self.switch_ports.items()},\u001b[39;00m\n\u001b[1;32m 956\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mfile_system\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfile_system\u001b[39m.\u001b[39mdescribe_state(),\n\u001b[1;32m 957\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mapplications\u001b[39m\u001b[39m\"\u001b[39m: {uuid: app\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, app \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mapplications\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 958\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mservices\u001b[39m\u001b[39m\"\u001b[39m: {uuid: svc\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, svc \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mservices\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 959\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mprocess\u001b[39m\u001b[39m\"\u001b[39m: {uuid: proc\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, proc \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mprocesses\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 960\u001b[0m }\n\u001b[1;32m 961\u001b[0m )\n\u001b[1;32m 962\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/network/hardware/base.py:954\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 941\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 942\u001b[0m \u001b[39mProduce a dictionary describing the current state of this object.\u001b[39;00m\n\u001b[1;32m 943\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 947\u001b[0m \u001b[39m:rtype: Dict\u001b[39;00m\n\u001b[1;32m 948\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 949\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 950\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 951\u001b[0m {\n\u001b[1;32m 952\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mhostname\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mhostname,\n\u001b[1;32m 953\u001b[0m \u001b[39m\"\u001b[39m\u001b[39moperating_state\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39moperating_state\u001b[39m.\u001b[39mvalue,\n\u001b[0;32m--> 954\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mNICs\u001b[39m\u001b[39m\"\u001b[39m: {uuid: nic\u001b[39m.\u001b[39;49mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, nic \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mnics\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 955\u001b[0m \u001b[39m# \"switch_ports\": {uuid, sp for uuid, sp in self.switch_ports.items()},\u001b[39;00m\n\u001b[1;32m 956\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mfile_system\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfile_system\u001b[39m.\u001b[39mdescribe_state(),\n\u001b[1;32m 957\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mapplications\u001b[39m\u001b[39m\"\u001b[39m: {uuid: app\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, app \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mapplications\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 958\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mservices\u001b[39m\u001b[39m\"\u001b[39m: {uuid: svc\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, svc \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mservices\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 959\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mprocess\u001b[39m\u001b[39m\"\u001b[39m: {uuid: proc\u001b[39m.\u001b[39mdescribe_state() \u001b[39mfor\u001b[39;00m uuid, proc \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mprocesses\u001b[39m.\u001b[39mitems()},\n\u001b[1;32m 960\u001b[0m }\n\u001b[1;32m 961\u001b[0m )\n\u001b[1;32m 962\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/src/primaite/simulator/network/hardware/base.py:138\u001b[0m, in \u001b[0;36mNIC.describe_state\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 125\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 126\u001b[0m \u001b[39mProduce a dictionary describing the current state of this object.\u001b[39;00m\n\u001b[1;32m 127\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[39m:rtype: Dict\u001b[39;00m\n\u001b[1;32m 132\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 133\u001b[0m state \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdescribe_state()\n\u001b[1;32m 134\u001b[0m state\u001b[39m.\u001b[39mupdate(\n\u001b[1;32m 135\u001b[0m {\n\u001b[1;32m 136\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mip_adress\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mstr\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mip_address),\n\u001b[1;32m 137\u001b[0m \u001b[39m\"\u001b[39m\u001b[39msubnet_mask\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mstr\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39msubnet_mask),\n\u001b[0;32m--> 138\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mgateway\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mstr\u001b[39m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mgateway),\n\u001b[1;32m 139\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mmac_address\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmac_address,\n\u001b[1;32m 140\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mspeed\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mspeed,\n\u001b[1;32m 141\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mmtu\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmtu,\n\u001b[1;32m 142\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mwake_on_lan\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwake_on_lan,\n\u001b[1;32m 143\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mdns_servers\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mdns_servers,\n\u001b[1;32m 144\u001b[0m \u001b[39m\"\u001b[39m\u001b[39menabled\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39mself\u001b[39m\u001b[39m.\u001b[39menabled,\n\u001b[1;32m 145\u001b[0m }\n\u001b[1;32m 146\u001b[0m )\n\u001b[1;32m 147\u001b[0m \u001b[39mreturn\u001b[39;00m state\n", + "File \u001b[0;32m~/repos/PrimAITE/venv/lib/python3.10/site-packages/pydantic/main.py:720\u001b[0m, in \u001b[0;36mBaseModel.__getattr__\u001b[0;34m(self, item)\u001b[0m\n\u001b[1;32m 718\u001b[0m \u001b[39mreturn\u001b[39;00m pydantic_extra[item]\n\u001b[1;32m 719\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mKeyError\u001b[39;00m \u001b[39mas\u001b[39;00m exc:\n\u001b[0;32m--> 720\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mAttributeError\u001b[39;00m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00m\u001b[39mtype\u001b[39m(\u001b[39mself\u001b[39m)\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m\u001b[39m!r}\u001b[39;00m\u001b[39m object has no attribute \u001b[39m\u001b[39m{\u001b[39;00mitem\u001b[39m!r}\u001b[39;00m\u001b[39m'\u001b[39m) \u001b[39mfrom\u001b[39;00m \u001b[39mexc\u001b[39;00m\n\u001b[1;32m 721\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m 722\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mhasattr\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__class__\u001b[39m, item):\n", + "\u001b[0;31mAttributeError\u001b[0m: 'NIC' object has no attribute 'gateway'" + ] + } + ], + "source": [ + "from primaite.simulator.sim_container import Simulation\n", + "from primaite.simulator.network.hardware.nodes.computer import Computer\n", + "from primaite.game.actor.observations import FileObservation\n", + "\n", + "sim = Simulation()\n", + "pc = Computer(hostname=\"beep\", ip_address=\"123.123.123.123\", subnet_mask=\"255.255.255.0\")\n", + "sim.network.add_node(pc)\n", + "f = pc.file_system.create_file(file_name=\"dog.png\")\n", + "\n", + "sim.describe_state()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/game/__init__.py b/src/primaite/game/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/primaite/game/actor/__init__.py b/src/primaite/game/actor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/primaite/game/actor/actions.py b/src/primaite/game/actor/actions.py new file mode 100644 index 00000000..cefd9917 --- /dev/null +++ b/src/primaite/game/actor/actions.py @@ -0,0 +1,21 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict, List + +from pydantic import BaseModel + + +class AbstractAction(BaseModel): + @abstractmethod + def __call__(self, action: Any) -> List[str]: + """_summary_ + + :param action: _description_ + :type action: Any + :return: _description_ + :rtype: List[str] + """ + ... + + +class ActionSpace: + ... diff --git a/src/primaite/game/actor/interface.py b/src/primaite/game/actor/interface.py new file mode 100644 index 00000000..1fe43a32 --- /dev/null +++ b/src/primaite/game/actor/interface.py @@ -0,0 +1,32 @@ +# TODO: remove this comment... This is just here to point out that I've named this 'actor' rather than 'agent' +# That's because I want to point out that this is disctinct from 'agent' in the reinforcement learning sense of the word +# If you disagree, make a comment in the PR review and we can discuss +from abc import ABC, abstractmethod +from typing import Any, Dict, List + +from pydantic import BaseModel + +from primaite.game.actor.actions import ActionSpace +from primaite.game.actor.observations import ObservationSpace +from primaite.game.actor.rewards import RewardFunction + + +class AbstractActor(BaseModel): + """Base class for scripted and RL agents.""" + + ... + + +class AbstractScriptedActor(AbstractActor): + """Base class for actors which generate their own behaviour.""" + + ... + + +class AbstractPuppetActor(AbstractActor): + """Base class for actors controlled via external messages, such as RL policies.""" + + ... + + +# class AbstractRLActor(AbstractPuppetActor): ?? diff --git a/src/primaite/game/actor/observations.py b/src/primaite/game/actor/observations.py new file mode 100644 index 00000000..bcde1c8d --- /dev/null +++ b/src/primaite/game/actor/observations.py @@ -0,0 +1,107 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict, Hashable, List + +from pydantic import BaseModel + + +def access_from_nested_dict(dictionary: Dict, keys: List[Hashable]) -> Any: + """ + Access an item from a deeply dictionary with a list of keys. + + For example, if the dictionary is {1: 'a', 2: {3: {4: 'b'}}}, then the key [2, 3, 4] would return 'b', and the key + [2, 3] would return {4: 'b'}. Raises a KeyError if specified key does not exist at any level of nesting. + + :param dictionary: Deeply nested dictionary + :type dictionary: Dict + :param keys: List of dict keys used to traverse the nested dict. Each item corresponds to one level of depth. + :type keys: List[Hashable] + :return: The value in the dictionary + :rtype: Any + """ + if not keys: + return dictionary + k = keys.pop(0) + try: + return access_from_nested_dict(dictionary[k], keys) + except (TypeError, KeyError): + raise KeyError(f"Cannot find requested key `{k}` in nested dictionary") + + +class AbstractObservation(BaseModel): + @abstractmethod + def __call__(self, state: Dict) -> Any: + """_summary_ + + :param state: _description_ + :type state: Dict + :return: _description_ + :rtype: Any + """ + ... + # receive state dict + + +class FileObservation(AbstractObservation): + where: List[str] + """Store information about where in the simulation state dictionary to find the relevatn information.""" + + def __call__(self, state: Dict) -> Dict: + file_state = access_from_nested_dict(state, self.where) + + +class ObservationSpace: + """Manage the observations of an Actor.""" + + ... + # what this class does: + # keep a list of observations + # create observations for an actor from the config + + +# Example YAML file for agent observation space +""" +arcd_gate: + rl_framework: SB3 + rl_algo: PPO + n_learn_steps: 128 + n_learn_episodes: 1000 + +game_layer: + agents: + - ref: client_1_green_user + type: GREEN + node_ref: client_1 + service: WebBrowser + pol: + - step: 1 + action: START + + - ref: client_1_data_manip_red_bot + node_ref: client_1 + service: DataManipulationBot + execution_definition: + - server_ip_address: 192.168.1.10 + - server_password: + - payload: 'ATTACK' + + pol: + - step: 75 + action: EXECUTE + + + + +simulation: + nodes: + - ref: client_1 + hostname: client_1 + node_type: Computer + ip_address: 192.168.10.100 + services: + - name: DataManipulationBot + links: + endpoint_a: + endpoint_b: 1524552-fgfg4147gdh-25gh4gd +rewards: + +""" diff --git a/src/primaite/game/actor/rewards.py b/src/primaite/game/actor/rewards.py new file mode 100644 index 00000000..1db54176 --- /dev/null +++ b/src/primaite/game/actor/rewards.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict, List + +from pydantic import BaseModel + + +class AbstractReward(BaseModel): + def __call__(self, states: List[Dict]) -> float: + """_summary_ + + :param state: _description_ + :type state: Dict + :return: _description_ + :rtype: float + """ + ... + + +class RewardFunction(BaseModel): + ... diff --git a/src/primaite/game/session.py b/src/primaite/game/session.py new file mode 100644 index 00000000..47ef4ce9 --- /dev/null +++ b/src/primaite/game/session.py @@ -0,0 +1,6 @@ +# What do? Be an entry point for using PrimAITE +# 1. parse monoconfig +# 2. craete simulation +# 3. create actors and configure their actions/observations/rewards/ anything else +# 4. Create connection with ARCD GATE +# 5. idk diff --git a/src/primaite/simulator/file_system/file_system.py b/src/primaite/simulator/file_system/file_system.py index b2037729..a6d2689b 100644 --- a/src/primaite/simulator/file_system/file_system.py +++ b/src/primaite/simulator/file_system/file_system.py @@ -3,6 +3,7 @@ from __future__ import annotations import math import os.path import shutil +from enum import Enum from pathlib import Path from typing import Dict, Optional @@ -42,6 +43,14 @@ def convert_size(size_bytes: int) -> str: return f"{s} {size_name[i]}" +class FileSystemItemHealthStatus(Enum): + GOOD = 1 + COMPROMISED = 2 + CORRUPT = 3 + RESTORING = 4 + REPAIRING = 5 + + class FileSystemItemABC(SimComponent): """ Abstract base class for file system items used in the file system simulation. @@ -51,6 +60,7 @@ class FileSystemItemABC(SimComponent): name: str "The name of the FileSystemItemABC." + health_status: FileSystemItemHealthStatus = FileSystemItemHealthStatus.GOOD def describe_state(self) -> Dict: """ @@ -59,11 +69,7 @@ class FileSystemItemABC(SimComponent): :return: Current state of this object and child objects. """ state = super().describe_state() - state.update( - { - "name": self.name, - } - ) + state.update({"name": self.name, "health_status": self.health_status.value}) return state @property diff --git a/src/primaite/simulator/network/container.py b/src/primaite/simulator/network/container.py index c3a935b8..81ddf475 100644 --- a/src/primaite/simulator/network/container.py +++ b/src/primaite/simulator/network/container.py @@ -163,8 +163,8 @@ class Network(SimComponent): state = super().describe_state() state.update( { - "nodes": {i for i, node in self._node_id_map.items()}, - "links": {i: link.describe_state() for i, link in self._link_id_map.items()}, + "nodes": {uuid: node.describe_state() for uuid, node in self.nodes.items()}, + "links": {uuid: link.describe_state() for uuid, link in self.links.items()}, } ) return state