{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Requests and Responses\n",
    "\n",
    "© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK\n",
    "\n",
    "Agents interact with the PrimAITE simulation via the Request system.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sending a request"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's set up a minimal network simulation and send some requests to see how it works."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!primaite setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState\n",
    "from primaite.simulator.network.hardware.nodes.host.host_node import HostNode\n",
    "from primaite.simulator.sim_container import Simulation\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim = Simulation()\n",
    "sim.network.add_node(\n",
    "    HostNode(\n",
    "        hostname=\"client\",\n",
    "        ip_address='10.0.0.1',\n",
    "        subnet_mask='255.255.255.0',\n",
    "        operating_state=NodeOperatingState.ON)\n",
    ")\n",
    "client = sim.network.get_node_by_hostname('client')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A request is structured in a similar way to a command line interface - a list of strings with positional args. It's also possible to supply an optional `context` dictionary. We will craft a request that stops the pre-installed DNSClient service on the client node.\n",
    "\n",
    "First let's verify that the DNS Client is running on the client.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "client.software_manager.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Send a request to the simulator to stop the DNSClient."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = sim.apply_request(\n",
    "    request=[\"network\", \"node\", \"client\", \"service\", \"DNSClient\", \"stop\"],\n",
    "    context={}\n",
    "    )\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "The request returns a `RequestResponse` object which tells us that the request was successfully executed. Let's verify that the DNS client is in a stopped state now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"DNS Client state: {client.software_manager.software.get('DNSClient').operating_state.name}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unreachable requests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we attempt to send a request to something that doesn't exist, we will get an unreachable request status."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = sim.apply_request(\n",
    "    request=[\"network\", \"node\", \"client\", \"service\", \"NonExistentApplication\", \"stop\"],\n",
    "    context={}\n",
    "    )\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Failed requests\n",
    "\n",
    "Sometimes requests cannot be executed by the simulation. For example if we turn off the client node, we cannot execute the software that is running on it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = sim.apply_request(\n",
    "    request = [\"network\", \"node\", \"client\", \"shutdown\"],\n",
    "    context = {}\n",
    ")\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We need to apply timestep a few times for the client to go from `SHUTTING_DOWN` to `OFF` state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"client is in state: {client.operating_state.name}\")\n",
    "sim.apply_timestep(1)\n",
    "sim.apply_timestep(2)\n",
    "sim.apply_timestep(3)\n",
    "sim.apply_timestep(4)\n",
    "print(f\"client is in state: {client.operating_state.name}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, if we try to start the DNSClient back up, we get a failure because we cannot start software on a node that is turned off."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = sim.apply_request(\n",
    "    request=[\"network\", \"node\", \"client\", \"service\", \"DNSClient\", \"start\"],\n",
    "    context={}\n",
    "    )\n",
    "print(response)"
   ]
  }
 ],
 "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"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
