Repository: napalm-automation-community/napalm-sros (language: python)

Issue:
'close()' doesn't close ssh connection
Calling `close()` method of the driver doesn't close the SSH connection to the device.

This is causing the configuration is still locked by the "napalm connection" so when connecting to CLI manually I can't run `configure global`
>MINOR: MGMT_CORE #2051: Global datastore access unavailable - the MD-CLI has exclusive lock on configuration

To fix this issue I needed to run `device.conn_ssh.get_transport().close()` manually.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/napalm-sros-ci.yml b/.github/workflows/napalm-sros-ci.yml
index 28cde60..402b5f4 100644
--- a/.github/workflows/napalm-sros-ci.yml
+++ b/.github/workflows/napalm-sros-ci.yml
@@ -3,29 +3,50 @@ name: napalm-sros
 on: [push, pull_request]
 
 jobs:
-  build:
-
+  std_tests:
     runs-on: ubuntu-latest
+    strategy:
+      max-parallel: 5
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
 
     steps:
-    - uses: actions/checkout@v2
-    - name: Set up Python 3.6
-      uses: actions/setup-python@v2
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
       with:
-        python-version: "3.6"
+        python-version: ${{ matrix.python-version }}
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip
-        pip install flake8 pytest
-        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
-        if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
-    - name: Lint with flake8
+        python -m pip install -e .
+        pip install -r requirements.txt -r requirements-dev.txt
+    # Code formatter
+    # - name: Run black
+    #  run: |
+    #    black --check .
+    # - name: Run linter
+    #  run: |
+    #    pylama .
+    # - name: Lint with flake8
+    #   run: |
+    #     # stop the build if there are Python syntax errors or undefined names
+    #     flake8 . --exclude .github,__pycache__,build,dist --count --select=E9,F63,F7,F82 --show-source --statistics
+    #     # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+    #     flake8 . --exclude .github,__pycache__,build,dist --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+    - name: Lint with Ruff, ignore line length and imports not at top or multiple
       run: |
-        # stop the build if there are Python syntax errors or undefined names
-        flake8 . --exclude .github,__pycache__,build,dist --count --select=E9,F63,F7,F82 --show-source --statistics
-        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
-        flake8 . --exclude .github,__pycache__,build,dist --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+        ruff check napalm_sros test --ignore E501,E402,E401
+
+
+      # TODO future
+      # - name: Run type checker
+      #  run: |
+      #    mypy -p napalm --config-file mypy.ini
+
+      # - name: Run Tests
+      #  run: |
+      #    py.test --cov=napalm --cov-report term-missing -vs --pylama
     - name: Test with pytest
       run: |
         pytest
-
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..16665f9
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,13 @@
+repos:
+- repo: https://github.com/charliermarsh/ruff-pre-commit
+  # Ruff version.
+  rev: 'v0.0.245'
+  hooks:
+    - id: ruff
+      args: 
+      - --fix
+      - --exit-non-zero-on-fix 
+      - --ignore 
+      - E401
+      - --ignore 
+      - E501
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..72200b3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+# CI/CD tasks
+
+.DEFAULT_GOAL := help
+
+.PHONY: help .venv test dist release
+
+# TESTS := $(shell find test/ci -name '*.py')
+
+help:
+	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+
+# deploy-clab-ci: ## Deploy "ci" test topology
+# 	cd .clab && sudo clab deploy -t ci-topology.yml
+# 
+# destroy-clab-ci: ## Destroy "ci" test topology
+# 	cd .clab && sudo clab destroy -t ci-topology.yml
+
+# run-tests: $(TESTS) ## Run all CI tests under test/ci
+# 	PYTHONPATH="." python3 $<
+
+.venv: ## Creates a .venv with dependencies, fully self contained
+	python3 -m venv .venv
+	. .venv/bin/activate && python3 -m pip install --upgrade -r requirements.txt -r requirements-dev.txt
+
+test: .venv
+	. .venv/bin/activate && pytest
+
+dist: ## This creates a ./dist directory with wheel package
+	python3 -m pip install --upgrade build
+	python3 -m build
+
+release: dist ## release to PyPi
+	python3 -m pip install --upgrade twine
+	# --repository testpypi
+	python3 -m twine upload dist/*
diff --git a/napalm_sros/api/__init__.py b/napalm_sros/api/__init__.py
index fd75e53..6b1d86e 100644
--- a/napalm_sros/api/__init__.py
+++ b/napalm_sros/api/__init__.py
@@ -16,5 +16,6 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from .get_bgp_neighbors import get_bgp_neighbors
-from .get_bgp_neighbors_detail import get_bgp_neighbors_detail
+from .get_bgp_neighbors import get_bgp_neighbors # noqa
+from .get_bgp_neighbors_detail import get_bgp_neighbors_detail # noqa
+
diff --git a/napalm_sros/nc_filters.py b/napalm_sros/nc_filters.py
index 13621f1..b592fd4 100644
--- a/napalm_sros/nc_filters.py
+++ b/napalm_sros/nc_filters.py
@@ -417,17 +417,15 @@
                     <local-as>
                         <as-number/>
                     </local-as>
-                    <import>
-                        <policy/>
-                    </import>
-                    <export>
-                        <policy/>
-                    </export>
                     <remove-private/>
                     <cluster>
                         <cluster-id/>
                     </cluster>
-
+                    <multipath>
+                     <ebgp/>
+                     <ibgp/>
+                    </multipath>
+                    <client-reflect/>
                     <group>
                         <group-name>{group_name}</group-name>
                         <description/>
@@ -437,6 +435,7 @@
                         <peer-as/>
                         <local-address/>
                         <next-hop-self/>
+                        <client-reflect/>
                         <local-as>
                             <as-number/>
                         </local-as>
@@ -449,7 +448,6 @@
                         <remove-private>
                             <limited/>
                         </remove-private>
-                        <multipath-eligible/>
                         <prefix-limit>
                             <family/>
                             <maximum/>
@@ -461,6 +459,7 @@
                     <neighbor>
                         <ip-address>{neighbor}</ip-address>
                         <group/>
+                        <type/>
                         <authentication-keychain/>
                         <description/>
                         <peer-as/>
@@ -490,25 +489,23 @@
             </router>
             <service>
                 <vprn>
-                    <autonomous-system/>
-                    <bgp>
+                  <autonomous-system/>
+                  <bgp>
                     <description/>
                     <apply-groups/>
                     <multihop/>
                     <local-as>
                         <as-number/>
                     </local-as>
-                    <import>
-                        <policy/>
-                    </import>
-                    <export>
-                        <policy/>
-                    </export>
                     <remove-private/>
                     <cluster>
                         <cluster-id/>
                     </cluster>
-
+                    <multipath>
+                     <ebgp/>
+                     <ibgp/>
+                    </multipath>
+                    <client-reflect/>
                     <group>
                         <group-name>{group_name}</group-name>
                         <description/>
@@ -540,15 +537,17 @@
                         <cluster>
                             <cluster-id/>
                         </cluster>
+                        <client-reflect/>
                     </group>
-
                     <neighbor>
                         <ip-address>{neighbor}</ip-address>
                         <group/>
+                        <type/>
                         <authentication-keychain/>
                         <description/>
                         <peer-as/>
                         <next-hop-self/>
+                        <client-reflect/>
                         <local-as>
                             <as-number/>
                         </local-as>
@@ -572,7 +571,7 @@
                             <cluster-id/>
                         </cluster>
                     </neighbor>
-                </bgp>
+                  </bgp>
                 </vprn>
             </service>
         </configure>
diff --git a/napalm_sros/sros.py b/napalm_sros/sros.py
index f8448f6..1c6c654 100644
--- a/napalm_sros/sros.py
+++ b/napalm_sros/sros.py
@@ -51,10 +51,14 @@
 
 # import local modules
 from napalm_sros.utils.parse_output_to_dict import parse_with_textfsm
-from napalm_sros.nc_filters import *
+from napalm_sros.nc_filters import GET_ARP_TABLE,GET_BGP_CONFIG,GET_ENVIRONMENT, \
+     GET_FACTS,GET_INTERFACES,GET_INTERFACES_COUNTERS,GET_INTERFACES_IP, \
+     GET_IPV6_NEIGHBORS_TABLE,GET_LLDP_NEIGHBORS,GET_LLDP_NEIGHBORS_DETAIL, \
+     GET_NETWORK_INSTANCES,GET_NTP_PEERS,GET_NTP_SERVERS,GET_OPTICS, \
+     GET_PROBES_CONFIG,GET_ROUTE_TO,GET_SNMP_INFORMATION,GET_USERS
 from napalm_sros.utils.utils import init_logging
 
-from .api import get_bgp_neighbors,get_bgp_neighbors_detail
+from .api import get_bgp_neighbors, get_bgp_neighbors_detail
 
 log = init_logging()
 
@@ -75,15 +79,25 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
         self.locked = False
         self.terminal_stdout_re = [
             re.compile(
-                "[\r\n]*\!?\*?(\((ex|gl|pr|ro)\))?\[.*\][\r\n]+[ABCD]\:\S+\@\S+\#\s$"
+                r"[\r\n]*\!?\*?(\((ex|gl|pr|ro)\))?\[.*\][\r\n]+[ABCD]\:\S+\@\S+\#\s$"
             ),
-            re.compile("[\r\n]*\*?[ABCD]:[\w\-\.\,\>]+[#\$]\s"),
+            re.compile(r"[\r\n]*\*?[ABCD]:[\w\-\.\,\>]+[#\$]\s"),
         ]
         self.terminal_stderr_re = [
             re.compile("Error: .*[\r\n]+"),
             re.compile("(MINOR|MAJOR|CRITICAL): .*[\r\n]+"),
         ]
 
+        self.ipv4_address_re = re.compile(
+            r"(([2][5][0-5]\.)|([2][0-4][0-9]\.)|([0-1]?[0-9]?[0-9]\.)){3}"
+            + "(([2][5][0-5])|([2][0-4][0-9])|([0-1]?[0-9]?[0-9]))"
+        )
+
+        self.ipv6_address_re = re.compile(
+          r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"
+        )
+        self.cmd_line_pattern_re = re.compile(r"\*?(.*?)(>.*)*#.*?")
+
         if optional_args is None:
             optional_args = {}
         self.sros_get_format = optional_args.get("sros_get_format", "xml")
@@ -157,7 +171,12 @@ def _create_ssh(self):
             print("Error in opening a ssh connection: {}".format(e))
             log.error("Error in opening a ssh connection: %s" % traceback.format_exc())
 
-    def _perform_cli_commands(self, commands, is_get):
+    def _perform_cli_commands(self, commands, is_get, no_more=False):
+        if no_more:
+            # Disable paged responses, note that the '/' changes the filenames
+            # for mocked data responses under test/unit/mocked_data
+            # - they now require a leading '_'
+            commands = ["/environment more false"] + commands
         try:
             is_alive = False
             if self.conn_ssh is not None:
@@ -175,12 +194,13 @@ def _perform_cli_commands(self, commands, is_get):
                         resp = self.ssh_channel.recv(9999)
                         buff += resp.decode("ascii")
                         if re.search(self.terminal_stdout_re[0], buff):
-                            break
+                          break
             else:
                 # chunk commands into lists of length 500
                 # send all the 500 commands together
                 # receive the output from the router
-                # the last while loop is to ensure that we have received all the output from the router for the remaining commands
+                # the last while loop is to ensure that we have received all the output 
+                # from the router for the remaining commands
 
                 command_list = []
                 if len(commands) > 500:
@@ -218,7 +238,8 @@ def _perform_cli_commands(self, commands, is_get):
             return buff
         except Exception as e:
             print("Error in method perform cli commands : {}".format(e))
-            log.error("Error in method perform cli commands : %s" % traceback.format_exc())
+            log.error("Error in _perform_cli_commands : %s" % traceback.format_exc())
+            return None
 
     def _lock_config(self):
         if not self.locked:
@@ -298,7 +319,7 @@ def is_alive(self):
             return is_alive_dict
         except Exception as e:  # in case of any exception, returns default
             print("Error occurred in is_alive method: {}".format(e))
-            logging.error("Error occurred in is_alive method: %s" % traceback.format_exc())
+            logging.error("Error occurred in is_alive: %s" % traceback.format_exc())
 
     def discard_config(self):
         """
@@ -319,9 +340,8 @@ def commit_config(self, message="", revert_in=None):
             buff = self._perform_cli_commands(["commit"], True)
             # If error while performing commit, return the error
             error = ""
-            cmd_line_pattern = re.compile("\*?(.*?)(>.*)*#\s")
             for item in buff.split("\n"):
-                if cmd_line_pattern.search(item):
+                if self.cmd_line_pattern_re.search(item):
                     continue
                 if any(match.search(item) for match in self.terminal_stderr_re):
                     row = item.strip()
@@ -338,7 +358,7 @@ def rollback(self):
         """
         If changes were made, revert changes to the original state.
         """
-        cmd = ["quit-config", "configure exclusive", "rollback 1", "commit", "exit"]
+        cmd = ["/quit-config", "/configure exclusive", "rollback 1", "commit", "exit"]
         buff = self._perform_cli_commands(cmd, True)
         error = ""
 
@@ -362,11 +382,8 @@ def compare_config(self):
         """
         buff = ""
         if self.fmt == "text":
-            buff = self._perform_cli_commands(
-                ["environment more false", "compare"], True
-            )
-            cmd_line_pattern = re.compile("\*?(.*?)(>.*)*#.*?")
-            # buff = self._perform_cli_commands(["environment more false", "compare"])
+            buff = self._perform_cli_commands( ["compare"], True, no_more=True )
+            # buff = self._perform_cli_commands(["/environment more false", "compare"])
         else:
             #  if format is xml we convert them into dict and perform a diff on configs to return the difference
             # running_dict = xmltodict.parse(running_config, process_namespaces=True)
@@ -403,9 +420,9 @@ def compare_config(self):
                     continue
                 elif "(ex)[]" in item:
                     continue
-                elif "environment more false" in item:
+                elif "/environment more false" in item:
                     continue
-                elif cmd_line_pattern.search(item):
+                elif self.cmd_line_pattern_re.search(item):
                     continue
                 elif self.terminal_stdout_re[0].search(item):
                     continue
@@ -443,44 +460,42 @@ def load_merge_candidate(self, filename=None, config=None):
             with open(filename) as f:
                 configuration = f.read()
 
-        try:
-            self.fmt = self._determinne_config_format(configuration)
-            if self.fmt == "xml":
-                if not self.lock_disable and not self.session_config_lock:
-                    self._lock_config()
-                configuration = etree.XML(configuration)
-                configuration_tree = etree.ElementTree(configuration)
-                root = configuration_tree.getroot()
-                if root.tag != "{urn:ietf:params:xml:ns:netconf:base:1.0}config":
-                    newroot = etree.Element(
-                        "{urn:ietf:params:xml:ns:netconf:base:1.0}config"
-                    )
-                    newroot.insert(0, root)
-                    self.conn.edit_config(
-                        config=newroot, target="candidate", default_operation="merge"
-                    )
+        self.fmt = self._determinne_config_format(configuration)
+        if self.fmt == "xml":
+            if not self.lock_disable and not self.session_config_lock:
+                self._lock_config()
+            configuration = etree.XML(configuration)
+            configuration_tree = etree.ElementTree(configuration)
+            root = configuration_tree.getroot()
+            if root.tag != "{urn:ietf:params:xml:ns:netconf:base:1.0}config":
+                newroot = etree.Element(
+                    "{urn:ietf:params:xml:ns:netconf:base:1.0}config"
+                )
+                newroot.insert(0, root)
+                self.conn.edit_config(
+                    config=newroot, target="candidate", default_operation="merge"
+                )
 
-                else:
-                    self.conn.edit_config(
-                        config=configuration,
-                        target="candidate",
-                        default_operation="merge",
-                    )
-                self.conn.validate(source="candidate")
+            else:
+                self.conn.edit_config(
+                    config=configuration,
+                    target="candidate",
+                    default_operation="merge",
+                )
+            self.conn.validate(source="candidate")
 
+        else:
+            configuration = configuration.split("\n")
+            configuration.insert(0, "edit-config exclusive")
+            buff = self._perform_cli_commands(configuration, False)
+            # error checking
+            if buff is not None:
+                for item in buff.split("\n"):
+                    if any(match.search(item) for match in self.terminal_stderr_re):
+                        log.error(f"Merge issue : {item}")
+                        raise MergeConfigException("Merge issue: %s", item)
             else:
-                configuration = configuration.split("\n")
-                configuration.insert(0, "edit-config exclusive")
-                buff = self._perform_cli_commands(configuration, False)
-                # error checking
-                if buff is not None:
-                    for item in buff.split("\n"):
-                        if any(match.search(item) for match in self.terminal_stderr_re):
-                            raise MergeConfigException("Merge issue: %s", item)
-
-        except MergeConfigException as me:
-            print("Merge issue : {}".format(me))
-            log.error("Merge issue : %s" %traceback.format_exc())
+                raise MergeConfigException("Timeout during load_merge_candidate")
 
     def load_replace_candidate(self, filename=None, config=None):
         """
@@ -501,42 +516,40 @@ def load_replace_candidate(self, filename=None, config=None):
             with open(filename) as f:
                 configuration = f.read()
 
-        try:
-            self.fmt = self._determinne_config_format(configuration)
-            if self.fmt == "xml":
-                if not self.lock_disable and not self.session_config_lock:
-                    self._lock_config()
-                configuration = etree.XML(configuration)
-                configuration_tree = etree.ElementTree(configuration)
-                root = configuration_tree.getroot()
-                if root.tag != "{urn:ietf:params:xml:ns:netconf:base:1.0}config":
-                    newroot = etree.Element(
-                        "{urn:ietf:params:xml:ns:netconf:base:1.0}config"
-                    )
-                    newroot.insert(0, root)
-                    self.conn.edit_config(
-                        config=newroot, target="candidate", default_operation="replace",
-                    )
-                else:
-                    self.conn.edit_config(
-                        config=configuration,
-                        target="candidate",
-                        default_operation="replace",
-                    )
-                self.conn.validate(source="candidate")
+        self.fmt = self._determinne_config_format(configuration)
+        if self.fmt == "xml":
+            if not self.lock_disable and not self.session_config_lock:
+                self._lock_config()
+            configuration = etree.XML(configuration)
+            configuration_tree = etree.ElementTree(configuration)
+            root = configuration_tree.getroot()
+            if root.tag != "{urn:ietf:params:xml:ns:netconf:base:1.0}config":
+                newroot = etree.Element(
+                    "{urn:ietf:params:xml:ns:netconf:base:1.0}config"
+                )
+                newroot.insert(0, root)
+                self.conn.edit_config(
+                    config=newroot, target="candidate", default_operation="replace",
+                )
+            else:
+                self.conn.edit_config(
+                    config=configuration,
+                    target="candidate",
+                    default_operation="replace",
+                )
+            self.conn.validate(source="candidate")
+        else:
+            configuration = configuration.split("\n")
+            configuration.insert(0, "edit-config exclusive")
+            configuration.insert(1, "delete configure")
+            buff = self._perform_cli_commands(configuration, False)
+            # error checking
+            if buff is not None:
+                for item in buff.split("\n"):
+                    if any(match.search(item) for match in self.terminal_stderr_re):
+                        raise ReplaceConfigException("Replace issue: %s", item)
             else:
-                configuration = configuration.split("\n")
-                configuration.insert(0, "edit-config exclusive")
-                configuration.insert(1, "delete configure")
-                buff = self._perform_cli_commands(configuration, False)
-                # error checking
-                if buff is not None:
-                    for item in buff.split("\n"):
-                        if any(match.search(item) for match in self.terminal_stderr_re):
-                            raise ReplaceConfigException("Replace issue: %s", item)
-        except ReplaceConfigException as rex:
-            print("Replace issue: {}".format(rex))
-            log.error("Replace issue: %s" % traceback.format_exc())
+                raise ReplaceConfigException("Timeout during load_replace_candidate")
 
     def get_facts(self):
         """
@@ -567,10 +580,12 @@ def get_facts(self):
                 default="",
                 namespaces=self.nsmap,
             )
-            # From uptime, removing last three digits which are milliseconds
+            # In uptime, last three digits are milliseconds
             if uptime:
-                uptime = uptime[:-3]
-                uptime = convert(int, uptime, default=0)
+                uptime = uptime[:-3]+ "." + uptime[-3:]
+                uptime = convert(float, uptime, default=0.0)
+            else:
+                uptime = -1.0
             interfaces = result.xpath(
                 "state_ns:state/state_ns:router/state_ns:interface/state_ns:interface-name",
                 namespaces=self.nsmap,
@@ -1126,7 +1141,6 @@ def _update_buff(buff):
                     else:
                         updated_buff = [buff]
                     new_buff = ""
-                    cmd_line_pattern = re.compile("\*?(.*?)(>.*)*#.*?")
                     match_strings = [
                         cmd_candidate[0],
                         cmd_candidate[1],
@@ -1140,7 +1154,7 @@ def _update_buff(buff):
                             continue
                         if "[]" in item:
                             continue
-                        elif cmd_line_pattern.search(item) or not row:
+                        elif self.cmd_line_pattern_re.search(item) or not row:
                             continue
                         elif "persistent-indices" in item:
                             break
@@ -1188,7 +1202,7 @@ def _update_buff(buff):
                     )
                     # remove xml declaration
                     config_data_running_xml = re.sub(
-                        "<\?xml.*\?>", "", config_data_running_xml
+                        r"<\?xml.*\?>", "", config_data_running_xml
                     )
                     configuration["running"] = config_data_running_xml
 
@@ -1205,7 +1219,7 @@ def _update_buff(buff):
                         )[0]
                     )
                     config_data_candidate_xml = re.sub(
-                        "<\?xml.*\?>", "", config_data_candidate_xml
+                        r"<\?xml.*\?>", "", config_data_candidate_xml
                     )
                     configuration["candidate"] = config_data_candidate_xml
                 return configuration
@@ -1582,17 +1596,13 @@ def get_ntp_stats(self):
 
             # helper method
             def _get_ntp_stats_data(buff):
-                ip_pattern = re.compile(
-                    "(([2][5][0-5]\.)|([2][0-4][0-9]\.)|([0-1]?[0-9]?[0-9]\.)){3}"
-                    + "(([2][5][0-5])|([2][0-4][0-9])|([0-1]?[0-9]?[0-9]))"
-                )
                 dashed_row = False
                 temp_dict = {}
                 for item in buff.split("\n"):
                     if "---" in item:
                         dashed_row = True
                         continue
-                    if ip_pattern.search(item) or dashed_row:
+                    if self.ipv4_address_re.search(item) or dashed_row:
                         row = item.strip()
                         row_list = row.split()
                         if len(row_list) == 8:
@@ -1619,11 +1629,11 @@ def _get_ntp_stats_data(buff):
                 ntp_stats_list.append(temp_dict)
                 return ntp_stats_list
 
-            cmd = ["environment more false", "show system ntp servers"]
-            buff_servers = self._perform_cli_commands(cmd, True)
+            cmd = ["/show system ntp servers"]
+            buff_servers = self._perform_cli_commands(cmd, True, no_more=True)
             ntp_stats_list = _get_ntp_stats_data(buff_servers)
-            cmd = ["environment more false", "show system ntp peers"]
-            buff_peers = self._perform_cli_commands(cmd, True)
+            cmd = ["/show system ntp peers"]
+            buff_peers = self._perform_cli_commands(cmd, True, no_more=True)
             ntp_stats_list = _get_ntp_stats_data(buff_peers)
 
             return ntp_stats_list
@@ -1803,10 +1813,8 @@ def get_route_to(self, destination="", protocol="", longer=False):
 
             def _get_protocol_attributes(router_name, local_protocol):
                 # destination needs to be with prefix
-                command = f"show router {router_name} route-table {destination} protocol {local_protocol} extensive all"
-                output = self._perform_cli_commands(
-                    ["environment more false", command], True
-                )
+                command = f"/show router {router_name} route-table {destination} protocol {local_protocol} extensive all"
+                output = self._perform_cli_commands([command], True, no_more=True)
                 destination_address_with_prefix = ""
                 next_hop_once = False
                 next_hop = ""
@@ -1873,10 +1881,8 @@ def _get_protocol_attributes(router_name, local_protocol):
                     elif "Next-Hop" in item_1:
                         row_1 = item_1.strip()
                         row_1_list = row_1.split(": ")
-                        if ip_pattern.search(row_1_list[1]):
-                            temp_2_dict = {"selected_next_hop": True}
-                        else:
-                            temp_2_dict = {"selected_next_hop": False}
+                        _sel = self.ipv4_address_re.search(row_1_list[1])
+                        temp_2_dict = {"selected_next_hop": bool(_sel)}
                         if "Indirect" in item_1:
                             if next_hop_once:
                                 next_hop = row_1_list[1]
@@ -1947,10 +1953,8 @@ def _get_bgp_protocol_attributes(router_name):
                     destination_address_with_prefix = k
                 if destination_address_with_prefix:
                     # protocol attributes local_as, as_path, local_preference
-                    cmd = f"show router {router_name} bgp routes {destination_address_with_prefix} detail"
-                    buff_1 = self._perform_cli_commands(
-                        ["environment more false", cmd], True
-                    )
+                    cmd = f"/show router {router_name} bgp routes {destination_address_with_prefix} detail"
+                    buff_1 = self._perform_cli_commands( [cmd], True, no_more=True )
 
                     for d in route_to_dict[destination_address_with_prefix]:
                         next_hop = d.get("next_hop")
@@ -2057,10 +2061,8 @@ def _get_isis_protocol_attributes(router_name):
                 if destination_address_with_prefix:
                     for d in route_to_dict[destination_address_with_prefix]:
                         d.update({"protocol_attributes": {}})
-                    command = f"show router {router_name} isis routes ip-prefix-prefix-length {destination_address_with_prefix}"
-                    buff_1 = self._perform_cli_commands(
-                        ["environment more false", command], True
-                    )
+                    command = f"/show router {router_name} isis routes ip-prefix-prefix-length {destination_address_with_prefix}"
+                    buff_1 = self._perform_cli_commands([command], True, no_more=True)
                     prev_row = ""
                     for item_1 in buff_1.split("\n"):
                         if destination_address_with_prefix in item_1 or prev_row:
@@ -2088,10 +2090,8 @@ def _get_ospf_protocol_attributes(router_name):
                 if destination_address_with_prefix:
                     for d in route_to_dict[destination_address_with_prefix]:
                         d.update({"protocol_attributes": {}})
-                    command = f"show router {router_name} ospf routes {destination_address_with_prefix}"
-                    buff_1 = self._perform_cli_commands(
-                        ["environment more false", command], True
-                    )
+                    command = f"/show router {router_name} ospf routes {destination_address_with_prefix}"
+                    buff_1 = self._perform_cli_commands([command], True, no_more=True)
                     first_row = False
                     for item_1 in buff_1.split("\n"):
                         if destination_address_with_prefix in item_1 or first_row:
@@ -2126,11 +2126,6 @@ def _get_ospf_protocol_attributes(router_name):
                     self._find_txt(vprn, "state_ns:oper-service-id", namespaces=self.nsmap)
                 )
 
-            ip_pattern = re.compile(
-                "(([2][5][0-5]\.)|([2][0-4][0-9]\.)|([0-1]?[0-9]?[0-9]\.)){3}"
-                + "(([2][5][0-5])|([2][0-4][0-9])|([0-1]?[0-9]?[0-9]))"
-            )
-
             for name in name_list:
 
                 bgp_once = False
@@ -2144,13 +2139,13 @@ def _get_ospf_protocol_attributes(router_name):
                         destination_address_with_prefix = destination + "/32"
                     else:
                         destination_address_with_prefix = destination
-                    cmd = f"show router {name} route-table {destination_address_with_prefix} longer\n"
+                    cmd = f"/show router {name} route-table {destination_address_with_prefix} longer\n"
                 else:
-                    cmd = f"show router {name} route-table {destination} \n"
+                    cmd = f"/show router {name} route-table {destination} \n"
 
-                buff = self._perform_cli_commands(["environment more false", cmd], True)
+                buff = self._perform_cli_commands([cmd], True, no_more=True)
                 for item in buff.split("\n"):
-                    if ip_pattern.search(item):
+                    if self.ipv4_address_re.search(item):
                         if "# show" in item:
                             continue
                         row = item.strip()
@@ -2234,8 +2229,8 @@ def get_probes_results(self):
                     probes_results.update({probe_name: {}})
                 probes_results[probe_name].update({test_name: {}})
                 path = "configure_ns:type/configure_ns:icmp-ping"
-                cmd = f"show saa {test_name}"
-                buff = self._perform_cli_commands(["environment more false", cmd], True)
+                cmd = f"/show saa {test_name}"
+                buff = self._perform_cli_commands([cmd], True, no_more=True)
                 test_number_1 = ""
                 test_number_2 = 0
                 found_first_test = False
@@ -2247,7 +2242,6 @@ def get_probes_results(self):
                 current_test_max_delay = ""
                 current_test_avg_delay = ""
                 roundtrip_jitter = ""
-                current_test_loss = ""
                 for item in buff.split("\n"):
                     if "Test runs since last clear" in item:
                         row = item.strip()
@@ -2440,8 +2434,8 @@ def get_mac_address_table(self):
         try:
             mac_address_list = []
 
-            cmd = "show service fdb-mac"
-            buff = self._perform_cli_commands(["environment more false", cmd], True)
+            cmd = "/show service fdb-mac"
+            buff = self._perform_cli_commands([cmd], True, no_more=True)
             template = "textfsm_templates//nokia_sros_show_service_fdb_mac.tpl"
             # template = "textfsm_templates\\nokia_sros_show_service_fdb_mac.tpl"
             output_list = parse_with_textfsm(template, buff)
@@ -2617,6 +2611,10 @@ def get_bgp_config(self, group="", neighbor=""):
                 }
             }
         """
+        #
+        # Note: returns all bgp neighbors across all VRFs, merging groups with the same name
+        # Does not (cannot) report dynamic neighbors, only static ones
+        #
         try:
             bgp_config = {}
 
@@ -2664,64 +2662,69 @@ def _get_policies(policies_xml):
                 policies = [ele.text for ele in policies_xml]
                 return ", ".join(policies)
 
-            def _get_bgp_neighbor_group(bgp_neighbors, global_autonomous):
+            def _route_reflect(xml):
+              _cluster_id = self._find_txt(xml,"configure_ns:cluster/configure_ns:cluster-id",namespaces=self.nsmap)
+              _client_reflect = self._find_txt(xml,"configure_ns:client-reflect", namespaces=self.nsmap)
+              return (bool(_cluster_id),_client_reflect) # keep client_reflect as string to distinguish between not set and 'false'
+
+            def _get_bgp_neighbor_group(bgp_neighbors,global_autonomous):
                 for bgp_neighbor in bgp_neighbors:
                     group_name = self._find_txt(
                         bgp_neighbor, "configure_ns:group", namespaces=self.nsmap
                     )
+
+                    def _group_attr(attr):
+                      return bgp_groups[group_name][attr] if group_name in bgp_groups and attr in bgp_groups[group_name] else None
+
                     peer = ip(
                         self._find_txt(
                             bgp_neighbor, "configure_ns:ip-address", namespaces=self.nsmap
                         )
                     )
-                    type_ = self._find_txt(
-                        bgp_neighbor, "configure_ns:type", namespaces=self.nsmap
-                    )
 
                     if neighbor != "" and peer != neighbor:
                         continue
 
-                    nhs = (
-                        True
-                        if self._find_txt(
-                            bgp_neighbor,
-                            "configure_ns:next-hop-self",
-                            namespaces=self.nsmap,
-                        )
-                        == "true"
-                        else False
-                    )
-                    cluster_id = self._find_txt(
-                        bgp_neighbor,
-                        "configure_ns:cluster/configure_ns:cluster-id",
-                        namespaces=self.nsmap,
-                    )
-                    client_reflect = self._find_txt(
-                        bgp_neighbor, "configure_ns:client-reflect", namespaces=self.nsmap
+                    # JvB note: 'type' configuration allows implicit peer AS configuration for iBGP
+                    type_ = self._find_txt(
+                        bgp_neighbor, "configure_ns:type", namespaces=self.nsmap
+                    ) or _group_attr('type')
+
+                    _nhs = self._find_txt(
+                        bgp_neighbor,"configure_ns:next-hop-self",namespaces=self.nsmap,
                     )
-                    route_reflector = ""
-                    if cluster_id and client_reflect == "true":
-                        route_reflector = True
+                    _next_hop_self = (_nhs != "false") if _nhs else _group_attr('_nhs')
+
+                    _cluster_id,_client_reflect = _route_reflect(bgp_neighbor)
+                    route_reflector = (_cluster_id or _group_attr('_cluster_id')) \
+                                  and (_group_attr('_client_reflect') and _client_reflect=="")
 
                     explicit_local_as = self._find_txt(
                         bgp_neighbor,
                         "configure_ns:local-as/configure_ns:as-number",
                         namespaces=self.nsmap,
                     )
-                    if explicit_local_as:
-                        local_as = explicit_local_as
-                    else:
-                        local_as = global_autonomous
+
+                    # Order of priority:
+                    # 1. Neighbor level local-as
+                    # 2. Group level local-as
+                    # 3. Global AS
+                    local_as = explicit_local_as or _group_attr('local_as') or global_autonomous
 
                     explicit_peer_as = self._find_txt(
                         bgp_neighbor, "configure_ns:peer-as", namespaces=self.nsmap
                     )
+
                     if explicit_peer_as:
-                        peer_as = explicit_peer_as
-                    elif type_ == "internal" and not explicit_peer_as:
-                        peer_as = global_autonomous
+                      peer_as = explicit_peer_as
                     else:
-                        peer_as = 0
+                      group_remote_as = _group_attr('remote_as')
+                      if group_remote_as:
+                        peer_as = group_remote_as
+                      elif type_=="internal": # implicit peer_as configuration
+                        peer_as = local_as
+                      else:
+                        peer_as = 0 # Not configured
 
                     if group_name not in bgp_group_neighbors.keys():
                         bgp_group_neighbors[group_name] = {}
@@ -2729,6 +2732,7 @@ def _get_bgp_neighbor_group(bgp_neighbors, global_autonomous):
                         "description": self._find_txt(
                             bgp_neighbor, "configure_ns:description", namespaces=self.nsmap
                         ),
+                        "local_as": as_number(local_as),
                         "remote_as": as_number(peer_as),
                         "prefix_limit": _build_prefix_limit(bgp_neighbor),
                         "import_policy": _get_policies(
@@ -2751,19 +2755,19 @@ def _get_bgp_neighbor_group(bgp_neighbors, global_autonomous):
                                 namespaces=self.nsmap,
                             ),
                         ),
-                        "local_as": as_number(local_as),
+                        # Note: ignoring any group level authentication key here
                         "authentication_key": self._find_txt(
                             bgp_neighbor,
                             "configure_ns:authentication-key",
                             namespaces=self.nsmap,
                         ),
-                        "nhs": nhs,
+                        "nhs": bool(_next_hop_self),
                         "route_reflector_client": route_reflector,
                     }
                     if neighbor != "" and peer == neighbor:
                         break
 
-            def _get_bgp_group_data(bgp_groups_list):
+            def _get_bgp_group_data(bgp_groups_list,local_as_number,g_cluster_id,g_client_reflect):
                 for bgp_group in bgp_groups_list:
                     group_name = self._find_txt(
                         bgp_group, "configure_ns:group-name", namespaces=self.nsmap
@@ -2771,10 +2775,6 @@ def _get_bgp_group_data(bgp_groups_list):
                     if group != "" and group != group_name:
                         continue
 
-                    type_ = self._find_txt(
-                        bgp_group, "configure_ns:type", namespaces=self.nsmap
-                    )
-
                     remove_private = (
                         True
                         if self._find_txt(
@@ -2785,58 +2785,39 @@ def _get_bgp_group_data(bgp_groups_list):
                         == "true"
                         else False
                     )
-                    multipath = (
-                        True
-                        if self._find_txt(
-                            bgp_group,
-                            "configure_ns:multipath-eligible",
-                            namespaces=self.nsmap,
-                        )
-                        == "true"
-                        else False
+                    type_ = self._find_txt(
+                        bgp_group, "configure_ns:type", namespaces=self.nsmap
                     )
-
                     explicit_local_as = self._find_txt(
                         bgp_group,
                         "configure_ns:local-as/configure_ns:as-number",
                         namespaces=self.nsmap,
                     )
-                    if explicit_local_as:
-                        local_as = explicit_local_as
-                    else:
-                        local_as = global_as
+                    local_as = int(explicit_local_as or local_as_number)
 
                     explicit_peer_as = self._find_txt(
                         bgp_group, "configure_ns:peer-as", namespaces=self.nsmap
                     )
                     if explicit_peer_as:
-                        peer_as = explicit_peer_as
-                    elif type_ == "internal" and not explicit_peer_as:
-                        peer_as = global_as
-                    elif type_ == "internal" and explicit_local_as:
-                        peer_as = explicit_local_as
+                      peer_as = int(explicit_peer_as)
+                      type_ = "internal" if peer_as==local_as else "external"
+                    elif type_=="internal":
+                      peer_as = local_as # Implicitly configured
                     else:
-                        peer_as = 0
+                      peer_as = 0 # Not configured, type_ may be 'no-type'
 
-                    nhs = (
-                        True
-                        if self._find_txt(
-                            bgp_group, "configure_ns:next-hop-self", namespaces=self.nsmap
-                        )
-                        == "true"
-                        else False
-                    )
-                    cluster_id = self._find_txt(
+                    xbgp = "ibgp" if type_=="internal" else "ebgp"
+                    max_path = self._find_txt(
                         bgp_group,
-                        "configure_ns:cluster/configure_ns:cluster-id",
+                        f"../configure_ns:multipath/configure_ns:{xbgp}",
                         namespaces=self.nsmap,
                     )
-                    client_reflect = self._find_txt(
-                        bgp_group, "configure_ns:client-reflect", namespaces=self.nsmap
-                    )
-                    route_reflector = False
-                    if cluster_id and client_reflect == "true":
-                        route_reflector = True
+                    multipath = bool( peer_as and max_path and int(max_path)>1 )
+
+                    _nhs = self._find_txt(bgp_group, "configure_ns:next-hop-self", namespaces=self.nsmap)
+                    # Can only set client_reflect to 'false' at group level
+                    _cluster_id,_client_reflect = _route_reflect(bgp_group)
+
                     apply_groups_list = []
                     for apply_group in bgp_group.xpath(
                         "configure_ns:apply-groups", namespaces=self.nsmap
@@ -2844,12 +2825,14 @@ def _get_bgp_group_data(bgp_groups_list):
                         apply_groups_list.append(apply_group)
 
                     bgp_groups[group_name] = {
-                        "apply_groups": apply_groups_list,
+                        "type": type_,
                         "description": self._find_txt(
                             bgp_group, "configure_ns:description", namespaces=self.nsmap
                         ),
+                        "apply_groups": apply_groups_list,
                         "local_as": as_number(local_as),
-                        "type": type_,
+                        "remote_as": as_number(peer_as),
+                        "remove_private_as": remove_private,
                         "import_policy": _get_policies(
                             bgp_group.xpath(
                                 "configure_ns:import/configure_ns:policy",
@@ -2878,11 +2861,10 @@ def _get_bgp_group_data(bgp_groups_list):
                             ),
                             default=-1,
                         ),
-                        "remote_as": as_number(peer_as),
-                        "remove_private_as": remove_private,
                         "prefix_limit": _build_prefix_limit(bgp_group),
-                        "_nhs": nhs,
-                        "_route_reflector_client": route_reflector,
+                        "_nhs": bool(_nhs != "false"),
+                        "_cluster_id": g_cluster_id or bool(_cluster_id),
+                        "_client_reflect": g_client_reflect and _client_reflect=="",
                         "neighbors": {},
                     }
                     if group != "" and group == group_name:
@@ -2894,77 +2876,57 @@ def _get_bgp_group_data(bgp_groups_list):
                     with_defaults="report-all",
                 ).data_xml
             )
+            # print( to_xml(bgp_running_config, pretty_print=True) )
 
             bgp_group_neighbors = {}
             bgp_groups = {}
+            global_as = self._find_txt(
+                bgp_running_config,
+                "configure_ns:configure/configure_ns:router/configure_ns:autonomous-system",
+                namespaces=self.nsmap,
+            )
 
-            for bgp_neighbor_router in bgp_running_config.xpath(
+            for router in bgp_running_config.xpath(
                 "configure_ns:configure/configure_ns:router/configure_ns:bgp",
                 namespaces=self.nsmap,
             ):
-                global_as = self._find_txt(
-                    bgp_running_config,
-                    "configure_ns:configure/configure_ns:router/configure_ns:autonomous-system",
-                    namespaces=self.nsmap,
+                _cluster_id,_client_reflect = _route_reflect(router)
+                _get_bgp_group_data(
+                    router.xpath("configure_ns:group", namespaces=self.nsmap),
+                    local_as_number=int(global_as),
+                    g_cluster_id=_cluster_id,g_client_reflect=_client_reflect
                 )
                 _get_bgp_neighbor_group(
-                    bgp_neighbor_router.xpath(
-                        "configure_ns:neighbor", namespaces=self.nsmap
-                    ),
+                    router.xpath("configure_ns:neighbor", namespaces=self.nsmap),
                     global_as,
                 )
 
-            for bgp_neighbor_vprn in bgp_running_config.xpath(
+            for vprn in bgp_running_config.xpath(
                 "configure_ns:configure/configure_ns:service/configure_ns:vprn/configure_ns:bgp",
                 namespaces=self.nsmap,
             ):
-                global_as = self._find_txt(
-                    bgp_running_config,
-                    "configure_ns:configure/configure_ns:service/configure_ns:vprn/configure_ns:autonomous-system",
+                vprn_as = self._find_txt(
+                    vprn,"../configure_ns:autonomous-system",
                     namespaces=self.nsmap,
                 )
-                _get_bgp_neighbor_group(bgp_neighbor_vprn.xpath("neighbor"), global_as)
-
-            if neighbor and not group:
-                logging.error("Specify a group where to look for given neighbor")
-                neighbor = ""
-                return bgp_config
-
-            for bgp_group_router in bgp_running_config.xpath(
-                "configure_ns:configure/configure_ns:router/configure_ns:bgp",
-                namespaces=self.nsmap,
-            ):
+                _cluster_id,_client_reflect = _route_reflect(vprn)
                 _get_bgp_group_data(
-                    bgp_group_router.xpath("configure_ns:group", namespaces=self.nsmap)
+                    vprn.xpath("configure_ns:group", namespaces=self.nsmap),
+                    local_as_number=int(vprn_as),
+                    g_cluster_id=_cluster_id,g_client_reflect=_client_reflect
                 )
-
-            for bgp_group_vprn in bgp_running_config.xpath(
-                "configure_ns:configure/configure_ns:service/configure_ns:vprn/configure_ns:bgp",
-                namespaces=self.nsmap,
-            ):
-                _get_bgp_group_data(
-                    bgp_group_vprn.xpath("configure_ns:group", namespaces=self.nsmap)
+                _get_bgp_neighbor_group(
+                    vprn.xpath("configure_ns:neighbor",namespaces=self.nsmap),
+                    vprn_as,
                 )
 
             # Assemble groups and neighbors
             for grp_name, grp_data in bgp_groups.items():
                 neighbors = bgp_group_neighbors.get(grp_name, {})
 
-                # Update values from group level
-                for n_data in neighbors.values():
-                    # Update NHS from group level
-                    if not n_data.get("nhs"):
-                        n_data["nhs"] = grp_data.get("_nhs")
-                    # Update remote-as to local-as when group is internal
-                    if grp_data.get("type") == "internal":
-                        n_data["remote_as"] = n_data.get("local_as")
-                    # Update route_reflector_client from group_level
-                    if not n_data.get("route_reflector_client"):
-                        n_data["route_reflector_client"] = grp_data.get(
-                            "_route_reflector_client"
-                        )
-                grp_data.pop("_nhs")  # remove temporary key
-                grp_data.pop("_route_reflector_client")  # remove temporary key
+                grp_data.pop("_nhs")  # remove temporary keys
+                grp_data.pop("_cluster_id")
+                grp_data.pop("_client_reflect")
 
                 grp_data["neighbors"] = neighbors  # Add updated neighbors to group
 
@@ -2988,6 +2950,7 @@ def _get_bgp_group_data(bgp_groups_list):
                 }
 
             return bgp_config
+
         except Exception as e:
             print("Error in method get bgp config : {}".format(e))
             log.error("Error in method get bgp config : %s" % traceback.format_exc())
@@ -3230,11 +3193,11 @@ def _build_temperature_dict(instance, choice=1):
             # get the output of each power-module using MD-CLI
             buff = self._perform_cli_commands(
                 [
-                    "environment more false",
-                    "show chassis power-management utilization detail",
+                    "/show chassis power-management utilization detail",
                     # JvB: on VSR this is 'power-supply'
                 ],
                 True,
+                no_more=True
             )
             total_power_modules = 0
             output = 0.0
@@ -3242,8 +3205,8 @@ def _build_temperature_dict(instance, choice=1):
                 if "Power Module" in item:
                     total_power_modules = total_power_modules + 1
                 if "Current Util." in item:
-                    row = item.strip()
-                    watts = re.match("^.*:\s*(\d+[.]\d+) Watts.*$", item)
+                    item.strip()
+                    watts = re.match(r"^.*:\s*(\d+[.]\d+) Watts.*$", item)
                     if watts:
                         output = float(watts.groups()[0])
 
@@ -3375,15 +3338,12 @@ def get_ipv6_neighbors_table(self):
             ipv6_neighbor_list = []
 
             for name in name_list:
-                cmd = ["environment more false", f"show router {name} neighbor"]
-                buff = self._perform_cli_commands(cmd, True)
-                ipv6_address_regex = re.compile(
-                    "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"
-                )
+                cmd = [f"/show router {name} neighbor"]
+                buff = self._perform_cli_commands(cmd, True, no_more=True)
                 prev_row = ""
                 ip_address = ""
                 for item in buff.split("\n"):
-                    if ipv6_address_regex.search(item) or prev_row:
+                    if self.ipv6_address_re.search(item) or prev_row:
                         row = item.strip()
                         prev_row = row
                         row_list = row.split()
@@ -3455,7 +3415,7 @@ def ping(
                 d6=str(count),
                 d7=vrf,
             )
-            buff = self._perform_cli_commands(["environment more false", command], True)
+            buff = self._perform_cli_commands([command], True, no_more=True)
             for item in buff.split("\n"):
                 if "No route to destination" in item:
                     value = "unknown host " + destination
@@ -3532,11 +3492,10 @@ def traceroute(
                 d1=destination, d2=str(timeout), d3=str(ttl), d4=source, d5=vrf,
             )
             command = [
-                "environment more false",
-                "environment progress-indicator admin-state disable",
+                "/environment progress-indicator admin-state disable",
                 cmd,
             ]
-            buff = self._perform_cli_commands(command, True)
+            buff = self._perform_cli_commands(command, True, no_more=True)
             for item in buff.split("\n"):
                 if "* * *" in item:
                     value = "unknown host " + destination
@@ -3581,20 +3540,21 @@ def traceroute(
             print("Error in method traceroute : {}".format(e))
             log.error("Error in method traceroute : %s" % traceback.format_exc())
 
-    def cli(self, commands):
+    def cli(self, commands, encoding="text"):
         """
         Will execute a list of commands and return the output in a dictionary format.
         """
+        if encoding not in ("text",):
+            raise NotImplementedError("%s is not a supported encoding" % encoding)
         try:
             cli_output = {}
             for cmd in commands:
                 buff = self._perform_cli_commands([cmd], True)
                 new_buff = ""
-                cmd_line_pattern = re.compile("\*?(.*?)(>.*)*#\s")
                 for item in buff.split("\n"):
                     if "[]" in item:
                         continue
-                    elif cmd_line_pattern.search(item):
+                    elif self.cmd_line_pattern_re.search(item):
                         continue
                     else:
                         row = item.strip()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..33eef13
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,37 @@
+[build-system]
+requires = ["setuptools>=56.0.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "napalm-sros"
+version="1.0.1"
+description="Network Automation and Programmability Abstraction Layer driver for Nokia SR OS"
+readme = "README.md"
+requires-python = ">=3.7"
+classifiers = [
+    "Topic :: System :: Networking",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Natural Language :: English",
+]
+
+authors = [
+  { name="Ashna Shah", email="ashna.shah@nokia.com" },
+  { name="Roman Dodin", email="roman.dodin@nokia.com" },
+  { name="Patryk Szulczewski", email="patryk.szulczewski@nokia.com" },
+  { name="Jeroen van Bemmel", email="jeroen.van_bemmel@nokia.com" },
+]
+
+[project.urls]
+"Homepage" = "https://github.com/napalm-automation-community/napalm-sros"
+"Bug Tracker" = "https://github.com/napalm-automation-community/napalm-sros/issues"
+
+[project.optional-dependencies]
+tests = [
+  'pytest',
+  'ruff',
+]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1b9ee1d..168db82 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,10 +1,13 @@
 coveralls
 ddt
-flake8-import-order
+# flake8
+# flake8-import-order==0.18.1
+ruff
+pre-commit
 pytest
 pytest-cov
 pytest-json
 pytest-pythonpath
 pylama
 mock
-tox
\ No newline at end of file
+tox
diff --git a/requirements.txt b/requirements.txt
index ebf9252..98b21ca 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,8 @@
-napalm>=3.4.1
+napalm>=4.0.0
 pytest>=7.0.1
 setuptools>=47.3.1
 pip>=21.3.1
-textfsm>=1.1.3
+textfsm
 paramiko>=2.11.0
 lxml>=4.9.1
 ncclient>=0.6.13
diff --git a/setup.py b/setup.py
index 434f22d..8da4843 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,8 @@
 from setuptools import setup, find_packages
 
+with open("requirements.txt", "r") as fs:
+    reqs = [r for r in fs.read().splitlines() if (len(r) > 0 and not r.startswith("#"))]
+
 with open("README.md", "r", encoding="utf-8") as f:
     long_description = f.read()
 
@@ -11,25 +14,21 @@
     author_email="",
     description="Network Automation and Programmability Abstraction Layer with Multivendor support",
     classifiers=[
-        "Topic :: Internet",
-        "Programming Language :: Python :: 3.6",
+        "Topic :: Utilities",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.11",
         "Natural Language :: English",
         "Development Status :: 4 - Beta",
+        "License :: OSI Approved :: Apache Software License",
     ],
     url="https://github.com/napalm-automation/napalm-sros",
     include_package_data=True,
-    install_requires=[
-        "napalm>=3.4.1",
-        "pytest>=7.0.1",
-        "textfsm>=1.1.3",
-        "paramiko>=2.11.0",
-        "lxml>=4.9.1",
-        "ncclient>=0.6.13",
-        "xmltodict>=0.12.0",
-        "dictdiffer>=0.9.0",
-        "datetime>=4.7",
-    ],
-    python_requires=">=3.6",
+    install_requires=reqs,
+    python_requires=">=3.7",
     long_description=long_description,
     long_description_content_type="text/markdown",
 )


Test patch:
diff --git a/test/sample_test_api.py b/test/sample_test_api.py
index 1dd1691..ea84bb4 100644
--- a/test/sample_test_api.py
+++ b/test/sample_test_api.py
@@ -19,7 +19,8 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-import logging, sys, os
+import sys
+import logging
 
 def test_get_bgp_neighbors_detail():
   LOG_FORMAT = '%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s'
diff --git a/test/unit/mocked_data/test_get_bgp_config/normal/expected_result.json b/test/unit/mocked_data/test_get_bgp_config/normal/expected_result.json
index dc29884..c23f770 100644
--- a/test/unit/mocked_data/test_get_bgp_config/normal/expected_result.json
+++ b/test/unit/mocked_data/test_get_bgp_config/normal/expected_result.json
@@ -1,58 +1,141 @@
 {
-    "eBGP-Peers": {
-        "apply_groups": [],
+  "ebgp": {
+    "type": "external",
+    "description": "",
+    "apply_groups": [],
+    "local_as": 65000,
+    "remote_as": 65001,
+    "remove_private_as": false,
+    "import_policy": "",
+    "export_policy": "",
+    "local_address": "",
+    "multipath": true,
+    "multihop_ttl": -1,
+    "prefix_limit": {},
+    "neighbors": {
+      "192.0.0.1": {
         "description": "",
         "local_as": 65000,
-        "type": "external",
-        "import_policy": "eBGP-import",
+        "remote_as": 65001,
+        "prefix_limit": {},
+        "import_policy": "",
         "export_policy": "",
         "local_address": "",
-        "multipath": false,
-        "multihop_ttl": -1,
-        "remote_as": 0,
-        "remove_private_as": false,
-        "prefix_limit": {},
-        "neighbors": {
-            "1.1.0.1": {
-              "description": "",
-              "remote_as": 65101,
-              "prefix_limit": {},
-              "import_policy": "",
-              "export_policy": "",
-              "local_address": "",
-              "local_as": 65000,
-              "authentication_key": "",
-              "nhs": false,
-              "route_reflector_client": false
-            }
-        }
-    },
-    "iBGP-Peers": {
-        "apply_groups": [],
+        "authentication_key": "",
+        "nhs": false,
+        "route_reflector_client": false
+      },
+      "192.0.0.3": {
         "description": "",
         "local_as": 65000,
-        "type": "internal",
-        "import_policy": "add-comm-ibgp",
+        "remote_as": 65002,
+        "prefix_limit": {},
+        "import_policy": "",
         "export_policy": "",
         "local_address": "",
-        "multipath": false,
-        "multihop_ttl": -1,
+        "authentication_key": "",
+        "nhs": false,
+        "route_reflector_client": false
+      }
+    }
+  },
+  "ibgp": {
+    "type": "external",
+    "description": "",
+    "apply_groups": [],
+    "local_as": 65123,
+    "remote_as": 0,
+    "remove_private_as": false,
+    "import_policy": "",
+    "export_policy": "",
+    "local_address": "",
+    "multipath": false,
+    "multihop_ttl": -1,
+    "prefix_limit": {},
+    "neighbors": {
+      "2001:192::2": {
+        "description": "",
+        "local_as": 65123,
+        "remote_as": 65123,
+        "prefix_limit": {},
+        "import_policy": "",
+        "export_policy": "",
+        "local_address": "",
+        "authentication_key": "",
+        "nhs": false,
+        "route_reflector_client": false
+      }
+    }
+  },
+  "ebgp-vprn": {
+    "type": "external",
+    "description": "",
+    "apply_groups": [],
+    "local_as": 65001,
+    "remote_as": 65000,
+    "remove_private_as": false,
+    "import_policy": "",
+    "export_policy": "",
+    "local_address": "",
+    "multipath": false,
+    "multihop_ttl": -1,
+    "prefix_limit": {},
+    "neighbors": {
+      "192.0.0.0": {
+        "description": "",
+        "local_as": 65001,
         "remote_as": 65000,
-        "remove_private_as": false,
         "prefix_limit": {},
-        "neighbors": {
-            "2.1.0.1": {
-                "description": "",
-                "remote_as": 65000,
-                "prefix_limit": {},
-                "import_policy": "",
-                "export_policy": "",
-                "local_address": "",
-                "local_as": 65000,
-                "authentication_key": "",
-                "nhs": false,
-                "route_reflector_client": false
-            }
-        }
+        "import_policy": "",
+        "export_policy": "",
+        "local_address": "",
+        "authentication_key": "",
+        "nhs": false,
+        "route_reflector_client": false
+      }
+    }
+  },
+  "ibgp-vprn": {
+    "type": "external",
+    "description": "",
+    "apply_groups": [],
+    "local_as": 65001,
+    "remote_as": 65123,
+    "remove_private_as": false,
+    "import_policy": "",
+    "export_policy": "",
+    "local_address": "",
+    "multipath": false,
+    "multihop_ttl": -1,
+    "prefix_limit": {},
+    "neighbors": {
+      "2001:192::1": {
+        "description": "",
+        "local_as": 65123,
+        "remote_as": 65123,
+        "prefix_limit": {},
+        "import_policy": "",
+        "export_policy": "",
+        "local_address": "",
+        "authentication_key": "",
+        "nhs": true,
+        "route_reflector_client": false
+      }
     }
+  },
+  "ebgp-vprn2": {
+    "type": "no-type",
+    "description": "",
+    "apply_groups": [],
+    "local_as": 65002,
+    "remote_as": 0,
+    "remove_private_as": true,
+    "import_policy": "",
+    "export_policy": "reject_all",
+    "local_address": "192.0.0.3",
+    "multipath": false,
+    "multihop_ttl": -1,
+    "prefix_limit": {},
+    "neighbors": {}
+  }
 }
diff --git a/test/unit/mocked_data/test_get_bgp_config/normal/get_bgp_config.xml b/test/unit/mocked_data/test_get_bgp_config/normal/get_bgp_config.xml
index 0d84c0f..f8ee47d 100644
--- a/test/unit/mocked_data/test_get_bgp_config/normal/get_bgp_config.xml
+++ b/test/unit/mocked_data/test_get_bgp_config/normal/get_bgp_config.xml
@@ -1,36 +1,164 @@
-<data xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <configure xmlns="urn:nokia.com:sros:ns:yang:sr:conf">
+<?xml version="1.0" encoding="UTF-8"?><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <configure xmlns="urn:nokia.com:sros:ns:yang:sr:conf" xmlns:nokia-attr="urn:nokia.com:sros:ns:yang:sr:attributes">
         <router>
             <router-name>Base</router-name>
             <autonomous-system>65000</autonomous-system>
             <bgp>
+                <client-reflect>true</client-reflect>
+                <cluster>
+                </cluster>
+                <local-as>
+                </local-as>
+                <import>
+                    <policy>accept_all</policy>
+                </import>
+                <multipath>
+                    <ebgp>2</ebgp>
+                </multipath>
                 <group>
-                    <group-name>eBGP-Peers</group-name>
-                    <type>external</type>
-                    <import>
-                        <policy>eBGP-import</policy>
-                    </import>
+                    <group-name>ebgp</group-name>
+                    <next-hop-self>false</next-hop-self>
+                    <type>internal</type>
+                    <peer-as>65001</peer-as>
+                    <local-as>
+                    </local-as>
                 </group>
                 <group>
-                    <group-name>iBGP-Peers</group-name>
-                    <type>internal</type>
+                    <group-name>ibgp</group-name>
+                    <next-hop-self>false</next-hop-self>
+                    <type>external</type>
+                    <local-as>
+                        <as-number>65123</as-number>
+                    </local-as>
+                </group>
+                <neighbor>
+                    <ip-address>192.0.0.1</ip-address>
+                    <group>ebgp</group>
                     <cluster>
-                        <cluster-id>10.1.1.1</cluster-id>
                     </cluster>
-                    <import>
-                        <policy>add-comm-ibgp</policy>
-                    </import>
-                </group>
+                    <local-as>
+                    </local-as>
+                </neighbor>
                 <neighbor>
-                    <ip-address>1.1.0.1</ip-address>
-                    <group>eBGP-Peers</group>
-                    <peer-as>65101</peer-as>
+                    <ip-address>192.0.0.3</ip-address>
+                    <group>ebgp</group>
+                    <peer-as>65002</peer-as>
+                    <cluster>
+                    </cluster>
+                    <local-as>
+                    </local-as>
                 </neighbor>
                 <neighbor>
-                    <ip-address>2.1.0.1</ip-address>
-                    <group>iBGP-Peers</group>
+                    <ip-address>2001:192::2</ip-address>
+                    <group>ibgp</group>
+                    <type>internal</type>
+                    <cluster>
+                    </cluster>
+                    <local-as>
+                    </local-as>
                 </neighbor>
             </bgp>
         </router>
+        <router>
+            <router-name>management</router-name>
+        </router>
+        <router>
+            <router-name>vpls-management</router-name>
+        </router>
+        <service>
+            <vprn>
+                <service-name>peer</service-name>
+                <autonomous-system>65001</autonomous-system>
+                <bgp>
+                    <client-reflect>true</client-reflect>
+                    <cluster>
+                    </cluster>
+                    <local-as>
+                    </local-as>
+                    <import>
+                        <policy>accept_all</policy>
+                    </import>
+                    <export>
+                        <policy>accept_all</policy>
+                    </export>
+                    <multipath>
+                    </multipath>
+                    <group>
+                        <group-name>ebgp-vprn</group-name>
+                        <next-hop-self>false</next-hop-self>
+                        <type>no-type</type>
+                        <peer-as>65000</peer-as>
+                        <cluster>
+                        </cluster>
+                        <local-as>
+                        </local-as>
+                    </group>
+                    <group>
+                        <group-name>ibgp-vprn</group-name>
+                        <next-hop-self>true</next-hop-self>
+                        <type>no-type</type>
+                        <peer-as>65123</peer-as>
+                        <cluster>
+                        </cluster>
+                        <local-as>
+                        </local-as>
+                    </group>
+                    <neighbor>
+                        <ip-address>192.0.0.0</ip-address>
+                        <group>ebgp-vprn</group>
+                        <cluster>
+                        </cluster>
+                        <local-as>
+                        </local-as>
+                    </neighbor>
+                    <neighbor>
+                        <ip-address>2001:192::1</ip-address>
+                        <group>ibgp-vprn</group>
+                        <client-reflect>false</client-reflect>
+                        <cluster>
+                            <cluster-id>1.2.3.4</cluster-id>
+                        </cluster>
+                        <local-as>
+                            <as-number>65123</as-number>
+                        </local-as>
+                    </neighbor>
+                </bgp>
+            </vprn>
+            <vprn>
+                <service-name>peer2</service-name>
+                <autonomous-system>65002</autonomous-system>
+                <bgp>
+                    <client-reflect>true</client-reflect>
+                    <cluster>
+                    </cluster>
+                    <local-as>
+                    </local-as>
+                    <export>
+                        <policy>accept_all</policy>
+                    </export>
+                    <multipath>
+                    </multipath>
+                    <group>
+                        <group-name>ebgp-vprn2</group-name>
+                        <next-hop-self>false</next-hop-self>
+                        <type>no-type</type>
+                        <local-address>192.0.0.3</local-address>
+                        <remove-private>
+                            <limited>true</limited>
+                            <skip-peer-as>false</skip-peer-as>
+                            <replace>false</replace>
+                        </remove-private>
+                        <cluster>
+                        </cluster>
+                        <local-as>
+                            <as-number>65002</as-number>
+                        </local-as>
+                        <export>
+                            <policy>reject_all</policy>
+                        </export>
+                    </group>
+                </bgp>
+            </vprn>
+        </service>
     </configure>
 </data>
diff --git a/test/unit/mocked_data/test_get_environment/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_environment/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_environment/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_environment/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_environment/normal/show_chassis_power-management_utilization_detail.txt b/test/unit/mocked_data/test_get_environment/normal/_show_chassis_power-management_utilization_detail.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_environment/normal/show_chassis_power-management_utilization_detail.txt
rename to test/unit/mocked_data/test_get_environment/normal/_show_chassis_power-management_utilization_detail.txt
diff --git a/test/unit/mocked_data/test_get_facts/normal/expected_result.json b/test/unit/mocked_data/test_get_facts/normal/expected_result.json
index 0b30891..8d74c76 100644
--- a/test/unit/mocked_data/test_get_facts/normal/expected_result.json
+++ b/test/unit/mocked_data/test_get_facts/normal/expected_result.json
@@ -5,7 +5,7 @@
     "os_version": "B-19.10.R4",
     "hostname": "nokia01.sfo07",
     "fqdn": "nokia01.sfo07",
-    "uptime": 3802044,
+    "uptime": 3802044.410,
     "interface_list": [
         "system",
         "to_RTR-01",
diff --git a/test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/show_router_Base_neighbor.txt b/test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/_show_router_Base_neighbor.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/show_router_Base_neighbor.txt
rename to test/unit/mocked_data/test_get_ipv6_neighbors_table/normal/_show_router_Base_neighbor.txt
diff --git a/test/unit/mocked_data/test_get_mac_address_table/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_mac_address_table/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_mac_address_table/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_mac_address_table/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_mac_address_table/normal/show_service_fdb-mac.txt b/test/unit/mocked_data/test_get_mac_address_table/normal/_show_service_fdb-mac.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_mac_address_table/normal/show_service_fdb-mac.txt
rename to test/unit/mocked_data/test_get_mac_address_table/normal/_show_service_fdb-mac.txt
diff --git a/test/unit/mocked_data/test_get_ntp_stats/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_ntp_stats/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_ntp_stats/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_ntp_stats/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_ntp_stats/normal/show_system_ntp_peers.txt b/test/unit/mocked_data/test_get_ntp_stats/normal/_show_system_ntp_peers.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_ntp_stats/normal/show_system_ntp_peers.txt
rename to test/unit/mocked_data/test_get_ntp_stats/normal/_show_system_ntp_peers.txt
diff --git a/test/unit/mocked_data/test_get_ntp_stats/normal/show_system_ntp_servers.txt b/test/unit/mocked_data/test_get_ntp_stats/normal/_show_system_ntp_servers.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_ntp_stats/normal/show_system_ntp_servers.txt
rename to test/unit/mocked_data/test_get_ntp_stats/normal/_show_system_ntp_servers.txt
diff --git a/test/unit/mocked_data/test_get_probes_results/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_probes_results/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_probes_results/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_probes_results/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_probes_results/normal/show_saa_2.txt b/test/unit/mocked_data/test_get_probes_results/normal/_show_saa_2.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_probes_results/normal/show_saa_2.txt
rename to test/unit/mocked_data/test_get_probes_results/normal/_show_saa_2.txt
diff --git a/test/unit/mocked_data/test_get_route_to/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_route_to/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_route_to/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_route_to/normal/show_router_Base_bgp_routes_1_0_4_0_24_detail.txt b/test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to/normal/show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
rename to test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
diff --git a/test/unit/mocked_data/test_get_route_to/normal/show_router_Base_route-table_1_0_4_0_24_.txt b/test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_route-table_1_0_4_0_24_.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to/normal/show_router_Base_route-table_1_0_4_0_24_.txt
rename to test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_route-table_1_0_4_0_24_.txt
diff --git a/test/unit/mocked_data/test_get_route_to/normal/show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt b/test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to/normal/show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
rename to test/unit/mocked_data/test_get_route_to/normal/_show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
diff --git a/test/unit/mocked_data/test_get_route_to_longer/normal/environment_more_false.txt b/test/unit/mocked_data/test_get_route_to_longer/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to_longer/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_get_route_to_longer/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_bgp_routes_1_0_4_0_24_detail.txt b/test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
rename to test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_bgp_routes_1_0_4_0_24_detail.txt
diff --git a/test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_route-table_1_0_4_0_24_longer.txt b/test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_route-table_1_0_4_0_24_longer.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_route-table_1_0_4_0_24_longer.txt
rename to test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_route-table_1_0_4_0_24_longer.txt
diff --git a/test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt b/test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
similarity index 100%
rename from test/unit/mocked_data/test_get_route_to_longer/normal/show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
rename to test/unit/mocked_data/test_get_route_to_longer/normal/_show_router_Base_route-table_1_0_4_0_24_protocol_bgp_extensive_all.txt
diff --git a/test/unit/mocked_data/test_ping/normal/environment_more_false.txt b/test/unit/mocked_data/test_ping/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_ping/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_ping/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_traceroute/normal/environment_more_false.txt b/test/unit/mocked_data/test_traceroute/normal/_environment_more_false.txt
similarity index 100%
rename from test/unit/mocked_data/test_traceroute/normal/environment_more_false.txt
rename to test/unit/mocked_data/test_traceroute/normal/_environment_more_false.txt
diff --git a/test/unit/mocked_data/test_traceroute/normal/environment_progress-indicator_admin-state_disable.txt b/test/unit/mocked_data/test_traceroute/normal/_environment_progress-indicator_admin-state_disable.txt
similarity index 100%
rename from test/unit/mocked_data/test_traceroute/normal/environment_progress-indicator_admin-state_disable.txt
rename to test/unit/mocked_data/test_traceroute/normal/_environment_progress-indicator_admin-state_disable.txt