diff --git a/.gitignore b/.gitignore index 4bb700b2..5d6434f1 100644 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,7 @@ cython_debug/ # IDE .idea/ +docs/source/primaite-dependencies.rst # outputs src/primaite/outputs/ diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/_templates/custom-class-template.rst b/docs/_templates/custom-class-template.rst index 01e5299d..8a539bc9 100644 --- a/docs/_templates/custom-class-template.rst +++ b/docs/_templates/custom-class-template.rst @@ -9,7 +9,7 @@ :members: :show-inheritance: :inherited-members: - :special-members: __call__, __add__, __mul__ + :special-members: __init__, __call__, __add__, __mul__ {% block methods %} {% if methods %} diff --git a/docs/index.rst b/docs/index.rst index 1f465b2d..ca7aae51 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,6 +44,7 @@ The best place to start is :ref:`about` :caption: Project Links: :hidden: +.. #Code <> #Issues <> #Pull Requests <> diff --git a/docs/source/about.rst b/docs/source/about.rst index 1f4669fe..a599829a 100644 --- a/docs/source/about.rst +++ b/docs/source/about.rst @@ -186,7 +186,7 @@ Observation Spaces ****************** The observation space provides the blue agent with information about the current status of nodes and links. -PrimAITE builds on top of Gym Spaces to create an observation space that is easily configurable for users. It's made up of components which are managed by the :py:class:`primaite.environment.observations.ObservationHandler`. Each training scenario can define its own observation space, and the user can choose which information to inlude, and how it should be formatted. +PrimAITE builds on top of Gym Spaces to create an observation space that is easily configurable for users. It's made up of components which are managed by the :py:class:`primaite.environment.observations.ObservationsHandler`. Each training scenario can define its own observation space, and the user can choose which information to inlude, and how it should be formatted. NodeLinkTable component ----------------------- @@ -416,4 +416,3 @@ The PrimAITE project has an ambition to include the following enhancements in fu * Integration with a suitable standardised framework to allow multi-agent integration * Integration with external threat emulation tools, either using off-line data, or integrating at runtime -* Provision of data such that agents can construct alternative observation spaces (as an alternative to the default PrimAITE observation space) diff --git a/docs/source/config.rst b/docs/source/config.rst index c80baa3c..a28f0ec1 100644 --- a/docs/source/config.rst +++ b/docs/source/config.rst @@ -48,6 +48,41 @@ The environment config file consists of the following attributes: Determines whether a NODE, ACL, or ANY (combined NODE & ACL) action space format is adopted for the session +* **OBSERVATION_SPACE** [dict] + + Allows for user to configure observation space by combining one or more observation components. List of available + components is in :py:mod:`primaite.environment.observations`. + + The observation space config item should have a ``components`` key which is a list of components. Each component + config must have a ``name`` key, and can optionally have an ``options`` key. The ``options`` are passed to the + component while it is being initialised. + + This example illustrates the correct format for the observation space config item + + .. code-block:: yaml + + observation_space: + flatten: true + components: + - name: NODE_LINK_TABLE + - name: NODE_STATUSES + - name: LINK_TRAFFIC_LEVELS + options: + combine_service_traffic : False + quantisation_levels: 99 + + + Currently available components are: + + * :py:mod:`NODE_LINK_TABLE` this does not accept any additional options + * :py:mod:`NODE_STATUSES`, this does not accept any additional options + * :py:mod:`LINK_TRAFFIC_LEVELS`, this accepts the following options: + + * ``combine_service_traffic`` - whether to consider bandwidth use separately for each network protocol or combine them into a single bandwidth reading (boolean) + * ``quantisation_levels`` - how many discrete bandwidth usage levels to use for encoding. This can be an integer equal to or greater than 3. + + The other configurable item is ``flatten`` which is false by default. When set to true, the observation space is flattened (turned into a 1-D vector). You should use this if your RL agent does not natively support observation space types like ``gym.Spaces.Tuple``. + * **num_episodes** [int] This defines the number of episodes that the agent will train or be evaluated over. @@ -321,31 +356,6 @@ The Lay Down Config The lay down config file consists of the following attributes: -* **itemType: ACTIONS** [enum] - - Determines whether a NODE or ACL action space format is adopted for the session - -* **itemType: OBSERVATION_SPACE** [dict] - - Allows for user to configure observation space by combining one or more observation components. List of available - components is is :py:mod:'primaite.environment.observations'. - - The observation space config item should have a ``components`` key which is a list of components. Each component - config must have a ``name`` key, and can optionally have an ``options`` key. The ``options`` are passed to the - component while it is being initialised. - - This example illustrates the correct format for the observation space config item - -.. code-block::yaml - - - item_type: OBSERVATION_SPACE - components: - - name: LINK_TRAFFIC_LEVELS - options: - combine_service_traffic: false - quantisation_levels: 8 - - name: NODE_STATUSES - - name: LINK_TRAFFIC_LEVELS * **itemType: STEPS** [int] diff --git a/docs/source/primaite-dependencies.rst b/docs/source/primaite-dependencies.rst deleted file mode 100644 index 9ca8a03b..00000000 --- a/docs/source/primaite-dependencies.rst +++ /dev/null @@ -1,429 +0,0 @@ -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Name | Version | License | URL | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Babel | 2.11.0 | BSD License | https://babel.pocoo.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| DataProperty | 0.55.0 | MIT License | https://github.com/thombashi/DataProperty | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Jinja2 | 3.1.2 | BSD License | https://palletsprojects.com/p/jinja/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Mako | 1.2.4 | MIT License | https://www.makotemplates.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Markdown | 3.4.1 | BSD License | https://Python-Markdown.github.io/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| MarkupSafe | 2.1.2 | BSD License | https://palletsprojects.com/p/markupsafe/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Pillow | 9.4.0 | Historical Permission Notice and Disclaimer (HPND) | https://python-pillow.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| PyYAML | 6.0 | MIT License | https://pyyaml.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Pygments | 2.14.0 | BSD License | https://pygments.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| SQLAlchemy | 2.0.1 | MIT License | https://www.sqlalchemy.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Send2Trash | 1.8.0 | BSD License | https://github.com/arsenetar/send2trash | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Sphinx | 6.1.3 | BSD License | https://www.sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| Werkzeug | 2.2.3 | BSD License | https://palletsprojects.com/p/werkzeug/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| absl-py | 1.4.0 | Apache Software License | https://github.com/abseil/abseil-py | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| aiofiles | 22.1.0 | Apache Software License | https://github.com/Tinche/aiofiles | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| aiosqlite | 0.18.0 | MIT License | https://aiosqlite.omnilib.dev | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| alabaster | 0.7.13 | BSD License | https://alabaster.readthedocs.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| alembic | 1.9.2 | MIT License | https://alembic.sqlalchemy.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| anyio | 3.6.2 | MIT License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| argon2-cffi | 21.3.0 | MIT License | https://github.com/hynek/argon2-cffi/blob/main/CHANGELOG.md | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| argon2-cffi-bindings | 21.2.0 | MIT License | https://github.com/hynek/argon2-cffi-bindings | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| arrow | 1.2.3 | Apache Software License | https://arrow.readthedocs.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| astroid | 2.15.5 | GNU Lesser General Public License v2 (LGPLv2) | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| asttokens | 2.2.1 | Apache 2.0 | https://github.com/gristlabs/asttokens | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| astunparse | 1.6.3 | BSD License | https://github.com/simonpercivall/astunparse | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| attrs | 22.2.0 | MIT License | https://www.attrs.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| backcall | 0.2.0 | BSD License | https://github.com/takluyver/backcall | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| beautifulsoup4 | 4.11.2 | MIT License | https://www.crummy.com/software/BeautifulSoup/bs4/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| bleach | 6.0.0 | Apache Software License | https://github.com/mozilla/bleach | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| build | 0.10.0 | MIT License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cachetools | 5.3.0 | MIT License | https://github.com/tkem/cachetools/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| certifi | 2022.12.7 | Mozilla Public License 2.0 (MPL 2.0) | https://github.com/certifi/python-certifi | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cffi | 1.15.1 | MIT License | http://cffi.readthedocs.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cfgv | 3.3.1 | MIT License | https://github.com/asottile/cfgv | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| chardet | 5.1.0 | GNU Lesser General Public License v2 or later (LGPLv2+) | https://github.com/chardet/chardet | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| charset-normalizer | 3.0.1 | MIT License | https://github.com/Ousret/charset_normalizer | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cloudpickle | 2.2.1 | BSD License | https://github.com/cloudpipe/cloudpickle | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cmaes | 0.9.1 | MIT License | https://github.com/CyberAgentAILab/cmaes | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| colorama | 0.4.6 | BSD License | https://github.com/tartley/colorama | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| colorlog | 6.7.0 | MIT License | https://github.com/borntyping/python-colorlog | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| comm | 0.1.2 | BSD License | https://github.com/ipython/comm | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| contourpy | 1.0.7 | BSD License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| coverage | 7.2.6 | Apache Software License | https://github.com/nedbat/coveragepy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| cycler | 0.11.0 | BSD License | https://github.com/matplotlib/cycler | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| debugpy | 1.6.6 | Eclipse Public License 2.0 (EPL-2.0); MIT License | https://aka.ms/debugpy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| decorator | 5.1.1 | BSD License | https://github.com/micheles/decorator | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| defusedxml | 0.7.1 | Python Software Foundation License | https://github.com/tiran/defusedxml | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| dill | 0.3.6 | BSD License | https://github.com/uqfoundation/dill | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| distlib | 0.3.6 | Python Software Foundation License | https://github.com/pypa/distlib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| docutils | 0.19 | BSD License; GNU General Public License (GPL); Public Domain; Python Software Foundation License | https://docutils.sourceforge.io/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| exceptiongroup | 1.1.0 | MIT License | https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| executing | 1.2.0 | MIT License | https://github.com/alexmojaki/executing | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| fastjsonschema | 2.16.2 | BSD License | https://github.com/horejsek/python-fastjsonschema | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| filelock | 3.9.0 | The Unlicense (Unlicense) | https://github.com/tox-dev/py-filelock | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| flake8 | 6.0.0 | MIT License | https://github.com/pycqa/flake8 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| flatbuffers | 23.1.21 | Apache Software License | https://google.github.io/flatbuffers/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| fonttools | 4.38.0 | MIT License | http://github.com/fonttools/fonttools | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| fqdn | 1.5.1 | Mozilla Public License 2.0 (MPL 2.0) | https://github.com/ypcrts/fqdn | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| furo | 2023.3.27 | MIT License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| gast | 0.4.0 | BSD License | https://github.com/serge-sans-paille/gast/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| google-auth | 2.16.1 | Apache Software License | https://github.com/googleapis/google-auth-library-python | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| google-auth-oauthlib | 0.4.6 | Apache Software License | https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| google-pasta | 0.2.0 | Apache Software License | https://github.com/google/pasta | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| greenlet | 2.0.2 | MIT License | https://greenlet.readthedocs.io/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| grpcio | 1.51.3 | Apache Software License | https://grpc.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| gym | 0.21.0 | UNKNOWN | https://github.com/openai/gym | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| gym-notices | 0.0.8 | MIT License | https://github.com/Farama-Foundation/gym-notices | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| h5py | 3.8.0 | BSD License | https://www.h5py.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| huggingface-hub | 0.12.0 | Apache Software License | https://github.com/huggingface/huggingface_hub | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| huggingface-sb3 | 2.2.4 | Apache | https://github.com/huggingface/huggingface_sb3 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| identify | 2.5.22 | MIT License | https://github.com/pre-commit/identify | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| idna | 3.4 | BSD License | https://github.com/kjd/idna | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| imagesize | 1.4.1 | MIT License | https://github.com/shibukawa/imagesize_py | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| importlib | 1.0.4 | Python Software Foundation License | https://github.com/brettcannon/importlib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| importlib-metadata | 4.13.0 | Apache Software License | https://github.com/python/importlib_metadata | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| iniconfig | 2.0.0 | MIT License | https://github.com/pytest-dev/iniconfig | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| ipykernel | 6.21.1 | BSD License | https://ipython.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| ipython | 8.9.0 | BSD License | https://ipython.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| ipython-genutils | 0.2.0 | BSD License | http://ipython.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| isoduration | 20.11.0 | ISC License (ISCL) | https://github.com/bolsote/isoduration | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| isort | 5.12.0 | MIT License | https://pycqa.github.io/isort/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jedi | 0.18.2 | MIT License | https://github.com/davidhalter/jedi | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| json5 | 0.9.11 | Apache Software License | https://github.com/dpranke/pyjson5 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jsonpointer | 2.3 | BSD License | https://github.com/stefankoegl/python-json-pointer | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jsonschema | 4.17.3 | MIT License | https://github.com/python-jsonschema/jsonschema | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter-events | 0.5.0 | BSD License | http://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter-ydoc | 0.2.2 | BSD 3-Clause License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_client | 8.0.2 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_core | 5.2.0 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_server | 2.2.1 | BSD License | https://jupyter-server.readthedocs.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_server_fileid | 0.6.0 | BSD License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_server_terminals | 0.4.4 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyter_server_ydoc | 0.6.1 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyterlab | 3.6.0 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyterlab-pygments | 0.2.2 | BSD | https://github.com/jupyterlab/jupyterlab_pygments | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| jupyterlab_server | 2.19.0 | BSD License | https://jupyterlab-server.readthedocs.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| keras | 2.11.0 | Apache Software License | https://keras.io/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| keras-rl2 | 1.0.5 | MIT | https://github.com/wau/keras-rl2 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| kiwisolver | 1.4.4 | BSD License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| lazy-object-proxy | 1.9.0 | BSD License | https://github.com/ionelmc/python-lazy-object-proxy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| libclang | 15.0.6.1 | Apache Software License | https://github.com/sighingnow/libclang | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| lxml | 4.9.2 | BSD License | https://lxml.de/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| markdown-it-py | 2.1.0 | MIT License | https://github.com/executablebooks/markdown-it-py | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| matplotlib | 3.7.1 | Python Software Foundation License | https://matplotlib.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| matplotlib-inline | 0.1.6 | BSD 3-Clause | https://github.com/ipython/matplotlib-inline | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| mbstrdecoder | 1.1.1 | MIT License | https://github.com/thombashi/mbstrdecoder | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| mccabe | 0.7.0 | MIT License | https://github.com/pycqa/mccabe | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| mdurl | 0.1.2 | MIT License | https://github.com/executablebooks/mdurl | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| mistune | 2.0.4 | BSD License | https://github.com/lepture/mistune | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nbclassic | 0.5.1 | BSD License | https://github.com/jupyter/nbclassic | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nbclient | 0.7.2 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nbconvert | 7.2.9 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nbformat | 5.7.3 | BSD License | https://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nest-asyncio | 1.5.6 | BSD License | https://github.com/erdewit/nest_asyncio | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| networkx | 3.1 | BSD License | https://networkx.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| nodeenv | 1.7.0 | BSD License | https://github.com/ekalinin/nodeenv | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| notebook | 6.5.2 | BSD License | http://jupyter.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| notebook_shim | 0.2.2 | BSD License | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| numpy | 1.23.5 | BSD License | https://www.numpy.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| oauthlib | 3.2.2 | BSD License | https://github.com/oauthlib/oauthlib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| opt-einsum | 3.3.0 | MIT | https://github.com/dgasmith/opt_einsum | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| optuna | 3.1.0 | MIT License | https://optuna.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| packaging | 23.0 | Apache Software License; BSD License | https://github.com/pypa/packaging | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pandas | 1.5.3 | BSD License | https://pandas.pydata.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pandocfilters | 1.5.0 | BSD License | http://github.com/jgm/pandocfilters | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| parso | 0.8.3 | MIT License | https://github.com/davidhalter/parso | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pathvalidate | 2.5.2 | MIT License | https://github.com/thombashi/pathvalidate | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pickleshare | 0.7.5 | MIT License | https://github.com/pickleshare/pickleshare | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| platformdirs | 2.6.2 | MIT License | https://github.com/platformdirs/platformdirs | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pluggy | 1.0.0 | MIT License | https://github.com/pytest-dev/pluggy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| ppo | 0.1.1 | UNKNOWN | https://github.com/iffy/ppo | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pre-commit | 2.20.0 | MIT License | https://github.com/pre-commit/pre-commit | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| primaite | 2.0.0.dev0 | GFX | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| prometheus-client | 0.16.0 | Apache Software License | https://github.com/prometheus/client_python | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| prompt-toolkit | 3.0.36 | BSD License | https://github.com/prompt-toolkit/python-prompt-toolkit | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| protobuf | 3.19.6 | 3-Clause BSD License | https://developers.google.com/protocol-buffers/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| psutil | 5.9.4 | BSD License | https://github.com/giampaolo/psutil | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pure-eval | 0.2.2 | MIT License | http://github.com/alexmojaki/pure_eval | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyasn1 | 0.4.8 | BSD License | https://github.com/etingof/pyasn1 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyasn1-modules | 0.2.8 | BSD License | https://github.com/etingof/pyasn1-modules | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pycodestyle | 2.10.0 | MIT License | https://pycodestyle.pycqa.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pycparser | 2.21 | BSD License | https://github.com/eliben/pycparser | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyflakes | 3.0.1 | MIT License | https://github.com/PyCQA/pyflakes | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pylint | 2.17.4 | GNU General Public License v2 (GPLv2) | https://github.com/PyCQA/pylint/issues | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyparsing | 3.0.9 | MIT License | https://github.com/pyparsing/pyparsing/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyproject_hooks | 1.0.0 | MIT License | https://github.com/pypa/pyproject-hooks | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyrsistent | 0.19.3 | MIT License | https://github.com/tobgu/pyrsistent/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pytablewriter | 0.64.2 | MIT License | https://github.com/thombashi/pytablewriter | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pytest | 7.2.0 | MIT License | https://docs.pytest.org/en/latest/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pytest-cov | 4.0.0 | MIT License | https://github.com/pytest-dev/pytest-cov | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pytest-flake8 | 1.1.1 | BSD License | https://github.com/tholo/pytest-flake8 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| python-dateutil | 2.8.2 | Apache Software License; BSD License | https://github.com/dateutil/dateutil | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| python-json-logger | 2.0.4 | BSD License | http://github.com/madzak/python-json-logger | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pytz | 2022.7.1 | MIT License | http://pythonhosted.org/pytz | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pywin32 | 305 | Python Software Foundation License | https://github.com/mhammond/pywin32 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pywinpty | 2.0.10 | MIT | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| pyzmq | 25.0.0 | BSD License; GNU Library or Lesser General Public License (LGPL) | https://pyzmq.readthedocs.org | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| requests | 2.28.2 | Apache Software License | https://requests.readthedocs.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| requests-oauthlib | 1.3.1 | BSD License | https://github.com/requests/requests-oauthlib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| rfc3339-validator | 0.1.4 | MIT License | https://github.com/naimetti/rfc3339-validator | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| rfc3986-validator | 0.1.1 | MIT License | https://github.com/naimetti/rfc3986-validator | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| rich | 13.3.1 | MIT License | https://github.com/Textualize/rich | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| rl-zoo3 | 1.7.0 | MIT | https://github.com/DLR-RM/rl-baselines3-zoo | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| rsa | 4.9 | Apache Software License | https://stuvel.eu/rsa | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sb3-contrib | 1.7.0 | MIT | https://github.com/Stable-Baselines-Team/stable-baselines3-contrib | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| six | 1.16.0 | MIT License | https://github.com/benjaminp/six | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sniffio | 1.3.0 | Apache Software License; MIT License | https://github.com/python-trio/sniffio | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| snowballstemmer | 2.2.0 | BSD License | https://github.com/snowballstem/snowball | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| soupsieve | 2.3.2.post1 | MIT License | https://github.com/facelessuser/soupsieve | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinx-basic-ng | 1.0.0b1 | MIT License | https://github.com/pradyunsg/sphinx-basic-ng | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinx-code-tabs | 0.5.3 | The Unlicense (Unlicense) | https://github.com/coldfix/sphinx-code-tabs | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinx-copybutton | 0.5.2 | MIT License | https://github.com/executablebooks/sphinx-copybutton | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinx-rtd-theme | 1.1.1 | MIT License | https://github.com/readthedocs/sphinx_rtd_theme | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-applehelp | 1.0.4 | BSD License | https://www.sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-devhelp | 1.0.2 | BSD License | http://sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-htmlhelp | 2.0.1 | BSD License | https://www.sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-jsmath | 1.0.1 | BSD License | http://sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-qthelp | 1.0.3 | BSD License | http://sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| sphinxcontrib-serializinghtml | 1.1.5 | BSD License | http://sphinx-doc.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| stable-baselines3 | 1.6.2 | MIT | https://github.com/DLR-RM/stable-baselines3 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| stack-data | 0.6.2 | MIT License | http://github.com/alexmojaki/stack_data | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tabledata | 1.3.0 | MIT License | https://github.com/thombashi/tabledata | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tcolorpy | 0.1.2 | MIT License | https://github.com/thombashi/tcolorpy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorboard | 2.11.2 | Apache Software License | https://github.com/tensorflow/tensorboard | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorboard-data-server | 0.6.1 | Apache Software License | https://github.com/tensorflow/tensorboard/tree/master/tensorboard/data/server | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorboard-plugin-wit | 1.8.1 | Apache 2.0 | https://whatif-tool.dev | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorflow | 2.11.0 | Apache Software License | https://www.tensorflow.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorflow-estimator | 2.11.0 | Apache Software License | https://www.tensorflow.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorflow-intel | 2.11.0 | Apache Software License | https://www.tensorflow.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tensorflow-io-gcs-filesystem | 0.31.0 | Apache Software License | https://github.com/tensorflow/io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| termcolor | 2.2.0 | MIT License | https://github.com/termcolor/termcolor | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| terminado | 0.17.1 | BSD License | https://github.com/jupyter/terminado | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tinycss2 | 1.2.1 | BSD License | https://www.courtbouillon.org/tinycss2 | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| toml | 0.10.2 | MIT License | https://github.com/uiri/toml | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tomli | 2.0.1 | MIT License | https://github.com/hukkin/tomli | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tomlkit | 0.11.8 | MIT License | https://github.com/sdispater/tomlkit | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| torch | 1.13.1 | BSD License | https://pytorch.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tornado | 6.2 | Apache Software License | http://www.tornadoweb.org/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| tqdm | 4.64.1 | MIT License; Mozilla Public License 2.0 (MPL 2.0) | https://tqdm.github.io | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| traitlets | 5.9.0 | BSD License | https://github.com/ipython/traitlets | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| typepy | 1.3.0 | MIT License | https://github.com/thombashi/typepy | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| typing_extensions | 4.4.0 | Python Software Foundation License | https://github.com/python/typing_extensions/issues | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| uri-template | 1.2.0 | MIT License | https://github.com/plinss/uri_template/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| urllib3 | 1.26.14 | MIT License | https://urllib3.readthedocs.io/ | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| virtualenv | 20.21.0 | MIT License | https://github.com/pypa/virtualenv | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| wasabi | 1.1.1 | MIT | https://github.com/explosion/wasabi | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| webcolors | 1.12 | BSD License | https://github.com/ubernostrum/webcolors | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| webencodings | 0.5.1 | BSD License | https://github.com/SimonSapin/python-webencodings | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| websocket-client | 1.5.0 | Apache Software License | https://github.com/websocket-client/websocket-client.git | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| wrapt | 1.15.0 | BSD License | https://github.com/GrahamDumpleton/wrapt | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| y-py | 0.5.5 | UNKNOWN | UNKNOWN | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| ypy-websocket | 0.8.2 | UNKNOWN | https://github.com/y-crdt/ypy-websocket | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ -| zipp | 3.12.0 | MIT License | https://github.com/jaraco/zipp | -+-------------------------------+-------------+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------+ diff --git a/src/primaite/acl/__init__.py b/src/primaite/acl/__init__.py index 63f825c2..2623efbc 100644 --- a/src/primaite/acl/__init__.py +++ b/src/primaite/acl/__init__.py @@ -1 +1,2 @@ # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. +"""Access Control List. Models firewall functionality.""" diff --git a/src/primaite/acl/access_control_list.py b/src/primaite/acl/access_control_list.py index 3b0e9234..9a8444e5 100644 --- a/src/primaite/acl/access_control_list.py +++ b/src/primaite/acl/access_control_list.py @@ -9,20 +9,20 @@ class AccessControlList: """Access Control List class.""" def __init__(self): - """Init.""" - self.acl: Dict[str, AccessControlList] = {} # A dictionary of ACL Rules + """Initialise an empty AccessControlList.""" + self.acl: Dict[str, ACLRule] = {} # A dictionary of ACL Rules - def check_address_match(self, _rule, _source_ip_address, _dest_ip_address): - """ - Checks for IP address matches. + def check_address_match(self, _rule: ACLRule, _source_ip_address: str, _dest_ip_address: str) -> bool: + """Checks for IP address matches. - Args: - _rule: The rule being checked - _source_ip_address: the source IP address to compare - _dest_ip_address: the destination IP address to compare - - Returns: - True if match; False otherwise. + :param _rule: The rule object to check + :type _rule: ACLRule + :param _source_ip_address: Source IP address to compare + :type _source_ip_address: str + :param _dest_ip_address: Destination IP address to compare + :type _dest_ip_address: str + :return: True if there is a match, otherwise False. + :rtype: bool """ if ( (_rule.get_source_ip() == _source_ip_address and _rule.get_dest_ip() == _dest_ip_address) @@ -34,7 +34,7 @@ class AccessControlList: else: return False - def is_blocked(self, _source_ip_address, _dest_ip_address, _protocol, _port): + def is_blocked(self, _source_ip_address: str, _dest_ip_address: str, _protocol: str, _port: str) -> bool: """ Checks for rules that block a protocol / port. @@ -116,3 +116,27 @@ class AccessControlList: rule = ACLRule(_permission, _source_ip, _dest_ip, _protocol, str(_port)) hash_value = hash(rule) return hash_value + + def get_relevant_rules(self, _source_ip_address, _dest_ip_address, _protocol, _port): + """Get all ACL rules that relate to the given arguments. + + :param _source_ip_address: the source IP address to check + :param _dest_ip_address: the destination IP address to check + :param _protocol: the protocol to check + :param _port: the port to check + :return: Dictionary of all ACL rules that relate to the given arguments + :rtype: Dict[str, ACLRule] + """ + relevant_rules = {} + + for rule_key, rule_value in self.acl.items(): + if self.check_address_match(rule_value, _source_ip_address, _dest_ip_address): + if ( + rule_value.get_protocol() == _protocol or rule_value.get_protocol() == "ANY" or _protocol == "ANY" + ) and ( + str(rule_value.get_port()) == str(_port) or rule_value.get_port() == "ANY" or str(_port) == "ANY" + ): + # There's a matching rule. + relevant_rules[rule_key] = rule_value + + return relevant_rules diff --git a/src/primaite/acl/acl_rule.py b/src/primaite/acl/acl_rule.py index 05daecc4..a1fd93f2 100644 --- a/src/primaite/acl/acl_rule.py +++ b/src/primaite/acl/acl_rule.py @@ -7,14 +7,13 @@ class ACLRule: def __init__(self, _permission, _source_ip, _dest_ip, _protocol, _port): """ - Init. + Initialise an ACL Rule. - Args: - _permission: The permission (ALLOW or DENY) - _source_ip: The source IP address - _dest_ip: The destination IP address - _protocol: The rule protocol - _port: The rule port + :param _permission: The permission (ALLOW or DENY) + :param _source_ip: The source IP address + :param _dest_ip: The destination IP address + :param _protocol: The rule protocol + :param _port: The rule port """ self.permission = _permission self.source_ip = _source_ip diff --git a/src/primaite/agents/__init__.py b/src/primaite/agents/__init__.py index e69de29b..89580145 100644 --- a/src/primaite/agents/__init__.py +++ b/src/primaite/agents/__init__.py @@ -0,0 +1 @@ +"""Common interface between RL agents from different libraries and PrimAITE.""" diff --git a/src/primaite/agents/agent.py b/src/primaite/agents/agent.py index 32118597..a9bdfb1e 100644 --- a/src/primaite/agents/agent.py +++ b/src/primaite/agents/agent.py @@ -42,12 +42,21 @@ class AgentSessionABC(ABC): """ An ABC that manages training and/or evaluation of agents in PrimAITE. - This class cannot be directly instantiated and must be inherited from - with all implemented abstract methods implemented. + This class cannot be directly instantiated and must be inherited from with all implemented abstract methods + implemented. """ @abstractmethod def __init__(self, training_config_path, lay_down_config_path): + """ + Initialise an agent session from config files. + + :param training_config_path: YAML file containing configurable items defined in + `primaite.config.training_config.TrainingConfig` + :type training_config_path: Union[path, str] + :param lay_down_config_path: YAML file containing configurable items for generating network laydown. + :type lay_down_config_path: Union[path, str] + """ if not isinstance(training_config_path, Path): training_config_path = Path(training_config_path) self._training_config_path: Final[Union[Path, str]] = training_config_path @@ -55,7 +64,7 @@ class AgentSessionABC(ABC): if not isinstance(lay_down_config_path, Path): lay_down_config_path = Path(lay_down_config_path) - self._lay_down_config_path: Final[Union[Path]] = lay_down_config_path + self._lay_down_config_path: Final[Union[Path, str]] = lay_down_config_path self._lay_down_config: Dict = lay_down_config.load(self._lay_down_config_path) self.sb3_output_verbose_level = self._training_config.sb3_output_verbose_level @@ -305,11 +314,20 @@ class HardCodedAgentSessionABC(AgentSessionABC): """ An Agent Session ABC for evaluation deterministic agents. - This class cannot be directly instantiated and must be inherited from - with all implemented abstract methods implemented. + This class cannot be directly instantiated and must be inherited from with all implemented abstract methods + implemented. """ def __init__(self, training_config_path, lay_down_config_path): + """ + Initialise a hardcoded agent session. + + :param training_config_path: YAML file containing configurable items defined in + `primaite.config.training_config.TrainingConfig` + :type training_config_path: Union[path, str] + :param lay_down_config_path: YAML file containing configurable items for generating network laydown. + :type lay_down_config_path: Union[path, str] + """ super().__init__(training_config_path, lay_down_config_path) self._setup() diff --git a/src/primaite/agents/hardcoded_acl.py b/src/primaite/agents/hardcoded_acl.py index 263ccbdc..166ff415 100644 --- a/src/primaite/agents/hardcoded_acl.py +++ b/src/primaite/agents/hardcoded_acl.py @@ -1,5 +1,9 @@ +from typing import Any, Dict, List, Union + import numpy as np +from primaite.acl.access_control_list import AccessControlList +from primaite.acl.acl_rule import ACLRule from primaite.agents.agent import HardCodedAgentSessionABC from primaite.agents.utils import ( get_new_action, @@ -7,13 +11,17 @@ from primaite.agents.utils import ( transform_action_acl_enum, transform_change_obs_readable, ) +from primaite.common.custom_typing import NodeUnion from primaite.common.enums import HardCodedAgentView +from primaite.nodes.active_node import ActiveNode +from primaite.nodes.service_node import ServiceNode +from primaite.pol.ier import IER class HardCodedACLAgent(HardCodedAgentSessionABC): """An Agent Session class that implements a deterministic ACL agent.""" - def _calculate_action(self, obs): + def _calculate_action(self, obs: np.ndarray) -> int: if self._training_config.hard_coded_agent_view == HardCodedAgentView.BASIC: # Basic view action using only the current observation return self._calculate_action_basic_view(obs) @@ -22,12 +30,19 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): # history and reward feedback return self._calculate_action_full_view(obs) - def get_blocked_green_iers(self, green_iers, acl, nodes): - """ - Get blocked green IERs. + def get_blocked_green_iers( + self, green_iers: Dict[str, IER], acl: AccessControlList, nodes: Dict[str, NodeUnion] + ) -> Dict[Any, Any]: + """Get blocked green IERs. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param green_iers: Green IERs to check for being + :type green_iers: Dict[str, IER] + :param acl: Firewall rules + :type acl: AccessControlList + :param nodes: Nodes in the network + :type nodes: Dict[str,NodeUnion] + :return: Same as `green_iers` input dict, but filtered to only contain the blocked ones. + :rtype: Dict[str, IER] """ blocked_green_iers = {} @@ -45,12 +60,17 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): return blocked_green_iers - def get_matching_acl_rules_for_ier(self, ier, acl, nodes): - """ - Get matching ACL rules for an IER. + def get_matching_acl_rules_for_ier(self, ier: IER, acl: AccessControlList, nodes: Dict[str, NodeUnion]): + """Get list of ACL rules which are relevant to an IER. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param ier: Information Exchange Request to query against the ACL list + :type ier: IER + :param acl: Firewall rules + :type acl: AccessControlList + :param nodes: Nodes in the network + :type nodes: Dict[str,NodeUnion] + :return: _description_ + :rtype: _type_ """ source_node_id = ier.get_source_node_id() source_node_address = nodes[source_node_id].ip_address @@ -58,11 +78,12 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): dest_node_address = nodes[dest_node_id].ip_address protocol = ier.get_protocol() # e.g. 'TCP' port = ier.get_port() - matching_rules = acl.get_relevant_rules(source_node_address, dest_node_address, protocol, port) return matching_rules - def get_blocking_acl_rules_for_ier(self, ier, acl, nodes): + def get_blocking_acl_rules_for_ier( + self, ier: IER, acl: AccessControlList, nodes: Dict[str, NodeUnion] + ) -> Dict[str, Any]: """ Get blocking ACL rules for an IER. @@ -70,8 +91,14 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): Can return empty dict but IER can still be blocked by default (No ALLOW rule, therefore blocked). - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param ier: Information Exchange Request to query against the ACL list + :type ier: IER + :param acl: Firewall rules + :type acl: AccessControlList + :param nodes: Nodes in the network + :type nodes: Dict[str,NodeUnion] + :return: _description_ + :rtype: _type_ """ matching_rules = self.get_matching_acl_rules_for_ier(ier, acl, nodes) @@ -82,12 +109,19 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): return blocked_rules - def get_allow_acl_rules_for_ier(self, ier, acl, nodes): - """ - Get all allowing ACL rules for an IER. + def get_allow_acl_rules_for_ier( + self, ier: IER, acl: AccessControlList, nodes: Dict[str, NodeUnion] + ) -> Dict[str, Any]: + """Get all allowing ACL rules for an IER. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param ier: Information Exchange Request to query against the ACL list + :type ier: IER + :param acl: Firewall rules + :type acl: AccessControlList + :param nodes: Nodes in the network + :type nodes: Dict[str,NodeUnion] + :return: _description_ + :rtype: _type_ """ matching_rules = self.get_matching_acl_rules_for_ier(ier, acl, nodes) @@ -100,19 +134,32 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): def get_matching_acl_rules( self, - source_node_id, - dest_node_id, - protocol, - port, - acl, - nodes, - services_list, - ): - """ - Get matching ACL rules. + source_node_id: str, + dest_node_id: str, + protocol: str, + port: str, + acl: AccessControlList, + nodes: Dict[str, Union[ServiceNode, ActiveNode]], + services_list: List[str], + ) -> Dict[str, ACLRule]: + """Filter ACL rules to only those which are relevant to the specified nodes. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param source_node_id: Source node + :type source_node_id: str + :param dest_node_id: Destination nodes + :type dest_node_id: str + :param protocol: Network protocol + :type protocol: str + :param port: Network port + :type port: str + :param acl: Access Control list which will be filtered + :type acl: AccessControlList + :param nodes: The environment's node directory. + :type nodes: Dict[str, Union[ServiceNode, ActiveNode]] + :param services_list: List of services registered for the environment. + :type services_list: List[str] + :return: Filtered version of 'acl' + :rtype: Dict[str, ACLRule] """ if source_node_id != "ANY": source_node_address = nodes[str(source_node_id)].ip_address @@ -132,19 +179,33 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): def get_allow_acl_rules( self, - source_node_id, - dest_node_id, - protocol, - port, - acl, - nodes, - services_list, - ): - """ - Get the ALLOW ACL rules. + source_node_id: int, + dest_node_id: str, + protocol: int, + port: str, + acl: AccessControlList, + nodes: Dict[str, NodeUnion], + services_list: List[str], + ) -> Dict[str, ACLRule]: + """List ALLOW rules relating to specified nodes. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param source_node_id: Source node id + :type source_node_id: int + :param dest_node_id: Destination node + :type dest_node_id: str + :param protocol: Network protocol + :type protocol: int + :param port: Port + :type port: str + :param acl: Firewall ruleset which is applied to the network + :type acl: AccessControlList + :param nodes: The simulation's node store + :type nodes: Dict[str, NodeUnion] + :param services_list: Services list + :type services_list: List[str] + :return: Filtered ACL Rule directory which includes only those rules which affect the specified source and + desination nodes + :rtype: Dict[str, ACLRule] """ matching_rules = self.get_matching_acl_rules( source_node_id, @@ -165,19 +226,33 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): def get_deny_acl_rules( self, - source_node_id, - dest_node_id, - protocol, - port, - acl, - nodes, - services_list, - ): - """ - Get the DENY ACL rules. + source_node_id: int, + dest_node_id: str, + protocol: int, + port: str, + acl: AccessControlList, + nodes: Dict[str, NodeUnion], + services_list: List[str], + ) -> Dict[str, ACLRule]: + """List DENY rules relating to specified nodes. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param source_node_id: Source node id + :type source_node_id: int + :param dest_node_id: Destination node + :type dest_node_id: str + :param protocol: Network protocol + :type protocol: int + :param port: Port + :type port: str + :param acl: Firewall ruleset which is applied to the network + :type acl: AccessControlList + :param nodes: The simulation's node store + :type nodes: Dict[str, NodeUnion] + :param services_list: Services list + :type services_list: List[str] + :return: Filtered ACL Rule directory which includes only those rules which affect the specified source and + desination nodes + :rtype: Dict[str, ACLRule] """ matching_rules = self.get_matching_acl_rules( source_node_id, @@ -196,7 +271,7 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): return allowed_rules - def _calculate_action_full_view(self, obs): + def _calculate_action_full_view(self, obs: np.ndarray) -> int: """ Calculate a good acl-based action for the blue agent to take. @@ -224,8 +299,10 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): nodes once a service becomes overwhelmed. However currently the ACL action space has no way of reversing an overwhelmed state, so we don't do this. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: current observation from the gym environment + :type obs: np.ndarray + :return: Optimal action to take in the environment (chosen from the discrete action space) + :rtype: int """ # obs = convert_to_old_obs(obs) r_obs = transform_change_obs_readable(obs) @@ -361,8 +438,9 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): action = get_new_action(action, self._env.action_dict) return action - def _calculate_action_basic_view(self, obs): - """Calculate a good acl-based action for the blue agent to take. + def _calculate_action_basic_view(self, obs: np.ndarray) -> int: + """ + Calculate a good acl-based action for the blue agent to take. Uses ONLY information from the current observation with NO knowledge of previous actions taken and NO reward feedback. @@ -379,8 +457,10 @@ class HardCodedACLAgent(HardCodedAgentSessionABC): Currently, a deny rule does not overwrite an allow rule. The allow rules must be deleted. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: current observation from the gym environment + :type obs: np.ndarray + :return: Optimal action to take in the environment (chosen from the discrete action space) + :rtype: int """ action_dict = self._env.action_dict r_obs = transform_change_obs_readable(obs) diff --git a/src/primaite/agents/hardcoded_node.py b/src/primaite/agents/hardcoded_node.py index 310fc178..c00cf421 100644 --- a/src/primaite/agents/hardcoded_node.py +++ b/src/primaite/agents/hardcoded_node.py @@ -1,3 +1,5 @@ +import numpy as np + from primaite.agents.agent import HardCodedAgentSessionABC from primaite.agents.utils import get_new_action, transform_action_node_enum, transform_change_obs_readable @@ -5,12 +7,14 @@ from primaite.agents.utils import get_new_action, transform_action_node_enum, tr class HardCodedNodeAgent(HardCodedAgentSessionABC): """An Agent Session class that implements a deterministic Node agent.""" - def _calculate_action(self, obs): + def _calculate_action(self, obs: np.ndarray) -> int: """ Calculate a good node-based action for the blue agent to take. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: current observation from the gym environment + :type obs: np.ndarray + :return: Optimal action to take in the environment (chosen from the discrete action space) + :rtype: int """ action_dict = self._env.action_dict r_obs = transform_change_obs_readable(obs) diff --git a/src/primaite/agents/rllib.py b/src/primaite/agents/rllib.py index 0bc41762..19939af8 100644 --- a/src/primaite/agents/rllib.py +++ b/src/primaite/agents/rllib.py @@ -44,6 +44,18 @@ class RLlibAgent(AgentSessionABC): """An AgentSession class that implements a Ray RLlib agent.""" def __init__(self, training_config_path, lay_down_config_path): + """ + Initialise the RLLib Agent training session. + + :param training_config_path: YAML file containing configurable items defined in + `primaite.config.training_config.TrainingConfig` + :type training_config_path: Union[path, str] + :param lay_down_config_path: YAML file containing configurable items for generating network laydown. + :type lay_down_config_path: Union[path, str] + :raises ValueError: If the training config contains an unexpected value for agent_framework (should be "RLLIB") + :raises ValueError: If the training config contains an unexpected value for agent_identifies (should be `PPO` + or `A2C`) + """ super().__init__(training_config_path, lay_down_config_path) if not self._training_config.agent_framework == AgentFramework.RLLIB: msg = f"Expected RLLIB agent_framework, " f"got {self._training_config.agent_framework}" diff --git a/src/primaite/agents/sb3.py b/src/primaite/agents/sb3.py index aa8e312d..885ff956 100644 --- a/src/primaite/agents/sb3.py +++ b/src/primaite/agents/sb3.py @@ -19,6 +19,18 @@ class SB3Agent(AgentSessionABC): """An AgentSession class that implements a Stable Baselines3 agent.""" def __init__(self, training_config_path, lay_down_config_path): + """ + Initialise the SB3 Agent training session. + + :param training_config_path: YAML file containing configurable items defined in + `primaite.config.training_config.TrainingConfig` + :type training_config_path: Union[path, str] + :param lay_down_config_path: YAML file containing configurable items for generating network laydown. + :type lay_down_config_path: Union[path, str] + :raises ValueError: If the training config contains an unexpected value for agent_framework (should be "SB3") + :raises ValueError: If the training config contains an unexpected value for agent_identifies (should be `PPO` + or `A2C`) + """ super().__init__(training_config_path, lay_down_config_path) if not self._training_config.agent_framework == AgentFramework.SB3: msg = f"Expected SB3 agent_framework, " f"got {self._training_config.agent_framework}" diff --git a/src/primaite/agents/simple.py b/src/primaite/agents/simple.py index 5a6c9da5..b429a2f5 100644 --- a/src/primaite/agents/simple.py +++ b/src/primaite/agents/simple.py @@ -17,8 +17,7 @@ class DummyAgent(HardCodedAgentSessionABC): """ A Dummy Agent. - All action spaces setup so dummy action is always 0 regardless of action - type used. + All action spaces setup so dummy action is always 0 regardless of action type used. """ def _calculate_action(self, obs): diff --git a/src/primaite/agents/utils.py b/src/primaite/agents/utils.py index 8c59faf7..fdf061ab 100644 --- a/src/primaite/agents/utils.py +++ b/src/primaite/agents/utils.py @@ -1,5 +1,8 @@ +from typing import Dict, List, Union + import numpy as np +from primaite.common.custom_typing import NodeUnion from primaite.common.enums import ( HardwareState, LinkStatus, @@ -10,15 +13,17 @@ from primaite.common.enums import ( ) -def transform_action_node_readable(action): - """ - Convert a node action from enumerated format to readable format. +def transform_action_node_readable(action: List[int]) -> List[Union[int, str]]: + """Convert a node action from enumerated format to readable format. example: [1, 3, 1, 0] -> [1, 'SERVICE', 'PATCHING', 0] - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Agent action, formatted as a list of ints, for more information check out + `primaite.environment.primaite_env.Primaite` + :type action: List[int] + :return: The same action list, but with the encodings translated back into meaningful labels + :rtype: List[Union[int,str]] """ action_node_property = NodePOLType(action[1]).name @@ -33,15 +38,18 @@ def transform_action_node_readable(action): return new_action -def transform_action_acl_readable(action): +def transform_action_acl_readable(action: List[str]) -> List[Union[str, int]]: """ Transform an ACL action to a more readable format. example: [0, 1, 2, 5, 0, 1] -> ['NONE', 'ALLOW', 2, 5, 'ANY', 1] - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Agent action, formatted as a list of ints, for more information check out + `primaite.environment.primaite_env.Primaite` + :type action: List[int] + :return: The same action list, but with the encodings translated back into meaningful labels + :rtype: List[Union[int,str]] """ action_decisions = {0: "NONE", 1: "CREATE", 2: "DELETE"} action_permissions = {0: "DENY", 1: "ALLOW"} @@ -58,8 +66,9 @@ def transform_action_acl_readable(action): return new_action -def is_valid_node_action(action): - """Is the node action an actual valid action. +def is_valid_node_action(action: List[int]) -> bool: + """ + Is the node action an actual valid action. Only uses information about the action to determine if the action has an effect @@ -67,8 +76,11 @@ def is_valid_node_action(action): - Node ID not valid to perform an operation - e.g. selected node has no service so cannot patch - Node already being in that state (turning an ON node ON) - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Agent action, formatted as a list of ints, for more information check out + `primaite.environment.primaite_env.Primaite` + :type action: List[int] + :return: Whether the action is valid + :rtype: bool """ action_r = transform_action_node_readable(action) @@ -93,7 +105,7 @@ def is_valid_node_action(action): return True -def is_valid_acl_action(action): +def is_valid_acl_action(action: List[int]) -> bool: """ Is the ACL action an actual valid action. @@ -103,8 +115,11 @@ def is_valid_acl_action(action): - Trying to create identical rules - Trying to create a rule which is a subset of another rule (caused by "ANY") - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Agent action, formatted as a list of ints, for more information check out + `primaite.environment.primaite_env.Primaite` + :type action: List[int] + :return: Whether the action is valid + :rtype: bool """ action_r = transform_action_acl_readable(action) @@ -126,12 +141,15 @@ def is_valid_acl_action(action): return True -def is_valid_acl_action_extra(action): +def is_valid_acl_action_extra(action: List[int]) -> bool: """ Harsher version of valid acl actions, does not allow action. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Agent action, formatted as a list of ints, for more information check out + `primaite.environment.primaite_env.Primaite` + :type action: List[int] + :return: Whether the action is valid + :rtype: bool """ if is_valid_acl_action(action) is False: return False @@ -150,22 +168,24 @@ def is_valid_acl_action_extra(action): return True -def transform_change_obs_readable(obs): - """ - Transform list of transactions to readable list of each observation property. +def transform_change_obs_readable(obs: np.ndarray) -> List[List[Union[str, int]]]: + """Transform list of transactions to readable list of each observation property. example: np.array([[1,2,1,3],[2,1,1,1]]) -> [[1, 2], ['OFF', 'ON'], ['GOOD', 'GOOD'], ['COMPROMISED', 'GOOD']] - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: Raw observation from the environment. + :type obs: np.ndarray + :return: The same observation, but the encoded integer values are replaced with readable names. + :rtype: List[List[Union[str, int]]] """ ids = [i for i in obs[:, 0]] operating_states = [HardwareState(i).name for i in obs[:, 1]] os_states = [SoftwareState(i).name for i in obs[:, 2]] new_obs = [ids, operating_states, os_states] - for service in range(3, obs.shape[1]): + # changed range(3,...) to range(4,...) because we added file system which was new since ADSP + for service in range(4, obs.shape[1]): # Links bit/s don't have a service state service_states = [SoftwareState(i).name if i <= 4 else i for i in obs[:, service]] new_obs.append(service_states) @@ -173,14 +193,16 @@ def transform_change_obs_readable(obs): return new_obs -def transform_obs_readable(obs): - """ - Transform observation to readable format. +def transform_obs_readable(obs: np.ndarray) -> List[List[Union[str, int]]]: + """Transform observation to readable format. + example np.array([[1,2,1,3],[2,1,1,1]]) -> [[1, 'OFF', 'GOOD', 'COMPROMISED'], [2, 'ON', 'GOOD', 'GOOD']] - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: Raw observation from the environment. + :type obs: np.ndarray + :return: The same observation, but the encoded integer values are replaced with readable names. + :rtype: List[List[Union[str, int]]] """ changed_obs = transform_change_obs_readable(obs) new_obs = list(zip(*changed_obs)) @@ -190,21 +212,23 @@ def transform_obs_readable(obs): return new_obs -def convert_to_new_obs(obs, num_nodes=10): - """ - Convert original gym Box observation space to new multiDiscrete observation space. +def convert_to_new_obs(obs: np.ndarray, num_nodes: int = 10) -> np.ndarray: + """Convert original gym Box observation space to new multiDiscrete observation space. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs: observation in the 'old' (NodeLinkTable) format + :type obs: np.ndarray + :param num_nodes: number of nodes in the network, defaults to 10 + :type num_nodes: int, optional + :return: reformatted observation + :rtype: np.ndarray """ # Remove ID columns, remove links and flatten to MultiDiscrete observation space new_obs = obs[:num_nodes, 1:].flatten() return new_obs -def convert_to_old_obs(obs, num_nodes=10, num_links=10, num_services=1): - """ - Convert to old observation. +def convert_to_old_obs(obs: np.ndarray, num_nodes: int = 10, num_links: int = 10, num_services: int = 1) -> np.ndarray: + """Convert to old observation. Links filled with 0's as no information is included in new observation space. @@ -216,8 +240,17 @@ def convert_to_old_obs(obs, num_nodes=10, num_links=10, num_services=1): [ 3, 1, 1, 1], ... [20, 0, 0, 0]]) - TODO: Add params and return in docstring. - TODO: Typehint params and return. + + :param obs: observation in the 'new' (MultiDiscrete) format + :type obs: np.ndarray + :param num_nodes: number of nodes in the network, defaults to 10 + :type num_nodes: int, optional + :param num_links: number of links in the network, defaults to 10 + :type num_links: int, optional + :param num_services: number of services on the network, defaults to 1 + :type num_services: int, optional + :return: 2-d BOX observation space, in the same format as NodeLinkTable + :rtype: np.ndarray """ # Convert back to more readable, original format reshaped_nodes = obs[:-num_links].reshape(num_nodes, num_services + 2) @@ -239,17 +272,28 @@ def convert_to_old_obs(obs, num_nodes=10, num_links=10, num_services=1): return new_obs -def describe_obs_change(obs1, obs2, num_nodes=10, num_links=10, num_services=1): - """ - Return string describing change between two observations. +def describe_obs_change( + obs1: np.ndarray, obs2: np.ndarray, num_nodes: int = 10, num_links: int = 10, num_services: int = 1 +) -> str: + """Build a string describing the difference between two observations. example: obs_1 = array([[1, 1, 1, 1, 3], [2, 1, 1, 1, 1]]) obs_2 = array([[1, 1, 1, 1, 1], [2, 1, 1, 1, 1]]) output = 'ID 1: SERVICE 2 set to GOOD' - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs1: First observation + :type obs1: np.ndarray + :param obs2: Second observation + :type obs2: np.ndarray + :param num_nodes: How many nodes are in the network laydown, defaults to 10 + :type num_nodes: int, optional + :param num_links: How many links are in the network laydown, defaults to 10 + :type num_links: int, optional + :param num_services: How many services are configured for this scenario, defaults to 1 + :type num_services: int, optional + :return: A multi-line string with a human-readable description of the difference. + :rtype: str """ obs1 = convert_to_old_obs(obs1, num_nodes, num_links, num_services) obs2 = convert_to_old_obs(obs2, num_nodes, num_links, num_services) @@ -268,7 +312,7 @@ def describe_obs_change(obs1, obs2, num_nodes=10, num_links=10, num_services=1): return change_string -def _describe_obs_change_helper(obs_change, is_link): +def _describe_obs_change_helper(obs_change: List[int], is_link: bool) -> str: """ Helper funcion to describe what has changed. @@ -277,8 +321,14 @@ def _describe_obs_change_helper(obs_change, is_link): Handles multiple changes e.g. 'ID 1: SERVICE 1 changed to PATCHING. SERVICE 2 set to GOOD.' - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param obs_change: List of integers generated within the `describe_obs_change` function. It should correspond to one + row of the observation table, and have `-1` at locations where the observation hasn't changed, and the new + status where it has changed. + :type obs_change: List[int] + :param is_link: Whether the row of the observation space corresponds to a link. False means it represents a node. + :type is_link: bool + :return: A human-readable description of the difference between the two observation rows. + :rtype: str """ # Indexes where a change has occured, not including 0th index index_changed = [i for i in range(1, len(obs_change)) if obs_change[i] != -1] @@ -304,15 +354,15 @@ def _describe_obs_change_helper(obs_change, is_link): return desc -def transform_action_node_enum(action): - """ - Convert a node action from readable string format, to enumerated format. +def transform_action_node_enum(action: List[Union[str, int]]) -> List[int]: + """Convert a node action from readable string format, to enumerated format. example: [1, 'SERVICE', 'PATCHING', 0] -> [1, 3, 1, 0] - - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: Action in 'readable' format + :type action: List[Union[str,int]] + :return: Action with verbs encoded as ints + :rtype: List[int] """ action_node_id = action[0] action_node_property = NodePOLType[action[1]].value @@ -336,63 +386,14 @@ def transform_action_node_enum(action): return new_action -def transform_action_node_readable(action): - """ - Convert a node action from enumerated format to readable format. - - example: - [1, 3, 1, 0] -> [1, 'SERVICE', 'PATCHING', 0] - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - action_node_property = NodePOLType(action[1]).name - - if action_node_property == "OPERATING": - property_action = NodeHardwareAction(action[2]).name - elif (action_node_property == "OS" or action_node_property == "SERVICE") and action[2] <= 1: - property_action = NodeSoftwareAction(action[2]).name - else: - property_action = "NONE" - - new_action = [action[0], action_node_property, property_action, action[3]] - return new_action - - -def node_action_description(action): - """ - Generate string describing a node-based action. - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - if isinstance(action[1], (int, np.int64)): - # transform action to readable format - action = transform_action_node_readable(action) - - node_id = action[0] - node_property = action[1] - property_action = action[2] - service_id = action[3] - - if property_action == "NONE": - return "" - if node_property == "OPERATING" or node_property == "OS": - description = f"NODE {node_id}, {node_property}, SET TO {property_action}" - elif node_property == "SERVICE": - description = f"NODE {node_id} FROM SERVICE {service_id}, SET TO {property_action}" - else: - return "" - - return description - - -def transform_action_acl_enum(action): +def transform_action_acl_enum(action: List[Union[int, str]]) -> np.ndarray: """ Convert acl action from readable str format, to enumerated format. - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param action: ACL-based action expressed as a list of human-readable ints and strings + :type action: List[Union[int,str]] + :return: The same action but encoded to contain only integers. + :rtype: np.ndarray """ action_decisions = {"NONE": 0, "CREATE": 1, "DELETE": 2} action_permissions = {"DENY": 0, "ALLOW": 1} @@ -410,35 +411,17 @@ def transform_action_acl_enum(action): return new_action -def acl_action_description(action): - """ - Generate string describing an acl-based action. - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - if isinstance(action[0], (int, np.int64)): - # transform action to readable format - action = transform_action_acl_readable(action) - if action[0] == "NONE": - description = "NO ACL RULE APPLIED" - else: - description = ( - f"{action[0]} RULE: {action[1]} traffic from IP {action[2]} to IP {action[3]}," - f" for protocol/service index {action[4]} on port index {action[5]}" - ) - - return description - - -def get_node_of_ip(ip, node_dict): - """ - Get the node ID of an IP address. +def get_node_of_ip(ip: str, node_dict: Dict[str, NodeUnion]) -> str: + """Get the node ID of an IP address. node_dict: dictionary of nodes where key is ID, and value is the node (can be ontained from env.nodes) - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param ip: The IP address of the node whose ID is required + :type ip: str + :param node_dict: The environment's node registry dictionary + :type node_dict: Dict[str,NodeUnion] + :return: The key from the registry dict that corresponds to the node with the IP adress provided by `ip` + :rtype: str """ for node_key, node_value in node_dict.items(): node_ip = node_value.ip_address @@ -446,104 +429,18 @@ def get_node_of_ip(ip, node_dict): return node_key -def is_valid_node_action(action): - """Is the node action an actual valid action. - - Only uses information about the action to determine if the action has an effect - - Does NOT consider: - - Node ID not valid to perform an operation - e.g. selected node has no service so cannot patch - - Node already being in that state (turning an ON node ON) - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - action_r = transform_action_node_readable(action) - - node_property = action_r[1] - node_action = action_r[2] - - if node_property == "NONE": - return False - if node_action == "NONE": - return False - if node_property == "OPERATING" and node_action == "PATCHING": - # Operating State cannot PATCH - return False - if node_property != "OPERATING" and node_action not in [ - "NONE", - "PATCHING", - ]: - # Software States can only do Nothing or Patch - return False - return True - - -def is_valid_acl_action(action): - """ - Is the ACL action an actual valid action. - - Only uses information about the action to determine if the action has an effect - - Does NOT consider: - - Trying to create identical rules - - Trying to create a rule which is a subset of another rule (caused by "ANY") - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - action_r = transform_action_acl_readable(action) - - action_decision = action_r[0] - action_permission = action_r[1] - action_source_id = action_r[2] - action_destination_id = action_r[3] - - if action_decision == "NONE": - return False - if action_source_id == action_destination_id and action_source_id != "ANY" and action_destination_id != "ANY": - # ACL rule towards itself - return False - if action_permission == "DENY": - # DENY is unnecessary, we can create and delete allow rules instead - # No allow rule = blocked/DENY by feault. ALLOW overrides existing DENY. - return False - - return True - - -def is_valid_acl_action_extra(action): - """ - Harsher version of valid acl actions, does not allow action. - - TODO: Add params and return in docstring. - TODO: Typehint params and return. - """ - if is_valid_acl_action(action) is False: - return False - - action_r = transform_action_acl_readable(action) - action_protocol = action_r[4] - action_port = action_r[5] - - # Don't allow protocols or ports to be ANY - # in the future we might want to do the opposite, and only have ANY option for ports and service - if action_protocol == "ANY": - return False - if action_port == "ANY": - return False - - return True - - -def get_new_action(old_action, action_dict): +def get_new_action(old_action: np.ndarray, action_dict: Dict[int, List]) -> int: """ Get new action (e.g. 32) from old action e.g. [1,1,1,0]. Old_action can be either node or acl action type - TODO: Add params and return in docstring. - TODO: Typehint params and return. + :param old_action: Action expressed as a list of choices, eg. [1,1,1,0] + :type old_action: np.ndarray + :param action_dict: Dictionary for translating the multidiscrete actions into the list-based actions. + :type action_dict: Dict[int,List] + :return: Action key correspoinding to the input `old_action` + :rtype: int """ for key, val in action_dict.items(): if list(val) == list(old_action): diff --git a/src/primaite/common/__init__.py b/src/primaite/common/__init__.py index 63f825c2..5f47b0b5 100644 --- a/src/primaite/common/__init__.py +++ b/src/primaite/common/__init__.py @@ -1 +1,2 @@ # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. +"""Objects which are shared between many PrimAITE modules.""" diff --git a/src/primaite/common/protocol.py b/src/primaite/common/protocol.py index 2e3683e8..ad6a1d83 100644 --- a/src/primaite/common/protocol.py +++ b/src/primaite/common/protocol.py @@ -7,10 +7,10 @@ class Protocol(object): def __init__(self, _name): """ - Init. + Initialise a protocol. - Args: - _name: The protocol name + :param _name: The name of the protocol + :type _name: str """ self.name = _name self.load = 0 # bps diff --git a/src/primaite/common/service.py b/src/primaite/common/service.py index 51403879..258ac8f9 100644 --- a/src/primaite/common/service.py +++ b/src/primaite/common/service.py @@ -9,7 +9,7 @@ class Service(object): def __init__(self, name: str, port: str, software_state: SoftwareState): """ - Init. + Initialise a service. :param name: The service name. :param port: The service port. diff --git a/src/primaite/config/__init__.py b/src/primaite/config/__init__.py index e69de29b..03ed4cf1 100644 --- a/src/primaite/config/__init__.py +++ b/src/primaite/config/__init__.py @@ -0,0 +1 @@ +"""Configuration parameters for running experiments.""" diff --git a/src/primaite/config/lay_down_config.py b/src/primaite/config/lay_down_config.py index 08f77b2f..3a85b9da 100644 --- a/src/primaite/config/lay_down_config.py +++ b/src/primaite/config/lay_down_config.py @@ -26,8 +26,7 @@ def load(file_path: Union[str, Path], legacy_file: bool = False) -> Dict: Read in a lay down config yaml file. :param file_path: The config file path. - :param legacy_file: True if the config file is legacy format, otherwise - False. + :param legacy_file: True if the config file is legacy format, otherwise False. :return: The lay down config as a dict. :raises ValueError: If the file_path does not exist. """ diff --git a/src/primaite/config/training_config.py b/src/primaite/config/training_config.py index e7b701c7..8d38c0ef 100644 --- a/src/primaite/config/training_config.py +++ b/src/primaite/config/training_config.py @@ -291,14 +291,11 @@ def convert_legacy_training_config_dict( Convert a legacy training config dict to the new format. :param legacy_config_dict: A legacy training config dict. - :param agent_framework: The agent framework to use as legacy training - configs don't have agent_framework values. - :param agent_identifier: The red agent identifier to use as legacy - training configs don't have agent_identifier values. - :param action_type: The action space type to set as legacy training configs - don't have action_type values. - :param num_steps: The number of steps to set as legacy training configs - don't have num_steps values. + :param agent_framework: The agent framework to use as legacy training configs don't have agent_framework values. + :param agent_identifier: The red agent identifier to use as legacy training configs don't have agent_identifier + values. + :param action_type: The action space type to set as legacy training configs don't have action_type values. + :param num_steps: The number of steps to set as legacy training configs don't have num_steps values. :return: The converted training config dict. """ config_dict = { diff --git a/src/primaite/data_viz/__init__.py b/src/primaite/data_viz/__init__.py index a7cc3e8b..db6ce6c8 100644 --- a/src/primaite/data_viz/__init__.py +++ b/src/primaite/data_viz/__init__.py @@ -1,3 +1,4 @@ +"""Utility to generate plots of sessions metrics after PrimAITE.""" from enum import Enum diff --git a/src/primaite/environment/__init__.py b/src/primaite/environment/__init__.py index 63f825c2..8b0060c0 100644 --- a/src/primaite/environment/__init__.py +++ b/src/primaite/environment/__init__.py @@ -1 +1,2 @@ # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. +"""Gym/Gymnasium environment for RL agents consisting of a simulated computer network.""" diff --git a/src/primaite/environment/observations.py b/src/primaite/environment/observations.py index 23bc4a39..53c173fd 100644 --- a/src/primaite/environment/observations.py +++ b/src/primaite/environment/observations.py @@ -25,6 +25,12 @@ class AbstractObservationComponent(ABC): @abstractmethod def __init__(self, env: "Primaite"): + """ + Initialise observation component. + + :param env: Primaite training environment. + :type env: Primaite + """ _LOGGER.info(f"Initialising {self} observation component") self.env: "Primaite" = env self.space: spaces.Space @@ -44,11 +50,13 @@ class AbstractObservationComponent(ABC): class NodeLinkTable(AbstractObservationComponent): - """Table with nodes and links as rows and hardware/software status as cols. + """ + Table with nodes and links as rows and hardware/software status as cols. This will create the observation space formatted as a table of integers. There is one row per node, followed by one row per link. The number of columns is 4 plus one per service. They are: + * node/link ID * node hardware status / 0 for links * node operating system status (if active/service) / 0 for links @@ -67,6 +75,12 @@ class NodeLinkTable(AbstractObservationComponent): _DATA_TYPE: type = np.int64 def __init__(self, env: "Primaite"): + """ + Initialise a NodeLinkTable observation space component. + + :param env: Training environment. + :type env: Primaite + """ super().__init__(env) # 1. Define the shape of your observation space component @@ -88,7 +102,8 @@ class NodeLinkTable(AbstractObservationComponent): self.structure = self.generate_structure() def update(self): - """Update the observation based on current environment state. + """ + Update the observation based on current environment state. The structure of the observation space is described in :class:`.NodeLinkTable` """ @@ -169,12 +184,14 @@ class NodeLinkTable(AbstractObservationComponent): class NodeStatuses(AbstractObservationComponent): - """Flat list of nodes' hardware, OS, file system, and service states. + """ + Flat list of nodes' hardware, OS, file system, and service states. The MultiDiscrete observation space can be though of as a one-dimensional vector of discrete states, represented by integers. Each node has 3 elements plus 1 per service. It will have the following structure: .. code-block:: + [ node1 hardware state, node1 OS state, @@ -190,14 +207,17 @@ class NodeStatuses(AbstractObservationComponent): node2 serviceN state (one for each service), ... ] - - :param env: The environment that forms the basis of the observations - :type env: Primaite """ _DATA_TYPE: type = np.int64 def __init__(self, env: "Primaite"): + """ + Initialise a NodeStatuses observation component. + + :param env: Training environment. + :type env: Primaite + """ super().__init__(env) # 1. Define the shape of your observation space component @@ -218,7 +238,8 @@ class NodeStatuses(AbstractObservationComponent): self.structure = self.generate_structure() def update(self): - """Update the observation based on current environment state. + """ + Update the observation based on current environment state. The structure of the observation space is described in :class:`.NodeStatuses` """ @@ -271,28 +292,22 @@ class NodeStatuses(AbstractObservationComponent): class LinkTrafficLevels(AbstractObservationComponent): - """Flat list of traffic levels encoded into banded categories. + """ + Flat list of traffic levels encoded into banded categories. For each link, total traffic or traffic per service is encoded into a categorical value. For example, if ``quantisation_levels=5``, the traffic levels represent these values: - 0 = No traffic (0% of bandwidth) - 1 = No traffic (0%-33% of bandwidth) - 2 = No traffic (33%-66% of bandwidth) - 3 = No traffic (66%-100% of bandwidth) - 4 = No traffic (100% of bandwidth) + + * 0 = No traffic (0% of bandwidth) + * 1 = No traffic (0%-33% of bandwidth) + * 2 = No traffic (33%-66% of bandwidth) + * 3 = No traffic (66%-100% of bandwidth) + * 4 = No traffic (100% of bandwidth) .. note:: The lowest category always corresponds to no traffic and the highest category to the link being at max capacity. Any amount of traffic between 0% and 100% (exclusive) is divided evenly into the remaining categories. - :param env: The environment that forms the basis of the observations - :type env: Primaite - :param combine_service_traffic: Whether to consider total traffic on the link, or each protocol individually, - defaults to False - :type combine_service_traffic: bool, optional - :param quantisation_levels: How many bands to consider when converting the traffic amount to a categorical value , - defaults to 5 - :type quantisation_levels: int, optional """ _DATA_TYPE: type = np.int64 @@ -303,6 +318,18 @@ class LinkTrafficLevels(AbstractObservationComponent): combine_service_traffic: bool = False, quantisation_levels: int = 5, ): + """ + Initialise a LinkTrafficLevels observation component. + + :param env: The environment that forms the basis of the observations + :type env: Primaite + :param combine_service_traffic: Whether to consider total traffic on the link, or each protocol individually, + defaults to False + :type combine_service_traffic: bool, optional + :param quantisation_levels: How many bands to consider when converting the traffic amount to a categorical + value, defaults to 5 + :type quantisation_levels: int, optional + """ if quantisation_levels < 3: _msg = ( f"quantisation_levels must be 3 or more because the lowest and highest levels are " @@ -333,7 +360,8 @@ class LinkTrafficLevels(AbstractObservationComponent): self.structure = self.generate_structure() def update(self): - """Update the observation based on current environment state. + """ + Update the observation based on current environment state. The structure of the observation space is described in :class:`.LinkTrafficLevels` """ @@ -374,10 +402,11 @@ class LinkTrafficLevels(AbstractObservationComponent): class ObservationsHandler: - """Component-based observation space handler. + """ + Component-based observation space handler. - This allows users to configure observation spaces by mixing and matching components. - Each component can also define further parameters to make them more flexible. + This allows users to configure observation spaces by mixing and matching components. Each component can also define + further parameters to make them more flexible. """ _REGISTRY: Final[Dict[str, type]] = { @@ -387,6 +416,7 @@ class ObservationsHandler: } def __init__(self): + """Initialise the observation handler.""" self.registered_obs_components: List[AbstractObservationComponent] = [] # internal the observation space (unflattened version of space if flatten=True) @@ -414,7 +444,8 @@ class ObservationsHandler: self._flat_observation = spaces.flatten(self._space, self._observation) def register(self, obs_component: AbstractObservationComponent): - """Add a component for this handler to track. + """ + Add a component for this handler to track. :param obs_component: The component to add. :type obs_component: AbstractObservationComponent @@ -423,10 +454,11 @@ class ObservationsHandler: self.update_space() def deregister(self, obs_component: AbstractObservationComponent): - """Remove a component from this handler. + """ + Remove a component from this handler. :param obs_component: Which component to remove. It must exist within this object's - ``registered_obs_components`` attribute. + ``registered_obs_components`` attribute. :type obs_component: AbstractObservationComponent """ self.registered_obs_components.remove(obs_component) @@ -466,11 +498,13 @@ class ObservationsHandler: @classmethod def from_config(cls, env: "Primaite", obs_space_config: dict): - """Parse a config dictinary, return a new observation handler populated with new observation component objects. + """ + Parse a config dictinary, return a new observation handler populated with new observation component objects. The expected format for the config dictionary is: - ..code-block::python + .. code-block:: python + config = { components: [ { @@ -510,7 +544,8 @@ class ObservationsHandler: return handler def describe_structure(self): - """Create a list of names for the features of the obs space. + """ + Create a list of names for the features of the obs space. The order of labels follows the flattened version of the space. """ diff --git a/src/primaite/environment/primaite_env.py b/src/primaite/environment/primaite_env.py index 3a40066a..d3c37882 100644 --- a/src/primaite/environment/primaite_env.py +++ b/src/primaite/environment/primaite_env.py @@ -73,8 +73,7 @@ class Primaite(Env): :param training_config_path: The training config filepath. :param lay_down_config_path: The lay down config filepath. :param session_path: The directory path the session is writing to. - :param timestamp_str: The session timestamp in the format: - _. + :param timestamp_str: The session timestamp in the format: _. """ self.session_path: Final[Path] = session_path self.timestamp_str: Final[str] = timestamp_str @@ -660,7 +659,8 @@ class Primaite(Env): pass def init_observations(self) -> Tuple[spaces.Space, np.ndarray]: - """Create the environment's observation handler. + """ + Create the environment's observation handler. :return: The observation space, initial observation (zeroed out array with the correct shape) :rtype: Tuple[spaces.Space, np.ndarray] @@ -1040,7 +1040,8 @@ class Primaite(Env): self.num_ports = len(self.ports_list) def get_observation_info(self, observation_info): - """Extracts observation_info. + """ + Extracts observation_info. :param observation_info: Config item that defines which type of observation space to use :type observation_info: str @@ -1057,7 +1058,8 @@ class Primaite(Env): self.action_type = ActionType[action_info["type"]] def save_obs_config(self, obs_config: dict): - """Cache the config for the observation space. + """ + Cache the config for the observation space. This is necessary as the observation space can't be built while reading the config, it must be done after all the nodes, links, and services have been initialised. @@ -1070,10 +1072,9 @@ class Primaite(Env): def reset_environment(self): """ - # Resets environment. + Resets environment. - Uses config data config data in order to build the environment - configuration. + Uses config data config data in order to build the environment configuration. """ for item in self.lay_down_config: if item["item_type"] == "NODE": @@ -1157,7 +1158,6 @@ class Primaite(Env): 5: [1, 3, 1, 0], ... } - """ # reserve 0 action to be a nothing action actions = {0: [1, 0, 0, 0]} @@ -1213,7 +1213,6 @@ class Primaite(Env): Create a dictionary mapping each possible discrete action to a more readable mutlidiscrete action. The dictionary contains actions of both Node and ACL action types. - """ node_action_dict = self.create_node_action_dict() acl_action_dict = self.create_acl_action_dict() diff --git a/src/primaite/links/__init__.py b/src/primaite/links/__init__.py index 63f825c2..6257f282 100644 --- a/src/primaite/links/__init__.py +++ b/src/primaite/links/__init__.py @@ -1 +1,2 @@ # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. +"""Network connections between nodes in the simulation.""" diff --git a/src/primaite/links/link.py b/src/primaite/links/link.py index 90235e9f..f61281cd 100644 --- a/src/primaite/links/link.py +++ b/src/primaite/links/link.py @@ -10,14 +10,13 @@ class Link(object): def __init__(self, _id, _bandwidth, _source_node_name, _dest_node_name, _services): """ - Init. + Initialise a Link within the simulated network. - Args: - _id: The IER id - _bandwidth: The bandwidth of the link (bps) - _source_node_name: The name of the source node - _dest_node_name: The name of the destination node - _protocols: The protocols to add to the link + :param _id: The IER id + :param _bandwidth: The bandwidth of the link (bps) + :param _source_node_name: The name of the source node + :param _dest_node_name: The name of the destination node + :param _protocols: The protocols to add to the link """ self.id = _id self.bandwidth = _bandwidth diff --git a/src/primaite/main.py b/src/primaite/main.py index 7b1d7ab3..f2d1b9c2 100644 --- a/src/primaite/main.py +++ b/src/primaite/main.py @@ -14,7 +14,8 @@ def run( training_config_path: Union[str, Path], lay_down_config_path: Union[str, Path], ): - """Run the PrimAITE Session. + """ + Run the PrimAITE Session. :param training_config_path: The training config filepath. :param lay_down_config_path: The lay down config filepath. diff --git a/src/primaite/nodes/__init__.py b/src/primaite/nodes/__init__.py index 63f825c2..19347372 100644 --- a/src/primaite/nodes/__init__.py +++ b/src/primaite/nodes/__init__.py @@ -1 +1,2 @@ # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. +"""Nodes represent network hosts in the simulation.""" diff --git a/src/primaite/nodes/active_node.py b/src/primaite/nodes/active_node.py index 07a0ea0a..f86f818b 100644 --- a/src/primaite/nodes/active_node.py +++ b/src/primaite/nodes/active_node.py @@ -26,7 +26,7 @@ class ActiveNode(Node): config_values: TrainingConfig, ): """ - Init. + Initialise an active node. :param node_id: The node ID :param name: The node name diff --git a/src/primaite/nodes/node.py b/src/primaite/nodes/node.py index bac1792d..9fd5b719 100644 --- a/src/primaite/nodes/node.py +++ b/src/primaite/nodes/node.py @@ -19,7 +19,7 @@ class Node: config_values: TrainingConfig, ): """ - Init. + Initialise a node. :param node_id: The node id. :param name: The name of the node. diff --git a/src/primaite/nodes/node_state_instruction_green.py b/src/primaite/nodes/node_state_instruction_green.py index 2b1d94be..7ebe3886 100644 --- a/src/primaite/nodes/node_state_instruction_green.py +++ b/src/primaite/nodes/node_state_instruction_green.py @@ -16,16 +16,15 @@ class NodeStateInstructionGreen(object): _state, ): """ - Init. + Initialise the Node State Instruction. - Args: - _id: The node state instruction id - _start_step: The start step of the instruction - _end_step: The end step of the instruction - _node_id: The id of the associated node - _node_pol_type: The pattern of life type - _service_name: The service name - _state: The state (node or service) + :param _id: The node state instruction id + :param _start_step: The start step of the instruction + :param _end_step: The end step of the instruction + :param _node_id: The id of the associated node + :param _node_pol_type: The pattern of life type + :param _service_name: The service name + :param _state: The state (node or service) """ self.id = _id self.start_step = _start_step diff --git a/src/primaite/nodes/node_state_instruction_red.py b/src/primaite/nodes/node_state_instruction_red.py index 4272ce24..540625cc 100644 --- a/src/primaite/nodes/node_state_instruction_red.py +++ b/src/primaite/nodes/node_state_instruction_red.py @@ -24,20 +24,19 @@ class NodeStateInstructionRed(object): _pol_source_node_service_state, ): """ - Init. + Initialise the Node State Instruction for the red agent. - Args: - _id: The node state instruction id - _start_step: The start step of the instruction - _end_step: The end step of the instruction - _target_node_id: The id of the associated node - -pol_initiator: The way the PoL is applied (DIRECT, IER or SERVICE) - _pol_type: The pattern of life type - -pol_protocol: The pattern of life protocol/service affected - _pol_state: The state (node or service) - _pol_source_node_id: The source node Id (used for initiator type SERVICE) - _pol_source_node_service: The source node service (used for initiator type SERVICE) - _pol_source_node_service_state: The source node service state (used for initiator type SERVICE) + :param _id: The node state instruction id + :param _start_step: The start step of the instruction + :param _end_step: The end step of the instruction + :param _target_node_id: The id of the associated node + :param -pol_initiator: The way the PoL is applied (DIRECT, IER or SERVICE) + :param _pol_type: The pattern of life type + :param pol_protocol: The pattern of life protocol/service affected + :param _pol_state: The state (node or service) + :param _pol_source_node_id: The source node Id (used for initiator type SERVICE) + :param _pol_source_node_service: The source node service (used for initiator type SERVICE) + :param _pol_source_node_service_state: The source node service state (used for initiator type SERVICE) """ self.id = _id self.start_step = _start_step diff --git a/src/primaite/nodes/passive_node.py b/src/primaite/nodes/passive_node.py index 9aa5c7d7..afe4e2d1 100644 --- a/src/primaite/nodes/passive_node.py +++ b/src/primaite/nodes/passive_node.py @@ -18,7 +18,7 @@ class PassiveNode(Node): config_values: TrainingConfig, ): """ - Init. + Initialise a passive node. :param node_id: The node id. :param name: The name of the node. diff --git a/src/primaite/nodes/service_node.py b/src/primaite/nodes/service_node.py index 5d69df92..4ad52a1e 100644 --- a/src/primaite/nodes/service_node.py +++ b/src/primaite/nodes/service_node.py @@ -27,7 +27,7 @@ class ServiceNode(ActiveNode): config_values: TrainingConfig, ): """ - Init. + Initialise a Service Node. :param node_id: The node ID :param name: The node name @@ -77,8 +77,7 @@ class ServiceNode(ActiveNode): Indicates whether a service is in a running state on the node. :param protocol_name: The service (protocol) - :return: True if service (protocol) is in a running state on the - node, otherwise False. + :return: True if service (protocol) is in a running state on the node, otherwise False. """ for service_key, service_value in self.services.items(): if service_key == protocol_name: @@ -93,8 +92,7 @@ class ServiceNode(ActiveNode): Indicates whether a service is in an overwhelmed state on the node. :param protocol_name: The service (protocol) - :return: True if service (protocol) is in an overwhelmed state on the - node, otherwise False. + :return: True if service (protocol) is in an overwhelmed state on the node, otherwise False. """ for service_key, service_value in self.services.items(): if service_key == protocol_name: diff --git a/src/primaite/notebooks/__init__.py b/src/primaite/notebooks/__init__.py index 0e81e581..6ca1d3f6 100644 --- a/src/primaite/notebooks/__init__.py +++ b/src/primaite/notebooks/__init__.py @@ -1,3 +1,4 @@ +"""Contains default jupyter notebooks which demonstrate PrimAITE functionality.""" # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. import importlib.util import os diff --git a/src/primaite/pol/__init__.py b/src/primaite/pol/__init__.py index 63f825c2..cba4b28b 100644 --- a/src/primaite/pol/__init__.py +++ b/src/primaite/pol/__init__.py @@ -1 +1,2 @@ +"""Pattern of Life- Represents the actions of users on the network.""" # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. diff --git a/src/primaite/pol/ier.py b/src/primaite/pol/ier.py index daa49727..2de8fe6f 100644 --- a/src/primaite/pol/ier.py +++ b/src/primaite/pol/ier.py @@ -23,19 +23,18 @@ class IER(object): _running=False, ): """ - Init. + Initialise an Information Exchange Request. - Args: - _id: The IER id - _start_step: The step when this IER should start - _end_step: The step when this IER should end - _load: The load this IER should put on a link (bps) - _protocol: The protocol of this IER - _port: The port this IER runs on - _source_node_id: The source node ID - _dest_node_id: The destination node ID - _mission_criticality: Criticality of this IER to the mission (0 none, 5 mission critical) - _running: Indicates whether the IER is currently running + :param _id: The IER id + :param _start_step: The step when this IER should start + :param _end_step: The step when this IER should end + :param _load: The load this IER should put on a link (bps) + :param _protocol: The protocol of this IER + :param _port: The port this IER runs on + :param _source_node_id: The source node ID + :param _dest_node_id: The destination node ID + :param _mission_criticality: Criticality of this IER to the mission (0 none, 5 mission critical) + :param _running: Indicates whether the IER is currently running """ self.id = _id self.start_step = _start_step diff --git a/src/primaite/pol/red_agent_pol.py b/src/primaite/pol/red_agent_pol.py index bff19bf8..1a8bd406 100644 --- a/src/primaite/pol/red_agent_pol.py +++ b/src/primaite/pol/red_agent_pol.py @@ -296,11 +296,17 @@ def apply_red_agent_node_pol( pass -def is_red_ier_incoming(node, iers, node_pol_type): - """ - Checks if the RED IER is incoming. +def is_red_ier_incoming(node: NodeUnion, iers: Dict[str, IER], node_pol_type: NodePOLType) -> bool: + """Checks if the RED IER is incoming. - TODO: Write more descriptive docstring with params and returns. + :param node: Destination node of the IER + :type node: NodeUnion + :param iers: Directory of IERs + :type iers: Dict[str,IER] + :param node_pol_type: Type of Pattern-Of-Life + :type node_pol_type: NodePOLType + :return: Whether the RED IER is incoming. + :rtype: bool """ node_id = node.node_id diff --git a/src/primaite/primaite_session.py b/src/primaite/primaite_session.py index df3ebec1..caa85e9e 100644 --- a/src/primaite/primaite_session.py +++ b/src/primaite/primaite_session.py @@ -1,3 +1,4 @@ +"""Main entry point to PrimAITE. Configure training/evaluation experiments and input/output.""" from __future__ import annotations from pathlib import Path @@ -21,8 +22,7 @@ class PrimaiteSession: """ The PrimaiteSession class. - Provides a single learning and evaluation entry point for all training - and lay down configurations. + Provides a single learning and evaluation entry point for all training and lay down configurations. """ def __init__( @@ -38,12 +38,12 @@ class PrimaiteSession: """ if not isinstance(training_config_path, Path): training_config_path = Path(training_config_path) - self._training_config_path: Final[Union[Path]] = training_config_path + self._training_config_path: Final[Union[Path, str]] = training_config_path self._training_config: Final[TrainingConfig] = training_config.load(self._training_config_path) if not isinstance(lay_down_config_path, Path): lay_down_config_path = Path(lay_down_config_path) - self._lay_down_config_path: Final[Union[Path]] = lay_down_config_path + self._lay_down_config_path: Final[Union[Path, str]] = lay_down_config_path self._lay_down_config: Dict = lay_down_config.load(self._lay_down_config_path) self._agent_session: AgentSessionABC = None # noqa diff --git a/src/primaite/setup/__init__.py b/src/primaite/setup/__init__.py index 63f825c2..3c0bfe14 100644 --- a/src/primaite/setup/__init__.py +++ b/src/primaite/setup/__init__.py @@ -1 +1,2 @@ +"""Utilities to prepare the user's data folders.""" # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. diff --git a/src/primaite/setup/reset_demo_notebooks.py b/src/primaite/setup/reset_demo_notebooks.py index 7fa96783..793f9ade 100644 --- a/src/primaite/setup/reset_demo_notebooks.py +++ b/src/primaite/setup/reset_demo_notebooks.py @@ -15,8 +15,7 @@ def run(overwrite_existing: bool = True): """ Resets the demo jupyter notebooks in the users app notebooks directory. - :param overwrite_existing: A bool to toggle replacing existing edited - notebooks on or off. + :param overwrite_existing: A bool to toggle replacing existing edited notebooks on or off. """ notebooks_package_data_root = pkg_resources.resource_filename("primaite", "notebooks/_package_data") for subdir, dirs, files in os.walk(notebooks_package_data_root): diff --git a/src/primaite/setup/reset_example_configs.py b/src/primaite/setup/reset_example_configs.py index 5d62298c..599de8dc 100644 --- a/src/primaite/setup/reset_example_configs.py +++ b/src/primaite/setup/reset_example_configs.py @@ -14,8 +14,7 @@ def run(overwrite_existing=True): """ Resets the example config files in the users app config directory. - :param overwrite_existing: A bool to toggle replacing existing edited - config on or off. + :param overwrite_existing: A bool to toggle replacing existing edited config on or off. """ configs_package_data_root = pkg_resources.resource_filename("primaite", "config/_package_data") diff --git a/src/primaite/transactions/__init__.py b/src/primaite/transactions/__init__.py index 63f825c2..45315b22 100644 --- a/src/primaite/transactions/__init__.py +++ b/src/primaite/transactions/__init__.py @@ -1 +1,2 @@ +"""Record data of the system's state and agent's observations and actions.""" # Crown Copyright (C) Dstl 2022. DEFCON 703. Shared in confidence. diff --git a/src/primaite/transactions/transaction.py b/src/primaite/transactions/transaction.py index 95be8115..f49d4ec2 100644 --- a/src/primaite/transactions/transaction.py +++ b/src/primaite/transactions/transaction.py @@ -86,8 +86,7 @@ def _turn_obs_space_to_array(obs_space, obs_assets, obs_features) -> List[str]: Turns observation space into a string array so it can be saved to csv. :param obs_space: The observation space - :param obs_assets: The number of assets (i.e. nodes or links) in the - observation space + :param obs_assets: The number of assets (i.e. nodes or links) in the observation space :param obs_features: The number of features associated with the asset :return: The observation space as an array of strings """ diff --git a/src/primaite/utils/__init__.py b/src/primaite/utils/__init__.py index e69de29b..55e8a6ba 100644 --- a/src/primaite/utils/__init__.py +++ b/src/primaite/utils/__init__.py @@ -0,0 +1 @@ +"""Utilities for PrimAITE.""" diff --git a/src/primaite/utils/session_output_reader.py b/src/primaite/utils/session_output_reader.py index d04f375e..e70c98e2 100644 --- a/src/primaite/utils/session_output_reader.py +++ b/src/primaite/utils/session_output_reader.py @@ -10,8 +10,7 @@ def av_rewards_dict(av_rewards_csv_file: Union[str, Path]) -> Dict[int, float]: """ Read an average rewards per episode csv file and return as a dict. - The dictionary keys are the episode number, and the values are the mean - reward that episode. + The dictionary keys are the episode number, and the values are the mean reward that episode. :param av_rewards_csv_file: The average rewards per episode csv file path. :return: The average rewards per episode cdv as a dict. diff --git a/src/primaite/utils/session_output_writer.py b/src/primaite/utils/session_output_writer.py index a05b0453..104acc62 100644 --- a/src/primaite/utils/session_output_writer.py +++ b/src/primaite/utils/session_output_writer.py @@ -29,6 +29,18 @@ class SessionOutputWriter: transaction_writer: bool = False, learning_session: bool = True, ): + """ + Initialise the Session Output Writer. + + :param env: PrimAITE gym environment. + :type env: Primaite + :param transaction_writer: If `true`, this will output a full account of every transaction taken by the agent. + If `false` it will output the average reward per episode, defaults to False + :type transaction_writer: bool, optional + :param learning_session: Set to `true` to indicate that the current session is a training session. This + determines the name of the folder which contains the final output csv. Defaults to True + :type learning_session: bool, optional + """ self._env = env self.transaction_writer = transaction_writer self.learning_session = learning_session @@ -68,8 +80,7 @@ class SessionOutputWriter: """ Write a row of session data. - :param data: The row of data to write. Can be a Tuple or an instance - of Transaction. + :param data: The row of data to write. Can be a Tuple or an instance of Transaction. """ if isinstance(data, Transaction): header, data = data.as_csv_data() diff --git a/tests/test_observation_space.py b/tests/test_observation_space.py index d1082049..d5844fd9 100644 --- a/tests/test_observation_space.py +++ b/tests/test_observation_space.py @@ -75,7 +75,8 @@ class TestNodeLinkTable: assert env.env_obs.shape == (5, 6) def test_value(self, temp_primaite_session): - """Test that the observation is generated correctly. + """ + Test that the observation is generated correctly. The laydown has: * 3 nodes (2 service nodes and 1 active node) @@ -157,7 +158,8 @@ class TestNodeStatuses: assert env.env_obs.shape == (15,) def test_values(self, temp_primaite_session): - """Test that the hardware and software states are encoded correctly. + """ + Test that the hardware and software states are encoded correctly. The laydown has: * one node with a compromised operating system state @@ -213,7 +215,8 @@ class TestLinkTrafficLevels: assert env.env_obs.shape == (2 * 2,) def test_values(self, temp_primaite_session): - """Test that traffic values are encoded correctly. + """ + Test that traffic values are encoded correctly. The laydown has: * two services