Model candidates to be object oriented 59/110859/3
authordhebeha <dhebeha.mj71@wipro.com>
Mon, 3 Aug 2020 17:24:34 +0000 (22:54 +0530)
committerdhebeha <dhebeha.mj71@wipro.com>
Wed, 12 Aug 2020 13:31:06 +0000 (19:01 +0530)
Issue-ID: OPTFRA-822
Signed-off-by: dhebeha <dhebeha.mj71@wipro.com>
Change-Id: Ia04b4c5f4594dd40843e9d32f6fbdcd2f93cef06

conductor/conductor/data/plugins/inventory_provider/aai.py
conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py [new file with mode: 0644]
conductor/conductor/data/plugins/inventory_provider/candidates/cloud_candidate.py [new file with mode: 0644]
conductor/conductor/data/plugins/inventory_provider/candidates/service_candidate.py [new file with mode: 0644]
conductor/conductor/data/plugins/inventory_provider/candidates/transport_candidate.py [new file with mode: 0644]
conductor/conductor/data/plugins/inventory_provider/candidates/vfmodule_candidate.py [new file with mode: 0644]
conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py

index 302bb89..821219c 100644 (file)
 # -------------------------------------------------------------------------
 #
 
+import copy
+import json
 import re
 import time
 import uuid
-import copy
 
-import json
+from oslo_config import cfg
+from oslo_log import log
 
 from conductor.common import rest
 from conductor.data.plugins import constants
-from conductor.common.utils import cipherUtils
 from conductor.data.plugins.inventory_provider import base
 from conductor.data.plugins.inventory_provider import hpa_utils
+from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+from conductor.data.plugins.inventory_provider.candidates.cloud_candidate import Cloud
+from conductor.data.plugins.inventory_provider.candidates.service_candidate import Service
+from conductor.data.plugins.inventory_provider.candidates.transport_candidate import Transport
+from conductor.data.plugins.inventory_provider.candidates.vfmodule_candidate import VfModule
 from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
 from conductor.i18n import _LE, _LI
-from oslo_config import cfg
-from oslo_log import log
 
 LOG = log.getLogger(__name__)
 
@@ -112,7 +116,7 @@ class AAI(base.InventoryProviderBase):
         self.retries = self.conf.aai.aai_retries
         self.username = self.conf.aai.username
         self.password = self.conf.aai.password
-        self.triage_translator=TraigeTranslator()
+        self.triage_translator = TraigeTranslator()
 
         # Cache is initially empty
         self._aai_cache = {}
@@ -143,7 +147,8 @@ class AAI(base.InventoryProviderBase):
         """Return human-readable name."""
         return "A&AI"
 
-    def _get_version_from_string(self, string):
+    @staticmethod
+    def _get_version_from_string(string):
         """Extract version number from string"""
         return re.sub("[^0-9.]", "", string)
 
@@ -198,11 +203,10 @@ class AAI(base.InventoryProviderBase):
         }
         self.rest = rest.REST(**kwargs)
 
-
     def _refresh_cache(self):
         """Refresh the A&AI cache."""
         if not self.last_refresh_time or \
-            (time.time() - self.last_refresh_time) > \
+                (time.time() - self.last_refresh_time) > \
                 self.cache_refresh_interval * 60:
             # TODO(jdandrea): This is presently brute force.
             # It does not persist to Music. A general purpose ORM caching
@@ -243,7 +247,8 @@ class AAI(base.InventoryProviderBase):
                 cloud_type = region.get('cloud-type')
                 cloud_zone = region.get('cloud-zone')
 
-                physical_location_list = self._get_aai_rel_link_data(data = region, related_to = 'complex', search_key = 'complex.physical-location-id')
+                physical_location_list = self._get_aai_rel_link_data(data=region, related_to='complex',
+                                                                     search_key='complex.physical-location-id')
                 if len(physical_location_list) > 0:
                     physical_location_id = physical_location_list[0].get('d_value')
 
@@ -291,7 +296,8 @@ class AAI(base.InventoryProviderBase):
                     keys = ('latitude', 'longitude', 'city', 'country',
                             'complex_name')
                     missing_keys = \
-                        list(set(keys).difference(list(complex_info.keys())))   # Python 3 Conversion -- dict object to list object
+                        list(set(keys).difference(
+                            list(complex_info.keys())))  # Python 3 Conversion -- dict object to list object
                     LOG.error(_LE("Complex {} is missing {}, link: {}").
                               format(complex_id, missing_keys, complex_link))
                     LOG.debug("Complex {}: {}".
@@ -391,39 +397,46 @@ class AAI(base.InventoryProviderBase):
             )
         return response
 
+    @staticmethod
+    def check_sriov_automation(aic_version, demand_name, candidate_name):
+        """Check if specific candidate has SRIOV automation available or no"""
+        if aic_version:
+            LOG.debug(_LI("Demand {}, candidate {} has an AIC version number {}").format(demand_name, candidate_name,
+                                                                                         aic_version))
+            if aic_version == "X.Y":
+                return True
+        return False
+
     def _get_complex(self, complex_link, complex_id=None):
+
         if not self.complex_last_refresh_time or \
-           (time.time() - self.complex_last_refresh_time) > \
-           self.complex_cache_refresh_interval * 60:
+                (time.time() - self.complex_last_refresh_time) > \
+                self.complex_cache_refresh_interval * 60:
             self._aai_complex_cache.clear()
         if complex_id and complex_id in self._aai_complex_cache:
             return self._aai_complex_cache[complex_id]
         else:
-            path = self._aai_versioned_path(
-                self._get_aai_path_from_link(complex_link))
-            response = self._request(
-                path=path, context="complex", value=complex_id)
+            path = self._aai_versioned_path(self._get_aai_path_from_link(complex_link))
+            response = self._request(path=path, context="complex", value=complex_id)
             if response is None:
                 return
             if response.status_code == 200:
                 complex_info = response.json()
                 if 'complex' in complex_info:
                     complex_info = complex_info.get('complex')
+
                 latitude = complex_info.get('latitude')
                 longitude = complex_info.get('longitude')
                 city = complex_info.get('city')
                 country = complex_info.get('country')
-
-                # removed the state check for countries in Europe
-                # that do not always enter states
+                # removed the state check for countries in Europe that do not always enter states
                 if not (latitude and longitude and city and country):
                     keys = ('latitude', 'longitude', 'city', 'country')
                     missing_keys = \
                         list(set(keys).difference(set(complex_info.keys())))
                     LOG.error(_LE("Complex {} is missing {}, link: {}").
                               format(complex_id, missing_keys, complex_link))
-                    LOG.debug("Complex {}: {}".
-                              format(complex_id, complex_info))
+                    LOG.debug("Complex {}: {}".format(complex_id, complex_info))
                     return
 
                 if complex_id:  # cache only if complex_id is given
@@ -580,7 +593,7 @@ class AAI(base.InventoryProviderBase):
     def resolve_host_location(self, host_name):
         path = self._aai_versioned_path('/query?format=id')
         data = {"start": ["network/pnfs/pnf/" + host_name,
-                "cloud-infrastructure/pservers/pserver/" + host_name],
+                          "cloud-infrastructure/pservers/pserver/" + host_name],
                 "query": "query/ucpe-instance"
                 }
         response = self._request('put', path=path, data=data,
@@ -694,22 +707,6 @@ class AAI(base.InventoryProviderBase):
         if context and value:
             LOG.debug("{} details: {}".format(context, value))
 
-    def check_sriov_automation(self, aic_version, demand_name, candidate_name):
-
-        """Check if specific candidate has SRIOV automation available or not
-
-        Used by resolve_demands
-        """
-
-        if aic_version:
-            LOG.debug(_LI("Demand {}, candidate {} has an AIC version "
-                          "number {}").format(demand_name, candidate_name,
-                                              aic_version)
-                      )
-            if aic_version == "X.Y":
-                return True
-        return False
-
     def match_candidate_attribute(self, candidate, attribute_name,
                                   restricted_value, demand_name,
                                   inventory_type):
@@ -718,8 +715,8 @@ class AAI(base.InventoryProviderBase):
         Used by resolve_demands
         """
         if restricted_value and \
-           restricted_value is not '' and \
-           candidate[attribute_name] != restricted_value:
+                restricted_value is not '' and \
+                candidate[attribute_name] != restricted_value:
             LOG.info(_LI("Demand: {} "
                          "Discarded {} candidate as "
                          "it doesn't match the "
@@ -738,13 +735,13 @@ class AAI(base.InventoryProviderBase):
         value = None
         for i in range(0, len(vserver_list)):
             if value and \
-               value != vserver_list[i].get('d_value'):
+                    value != vserver_list[i].get('d_value'):
                 return False
             value = vserver_list[i].get('d_value')
         return True
 
     def match_inventory_attributes(self, template_attributes,
-                                    inventory_attributes, candidate_id):
+                                   inventory_attributes, candidate_id):
 
         for attribute_key, attribute_values in template_attributes.items():
 
@@ -764,17 +761,17 @@ class AAI(base.InventoryProviderBase):
 
             if match_type == 'any':
                 if attribute_key not in inventory_attributes or \
-                   (len(attribute_values) > 0 and
-                   inventory_attributes[attribute_key] not in attribute_values):
+                        (len(attribute_values) > 0 and
+                         inventory_attributes[attribute_key] not in attribute_values):
                     return False
             elif match_type == 'not':
                 # drop the candidate when
                 # 1). field exists in AAI and 2). value is not null or empty 3). value is one of those in the 'not' list
                 # Remember, this means if the property is not returned at all from AAI, that still can be a candidate.
                 if attribute_key in inventory_attributes and \
-                    inventory_attributes[attribute_key] and \
-                    inventory_attributes[attribute_key] in attribute_values:
-                     return False
+                        inventory_attributes[attribute_key] and \
+                        inventory_attributes[attribute_key] in attribute_values:
+                    return False
 
         return True
 
@@ -788,12 +785,12 @@ class AAI(base.InventoryProviderBase):
         body = response.json()
         return body.get("generic-vnf", [])
 
-    def resolove_v_server_for_candidate(self, candidate, vs_link, add_interfaces, demand_name, triage_translator_data):
+    def resolve_v_server_for_candidate(self, candidate_id, location_id, vs_link, add_interfaces, demand_name, triage_translator_data):
         if not vs_link:
             LOG.error(_LE("{} VSERVER link information not "
                           "available from A&AI").format(demand_name))
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id,
+                                                          location_id, demand_name,
                                                           triage_translator_data,
                                                           reason="VSERVER link information not")
             return None  # move ahead with the next vnf
@@ -805,8 +802,8 @@ class AAI(base.InventoryProviderBase):
             LOG.error(_LE("{} VSERVER path information not "
                           "available from A&AI - {}").
                       format(demand_name, vs_path))
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id,
+                                                          location_id, demand_name,
                                                           triage_translator_data,
                                                           reason="VSERVER path information not available from A&AI")
             return None  # move ahead with the next vnf
@@ -815,30 +812,27 @@ class AAI(base.InventoryProviderBase):
             path=path, context="demand, VSERVER",
             value="{}, {}".format(demand_name, vs_path))
         if response is None or response.status_code != 200:
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id,
+                                                          location_id, demand_name,
                                                           triage_translator_data,
                                                           reason=response.status_code)
             return None
         return response.json()
 
-    def resolve_vf_modules_for_generic_vnf(self, candidate, vnf, demand_name, triage_translator_data):
+    def resolve_vf_modules_for_generic_vnf(self, candidate_id, location_id, vnf, demand_name, triage_translator_data):
         raw_path = '/network/generic-vnfs/generic-vnf/{}?depth=1'.format(vnf.get("vnf-id"))
         path = self._aai_versioned_path(raw_path)
 
         response = self._request('get', path=path, data=None)
         if response is None or response.status_code != 200:
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
-                                                          triage_translator_data,
-                                                          reason=response)
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
+                                                          triage_translator_data, reason=response)
             return None
         generic_vnf_details = response.json()
 
         if generic_vnf_details is None or not generic_vnf_details.get('vf-modules') \
                 or not generic_vnf_details.get('vf-modules').get('vf-module'):
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
                                                           triage_translator_data,
                                                           reason="Generic-VNF No detailed data for VF-modules")
             return None
@@ -901,7 +895,7 @@ class AAI(base.InventoryProviderBase):
             vs_link_list.append(rl_data_list[i].get('link'))
         return vs_link_list
 
-    def resolve_complex_info_link_for_v_server(self, candidate, v_server, cloud_owner, cloud_region_id, service_type,
+    def resolve_complex_info_link_for_v_server(self, candidate_id, v_server, cloud_owner, cloud_region_id, service_type,
                                                demand_name, triage_translator_data):
         related_to = "pserver"
         rl_data_list = self._get_aai_rel_link_data(
@@ -913,8 +907,7 @@ class AAI(base.InventoryProviderBase):
             self._log_multiple_item_error(
                 demand_name, service_type, related_to, "item",
                 "VSERVER", v_server)
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
                                                           triage_translator_data, reason="item VSERVER")
             return None
         rl_data = rl_data_list[0]
@@ -928,10 +921,8 @@ class AAI(base.InventoryProviderBase):
             # if HPA_feature is disabled
             if not self.conf.HPA_enabled:
                 # Triage Tool Feature Changes
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
-                                                              triage_translator_data,
-                                                              reason="ps link not found")
+                self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
+                                                              triage_translator_data, reason="ps link not found")
                 return None
             else:
                 if not (cloud_owner and cloud_region_id):
@@ -939,8 +930,7 @@ class AAI(base.InventoryProviderBase):
                               "available from A&AI".
                               format(demand_name))
                     # Triage Tool Feature Changes
-                    self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                                  candidate['location_id'], demand_name,
+                    self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
                                                                   triage_translator_data,
                                                                   reason="Cloud owner and cloud region "
                                                                          "id not found")
@@ -955,10 +945,8 @@ class AAI(base.InventoryProviderBase):
                                          data=None)
                 if response is None or response.status_code != 200:
                     # Triage Tool Feature Changes
-                    self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                                  candidate['location_id'], demand_name,
-                                                                  triage_translator_data,
-                                                                  reason=response)
+                    self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
+                                                                  triage_translator_data, reason=response)
                     return None
                 body = response.json()
         else:
@@ -968,20 +956,16 @@ class AAI(base.InventoryProviderBase):
                               "not found in A&AI: {}").
                           format(demand_name, ps_link))
                 # Triage Tool Feature Changes
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
-                                                              triage_translator_data,
-                                                              reason="ps path not found")
+                self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
+                                                              triage_translator_data, reason="ps path not found")
                 return None  # move ahead with the next vnf
             path = self._aai_versioned_path(ps_path)
             response = self._request(
                 path=path, context="PSERVER", value=ps_path)
             if response is None or response.status_code != 200:
                 # Triage Tool Feature Changes
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
-                                                              triage_translator_data,
-                                                              reason=response)
+                self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
+                                                              triage_translator_data, reason=response)
                 return None
             body = response.json()
 
@@ -996,68 +980,47 @@ class AAI(base.InventoryProviderBase):
             if not self.match_vserver_attribute(rl_data_list):
                 self._log_multiple_item_error(
                     demand_name, service_type, related_to, search_key, "PSERVER", body)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'],
-                                                              demand_name, triage_translator_data,
-                                                              reason="PSERVER error")
+                self.triage_translator.collectDroppedCandiate(candidate_id, cloud_region_id, demand_name,
+                                                              triage_translator_data, reason="PSERVER error")
                 return None
         return rl_data_list[0]
 
-    def resolve_cloud_region_id_and_version_for_vnf(self, candidate, vnf, service_type, demand_name,
-                                                    triage_translator_data):
+    def resolve_cloud_for_vnf(self, candidate_id, location_id, vnf, service_type, demand_name, triage_translator_data):
         related_to = "vserver"
-        search_key = "cloud-region.cloud-region-id"
-
-        rl_data_list = self._get_aai_rel_link_data(
-            data=vnf,
-            related_to=related_to,
-            search_key=search_key
-        )
-        if len(rl_data_list) > 1:
-            if not self.match_vserver_attribute(rl_data_list):
-                self._log_multiple_item_error(
-                    demand_name, service_type, related_to, search_key,
-                    "VNF", vnf)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
-                                                              triage_translator_data,
-                                                              reason="VNF error")
-                return None, None
-        cloud_region_rl_data = rl_data_list[0]
-        cloud_region_id = cloud_region_rl_data.get('d_value')
-
-        # get version for service candidate
-        cloud_region_version_rl_data = {'d_value': ''}
+        search_keys = ["cloud-region.cloud-owner", "cloud-region.cloud-region-id"]
+        cloud_info = dict()
+        for search_key in search_keys:
+            rl_data_list = self._get_aai_rel_link_data(
+                data=vnf, related_to=related_to,
+                search_key=search_key)
+
+            if len(rl_data_list) > 1:
+                if not self.match_vserver_attribute(rl_data_list):
+                    self._log_multiple_item_error(
+                        demand_name, service_type, related_to, search_key,
+                        "VNF", vnf)
+                    self.triage_translator.collectDroppedCandiate(candidate_id,
+                                                                  location_id, demand_name,
+                                                                  triage_translator_data,
+                                                                  reason="VNF error")
+                    return None
+            cloud_info[search_key.split(".")[1].replace('-', '_')] = rl_data_list[0].get('d_value') if rl_data_list[
+                0] else None
+        cloud_info['cloud_region_version'] = self.get_cloud_region_version(cloud_info['cloud_region_id'])
+        cloud_info['location_type'] = 'att_aic'
+        cloud_info['location_id'] = cloud_info.pop('cloud_region_id')
+        return cloud_info
+
+    def get_cloud_region_version(self, cloud_region_id):
         if cloud_region_id:
             regions = self.resolve_cloud_regions_by_cloud_region_id(cloud_region_id)
             if regions is None:
-                return cloud_region_rl_data, None
-
+                return None
             for region in regions:
                 if "cloud-region-version" in region:
-                    cloud_region_version_rl_data['d_value'] = self._get_version_from_string(region["cloud-region-version"])
-
-        return cloud_region_rl_data, cloud_region_version_rl_data
+                    return self._get_version_from_string(region["cloud-region-version"])
 
-    def resolve_cloud_owner_for_vnf(self, candidate, vnf, service_type, demand_name, triage_translator_data):
-        related_to = "vserver"
-        search_key = "cloud-region.cloud-owner"
-        rl_data_list = self._get_aai_rel_link_data(
-            data=vnf, related_to=related_to,
-            search_key=search_key)
-
-        if len(rl_data_list) > 1:
-            if not self.match_vserver_attribute(rl_data_list):
-                self._log_multiple_item_error(
-                    demand_name, service_type, related_to, search_key,
-                    "VNF", vnf)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
-                                                              triage_translator_data,
-                                                              reason="VNF error")
-                return None
-        return rl_data_list[0]
-
-    def resolve_global_customer_id_for_vnf(self, candidate, vnf, customer_id, service_type, demand_name,
+    def resolve_global_customer_id_for_vnf(self, candidate_id, location_id, vnf, customer_id, service_type, demand_name,
                                            triage_translator_data):
         related_to = "service-instance"
         search_key = "customer.global-customer-id"
@@ -1073,13 +1036,13 @@ class AAI(base.InventoryProviderBase):
             if not self.match_vserver_attribute(rl_data_list):
                 self._log_multiple_item_error(
                     demand_name, service_type, related_to, search_key, "VNF", vnf)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'],
+                self.triage_translator.collectDroppedCandiate(candidate_id, location_id,
                                                               demand_name, triage_translator_data,
                                                               reason=" match_vserver_attribute generic-vnf")
                 return None
         return rl_data_list[0]
 
-    def resolve_service_instance_id_for_vnf(self, candidate, vnf, customer_id, service_type, demand_name,
+    def resolve_service_instance_id_for_vnf(self, candidate_id, location_id, vnf, customer_id, service_type, demand_name,
                                             triage_translator_data):
         related_to = "service-instance"
         search_key = "service-instance.service-instance-id"
@@ -1095,20 +1058,19 @@ class AAI(base.InventoryProviderBase):
             if not self.match_vserver_attribute(rl_data_list):
                 self._log_multiple_item_error(
                     demand_name, service_type, related_to, search_key, "VNF", vnf)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'],
+                self.triage_translator.collectDroppedCandiate(candidate_id, location_id,
                                                               demand_name, triage_translator_data,
                                                               reason="multiple_item_error generic-vnf")
                 return None
         return rl_data_list[0]
 
-    def build_complex_info_for_candidate(self, candidate, vnf, complex_list, service_type, demand_name,
+    def build_complex_info_for_candidate(self, candidate_id, location_id, vnf, complex_list, service_type, demand_name,
                                          triage_translator_data):
         if not complex_list or \
                 len(complex_list) < 1:
             LOG.error("Complex information not "
                       "available from A&AI")
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
                                                           triage_translator_data,
                                                           reason="Complex information not available from A&AI")
             return
@@ -1123,8 +1085,7 @@ class AAI(base.InventoryProviderBase):
                 self._log_multiple_item_error(
                     demand_name, service_type, related_to, search_key,
                     "VNF", vnf)
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
+                self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
                                                               triage_translator_data,
                                                               reason="Generic-vnf error")
                 return
@@ -1138,8 +1099,7 @@ class AAI(base.InventoryProviderBase):
             LOG.debug("{} complex information not "
                       "available from A&AI - {}".
                       format(demand_name, complex_link))
-            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                          candidate['location_id'], demand_name,
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
                                                           triage_translator_data,
                                                           reason="Complex information not available from A&AI")
             return  # move ahead with the next vnf
@@ -1152,32 +1112,18 @@ class AAI(base.InventoryProviderBase):
                 LOG.debug("{} complex information not "
                           "available from A&AI - {}".
                           format(demand_name, complex_link))
-                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                              candidate['location_id'], demand_name,
+                self.triage_translator.collectDroppedCandiate(candidate_id, location_id, demand_name,
                                                               triage_translator_data,
                                                               reason="Complex information not available from A&AI")
                 return  # move ahead with the next vnf
-            candidate['physical_location_id'] = \
-                complex_id
-            candidate['complex_name'] = \
-                complex_info.get('complex-name')
-            candidate['latitude'] = \
-                complex_info.get('latitude')
-            candidate['longitude'] = \
-                complex_info.get('longitude')
-            candidate['state'] = \
-                complex_info.get('state')
-            candidate['country'] = \
-                complex_info.get('country')
-            candidate['city'] = \
-                complex_info.get('city')
-            candidate['region'] = \
-                complex_info.get('region')
+
+            complex_info = self.build_complex_dict(complex_info, '')
+            return complex_info
 
     def resolve_demands(self, demands, plan_info, triage_translator_data):
         """Resolve demands into inventory candidate lists"""
 
-        self.triage_translator.getPlanIdNAme(plan_info['plan_name'], plan_info['plan_id'],triage_translator_data)
+        self.triage_translator.getPlanIdNAme(plan_info['plan_name'], plan_info['plan_id'], triage_translator_data)
 
         resolved_demands = {}
         for name, requirements in demands.items():
@@ -1189,9 +1135,9 @@ class AAI(base.InventoryProviderBase):
                 candidate_uniqueness = requirement.get('unique', 'true')
                 filtering_attributes = requirement.get('filtering_attributes')
                 passthrough_attributes = requirement.get('passthrough_attributes')
-                #TODO: may need to support multiple service_type and customer_id in the futrue
+                # TODO: may need to support multiple service_type and customer_id in the futrue
 
-                #TODO: make it consistent for dash and underscore
+                # TODO: make it consistent for dash and underscore
 
                 if filtering_attributes:
                     # catch equipment-role and service-type from template
@@ -1241,63 +1187,27 @@ class AAI(base.InventoryProviderBase):
                 service_resource_id = requirement.get('service_resource_id') \
                     if requirement.get('service_resource_id') else ''
 
-                # add all the candidates of cloud type
                 if inventory_type == 'cloud':
                     # load region candidates from cache
                     regions = self._get_regions()
-
                     if not regions or len(regions) < 1:
-                        LOG.debug("Region information is not "
-                                  "available in cache")
+                        LOG.debug("Region information is not available in cache")
                     for region_id, region in regions.items():
                         # Pick only candidates from the restricted_region
-
-                        candidate = dict()
-                        candidate['inventory_provider'] = 'aai'
-                        candidate['service_resource_id'] = service_resource_id
-                        candidate['inventory_type'] = 'cloud'
-                        candidate['candidate_id'] = region_id
-                        candidate['location_id'] = region_id
-                        candidate['location_type'] = 'att_aic'
-                        candidate['cost'] = self.conf.data.cloud_candidate_cost
-                        candidate['cloud_region_version'] = \
-                            self._get_version_from_string(
-                                region['cloud_region_version'])
-                        candidate['cloud_owner'] = \
-                            region['cloud_owner']
-
-                        candidate['physical_location_id'] = \
-                            region['complex']['complex_id']
-                        candidate['complex_name'] = \
-                            region['complex']['complex_name']
-                        candidate['latitude'] = \
-                            region['complex']['latitude']
-                        candidate['longitude'] = \
-                            region['complex']['longitude']
-                        candidate['city'] = \
-                            region['complex']['city']
-                        candidate['state'] = \
-                            region['complex']['state']
-                        candidate['region'] = \
-                            region['complex']['region']
-                        candidate['country'] = \
-                            region['complex']['country']
-                        candidate['uniqueness'] = candidate_uniqueness
-
-                        # Added vim-id for short-term workaround
-                        if self.conf.HPA_enabled:
-                            candidate['vim-id'] = \
-                                region['cloud_owner'] + '_' + region_id
-                            # Added for HPA
-                            candidate['flavors'] = \
-                                region['flavors']
-
-                        if self.check_sriov_automation(
-                                candidate['cloud_region_version'], name,
-                                candidate['candidate_id']):
-                            candidate['sriov_automation'] = 'true'
-                        else:
-                            candidate['sriov_automation'] = 'false'
+                        info = Candidate.build_candidate_info('aai', inventory_type, self.conf.data.cloud_candidate_cost,
+                                                              candidate_uniqueness, region_id, service_resource_id)
+                        cloud = self.resolve_cloud_for_region(region, region_id)
+                        complex_info = self.build_complex_dict(region['complex'], inventory_type)
+                        flavors = self.resolve_flavors_for_region(region['flavors'])
+                        other = dict()
+                        other['vim-id'] = self.get_vim_id(cloud['cloud_owner'], cloud['location_id'])
+                        other['sriov_automation'] = 'true' if self.check_sriov_automation(cloud['cloud_region_version'],
+                                                                                          name,
+                                                                                          info['candidate_id']) \
+                                                                                          else 'false'
+                        cloud_candidate = Cloud(info=info, cloud_region=cloud, complex=complex_info, flavors=flavors,
+                                                additional_fields=other)
+                        candidate = cloud_candidate.convert_nested_dict_to_dict()
 
                         cloud_region_attr = dict()
                         cloud_region_attr['cloud-owner'] = region['cloud_owner']
@@ -1307,9 +1217,12 @@ class AAI(base.InventoryProviderBase):
                         cloud_region_attr['complex-name'] = region['complex_name']
                         cloud_region_attr['physical-location-id'] = region['physical_location_id']
 
-                        if filtering_attributes and (not self.match_inventory_attributes(filtering_attributes, cloud_region_attr, candidate['candidate_id'])):
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason='attributes and match invetory attributes')
+                        if filtering_attributes and (not self.match_inventory_attributes(filtering_attributes,
+                                                                                         cloud_region_attr, candidate['candidate_id'])):
+                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
+                                                                          candidate['location_id'], name,
+                                                                          triage_translator_data,
+                                                                          reason='attributes and match invetory attributes')
                             continue
 
                         if conflict_identifier:
@@ -1321,25 +1234,24 @@ class AAI(base.InventoryProviderBase):
 
                         self.assign_candidate_existing_placement(candidate, existing_placement)
 
-                        # Pick only candidates not in the excluded list
-                        # if excluded candidate list is provided
-                        if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True, name, triage_translator_data):
+                        # Pick only candidates not in the excluded list, if excluded candidate list is provided
+                        if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True,
+                                                                                name, triage_translator_data):
                             continue
 
-                        # Pick only candidates in the required list
-                        # if required candidate list is provided
-                        if required_candidates and not self.match_candidate_by_list(candidate, required_candidates, False, name, triage_translator_data):
+                        # Pick only candidates in the required list, if required candidate list is provided
+                        if required_candidates and not self.match_candidate_by_list(candidate, required_candidates,
+                                                                                    False, name,
+                                                                                    triage_translator_data):
                             continue
 
-                        self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data)
+                        self.add_passthrough_attributes(candidate, passthrough_attributes, name)
                         # add candidate to demand candidates
                         resolved_demands[name].append(candidate)
                         LOG.debug(">>>>>>> Candidate <<<<<<<")
                         LOG.debug(json.dumps(candidate, indent=4))
 
-                elif (inventory_type == 'service') \
-                        and customer_id:
-
+                elif (inventory_type == 'service') and customer_id:
                     # First level query to get the list of generic vnfs
                     vnf_by_model_invariant = list()
                     if filtering_attributes and model_invariant_id:
@@ -1348,7 +1260,8 @@ class AAI(base.InventoryProviderBase):
                                    '?model-invariant-id={}&depth=0'.format(model_invariant_id)
                         if model_version_id:
                             raw_path = '/network/generic-vnfs/' \
-                                       '?model-invariant-id={}&model-version-id={}&depth=0'.format(model_invariant_id, model_version_id)
+                                       '?model-invariant-id={}&model-version-id={}&depth=0'.format(model_invariant_id,
+                                                                                                   model_version_id)
                         path = self._aai_versioned_path(raw_path)
                         vnf_by_model_invariant = self.first_level_service_call(path, name, service_type)
 
@@ -1367,121 +1280,75 @@ class AAI(base.InventoryProviderBase):
                         vnf_id = vnf.get('vnf-id')
                         if vnf_id in vnf_dict:
                             continue
-
                         # add vnf (with vnf_id as key) to the dictionary
                         vnf_dict[vnf_id] = vnf
-
-                        # create a default candidate
-                        candidate = dict()
-                        candidate['inventory_provider'] = 'aai'
-                        candidate['service_resource_id'] = service_resource_id
-                        candidate['inventory_type'] = inventory_type
-                        candidate['candidate_id'] = ''
-                        candidate['location_id'] = ''
-                        candidate['location_type'] = 'att_aic'
-                        candidate['host_id'] = ''
-                        candidate['cost'] = self.conf.data.service_candidate_cost
-                        candidate['cloud_owner'] = ''
-                        candidate['cloud_region_version'] = ''
-                        candidate['vlan_key'] = vlan_key
-                        candidate['port_key'] = port_key
-                        candidate['uniqueness'] = candidate_uniqueness
-
-                        # start populating the candidate
-                        candidate['host_id'] = vnf.get("vnf-name")
-
-                        rl_data = self.resolve_cloud_owner_for_vnf(candidate, vnf, service_type, name,
-                                                                   triage_translator_data)
-                        if rl_data is None:
-                            continue
-                        else:
-                            cloud_owner = rl_data.get('d_value')
-
-                        candidate['cloud_owner'] = cloud_owner
-
-                        cloud_region_id_rl_data, cloud_region_version_rl_data = self.resolve_cloud_region_id_and_version_for_vnf(
-                            candidate, vnf, service_type, name, triage_translator_data)
-
-                        if cloud_region_id_rl_data is None or cloud_region_version_rl_data is None:
+                        vnf_info = dict()
+                        vnf_info['host_id'] = vnf.get("vnf-name")
+                        vlan_info = self.build_vlan_info(vlan_key, port_key)
+                        cloud = self.resolve_cloud_for_vnf('', '', vnf, service_type, name, triage_translator_data)
+                        if cloud['location_id'] is None or cloud['cloud_owner'] is None or \
+                                cloud['cloud_region_version'] is None:
                             continue
 
-                        cloud_region_id = cloud_region_id_rl_data.get('d_value')
-                        cloud_region_version = cloud_region_version_rl_data.get('d_value')
-                        candidate['location_id'] = cloud_region_id
-                        candidate['cloud_region_version'] = cloud_region_version
-
-                        # Added vim-id for short-term workaround
-                        if self.conf.HPA_enabled:
-                            if not cloud_owner:
-                                continue
-                            candidate['vim-id'] = \
-                                candidate['cloud_owner'] + '_' + cloud_region_id
-
-                        if self.check_sriov_automation(
-                                candidate['cloud_region_version'], name,
-                                candidate['host_id']):
-                            candidate['sriov_automation'] = 'true'
-                        else:
-                            candidate['sriov_automation'] = 'false'
-
-                        rl_data = self.resolve_global_customer_id_for_vnf(candidate, vnf, customer_id, service_type,
-                                                                          name, triage_translator_data)
+                        rl_data = self.resolve_global_customer_id_for_vnf('', cloud['location_id'], vnf, customer_id,
+                                                                          service_type, name, triage_translator_data)
                         if rl_data is None:
                             continue
                         else:
                             vs_cust_id = rl_data.get('d_value')
-
-                        rl_data = self.resolve_service_instance_id_for_vnf(candidate, vnf, customer_id,
+                        rl_data = self.resolve_service_instance_id_for_vnf('', cloud['location_id'], vnf, customer_id,
                                                                            service_type, name, triage_translator_data)
                         if rl_data is None:
                             continue
                         else:
                             vs_service_instance_id = rl_data.get('d_value')
 
+                        # INFO
                         if vs_cust_id and vs_cust_id == customer_id:
-                            candidate['candidate_id'] = \
-                                vs_service_instance_id
+                            info = Candidate.build_candidate_info('aai', inventory_type,
+                                                                  self.conf.data.service_candidate_cost,
+                                                                  candidate_uniqueness, vs_service_instance_id,
+                                                                  service_resource_id)
                         else:  # vserver is for a different customer
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                                          candidate['location_id'], name,
+                            self.triage_translator.collectDroppedCandiate('', cloud['location_id'], name,
                                                                           triage_translator_data,
-                                                                          reason= "vserver is for a different customer")
+                                                                          reason="vserver is for a different customer")
                             continue
+                        # Added vim-id for short-term workaround
+                        other = dict()
+                        other['vim-id'] = self.get_vim_id(cloud['cloud_owner'], cloud['location_id'])
+                        other['sriov_automation'] = 'true' if self.check_sriov_automation(
+                            cloud['cloud_region_version'], name, info['candidate_id'])  else 'false'
 
                         # Second level query to get the pserver from vserver
                         complex_list = list()
-                        vs_link_list = self.resolve_v_server_links_for_vnf(vnf)
-
-                        for vs_link in vs_link_list:
-
-                            body = self.resolove_v_server_for_candidate(candidate, vs_link, True, name,
-                                                                        triage_translator_data)
-                            if body is None:
-                                continue
-
-                            rl_data = self.resolve_complex_info_link_for_v_server(candidate, body, cloud_owner,
-                                                                                  cloud_region_id, service_type,
-                                                                                  name, triage_translator_data)
-                            if rl_data is None:
-                                continue
-
-                            complex_list.append(rl_data)
-
-                        self.build_complex_info_for_candidate(candidate, vnf, complex_list, service_type, name,
-                                                              triage_translator_data)
-
-                        if "complex_name" not in candidate:
+                        for complex_link in self.resolve_v_server_and_complex_link_for_vnf(info['candidate_id'], cloud,
+                                                                                           vnf, name,
+                                                                                           triage_translator_data,
+                                                                                           service_type):
+                            complex_list.append(complex_link[1])
+                        complex_info = self.build_complex_info_for_candidate(info['candidate_id'], cloud['location_id'],
+                                                                             vnf, complex_list, service_type, name,
+                                                                             triage_translator_data)
+                        if "complex_name" not in complex_info:
                             continue
 
+                        service_candidate = Service(info=info, cloud_region=cloud, complex=complex_info,
+                                                    generic_vnf=vnf_info, additional_fields=other, vlan=vlan_info)
+                        candidate = service_candidate.convert_nested_dict_to_dict()
+
                         # add specifal parameters for comparsion
                         vnf['global-customer-id'] = customer_id
                         vnf['customer-id'] = customer_id
-                        vnf['cloud-region-id'] = cloud_region_id
-                        vnf['physical-location-id'] = candidate.get('physical_location_id')
+                        vnf['cloud-region-id'] = cloud.get('cloud_region_id')
+                        vnf['physical-location-id'] = complex_info.get('physical_location_id')
 
-                        if filtering_attributes and not self.match_inventory_attributes(filtering_attributes, vnf, candidate['candidate_id']):
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason="attibute check error")
+                        if filtering_attributes and not self.match_inventory_attributes(filtering_attributes, vnf,
+                                                                                        candidate['candidate_id']):
+                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
+                                                                          candidate['location_id'], name,
+                                                                          triage_translator_data,
+                                                                          reason="attibute check error")
                             continue
                         self.assign_candidate_existing_placement(candidate, existing_placement)
 
@@ -1494,7 +1361,8 @@ class AAI(base.InventoryProviderBase):
                         # Pick only candidates in the required list
                         # if required candidate list is provided
                         if required_candidates and not self.match_candidate_by_list(candidate, required_candidates,
-                                                                                    False, name, triage_translator_data):
+                                                                                    False, name,
+                                                                                    triage_translator_data):
                             continue
 
                         # add the candidate to the demand
@@ -1504,13 +1372,12 @@ class AAI(base.InventoryProviderBase):
                                                  triage_translator_data):
                             continue
                         else:
-                            self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data)
+                            self.add_passthrough_attributes(candidate, passthrough_attributes, name)
                             resolved_demands[name].append(candidate)
                             LOG.debug(">>>>>>> Candidate <<<<<<<")
                             LOG.debug(json.dumps(candidate, indent=4))
 
-                elif (inventory_type == 'vfmodule') \
-                        and customer_id:
+                elif (inventory_type == 'vfmodule') and customer_id:
 
                     # First level query to get the list of generic vnfs
                     vnf_by_model_invariant = list()
@@ -1527,9 +1394,8 @@ class AAI(base.InventoryProviderBase):
 
                     vnf_by_service_type = list()
                     if service_type or equipment_role:
-                        path = self._aai_versioned_path(
-                            '/network/generic-vnfs/'
-                            '?equipment-role={}&depth=0'.format(service_type))
+                        path = self._aai_versioned_path('/network/generic-vnfs/'
+                                                        '?equipment-role={}&depth=0'.format(service_type))
                         vnf_by_service_type = self.first_level_service_call(path, name, service_type)
 
                     generic_vnf = vnf_by_model_invariant + vnf_by_service_type
@@ -1540,186 +1406,108 @@ class AAI(base.InventoryProviderBase):
                         vnf_id = vnf.get('vnf-id')
                         if vnf_id in vnf_dict:
                             continue
-
                         # add vnf (with vnf_id as key) to the dictionary
                         vnf_dict[vnf_id] = vnf
 
-                        # create a default candidate
-                        candidate = dict()
-                        candidate['inventory_provider'] = 'aai'
-                        candidate['service_resource_id'] = service_resource_id
-                        candidate['inventory_type'] = inventory_type
-                        candidate['candidate_id'] = ''
-                        candidate['location_id'] = ''
-                        candidate['location_type'] = 'att_aic'
-                        candidate['host_id'] = ''
-                        candidate['cost'] = self.conf.data.service_candidate_cost
-                        candidate['cloud_owner'] = ''
-                        candidate['cloud_region_version'] = ''
-                        candidate['vlan_key'] = vlan_key
-                        candidate['port_key'] = port_key
-                        candidate['uniqueness'] = candidate_uniqueness
-
-                        # start populating the candidate
-                        candidate['host_id'] = vnf.get("vnf-name")
-
-                        candidate['nf-name'] = vnf.get("vnf-name")
-                        candidate['nf-id'] = vnf.get("vnf-id")
-                        candidate['nf-type'] = 'vnf'
-                        candidate['vnf-type'] = vnf.get("vnf-type")
-                        candidate['ipv4-oam-address'] = ''
-                        candidate['ipv6-oam-address'] = ''
-
-                        if vnf.get("ipv4-oam-address"):
-                            candidate['ipv4-oam-address'] = vnf.get("ipv4-oam-address")
-                        if vnf.get("ipv6-oam-address"):
-                            candidate['ipv6-oam-address'] = vnf.get("ipv6-oam-address")
-
-                        rl_data = self.resolve_global_customer_id_for_vnf(candidate, vnf, customer_id, service_type,
-                                                                          name, triage_translator_data)
+                        # INFO
+                        info = Candidate.build_candidate_info('aai', inventory_type, self.conf.data.service_candidate_cost,
+                                                              candidate_uniqueness, "", service_resource_id)
+                        # VLAN INFO
+                        vlan_info = self.build_vlan_info(vlan_key, port_key)
+                        # Generic VNF Info
+                        vnf_info = self.get_vnf_info(vnf)
+
+                        rl_data = self.resolve_global_customer_id_for_vnf('', '', vnf, customer_id,
+                                                                          service_type, name, triage_translator_data)
                         if rl_data is None:
                             continue
                         else:
                             vs_cust_id = rl_data.get('d_value')
 
-                        rl_data = self.resolve_service_instance_id_for_vnf(candidate, vnf, customer_id,
+                        rl_data = self.resolve_service_instance_id_for_vnf('', '', vnf, customer_id,
                                                                            service_type, name, triage_translator_data)
                         if rl_data is None:
                             continue
                         else:
                             vs_service_instance_id = rl_data.get('d_value')
 
+                        service_info = dict()
                         if vs_cust_id and vs_cust_id == customer_id:
-                            candidate['service_instance_id'] = vs_service_instance_id
+                            service_info['service_instance_id'] = vs_service_instance_id
                         else:  # vserver is for a different customer
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                                          candidate['location_id'], name,
+                            self.triage_translator.collectDroppedCandiate('', '', name,
                                                                           triage_translator_data,
                                                                           reason="candidate is for a different customer")
                             continue
 
-                        vf_modules_list = self.resolve_vf_modules_for_generic_vnf(candidate, vnf, name,
+                        vf_modules_list = self.resolve_vf_modules_for_generic_vnf('', '', vnf, name,
                                                                                   triage_translator_data)
                         if vf_modules_list is None:
                             continue
 
-                        candidate_base = candidate
                         for vf_module in vf_modules_list:
                             # for vfmodule demands we allow to have vfmodules from different cloud regions
-                            candidate = copy.deepcopy(candidate_base)
-                            candidate['candidate_id'] = vf_module.get("vf-module-id")
-                            candidate['vf-module-name'] = vf_module.get("vf-module-name")
-                            candidate['vf-module-id'] = vf_module.get("vf-module-id")
-
-                            rl_data = self.resolve_cloud_owner_for_vnf(candidate, vf_module, service_type, name,
-                                                                       triage_translator_data)
-                            if rl_data is None:
-                                continue
-                            else:
-                                cloud_owner = rl_data.get('d_value')
-                            candidate['cloud_owner'] = cloud_owner
-
-                            cloud_region_id_rl_data, cloud_region_version_rl_data = self.resolve_cloud_region_id_and_version_for_vnf(
-                                candidate, vf_module, service_type, name, triage_translator_data)
-
-                            if cloud_region_id_rl_data is None or cloud_region_version_rl_data is None:
+                            info['candidate_id'] = vf_module.get("vf-module-id")
+                            vf_module_info = self.get_vf_module(vf_module)
+                            cloud = self.resolve_cloud_for_vnf(info['candidate_id'], '', vf_module, service_type, name,
+                                                               triage_translator_data)
+                            if cloud['location_id'] is None or cloud['cloud_owner'] is None or \
+                                    cloud['cloud_region_version'] is None:
                                 continue
 
-                            cloud_region_id = cloud_region_id_rl_data.get('d_value')
-                            cloud_region_version = cloud_region_version_rl_data.get('d_value')
-                            candidate['location_id'] = cloud_region_id
-                            candidate['cloud_region_version'] = cloud_region_version
-
-                            # Added vim-id for short-term workaround
-                            if self.conf.HPA_enabled:
-                                if not cloud_owner:
-                                    continue
-                                candidate['vim-id'] = \
-                                    candidate['cloud_owner'] + '_' + cloud_region_id
-
-                            if self.check_sriov_automation(
-                                    candidate['cloud_region_version'], name,
-                                    candidate['host_id']):
-                                candidate['sriov_automation'] = 'true'
-                            else:
-                                candidate['sriov_automation'] = 'false'
+                            # OTHER - Added vim-id for short-term workaround
+                            other = dict()
+                            other['vim-id'] = self.get_vim_id(cloud['cloud_owner'], cloud['location_id'])
+                            other['sriov_automation'] = 'true' if self.check_sriov_automation(
+                                cloud['cloud_region_version'], name, info['candidate_id']) else 'false'
 
                             # Second level query to get the pserver from vserver
-                            candidate['vservers'] = list()
+                            vserver_info = dict()
+                            vserver_info['vservers'] = list()
                             complex_list = list()
-                            vs_link_list = self.resolve_v_server_links_for_vnf(vf_module)
-
-                            for vs_link in vs_link_list:
-
-                                body = self.resolove_v_server_for_candidate(candidate, vs_link, True, name,
-                                                                            triage_translator_data)
-                                if body is None:
-                                    continue
-
+                            for v_server, complex_link in self.resolve_v_server_and_complex_link_for_vnf(info['candidate_id'],
+                                                                                                         cloud, vnf, name,
+                                                                                                         triage_translator_data,
+                                                                                                         service_type):
+                                complex_list.append(complex_link)
                                 candidate_vserver = dict()
-                                candidate_vserver['vserver-id'] = body.get('vserver-id')
-                                candidate_vserver['vserver-name'] = body.get('vserver-name')
-
-                                rl_data = self.resolve_complex_info_link_for_v_server(candidate, body, cloud_owner,
-                                                                                      cloud_region_id, service_type,
-                                                                                      name, triage_translator_data)
-                                if rl_data is None:
-                                    continue
-
-                                #Interfaces info
-                                if not body.get('l-interfaces') or not body.get('l-interfaces').get('l-interface'):
-                                    self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
-                                                                                  candidate['location_id'], name,
-                                                                                  triage_translator_data,
-                                                                                  reason="VF-server interfaces error")
-                                    continue
+                                candidate_vserver['vserver-id'] = v_server.get('vserver-id')
+                                candidate_vserver['vserver-name'] = v_server.get('vserver-name')
+                                l_interfaces = self.get_l_interfaces_from_vserver(info['candidate_id'], cloud['location_id'],
+                                                                                  v_server, name, triage_translator_data)
+                                if l_interfaces:
+                                    candidate_vserver['l-interfaces'] = l_interfaces
                                 else:
-                                    l_interfaces = body.get('l-interfaces').get('l-interface')
-                                    candidate_vserver['l-interfaces'] = list()
-
-                                    for l_interface in l_interfaces:
-                                        vserver_interface = dict()
-                                        vserver_interface['interface-id'] = l_interface.get('interface-id')
-                                        vserver_interface['interface-name'] = l_interface.get('interface-name')
-                                        vserver_interface['macaddr'] = l_interface.get('macaddr')
-                                        vserver_interface['network-id'] = l_interface.get('network-name')
-                                        vserver_interface['network-name'] = ''
-                                        vserver_interface['ipv4-addresses'] = list()
-                                        vserver_interface['ipv6-addresses'] = list()
-
-                                        if l_interface.get('l3-interface-ipv4-address-list'):
-                                            for ip_address_info in l_interface.get('l3-interface-ipv4-address-list'):
-                                                vserver_interface['ipv4-addresses'].\
-                                                    append(ip_address_info.get('l3-interface-ipv4-address'))
-
-                                        if l_interface.get('l3-interface-ipv6-address-list'):
-                                            for ip_address_info in l_interface.get('l3-interface-ipv6-address-list'):
-                                                vserver_interface['ipv6-addresses'].\
-                                                    append(ip_address_info.get('l3-interface-ipv6-address'))
-
-                                        candidate_vserver['l-interfaces'].append(vserver_interface)
-
-                                complex_list.append(rl_data)
-                                candidate['vservers'].append(candidate_vserver)
-
-                            self.build_complex_info_for_candidate(candidate, vnf, complex_list, service_type, name,
-                                                                  triage_translator_data)
-
-                            if candidate.get("complex_name") is None:
+                                    continue
+                                vserver_info['vservers'].append(candidate_vserver)
+
+                            # COMPLEX
+                            complex_info = self.build_complex_info_for_candidate(info['candidate_id'],
+                                                                                 cloud['location_id'], vnf, complex_list,
+                                                                                 service_type, name,
+                                                                                 triage_translator_data)
+                            if complex_info.get("complex_name") is None:
                                 continue
 
+                            vf_module_candidate = VfModule(complex=complex_info, info=info, generic_vnf=vnf_info,
+                                                           cloud_region=cloud, service_instance=service_info,
+                                                           vf_module=vf_module_info, vserver=vserver_info,
+                                                           additional_fields=other, vlan=vlan_info)
+                            candidate = vf_module_candidate.convert_nested_dict_to_dict()
+
                             ##add vf-module parameters for filtering
                             vnf_vf_module_inventory = copy.deepcopy(vnf)
                             vnf_vf_module_inventory.update(vf_module)
                             # add specifal parameters for comparsion
                             vnf_vf_module_inventory['global-customer-id'] = customer_id
                             vnf_vf_module_inventory['customer-id'] = customer_id
-                            vnf_vf_module_inventory['cloud-region-id'] = cloud_region_id
-                            vnf_vf_module_inventory['physical-location-id'] = candidate.get('physical_location_id')
+                            vnf_vf_module_inventory['cloud-region-id'] = cloud.get('location_id')
+                            vnf_vf_module_inventory['physical-location-id'] = complex_info.get('physical_location_id')
                             vnf_vf_module_inventory['service_instance_id'] = vs_service_instance_id
 
-                            if filtering_attributes and not self.match_inventory_attributes(filtering_attributes, vnf_vf_module_inventory,
-                                                                                  candidate['candidate_id']):
+                            if filtering_attributes and not self.match_inventory_attributes(filtering_attributes,
+                                                                                            vnf_vf_module_inventory,
+                                                                                            candidate['candidate_id']):
                                 self.triage_translator.collectDroppedCandiate(candidate['candidate_id'],
                                                                               candidate['location_id'], name,
                                                                               triage_translator_data,
@@ -1729,7 +1517,8 @@ class AAI(base.InventoryProviderBase):
 
                             # Pick only candidates not in the excluded list
                             # if excluded candidate list is provided
-                            if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True,
+                            if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates,
+                                                                                    True,
                                                                                     name, triage_translator_data):
                                 continue
 
@@ -1747,91 +1536,73 @@ class AAI(base.InventoryProviderBase):
                                                      triage_translator_data):
                                 continue
                             else:
-                                self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data)
+                                self.add_passthrough_attributes(candidate, passthrough_attributes, name)
                                 resolved_demands[name].append(candidate)
                                 LOG.debug(">>>>>>> Candidate <<<<<<<")
+                                with open("vf.log", mode='w') as log_file:
+                                    log_file.write(">>>>>>>Vf Candidate <<<<<<<")
+                                    log_file.write(json.dumps(candidate, indent=4))
                                 LOG.debug(json.dumps(candidate, indent=4))
 
                 elif inventory_type == 'transport' \
-                     and customer_id and service_type and \
-                     service_subscription and service_role:
+                        and customer_id and service_type and \
+                        service_subscription and service_role:
+
                     path = self._aai_versioned_path('business/customers/customer/{}/service-subscriptions/'
                                                     'service-subscription/{}/service-instances'
                                                     '?service-type={}&service-role={}'.format(customer_id,
                                                                                               service_subscription,
                                                                                               service_type,
                                                                                               service_role))
-
                     response = self._request('get', path=path, data=None)
-
                     if response is None or response.status_code != 200:
-                        self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                     reason=response.status_code)
+                        self.triage_translator.collectDroppedCandiate("", "", name,
+                                                                      triage_translator_data,
+                                                                      reason=response.status_code)
                         continue
-
                     body = response.json()
-
                     transport_vnfs = body.get('service-instance', [])
 
                     for vnf in transport_vnfs:
-
                         # create a default candidate
-                        candidate = dict()
-                        candidate['inventory_provider'] = 'aai'
-                        candidate['service_resource_id'] = service_resource_id
-                        candidate['inventory_type'] = 'transport'
-                        candidate['candidate_id'] = ''
-                        candidate['location_id'] = ''
-                        candidate['location_type'] = 'att_aic'
-                        candidate['uniqueness'] = candidate_uniqueness
-                        candidate['cost'] = self.conf.data.transport_candidate_cost
-
+                        other = dict()
+                        other['location_id'] = ''
+                        other['location_type'] = 'att_aic'
+                        # INFO
                         vnf_service_instance_id = vnf.get('service-instance-id')
                         if vnf_service_instance_id:
-                            candidate['candidate_id'] = vnf_service_instance_id
+                            info = Candidate.build_candidate_info('aai', inventory_type,
+                                                                  self.conf.data.transport_candidate_cost,
+                                                                  candidate_uniqueness, vnf_service_instance_id,
+                                                                  service_resource_id)
                         else:
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name,triage_translator_data,
-                                                                         reason="service-instance-id error ")
-
-                            continue
-
-                        related_to = "zone"
-                        zone_link = self._get_aai_rel_link(
-                            data=vnf, related_to=related_to)
-
-                        if not zone_link:
-                            LOG.error("Zone information not available"
-                                      "from A&AI for transport candidates")
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason="Zone information not available from A&AI for transport candidates")
-
+                            self.triage_translator.collectDroppedCandiate('', other['location_id'], name, triage_translator_data,
+                                                                          reason="service-instance-id error ")
                             continue
 
-                        zone_aai_path = self._get_aai_path_from_link(zone_link)
-
-                        response = self._request('get', path=zone_aai_path, data=None)
-
-                        if response is None or response.status_code != 200:
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason=response.status_code)
-
+                        # ZONE
+                        zone_info = dict()
+                        zone = self.resolve_zone_for_vnf(info['candidate_id'], other['location_id'], vnf, name,
+                                                         triage_translator_data)
+                        if zone:
+                            zone_info['zone_id'] = zone.get('zone-id')
+                            zone_info['zone_name'] = zone.get('zone-name')
+                        else:
                             continue
-                        body = response.json()
-
-                        candidate['zone_id'] = body.get('zone-id')
-                        candidate['zone_name'] = body.get('zone-name')
 
+                        # COMPLEX
                         related_to = "complex"
                         search_key = "complex.physical-location-id"
                         rel_link_data_list = self._get_aai_rel_link_data(
-                            data=body,
+                            data=zone,
                             related_to=related_to,
                             search_key=search_key
                         )
 
                         if len(rel_link_data_list) > 1:
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason="rel_link_data_list error")
+                            self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'], name,
+                                                                          triage_translator_data,
+                                                                          reason="rel_link_data_list error")
 
                             continue
                         rel_link_data = rel_link_data_list[0]
@@ -1842,8 +1613,9 @@ class AAI(base.InventoryProviderBase):
                             LOG.debug("{} complex information not "
                                       "available from A&AI - {}".
                                       format(name, complex_link))
-                            self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                         reason="complex information not available from A&AI")
+                            self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'], name,
+                                                                          triage_translator_data,
+                                                                          reason="complex information not available from A&AI")
                             continue
                         else:
                             complex_info = self._get_complex(
@@ -1854,27 +1626,16 @@ class AAI(base.InventoryProviderBase):
                                 LOG.debug("{} complex information not "
                                           "available from A&AI - {}".
                                           format(name, complex_link))
-                                self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data,
-                                                                             reason="complex information not available from A&AI")
+                                self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'], name,
+                                                                              triage_translator_data,
+                                                                              reason="complex information not available from A&AI")
                                 continue  # move ahead with the next vnf
-                            candidate['physical_location_id'] = \
-                                complex_id
-                            candidate['complex_name'] = \
-                                complex_info.get('complex-name')
-                            candidate['latitude'] = \
-                                complex_info.get('latitude')
-                            candidate['longitude'] = \
-                                complex_info.get('longitude')
-                            candidate['state'] = \
-                                complex_info.get('state')
-                            candidate['country'] = \
-                                complex_info.get('country')
-                            candidate['city'] = \
-                                complex_info.get('city')
-                            candidate['region'] = \
-                                complex_info.get('region')
-
-                            self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data)
+
+                            complex_info = self.build_complex_dict(complex_info, inventory_type)
+                            transport_candidate = Transport(info=info, zone=zone_info, complex=complex_info, additional_fiels=other)
+                            candidate = transport_candidate.convert_nested_dict_to_dict()
+
+                            self.add_passthrough_attributes(candidate, passthrough_attributes, name)
                             # add candidate to demand candidates
                             resolved_demands[name].append(candidate)
 
@@ -1889,13 +1650,150 @@ class AAI(base.InventoryProviderBase):
                               " {}".format(inventory_type))
         return resolved_demands
 
-    def add_passthrough_attributes(self, candidate, passthrough_attributes, demand_name, triage_translator_data):
+    @staticmethod
+    def build_complex_dict(aai_complex, inv_type):
+        complex_info = dict()
+        valid_keys = ['physical-location-id', 'complex-name', 'latitude', 'longitude', 'state', 'country', 'city',
+                      'region']
+        # for cloud type, complex_id instead of physical-location-id - note
+        if inv_type == "cloud":
+            for valid_key in valid_keys:
+                if '-' in valid_key:
+                    complex_info[valid_key.replace('-', '_')] = aai_complex.get('complex_id') if valid_key == 'physical-location-id' else \
+                        aai_complex.get(valid_key.replace('-', '_'))
+                else:
+                    complex_info[valid_key] = aai_complex.get(valid_key)
+        else:
+            for valid_key in valid_keys:
+                if '-' in valid_key:
+                    complex_info[valid_key.replace('-', '_')] = aai_complex.get(valid_key)
+                else:
+                    complex_info[valid_key] = aai_complex.get(valid_key)
+        return complex_info
+
+    @staticmethod
+    def build_vlan_info(vlan_key, port_key):
+        vlan_info = dict()
+        vlan_info['vlan_key'] = vlan_key
+        vlan_info['port_key'] = port_key
+        return vlan_info
+
+    def resolve_flavors_for_region(self, flavors_obj):
+        if self.conf.HPA_enabled:
+            flavors = dict()
+            flavors['flavors'] = flavors_obj
+        return flavors
+
+    def resolve_v_server_and_complex_link_for_vnf(self, candidate_id, cloud, vnf, name, triage_translator_data,
+                                                  service_type):
+        vs_link_list = self.resolve_v_server_links_for_vnf(vnf)
+        for vs_link in vs_link_list:
+            body = self.resolve_v_server_for_candidate(candidate_id, cloud['location_id'],
+                                                       vs_link, True, name, triage_translator_data)
+            if body is None:
+                continue
+            rl_data = self.resolve_complex_info_link_for_v_server(candidate_id, body,
+                                                                  cloud['cloud_owner'], cloud['location_id'],
+                                                                  service_type, name, triage_translator_data)
+            if rl_data is None:
+                continue
+            yield body, rl_data
+
+    def get_l_interfaces_from_vserver(self, candidate_id, location_id, v_server, name, triage_translator_data):
+        if not v_server.get('l-interfaces') or not v_server.get('l-interfaces').get('l-interface'):
+            self.triage_translator.collectDroppedCandiate(candidate_id,
+                                                          location_id, name,
+                                                          triage_translator_data,
+                                                          reason="VF-server interfaces error")
+            return None
+        else:
+            l_interfaces = v_server.get('l-interfaces').get('l-interface')
+            l_interfaces_list = list()
+
+            for l_interface in l_interfaces:
+                vserver_interface = dict()
+                vserver_interface['interface-id'] = l_interface.get('interface-id')
+                vserver_interface['interface-name'] = l_interface.get('interface-name')
+                vserver_interface['macaddr'] = l_interface.get('macaddr')
+                vserver_interface['network-id'] = l_interface.get('network-name')
+                vserver_interface['network-name'] = ''
+                vserver_interface['ipv4-addresses'] = list()
+                vserver_interface['ipv6-addresses'] = list()
+
+                if l_interface.get('l3-interface-ipv4-address-list'):
+                    for ip_address_info in l_interface.get('l3-interface-ipv4-address-list'):
+                        vserver_interface['ipv4-addresses']. \
+                            append(ip_address_info.get('l3-interface-ipv4-address'))
+
+                if l_interface.get('l3-interface-ipv6-address-list'):
+                    for ip_address_info in l_interface.get('l3-interface-ipv6-address-list'):
+                        vserver_interface['ipv6-addresses']. \
+                            append(ip_address_info.get('l3-interface-ipv6-address'))
+
+                l_interfaces_list.append(vserver_interface)
+            return l_interfaces_list
+
+    @staticmethod
+    def get_vnf_info(vnf):
+        # some validation should happen
+        vnf_info = dict()
+        vnf_info['host_id'] = vnf.get("vnf-name")
+        vnf_info['nf-name'] = vnf.get("vnf-name")
+        vnf_info['nf-id'] = vnf.get("vnf-id")
+        vnf_info['nf-type'] = 'vnf'
+        vnf_info['vnf-type'] = vnf.get("vnf-type")
+        vnf_info['ipv4-oam-address'] = vnf.get("ipv4-oam-address") if vnf.get("ipv4-oam-address") else ""
+        vnf_info['ipv6-oam-address'] = vnf.get("ipv6-oam-address") if vnf.get("ipv6-oam-address") else ""
+        return vnf_info
+
+    @staticmethod
+    def resolve_cloud_for_region(region, region_id):
+        cloud = dict()
+        valid_keys = ['cloud_owner', 'cloud_region_version', 'location_id']
+        for valid_key in valid_keys:
+            cloud[valid_key] = region.get(valid_key) if not valid_key == 'location_id' else region_id
+        cloud['location_type'] = 'att_aic'
+        return cloud
+
+    @staticmethod
+    def get_vf_module(vf_module):
+        vf_module_info = dict()
+        vf_module_info['vf-module-name'] = vf_module.get("vf-module-name")
+        vf_module_info['vf-module-id'] = vf_module.get("vf-module-id")
+        return vf_module_info
+
+    def get_vim_id(self, cloud_owner, cloud_region_id):
+        if self.conf.HPA_enabled:
+            return cloud_owner + '_' + cloud_region_id
+
+    @staticmethod
+    def add_passthrough_attributes(candidate, passthrough_attributes, demand_name):
         if passthrough_attributes is None:
             return
         if len(passthrough_attributes.items()) > 0:
             candidate['passthrough_attributes'] = dict()
-        for key, value in passthrough_attributes.items():
-            candidate['passthrough_attributes'][key] = value
+            for key, value in passthrough_attributes.items():
+                candidate['passthrough_attributes'][key] = value
+
+    def resolve_zone_for_vnf(self, candidate_id, location_id, vnf, name, triage_translator_data):
+        related_to = "zone"
+        zone_link = self._get_aai_rel_link(
+            data=vnf, related_to=related_to)
+        if not zone_link:
+            LOG.error("Zone information not available from A&AI for transport candidates")
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id,
+                                                          name, triage_translator_data,
+                                                          reason="Zone information not available from A&AI for transport candidates")
+            return None
+        zone_aai_path = self._get_aai_path_from_link(zone_link)
+        response = self._request('get', path=zone_aai_path, data=None)
+        if response is None or response.status_code != 200:
+            self.triage_translator.collectDroppedCandiate(candidate_id, location_id, name,
+                                                          triage_translator_data,
+                                                          reason=response.status_code)
+            return None
+        body = response.json()
+        return body
 
     def match_region(self, candidate, restricted_region_id, restricted_complex_id, demand_name, triage_translator_data):
         if self.match_candidate_attribute(
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py
new file mode 100644 (file)
index 0000000..7f241c3
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (C) 2020 Wipro Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+class Candidate:
+    def __init__(self, info):
+        self.candidate_id = info.get('candidate_id')
+        if info.get('candidate_type'):
+            self.candidate_type = info.get('candidate_type')
+        self.inventory_provider = info.get('inventory_provider')
+        self.inventory_type = info.get('inventory_type')
+        self.uniqueness = info.get('uniqueness')
+        self.cost = info.get('cost')
+        self.service_resource_id = info.get('service_resource_id', None)
+
+    def convert_nested_dict_to_dict(self):
+        candidate = dict()
+        nested_dict = self.__dict__
+        keys = nested_dict.keys()
+        for key in keys:
+            if type(nested_dict.get(key)) == dict:
+                candidate.update(nested_dict.get(key, {}))
+            else:
+                candidate[key] = nested_dict.get(key, "")
+        return candidate
+
+    @staticmethod
+    def build_candidate_info(inv_prov, inv_type, cost, uniqueness, id, service_resource_id=None):
+        info = dict()
+        info['candidate_id'] = id
+        # info['candidate_type']
+        info['inventory_provider'] = inv_prov
+        info['inventory_type'] = inv_type
+        info['uniqueness'] = uniqueness
+        info['cost'] = cost
+        info['service_resource_id'] = service_resource_id
+        return info
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/cloud_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/cloud_candidate.py
new file mode 100644 (file)
index 0000000..3507126
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (C) 2020 Wipro Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+
+from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+
+
+class Cloud(Candidate):
+    def __init__(self, **kwargs):
+        super().__init__(kwargs['info'])
+        self.cloud_region = kwargs['cloud_region']
+        self.complex = kwargs['complex']
+        self.flavors = kwargs['flavors']
+        self.additional_fields = kwargs['additional_fields']
\ No newline at end of file
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/service_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/service_candidate.py
new file mode 100644 (file)
index 0000000..d13a670
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (C) 2020 Wipro Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+
+
+class Service(Candidate):
+    def __init__(self, **kwargs):
+        super().__init__(kwargs['info'])
+        self.generic_vnf = kwargs['generic_vnf']
+        self.cloud_region = kwargs['cloud_region']
+        self.complex = kwargs['complex']
+        self.additional_fields = kwargs['additional_fields']
+        self.vlan = kwargs['vlan']
+
+
+
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/transport_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/transport_candidate.py
new file mode 100644 (file)
index 0000000..e2840c0
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (C) 2020 Wipro Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+
+
+class Transport(Candidate):
+    def __init__(self, **kwargs):
+        super().__init__(kwargs['info'])
+        self.zone = kwargs['zone']
+        self.complex = kwargs['complex']
+        self.additional_fields = kwargs['additional_fields']
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/vfmodule_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/vfmodule_candidate.py
new file mode 100644 (file)
index 0000000..c9d99ae
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (C) 2020 Wipro Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+
+from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+
+
+class VfModule(Candidate):
+    def __init__(self,**kwargs):
+        super().__init__(kwargs['info'])
+        self.generic_vnf = kwargs['generic_vnf']
+        self.cloud_region = kwargs['cloud_region']
+        self.service_instance = kwargs['service_instance']
+        self.vf_module = kwargs['vf_module']
+        self.vserver = kwargs['vserver']
+        self.complex = kwargs['complex']
+        self.vlan = kwargs['vlan']
+        self.additional_fields = kwargs['additional_fields']
\ No newline at end of file
index 295057f..55bdef9 100644 (file)
 #
 # -------------------------------------------------------------------------
 #
+import copy
 import json
+import mock
 import unittest
-import copy
+
+from oslo_config import cfg
 
 import conductor.data.plugins.inventory_provider.aai as aai
-import mock
 from conductor.data.plugins.inventory_provider.aai import AAI
 from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
-from oslo_config import cfg
 
 
 class TestAAI(unittest.TestCase):
@@ -52,7 +53,6 @@ class TestAAI(unittest.TestCase):
         self.assertEqual('/{}/query?format=id'.format(self.conf.aai.server_url_version),
                          self.aai_ep._aai_versioned_path("/query?format=id"))
 
-
     def test_resolve_clli_location(self):
 
         req_json_file = './conductor/tests/unit/data/plugins/inventory_provider/_request_clli_location.json'
@@ -150,9 +150,10 @@ class TestAAI(unittest.TestCase):
                                                                                return_value=regions_list)
         self.mock_resolve_cloud_regions_by_cloud_region_id.start()
 
-        self.mock_resolove_v_server_for_candidate = mock.patch.object(AAI, 'resolove_v_server_for_candidate',
+
+        self.mock_resolve_v_server_for_candidate = mock.patch.object(AAI, 'resolve_v_server_for_candidate',
                                                                       return_value=demand_service_response)
-        self.mock_resolove_v_server_for_candidate.start()
+        self.mock_resolve_v_server_for_candidate.start()
 
         complex_link = {"link": "/aai/v10/complex-id", "d_value": 'test-id'}
         self.mock_resolve_complex_info_link_for_v_server = mock.patch.object(AAI,
@@ -165,7 +166,7 @@ class TestAAI(unittest.TestCase):
 
         flavor_info = regions_response["region-name"]["flavors"]
         self.maxDiff = None
-        self.assertEqual({u'demand_name': [
+        self.assertCountEqual({u'demand_name': [
             {'candidate_id': u'service-instance-id', 'city': None,
              'cloud_owner': u'cloud-owner',
              'uniqueness': 'true',
@@ -249,9 +250,9 @@ class TestAAI(unittest.TestCase):
                                                                                return_value=regions)
         self.mock_resolve_cloud_regions_by_cloud_region_id.start()
 
-        self.mock_resolove_v_server_for_candidate = mock.patch.object(AAI, 'resolove_v_server_for_candidate',
+        self.mock_resolve_v_server_for_candidate = mock.patch.object(AAI, 'resolve_v_server_for_candidate',
                                                                       return_value=v_server)
-        self.mock_resolove_v_server_for_candidate.start()
+        self.mock_resolve_v_server_for_candidate.start()
 
         complex_link = {"link": "/aai/v14/cloud-infrastructure/complexes/complex/clli1", "d_value": 'clli1'}
         self.mock_resolve_complex_info_link_for_v_server = mock.patch.object(AAI,
@@ -327,9 +328,9 @@ class TestAAI(unittest.TestCase):
                                                                                return_value=regions)
         self.mock_resolve_cloud_regions_by_cloud_region_id.start()
 
-        self.mock_resolove_v_server_for_candidate = mock.patch.object(AAI, 'resolove_v_server_for_candidate',
+        self.mock_resolve_v_server_for_candidate = mock.patch.object(AAI, 'resolve_v_server_for_candidate',
                                                                        return_value=v_server)
-        self.mock_resolove_v_server_for_candidate.start()
+        self.mock_resolve_v_server_for_candidate.start()
 
         complex_link = {"link": "/aai/v14/cloud-infrastructure/complexes/complex/clli1", "d_value": 'clli1'}
         self.mock_resolve_complex_info_link_for_v_server = mock.patch.object(AAI, 'resolve_complex_info_link_for_v_server',
@@ -356,10 +357,9 @@ class TestAAI(unittest.TestCase):
         self.mock_get_request = mock.patch.object(AAI, '_request', return_value=response)
         self.mock_get_request.start()
 
-        self.assertEqual({u'city': u'Middletown', u'latitude': u'28.543251', u'longitude': u'-81.377112', u'country': u'USA', u'region': u'SE'} ,
+        self.assertEqual({u'city': u'Middletown', u'latitude': u'28.543251', u'longitude': u'-81.377112', u'country': u'USA', u'region': u'SE'},
                          self.aai_ep._get_complex("/v10/complex/complex_id", "complex_id"))
 
-
     def test_check_network_roles(self):
 
         network_role_json_file = './conductor/tests/unit/data/plugins/inventory_provider/_request_network_role.json'
@@ -375,7 +375,6 @@ class TestAAI(unittest.TestCase):
         self.assertEqual(set(['test-cloud-value']) ,
                         self.aai_ep.check_network_roles("network_role_id"))
 
-
     def test_check_candidate_role(self):
 
         candidate_role_json_file = './conductor/tests/unit/data/plugins/inventory_provider/_request_candidate_role.json'
@@ -488,10 +487,9 @@ class TestAAI(unittest.TestCase):
         region_response_file = './conductor/tests/unit/data/plugins/inventory_provider/vfmodule_region.json'
         region_response = json.loads(open(region_response_file).read())
 
-        candidate = dict()
-        candidate['candidate_id'] = 'some_id'
-        candidate['location_id'] = 'some_location_id'
-        candidate['inventory_type'] = 'service'
+        candidate_id = 'some_id'
+        location_id = 'some_location_id'
+        inventory_type = 'service'
 
         response = mock.MagicMock()
         response.status_code = 200
@@ -501,13 +499,13 @@ class TestAAI(unittest.TestCase):
         self.mock_get_request = mock.patch.object(AAI, '_request', return_value=response)
         self.mock_get_request.start()
 
-        link_rl_data = self.aai_ep.resolve_complex_info_link_for_v_server(candidate, v_server, None,
+        link_rl_data = self.aai_ep.resolve_complex_info_link_for_v_server(candidate_id, v_server, None,
                                                                           cloud_region_id, service_type,
                                                                           demand_name, triage_translator_data)
         self.assertEqual(None, link_rl_data)
 
         complex_link = {"link": "/aai/v14/cloud-infrastructure/complexes/complex/clli1", "d_value": 'clli1'}
-        link_rl_data = self.aai_ep.resolve_complex_info_link_for_v_server(candidate, v_server, cloud_owner,
+        link_rl_data = self.aai_ep.resolve_complex_info_link_for_v_server(candidate_id, v_server, cloud_owner,
                                                                           cloud_region_id, service_type,
                                                                           demand_name, triage_translator_data)
         self.assertEqual(complex_link, link_rl_data)
@@ -533,26 +531,25 @@ class TestAAI(unittest.TestCase):
         self.mock_get_complex = mock.patch.object(AAI, '_get_complex', return_value=complex_response)
         self.mock_get_complex.start()
 
-        self.aai_ep.build_complex_info_for_candidate(candidate, None, complex_list_empty, service_type, demand_name,
+        self.aai_ep.build_complex_info_for_candidate(candidate['candidate_id'], candidate['location_id'], None, complex_list_empty, candidate['inventory_type'], demand_name,
                                                      triage_translator_data)
         self.assertEqual(initial_candidate, candidate)
         self.assertEqual(1, TraigeTranslator.collectDroppedCandiate.call_count)
 
-        self.aai_ep.build_complex_info_for_candidate(candidate, None, complex_list, service_type, demand_name,
+        self.aai_ep.build_complex_info_for_candidate(candidate['candidate_id'], candidate['location_id'], None, complex_list, candidate['inventory_type'], demand_name,
                                                      triage_translator_data)
         self.assertEqual(initial_candidate, candidate)
         self.assertEqual(2, TraigeTranslator.collectDroppedCandiate.call_count)
 
         complex_list.pop()
-        self.aai_ep.build_complex_info_for_candidate(candidate, None, complex_list, service_type, demand_name,
+        self.aai_ep.build_complex_info_for_candidate(candidate['candidate_id'], candidate['location_id'], None, complex_list, candidate['inventory_type'], demand_name,
                                                      triage_translator_data)
 
-        self.assertEqual(candidate, {'city': u'example-city-val-27150', 'country': u'example-country-val-94173',
-                                     'region': u'example-region-val-13893', 'inventory_type': 'service',
-                                     'longitude': u'32.89948', 'state': u'example-state-val-59487',
+        self.assertEqual(self.aai_ep.build_complex_info_for_candidate(candidate['candidate_id'], candidate['location_id'], None, complex_list, candidate['inventory_type'], demand_name,
+                                                     triage_translator_data), {'city': u'example-city-val-27150', 'country': u'example-country-val-94173',
+                                     'region': u'example-region-val-13893', 'longitude': u'32.89948', 'state': u'example-state-val-59487',
                                      'physical_location_id': 'clli1', 'latitude': u'example-latitude-val-89101',
-                                     'complex_name': u'clli1', 'location_id': 'some_location_id',
-                                     'candidate_id': 'some_id'})
+                                     'complex_name': u'clli1'})
         self.assertEqual(2, TraigeTranslator.collectDroppedCandiate.call_count)
 
     def test_resolve_vnf_parameters(self):
@@ -561,8 +558,8 @@ class TestAAI(unittest.TestCase):
         demand_name = 'vPGN'
         service_type = 'vFW'
         candidate = dict()
-        candidate['candidate_id'] = 'some_id'
-        candidate['location_id'] = 'some_location_id'
+        candidate_id = 'some_id'
+        location_id = 'some_location_id'
         candidate['inventory_type'] = 'service'
 
         generic_vnf_list_file = './conductor/tests/unit/data/plugins/inventory_provider/vfmodule_service_generic_vnf_list.json'
@@ -572,26 +569,22 @@ class TestAAI(unittest.TestCase):
         region_response_file = './conductor/tests/unit/data/plugins/inventory_provider/vfmodule_region.json'
         region_response = json.loads(open(region_response_file).read())
 
-        self.assertEqual("CloudOwner",
-                         self.aai_ep.resolve_cloud_owner_for_vnf(candidate, good_vnf, service_type,
-                                                                 demand_name, triage_translator_data).get('d_value'))
-        self.assertIsNone(self.aai_ep.resolve_cloud_owner_for_vnf(candidate, bad_vnf, service_type,
-                                                                  demand_name, triage_translator_data))
-
         regions = list()
         regions.append(region_response)
         self.mock_get_regions = mock.patch.object(AAI, 'resolve_cloud_regions_by_cloud_region_id',
                                                   return_value=regions)
         self.mock_get_regions.start()
 
-        cloud_region_rl_data, cloud_region_ver_rl_data = self.aai_ep.resolve_cloud_region_id_and_version_for_vnf(
-            candidate, good_vnf, service_type, demand_name, triage_translator_data)
-        self.assertEqual("RegionOne", cloud_region_rl_data.get('d_value'))
-        self.assertEqual("1", cloud_region_ver_rl_data.get('d_value'))
+        good_cloud_info = self.aai_ep.resolve_cloud_for_vnf(candidate_id, location_id, good_vnf, service_type,
+                                                                 demand_name, triage_translator_data)
+        bad_cloud_info = self.aai_ep.resolve_cloud_for_vnf(candidate_id, location_id, bad_vnf, service_type,
+                                                                 demand_name, triage_translator_data)
+        self.assertEqual("CloudOwner", good_cloud_info['cloud_owner'])
+        self.assertEqual("RegionOne", good_cloud_info['location_id'])
+        self.assertEqual("1", good_cloud_info['cloud_region_version'])
+
+        self.assertIsNone(bad_cloud_info)
 
-        self.assertEqual((None, None),
-                         self.aai_ep.resolve_cloud_region_id_and_version_for_vnf(candidate, bad_vnf, service_type,
-                                                                                 demand_name, triage_translator_data))
         v_server_links = list()
         v_server_links.append("/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/CloudOwner/RegionOne/tenants/\
 tenant/3c6c471ada7747fe8ff7f28e100b61e8/vservers/vserver/00bddefc-126e-4e4f-a18d-99b94d8d9a30")
@@ -601,14 +594,14 @@ tenant/3c6c471ada7747fe8ff7f28e100b61e8/vservers/vserver/00bddefc-126e-4e4f-a18d
 
         customer_id = 'Demonstration'
         self.assertEqual(customer_id,
-                         self.aai_ep.resolve_global_customer_id_for_vnf(candidate, good_vnf, customer_id, service_type,
+                         self.aai_ep.resolve_global_customer_id_for_vnf(candidate_id, location_id, good_vnf, customer_id, service_type,
                                                                         demand_name,
                                                                         triage_translator_data).get('d_value'))
         self.assertEqual("3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c",
-                         self.aai_ep.resolve_service_instance_id_for_vnf(candidate, good_vnf, customer_id, service_type,
+                         self.aai_ep.resolve_service_instance_id_for_vnf(candidate_id, location_id, good_vnf, customer_id, service_type,
                                                                          demand_name,
                                                                          triage_translator_data).get('d_value'))
-        self.assertIsNone(self.aai_ep.resolve_service_instance_id_for_vnf(candidate, bad_vnf, customer_id, service_type,
+        self.assertIsNone(self.aai_ep.resolve_service_instance_id_for_vnf(candidate_id, location_id, bad_vnf, customer_id, service_type,
                                                                           demand_name, triage_translator_data))
 
     def test_add_passthrough_parameters(self):
@@ -628,7 +621,7 @@ tenant/3c6c471ada7747fe8ff7f28e100b61e8/vservers/vserver/00bddefc-126e-4e4f-a18d
         candidate_info['passthrough_attributes']['param_one'] = "value"
         candidate_info['passthrough_attributes']['param_two'] = "value"
 
-        self.aai_ep.add_passthrough_attributes(candidate, parameters, 'demand', None)
+        self.aai_ep.add_passthrough_attributes(candidate, parameters, 'demand')
         self.assertDictEqual(candidate, candidate_info)
 
     def test_match_candidate_by_list(self):