Merge "[Solution] Update to ONAP Montreal releases version"
diff --git a/code/network-generator/.gitignore b/code/network-generator/.gitignore
index 403392b..64369ef 100644
--- a/code/network-generator/.gitignore
+++ b/code/network-generator/.gitignore
@@ -130,6 +130,7 @@
 # Environments
 .env
 .venv
+.oam
 env/
 venv/
 ENV/
diff --git a/code/network-generator/CONTRIBUTING.md b/code/network-generator/CONTRIBUTING.md
index 478386a..7a22c5d 100644
--- a/code/network-generator/CONTRIBUTING.md
+++ b/code/network-generator/CONTRIBUTING.md
@@ -22,7 +22,7 @@
 
 ## Setting up your own virtual environment
 
-Run `make virtualenv` to create a virtual environment.
+Run `make virtual_env_4_oam` to create a virtual environment.
 then activate it with `source .venv/bin/activate`.
 
 ## Install the project in develop mode
@@ -85,18 +85,18 @@
 Usage: make <target>
 
 Targets:
-help:             ## Show the help.
-install:          ## Install the project in dev mode.
-fmt:              ## Format code using black & isort.
-lint:             ## Run pep8, black, mypy linters.
-test: lint        ## Run tests and generate coverage report.
-watch:            ## Run tests on every change.
-clean:            ## Clean unused files.
-virtualenv:       ## Create a virtual environment.
-release:          ## Create a new tag for release.
-docs:             ## Build the documentation.
-switch-to-poetry: ## Switch to poetry package manager.
-init:             ## Initialize the project based on an application template.
+help:              ## Show the help.
+install:           ## Install the project in dev mode.
+fmt:               ## Format code using black & isort.
+lint:              ## Run pep8, black, mypy linters.
+test: lint         ## Run tests and generate coverage report.
+watch:             ## Run tests on every change.
+clean:             ## Clean unused files.
+virtual_env_4_oam: ## Create a virtual environment.
+release:           ## Create a new tag for release.
+docs:              ## Build the documentation.
+switch-to-poetry:  ## Switch to poetry package manager.
+init:              ## Initialize the project based on an application template.
 ```
 
 ## Making a new release
diff --git a/code/network-generator/Makefile b/code/network-generator/Makefile
index ab2b357..d396d83 100644
--- a/code/network-generator/Makefile
+++ b/code/network-generator/Makefile
@@ -1,5 +1,5 @@
 .ONESHELL:
-ENV_PREFIX=$(shell python -c "if __import__('pathlib').Path('.venv/bin/pip').exists(): print('.venv/bin/')")
+ENV_PREFIX=$(shell python -c "if __import__('pathlib').Path('.oam/bin/pip').exists(): print('.oam/bin/')")
 USING_POETRY=$(shell grep "tool.poetry" pyproject.toml && echo "yes")
 
 .PHONY: help
@@ -21,7 +21,7 @@
 .PHONY: install
 install:          ## Install the project in dev mode.
 	@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
-	@echo "Don't forget to run 'make virtualenv' if you got errors."
+	@echo "Don't forget to run 'make virtual_env_4_oam' if you got errors."
 	$(ENV_PREFIX)pip install -e .[test]
 
 .PHONY: format
@@ -64,16 +64,19 @@
 	@rm -rf .tox/
 	@rm -rf docs/_build
 
-.PHONY: virtualenv
-virtualenv:       ## Create a virtual environment.
+.PHONY: virtual_env_4_oam
+virtual_env_4_oam:       ## Create a virtual environment for O-RAN-SC OAM project.
 	@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
-	@echo "creating virtualenv ..."
-	@rm -rf .venv
-	@python3 -m venv .venv
-	@./.venv/bin/pip install -U pip
-	@./.venv/bin/pip install -e .[test]
+	@echo "Creating a virtual environment for O-RAN-SC OAM project ..."
+	@rm -rf .oam
+	@python3 -m venv .oam
+	@./.oam/bin/pip install -U pip
+	@./.oam/bin/pip install -e .[test]
+	@./.oam/bin/pip install -r requirements.txt
+	@./.oam/bin/pip install -r requirements-test.txt
+
 	@echo
-	@echo "!!! Please run 'source .venv/bin/activate' to enable the environment !!!"
+	@echo "!!! Please run 'source .oam/bin/activate' to enable the OAM environment !!!"
 
 .PHONY: release
 release:          ## Create a new tag for release.
@@ -98,7 +101,7 @@
 switch-to-poetry: ## Switch to poetry package manager.
 	@echo "Switching to poetry ..."
 	@if ! poetry --version > /dev/null; then echo 'poetry is required, install from https://python-poetry.org/'; exit 1; fi
-	@rm -rf .venv
+	@rm -rf .oam
 	@poetry init --no-interaction --name=a_flask_test --author=rochacbruno
 	@echo "" >> pyproject.toml
 	@echo "[tool.poetry.scripts]" >> pyproject.toml
diff --git a/code/network-generator/README.md b/code/network-generator/README.md
index 0f91dfb..66a6326 100644
--- a/code/network-generator/README.md
+++ b/code/network-generator/README.md
@@ -28,6 +28,8 @@
 ## Usage 
 
 ```
+make virtual_env_4_oam
+source .oam/bin/activate
 python3 -m network_generation config.json
 ```
 
diff --git a/code/network-generator/config.json b/code/network-generator/config.json
index 9cc8afe..ff785f7 100644
--- a/code/network-generator/config.json
+++ b/code/network-generator/config.json
@@ -39,6 +39,10 @@
   },
   "outputFolder": "output",
   "generationTasks": {
+    "network_dir": {
+      "enabled": true,
+      "compressed": false
+    },
     "topology": {
       "enabled": true,
       "compressed": true
diff --git a/code/network-generator/network_generation/cli.py b/code/network-generator/network_generation/cli.py
index fa8a8f3..e156fcd 100644
--- a/code/network-generator/network_generation/cli.py
+++ b/code/network-generator/network_generation/cli.py
@@ -59,6 +59,13 @@
                 configuration["generationTasks"]["topology"]["compressed"]
             )
 
+        # dir structure for day0 configuration
+        # Note: compressed option ignored
+        if configuration["generationTasks"]["network_dir"]["enabled"] is True:
+            viewer.to_directory(
+                output_folder
+            )
+
         # svg xml
         if configuration["generationTasks"]["svg"]["enabled"] is True:
             viewer.svg(
diff --git a/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json b/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json
index e2fb5fd..edfdfd6 100644
--- a/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json
+++ b/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json
@@ -306,6 +306,10 @@
           "description": "If enabled, an ietf-network-topology file will be generated.",
           "$ref": "#/$defs/export"
         },
+        "network_dir": {
+          "description": "If enabled, an ietf-network-topology network subdirectory file structure is generated.",
+          "$ref": "#/$defs/export"
+        },
         "svg": {
           "description": "If enabled, a svg file will be generated.",
           "$ref": "#/$defs/export"
diff --git a/code/network-generator/network_generation/model/python/nr_cell_du.py b/code/network-generator/network_generation/model/python/nr_cell_du.py
index acdafe1..13fc42b 100644
--- a/code/network-generator/network_generation/model/python/nr_cell_du.py
+++ b/code/network-generator/network_generation/model/python/nr_cell_du.py
@@ -240,3 +240,6 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        pass
diff --git a/code/network-generator/network_generation/model/python/o_ran_cloud_du.py b/code/network-generator/network_generation/model/python/o_ran_cloud_du.py
index b97f97e..d3a4d10 100644
--- a/code/network-generator/network_generation/model/python/o_ran_cloud_du.py
+++ b/code/network-generator/network_generation/model/python/o_ran_cloud_du.py
@@ -136,3 +136,7 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        for tower in self.towers:
+            tower.to_directory(parent_dir)
diff --git a/code/network-generator/network_generation/model/python/o_ran_cu.py b/code/network-generator/network_generation/model/python/o_ran_cu.py
index be0006d..2df40f6 100644
--- a/code/network-generator/network_generation/model/python/o_ran_cu.py
+++ b/code/network-generator/network_generation/model/python/o_ran_cu.py
@@ -143,3 +143,7 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        for o_ran_cloud_du in self.o_ran_cloud_dus:
+            o_ran_cloud_du.to_directory(parent_dir)
diff --git a/code/network-generator/network_generation/model/python/o_ran_du.py b/code/network-generator/network_generation/model/python/o_ran_du.py
index 0392d2c..98e8c1a 100644
--- a/code/network-generator/network_generation/model/python/o_ran_du.py
+++ b/code/network-generator/network_generation/model/python/o_ran_du.py
@@ -18,6 +18,7 @@
 A Class representing an O-RAN distributed unit (ORanDu)
 """
 import xml.etree.ElementTree as ET
+import os
 from typing import Any, cast
 
 from network_generation.model.python.o_ran_node import IORanNode, ORanNode
@@ -54,6 +55,7 @@
             if o_ran_du_data and "oRanRuCount" in o_ran_du_data
             else 1
         )
+        self.type = "ntsim-ng-o-du"
 
     def _to_o_ran_du_data(self, data: dict[str, Any]) -> IORanDu:
         result: IORanDu = default_value
@@ -114,3 +116,11 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        parent_path = os.path.join(parent_dir, self.type)
+        path = os.path.join(parent_path, self.name)
+        if not os.path.exists(parent_path):
+            os.makedirs(parent_path, exist_ok=True)
+        if not os.path.exists(path):
+            os.mkdir(path)
diff --git a/code/network-generator/network_generation/model/python/o_ran_near_rt_ric.py b/code/network-generator/network_generation/model/python/o_ran_near_rt_ric.py
index 5a44843..17208f8 100644
--- a/code/network-generator/network_generation/model/python/o_ran_near_rt_ric.py
+++ b/code/network-generator/network_generation/model/python/o_ran_near_rt_ric.py
@@ -145,3 +145,7 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        for o_ran_cu in self.o_ran_cus:
+            o_ran_cu.to_directory(parent_dir)
diff --git a/code/network-generator/network_generation/model/python/o_ran_network.py b/code/network-generator/network_generation/model/python/o_ran_network.py
index 962e5b1..901e83f 100644
--- a/code/network-generator/network_generation/model/python/o_ran_network.py
+++ b/code/network-generator/network_generation/model/python/o_ran_network.py
@@ -17,6 +17,7 @@
 Module for a class representing a O-RAN Network
 """
 import xml.etree.ElementTree as ET
+import os
 from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
@@ -148,6 +149,9 @@
             }
         }
 
+    def to_directory(self, parent_dir: str) -> None:
+        self._o_ran_smo.to_directory(os.path.join(parent_dir, self.id))
+
     def toKml(self) -> ET.Element:
         root: ET.Element = ET.Element(
             "kml", xmlns="http://www.opengis.net/kml/2.2"
diff --git a/code/network-generator/network_generation/model/python/o_ran_node.py b/code/network-generator/network_generation/model/python/o_ran_node.py
index 6a2b3cb..8dba3bc 100644
--- a/code/network-generator/network_generation/model/python/o_ran_node.py
+++ b/code/network-generator/network_generation/model/python/o_ran_node.py
@@ -216,3 +216,7 @@
     @abstractmethod
     def toSvg(self) -> ET.Element:
         pass
+
+    @abstractmethod
+    def to_directory(self, parent_dir: str) -> None:
+        pass
diff --git a/code/network-generator/network_generation/model/python/o_ran_ru.py b/code/network-generator/network_generation/model/python/o_ran_ru.py
index 265e6f4..c88dfec 100644
--- a/code/network-generator/network_generation/model/python/o_ran_ru.py
+++ b/code/network-generator/network_generation/model/python/o_ran_ru.py
@@ -18,6 +18,7 @@
 A Class representing an O-RAN radio unit (ORanRu)
 """
 import xml.etree.ElementTree as ET
+import os
 from typing import Any, cast
 
 from network_generation.model.python.nr_cell_du import NrCellDu
@@ -70,6 +71,7 @@
         )
         self._cells: list[NrCellDu] = self._create_cells()
         name: str = self.name.replace("RU", "DU")
+        self.type = "ntsim-ng-o-ru"
 
         o_ran_du_data: dict[str, Any] = {
             "name": name,
@@ -185,3 +187,12 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        self.oRanDu.to_directory(parent_dir)
+        parent_path = os.path.join(parent_dir, self.type)
+        path = os.path.join(parent_path, self.name)
+        if not os.path.exists(parent_path):
+            os.makedirs(parent_path, exist_ok=True)
+        if not os.path.exists(path):
+            os.mkdir(path)
diff --git a/code/network-generator/network_generation/model/python/o_ran_smo.py b/code/network-generator/network_generation/model/python/o_ran_smo.py
index 9308291..d9a7e9a 100644
--- a/code/network-generator/network_generation/model/python/o_ran_smo.py
+++ b/code/network-generator/network_generation/model/python/o_ran_smo.py
@@ -153,3 +153,7 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("not-implemented-yet-TODO")
+
+    def to_directory(self, parent_dir: str) -> None:
+        for ric in self.o_ran_near_rt_rics:
+            ric.to_directory(parent_dir)
diff --git a/code/network-generator/network_generation/model/python/tower.py b/code/network-generator/network_generation/model/python/tower.py
index dd1eb70..2aae0b3 100644
--- a/code/network-generator/network_generation/model/python/tower.py
+++ b/code/network-generator/network_generation/model/python/tower.py
@@ -154,3 +154,7 @@
 
     def toSvg(self) -> ET.Element:
         return ET.Element("to-be-implemented")
+
+    def to_directory(self, parent_dir: str) -> None:
+        for o_ran_ru in self.o_ran_rus:
+            o_ran_ru.to_directory(parent_dir)
diff --git a/code/network-generator/network_generation/view/network_viewer.py b/code/network-generator/network_generation/view/network_viewer.py
index b55b476..750a661 100644
--- a/code/network-generator/network_generation/view/network_viewer.py
+++ b/code/network-generator/network_generation/view/network_viewer.py
@@ -58,6 +58,13 @@
         """
         print(self.__network)
 
+    def to_directory(self, parent_dir: str) -> None:
+        """
+        Method converting the network to a subdirectory file structure
+        """
+        self.__network.to_directory(parent_dir)
+        print(f'Directory structure saved to "{parent_dir}"')
+
     def save(self, filename: str, compressed: bool = True) -> None:
         """
         Method saving the class content to a file in json format.
diff --git a/code/network-generator/requirements.txt b/code/network-generator/requirements.txt
index 4957d9d..d34d9cc 100644
--- a/code/network-generator/requirements.txt
+++ b/code/network-generator/requirements.txt
@@ -13,3 +13,4 @@
 # limitations under the License.
 
 jsonschema
+typing-extensions
\ No newline at end of file