From d32f75145676bacefde0d08a14680a5984623451 Mon Sep 17 00:00:00 2001
From: Koren Lev <korenlev@gmail.com>
Date: Fri, 29 Sep 2017 01:38:18 +0300
Subject: release 1.0 calipso for opnfv apex

Change-Id: I3e63cd27c5f4d3756e67a07c749863a68e84dde2
Signed-off-by: Koren Lev <korenlev@gmail.com>
---
 .../resource/test_environment_configs.py           | 22 ++++--
 app/test/api/responders_test/test_data/base.py     | 11 ++-
 .../test_data/environment_configs.py               | 16 +++-
 app/test/api/test_base.py                          |  6 +-
 app/test/event_based_scan/test_interface_add.py    |  2 +-
 app/test/event_based_scan/test_interface_delete.py |  3 +-
 app/test/event_based_scan/test_router_add.py       |  2 +-
 app/test/event_based_scan/test_router_update.py    |  2 +-
 .../fetch/api_fetch/test_data/configurations.py    |  3 +-
 app/test/fetch/cli_fetch/test_cli_access.py        | 13 +++-
 app/test/fetch/db_fetch/mock_cursor.py             | 24 +++++-
 .../test_data/db_fetch_host_network_agents.py      |  8 +-
 .../fetch/db_fetch/test_data/db_fetch_oteps.py     |  6 +-
 .../db_fetch/test_data/db_fetch_vedges_ovs.py      | 55 +++++++++++++
 app/test/fetch/db_fetch/test_db_fetch_oteps.py     |  2 +
 .../fetch/db_fetch/test_db_fetch_vedges_ovs.py     | 90 +++++++++++++++++++++-
 app/test/scan/test_data/configurations.py          |  3 +-
 app/test/scan/test_data/scanner.py                 |  6 +-
 app/test/utils/test_cli_dist_translator.py         | 38 +++++++++
 19 files changed, 273 insertions(+), 39 deletions(-)
 create mode 100644 app/test/utils/test_cli_dist_translator.py

(limited to 'app/test')

diff --git a/app/test/api/responders_test/resource/test_environment_configs.py b/app/test/api/responders_test/resource/test_environment_configs.py
index 7002ed7..6356f06 100644
--- a/app/test/api/responders_test/resource/test_environment_configs.py
+++ b/app/test/api/responders_test/resource/test_environment_configs.py
@@ -72,12 +72,21 @@ class TestEnvironmentConfigs(TestBase):
                                   },
                                   expected_code=base.BAD_REQUEST_CODE)
 
+    def test_get_environment_configs_list_with_wrong_distribution_version(self):
+        self.validate_get_request(environment_configs.URL,
+                                  params={
+                                      "distribution_version":
+                                          environment_configs.WRONG_DIST_VER
+                                  },
+                                  expected_code=base.BAD_REQUEST_CODE)
+
     @patch(base.RESPONDER_BASE_READ)
     def test_get_environment_configs_list_with_distribution(self, read):
         self.validate_get_request(environment_configs.URL,
                                   params={
                                       "distribution":
-                                          environment_configs.CORRECT_DISTRIBUTION
+                                          environment_configs.
+                                          CORRECT_DISTRIBUTION
                                   },
                                   mocks={
                                       read: environment_configs.
@@ -377,11 +386,12 @@ class TestEnvironmentConfigs(TestBase):
 
     def mock_validate_env_config_with_supported_envs(self, scanning,
                                                      monitoring, listening):
-        InventoryMgr.is_feature_supported_in_env = lambda self, matches, feature: {
-            EnvironmentFeatures.SCANNING: scanning,
-            EnvironmentFeatures.MONITORING: monitoring,
-            EnvironmentFeatures.LISTENING: listening
-        }[feature]
+        InventoryMgr.is_feature_supported_in_env = \
+            lambda self, matches, feature: {
+                EnvironmentFeatures.SCANNING: scanning,
+                EnvironmentFeatures.MONITORING: monitoring,
+                EnvironmentFeatures.LISTENING: listening
+            }[feature]
 
     @patch(base.RESPONDER_BASE_WRITE)
     def test_post_environment_config(self, write):
diff --git a/app/test/api/responders_test/test_data/base.py b/app/test/api/responders_test/test_data/base.py
index d320340..b99d5bb 100644
--- a/app/test/api/responders_test/test_data/base.py
+++ b/app/test/api/responders_test/test_data/base.py
@@ -53,7 +53,9 @@ WRONG_ENV_TYPE = ""
 CORRECT_ENV_TYPE = "development"
 
 WRONG_DISTRIBUTION = "wrong-environment"
-CORRECT_DISTRIBUTION = "Mirantis-6.0"
+WRONG_DIST_VER = "wrong-environment"
+CORRECT_DISTRIBUTION = "Mirantis"
+CORRECT_DIST_VER = "6.0"
 
 WRONG_OBJECT_ID = "58a2406e6a283a8bee15d43"
 CORRECT_OBJECT_ID = "58a2406e6a283a8bee15d43f"
@@ -150,11 +152,8 @@ CONSTANTS_BY_NAMES = {
         "production"
     ],
     "distributions": [
-        "Mirantis-6.0",
-        "Mirantis-7.0",
-        "Mirantis-8.0",
-        "Mirantis-9.0",
-        "RDO-Juno"
+        "Mirantis",
+        "RDO"
     ],
     "environment_operational_status": [
         "stopped",
diff --git a/app/test/api/responders_test/test_data/environment_configs.py b/app/test/api/responders_test/test_data/environment_configs.py
index a9e8885..4cea105 100644
--- a/app/test/api/responders_test/test_data/environment_configs.py
+++ b/app/test/api/responders_test/test_data/environment_configs.py
@@ -15,7 +15,9 @@ URL = "/environment_configs"
 NAME = "Mirantis-Liberty-API"
 UNKNOWN_NAME = "UNKNOWN NAME"
 WRONG_DISTRIBUTION = base.WRONG_DISTRIBUTION
+WRONG_DIST_VER = base.WRONG_DIST_VER
 CORRECT_DISTRIBUTION = base.CORRECT_DISTRIBUTION
+CORRECT_DIST_VER = base.CORRECT_DIST_VER
 WRONG_MECHANISM_DRIVER = base.WRONG_MECHANISM_DRIVER
 CORRECT_MECHANISM_DRIVER = base.CORRECT_MECHANISM_DRIVER
 WRONG_TYPE_DRIVER = base.WRONG_TYPE_DRIVER
@@ -29,11 +31,13 @@ BOOL_LISTEN = BOOL_SCANNED = \
 
 ENV_CONFIGS = [
     {
-        "distribution": "Mirantis-8.0",
+        "distribution": "Mirantis",
+        "distribution_version": "8.0",
         "name": "Mirantis-Liberty-API"
     },
     {
-        "distribution": "Mirantis-9.0",
+        "distribution": "Mirantis",
+        "distribution_version": "9.0",
         "name": "Mirantis-Liberty"
     }
 ]
@@ -44,7 +48,8 @@ ENV_CONFIGS_RESPONSE = {
 
 ENV_CONFIGS_WITH_SPECIFIC_NAME = [
     {
-        "distribution": "Mirantis-8.0",
+        "distribution": "Mirantis",
+        "distribution_version": "8.0",
         "name": NAME
     }
 ]
@@ -52,10 +57,12 @@ ENV_CONFIGS_WITH_SPECIFIC_NAME = [
 ENV_CONFIGS_WITH_SPECIFIC_DISTRIBUTION = [
     {
         "distribution": CORRECT_DISTRIBUTION,
+        "distribution_version": CORRECT_DIST_VER,
         "name": "Mirantis-Liberty-API",
     },
     {
         "distribution": CORRECT_DISTRIBUTION,
+        "distribution_version": CORRECT_DIST_VER,
         "name": "Mirantis-Liberty"
     }
 ]
@@ -206,7 +213,8 @@ ENV_CONFIG = {
             "type": "Sensu"
         }
     ],
-    "distribution": "Mirantis-8.0",
+    "distribution": "Mirantis",
+    "distribution_version": "8.0",
     "last_scanned": "2017-03-16T11:14:54Z",
     "listen": True,
     "mechanism_drivers": [
diff --git a/app/test/api/test_base.py b/app/test/api/test_base.py
index c126b2b..33185ec 100644
--- a/app/test/api/test_base.py
+++ b/app/test/api/test_base.py
@@ -84,8 +84,12 @@ class TestBase(TestCase):
                               expected_code,
                               expected_response)
 
-    def get_updated_data(self, original_data, deleted_keys=[], updates={}):
+    def get_updated_data(self, original_data, deleted_keys=None, updates=None):
         copy_data = copy.deepcopy(original_data)
+        if deleted_keys is None:
+            deleted_keys = []
+        if updates is None:
+            updates = {}
 
         for key in deleted_keys:
             del copy_data[key]
diff --git a/app/test/event_based_scan/test_interface_add.py b/app/test/event_based_scan/test_interface_add.py
index 04a1982..542f84e 100644
--- a/app/test/event_based_scan/test_interface_add.py
+++ b/app/test/event_based_scan/test_interface_add.py
@@ -25,7 +25,7 @@ class TestInterfaceAdd(TestEvent):
     def get_by_id(self, env, object_id):
         interface = self.values["payload"]["router_interface"]
         host_id = self.values["publisher_id"].replace("network.", "", 1)
-        router_id = encode_router_id(host_id, interface['id'])
+        router_id = encode_router_id(interface['id'])
 
         if object_id == host_id:
             return HOST
diff --git a/app/test/event_based_scan/test_interface_delete.py b/app/test/event_based_scan/test_interface_delete.py
index e416be4..7c3684a 100644
--- a/app/test/event_based_scan/test_interface_delete.py
+++ b/app/test/event_based_scan/test_interface_delete.py
@@ -34,8 +34,7 @@ class TestInterfaceDelete(TestEvent):
         self.payload = self.values['payload']
         self.interface = self.payload['router_interface']
         self.port_id = self.interface['port_id']
-        self.host_id = self.values["publisher_id"].replace("network.", "", 1)
-        self.router_id = encode_router_id(self.host_id, self.interface['id'])
+        self.router_id = encode_router_id(self.interface['id'])
 
         port_delete_mock = port_delete_class_mock.return_value
         port_delete_mock.delete_port.return_value = EventResult(result=True)
diff --git a/app/test/event_based_scan/test_router_add.py b/app/test/event_based_scan/test_router_add.py
index 03be8df..b450cf5 100644
--- a/app/test/event_based_scan/test_router_add.py
+++ b/app/test/event_based_scan/test_router_add.py
@@ -45,7 +45,7 @@ class TestRouterAdd(TestEvent):
         self.router = self.payload['router']
         self.network_id = self.router['external_gateway_info']['network_id']
         self.host_id = self.values["publisher_id"].replace("network.", "", 1)
-        self.router_id = encode_router_id(self.host_id, self.router['id'])
+        self.router_id = encode_router_id(self.router['id'])
 
         self.inv.get_by_id.side_effect = self.get_by_id
 
diff --git a/app/test/event_based_scan/test_router_update.py b/app/test/event_based_scan/test_router_update.py
index 390bd6e..93f44a3 100644
--- a/app/test/event_based_scan/test_router_update.py
+++ b/app/test/event_based_scan/test_router_update.py
@@ -36,7 +36,7 @@ class TestRouterUpdate(TestEvent):
         self.payload = self.values['payload']
         self.router = self.payload['router']
         self.host_id = self.values['publisher_id'].replace("network.", "", 1)
-        self.router_id = encode_router_id(self.host_id, self.router['id'])
+        self.router_id = encode_router_id(self.router['id'])
         self.gw_port_id = ROUTER_DOCUMENT['gw_port_id']
 
         scanner_mock = scanner_class_mock.return_value
diff --git a/app/test/fetch/api_fetch/test_data/configurations.py b/app/test/fetch/api_fetch/test_data/configurations.py
index cca43be..8e7eb5d 100644
--- a/app/test/fetch/api_fetch/test_data/configurations.py
+++ b/app/test/fetch/api_fetch/test_data/configurations.py
@@ -41,7 +41,8 @@ CONFIGURATIONS = {
             "pwd": "NF2nSv3SisooxPkCTr8fbfOa"
         }
     ],
-    "distribution": "Mirantis-8.0",
+    "distribution": "Mirantis",
+    "distribution_version": "8.0",
     "last_scanned:": "5/8/16",
     "name": "Mirantis-Liberty-Xiaocong",
     "network_plugins": [
diff --git a/app/test/fetch/cli_fetch/test_cli_access.py b/app/test/fetch/cli_fetch/test_cli_access.py
index d32e1ed..1d14450 100644
--- a/app/test/fetch/cli_fetch/test_cli_access.py
+++ b/app/test/fetch/cli_fetch/test_cli_access.py
@@ -10,10 +10,11 @@
 import time
 
 from discover.fetchers.cli.cli_access import CliAccess
+from discover.configuration import Configuration
+from test.fetch.api_fetch.test_data.configurations import CONFIGURATIONS
 from test.fetch.cli_fetch.test_data.cli_access import *
 from test.fetch.test_fetch import TestFetch
-from unittest.mock import MagicMock, patch
-from utils.ssh_conn import SshConn
+from unittest.mock import MagicMock
 
 
 class TestCliAccess(TestFetch):
@@ -22,6 +23,11 @@ class TestCliAccess(TestFetch):
         super().setUp()
         self.configure_environment()
         self.cli_access = CliAccess()
+        self.conf = Configuration()
+        self.cli_access.configuration = self.conf
+        self.conf.use_env = MagicMock()
+        self.conf.environment = CONFIGURATIONS
+        self.conf.configuration = CONFIGURATIONS["configuration"]
 
     def check_run_result(self, is_gateway_host,
                          enable_cache,
@@ -40,7 +46,8 @@ class TestCliAccess(TestFetch):
         self.ssh_conn.exec.return_value = exec_result
         self.ssh_conn.is_gateway_host.return_value = is_gateway_host
         result = self.cli_access.run(COMMAND, COMPUTE_HOST_ID,
-                                     on_gateway=False, enable_cache=enable_cache)
+                                     on_gateway=False,
+                                     enable_cache=enable_cache)
         self.assertEqual(result, expected_result, err_msg)
 
         # reset the cached commands after testing
diff --git a/app/test/fetch/db_fetch/mock_cursor.py b/app/test/fetch/db_fetch/mock_cursor.py
index 71efd3b..10c67e1 100644
--- a/app/test/fetch/db_fetch/mock_cursor.py
+++ b/app/test/fetch/db_fetch/mock_cursor.py
@@ -7,19 +7,39 @@
 # which accompanies this distribution, and is available at                    #
 # http://www.apache.org/licenses/LICENSE-2.0                                  #
 ###############################################################################
+
+
+def require_open(method):
+    def wrapped(self, *args, **kwargs):
+        if self.closed:
+            raise ValueError("Cursor is closed")
+        return method(self, *args, **kwargs)
+    return wrapped
+
+
 class MockCursor:
 
     def __init__(self, result):
         self.result = result
         self.current = 0
+        self.closed = False
 
+    @require_open
     def __next__(self):
         if self.current < len(self.result):
-            next = self.result[self.current]
+            nxt = self.result[self.current]
             self.current += 1
-            return next
+            return nxt
         else:
             raise StopIteration
 
+    @require_open
     def __iter__(self):
         return self
+
+    @require_open
+    def fetchall(self):
+        return self.result
+
+    def close(self):
+        self.closed = True
diff --git a/app/test/fetch/db_fetch/test_data/db_fetch_host_network_agents.py b/app/test/fetch/db_fetch/test_data/db_fetch_host_network_agents.py
index 6188ddf..b6d344c 100644
--- a/app/test/fetch/db_fetch/test_data/db_fetch_host_network_agents.py
+++ b/app/test/fetch/db_fetch/test_data/db_fetch_host_network_agents.py
@@ -37,13 +37,13 @@ NETWORK_AGENT = [
 NETWORK_AGENT_WITH_MECHANISM_DRIVERS_IN_CONFIG_RESULTS = [
     {
         'configurations': {},
-        'id': 'OVS-1764430c-c09e-4717-86fa-c04350b1fcbb',
+        'id': 'neutron-openvswitch-agent-1764430c-c09e-4717-86fa-c04350b1fcbb',
         'binary': 'neutron-openvswitch-agent',
         'name': 'neutron-openvswitch-agent'
     },
     {
         'configurations': {},
-        'id': 'OVS-2c2ddfee-91f9-47da-bd65-aceecd998b7c',
+        'id': 'neutron-dhcp-agent-2c2ddfee-91f9-47da-bd65-aceecd998b7c',
         'binary': 'neutron-dhcp-agent',
         'name': 'neutron-dhcp-agent'
     }
@@ -52,13 +52,13 @@ NETWORK_AGENT_WITH_MECHANISM_DRIVERS_IN_CONFIG_RESULTS = [
 NETWORK_AGENT_WITHOUT_MECHANISM_DRIVERS_IN_CONFIG_RESULTS = [
     {
         'configurations': {},
-        'id': 'network_agent-1764430c-c09e-4717-86fa-c04350b1fcbb',
+        'id': 'neutron-openvswitch-agent-1764430c-c09e-4717-86fa-c04350b1fcbb',
         'binary': 'neutron-openvswitch-agent',
         'name': 'neutron-openvswitch-agent'
     },
     {
         'configurations': {},
-        'id': 'network_agent-2c2ddfee-91f9-47da-bd65-aceecd998b7c',
+        'id': 'neutron-dhcp-agent-2c2ddfee-91f9-47da-bd65-aceecd998b7c',
         'binary': 'neutron-dhcp-agent',
         'name': 'neutron-dhcp-agent'
     }
diff --git a/app/test/fetch/db_fetch/test_data/db_fetch_oteps.py b/app/test/fetch/db_fetch/test_data/db_fetch_oteps.py
index a5bc63d..2bd1784 100644
--- a/app/test/fetch/db_fetch/test_data/db_fetch_oteps.py
+++ b/app/test/fetch/db_fetch/test_data/db_fetch_oteps.py
@@ -34,10 +34,12 @@ VEDGE_WITHOUT_TUNNEL_TYPES = {
     }
 }
 NON_ICEHOUSE_CONFIGS = {
-    "distribution": "Mirantis-8.0"
+    "distribution": "Mirantis",
+    "distribution_version": "8.0"
 }
 ICEHOUSE_CONFIGS = {
-    "distribution": "Canonical-icehouse"
+    "distribution": "Canonical",
+    "distribution_version": "icehouse"
 }
 HOST = {
     "host": "node-5.cisco.com",
diff --git a/app/test/fetch/db_fetch/test_data/db_fetch_vedges_ovs.py b/app/test/fetch/db_fetch/test_data/db_fetch_vedges_ovs.py
index 818704c..c1f9d4f 100644
--- a/app/test/fetch/db_fetch/test_data/db_fetch_vedges_ovs.py
+++ b/app/test/fetch/db_fetch/test_data/db_fetch_vedges_ovs.py
@@ -166,3 +166,58 @@ DOC_TO_GET_OVERLAY = {
     "agent_type": "Open vSwitch agent",
     "configurations": {"tunneling_ip": "192.168.2.3"},
 }
+
+LIST_IFACES_LINES = [
+    "eth0",
+    "p",
+    "t"
+]
+LIST_IFACES_NAMES = LIST_IFACES_LINES
+LIST_IFACES_LINES_MIRANTIS = {
+    "eth0--br-eth0",
+    "phy-eth0"
+}
+LIST_IFACES_NAMES_MIRANTIS = ["eth0"]
+
+VEDGE_CONFIGURATIONS_MIRANTIS = {
+    "bridge_mappings": {
+        "br-prv": "eth0"
+    }
+}
+VEDGE_CONFIGURATIONS = {
+    "bridge_mappings": {
+        "physnet1": "eth0",
+        "physnet2": "p",
+        "physnet3": "t",
+        "physnet4": "p",
+        "physnet5": "p"
+    }
+}
+
+VEDGE_MIRANTIS = {
+    'host': HOST['host'],
+    'ports': {
+        "eth0": {"name": "eth0", "id": "eth0-port_id"}
+    },
+    'configurations': VEDGE_CONFIGURATIONS_MIRANTIS
+}
+VEDGE = {
+    'host': HOST['host'],
+    'ports': {
+        "eth0": {"name": "eth0", "id": "eth0-port_id"},
+        "p": {"name": "p", "id": "p-port_id"},
+        "t": {"name": "t", "id": "t-port_id"}
+    },
+    'configurations': VEDGE_CONFIGURATIONS
+}
+
+ANOTHER_DIST = "another distribution"
+
+PNICS_MIRANTS = {
+    "eth0": {"name": "eth0", "mac_address": "eth0 mac_address"}
+}
+PNICS = {
+    "eth0": {"name": "eth0", "mac_address": "eth0 mac_address"},
+    "p": {"name": "p", "mac_address": "p mac_address"},
+    "t": {"name": "t", "mac_address": "t mac_address"}
+}
diff --git a/app/test/fetch/db_fetch/test_db_fetch_oteps.py b/app/test/fetch/db_fetch/test_db_fetch_oteps.py
index 7d29622..a161e03 100644
--- a/app/test/fetch/db_fetch/test_db_fetch_oteps.py
+++ b/app/test/fetch/db_fetch/test_db_fetch_oteps.py
@@ -32,11 +32,13 @@ class TestDbFetchOteps(TestFetch):
         original_get_vconnector = self.fetcher.get_vconnector
         self.fetcher.get_vconnector = MagicMock()
         self.fetcher.inv.get_by_id = MagicMock(side_effect=[vedge, host])
+        original_get_env_config = self.fetcher.config.get_env_config
         self.fetcher.config.get_env_config = MagicMock(return_value=config)
         self.fetcher.get_objects_list_for_id = MagicMock(return_value=oteps_from_db)
         results = self.fetcher.get(VEDGE_ID)
         self.assertEqual(results, expected_results, err_msg)
         self.fetcher.get_vconnector = original_get_vconnector
+        self.fetcher.config.get_env_config = original_get_env_config
 
     def test_get(self):
         test_cases = [
diff --git a/app/test/fetch/db_fetch/test_db_fetch_vedges_ovs.py b/app/test/fetch/db_fetch/test_db_fetch_vedges_ovs.py
index 0cfb500..9916e5d 100644
--- a/app/test/fetch/db_fetch/test_db_fetch_vedges_ovs.py
+++ b/app/test/fetch/db_fetch/test_db_fetch_vedges_ovs.py
@@ -7,6 +7,8 @@
 # which accompanies this distribution, and is available at                    #
 # http://www.apache.org/licenses/LICENSE-2.0                                  #
 ###############################################################################
+import copy
+
 from discover.fetchers.db.db_fetch_vedges_ovs import DbFetchVedgesOvs
 from test.fetch.test_fetch import TestFetch
 from test.fetch.db_fetch.test_data.db_fetch_vedges_ovs import *
@@ -20,6 +22,12 @@ class TestDbFetchVedgesOvs(TestFetch):
         self.configure_environment()
         self.fetcher = DbFetchVedgesOvs()
         self.fetcher.set_env(self.env)
+        self.original_inv_set = self.fetcher.inv.set
+        self.fetcher.inv.set = MagicMock()
+
+    def tearDown(self):
+        super().tearDown()
+        self.fetcher.inv.set = self.original_inv_set
 
     def check_get_result(self,
                          objects_from_db, host,
@@ -32,7 +40,8 @@ class TestDbFetchVedgesOvs(TestFetch):
         original_fetch_ports = self.fetcher.fetch_ports
         original_get_overlay_tunnels = self.fetcher.get_overlay_tunnels
 
-        self.fetcher.get_objects_list_for_id = MagicMock(return_value=objects_from_db)
+        self.fetcher.get_objects_list_for_id = \
+            MagicMock(return_value=objects_from_db)
         self.fetcher.inv.get_by_id = MagicMock(return_value=host)
         self.fetcher.run_fetch_lines = MagicMock(return_value=vsctl_lines)
         self.fetcher.fetch_ports = MagicMock(return_value=ports)
@@ -96,7 +105,7 @@ class TestDbFetchVedgesOvs(TestFetch):
         results = self.fetcher.fetch_ports_from_dpctl(HOST['id'])
         self.fetcher.run_fetch_lines = original_run_fetch_lines
         self.assertEqual(results, DPCTL_RESULTS,
-                         "Can' t get correct ports info from dpctl lines")
+                         "Can't get correct ports info from dpctl lines")
 
     def test_fetch_port_tags_from_vsctl(self):
         ports = self.fetcher.fetch_port_tags_from_vsctl(VSCTL_LINES,
@@ -108,3 +117,80 @@ class TestDbFetchVedgesOvs(TestFetch):
         results = self.fetcher.get_overlay_tunnels(DOC_TO_GET_OVERLAY,
                                                    VSCTL_LINES)
         self.assertEqual(results, TUNNEL_PORTS)
+
+    @staticmethod
+    def get_test_pnic_for_interface_mirantis(search: dict,
+                                             get_single: bool=True):
+        if not get_single:
+            # we're only supposed to get calls with get_single == True
+            return []
+        return PNICS_MIRANTS.get(search.get('name'), {})
+
+    @staticmethod
+    def get_test_pnic_for_interface(search: dict,
+                                    get_single: bool=True):
+        if not get_single:
+            # we're only supposed to get calls with get_single == True
+            return []
+        return PNICS.get(search.get('name'), {})
+
+    @staticmethod
+    def get_expected_results_for_get_pnics(test_pnics: dict, ports: dict,
+                                           ifaces_names: list) -> dict:
+        expected_results = {}
+        for p in test_pnics.values():
+            if p.get("name") not in ifaces_names:
+                continue
+            p1 = copy.deepcopy(p)
+            name = p1["name"]
+            port = ports[name]
+            p1["port_id"] = port["id"]
+            expected_results[name] = p1
+        return expected_results
+
+    def test_get_pnics(self):
+        expected_results = \
+            self.get_expected_results_for_get_pnics(PNICS_MIRANTS,
+                                                    VEDGE_MIRANTIS["ports"],
+                                                    LIST_IFACES_NAMES_MIRANTIS)
+        self.check_get_pnics_for_dist(VEDGE_MIRANTIS,
+                                      LIST_IFACES_LINES_MIRANTIS,
+                                      LIST_IFACES_NAMES_MIRANTIS,
+                                      expected_results,
+                                      self.get_test_pnic_for_interface_mirantis,
+                                      self.fetcher.MIRANTIS_DIST,
+                                      ver="6.0",
+                                      msg="Incorrect get_pnics result "
+                                          "(Mirantis)")
+        expected_results = \
+            self.get_expected_results_for_get_pnics(PNICS,
+                                                    VEDGE["ports"],
+                                                    LIST_IFACES_NAMES)
+        self.check_get_pnics_for_dist(VEDGE,
+                                      LIST_IFACES_LINES,
+                                      LIST_IFACES_NAMES,
+                                      expected_results,
+                                      self.get_test_pnic_for_interface,
+                                      ANOTHER_DIST,
+                                      msg="Incorrect get_pnics result")
+
+    def check_get_pnics_for_dist(self, test_vedge,
+                                 ifaces_list_output, ifaces_list_clear,
+                                 expected_results,
+                                 pnic_find_func,
+                                 dist, ver=None, msg=None):
+        self.fetcher.configuration.environment = {
+            "distribution": dist,
+            "distribution_version": ver
+        }
+        original_run_fetch_lines = self.fetcher.run_fetch_lines
+        self.fetcher.run_fetch_lines = \
+            MagicMock(return_value=ifaces_list_output)
+        original_find_items = self.fetcher.inv.find_items
+        self.fetcher.inv.find_items = pnic_find_func
+        vedge = copy.deepcopy(test_vedge)
+        results = self.fetcher.get_pnics(vedge)
+        self.fetcher.run_fetch_lines = original_run_fetch_lines
+        self.fetcher.inv.find_items = original_find_items
+        self.assertTrue(vedge.get("pnic") in ifaces_list_clear)
+        self.assertEqual(results, expected_results, msg)
diff --git a/app/test/scan/test_data/configurations.py b/app/test/scan/test_data/configurations.py
index 59ad649..96dbc23 100644
--- a/app/test/scan/test_data/configurations.py
+++ b/app/test/scan/test_data/configurations.py
@@ -58,7 +58,8 @@ CONFIGURATIONS = {
             "type": "Sensu"
         }
     ],
-    "distribution": "Mirantis-8.0",
+    "distribution": "Mirantis",
+    "distribution_version": "8.0",
     "last_scanned:": "5/8/16",
     "name": "Mirantis-Liberty-Nvn",
     "mechanism_drivers": [
diff --git a/app/test/scan/test_data/scanner.py b/app/test/scan/test_data/scanner.py
index 36c2033..23838aa 100644
--- a/app/test/scan/test_data/scanner.py
+++ b/app/test/scan/test_data/scanner.py
@@ -120,7 +120,8 @@ CONFIGURATIONS = {
             "pwd": "NF2nSv3SisooxPkCTr8fbfOa"
         }
     ],
-    "distribution": "Mirantis-8.0",
+    "distribution": "Mirantis",
+    "distribution_version": "8.0",
     "last_scanned:": "5/8/16",
     "name": "Mirantis-Liberty-Nvn",
     "mechanism_drivers": [
@@ -330,7 +331,8 @@ CONFIGURATIONS_WITHOUT_MECHANISM_DRIVERS = {
             "pwd": "NF2nSv3SisooxPkCTr8fbfOa"
         }
     ],
-    "distribution": "Mirantis-8.0",
+    "distribution": "Mirantis",
+    "distribution_version": "8.0",
     "last_scanned:": "5/8/16",
     "name": "Mirantis-Liberty-Nvn",
     "operational": "yes",
diff --git a/app/test/utils/test_cli_dist_translator.py b/app/test/utils/test_cli_dist_translator.py
new file mode 100644
index 0000000..e6a8080
--- /dev/null
+++ b/app/test/utils/test_cli_dist_translator.py
@@ -0,0 +1,38 @@
+###############################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems)   #
+# and others                                                                  #
+#                                                                             #
+# All rights reserved. This program and the accompanying materials            #
+# are made available under the terms of the Apache License, Version 2.0       #
+# which accompanies this distribution, and is available at                    #
+# http://www.apache.org/licenses/LICENSE-2.0                                  #
+###############################################################################
+import unittest
+
+from utils.cli_dist_translator import CliDistTranslator
+
+
+class TestCliDistTranslator(unittest.TestCase):
+
+    MERCURY_DIST = 'Mercury'
+    MERCURY_VER = '10239'
+
+    SOURCE_TEXT = 'some text'
+    IP_LINK_TEXT = 'ip link show'
+    IP_LINK_TRANSLATED_MERCURY = \
+        'docker exec --user root ovs_vswitch_10239 ip link show'
+
+    def test_unknown_dist(self):
+        translator = CliDistTranslator('UNKNOWN')
+        result = translator.translate(self.SOURCE_TEXT)
+        self.assertEqual(result, self.SOURCE_TEXT,
+                         'unknown dist should not cause translation')
+
+    def test_mercury_dist(self):
+        translator = CliDistTranslator(self.MERCURY_DIST, self.MERCURY_VER)
+        result = translator.translate(self.SOURCE_TEXT)
+        self.assertEqual(result, self.SOURCE_TEXT,
+                         'known dist should not translate unrelated texts')
+        result = translator.translate(self.IP_LINK_TEXT)
+        self.assertEqual(result, self.IP_LINK_TRANSLATED_MERCURY,
+                         'incorrect translation of command for mercury dist')
-- 
cgit 1.2.3-korg